diff strophe.js @ 0:2a8d4e8600d0

Initial commit.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Fri, 21 Dec 2018 21:34:17 +0100
parents
children
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/strophe.js
@@ -0,0 +1,6101 @@
+(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["strophe"] = factory();
+	else
+		root["strophe"] = 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/strophe.js");
+/******/ })
+/************************************************************************/
+/******/ ({
+
+/***/ "./node_modules/webpack/buildin/global.js":
+/*!***********************************!*\
+  !*** (webpack)/buildin/global.js ***!
+  \***********************************/
+/*! no static exports found */
+/***/ (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;
+
+
+/***/ }),
+
+/***/ "./src/bosh.js":
+/*!*********************!*\
+  !*** ./src/bosh.js ***!
+  \*********************/
+/*! no exports provided */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony import */ var core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! core */ "./src/core.js");
+/*
+    This program is distributed under the terms of the MIT license.
+    Please see the LICENSE file for details.
+
+    Copyright 2006-2008, OGG, LLC
+*/
+
+/* global window, setTimeout, clearTimeout, XMLHttpRequest, ActiveXObject */
+
+var Strophe = core__WEBPACK_IMPORTED_MODULE_0__["default"].Strophe;
+var $build = core__WEBPACK_IMPORTED_MODULE_0__["default"].$build;
+/** PrivateClass: Strophe.Request
+ *  _Private_ helper class that provides a cross implementation abstraction
+ *  for a BOSH related XMLHttpRequest.
+ *
+ *  The Strophe.Request class is used internally to encapsulate BOSH request
+ *  information.  It is not meant to be used from user's code.
+ */
+
+/** PrivateConstructor: Strophe.Request
+ *  Create and initialize a new Strophe.Request object.
+ *
+ *  Parameters:
+ *    (XMLElement) elem - The XML data to be sent in the request.
+ *    (Function) func - The function that will be called when the
+ *      XMLHttpRequest readyState changes.
+ *    (Integer) rid - The BOSH rid attribute associated with this request.
+ *    (Integer) sends - The number of times this same request has been sent.
+ */
+
+Strophe.Request = function (elem, func, rid, sends) {
+  this.id = ++Strophe._requestId;
+  this.xmlData = elem;
+  this.data = Strophe.serialize(elem); // save original function in case we need to make a new request
+  // from this one.
+
+  this.origFunc = func;
+  this.func = func;
+  this.rid = rid;
+  this.date = NaN;
+  this.sends = sends || 0;
+  this.abort = false;
+  this.dead = null;
+
+  this.age = function () {
+    if (!this.date) {
+      return 0;
+    }
+
+    var now = new Date();
+    return (now - this.date) / 1000;
+  };
+
+  this.timeDead = function () {
+    if (!this.dead) {
+      return 0;
+    }
+
+    var now = new Date();
+    return (now - this.dead) / 1000;
+  };
+
+  this.xhr = this._newXHR();
+};
+
+Strophe.Request.prototype = {
+  /** PrivateFunction: getResponse
+   *  Get a response from the underlying XMLHttpRequest.
+   *
+   *  This function attempts to get a response from the request and checks
+   *  for errors.
+   *
+   *  Throws:
+   *    "parsererror" - A parser error occured.
+   *    "bad-format" - The entity has sent XML that cannot be processed.
+   *
+   *  Returns:
+   *    The DOM element tree of the response.
+   */
+  getResponse: function getResponse() {
+    var node = null;
+
+    if (this.xhr.responseXML && this.xhr.responseXML.documentElement) {
+      node = this.xhr.responseXML.documentElement;
+
+      if (node.tagName === "parsererror") {
+        Strophe.error("invalid response received");
+        Strophe.error("responseText: " + this.xhr.responseText);
+        Strophe.error("responseXML: " + Strophe.serialize(this.xhr.responseXML));
+        throw new Error("parsererror");
+      }
+    } else if (this.xhr.responseText) {
+      // In React Native, we may get responseText but no responseXML.  We can try to parse it manually.
+      Strophe.debug("Got responseText but no responseXML; attempting to parse it with DOMParser...");
+      node = new DOMParser().parseFromString(this.xhr.responseText, 'application/xml').documentElement;
+
+      if (!node) {
+        throw new Error('Parsing produced null node');
+      } else if (node.querySelector('parsererror')) {
+        Strophe.error("invalid response received: " + node.querySelector('parsererror').textContent);
+        Strophe.error("responseText: " + this.xhr.responseText);
+        var error = new Error();
+        error.name = Strophe.ErrorCondition.BAD_FORMAT;
+        throw error;
+      }
+    }
+
+    return node;
+  },
+
+  /** PrivateFunction: _newXHR
+   *  _Private_ helper function to create XMLHttpRequests.
+   *
+   *  This function creates XMLHttpRequests across all implementations.
+   *
+   *  Returns:
+   *    A new XMLHttpRequest.
+   */
+  _newXHR: function _newXHR() {
+    var xhr = null;
+
+    if (window.XMLHttpRequest) {
+      xhr = new XMLHttpRequest();
+
+      if (xhr.overrideMimeType) {
+        xhr.overrideMimeType("text/xml; charset=utf-8");
+      }
+    } else if (window.ActiveXObject) {
+      xhr = new ActiveXObject("Microsoft.XMLHTTP");
+    } // use Function.bind() to prepend ourselves as an argument
+
+
+    xhr.onreadystatechange = this.func.bind(null, this);
+    return xhr;
+  }
+};
+/** Class: Strophe.Bosh
+ *  _Private_ helper class that handles BOSH Connections
+ *
+ *  The Strophe.Bosh class is used internally by Strophe.Connection
+ *  to encapsulate BOSH sessions. It is not meant to be used from user's code.
+ */
+
+/** File: bosh.js
+ *  A JavaScript library to enable BOSH in Strophejs.
+ *
+ *  this library uses Bidirectional-streams Over Synchronous HTTP (BOSH)
+ *  to emulate a persistent, stateful, two-way connection to an XMPP server.
+ *  More information on BOSH can be found in XEP 124.
+ */
+
+/** PrivateConstructor: Strophe.Bosh
+ *  Create and initialize a Strophe.Bosh object.
+ *
+ *  Parameters:
+ *    (Strophe.Connection) connection - The Strophe.Connection that will use BOSH.
+ *
+ *  Returns:
+ *    A new Strophe.Bosh object.
+ */
+
+Strophe.Bosh = function (connection) {
+  this._conn = connection;
+  /* request id for body tags */
+
+  this.rid = Math.floor(Math.random() * 4294967295);
+  /* The current session ID. */
+
+  this.sid = null; // default BOSH values
+
+  this.hold = 1;
+  this.wait = 60;
+  this.window = 5;
+  this.errors = 0;
+  this.inactivity = null;
+  this.lastResponseHeaders = null;
+  this._requests = [];
+};
+
+Strophe.Bosh.prototype = {
+  /** Variable: strip
+   *
+   *  BOSH-Connections will have all stanzas wrapped in a <body> tag when
+   *  passed to <Strophe.Connection.xmlInput> or <Strophe.Connection.xmlOutput>.
+   *  To strip this tag, User code can set <Strophe.Bosh.strip> to "body":
+   *
+   *  > Strophe.Bosh.prototype.strip = "body";
+   *
+   *  This will enable stripping of the body tag in both
+   *  <Strophe.Connection.xmlInput> and <Strophe.Connection.xmlOutput>.
+   */
+  strip: null,
+
+  /** PrivateFunction: _buildBody
+   *  _Private_ helper function to generate the <body/> wrapper for BOSH.
+   *
+   *  Returns:
+   *    A Strophe.Builder with a <body/> element.
+   */
+  _buildBody: function _buildBody() {
+    var bodyWrap = $build('body', {
+      'rid': this.rid++,
+      'xmlns': Strophe.NS.HTTPBIND
+    });
+
+    if (this.sid !== null) {
+      bodyWrap.attrs({
+        'sid': this.sid
+      });
+    }
+
+    if (this._conn.options.keepalive && this._conn._sessionCachingSupported()) {
+      this._cacheSession();
+    }
+
+    return bodyWrap;
+  },
+
+  /** PrivateFunction: _reset
+   *  Reset the connection.
+   *
+   *  This function is called by the reset function of the Strophe Connection
+   */
+  _reset: function _reset() {
+    this.rid = Math.floor(Math.random() * 4294967295);
+    this.sid = null;
+    this.errors = 0;
+
+    if (this._conn._sessionCachingSupported()) {
+      window.sessionStorage.removeItem('strophe-bosh-session');
+    }
+
+    this._conn.nextValidRid(this.rid);
+  },
+
+  /** PrivateFunction: _connect
+   *  _Private_ function that initializes the BOSH connection.
+   *
+   *  Creates and sends the Request that initializes the BOSH connection.
+   */
+  _connect: function _connect(wait, hold, route) {
+    this.wait = wait || this.wait;
+    this.hold = hold || this.hold;
+    this.errors = 0;
+
+    var body = this._buildBody().attrs({
+      "to": this._conn.domain,
+      "xml:lang": "en",
+      "wait": this.wait,
+      "hold": this.hold,
+      "content": "text/xml; charset=utf-8",
+      "ver": "1.6",
+      "xmpp:version": "1.0",
+      "xmlns:xmpp": Strophe.NS.BOSH
+    });
+
+    if (route) {
+      body.attrs({
+        'route': route
+      });
+    }
+
+    var _connect_cb = this._conn._connect_cb;
+
+    this._requests.push(new Strophe.Request(body.tree(), this._onRequestStateChange.bind(this, _connect_cb.bind(this._conn)), body.tree().getAttribute("rid")));
+
+    this._throttledRequestHandler();
+  },
+
+  /** PrivateFunction: _attach
+   *  Attach to an already created and authenticated BOSH session.
+   *
+   *  This function is provided to allow Strophe to attach to BOSH
+   *  sessions which have been created externally, perhaps by a Web
+   *  application.  This is often used to support auto-login type features
+   *  without putting user credentials into the page.
+   *
+   *  Parameters:
+   *    (String) jid - The full JID that is bound by the session.
+   *    (String) sid - The SID of the BOSH session.
+   *    (String) rid - The current RID of the BOSH session.  This RID
+   *      will be used by the next request.
+   *    (Function) callback The connect callback function.
+   *    (Integer) wait - The optional HTTPBIND wait value.  This is the
+   *      time the server will wait before returning an empty result for
+   *      a request.  The default setting of 60 seconds is recommended.
+   *      Other settings will require tweaks to the Strophe.TIMEOUT value.
+   *    (Integer) hold - The optional HTTPBIND hold value.  This is the
+   *      number of connections the server will hold at one time.  This
+   *      should almost always be set to 1 (the default).
+   *    (Integer) wind - The optional HTTBIND window value.  This is the
+   *      allowed range of request ids that are valid.  The default is 5.
+   */
+  _attach: function _attach(jid, sid, rid, callback, wait, hold, wind) {
+    this._conn.jid = jid;
+    this.sid = sid;
+    this.rid = rid;
+    this._conn.connect_callback = callback;
+    this._conn.domain = Strophe.getDomainFromJid(this._conn.jid);
+    this._conn.authenticated = true;
+    this._conn.connected = true;
+    this.wait = wait || this.wait;
+    this.hold = hold || this.hold;
+    this.window = wind || this.window;
+
+    this._conn._changeConnectStatus(Strophe.Status.ATTACHED, null);
+  },
+
+  /** PrivateFunction: _restore
+   *  Attempt to restore a cached BOSH session
+   *
+   *  Parameters:
+   *    (String) jid - The full JID that is bound by the session.
+   *      This parameter is optional but recommended, specifically in cases
+   *      where prebinded BOSH sessions are used where it's important to know
+   *      that the right session is being restored.
+   *    (Function) callback The connect callback function.
+   *    (Integer) wait - The optional HTTPBIND wait value.  This is the
+   *      time the server will wait before returning an empty result for
+   *      a request.  The default setting of 60 seconds is recommended.
+   *      Other settings will require tweaks to the Strophe.TIMEOUT value.
+   *    (Integer) hold - The optional HTTPBIND hold value.  This is the
+   *      number of connections the server will hold at one time.  This
+   *      should almost always be set to 1 (the default).
+   *    (Integer) wind - The optional HTTBIND window value.  This is the
+   *      allowed range of request ids that are valid.  The default is 5.
+   */
+  _restore: function _restore(jid, callback, wait, hold, wind) {
+    var session = JSON.parse(window.sessionStorage.getItem('strophe-bosh-session'));
+
+    if (typeof session !== "undefined" && session !== null && session.rid && session.sid && session.jid && (typeof jid === "undefined" || jid === null || Strophe.getBareJidFromJid(session.jid) === Strophe.getBareJidFromJid(jid) || // If authcid is null, then it's an anonymous login, so
+    // we compare only the domains:
+    Strophe.getNodeFromJid(jid) === null && Strophe.getDomainFromJid(session.jid) === jid)) {
+      this._conn.restored = true;
+
+      this._attach(session.jid, session.sid, session.rid, callback, wait, hold, wind);
+    } else {
+      var error = new Error("_restore: no restoreable session.");
+      error.name = "StropheSessionError";
+      throw error;
+    }
+  },
+
+  /** PrivateFunction: _cacheSession
+   *  _Private_ handler for the beforeunload event.
+   *
+   *  This handler is used to process the Bosh-part of the initial request.
+   *  Parameters:
+   *    (Strophe.Request) bodyWrap - The received stanza.
+   */
+  _cacheSession: function _cacheSession() {
+    if (this._conn.authenticated) {
+      if (this._conn.jid && this.rid && this.sid) {
+        window.sessionStorage.setItem('strophe-bosh-session', JSON.stringify({
+          'jid': this._conn.jid,
+          'rid': this.rid,
+          'sid': this.sid
+        }));
+      }
+    } else {
+      window.sessionStorage.removeItem('strophe-bosh-session');
+    }
+  },
+
+  /** PrivateFunction: _connect_cb
+   *  _Private_ handler for initial connection request.
+   *
+   *  This handler is used to process the Bosh-part of the initial request.
+   *  Parameters:
+   *    (Strophe.Request) bodyWrap - The received stanza.
+   */
+  _connect_cb: function _connect_cb(bodyWrap) {
+    var typ = bodyWrap.getAttribute("type");
+
+    if (typ !== null && typ === "terminate") {
+      // an error occurred
+      var cond = bodyWrap.getAttribute("condition");
+      Strophe.error("BOSH-Connection failed: " + cond);
+      var conflict = bodyWrap.getElementsByTagName("conflict");
+
+      if (cond !== null) {
+        if (cond === "remote-stream-error" && conflict.length > 0) {
+          cond = "conflict";
+        }
+
+        this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, cond);
+      } else {
+        this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "unknown");
+      }
+
+      this._conn._doDisconnect(cond);
+
+      return Strophe.Status.CONNFAIL;
+    } // check to make sure we don't overwrite these if _connect_cb is
+    // called multiple times in the case of missing stream:features
+
+
+    if (!this.sid) {
+      this.sid = bodyWrap.getAttribute("sid");
+    }
+
+    var wind = bodyWrap.getAttribute('requests');
+
+    if (wind) {
+      this.window = parseInt(wind, 10);
+    }
+
+    var hold = bodyWrap.getAttribute('hold');
+
+    if (hold) {
+      this.hold = parseInt(hold, 10);
+    }
+
+    var wait = bodyWrap.getAttribute('wait');
+
+    if (wait) {
+      this.wait = parseInt(wait, 10);
+    }
+
+    var inactivity = bodyWrap.getAttribute('inactivity');
+
+    if (inactivity) {
+      this.inactivity = parseInt(inactivity, 10);
+    }
+  },
+
+  /** PrivateFunction: _disconnect
+   *  _Private_ part of Connection.disconnect for Bosh
+   *
+   *  Parameters:
+   *    (Request) pres - This stanza will be sent before disconnecting.
+   */
+  _disconnect: function _disconnect(pres) {
+    this._sendTerminate(pres);
+  },
+
+  /** PrivateFunction: _doDisconnect
+   *  _Private_ function to disconnect.
+   *
+   *  Resets the SID and RID.
+   */
+  _doDisconnect: function _doDisconnect() {
+    this.sid = null;
+    this.rid = Math.floor(Math.random() * 4294967295);
+
+    if (this._conn._sessionCachingSupported()) {
+      window.sessionStorage.removeItem('strophe-bosh-session');
+    }
+
+    this._conn.nextValidRid(this.rid);
+  },
+
+  /** PrivateFunction: _emptyQueue
+   * _Private_ function to check if the Request queue is empty.
+   *
+   *  Returns:
+   *    True, if there are no Requests queued, False otherwise.
+   */
+  _emptyQueue: function _emptyQueue() {
+    return this._requests.length === 0;
+  },
+
+  /** PrivateFunction: _callProtocolErrorHandlers
+   *  _Private_ function to call error handlers registered for HTTP errors.
+   *
+   *  Parameters:
+   *    (Strophe.Request) req - The request that is changing readyState.
+   */
+  _callProtocolErrorHandlers: function _callProtocolErrorHandlers(req) {
+    var reqStatus = this._getRequestStatus(req);
+
+    var err_callback = this._conn.protocolErrorHandlers.HTTP[reqStatus];
+
+    if (err_callback) {
+      err_callback.call(this, reqStatus);
+    }
+  },
+
+  /** PrivateFunction: _hitError
+   *  _Private_ function to handle the error count.
+   *
+   *  Requests are resent automatically until their error count reaches
+   *  5.  Each time an error is encountered, this function is called to
+   *  increment the count and disconnect if the count is too high.
+   *
+   *  Parameters:
+   *    (Integer) reqStatus - The request status.
+   */
+  _hitError: function _hitError(reqStatus) {
+    this.errors++;
+    Strophe.warn("request errored, status: " + reqStatus + ", number of errors: " + this.errors);
+
+    if (this.errors > 4) {
+      this._conn._onDisconnectTimeout();
+    }
+  },
+
+  /** PrivateFunction: _no_auth_received
+   *
+   * Called on stream start/restart when no stream:features
+   * has been received and sends a blank poll request.
+   */
+  _no_auth_received: function _no_auth_received(callback) {
+    Strophe.warn("Server did not yet offer a supported authentication " + "mechanism. Sending a blank poll request.");
+
+    if (callback) {
+      callback = callback.bind(this._conn);
+    } else {
+      callback = this._conn._connect_cb.bind(this._conn);
+    }
+
+    var body = this._buildBody();
+
+    this._requests.push(new Strophe.Request(body.tree(), this._onRequestStateChange.bind(this, callback), body.tree().getAttribute("rid")));
+
+    this._throttledRequestHandler();
+  },
+
+  /** PrivateFunction: _onDisconnectTimeout
+   *  _Private_ timeout handler for handling non-graceful disconnection.
+   *
+   *  Cancels all remaining Requests and clears the queue.
+   */
+  _onDisconnectTimeout: function _onDisconnectTimeout() {
+    this._abortAllRequests();
+  },
+
+  /** PrivateFunction: _abortAllRequests
+   *  _Private_ helper function that makes sure all pending requests are aborted.
+   */
+  _abortAllRequests: function _abortAllRequests() {
+    while (this._requests.length > 0) {
+      var req = this._requests.pop();
+
+      req.abort = true;
+      req.xhr.abort(); // jslint complains, but this is fine. setting to empty func
+      // is necessary for IE6
+
+      req.xhr.onreadystatechange = function () {}; // jshint ignore:line
+
+    }
+  },
+
+  /** PrivateFunction: _onIdle
+   *  _Private_ handler called by Strophe.Connection._onIdle
+   *
+   *  Sends all queued Requests or polls with empty Request if there are none.
+   */
+  _onIdle: function _onIdle() {
+    var data = this._conn._data; // if no requests are in progress, poll
+
+    if (this._conn.authenticated && this._requests.length === 0 && data.length === 0 && !this._conn.disconnecting) {
+      Strophe.info("no requests during idle cycle, sending " + "blank request");
+      data.push(null);
+    }
+
+    if (this._conn.paused) {
+      return;
+    }
+
+    if (this._requests.length < 2 && data.length > 0) {
+      var body = this._buildBody();
+
+      for (var i = 0; i < data.length; i++) {
+        if (data[i] !== null) {
+          if (data[i] === "restart") {
+            body.attrs({
+              "to": this._conn.domain,
+              "xml:lang": "en",
+              "xmpp:restart": "true",
+              "xmlns:xmpp": Strophe.NS.BOSH
+            });
+          } else {
+            body.cnode(data[i]).up();
+          }
+        }
+      }
+
+      delete this._conn._data;
+      this._conn._data = [];
+
+      this._requests.push(new Strophe.Request(body.tree(), this._onRequestStateChange.bind(this, this._conn._dataRecv.bind(this._conn)), body.tree().getAttribute("rid")));
+
+      this._throttledRequestHandler();
+    }
+
+    if (this._requests.length > 0) {
+      var time_elapsed = this._requests[0].age();
+
+      if (this._requests[0].dead !== null) {
+        if (this._requests[0].timeDead() > Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait)) {
+          this._throttledRequestHandler();
+        }
+      }
+
+      if (time_elapsed > Math.floor(Strophe.TIMEOUT * this.wait)) {
+        Strophe.warn("Request " + this._requests[0].id + " timed out, over " + Math.floor(Strophe.TIMEOUT * this.wait) + " seconds since last activity");
+
+        this._throttledRequestHandler();
+      }
+    }
+  },
+
+  /** PrivateFunction: _getRequestStatus
+   *
+   *  Returns the HTTP status code from a Strophe.Request
+   *
+   *  Parameters:
+   *    (Strophe.Request) req - The Strophe.Request instance.
+   *    (Integer) def - The default value that should be returned if no
+   *          status value was found.
+   */
+  _getRequestStatus: function _getRequestStatus(req, def) {
+    var reqStatus;
+
+    if (req.xhr.readyState === 4) {
+      try {
+        reqStatus = req.xhr.status;
+      } catch (e) {
+        // ignore errors from undefined status attribute. Works
+        // around a browser bug
+        Strophe.error("Caught an error while retrieving a request's status, " + "reqStatus: " + reqStatus);
+      }
+    }
+
+    if (typeof reqStatus === "undefined") {
+      reqStatus = typeof def === 'number' ? def : 0;
+    }
+
+    return reqStatus;
+  },
+
+  /** PrivateFunction: _onRequestStateChange
+   *  _Private_ handler for Strophe.Request state changes.
+   *
+   *  This function is called when the XMLHttpRequest readyState changes.
+   *  It contains a lot of error handling logic for the many ways that
+   *  requests can fail, and calls the request callback when requests
+   *  succeed.
+   *
+   *  Parameters:
+   *    (Function) func - The handler for the request.
+   *    (Strophe.Request) req - The request that is changing readyState.
+   */
+  _onRequestStateChange: function _onRequestStateChange(func, req) {
+    Strophe.debug("request id " + req.id + "." + req.sends + " state changed to " + req.xhr.readyState);
+
+    if (req.abort) {
+      req.abort = false;
+      return;
+    }
+
+    if (req.xhr.readyState !== 4) {
+      // The request is not yet complete
+      return;
+    }
+
+    var reqStatus = this._getRequestStatus(req);
+
+    this.lastResponseHeaders = req.xhr.getAllResponseHeaders();
+
+    if (this.disconnecting && reqStatus >= 400) {
+      this._hitError(reqStatus);
+
+      this._callProtocolErrorHandlers(req);
+
+      return;
+    }
+
+    var valid_request = reqStatus > 0 && reqStatus < 500;
+    var too_many_retries = req.sends > this._conn.maxRetries;
+
+    if (valid_request || too_many_retries) {
+      // remove from internal queue
+      this._removeRequest(req);
+
+      Strophe.debug("request id " + req.id + " should now be removed");
+    }
+
+    if (reqStatus === 200) {
+      // request succeeded
+      var reqIs0 = this._requests[0] === req;
+      var reqIs1 = this._requests[1] === req; // if request 1 finished, or request 0 finished and request
+      // 1 is over Strophe.SECONDARY_TIMEOUT seconds old, we need to
+      // restart the other - both will be in the first spot, as the
+      // completed request has been removed from the queue already
+
+      if (reqIs1 || reqIs0 && this._requests.length > 0 && this._requests[0].age() > Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait)) {
+        this._restartRequest(0);
+      }
+
+      this._conn.nextValidRid(Number(req.rid) + 1);
+
+      Strophe.debug("request id " + req.id + "." + req.sends + " got 200");
+      func(req); // call handler
+
+      this.errors = 0;
+    } else if (reqStatus === 0 || reqStatus >= 400 && reqStatus < 600 || reqStatus >= 12000) {
+      // request failed
+      Strophe.error("request id " + req.id + "." + req.sends + " error " + reqStatus + " happened");
+
+      this._hitError(reqStatus);
+
+      this._callProtocolErrorHandlers(req);
+
+      if (reqStatus >= 400 && reqStatus < 500) {
+        this._conn._changeConnectStatus(Strophe.Status.DISCONNECTING, null);
+
+        this._conn._doDisconnect();
+      }
+    } else {
+      Strophe.error("request id " + req.id + "." + req.sends + " error " + reqStatus + " happened");
+    }
+
+    if (!valid_request && !too_many_retries) {
+      this._throttledRequestHandler();
+    } else if (too_many_retries && !this._conn.connected) {
+      this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "giving-up");
+    }
+  },
+
+  /** PrivateFunction: _processRequest
+   *  _Private_ function to process a request in the queue.
+   *
+   *  This function takes requests off the queue and sends them and
+   *  restarts dead requests.
+   *
+   *  Parameters:
+   *    (Integer) i - The index of the request in the queue.
+   */
+  _processRequest: function _processRequest(i) {
+    var _this = this;
+
+    var req = this._requests[i];
+
+    var reqStatus = this._getRequestStatus(req, -1); // make sure we limit the number of retries
+
+
+    if (req.sends > this._conn.maxRetries) {
+      this._conn._onDisconnectTimeout();
+
+      return;
+    }
+
+    var time_elapsed = req.age();
+    var primary_timeout = !isNaN(time_elapsed) && time_elapsed > Math.floor(Strophe.TIMEOUT * this.wait);
+    var secondary_timeout = req.dead !== null && req.timeDead() > Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait);
+    var server_error = req.xhr.readyState === 4 && (reqStatus < 1 || reqStatus >= 500);
+
+    if (primary_timeout || secondary_timeout || server_error) {
+      if (secondary_timeout) {
+        Strophe.error("Request ".concat(this._requests[i].id, " timed out (secondary), restarting"));
+      }
+
+      req.abort = true;
+      req.xhr.abort(); // setting to null fails on IE6, so set to empty function
+
+      req.xhr.onreadystatechange = function () {};
+
+      this._requests[i] = new Strophe.Request(req.xmlData, req.origFunc, req.rid, req.sends);
+      req = this._requests[i];
+    }
+
+    if (req.xhr.readyState === 0) {
+      Strophe.debug("request id " + req.id + "." + req.sends + " posting");
+
+      try {
+        var content_type = this._conn.options.contentType || "text/xml; charset=utf-8";
+        req.xhr.open("POST", this._conn.service, this._conn.options.sync ? false : true);
+
+        if (typeof req.xhr.setRequestHeader !== 'undefined') {
+          // IE9 doesn't have setRequestHeader
+          req.xhr.setRequestHeader("Content-Type", content_type);
+        }
+
+        if (this._conn.options.withCredentials) {
+          req.xhr.withCredentials = true;
+        }
+      } catch (e2) {
+        Strophe.error("XHR open failed: " + e2.toString());
+
+        if (!this._conn.connected) {
+          this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "bad-service");
+        }
+
+        this._conn.disconnect();
+
+        return;
+      } // Fires the XHR request -- may be invoked immediately
+      // or on a gradually expanding retry window for reconnects
+
+
+      var sendFunc = function sendFunc() {
+        req.date = new Date();
+
+        if (_this._conn.options.customHeaders) {
+          var headers = _this._conn.options.customHeaders;
+
+          for (var header in headers) {
+            if (Object.prototype.hasOwnProperty.call(headers, header)) {
+              req.xhr.setRequestHeader(header, headers[header]);
+            }
+          }
+        }
+
+        req.xhr.send(req.data);
+      }; // Implement progressive backoff for reconnects --
+      // First retry (send === 1) should also be instantaneous
+
+
+      if (req.sends > 1) {
+        // Using a cube of the retry number creates a nicely
+        // expanding retry window
+        var backoff = Math.min(Math.floor(Strophe.TIMEOUT * this.wait), Math.pow(req.sends, 3)) * 1000;
+        setTimeout(function () {
+          // XXX: setTimeout should be called only with function expressions (23974bc1)
+          sendFunc();
+        }, backoff);
+      } else {
+        sendFunc();
+      }
+
+      req.sends++;
+
+      if (this._conn.xmlOutput !== Strophe.Connection.prototype.xmlOutput) {
+        if (req.xmlData.nodeName === this.strip && req.xmlData.childNodes.length) {
+          this._conn.xmlOutput(req.xmlData.childNodes[0]);
+        } else {
+          this._conn.xmlOutput(req.xmlData);
+        }
+      }
+
+      if (this._conn.rawOutput !== Strophe.Connection.prototype.rawOutput) {
+        this._conn.rawOutput(req.data);
+      }
+    } else {
+      Strophe.debug("_processRequest: " + (i === 0 ? "first" : "second") + " request has readyState of " + req.xhr.readyState);
+    }
+  },
+
+  /** PrivateFunction: _removeRequest
+   *  _Private_ function to remove a request from the queue.
+   *
+   *  Parameters:
+   *    (Strophe.Request) req - The request to remove.
+   */
+  _removeRequest: function _removeRequest(req) {
+    Strophe.debug("removing request");
+
+    for (var i = this._requests.length - 1; i >= 0; i--) {
+      if (req === this._requests[i]) {
+        this._requests.splice(i, 1);
+      }
+    } // IE6 fails on setting to null, so set to empty function
+
+
+    req.xhr.onreadystatechange = function () {};
+
+    this._throttledRequestHandler();
+  },
+
+  /** PrivateFunction: _restartRequest
+   *  _Private_ function to restart a request that is presumed dead.
+   *
+   *  Parameters:
+   *    (Integer) i - The index of the request in the queue.
+   */
+  _restartRequest: function _restartRequest(i) {
+    var req = this._requests[i];
+
+    if (req.dead === null) {
+      req.dead = new Date();
+    }
+
+    this._processRequest(i);
+  },
+
+  /** PrivateFunction: _reqToData
+   * _Private_ function to get a stanza out of a request.
+   *
+   * Tries to extract a stanza out of a Request Object.
+   * When this fails the current connection will be disconnected.
+   *
+   *  Parameters:
+   *    (Object) req - The Request.
+   *
+   *  Returns:
+   *    The stanza that was passed.
+   */
+  _reqToData: function _reqToData(req) {
+    try {
+      return req.getResponse();
+    } catch (e) {
+      if (e.message !== "parsererror") {
+        throw e;
+      }
+
+      this._conn.disconnect("strophe-parsererror");
+    }
+  },
+
+  /** PrivateFunction: _sendTerminate
+   *  _Private_ function to send initial disconnect sequence.
+   *
+   *  This is the first step in a graceful disconnect.  It sends
+   *  the BOSH server a terminate body and includes an unavailable
+   *  presence if authentication has completed.
+   */
+  _sendTerminate: function _sendTerminate(pres) {
+    Strophe.info("_sendTerminate was called");
+
+    var body = this._buildBody().attrs({
+      type: "terminate"
+    });
+
+    if (pres) {
+      body.cnode(pres.tree());
+    }
+
+    var req = new Strophe.Request(body.tree(), this._onRequestStateChange.bind(this, this._conn._dataRecv.bind(this._conn)), body.tree().getAttribute("rid"));
+
+    this._requests.push(req);
+
+    this._throttledRequestHandler();
+  },
+
+  /** PrivateFunction: _send
+   *  _Private_ part of the Connection.send function for BOSH
+   *
+   * Just triggers the RequestHandler to send the messages that are in the queue
+   */
+  _send: function _send() {
+    var _this2 = this;
+
+    clearTimeout(this._conn._idleTimeout);
+
+    this._throttledRequestHandler();
+
+    this._conn._idleTimeout = setTimeout(function () {
+      return _this2._conn._onIdle();
+    }, 100);
+  },
+
+  /** PrivateFunction: _sendRestart
+   *
+   *  Send an xmpp:restart stanza.
+   */
+  _sendRestart: function _sendRestart() {
+    this._throttledRequestHandler();
+
+    clearTimeout(this._conn._idleTimeout);
+  },
+
+  /** PrivateFunction: _throttledRequestHandler
+   *  _Private_ function to throttle requests to the connection window.
+   *
+   *  This function makes sure we don't send requests so fast that the
+   *  request ids overflow the connection window in the case that one
+   *  request died.
+   */
+  _throttledRequestHandler: function _throttledRequestHandler() {
+    if (!this._requests) {
+      Strophe.debug("_throttledRequestHandler called with " + "undefined requests");
+    } else {
+      Strophe.debug("_throttledRequestHandler called with " + this._requests.length + " requests");
+    }
+
+    if (!this._requests || this._requests.length === 0) {
+      return;
+    }
+
+    if (this._requests.length > 0) {
+      this._processRequest(0);
+    }
+
+    if (this._requests.length > 1 && Math.abs(this._requests[0].rid - this._requests[1].rid) < this.window) {
+      this._processRequest(1);
+    }
+  }
+};
+
+/***/ }),
+
+/***/ "./src/core.js":
+/*!*********************!*\
+  !*** ./src/core.js ***!
+  \*********************/
+/*! exports provided: Strophe, $build, $iq, $msg, $pres, SHA1, MD5, default */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Strophe", function() { return Strophe; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "$build", function() { return $build; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "$iq", function() { return $iq; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "$msg", function() { return $msg; });
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "$pres", function() { return $pres; });
+/* harmony import */ var md5__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! md5 */ "./src/md5.js");
+/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "MD5", function() { return md5__WEBPACK_IMPORTED_MODULE_0__["default"]; });
+
+/* harmony import */ var sha1__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! sha1 */ "./src/sha1.js");
+/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "SHA1", function() { return sha1__WEBPACK_IMPORTED_MODULE_1__["default"]; });
+
+/* harmony import */ var utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! utils */ "./src/utils.js");
+function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }
+
+function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }
+
+function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }
+
+function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }
+
+function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
+/*
+    This program is distributed under the terms of the MIT license.
+    Please see the LICENSE file for details.
+
+    Copyright 2006-2018, OGG, LLC
+*/
+
+/*global define, document, sessionStorage, setTimeout, clearTimeout, ActiveXObject, DOMParser, btoa, atob, module */
+
+
+
+/** Function: $build
+ *  Create a Strophe.Builder.
+ *  This is an alias for 'new Strophe.Builder(name, attrs)'.
+ *
+ *  Parameters:
+ *    (String) name - The root element name.
+ *    (Object) attrs - The attributes for the root element in object notation.
+ *
+ *  Returns:
+ *    A new Strophe.Builder object.
+ */
+
+function $build(name, attrs) {
+  return new Strophe.Builder(name, attrs);
+}
+/** Function: $msg
+ *  Create a Strophe.Builder with a <message/> element as the root.
+ *
+ *  Parameters:
+ *    (Object) attrs - The <message/> element attributes in object notation.
+ *
+ *  Returns:
+ *    A new Strophe.Builder object.
+ */
+
+
+function $msg(attrs) {
+  return new Strophe.Builder("message", attrs);
+}
+/** Function: $iq
+ *  Create a Strophe.Builder with an <iq/> element as the root.
+ *
+ *  Parameters:
+ *    (Object) attrs - The <iq/> element attributes in object notation.
+ *
+ *  Returns:
+ *    A new Strophe.Builder object.
+ */
+
+
+function $iq(attrs) {
+  return new Strophe.Builder("iq", attrs);
+}
+/** Function: $pres
+ *  Create a Strophe.Builder with a <presence/> element as the root.
+ *
+ *  Parameters:
+ *    (Object) attrs - The <presence/> element attributes in object notation.
+ *
+ *  Returns:
+ *    A new Strophe.Builder object.
+ */
+
+
+function $pres(attrs) {
+  return new Strophe.Builder("presence", attrs);
+}
+/** Class: Strophe
+ *  An object container for all Strophe library functions.
+ *
+ *  This class is just a container for all the objects and constants
+ *  used in the library.  It is not meant to be instantiated, but to
+ *  provide a namespace for library objects, constants, and functions.
+ */
+
+
+var Strophe = {
+  /** Constant: VERSION */
+  VERSION: "1.3.1",
+
+  /** Constants: XMPP Namespace Constants
+   *  Common namespace constants from the XMPP RFCs and XEPs.
+   *
+   *  NS.HTTPBIND - HTTP BIND namespace from XEP 124.
+   *  NS.BOSH - BOSH namespace from XEP 206.
+   *  NS.CLIENT - Main XMPP client namespace.
+   *  NS.AUTH - Legacy authentication namespace.
+   *  NS.ROSTER - Roster operations namespace.
+   *  NS.PROFILE - Profile namespace.
+   *  NS.DISCO_INFO - Service discovery info namespace from XEP 30.
+   *  NS.DISCO_ITEMS - Service discovery items namespace from XEP 30.
+   *  NS.MUC - Multi-User Chat namespace from XEP 45.
+   *  NS.SASL - XMPP SASL namespace from RFC 3920.
+   *  NS.STREAM - XMPP Streams namespace from RFC 3920.
+   *  NS.BIND - XMPP Binding namespace from RFC 3920.
+   *  NS.SESSION - XMPP Session namespace from RFC 3920.
+   *  NS.XHTML_IM - XHTML-IM namespace from XEP 71.
+   *  NS.XHTML - XHTML body namespace from XEP 71.
+   */
+  NS: {
+    HTTPBIND: "http://jabber.org/protocol/httpbind",
+    BOSH: "urn:xmpp:xbosh",
+    CLIENT: "jabber:client",
+    AUTH: "jabber:iq:auth",
+    ROSTER: "jabber:iq:roster",
+    PROFILE: "jabber:iq:profile",
+    DISCO_INFO: "http://jabber.org/protocol/disco#info",
+    DISCO_ITEMS: "http://jabber.org/protocol/disco#items",
+    MUC: "http://jabber.org/protocol/muc",
+    SASL: "urn:ietf:params:xml:ns:xmpp-sasl",
+    STREAM: "http://etherx.jabber.org/streams",
+    FRAMING: "urn:ietf:params:xml:ns:xmpp-framing",
+    BIND: "urn:ietf:params:xml:ns:xmpp-bind",
+    SESSION: "urn:ietf:params:xml:ns:xmpp-session",
+    VERSION: "jabber:iq:version",
+    STANZAS: "urn:ietf:params:xml:ns:xmpp-stanzas",
+    XHTML_IM: "http://jabber.org/protocol/xhtml-im",
+    XHTML: "http://www.w3.org/1999/xhtml"
+  },
+
+  /** Constants: XHTML_IM Namespace
+   *  contains allowed tags, tag attributes, and css properties.
+   *  Used in the createHtml function to filter incoming html into the allowed XHTML-IM subset.
+   *  See http://xmpp.org/extensions/xep-0071.html#profile-summary for the list of recommended
+   *  allowed tags and their attributes.
+   */
+  XHTML: {
+    tags: ['a', 'blockquote', 'br', 'cite', 'em', 'img', 'li', 'ol', 'p', 'span', 'strong', 'ul', 'body'],
+    attributes: {
+      'a': ['href'],
+      'blockquote': ['style'],
+      'br': [],
+      'cite': ['style'],
+      'em': [],
+      'img': ['src', 'alt', 'style', 'height', 'width'],
+      'li': ['style'],
+      'ol': ['style'],
+      'p': ['style'],
+      'span': ['style'],
+      'strong': [],
+      'ul': ['style'],
+      'body': []
+    },
+    css: ['background-color', 'color', 'font-family', 'font-size', 'font-style', 'font-weight', 'margin-left', 'margin-right', 'text-align', 'text-decoration'],
+
+    /** Function: XHTML.validTag
+     *
+     * Utility method to determine whether a tag is allowed
+     * in the XHTML_IM namespace.
+     *
+     * XHTML tag names are case sensitive and must be lower case.
+     */
+    validTag: function validTag(tag) {
+      for (var i = 0; i < Strophe.XHTML.tags.length; i++) {
+        if (tag === Strophe.XHTML.tags[i]) {
+          return true;
+        }
+      }
+
+      return false;
+    },
+
+    /** Function: XHTML.validAttribute
+     *
+     * Utility method to determine whether an attribute is allowed
+     * as recommended per XEP-0071
+     *
+     * XHTML attribute names are case sensitive and must be lower case.
+     */
+    validAttribute: function validAttribute(tag, attribute) {
+      if (typeof Strophe.XHTML.attributes[tag] !== 'undefined' && Strophe.XHTML.attributes[tag].length > 0) {
+        for (var i = 0; i < Strophe.XHTML.attributes[tag].length; i++) {
+          if (attribute === Strophe.XHTML.attributes[tag][i]) {
+            return true;
+          }
+        }
+      }
+
+      return false;
+    },
+    validCSS: function validCSS(style) {
+      for (var i = 0; i < Strophe.XHTML.css.length; i++) {
+        if (style === Strophe.XHTML.css[i]) {
+          return true;
+        }
+      }
+
+      return false;
+    }
+  },
+
+  /** Constants: Connection Status Constants
+   *  Connection status constants for use by the connection handler
+   *  callback.
+   *
+   *  Status.ERROR - An error has occurred
+   *  Status.CONNECTING - The connection is currently being made
+   *  Status.CONNFAIL - The connection attempt failed
+   *  Status.AUTHENTICATING - The connection is authenticating
+   *  Status.AUTHFAIL - The authentication attempt failed
+   *  Status.CONNECTED - The connection has succeeded
+   *  Status.DISCONNECTED - The connection has been terminated
+   *  Status.DISCONNECTING - The connection is currently being terminated
+   *  Status.ATTACHED - The connection has been attached
+   *  Status.REDIRECT - The connection has been redirected
+   *  Status.CONNTIMEOUT - The connection has timed out
+   */
+  Status: {
+    ERROR: 0,
+    CONNECTING: 1,
+    CONNFAIL: 2,
+    AUTHENTICATING: 3,
+    AUTHFAIL: 4,
+    CONNECTED: 5,
+    DISCONNECTED: 6,
+    DISCONNECTING: 7,
+    ATTACHED: 8,
+    REDIRECT: 9,
+    CONNTIMEOUT: 10
+  },
+  ErrorCondition: {
+    BAD_FORMAT: "bad-format",
+    CONFLICT: "conflict",
+    MISSING_JID_NODE: "x-strophe-bad-non-anon-jid",
+    NO_AUTH_MECH: "no-auth-mech",
+    UNKNOWN_REASON: "unknown"
+  },
+
+  /** Constants: Log Level Constants
+   *  Logging level indicators.
+   *
+   *  LogLevel.DEBUG - Debug output
+   *  LogLevel.INFO - Informational output
+   *  LogLevel.WARN - Warnings
+   *  LogLevel.ERROR - Errors
+   *  LogLevel.FATAL - Fatal errors
+   */
+  LogLevel: {
+    DEBUG: 0,
+    INFO: 1,
+    WARN: 2,
+    ERROR: 3,
+    FATAL: 4
+  },
+
+  /** PrivateConstants: DOM Element Type Constants
+   *  DOM element types.
+   *
+   *  ElementType.NORMAL - Normal element.
+   *  ElementType.TEXT - Text data element.
+   *  ElementType.FRAGMENT - XHTML fragment element.
+   */
+  ElementType: {
+    NORMAL: 1,
+    TEXT: 3,
+    CDATA: 4,
+    FRAGMENT: 11
+  },
+
+  /** PrivateConstants: Timeout Values
+   *  Timeout values for error states.  These values are in seconds.
+   *  These should not be changed unless you know exactly what you are
+   *  doing.
+   *
+   *  TIMEOUT - Timeout multiplier. A waiting request will be considered
+   *      failed after Math.floor(TIMEOUT * wait) seconds have elapsed.
+   *      This defaults to 1.1, and with default wait, 66 seconds.
+   *  SECONDARY_TIMEOUT - Secondary timeout multiplier. In cases where
+   *      Strophe can detect early failure, it will consider the request
+   *      failed if it doesn't return after
+   *      Math.floor(SECONDARY_TIMEOUT * wait) seconds have elapsed.
+   *      This defaults to 0.1, and with default wait, 6 seconds.
+   */
+  TIMEOUT: 1.1,
+  SECONDARY_TIMEOUT: 0.1,
+
+  /** Function: addNamespace
+   *  This function is used to extend the current namespaces in
+   *  Strophe.NS.  It takes a key and a value with the key being the
+   *  name of the new namespace, with its actual value.
+   *  For example:
+   *  Strophe.addNamespace('PUBSUB', "http://jabber.org/protocol/pubsub");
+   *
+   *  Parameters:
+   *    (String) name - The name under which the namespace will be
+   *      referenced under Strophe.NS
+   *    (String) value - The actual namespace.
+   */
+  addNamespace: function addNamespace(name, value) {
+    Strophe.NS[name] = value;
+  },
+
+  /** Function: forEachChild
+   *  Map a function over some or all child elements of a given element.
+   *
+   *  This is a small convenience function for mapping a function over
+   *  some or all of the children of an element.  If elemName is null, all
+   *  children will be passed to the function, otherwise only children
+   *  whose tag names match elemName will be passed.
+   *
+   *  Parameters:
+   *    (XMLElement) elem - The element to operate on.
+   *    (String) elemName - The child element tag name filter.
+   *    (Function) func - The function to apply to each child.  This
+   *      function should take a single argument, a DOM element.
+   */
+  forEachChild: function forEachChild(elem, elemName, func) {
+    for (var i = 0; i < elem.childNodes.length; i++) {
+      var childNode = elem.childNodes[i];
+
+      if (childNode.nodeType === Strophe.ElementType.NORMAL && (!elemName || this.isTagEqual(childNode, elemName))) {
+        func(childNode);
+      }
+    }
+  },
+
+  /** Function: isTagEqual
+   *  Compare an element's tag name with a string.
+   *
+   *  This function is case sensitive.
+   *
+   *  Parameters:
+   *    (XMLElement) el - A DOM element.
+   *    (String) name - The element name.
+   *
+   *  Returns:
+   *    true if the element's tag name matches _el_, and false
+   *    otherwise.
+   */
+  isTagEqual: function isTagEqual(el, name) {
+    return el.tagName === name;
+  },
+
+  /** PrivateVariable: _xmlGenerator
+   *  _Private_ variable that caches a DOM document to
+   *  generate elements.
+   */
+  _xmlGenerator: null,
+
+  /** PrivateFunction: _makeGenerator
+   *  _Private_ function that creates a dummy XML DOM document to serve as
+   *  an element and text node generator.
+   */
+  _makeGenerator: function _makeGenerator() {
+    var doc; // IE9 does implement createDocument(); however, using it will cause the browser to leak memory on page unload.
+    // Here, we test for presence of createDocument() plus IE's proprietary documentMode attribute, which would be
+    // less than 10 in the case of IE9 and below.
+
+    if (document.implementation.createDocument === undefined || document.implementation.createDocument && document.documentMode && document.documentMode < 10) {
+      doc = this._getIEXmlDom();
+      doc.appendChild(doc.createElement('strophe'));
+    } else {
+      doc = document.implementation.createDocument('jabber:client', 'strophe', null);
+    }
+
+    return doc;
+  },
+
+  /** Function: xmlGenerator
+   *  Get the DOM document to generate elements.
+   *
+   *  Returns:
+   *    The currently used DOM document.
+   */
+  xmlGenerator: function xmlGenerator() {
+    if (!Strophe._xmlGenerator) {
+      Strophe._xmlGenerator = Strophe._makeGenerator();
+    }
+
+    return Strophe._xmlGenerator;
+  },
+
+  /** PrivateFunction: _getIEXmlDom
+   *  Gets IE xml doc object
+   *
+   *  Returns:
+   *    A Microsoft XML DOM Object
+   *  See Also:
+   *    http://msdn.microsoft.com/en-us/library/ms757837%28VS.85%29.aspx
+   */
+  _getIEXmlDom: function _getIEXmlDom() {
+    var doc = null;
+    var docStrings = ["Msxml2.DOMDocument.6.0", "Msxml2.DOMDocument.5.0", "Msxml2.DOMDocument.4.0", "MSXML2.DOMDocument.3.0", "MSXML2.DOMDocument", "MSXML.DOMDocument", "Microsoft.XMLDOM"];
+
+    for (var d = 0; d < docStrings.length; d++) {
+      if (doc === null) {
+        try {
+          doc = new ActiveXObject(docStrings[d]);
+        } catch (e) {
+          doc = null;
+        }
+      } else {
+        break;
+      }
+    }
+
+    return doc;
+  },
+
+  /** Function: xmlElement
+   *  Create an XML DOM element.
+   *
+   *  This function creates an XML DOM element correctly across all
+   *  implementations. Note that these are not HTML DOM elements, which
+   *  aren't appropriate for XMPP stanzas.
+   *
+   *  Parameters:
+   *    (String) name - The name for the element.
+   *    (Array|Object) attrs - An optional array or object containing
+   *      key/value pairs to use as element attributes. The object should
+   *      be in the format {'key': 'value'} or {key: 'value'}. The array
+   *      should have the format [['key1', 'value1'], ['key2', 'value2']].
+   *    (String) text - The text child data for the element.
+   *
+   *  Returns:
+   *    A new XML DOM element.
+   */
+  xmlElement: function xmlElement(name) {
+    if (!name) {
+      return null;
+    }
+
+    var node = Strophe.xmlGenerator().createElement(name); // FIXME: this should throw errors if args are the wrong type or
+    // there are more than two optional args
+
+    for (var a = 1; a < arguments.length; a++) {
+      var arg = arguments[a];
+
+      if (!arg) {
+        continue;
+      }
+
+      if (typeof arg === "string" || typeof arg === "number") {
+        node.appendChild(Strophe.xmlTextNode(arg));
+      } else if (_typeof(arg) === "object" && typeof arg.sort === "function") {
+        for (var i = 0; i < arg.length; i++) {
+          var attr = arg[i];
+
+          if (_typeof(attr) === "object" && typeof attr.sort === "function" && attr[1] !== undefined && attr[1] !== null) {
+            node.setAttribute(attr[0], attr[1]);
+          }
+        }
+      } else if (_typeof(arg) === "object") {
+        for (var k in arg) {
+          if (Object.prototype.hasOwnProperty.call(arg, k) && arg[k] !== undefined && arg[k] !== null) {
+            node.setAttribute(k, arg[k]);
+          }
+        }
+      }
+    }
+
+    return node;
+  },
+
+  /*  Function: xmlescape
+   *  Excapes invalid xml characters.
+   *
+   *  Parameters:
+   *     (String) text - text to escape.
+   *
+   *  Returns:
+   *      Escaped text.
+   */
+  xmlescape: function xmlescape(text) {
+    text = text.replace(/\&/g, "&amp;");
+    text = text.replace(/</g, "&lt;");
+    text = text.replace(/>/g, "&gt;");
+    text = text.replace(/'/g, "&apos;");
+    text = text.replace(/"/g, "&quot;");
+    return text;
+  },
+
+  /*  Function: xmlunescape
+  *  Unexcapes invalid xml characters.
+  *
+  *  Parameters:
+  *     (String) text - text to unescape.
+  *
+  *  Returns:
+  *      Unescaped text.
+  */
+  xmlunescape: function xmlunescape(text) {
+    text = text.replace(/\&amp;/g, "&");
+    text = text.replace(/&lt;/g, "<");
+    text = text.replace(/&gt;/g, ">");
+    text = text.replace(/&apos;/g, "'");
+    text = text.replace(/&quot;/g, "\"");
+    return text;
+  },
+
+  /** Function: xmlTextNode
+   *  Creates an XML DOM text node.
+   *
+   *  Provides a cross implementation version of document.createTextNode.
+   *
+   *  Parameters:
+   *    (String) text - The content of the text node.
+   *
+   *  Returns:
+   *    A new XML DOM text node.
+   */
+  xmlTextNode: function xmlTextNode(text) {
+    return Strophe.xmlGenerator().createTextNode(text);
+  },
+
+  /** Function: xmlHtmlNode
+   *  Creates an XML DOM html node.
+   *
+   *  Parameters:
+   *    (String) html - The content of the html node.
+   *
+   *  Returns:
+   *    A new XML DOM text node.
+   */
+  xmlHtmlNode: function xmlHtmlNode(html) {
+    var node; //ensure text is escaped
+
+    if (DOMParser) {
+      var parser = new DOMParser();
+      node = parser.parseFromString(html, "text/xml");
+    } else {
+      node = new ActiveXObject("Microsoft.XMLDOM");
+      node.async = "false";
+      node.loadXML(html);
+    }
+
+    return node;
+  },
+
+  /** Function: getText
+   *  Get the concatenation of all text children of an element.
+   *
+   *  Parameters:
+   *    (XMLElement) elem - A DOM element.
+   *
+   *  Returns:
+   *    A String with the concatenated text of all text element children.
+   */
+  getText: function getText(elem) {
+    if (!elem) {
+      return null;
+    }
+
+    var str = "";
+
+    if (elem.childNodes.length === 0 && elem.nodeType === Strophe.ElementType.TEXT) {
+      str += elem.nodeValue;
+    }
+
+    for (var i = 0; i < elem.childNodes.length; i++) {
+      if (elem.childNodes[i].nodeType === Strophe.ElementType.TEXT) {
+        str += elem.childNodes[i].nodeValue;
+      }
+    }
+
+    return Strophe.xmlescape(str);
+  },
+
+  /** Function: copyElement
+   *  Copy an XML DOM element.
+   *
+   *  This function copies a DOM element and all its descendants and returns
+   *  the new copy.
+   *
+   *  Parameters:
+   *    (XMLElement) elem - A DOM element.
+   *
+   *  Returns:
+   *    A new, copied DOM element tree.
+   */
+  copyElement: function copyElement(elem) {
+    var el;
+
+    if (elem.nodeType === Strophe.ElementType.NORMAL) {
+      el = Strophe.xmlElement(elem.tagName);
+
+      for (var i = 0; i < elem.attributes.length; i++) {
+        el.setAttribute(elem.attributes[i].nodeName, elem.attributes[i].value);
+      }
+
+      for (var _i = 0; _i < elem.childNodes.length; _i++) {
+        el.appendChild(Strophe.copyElement(elem.childNodes[_i]));
+      }
+    } else if (elem.nodeType === Strophe.ElementType.TEXT) {
+      el = Strophe.xmlGenerator().createTextNode(elem.nodeValue);
+    }
+
+    return el;
+  },
+
+  /** Function: createHtml
+   *  Copy an HTML DOM element into an XML DOM.
+   *
+   *  This function copies a DOM element and all its descendants and returns
+   *  the new copy.
+   *
+   *  Parameters:
+   *    (HTMLElement) elem - A DOM element.
+   *
+   *  Returns:
+   *    A new, copied DOM element tree.
+   */
+  createHtml: function createHtml(elem) {
+    var el;
+
+    if (elem.nodeType === Strophe.ElementType.NORMAL) {
+      var tag = elem.nodeName.toLowerCase(); // XHTML tags must be lower case.
+
+      if (Strophe.XHTML.validTag(tag)) {
+        try {
+          el = Strophe.xmlElement(tag);
+
+          for (var i = 0; i < Strophe.XHTML.attributes[tag].length; i++) {
+            var attribute = Strophe.XHTML.attributes[tag][i];
+            var value = elem.getAttribute(attribute);
+
+            if (typeof value === 'undefined' || value === null || value === '' || value === false || value === 0) {
+              continue;
+            }
+
+            if (attribute === 'style' && _typeof(value) === 'object' && typeof value.cssText !== 'undefined') {
+              value = value.cssText; // we're dealing with IE, need to get CSS out
+            } // filter out invalid css styles
+
+
+            if (attribute === 'style') {
+              var css = [];
+              var cssAttrs = value.split(';');
+
+              for (var j = 0; j < cssAttrs.length; j++) {
+                var attr = cssAttrs[j].split(':');
+                var cssName = attr[0].replace(/^\s*/, "").replace(/\s*$/, "").toLowerCase();
+
+                if (Strophe.XHTML.validCSS(cssName)) {
+                  var cssValue = attr[1].replace(/^\s*/, "").replace(/\s*$/, "");
+                  css.push(cssName + ': ' + cssValue);
+                }
+              }
+
+              if (css.length > 0) {
+                value = css.join('; ');
+                el.setAttribute(attribute, value);
+              }
+            } else {
+              el.setAttribute(attribute, value);
+            }
+          }
+
+          for (var _i2 = 0; _i2 < elem.childNodes.length; _i2++) {
+            el.appendChild(Strophe.createHtml(elem.childNodes[_i2]));
+          }
+        } catch (e) {
+          // invalid elements
+          el = Strophe.xmlTextNode('');
+        }
+      } else {
+        el = Strophe.xmlGenerator().createDocumentFragment();
+
+        for (var _i3 = 0; _i3 < elem.childNodes.length; _i3++) {
+          el.appendChild(Strophe.createHtml(elem.childNodes[_i3]));
+        }
+      }
+    } else if (elem.nodeType === Strophe.ElementType.FRAGMENT) {
+      el = Strophe.xmlGenerator().createDocumentFragment();
+
+      for (var _i4 = 0; _i4 < elem.childNodes.length; _i4++) {
+        el.appendChild(Strophe.createHtml(elem.childNodes[_i4]));
+      }
+    } else if (elem.nodeType === Strophe.ElementType.TEXT) {
+      el = Strophe.xmlTextNode(elem.nodeValue);
+    }
+
+    return el;
+  },
+
+  /** Function: escapeNode
+   *  Escape the node part (also called local part) of a JID.
+   *
+   *  Parameters:
+   *    (String) node - A node (or local part).
+   *
+   *  Returns:
+   *    An escaped node (or local part).
+   */
+  escapeNode: function escapeNode(node) {
+    if (typeof node !== "string") {
+      return node;
+    }
+
+    return node.replace(/^\s+|\s+$/g, '').replace(/\\/g, "\\5c").replace(/ /g, "\\20").replace(/\"/g, "\\22").replace(/\&/g, "\\26").replace(/\'/g, "\\27").replace(/\//g, "\\2f").replace(/:/g, "\\3a").replace(/</g, "\\3c").replace(/>/g, "\\3e").replace(/@/g, "\\40");
+  },
+
+  /** Function: unescapeNode
+   *  Unescape a node part (also called local part) of a JID.
+   *
+   *  Parameters:
+   *    (String) node - A node (or local part).
+   *
+   *  Returns:
+   *    An unescaped node (or local part).
+   */
+  unescapeNode: function unescapeNode(node) {
+    if (typeof node !== "string") {
+      return node;
+    }
+
+    return node.replace(/\\20/g, " ").replace(/\\22/g, '"').replace(/\\26/g, "&").replace(/\\27/g, "'").replace(/\\2f/g, "/").replace(/\\3a/g, ":").replace(/\\3c/g, "<").replace(/\\3e/g, ">").replace(/\\40/g, "@").replace(/\\5c/g, "\\");
+  },
+
+  /** Function: getNodeFromJid
+   *  Get the node portion of a JID String.
+   *
+   *  Parameters:
+   *    (String) jid - A JID.
+   *
+   *  Returns:
+   *    A String containing the node.
+   */
+  getNodeFromJid: function getNodeFromJid(jid) {
+    if (jid.indexOf("@") < 0) {
+      return null;
+    }
+
+    return jid.split("@")[0];
+  },
+
+  /** Function: getDomainFromJid
+   *  Get the domain portion of a JID String.
+   *
+   *  Parameters:
+   *    (String) jid - A JID.
+   *
+   *  Returns:
+   *    A String containing the domain.
+   */
+  getDomainFromJid: function getDomainFromJid(jid) {
+    var bare = Strophe.getBareJidFromJid(jid);
+
+    if (bare.indexOf("@") < 0) {
+      return bare;
+    } else {
+      var parts = bare.split("@");
+      parts.splice(0, 1);
+      return parts.join('@');
+    }
+  },
+
+  /** Function: getResourceFromJid
+   *  Get the resource portion of a JID String.
+   *
+   *  Parameters:
+   *    (String) jid - A JID.
+   *
+   *  Returns:
+   *    A String containing the resource.
+   */
+  getResourceFromJid: function getResourceFromJid(jid) {
+    var s = jid.split("/");
+
+    if (s.length < 2) {
+      return null;
+    }
+
+    s.splice(0, 1);
+    return s.join('/');
+  },
+
+  /** Function: getBareJidFromJid
+   *  Get the bare JID from a JID String.
+   *
+   *  Parameters:
+   *    (String) jid - A JID.
+   *
+   *  Returns:
+   *    A String containing the bare JID.
+   */
+  getBareJidFromJid: function getBareJidFromJid(jid) {
+    return jid ? jid.split("/")[0] : null;
+  },
+
+  /** PrivateFunction: _handleError
+   *  _Private_ function that properly logs an error to the console
+   */
+  _handleError: function _handleError(e) {
+    if (typeof e.stack !== "undefined") {
+      Strophe.fatal(e.stack);
+    }
+
+    if (e.sourceURL) {
+      Strophe.fatal("error: " + this.handler + " " + e.sourceURL + ":" + e.line + " - " + e.name + ": " + e.message);
+    } else if (e.fileName) {
+      Strophe.fatal("error: " + this.handler + " " + e.fileName + ":" + e.lineNumber + " - " + e.name + ": " + e.message);
+    } else {
+      Strophe.fatal("error: " + e.message);
+    }
+  },
+
+  /** Function: log
+   *  User overrideable logging function.
+   *
+   *  This function is called whenever the Strophe library calls any
+   *  of the logging functions.  The default implementation of this
+   *  function logs only fatal errors.  If client code wishes to handle the logging
+   *  messages, it should override this with
+   *  > Strophe.log = function (level, msg) {
+   *  >   (user code here)
+   *  > };
+   *
+   *  Please note that data sent and received over the wire is logged
+   *  via Strophe.Connection.rawInput() and Strophe.Connection.rawOutput().
+   *
+   *  The different levels and their meanings are
+   *
+   *    DEBUG - Messages useful for debugging purposes.
+   *    INFO - Informational messages.  This is mostly information like
+   *      'disconnect was called' or 'SASL auth succeeded'.
+   *    WARN - Warnings about potential problems.  This is mostly used
+   *      to report transient connection errors like request timeouts.
+   *    ERROR - Some error occurred.
+   *    FATAL - A non-recoverable fatal error occurred.
+   *
+   *  Parameters:
+   *    (Integer) level - The log level of the log message.  This will
+   *      be one of the values in Strophe.LogLevel.
+   *    (String) msg - The log message.
+   */
+  log: function log(level, msg) {
+    if (level === this.LogLevel.FATAL && _typeof(window.console) === 'object' && typeof window.console.error === 'function') {
+      window.console.error(msg);
+    }
+  },
+
+  /** Function: debug
+   *  Log a message at the Strophe.LogLevel.DEBUG level.
+   *
+   *  Parameters:
+   *    (String) msg - The log message.
+   */
+  debug: function debug(msg) {
+    this.log(this.LogLevel.DEBUG, msg);
+  },
+
+  /** Function: info
+   *  Log a message at the Strophe.LogLevel.INFO level.
+   *
+   *  Parameters:
+   *    (String) msg - The log message.
+   */
+  info: function info(msg) {
+    this.log(this.LogLevel.INFO, msg);
+  },
+
+  /** Function: warn
+   *  Log a message at the Strophe.LogLevel.WARN level.
+   *
+   *  Parameters:
+   *    (String) msg - The log message.
+   */
+  warn: function warn(msg) {
+    this.log(this.LogLevel.WARN, msg);
+  },
+
+  /** Function: error
+   *  Log a message at the Strophe.LogLevel.ERROR level.
+   *
+   *  Parameters:
+   *    (String) msg - The log message.
+   */
+  error: function error(msg) {
+    this.log(this.LogLevel.ERROR, msg);
+  },
+
+  /** Function: fatal
+   *  Log a message at the Strophe.LogLevel.FATAL level.
+   *
+   *  Parameters:
+   *    (String) msg - The log message.
+   */
+  fatal: function fatal(msg) {
+    this.log(this.LogLevel.FATAL, msg);
+  },
+
+  /** Function: serialize
+   *  Render a DOM element and all descendants to a String.
+   *
+   *  Parameters:
+   *    (XMLElement) elem - A DOM element.
+   *
+   *  Returns:
+   *    The serialized element tree as a String.
+   */
+  serialize: function serialize(elem) {
+    if (!elem) {
+      return null;
+    }
+
+    if (typeof elem.tree === "function") {
+      elem = elem.tree();
+    }
+
+    var names = _toConsumableArray(Array(elem.attributes.length).keys()).map(function (i) {
+      return elem.attributes[i].nodeName;
+    });
+
+    names.sort();
+    var result = names.reduce(function (a, n) {
+      return "".concat(a, " ").concat(n, "=\"").concat(Strophe.xmlescape(elem.attributes.getNamedItem(n).value), "\"");
+    }, "<".concat(elem.nodeName));
+
+    if (elem.childNodes.length > 0) {
+      result += ">";
+
+      for (var i = 0; i < elem.childNodes.length; i++) {
+        var child = elem.childNodes[i];
+
+        switch (child.nodeType) {
+          case Strophe.ElementType.NORMAL:
+            // normal element, so recurse
+            result += Strophe.serialize(child);
+            break;
+
+          case Strophe.ElementType.TEXT:
+            // text element to escape values
+            result += Strophe.xmlescape(child.nodeValue);
+            break;
+
+          case Strophe.ElementType.CDATA:
+            // cdata section so don't escape values
+            result += "<![CDATA[" + child.nodeValue + "]]>";
+        }
+      }
+
+      result += "</" + elem.nodeName + ">";
+    } else {
+      result += "/>";
+    }
+
+    return result;
+  },
+
+  /** PrivateVariable: _requestId
+   *  _Private_ variable that keeps track of the request ids for
+   *  connections.
+   */
+  _requestId: 0,
+
+  /** PrivateVariable: Strophe.connectionPlugins
+   *  _Private_ variable Used to store plugin names that need
+   *  initialization on Strophe.Connection construction.
+   */
+  _connectionPlugins: {},
+
+  /** Function: addConnectionPlugin
+   *  Extends the Strophe.Connection object with the given plugin.
+   *
+   *  Parameters:
+   *    (String) name - The name of the extension.
+   *    (Object) ptype - The plugin's prototype.
+   */
+  addConnectionPlugin: function addConnectionPlugin(name, ptype) {
+    Strophe._connectionPlugins[name] = ptype;
+  }
+};
+/** Class: Strophe.Builder
+ *  XML DOM builder.
+ *
+ *  This object provides an interface similar to JQuery but for building
+ *  DOM elements easily and rapidly.  All the functions except for toString()
+ *  and tree() return the object, so calls can be chained.  Here's an
+ *  example using the $iq() builder helper.
+ *  > $iq({to: 'you', from: 'me', type: 'get', id: '1'})
+ *  >     .c('query', {xmlns: 'strophe:example'})
+ *  >     .c('example')
+ *  >     .toString()
+ *
+ *  The above generates this XML fragment
+ *  > <iq to='you' from='me' type='get' id='1'>
+ *  >   <query xmlns='strophe:example'>
+ *  >     <example/>
+ *  >   </query>
+ *  > </iq>
+ *  The corresponding DOM manipulations to get a similar fragment would be
+ *  a lot more tedious and probably involve several helper variables.
+ *
+ *  Since adding children makes new operations operate on the child, up()
+ *  is provided to traverse up the tree.  To add two children, do
+ *  > builder.c('child1', ...).up().c('child2', ...)
+ *  The next operation on the Builder will be relative to the second child.
+ */
+
+/** Constructor: Strophe.Builder
+ *  Create a Strophe.Builder object.
+ *
+ *  The attributes should be passed in object notation.  For example
+ *  > let b = new Builder('message', {to: 'you', from: 'me'});
+ *  or
+ *  > let b = new Builder('messsage', {'xml:lang': 'en'});
+ *
+ *  Parameters:
+ *    (String) name - The name of the root element.
+ *    (Object) attrs - The attributes for the root element in object notation.
+ *
+ *  Returns:
+ *    A new Strophe.Builder.
+ */
+
+Strophe.Builder = function (name, attrs) {
+  // Set correct namespace for jabber:client elements
+  if (name === "presence" || name === "message" || name === "iq") {
+    if (attrs && !attrs.xmlns) {
+      attrs.xmlns = Strophe.NS.CLIENT;
+    } else if (!attrs) {
+      attrs = {
+        xmlns: Strophe.NS.CLIENT
+      };
+    }
+  } // Holds the tree being built.
+
+
+  this.nodeTree = Strophe.xmlElement(name, attrs); // Points to the current operation node.
+
+  this.node = this.nodeTree;
+};
+
+Strophe.Builder.prototype = {
+  /** Function: tree
+   *  Return the DOM tree.
+   *
+   *  This function returns the current DOM tree as an element object.  This
+   *  is suitable for passing to functions like Strophe.Connection.send().
+   *
+   *  Returns:
+   *    The DOM tree as a element object.
+   */
+  tree: function tree() {
+    return this.nodeTree;
+  },
+
+  /** Function: toString
+   *  Serialize the DOM tree to a String.
+   *
+   *  This function returns a string serialization of the current DOM
+   *  tree.  It is often used internally to pass data to a
+   *  Strophe.Request object.
+   *
+   *  Returns:
+   *    The serialized DOM tree in a String.
+   */
+  toString: function toString() {
+    return Strophe.serialize(this.nodeTree);
+  },
+
+  /** Function: up
+   *  Make the current parent element the new current element.
+   *
+   *  This function is often used after c() to traverse back up the tree.
+   *  For example, to add two children to the same element
+   *  > builder.c('child1', {}).up().c('child2', {});
+   *
+   *  Returns:
+   *    The Stophe.Builder object.
+   */
+  up: function up() {
+    this.node = this.node.parentNode;
+    return this;
+  },
+
+  /** Function: root
+   *  Make the root element the new current element.
+   *
+   *  When at a deeply nested element in the tree, this function can be used
+   *  to jump back to the root of the tree, instead of having to repeatedly
+   *  call up().
+   *
+   *  Returns:
+   *    The Stophe.Builder object.
+   */
+  root: function root() {
+    this.node = this.nodeTree;
+    return this;
+  },
+
+  /** Function: attrs
+   *  Add or modify attributes of the current element.
+   *
+   *  The attributes should be passed in object notation.  This function
+   *  does not move the current element pointer.
+   *
+   *  Parameters:
+   *    (Object) moreattrs - The attributes to add/modify in object notation.
+   *
+   *  Returns:
+   *    The Strophe.Builder object.
+   */
+  attrs: function attrs(moreattrs) {
+    for (var k in moreattrs) {
+      if (Object.prototype.hasOwnProperty.call(moreattrs, k)) {
+        if (moreattrs[k] === undefined) {
+          this.node.removeAttribute(k);
+        } else {
+          this.node.setAttribute(k, moreattrs[k]);
+        }
+      }
+    }
+
+    return this;
+  },
+
+  /** Function: c
+   *  Add a child to the current element and make it the new current
+   *  element.
+   *
+   *  This function moves the current element pointer to the child,
+   *  unless text is provided.  If you need to add another child, it
+   *  is necessary to use up() to go back to the parent in the tree.
+   *
+   *  Parameters:
+   *    (String) name - The name of the child.
+   *    (Object) attrs - The attributes of the child in object notation.
+   *    (String) text - The text to add to the child.
+   *
+   *  Returns:
+   *    The Strophe.Builder object.
+   */
+  c: function c(name, attrs, text) {
+    var child = Strophe.xmlElement(name, attrs, text);
+    this.node.appendChild(child);
+
+    if (typeof text !== "string" && typeof text !== "number") {
+      this.node = child;
+    }
+
+    return this;
+  },
+
+  /** Function: cnode
+   *  Add a child to the current element and make it the new current
+   *  element.
+   *
+   *  This function is the same as c() except that instead of using a
+   *  name and an attributes object to create the child it uses an
+   *  existing DOM element object.
+   *
+   *  Parameters:
+   *    (XMLElement) elem - A DOM element.
+   *
+   *  Returns:
+   *    The Strophe.Builder object.
+   */
+  cnode: function cnode(elem) {
+    var impNode;
+    var xmlGen = Strophe.xmlGenerator();
+
+    try {
+      impNode = xmlGen.importNode !== undefined;
+    } catch (e) {
+      impNode = false;
+    }
+
+    var newElem = impNode ? xmlGen.importNode(elem, true) : Strophe.copyElement(elem);
+    this.node.appendChild(newElem);
+    this.node = newElem;
+    return this;
+  },
+
+  /** Function: t
+   *  Add a child text element.
+   *
+   *  This *does not* make the child the new current element since there
+   *  are no children of text elements.
+   *
+   *  Parameters:
+   *    (String) text - The text data to append to the current element.
+   *
+   *  Returns:
+   *    The Strophe.Builder object.
+   */
+  t: function t(text) {
+    var child = Strophe.xmlTextNode(text);
+    this.node.appendChild(child);
+    return this;
+  },
+
+  /** Function: h
+   *  Replace current element contents with the HTML passed in.
+   *
+   *  This *does not* make the child the new current element
+   *
+   *  Parameters:
+   *    (String) html - The html to insert as contents of current element.
+   *
+   *  Returns:
+   *    The Strophe.Builder object.
+   */
+  h: function h(html) {
+    var fragment = document.createElement('body'); // force the browser to try and fix any invalid HTML tags
+
+    fragment.innerHTML = html; // copy cleaned html into an xml dom
+
+    var xhtml = Strophe.createHtml(fragment);
+
+    while (xhtml.childNodes.length > 0) {
+      this.node.appendChild(xhtml.childNodes[0]);
+    }
+
+    return this;
+  }
+};
+/** PrivateClass: Strophe.Handler
+ *  _Private_ helper class for managing stanza handlers.
+ *
+ *  A Strophe.Handler encapsulates a user provided callback function to be
+ *  executed when matching stanzas are received by the connection.
+ *  Handlers can be either one-off or persistant depending on their
+ *  return value. Returning true will cause a Handler to remain active, and
+ *  returning false will remove the Handler.
+ *
+ *  Users will not use Strophe.Handler objects directly, but instead they
+ *  will use Strophe.Connection.addHandler() and
+ *  Strophe.Connection.deleteHandler().
+ */
+
+/** PrivateConstructor: Strophe.Handler
+ *  Create and initialize a new Strophe.Handler.
+ *
+ *  Parameters:
+ *    (Function) handler - A function to be executed when the handler is run.
+ *    (String) ns - The namespace to match.
+ *    (String) name - The element name to match.
+ *    (String) type - The element type to match.
+ *    (String) id - The element id attribute to match.
+ *    (String) from - The element from attribute to match.
+ *    (Object) options - Handler options
+ *
+ *  Returns:
+ *    A new Strophe.Handler object.
+ */
+
+Strophe.Handler = function (handler, ns, name, type, id, from, options) {
+  this.handler = handler;
+  this.ns = ns;
+  this.name = name;
+  this.type = type;
+  this.id = id;
+  this.options = options || {
+    'matchBareFromJid': false,
+    'ignoreNamespaceFragment': false
+  }; // BBB: Maintain backward compatibility with old `matchBare` option
+
+  if (this.options.matchBare) {
+    Strophe.warn('The "matchBare" option is deprecated, use "matchBareFromJid" instead.');
+    this.options.matchBareFromJid = this.options.matchBare;
+    delete this.options.matchBare;
+  }
+
+  if (this.options.matchBareFromJid) {
+    this.from = from ? Strophe.getBareJidFromJid(from) : null;
+  } else {
+    this.from = from;
+  } // whether the handler is a user handler or a system handler
+
+
+  this.user = true;
+};
+
+Strophe.Handler.prototype = {
+  /** PrivateFunction: getNamespace
+   *  Returns the XML namespace attribute on an element.
+   *  If `ignoreNamespaceFragment` was passed in for this handler, then the
+   *  URL fragment will be stripped.
+   *
+   *  Parameters:
+   *    (XMLElement) elem - The XML element with the namespace.
+   *
+   *  Returns:
+   *    The namespace, with optionally the fragment stripped.
+   */
+  getNamespace: function getNamespace(elem) {
+    var elNamespace = elem.getAttribute("xmlns");
+
+    if (elNamespace && this.options.ignoreNamespaceFragment) {
+      elNamespace = elNamespace.split('#')[0];
+    }
+
+    return elNamespace;
+  },
+
+  /** PrivateFunction: namespaceMatch
+   *  Tests if a stanza matches the namespace set for this Strophe.Handler.
+   *
+   *  Parameters:
+   *    (XMLElement) elem - The XML element to test.
+   *
+   *  Returns:
+   *    true if the stanza matches and false otherwise.
+   */
+  namespaceMatch: function namespaceMatch(elem) {
+    var _this = this;
+
+    var nsMatch = false;
+
+    if (!this.ns) {
+      return true;
+    } else {
+      Strophe.forEachChild(elem, null, function (elem) {
+        if (_this.getNamespace(elem) === _this.ns) {
+          nsMatch = true;
+        }
+      });
+      return nsMatch || this.getNamespace(elem) === this.ns;
+    }
+  },
+
+  /** PrivateFunction: isMatch
+   *  Tests if a stanza matches the Strophe.Handler.
+   *
+   *  Parameters:
+   *    (XMLElement) elem - The XML element to test.
+   *
+   *  Returns:
+   *    true if the stanza matches and false otherwise.
+   */
+  isMatch: function isMatch(elem) {
+    var from = elem.getAttribute('from');
+
+    if (this.options.matchBareFromJid) {
+      from = Strophe.getBareJidFromJid(from);
+    }
+
+    var elem_type = elem.getAttribute("type");
+
+    if (this.namespaceMatch(elem) && (!this.name || Strophe.isTagEqual(elem, this.name)) && (!this.type || (Array.isArray(this.type) ? this.type.indexOf(elem_type) !== -1 : elem_type === this.type)) && (!this.id || elem.getAttribute("id") === this.id) && (!this.from || from === this.from)) {
+      return true;
+    }
+
+    return false;
+  },
+
+  /** PrivateFunction: run
+   *  Run the callback on a matching stanza.
+   *
+   *  Parameters:
+   *    (XMLElement) elem - The DOM element that triggered the
+   *      Strophe.Handler.
+   *
+   *  Returns:
+   *    A boolean indicating if the handler should remain active.
+   */
+  run: function run(elem) {
+    var result = null;
+
+    try {
+      result = this.handler(elem);
+    } catch (e) {
+      Strophe._handleError(e);
+
+      throw e;
+    }
+
+    return result;
+  },
+
+  /** PrivateFunction: toString
+   *  Get a String representation of the Strophe.Handler object.
+   *
+   *  Returns:
+   *    A String.
+   */
+  toString: function toString() {
+    return "{Handler: " + this.handler + "(" + this.name + "," + this.id + "," + this.ns + ")}";
+  }
+};
+/** PrivateClass: Strophe.TimedHandler
+ *  _Private_ helper class for managing timed handlers.
+ *
+ *  A Strophe.TimedHandler encapsulates a user provided callback that
+ *  should be called after a certain period of time or at regular
+ *  intervals.  The return value of the callback determines whether the
+ *  Strophe.TimedHandler will continue to fire.
+ *
+ *  Users will not use Strophe.TimedHandler objects directly, but instead
+ *  they will use Strophe.Connection.addTimedHandler() and
+ *  Strophe.Connection.deleteTimedHandler().
+ */
+
+/** PrivateConstructor: Strophe.TimedHandler
+ *  Create and initialize a new Strophe.TimedHandler object.
+ *
+ *  Parameters:
+ *    (Integer) period - The number of milliseconds to wait before the
+ *      handler is called.
+ *    (Function) handler - The callback to run when the handler fires.  This
+ *      function should take no arguments.
+ *
+ *  Returns:
+ *    A new Strophe.TimedHandler object.
+ */
+
+Strophe.TimedHandler = function (period, handler) {
+  this.period = period;
+  this.handler = handler;
+  this.lastCalled = new Date().getTime();
+  this.user = true;
+};
+
+Strophe.TimedHandler.prototype = {
+  /** PrivateFunction: run
+   *  Run the callback for the Strophe.TimedHandler.
+   *
+   *  Returns:
+   *    true if the Strophe.TimedHandler should be called again, and false
+   *      otherwise.
+   */
+  run: function run() {
+    this.lastCalled = new Date().getTime();
+    return this.handler();
+  },
+
+  /** PrivateFunction: reset
+   *  Reset the last called time for the Strophe.TimedHandler.
+   */
+  reset: function reset() {
+    this.lastCalled = new Date().getTime();
+  },
+
+  /** PrivateFunction: toString
+   *  Get a string representation of the Strophe.TimedHandler object.
+   *
+   *  Returns:
+   *    The string representation.
+   */
+  toString: function toString() {
+    return "{TimedHandler: " + this.handler + "(" + this.period + ")}";
+  }
+};
+/** Class: Strophe.Connection
+ *  XMPP Connection manager.
+ *
+ *  This class is the main part of Strophe.  It manages a BOSH or websocket
+ *  connection to an XMPP server and dispatches events to the user callbacks
+ *  as data arrives. It supports SASL PLAIN, SASL DIGEST-MD5, SASL SCRAM-SHA1
+ *  and legacy authentication.
+ *
+ *  After creating a Strophe.Connection object, the user will typically
+ *  call connect() with a user supplied callback to handle connection level
+ *  events like authentication failure, disconnection, or connection
+ *  complete.
+ *
+ *  The user will also have several event handlers defined by using
+ *  addHandler() and addTimedHandler().  These will allow the user code to
+ *  respond to interesting stanzas or do something periodically with the
+ *  connection. These handlers will be active once authentication is
+ *  finished.
+ *
+ *  To send data to the connection, use send().
+ */
+
+/** Constructor: Strophe.Connection
+ *  Create and initialize a Strophe.Connection object.
+ *
+ *  The transport-protocol for this connection will be chosen automatically
+ *  based on the given service parameter. URLs starting with "ws://" or
+ *  "wss://" will use WebSockets, URLs starting with "http://", "https://"
+ *  or without a protocol will use BOSH.
+ *
+ *  To make Strophe connect to the current host you can leave out the protocol
+ *  and host part and just pass the path, e.g.
+ *
+ *  > let conn = new Strophe.Connection("/http-bind/");
+ *
+ *  Options common to both Websocket and BOSH:
+ *  ------------------------------------------
+ *
+ *  cookies:
+ *
+ *  The *cookies* option allows you to pass in cookies to be added to the
+ *  document. These cookies will then be included in the BOSH XMLHttpRequest
+ *  or in the websocket connection.
+ *
+ *  The passed in value must be a map of cookie names and string values.
+ *
+ *  > { "myCookie": {
+ *  >     "value": "1234",
+ *  >     "domain": ".example.org",
+ *  >     "path": "/",
+ *  >     "expires": expirationDate
+ *  >     }
+ *  > }
+ *
+ *  Note that cookies can't be set in this way for other domains (i.e. cross-domain).
+ *  Those cookies need to be set under those domains, for example they can be
+ *  set server-side by making a XHR call to that domain to ask it to set any
+ *  necessary cookies.
+ *
+ *  mechanisms:
+ *
+ *  The *mechanisms* option allows you to specify the SASL mechanisms that this
+ *  instance of Strophe.Connection (and therefore your XMPP client) will
+ *  support.
+ *
+ *  The value must be an array of objects with Strophe.SASLMechanism
+ *  prototypes.
+ *
+ *  If nothing is specified, then the following mechanisms (and their
+ *  priorities) are registered:
+ *
+ *      SCRAM-SHA1 - 70
+ *      DIGEST-MD5 - 60
+ *      PLAIN - 50
+ *      OAUTH-BEARER - 40
+ *      OAUTH-2 - 30
+ *      ANONYMOUS - 20
+ *      EXTERNAL - 10
+ *
+ *  WebSocket options:
+ *  ------------------
+ *
+ *  If you want to connect to the current host with a WebSocket connection you
+ *  can tell Strophe to use WebSockets through a "protocol" attribute in the
+ *  optional options parameter. Valid values are "ws" for WebSocket and "wss"
+ *  for Secure WebSocket.
+ *  So to connect to "wss://CURRENT_HOSTNAME/xmpp-websocket" you would call
+ *
+ *  > let conn = new Strophe.Connection("/xmpp-websocket/", {protocol: "wss"});
+ *
+ *  Note that relative URLs _NOT_ starting with a "/" will also include the path
+ *  of the current site.
+ *
+ *  Also because downgrading security is not permitted by browsers, when using
+ *  relative URLs both BOSH and WebSocket connections will use their secure
+ *  variants if the current connection to the site is also secure (https).
+ *
+ *  BOSH options:
+ *  -------------
+ *
+ *  By adding "sync" to the options, you can control if requests will
+ *  be made synchronously or not. The default behaviour is asynchronous.
+ *  If you want to make requests synchronous, make "sync" evaluate to true.
+ *  > let conn = new Strophe.Connection("/http-bind/", {sync: true});
+ *
+ *  You can also toggle this on an already established connection.
+ *  > conn.options.sync = true;
+ *
+ *  The *customHeaders* option can be used to provide custom HTTP headers to be
+ *  included in the XMLHttpRequests made.
+ *
+ *  The *keepalive* option can be used to instruct Strophe to maintain the
+ *  current BOSH session across interruptions such as webpage reloads.
+ *
+ *  It will do this by caching the sessions tokens in sessionStorage, and when
+ *  "restore" is called it will check whether there are cached tokens with
+ *  which it can resume an existing session.
+ *
+ *  The *withCredentials* option should receive a Boolean value and is used to
+ *  indicate wether cookies should be included in ajax requests (by default
+ *  they're not).
+ *  Set this value to true if you are connecting to a BOSH service
+ *  and for some reason need to send cookies to it.
+ *  In order for this to work cross-domain, the server must also enable
+ *  credentials by setting the Access-Control-Allow-Credentials response header
+ *  to "true". For most usecases however this setting should be false (which
+ *  is the default).
+ *  Additionally, when using Access-Control-Allow-Credentials, the
+ *  Access-Control-Allow-Origin header can't be set to the wildcard "*", but
+ *  instead must be restricted to actual domains.
+ *
+ *  The *contentType* option can be set to change the default Content-Type
+ *  of "text/xml; charset=utf-8", which can be useful to reduce the amount of
+ *  CORS preflight requests that are sent to the server.
+ *
+ *  Parameters:
+ *    (String) service - The BOSH or WebSocket service URL.
+ *    (Object) options - A hash of configuration options
+ *
+ *  Returns:
+ *    A new Strophe.Connection object.
+ */
+
+Strophe.Connection = function (service, options) {
+  var _this2 = this;
+
+  // The service URL
+  this.service = service; // Configuration options
+
+  this.options = options || {};
+  var proto = this.options.protocol || ""; // Select protocal based on service or options
+
+  if (service.indexOf("ws:") === 0 || service.indexOf("wss:") === 0 || proto.indexOf("ws") === 0) {
+    this._proto = new Strophe.Websocket(this);
+  } else {
+    this._proto = new Strophe.Bosh(this);
+  }
+  /* The connected JID. */
+
+
+  this.jid = "";
+  /* the JIDs domain */
+
+  this.domain = null;
+  /* stream:features */
+
+  this.features = null; // SASL
+
+  this._sasl_data = {};
+  this.do_session = false;
+  this.do_bind = false; // handler lists
+
+  this.timedHandlers = [];
+  this.handlers = [];
+  this.removeTimeds = [];
+  this.removeHandlers = [];
+  this.addTimeds = [];
+  this.addHandlers = [];
+  this.protocolErrorHandlers = {
+    'HTTP': {},
+    'websocket': {}
+  };
+  this._idleTimeout = null;
+  this._disconnectTimeout = null;
+  this.authenticated = false;
+  this.connected = false;
+  this.disconnecting = false;
+  this.do_authentication = true;
+  this.paused = false;
+  this.restored = false;
+  this._data = [];
+  this._uniqueId = 0;
+  this._sasl_success_handler = null;
+  this._sasl_failure_handler = null;
+  this._sasl_challenge_handler = null; // Max retries before disconnecting
+
+  this.maxRetries = 5; // Call onIdle callback every 1/10th of a second
+
+  this._idleTimeout = setTimeout(function () {
+    return _this2._onIdle();
+  }, 100);
+  utils__WEBPACK_IMPORTED_MODULE_2__["default"].addCookies(this.options.cookies);
+  this.registerSASLMechanisms(this.options.mechanisms); // initialize plugins
+
+  for (var k in Strophe._connectionPlugins) {
+    if (Object.prototype.hasOwnProperty.call(Strophe._connectionPlugins, k)) {
+      var F = function F() {};
+
+      F.prototype = Strophe._connectionPlugins[k];
+      this[k] = new F();
+      this[k].init(this);
+    }
+  }
+};
+
+Strophe.Connection.prototype = {
+  /** Function: reset
+   *  Reset the connection.
+   *
+   *  This function should be called after a connection is disconnected
+   *  before that connection is reused.
+   */
+  reset: function reset() {
+    this._proto._reset(); // SASL
+
+
+    this.do_session = false;
+    this.do_bind = false; // handler lists
+
+    this.timedHandlers = [];
+    this.handlers = [];
+    this.removeTimeds = [];
+    this.removeHandlers = [];
+    this.addTimeds = [];
+    this.addHandlers = [];
+    this.authenticated = false;
+    this.connected = false;
+    this.disconnecting = false;
+    this.restored = false;
+    this._data = [];
+    this._requests = [];
+    this._uniqueId = 0;
+  },
+
+  /** Function: pause
+   *  Pause the request manager.
+   *
+   *  This will prevent Strophe from sending any more requests to the
+   *  server.  This is very useful for temporarily pausing
+   *  BOSH-Connections while a lot of send() calls are happening quickly.
+   *  This causes Strophe to send the data in a single request, saving
+   *  many request trips.
+   */
+  pause: function pause() {
+    this.paused = true;
+  },
+
+  /** Function: resume
+   *  Resume the request manager.
+   *
+   *  This resumes after pause() has been called.
+   */
+  resume: function resume() {
+    this.paused = false;
+  },
+
+  /** Function: getUniqueId
+   *  Generate a unique ID for use in <iq/> elements.
+   *
+   *  All <iq/> stanzas are required to have unique id attributes.  This
+   *  function makes creating these easy.  Each connection instance has
+   *  a counter which starts from zero, and the value of this counter
+   *  plus a colon followed by the suffix becomes the unique id. If no
+   *  suffix is supplied, the counter is used as the unique id.
+   *
+   *  Suffixes are used to make debugging easier when reading the stream
+   *  data, and their use is recommended.  The counter resets to 0 for
+   *  every new connection for the same reason.  For connections to the
+   *  same server that authenticate the same way, all the ids should be
+   *  the same, which makes it easy to see changes.  This is useful for
+   *  automated testing as well.
+   *
+   *  Parameters:
+   *    (String) suffix - A optional suffix to append to the id.
+   *
+   *  Returns:
+   *    A unique string to be used for the id attribute.
+   */
+  getUniqueId: function getUniqueId(suffix) {
+    var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
+      var r = Math.random() * 16 | 0,
+          v = c === 'x' ? r : r & 0x3 | 0x8;
+      return v.toString(16);
+    });
+
+    if (typeof suffix === "string" || typeof suffix === "number") {
+      return uuid + ":" + suffix;
+    } else {
+      return uuid + "";
+    }
+  },
+
+  /** Function: addProtocolErrorHandler
+   *  Register a handler function for when a protocol (websocker or HTTP)
+   *  error occurs.
+   *
+   *  NOTE: Currently only HTTP errors for BOSH requests are handled.
+   *  Patches that handle websocket errors would be very welcome.
+   *
+   *  Parameters:
+   *    (String) protocol - 'HTTP' or 'websocket'
+   *    (Integer) status_code - Error status code (e.g 500, 400 or 404)
+   *    (Function) callback - Function that will fire on Http error
+   *
+   *  Example:
+   *  function onError(err_code){
+   *    //do stuff
+   *  }
+   *
+   *  let conn = Strophe.connect('http://example.com/http-bind');
+   *  conn.addProtocolErrorHandler('HTTP', 500, onError);
+   *  // Triggers HTTP 500 error and onError handler will be called
+   *  conn.connect('user_jid@incorrect_jabber_host', 'secret', onConnect);
+   */
+  addProtocolErrorHandler: function addProtocolErrorHandler(protocol, status_code, callback) {
+    this.protocolErrorHandlers[protocol][status_code] = callback;
+  },
+
+  /** Function: connect
+   *  Starts the connection process.
+   *
+   *  As the connection process proceeds, the user supplied callback will
+   *  be triggered multiple times with status updates.  The callback
+   *  should take two arguments - the status code and the error condition.
+   *
+   *  The status code will be one of the values in the Strophe.Status
+   *  constants.  The error condition will be one of the conditions
+   *  defined in RFC 3920 or the condition 'strophe-parsererror'.
+   *
+   *  The Parameters _wait_, _hold_ and _route_ are optional and only relevant
+   *  for BOSH connections. Please see XEP 124 for a more detailed explanation
+   *  of the optional parameters.
+   *
+   *  Parameters:
+   *    (String) jid - The user's JID.  This may be a bare JID,
+   *      or a full JID.  If a node is not supplied, SASL OAUTHBEARER or
+   *      SASL ANONYMOUS authentication will be attempted (OAUTHBEARER will
+   *      process the provided password value as an access token).
+   *    (String) pass - The user's password.
+   *    (Function) callback - The connect callback function.
+   *    (Integer) wait - The optional HTTPBIND wait value.  This is the
+   *      time the server will wait before returning an empty result for
+   *      a request.  The default setting of 60 seconds is recommended.
+   *    (Integer) hold - The optional HTTPBIND hold value.  This is the
+   *      number of connections the server will hold at one time.  This
+   *      should almost always be set to 1 (the default).
+   *    (String) route - The optional route value.
+   *    (String) authcid - The optional alternative authentication identity
+   *      (username) if intending to impersonate another user.
+   *      When using the SASL-EXTERNAL authentication mechanism, for example
+   *      with client certificates, then the authcid value is used to
+   *      determine whether an authorization JID (authzid) should be sent to
+   *      the server. The authzid should not be sent to the server if the
+   *      authzid and authcid are the same. So to prevent it from being sent
+   *      (for example when the JID is already contained in the client
+   *      certificate), set authcid to that same JID. See XEP-178 for more
+   *      details.
+   */
+  connect: function connect(jid, pass, callback, wait, hold, route, authcid) {
+    this.jid = jid;
+    /** Variable: authzid
+     *  Authorization identity.
+     */
+
+    this.authzid = Strophe.getBareJidFromJid(this.jid);
+    /** Variable: authcid
+     *  Authentication identity (User name).
+     */
+
+    this.authcid = authcid || Strophe.getNodeFromJid(this.jid);
+    /** Variable: pass
+     *  Authentication identity (User password).
+     */
+
+    this.pass = pass;
+    /** Variable: servtype
+     *  Digest MD5 compatibility.
+     */
+
+    this.servtype = "xmpp";
+    this.connect_callback = callback;
+    this.disconnecting = false;
+    this.connected = false;
+    this.authenticated = false;
+    this.restored = false; // parse jid for domain
+
+    this.domain = Strophe.getDomainFromJid(this.jid);
+
+    this._changeConnectStatus(Strophe.Status.CONNECTING, null);
+
+    this._proto._connect(wait, hold, route);
+  },
+
+  /** Function: attach
+   *  Attach to an already created and authenticated BOSH session.
+   *
+   *  This function is provided to allow Strophe to attach to BOSH
+   *  sessions which have been created externally, perhaps by a Web
+   *  application.  This is often used to support auto-login type features
+   *  without putting user credentials into the page.
+   *
+   *  Parameters:
+   *    (String) jid - The full JID that is bound by the session.
+   *    (String) sid - The SID of the BOSH session.
+   *    (String) rid - The current RID of the BOSH session.  This RID
+   *      will be used by the next request.
+   *    (Function) callback The connect callback function.
+   *    (Integer) wait - The optional HTTPBIND wait value.  This is the
+   *      time the server will wait before returning an empty result for
+   *      a request.  The default setting of 60 seconds is recommended.
+   *      Other settings will require tweaks to the Strophe.TIMEOUT value.
+   *    (Integer) hold - The optional HTTPBIND hold value.  This is the
+   *      number of connections the server will hold at one time.  This
+   *      should almost always be set to 1 (the default).
+   *    (Integer) wind - The optional HTTBIND window value.  This is the
+   *      allowed range of request ids that are valid.  The default is 5.
+   */
+  attach: function attach(jid, sid, rid, callback, wait, hold, wind) {
+    if (this._proto instanceof Strophe.Bosh) {
+      this._proto._attach(jid, sid, rid, callback, wait, hold, wind);
+    } else {
+      var error = new Error('The "attach" method can only be used with a BOSH connection.');
+      error.name = 'StropheSessionError';
+      throw error;
+    }
+  },
+
+  /** Function: restore
+   *  Attempt to restore a cached BOSH session.
+   *
+   *  This function is only useful in conjunction with providing the
+   *  "keepalive":true option when instantiating a new Strophe.Connection.
+   *
+   *  When "keepalive" is set to true, Strophe will cache the BOSH tokens
+   *  RID (Request ID) and SID (Session ID) and then when this function is
+   *  called, it will attempt to restore the session from those cached
+   *  tokens.
+   *
+   *  This function must therefore be called instead of connect or attach.
+   *
+   *  For an example on how to use it, please see examples/restore.js
+   *
+   *  Parameters:
+   *    (String) jid - The user's JID.  This may be a bare JID or a full JID.
+   *    (Function) callback - The connect callback function.
+   *    (Integer) wait - The optional HTTPBIND wait value.  This is the
+   *      time the server will wait before returning an empty result for
+   *      a request.  The default setting of 60 seconds is recommended.
+   *    (Integer) hold - The optional HTTPBIND hold value.  This is the
+   *      number of connections the server will hold at one time.  This
+   *      should almost always be set to 1 (the default).
+   *    (Integer) wind - The optional HTTBIND window value.  This is the
+   *      allowed range of request ids that are valid.  The default is 5.
+   */
+  restore: function restore(jid, callback, wait, hold, wind) {
+    if (this._sessionCachingSupported()) {
+      this._proto._restore(jid, callback, wait, hold, wind);
+    } else {
+      var error = new Error('The "restore" method can only be used with a BOSH connection.');
+      error.name = 'StropheSessionError';
+      throw error;
+    }
+  },
+
+  /** PrivateFunction: _sessionCachingSupported
+   * Checks whether sessionStorage and JSON are supported and whether we're
+   * using BOSH.
+   */
+  _sessionCachingSupported: function _sessionCachingSupported() {
+    if (this._proto instanceof Strophe.Bosh) {
+      if (!JSON) {
+        return false;
+      }
+
+      try {
+        sessionStorage.setItem('_strophe_', '_strophe_');
+        sessionStorage.removeItem('_strophe_');
+      } catch (e) {
+        return false;
+      }
+
+      return true;
+    }
+
+    return false;
+  },
+
+  /** Function: xmlInput
+   *  User overrideable function that receives XML data coming into the
+   *  connection.
+   *
+   *  The default function does nothing.  User code can override this with
+   *  > Strophe.Connection.xmlInput = function (elem) {
+   *  >   (user code)
+   *  > };
+   *
+   *  Due to limitations of current Browsers' XML-Parsers the opening and closing
+   *  <stream> tag for WebSocket-Connoctions will be passed as selfclosing here.
+   *
+   *  BOSH-Connections will have all stanzas wrapped in a <body> tag. See
+   *  <Strophe.Bosh.strip> if you want to strip this tag.
+   *
+   *  Parameters:
+   *    (XMLElement) elem - The XML data received by the connection.
+   */
+
+  /* jshint unused:false */
+  xmlInput: function xmlInput(elem) {
+    return;
+  },
+
+  /* jshint unused:true */
+
+  /** Function: xmlOutput
+   *  User overrideable function that receives XML data sent to the
+   *  connection.
+   *
+   *  The default function does nothing.  User code can override this with
+   *  > Strophe.Connection.xmlOutput = function (elem) {
+   *  >   (user code)
+   *  > };
+   *
+   *  Due to limitations of current Browsers' XML-Parsers the opening and closing
+   *  <stream> tag for WebSocket-Connoctions will be passed as selfclosing here.
+   *
+   *  BOSH-Connections will have all stanzas wrapped in a <body> tag. See
+   *  <Strophe.Bosh.strip> if you want to strip this tag.
+   *
+   *  Parameters:
+   *    (XMLElement) elem - The XMLdata sent by the connection.
+   */
+
+  /* jshint unused:false */
+  xmlOutput: function xmlOutput(elem) {
+    return;
+  },
+
+  /* jshint unused:true */
+
+  /** Function: rawInput
+   *  User overrideable function that receives raw data coming into the
+   *  connection.
+   *
+   *  The default function does nothing.  User code can override this with
+   *  > Strophe.Connection.rawInput = function (data) {
+   *  >   (user code)
+   *  > };
+   *
+   *  Parameters:
+   *    (String) data - The data received by the connection.
+   */
+
+  /* jshint unused:false */
+  rawInput: function rawInput(data) {
+    return;
+  },
+
+  /* jshint unused:true */
+
+  /** Function: rawOutput
+   *  User overrideable function that receives raw data sent to the
+   *  connection.
+   *
+   *  The default function does nothing.  User code can override this with
+   *  > Strophe.Connection.rawOutput = function (data) {
+   *  >   (user code)
+   *  > };
+   *
+   *  Parameters:
+   *    (String) data - The data sent by the connection.
+   */
+
+  /* jshint unused:false */
+  rawOutput: function rawOutput(data) {
+    return;
+  },
+
+  /* jshint unused:true */
+
+  /** Function: nextValidRid
+   *  User overrideable function that receives the new valid rid.
+   *
+   *  The default function does nothing. User code can override this with
+   *  > Strophe.Connection.nextValidRid = function (rid) {
+   *  >    (user code)
+   *  > };
+   *
+   *  Parameters:
+   *    (Number) rid - The next valid rid
+   */
+
+  /* jshint unused:false */
+  nextValidRid: function nextValidRid(rid) {
+    return;
+  },
+
+  /* jshint unused:true */
+
+  /** Function: send
+   *  Send a stanza.
+   *
+   *  This function is called to push data onto the send queue to
+   *  go out over the wire.  Whenever a request is sent to the BOSH
+   *  server, all pending data is sent and the queue is flushed.
+   *
+   *  Parameters:
+   *    (XMLElement |
+   *     [XMLElement] |
+   *     Strophe.Builder) elem - The stanza to send.
+   */
+  send: function send(elem) {
+    if (elem === null) {
+      return;
+    }
+
+    if (typeof elem.sort === "function") {
+      for (var i = 0; i < elem.length; i++) {
+        this._queueData(elem[i]);
+      }
+    } else if (typeof elem.tree === "function") {
+      this._queueData(elem.tree());
+    } else {
+      this._queueData(elem);
+    }
+
+    this._proto._send();
+  },
+
+  /** Function: flush
+   *  Immediately send any pending outgoing data.
+   *
+   *  Normally send() queues outgoing data until the next idle period
+   *  (100ms), which optimizes network use in the common cases when
+   *  several send()s are called in succession. flush() can be used to
+   *  immediately send all pending data.
+   */
+  flush: function flush() {
+    // cancel the pending idle period and run the idle function
+    // immediately
+    clearTimeout(this._idleTimeout);
+
+    this._onIdle();
+  },
+
+  /** Function: sendPresence
+   *  Helper function to send presence stanzas. The main benefit is for
+   *  sending presence stanzas for which you expect a responding presence
+   *  stanza with the same id (for example when leaving a chat room).
+   *
+   *  Parameters:
+   *    (XMLElement) elem - The stanza to send.
+   *    (Function) callback - The callback function for a successful request.
+   *    (Function) errback - The callback function for a failed or timed
+   *      out request.  On timeout, the stanza will be null.
+   *    (Integer) timeout - The time specified in milliseconds for a
+   *      timeout to occur.
+   *
+   *  Returns:
+   *    The id used to send the presence.
+   */
+  sendPresence: function sendPresence(elem, callback, errback, timeout) {
+    var _this3 = this;
+
+    var timeoutHandler = null;
+
+    if (typeof elem.tree === "function") {
+      elem = elem.tree();
+    }
+
+    var id = elem.getAttribute('id');
+
+    if (!id) {
+      // inject id if not found
+      id = this.getUniqueId("sendPresence");
+      elem.setAttribute("id", id);
+    }
+
+    if (typeof callback === "function" || typeof errback === "function") {
+      var handler = this.addHandler(function (stanza) {
+        // remove timeout handler if there is one
+        if (timeoutHandler) {
+          _this3.deleteTimedHandler(timeoutHandler);
+        }
+
+        if (stanza.getAttribute('type') === 'error') {
+          if (errback) {
+            errback(stanza);
+          }
+        } else if (callback) {
+          callback(stanza);
+        }
+      }, null, 'presence', null, id); // if timeout specified, set up a timeout handler.
+
+      if (timeout) {
+        timeoutHandler = this.addTimedHandler(timeout, function () {
+          // get rid of normal handler
+          _this3.deleteHandler(handler); // call errback on timeout with null stanza
+
+
+          if (errback) {
+            errback(null);
+          }
+
+          return false;
+        });
+      }
+    }
+
+    this.send(elem);
+    return id;
+  },
+
+  /** Function: sendIQ
+   *  Helper function to send IQ stanzas.
+   *
+   *  Parameters:
+   *    (XMLElement) elem - The stanza to send.
+   *    (Function) callback - The callback function for a successful request.
+   *    (Function) errback - The callback function for a failed or timed
+   *      out request.  On timeout, the stanza will be null.
+   *    (Integer) timeout - The time specified in milliseconds for a
+   *      timeout to occur.
+   *
+   *  Returns:
+   *    The id used to send the IQ.
+  */
+  sendIQ: function sendIQ(elem, callback, errback, timeout) {
+    var _this4 = this;
+
+    var timeoutHandler = null;
+
+    if (typeof elem.tree === "function") {
+      elem = elem.tree();
+    }
+
+    var id = elem.getAttribute('id');
+
+    if (!id) {
+      // inject id if not found
+      id = this.getUniqueId("sendIQ");
+      elem.setAttribute("id", id);
+    }
+
+    if (typeof callback === "function" || typeof errback === "function") {
+      var handler = this.addHandler(function (stanza) {
+        // remove timeout handler if there is one
+        if (timeoutHandler) {
+          _this4.deleteTimedHandler(timeoutHandler);
+        }
+
+        var iqtype = stanza.getAttribute('type');
+
+        if (iqtype === 'result') {
+          if (callback) {
+            callback(stanza);
+          }
+        } else if (iqtype === 'error') {
+          if (errback) {
+            errback(stanza);
+          }
+        } else {
+          var error = new Error("Got bad IQ type of ".concat(iqtype));
+          error.name = "StropheError";
+          throw error;
+        }
+      }, null, 'iq', ['error', 'result'], id); // if timeout specified, set up a timeout handler.
+
+      if (timeout) {
+        timeoutHandler = this.addTimedHandler(timeout, function () {
+          // get rid of normal handler
+          _this4.deleteHandler(handler); // call errback on timeout with null stanza
+
+
+          if (errback) {
+            errback(null);
+          }
+
+          return false;
+        });
+      }
+    }
+
+    this.send(elem);
+    return id;
+  },
+
+  /** PrivateFunction: _queueData
+   *  Queue outgoing data for later sending.  Also ensures that the data
+   *  is a DOMElement.
+   */
+  _queueData: function _queueData(element) {
+    if (element === null || !element.tagName || !element.childNodes) {
+      var error = new Error("Cannot queue non-DOMElement.");
+      error.name = "StropheError";
+      throw error;
+    }
+
+    this._data.push(element);
+  },
+
+  /** PrivateFunction: _sendRestart
+   *  Send an xmpp:restart stanza.
+   */
+  _sendRestart: function _sendRestart() {
+    var _this5 = this;
+
+    this._data.push("restart");
+
+    this._proto._sendRestart();
+
+    this._idleTimeout = setTimeout(function () {
+      return _this5._onIdle();
+    }, 100);
+  },
+
+  /** Function: addTimedHandler
+   *  Add a timed handler to the connection.
+   *
+   *  This function adds a timed handler.  The provided handler will
+   *  be called every period milliseconds until it returns false,
+   *  the connection is terminated, or the handler is removed.  Handlers
+   *  that wish to continue being invoked should return true.
+   *
+   *  Because of method binding it is necessary to save the result of
+   *  this function if you wish to remove a handler with
+   *  deleteTimedHandler().
+   *
+   *  Note that user handlers are not active until authentication is
+   *  successful.
+   *
+   *  Parameters:
+   *    (Integer) period - The period of the handler.
+   *    (Function) handler - The callback function.
+   *
+   *  Returns:
+   *    A reference to the handler that can be used to remove it.
+   */
+  addTimedHandler: function addTimedHandler(period, handler) {
+    var thand = new Strophe.TimedHandler(period, handler);
+    this.addTimeds.push(thand);
+    return thand;
+  },
+
+  /** Function: deleteTimedHandler
+   *  Delete a timed handler for a connection.
+   *
+   *  This function removes a timed handler from the connection.  The
+   *  handRef parameter is *not* the function passed to addTimedHandler(),
+   *  but is the reference returned from addTimedHandler().
+   *
+   *  Parameters:
+   *    (Strophe.TimedHandler) handRef - The handler reference.
+   */
+  deleteTimedHandler: function deleteTimedHandler(handRef) {
+    // this must be done in the Idle loop so that we don't change
+    // the handlers during iteration
+    this.removeTimeds.push(handRef);
+  },
+
+  /** Function: addHandler
+   *  Add a stanza handler for the connection.
+   *
+   *  This function adds a stanza handler to the connection.  The
+   *  handler callback will be called for any stanza that matches
+   *  the parameters.  Note that if multiple parameters are supplied,
+   *  they must all match for the handler to be invoked.
+   *
+   *  The handler will receive the stanza that triggered it as its argument.
+   *  *The handler should return true if it is to be invoked again;
+   *  returning false will remove the handler after it returns.*
+   *
+   *  As a convenience, the ns parameters applies to the top level element
+   *  and also any of its immediate children.  This is primarily to make
+   *  matching /iq/query elements easy.
+   *
+   *  Options
+   *  ~~~~~~~
+   *  With the options argument, you can specify boolean flags that affect how
+   *  matches are being done.
+   *
+   *  Currently two flags exist:
+   *
+   *  - matchBareFromJid:
+   *      When set to true, the from parameter and the
+   *      from attribute on the stanza will be matched as bare JIDs instead
+   *      of full JIDs. To use this, pass {matchBareFromJid: true} as the
+   *      value of options. The default value for matchBareFromJid is false.
+   *
+   *  - ignoreNamespaceFragment:
+   *      When set to true, a fragment specified on the stanza's namespace
+   *      URL will be ignored when it's matched with the one configured for
+   *      the handler.
+   *
+   *      This means that if you register like this:
+   *      >   connection.addHandler(
+   *      >       handler,
+   *      >       'http://jabber.org/protocol/muc',
+   *      >       null, null, null, null,
+   *      >       {'ignoreNamespaceFragment': true}
+   *      >   );
+   *
+   *      Then a stanza with XML namespace of
+   *      'http://jabber.org/protocol/muc#user' will also be matched. If
+   *      'ignoreNamespaceFragment' is false, then only stanzas with
+   *      'http://jabber.org/protocol/muc' will be matched.
+   *
+   *  Deleting the handler
+   *  ~~~~~~~~~~~~~~~~~~~~
+   *  The return value should be saved if you wish to remove the handler
+   *  with deleteHandler().
+   *
+   *  Parameters:
+   *    (Function) handler - The user callback.
+   *    (String) ns - The namespace to match.
+   *    (String) name - The stanza name to match.
+   *    (String|Array) type - The stanza type (or types if an array) to match.
+   *    (String) id - The stanza id attribute to match.
+   *    (String) from - The stanza from attribute to match.
+   *    (String) options - The handler options
+   *
+   *  Returns:
+   *    A reference to the handler that can be used to remove it.
+   */
+  addHandler: function addHandler(handler, ns, name, type, id, from, options) {
+    var hand = new Strophe.Handler(handler, ns, name, type, id, from, options);
+    this.addHandlers.push(hand);
+    return hand;
+  },
+
+  /** Function: deleteHandler
+   *  Delete a stanza handler for a connection.
+   *
+   *  This function removes a stanza handler from the connection.  The
+   *  handRef parameter is *not* the function passed to addHandler(),
+   *  but is the reference returned from addHandler().
+   *
+   *  Parameters:
+   *    (Strophe.Handler) handRef - The handler reference.
+   */
+  deleteHandler: function deleteHandler(handRef) {
+    // this must be done in the Idle loop so that we don't change
+    // the handlers during iteration
+    this.removeHandlers.push(handRef); // If a handler is being deleted while it is being added,
+    // prevent it from getting added
+
+    var i = this.addHandlers.indexOf(handRef);
+
+    if (i >= 0) {
+      this.addHandlers.splice(i, 1);
+    }
+  },
+
+  /** Function: registerSASLMechanisms
+   *
+   * Register the SASL mechanisms which will be supported by this instance of
+   * Strophe.Connection (i.e. which this XMPP client will support).
+   *
+   *  Parameters:
+   *    (Array) mechanisms - Array of objects with Strophe.SASLMechanism prototypes
+   *
+   */
+  registerSASLMechanisms: function registerSASLMechanisms(mechanisms) {
+    this.mechanisms = {};
+    mechanisms = mechanisms || [Strophe.SASLAnonymous, Strophe.SASLExternal, Strophe.SASLMD5, Strophe.SASLOAuthBearer, Strophe.SASLXOAuth2, Strophe.SASLPlain, Strophe.SASLSHA1];
+    mechanisms.forEach(this.registerSASLMechanism.bind(this));
+  },
+
+  /** Function: registerSASLMechanism
+   *
+   * Register a single SASL mechanism, to be supported by this client.
+   *
+   *  Parameters:
+   *    (Object) mechanism - Object with a Strophe.SASLMechanism prototype
+   *
+   */
+  registerSASLMechanism: function registerSASLMechanism(mechanism) {
+    this.mechanisms[mechanism.prototype.name] = mechanism;
+  },
+
+  /** Function: disconnect
+   *  Start the graceful disconnection process.
+   *
+   *  This function starts the disconnection process.  This process starts
+   *  by sending unavailable presence and sending BOSH body of type
+   *  terminate.  A timeout handler makes sure that disconnection happens
+   *  even if the BOSH server does not respond.
+   *  If the Connection object isn't connected, at least tries to abort all pending requests
+   *  so the connection object won't generate successful requests (which were already opened).
+   *
+   *  The user supplied connection callback will be notified of the
+   *  progress as this process happens.
+   *
+   *  Parameters:
+   *    (String) reason - The reason the disconnect is occuring.
+   */
+  disconnect: function disconnect(reason) {
+    this._changeConnectStatus(Strophe.Status.DISCONNECTING, reason);
+
+    Strophe.info("Disconnect was called because: " + reason);
+
+    if (this.connected) {
+      var pres = false;
+      this.disconnecting = true;
+
+      if (this.authenticated) {
+        pres = $pres({
+          'xmlns': Strophe.NS.CLIENT,
+          'type': 'unavailable'
+        });
+      } // setup timeout handler
+
+
+      this._disconnectTimeout = this._addSysTimedHandler(3000, this._onDisconnectTimeout.bind(this));
+
+      this._proto._disconnect(pres);
+    } else {
+      Strophe.info("Disconnect was called before Strophe connected to the server");
+
+      this._proto._abortAllRequests();
+
+      this._doDisconnect();
+    }
+  },
+
+  /** PrivateFunction: _changeConnectStatus
+   *  _Private_ helper function that makes sure plugins and the user's
+   *  callback are notified of connection status changes.
+   *
+   *  Parameters:
+   *    (Integer) status - the new connection status, one of the values
+   *      in Strophe.Status
+   *    (String) condition - the error condition or null
+   *    (XMLElement) elem - The triggering stanza.
+   */
+  _changeConnectStatus: function _changeConnectStatus(status, condition, elem) {
+    // notify all plugins listening for status changes
+    for (var k in Strophe._connectionPlugins) {
+      if (Object.prototype.hasOwnProperty.call(Strophe._connectionPlugins, k)) {
+        var plugin = this[k];
+
+        if (plugin.statusChanged) {
+          try {
+            plugin.statusChanged(status, condition);
+          } catch (err) {
+            Strophe.error("".concat(k, " plugin caused an exception changing status: ").concat(err));
+          }
+        }
+      }
+    } // notify the user's callback
+
+
+    if (this.connect_callback) {
+      try {
+        this.connect_callback(status, condition, elem);
+      } catch (e) {
+        Strophe._handleError(e);
+
+        Strophe.error("User connection callback caused an exception: ".concat(e));
+      }
+    }
+  },
+
+  /** PrivateFunction: _doDisconnect
+   *  _Private_ function to disconnect.
+   *
+   *  This is the last piece of the disconnection logic.  This resets the
+   *  connection and alerts the user's connection callback.
+   */
+  _doDisconnect: function _doDisconnect(condition) {
+    if (typeof this._idleTimeout === "number") {
+      clearTimeout(this._idleTimeout);
+    } // Cancel Disconnect Timeout
+
+
+    if (this._disconnectTimeout !== null) {
+      this.deleteTimedHandler(this._disconnectTimeout);
+      this._disconnectTimeout = null;
+    }
+
+    Strophe.info("_doDisconnect was called");
+
+    this._proto._doDisconnect();
+
+    this.authenticated = false;
+    this.disconnecting = false;
+    this.restored = false; // delete handlers
+
+    this.handlers = [];
+    this.timedHandlers = [];
+    this.removeTimeds = [];
+    this.removeHandlers = [];
+    this.addTimeds = [];
+    this.addHandlers = []; // tell the parent we disconnected
+
+    this._changeConnectStatus(Strophe.Status.DISCONNECTED, condition);
+
+    this.connected = false;
+  },
+
+  /** PrivateFunction: _dataRecv
+   *  _Private_ handler to processes incoming data from the the connection.
+   *
+   *  Except for _connect_cb handling the initial connection request,
+   *  this function handles the incoming data for all requests.  This
+   *  function also fires stanza handlers that match each incoming
+   *  stanza.
+   *
+   *  Parameters:
+   *    (Strophe.Request) req - The request that has data ready.
+   *    (string) req - The stanza a raw string (optiona).
+   */
+  _dataRecv: function _dataRecv(req, raw) {
+    var _this6 = this;
+
+    Strophe.info("_dataRecv called");
+
+    var elem = this._proto._reqToData(req);
+
+    if (elem === null) {
+      return;
+    }
+
+    if (this.xmlInput !== Strophe.Connection.prototype.xmlInput) {
+      if (elem.nodeName === this._proto.strip && elem.childNodes.length) {
+        this.xmlInput(elem.childNodes[0]);
+      } else {
+        this.xmlInput(elem);
+      }
+    }
+
+    if (this.rawInput !== Strophe.Connection.prototype.rawInput) {
+      if (raw) {
+        this.rawInput(raw);
+      } else {
+        this.rawInput(Strophe.serialize(elem));
+      }
+    } // remove handlers scheduled for deletion
+
+
+    while (this.removeHandlers.length > 0) {
+      var hand = this.removeHandlers.pop();
+      var i = this.handlers.indexOf(hand);
+
+      if (i >= 0) {
+        this.handlers.splice(i, 1);
+      }
+    } // add handlers scheduled for addition
+
+
+    while (this.addHandlers.length > 0) {
+      this.handlers.push(this.addHandlers.pop());
+    } // handle graceful disconnect
+
+
+    if (this.disconnecting && this._proto._emptyQueue()) {
+      this._doDisconnect();
+
+      return;
+    }
+
+    var type = elem.getAttribute("type");
+
+    if (type !== null && type === "terminate") {
+      // Don't process stanzas that come in after disconnect
+      if (this.disconnecting) {
+        return;
+      } // an error occurred
+
+
+      var cond = elem.getAttribute("condition");
+      var conflict = elem.getElementsByTagName("conflict");
+
+      if (cond !== null) {
+        if (cond === "remote-stream-error" && conflict.length > 0) {
+          cond = "conflict";
+        }
+
+        this._changeConnectStatus(Strophe.Status.CONNFAIL, cond);
+      } else {
+        this._changeConnectStatus(Strophe.Status.CONNFAIL, Strophe.ErrorCondition.UNKOWN_REASON);
+      }
+
+      this._doDisconnect(cond);
+
+      return;
+    } // send each incoming stanza through the handler chain
+
+
+    Strophe.forEachChild(elem, null, function (child) {
+      // process handlers
+      var newList = _this6.handlers;
+      _this6.handlers = [];
+
+      for (var _i5 = 0; _i5 < newList.length; _i5++) {
+        var _hand = newList[_i5]; // encapsulate 'handler.run' not to lose the whole handler list if
+        // one of the handlers throws an exception
+
+        try {
+          if (_hand.isMatch(child) && (_this6.authenticated || !_hand.user)) {
+            if (_hand.run(child)) {
+              _this6.handlers.push(_hand);
+            }
+          } else {
+            _this6.handlers.push(_hand);
+          }
+        } catch (e) {
+          // if the handler throws an exception, we consider it as false
+          Strophe.warn('Removing Strophe handlers due to uncaught exception: ' + e.message);
+        }
+      }
+    });
+  },
+
+  /** Attribute: mechanisms
+   *  SASL Mechanisms available for Connection.
+   */
+  mechanisms: {},
+
+  /** PrivateFunction: _connect_cb
+   *  _Private_ handler for initial connection request.
+   *
+   *  This handler is used to process the initial connection request
+   *  response from the BOSH server. It is used to set up authentication
+   *  handlers and start the authentication process.
+   *
+   *  SASL authentication will be attempted if available, otherwise
+   *  the code will fall back to legacy authentication.
+   *
+   *  Parameters:
+   *    (Strophe.Request) req - The current request.
+   *    (Function) _callback - low level (xmpp) connect callback function.
+   *      Useful for plugins with their own xmpp connect callback (when they
+   *      want to do something special).
+   */
+  _connect_cb: function _connect_cb(req, _callback, raw) {
+    Strophe.info("_connect_cb was called");
+    this.connected = true;
+    var bodyWrap;
+
+    try {
+      bodyWrap = this._proto._reqToData(req);
+    } catch (e) {
+      if (e.name !== Strophe.ErrorCondition.BAD_FORMAT) {
+        throw e;
+      }
+
+      this._changeConnectStatus(Strophe.Status.CONNFAIL, Strophe.ErrorCondition.BAD_FORMAT);
+
+      this._doDisconnect(Strophe.ErrorCondition.BAD_FORMAT);
+    }
+
+    if (!bodyWrap) {
+      return;
+    }
+
+    if (this.xmlInput !== Strophe.Connection.prototype.xmlInput) {
+      if (bodyWrap.nodeName === this._proto.strip && bodyWrap.childNodes.length) {
+        this.xmlInput(bodyWrap.childNodes[0]);
+      } else {
+        this.xmlInput(bodyWrap);
+      }
+    }
+
+    if (this.rawInput !== Strophe.Connection.prototype.rawInput) {
+      if (raw) {
+        this.rawInput(raw);
+      } else {
+        this.rawInput(Strophe.serialize(bodyWrap));
+      }
+    }
+
+    var conncheck = this._proto._connect_cb(bodyWrap);
+
+    if (conncheck === Strophe.Status.CONNFAIL) {
+      return;
+    } // Check for the stream:features tag
+
+
+    var hasFeatures;
+
+    if (bodyWrap.getElementsByTagNameNS) {
+      hasFeatures = bodyWrap.getElementsByTagNameNS(Strophe.NS.STREAM, "features").length > 0;
+    } else {
+      hasFeatures = bodyWrap.getElementsByTagName("stream:features").length > 0 || bodyWrap.getElementsByTagName("features").length > 0;
+    }
+
+    if (!hasFeatures) {
+      this._proto._no_auth_received(_callback);
+
+      return;
+    }
+
+    var matched = [];
+    var mechanisms = bodyWrap.getElementsByTagName("mechanism");
+
+    if (mechanisms.length > 0) {
+      for (var i = 0; i < mechanisms.length; i++) {
+        var mech = Strophe.getText(mechanisms[i]);
+        if (this.mechanisms[mech]) matched.push(this.mechanisms[mech]);
+      }
+    }
+
+    if (matched.length === 0) {
+      if (bodyWrap.getElementsByTagName("auth").length === 0) {
+        // There are no matching SASL mechanisms and also no legacy
+        // auth available.
+        this._proto._no_auth_received(_callback);
+
+        return;
+      }
+    }
+
+    if (this.do_authentication !== false) {
+      this.authenticate(matched);
+    }
+  },
+
+  /** Function: sortMechanismsByPriority
+   *
+   *  Sorts an array of objects with prototype SASLMechanism according to
+   *  their priorities.
+   *
+   *  Parameters:
+   *    (Array) mechanisms - Array of SASL mechanisms.
+   *
+   */
+  sortMechanismsByPriority: function sortMechanismsByPriority(mechanisms) {
+    // Sorting mechanisms according to priority.
+    for (var i = 0; i < mechanisms.length - 1; ++i) {
+      var higher = i;
+
+      for (var j = i + 1; j < mechanisms.length; ++j) {
+        if (mechanisms[j].prototype.priority > mechanisms[higher].prototype.priority) {
+          higher = j;
+        }
+      }
+
+      if (higher !== i) {
+        var swap = mechanisms[i];
+        mechanisms[i] = mechanisms[higher];
+        mechanisms[higher] = swap;
+      }
+    }
+
+    return mechanisms;
+  },
+
+  /** PrivateFunction: _attemptSASLAuth
+   *
+   *  Iterate through an array of SASL mechanisms and attempt authentication
+   *  with the highest priority (enabled) mechanism.
+   *
+   *  Parameters:
+   *    (Array) mechanisms - Array of SASL mechanisms.
+   *
+   *  Returns:
+   *    (Boolean) mechanism_found - true or false, depending on whether a
+   *          valid SASL mechanism was found with which authentication could be
+   *          started.
+   */
+  _attemptSASLAuth: function _attemptSASLAuth(mechanisms) {
+    mechanisms = this.sortMechanismsByPriority(mechanisms || []);
+    var mechanism_found = false;
+
+    for (var i = 0; i < mechanisms.length; ++i) {
+      if (!mechanisms[i].prototype.test(this)) {
+        continue;
+      }
+
+      this._sasl_success_handler = this._addSysHandler(this._sasl_success_cb.bind(this), null, "success", null, null);
+      this._sasl_failure_handler = this._addSysHandler(this._sasl_failure_cb.bind(this), null, "failure", null, null);
+      this._sasl_challenge_handler = this._addSysHandler(this._sasl_challenge_cb.bind(this), null, "challenge", null, null);
+      this._sasl_mechanism = new mechanisms[i]();
+
+      this._sasl_mechanism.onStart(this);
+
+      var request_auth_exchange = $build("auth", {
+        'xmlns': Strophe.NS.SASL,
+        'mechanism': this._sasl_mechanism.name
+      });
+
+      if (this._sasl_mechanism.isClientFirst) {
+        var response = this._sasl_mechanism.onChallenge(this, null);
+
+        request_auth_exchange.t(btoa(response));
+      }
+
+      this.send(request_auth_exchange.tree());
+      mechanism_found = true;
+      break;
+    }
+
+    return mechanism_found;
+  },
+
+  /** PrivateFunction: _attemptLegacyAuth
+   *
+   *  Attempt legacy (i.e. non-SASL) authentication.
+   *
+   */
+  _attemptLegacyAuth: function _attemptLegacyAuth() {
+    if (Strophe.getNodeFromJid(this.jid) === null) {
+      // we don't have a node, which is required for non-anonymous
+      // client connections
+      this._changeConnectStatus(Strophe.Status.CONNFAIL, Strophe.ErrorCondition.MISSING_JID_NODE);
+
+      this.disconnect(Strophe.ErrorCondition.MISSING_JID_NODE);
+    } else {
+      // Fall back to legacy authentication
+      this._changeConnectStatus(Strophe.Status.AUTHENTICATING, null);
+
+      this._addSysHandler(this._auth1_cb.bind(this), null, null, null, "_auth_1");
+
+      this.send($iq({
+        'type': "get",
+        'to': this.domain,
+        'id': "_auth_1"
+      }).c("query", {
+        xmlns: Strophe.NS.AUTH
+      }).c("username", {}).t(Strophe.getNodeFromJid(this.jid)).tree());
+    }
+  },
+
+  /** Function: authenticate
+   * Set up authentication
+   *
+   *  Continues the initial connection request by setting up authentication
+   *  handlers and starting the authentication process.
+   *
+   *  SASL authentication will be attempted if available, otherwise
+   *  the code will fall back to legacy authentication.
+   *
+   *  Parameters:
+   *    (Array) matched - Array of SASL mechanisms supported.
+   *
+   */
+  authenticate: function authenticate(matched) {
+    if (!this._attemptSASLAuth(matched)) {
+      this._attemptLegacyAuth();
+    }
+  },
+
+  /** PrivateFunction: _sasl_challenge_cb
+   *  _Private_ handler for the SASL challenge
+   *
+   */
+  _sasl_challenge_cb: function _sasl_challenge_cb(elem) {
+    var challenge = atob(Strophe.getText(elem));
+
+    var response = this._sasl_mechanism.onChallenge(this, challenge);
+
+    var stanza = $build('response', {
+      'xmlns': Strophe.NS.SASL
+    });
+
+    if (response !== "") {
+      stanza.t(btoa(response));
+    }
+
+    this.send(stanza.tree());
+    return true;
+  },
+
+  /** PrivateFunction: _auth1_cb
+   *  _Private_ handler for legacy authentication.
+   *
+   *  This handler is called in response to the initial <iq type='get'/>
+   *  for legacy authentication.  It builds an authentication <iq/> and
+   *  sends it, creating a handler (calling back to _auth2_cb()) to
+   *  handle the result
+   *
+   *  Parameters:
+   *    (XMLElement) elem - The stanza that triggered the callback.
+   *
+   *  Returns:
+   *    false to remove the handler.
+   */
+
+  /* jshint unused:false */
+  _auth1_cb: function _auth1_cb(elem) {
+    // build plaintext auth iq
+    var iq = $iq({
+      type: "set",
+      id: "_auth_2"
+    }).c('query', {
+      xmlns: Strophe.NS.AUTH
+    }).c('username', {}).t(Strophe.getNodeFromJid(this.jid)).up().c('password').t(this.pass);
+
+    if (!Strophe.getResourceFromJid(this.jid)) {
+      // since the user has not supplied a resource, we pick
+      // a default one here.  unlike other auth methods, the server
+      // cannot do this for us.
+      this.jid = Strophe.getBareJidFromJid(this.jid) + '/strophe';
+    }
+
+    iq.up().c('resource', {}).t(Strophe.getResourceFromJid(this.jid));
+
+    this._addSysHandler(this._auth2_cb.bind(this), null, null, null, "_auth_2");
+
+    this.send(iq.tree());
+    return false;
+  },
+
+  /* jshint unused:true */
+
+  /** PrivateFunction: _sasl_success_cb
+   *  _Private_ handler for succesful SASL authentication.
+   *
+   *  Parameters:
+   *    (XMLElement) elem - The matching stanza.
+   *
+   *  Returns:
+   *    false to remove the handler.
+   */
+  _sasl_success_cb: function _sasl_success_cb(elem) {
+    var _this7 = this;
+
+    if (this._sasl_data["server-signature"]) {
+      var serverSignature;
+      var success = atob(Strophe.getText(elem));
+      var attribMatch = /([a-z]+)=([^,]+)(,|$)/;
+      var matches = success.match(attribMatch);
+
+      if (matches[1] === "v") {
+        serverSignature = matches[2];
+      }
+
+      if (serverSignature !== this._sasl_data["server-signature"]) {
+        // remove old handlers
+        this.deleteHandler(this._sasl_failure_handler);
+        this._sasl_failure_handler = null;
+
+        if (this._sasl_challenge_handler) {
+          this.deleteHandler(this._sasl_challenge_handler);
+          this._sasl_challenge_handler = null;
+        }
+
+        this._sasl_data = {};
+        return this._sasl_failure_cb(null);
+      }
+    }
+
+    Strophe.info("SASL authentication succeeded.");
+
+    if (this._sasl_mechanism) {
+      this._sasl_mechanism.onSuccess();
+    } // remove old handlers
+
+
+    this.deleteHandler(this._sasl_failure_handler);
+    this._sasl_failure_handler = null;
+
+    if (this._sasl_challenge_handler) {
+      this.deleteHandler(this._sasl_challenge_handler);
+      this._sasl_challenge_handler = null;
+    }
+
+    var streamfeature_handlers = [];
+
+    var wrapper = function wrapper(handlers, elem) {
+      while (handlers.length) {
+        _this7.deleteHandler(handlers.pop());
+      }
+
+      _this7._sasl_auth1_cb(elem);
+
+      return false;
+    };
+
+    streamfeature_handlers.push(this._addSysHandler(function (elem) {
+      return wrapper(streamfeature_handlers, elem);
+    }, null, "stream:features", null, null));
+    streamfeature_handlers.push(this._addSysHandler(function (elem) {
+      return wrapper(streamfeature_handlers, elem);
+    }, Strophe.NS.STREAM, "features", null, null)); // we must send an xmpp:restart now
+
+    this._sendRestart();
+
+    return false;
+  },
+
+  /** PrivateFunction: _sasl_auth1_cb
+   *  _Private_ handler to start stream binding.
+   *
+   *  Parameters:
+   *    (XMLElement) elem - The matching stanza.
+   *
+   *  Returns:
+   *    false to remove the handler.
+   */
+  _sasl_auth1_cb: function _sasl_auth1_cb(elem) {
+    // save stream:features for future usage
+    this.features = elem;
+
+    for (var i = 0; i < elem.childNodes.length; i++) {
+      var child = elem.childNodes[i];
+
+      if (child.nodeName === 'bind') {
+        this.do_bind = true;
+      }
+
+      if (child.nodeName === 'session') {
+        this.do_session = true;
+      }
+    }
+
+    if (!this.do_bind) {
+      this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);
+
+      return false;
+    } else {
+      this._addSysHandler(this._sasl_bind_cb.bind(this), null, null, null, "_bind_auth_2");
+
+      var resource = Strophe.getResourceFromJid(this.jid);
+
+      if (resource) {
+        this.send($iq({
+          type: "set",
+          id: "_bind_auth_2"
+        }).c('bind', {
+          xmlns: Strophe.NS.BIND
+        }).c('resource', {}).t(resource).tree());
+      } else {
+        this.send($iq({
+          type: "set",
+          id: "_bind_auth_2"
+        }).c('bind', {
+          xmlns: Strophe.NS.BIND
+        }).tree());
+      }
+    }
+
+    return false;
+  },
+
+  /** PrivateFunction: _sasl_bind_cb
+   *  _Private_ handler for binding result and session start.
+   *
+   *  Parameters:
+   *    (XMLElement) elem - The matching stanza.
+   *
+   *  Returns:
+   *    false to remove the handler.
+   */
+  _sasl_bind_cb: function _sasl_bind_cb(elem) {
+    if (elem.getAttribute("type") === "error") {
+      Strophe.info("SASL binding failed.");
+      var conflict = elem.getElementsByTagName("conflict");
+      var condition;
+
+      if (conflict.length > 0) {
+        condition = Strophe.ErrorCondition.CONFLICT;
+      }
+
+      this._changeConnectStatus(Strophe.Status.AUTHFAIL, condition, elem);
+
+      return false;
+    } // TODO - need to grab errors
+
+
+    var bind = elem.getElementsByTagName("bind");
+
+    if (bind.length > 0) {
+      var jidNode = bind[0].getElementsByTagName("jid");
+
+      if (jidNode.length > 0) {
+        this.jid = Strophe.getText(jidNode[0]);
+
+        if (this.do_session) {
+          this._addSysHandler(this._sasl_session_cb.bind(this), null, null, null, "_session_auth_2");
+
+          this.send($iq({
+            type: "set",
+            id: "_session_auth_2"
+          }).c('session', {
+            xmlns: Strophe.NS.SESSION
+          }).tree());
+        } else {
+          this.authenticated = true;
+
+          this._changeConnectStatus(Strophe.Status.CONNECTED, null);
+        }
+      }
+    } else {
+      Strophe.info("SASL binding failed.");
+
+      this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);
+
+      return false;
+    }
+  },
+
+  /** PrivateFunction: _sasl_session_cb
+   *  _Private_ handler to finish successful SASL connection.
+   *
+   *  This sets Connection.authenticated to true on success, which
+   *  starts the processing of user handlers.
+   *
+   *  Parameters:
+   *    (XMLElement) elem - The matching stanza.
+   *
+   *  Returns:
+   *    false to remove the handler.
+   */
+  _sasl_session_cb: function _sasl_session_cb(elem) {
+    if (elem.getAttribute("type") === "result") {
+      this.authenticated = true;
+
+      this._changeConnectStatus(Strophe.Status.CONNECTED, null);
+    } else if (elem.getAttribute("type") === "error") {
+      Strophe.info("Session creation failed.");
+
+      this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);
+
+      return false;
+    }
+
+    return false;
+  },
+
+  /** PrivateFunction: _sasl_failure_cb
+   *  _Private_ handler for SASL authentication failure.
+   *
+   *  Parameters:
+   *    (XMLElement) elem - The matching stanza.
+   *
+   *  Returns:
+   *    false to remove the handler.
+   */
+
+  /* jshint unused:false */
+  _sasl_failure_cb: function _sasl_failure_cb(elem) {
+    // delete unneeded handlers
+    if (this._sasl_success_handler) {
+      this.deleteHandler(this._sasl_success_handler);
+      this._sasl_success_handler = null;
+    }
+
+    if (this._sasl_challenge_handler) {
+      this.deleteHandler(this._sasl_challenge_handler);
+      this._sasl_challenge_handler = null;
+    }
+
+    if (this._sasl_mechanism) this._sasl_mechanism.onFailure();
+
+    this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);
+
+    return false;
+  },
+
+  /* jshint unused:true */
+
+  /** PrivateFunction: _auth2_cb
+   *  _Private_ handler to finish legacy authentication.
+   *
+   *  This handler is called when the result from the jabber:iq:auth
+   *  <iq/> stanza is returned.
+   *
+   *  Parameters:
+   *    (XMLElement) elem - The stanza that triggered the callback.
+   *
+   *  Returns:
+   *    false to remove the handler.
+   */
+  _auth2_cb: function _auth2_cb(elem) {
+    if (elem.getAttribute("type") === "result") {
+      this.authenticated = true;
+
+      this._changeConnectStatus(Strophe.Status.CONNECTED, null);
+    } else if (elem.getAttribute("type") === "error") {
+      this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);
+
+      this.disconnect('authentication failed');
+    }
+
+    return false;
+  },
+
+  /** PrivateFunction: _addSysTimedHandler
+   *  _Private_ function to add a system level timed handler.
+   *
+   *  This function is used to add a Strophe.TimedHandler for the
+   *  library code.  System timed handlers are allowed to run before
+   *  authentication is complete.
+   *
+   *  Parameters:
+   *    (Integer) period - The period of the handler.
+   *    (Function) handler - The callback function.
+   */
+  _addSysTimedHandler: function _addSysTimedHandler(period, handler) {
+    var thand = new Strophe.TimedHandler(period, handler);
+    thand.user = false;
+    this.addTimeds.push(thand);
+    return thand;
+  },
+
+  /** PrivateFunction: _addSysHandler
+   *  _Private_ function to add a system level stanza handler.
+   *
+   *  This function is used to add a Strophe.Handler for the
+   *  library code.  System stanza handlers are allowed to run before
+   *  authentication is complete.
+   *
+   *  Parameters:
+   *    (Function) handler - The callback function.
+   *    (String) ns - The namespace to match.
+   *    (String) name - The stanza name to match.
+   *    (String) type - The stanza type attribute to match.
+   *    (String) id - The stanza id attribute to match.
+   */
+  _addSysHandler: function _addSysHandler(handler, ns, name, type, id) {
+    var hand = new Strophe.Handler(handler, ns, name, type, id);
+    hand.user = false;
+    this.addHandlers.push(hand);
+    return hand;
+  },
+
+  /** PrivateFunction: _onDisconnectTimeout
+   *  _Private_ timeout handler for handling non-graceful disconnection.
+   *
+   *  If the graceful disconnect process does not complete within the
+   *  time allotted, this handler finishes the disconnect anyway.
+   *
+   *  Returns:
+   *    false to remove the handler.
+   */
+  _onDisconnectTimeout: function _onDisconnectTimeout() {
+    Strophe.info("_onDisconnectTimeout was called");
+
+    this._changeConnectStatus(Strophe.Status.CONNTIMEOUT, null);
+
+    this._proto._onDisconnectTimeout(); // actually disconnect
+
+
+    this._doDisconnect();
+
+    return false;
+  },
+
+  /** PrivateFunction: _onIdle
+   *  _Private_ handler to process events during idle cycle.
+   *
+   *  This handler is called every 100ms to fire timed handlers that
+   *  are ready and keep poll requests going.
+   */
+  _onIdle: function _onIdle() {
+    var _this8 = this;
+
+    // add timed handlers scheduled for addition
+    // NOTE: we add before remove in the case a timed handler is
+    // added and then deleted before the next _onIdle() call.
+    while (this.addTimeds.length > 0) {
+      this.timedHandlers.push(this.addTimeds.pop());
+    } // remove timed handlers that have been scheduled for deletion
+
+
+    while (this.removeTimeds.length > 0) {
+      var thand = this.removeTimeds.pop();
+      var i = this.timedHandlers.indexOf(thand);
+
+      if (i >= 0) {
+        this.timedHandlers.splice(i, 1);
+      }
+    } // call ready timed handlers
+
+
+    var now = new Date().getTime();
+    var newList = [];
+
+    for (var _i6 = 0; _i6 < this.timedHandlers.length; _i6++) {
+      var _thand = this.timedHandlers[_i6];
+
+      if (this.authenticated || !_thand.user) {
+        var since = _thand.lastCalled + _thand.period;
+
+        if (since - now <= 0) {
+          if (_thand.run()) {
+            newList.push(_thand);
+          }
+        } else {
+          newList.push(_thand);
+        }
+      }
+    }
+
+    this.timedHandlers = newList;
+    clearTimeout(this._idleTimeout);
+
+    this._proto._onIdle(); // reactivate the timer only if connected
+
+
+    if (this.connected) {
+      this._idleTimeout = setTimeout(function () {
+        return _this8._onIdle();
+      }, 100);
+    }
+  }
+};
+/** Class: Strophe.SASLMechanism
+ *
+ *  encapsulates SASL authentication mechanisms.
+ *
+ *  User code may override the priority for each mechanism or disable it completely.
+ *  See <priority> for information about changing priority and <test> for informatian on
+ *  how to disable a mechanism.
+ *
+ *  By default, all mechanisms are enabled and the priorities are
+ *
+ *      OAUTHBEARER - 60
+ *      SCRAM-SHA1 - 50
+ *      DIGEST-MD5 - 40
+ *      PLAIN - 30
+ *      ANONYMOUS - 20
+ *      EXTERNAL - 10
+ *
+ *  See: Strophe.Connection.addSupportedSASLMechanisms
+ */
+
+/**
+ * PrivateConstructor: Strophe.SASLMechanism
+ * SASL auth mechanism abstraction.
+ *
+ *  Parameters:
+ *    (String) name - SASL Mechanism name.
+ *    (Boolean) isClientFirst - If client should send response first without challenge.
+ *    (Number) priority - Priority.
+ *
+ *  Returns:
+ *    A new Strophe.SASLMechanism object.
+ */
+
+Strophe.SASLMechanism = function (name, isClientFirst, priority) {
+  /** PrivateVariable: name
+   *  Mechanism name.
+   */
+  this.name = name;
+  /** PrivateVariable: isClientFirst
+   *  If client sends response without initial server challenge.
+   */
+
+  this.isClientFirst = isClientFirst;
+  /** Variable: priority
+   *  Determines which <SASLMechanism> is chosen for authentication (Higher is better).
+   *  Users may override this to prioritize mechanisms differently.
+   *
+   *  In the default configuration the priorities are
+   *
+   *  SCRAM-SHA1 - 40
+   *  DIGEST-MD5 - 30
+   *  Plain - 20
+   *
+   *  Example: (This will cause Strophe to choose the mechanism that the server sent first)
+   *
+   *  > Strophe.SASLMD5.priority = Strophe.SASLSHA1.priority;
+   *
+   *  See <SASL mechanisms> for a list of available mechanisms.
+   *
+   */
+
+  this.priority = priority;
+};
+
+Strophe.SASLMechanism.prototype = {
+  /**
+   *  Function: test
+   *  Checks if mechanism able to run.
+   *  To disable a mechanism, make this return false;
+   *
+   *  To disable plain authentication run
+   *  > Strophe.SASLPlain.test = function() {
+   *  >   return false;
+   *  > }
+   *
+   *  See <SASL mechanisms> for a list of available mechanisms.
+   *
+   *  Parameters:
+   *    (Strophe.Connection) connection - Target Connection.
+   *
+   *  Returns:
+   *    (Boolean) If mechanism was able to run.
+   */
+
+  /* jshint unused:false */
+  test: function test(connection) {
+    return true;
+  },
+
+  /* jshint unused:true */
+
+  /** PrivateFunction: onStart
+   *  Called before starting mechanism on some connection.
+   *
+   *  Parameters:
+   *    (Strophe.Connection) connection - Target Connection.
+   */
+  onStart: function onStart(connection) {
+    this._connection = connection;
+  },
+
+  /** PrivateFunction: onChallenge
+   *  Called by protocol implementation on incoming challenge. If client is
+   *  first (isClientFirst === true) challenge will be null on the first call.
+   *
+   *  Parameters:
+   *    (Strophe.Connection) connection - Target Connection.
+   *    (String) challenge - current challenge to handle.
+   *
+   *  Returns:
+   *    (String) Mechanism response.
+   */
+
+  /* jshint unused:false */
+  onChallenge: function onChallenge(connection, challenge) {
+    throw new Error("You should implement challenge handling!");
+  },
+
+  /* jshint unused:true */
+
+  /** PrivateFunction: onFailure
+   *  Protocol informs mechanism implementation about SASL failure.
+   */
+  onFailure: function onFailure() {
+    this._connection = null;
+  },
+
+  /** PrivateFunction: onSuccess
+   *  Protocol informs mechanism implementation about SASL success.
+   */
+  onSuccess: function onSuccess() {
+    this._connection = null;
+  }
+};
+/** Constants: SASL mechanisms
+ *  Available authentication mechanisms
+ *
+ *  Strophe.SASLAnonymous - SASL ANONYMOUS authentication.
+ *  Strophe.SASLPlain - SASL PLAIN authentication.
+ *  Strophe.SASLMD5 - SASL DIGEST-MD5 authentication
+ *  Strophe.SASLSHA1 - SASL SCRAM-SHA1 authentication
+ *  Strophe.SASLOAuthBearer - SASL OAuth Bearer authentication
+ *  Strophe.SASLExternal - SASL EXTERNAL authentication
+ *  Strophe.SASLXOAuth2 - SASL X-OAuth2 authentication
+ */
+// Building SASL callbacks
+
+/** PrivateConstructor: SASLAnonymous
+ *  SASL ANONYMOUS authentication.
+ */
+
+Strophe.SASLAnonymous = function () {};
+
+Strophe.SASLAnonymous.prototype = new Strophe.SASLMechanism("ANONYMOUS", false, 20);
+
+Strophe.SASLAnonymous.prototype.test = function (connection) {
+  return connection.authcid === null;
+};
+/** PrivateConstructor: SASLPlain
+ *  SASL PLAIN authentication.
+ */
+
+
+Strophe.SASLPlain = function () {};
+
+Strophe.SASLPlain.prototype = new Strophe.SASLMechanism("PLAIN", true, 50);
+
+Strophe.SASLPlain.prototype.test = function (connection) {
+  return connection.authcid !== null;
+};
+
+Strophe.SASLPlain.prototype.onChallenge = function (connection) {
+  var auth_str = connection.authzid;
+  auth_str = auth_str + "\0";
+  auth_str = auth_str + connection.authcid;
+  auth_str = auth_str + "\0";
+  auth_str = auth_str + connection.pass;
+  return utils__WEBPACK_IMPORTED_MODULE_2__["default"].utf16to8(auth_str);
+};
+/** PrivateConstructor: SASLSHA1
+ *  SASL SCRAM SHA 1 authentication.
+ */
+
+
+Strophe.SASLSHA1 = function () {};
+
+Strophe.SASLSHA1.prototype = new Strophe.SASLMechanism("SCRAM-SHA-1", true, 70);
+
+Strophe.SASLSHA1.prototype.test = function (connection) {
+  return connection.authcid !== null;
+};
+
+Strophe.SASLSHA1.prototype.onChallenge = function (connection, challenge, test_cnonce) {
+  var cnonce = test_cnonce || md5__WEBPACK_IMPORTED_MODULE_0__["default"].hexdigest(Math.random() * 1234567890);
+  var auth_str = "n=" + utils__WEBPACK_IMPORTED_MODULE_2__["default"].utf16to8(connection.authcid);
+  auth_str += ",r=";
+  auth_str += cnonce;
+  connection._sasl_data.cnonce = cnonce;
+  connection._sasl_data["client-first-message-bare"] = auth_str;
+  auth_str = "n,," + auth_str;
+
+  this.onChallenge = function (connection, challenge) {
+    var nonce, salt, iter, Hi, U, U_old, i, k;
+    var responseText = "c=biws,";
+    var authMessage = "".concat(connection._sasl_data["client-first-message-bare"], ",").concat(challenge, ",");
+    var cnonce = connection._sasl_data.cnonce;
+    var attribMatch = /([a-z]+)=([^,]+)(,|$)/;
+
+    while (challenge.match(attribMatch)) {
+      var matches = challenge.match(attribMatch);
+      challenge = challenge.replace(matches[0], "");
+
+      switch (matches[1]) {
+        case "r":
+          nonce = matches[2];
+          break;
+
+        case "s":
+          salt = matches[2];
+          break;
+
+        case "i":
+          iter = matches[2];
+          break;
+      }
+    }
+
+    if (nonce.substr(0, cnonce.length) !== cnonce) {
+      connection._sasl_data = {};
+      return connection._sasl_failure_cb();
+    }
+
+    responseText += "r=" + nonce;
+    authMessage += responseText;
+    salt = atob(salt);
+    salt += "\x00\x00\x00\x01";
+    var pass = utils__WEBPACK_IMPORTED_MODULE_2__["default"].utf16to8(connection.pass);
+    Hi = U_old = sha1__WEBPACK_IMPORTED_MODULE_1__["default"].core_hmac_sha1(pass, salt);
+
+    for (i = 1; i < iter; i++) {
+      U = sha1__WEBPACK_IMPORTED_MODULE_1__["default"].core_hmac_sha1(pass, sha1__WEBPACK_IMPORTED_MODULE_1__["default"].binb2str(U_old));
+
+      for (k = 0; k < 5; k++) {
+        Hi[k] ^= U[k];
+      }
+
+      U_old = U;
+    }
+
+    Hi = sha1__WEBPACK_IMPORTED_MODULE_1__["default"].binb2str(Hi);
+    var clientKey = sha1__WEBPACK_IMPORTED_MODULE_1__["default"].core_hmac_sha1(Hi, "Client Key");
+    var serverKey = sha1__WEBPACK_IMPORTED_MODULE_1__["default"].str_hmac_sha1(Hi, "Server Key");
+    var clientSignature = sha1__WEBPACK_IMPORTED_MODULE_1__["default"].core_hmac_sha1(sha1__WEBPACK_IMPORTED_MODULE_1__["default"].str_sha1(sha1__WEBPACK_IMPORTED_MODULE_1__["default"].binb2str(clientKey)), authMessage);
+    connection._sasl_data["server-signature"] = sha1__WEBPACK_IMPORTED_MODULE_1__["default"].b64_hmac_sha1(serverKey, authMessage);
+
+    for (k = 0; k < 5; k++) {
+      clientKey[k] ^= clientSignature[k];
+    }
+
+    responseText += ",p=" + btoa(sha1__WEBPACK_IMPORTED_MODULE_1__["default"].binb2str(clientKey));
+    return responseText;
+  };
+
+  return auth_str;
+};
+/** PrivateConstructor: SASLMD5
+ *  SASL DIGEST MD5 authentication.
+ */
+
+
+Strophe.SASLMD5 = function () {};
+
+Strophe.SASLMD5.prototype = new Strophe.SASLMechanism("DIGEST-MD5", false, 60);
+
+Strophe.SASLMD5.prototype.test = function (connection) {
+  return connection.authcid !== null;
+};
+/** PrivateFunction: _quote
+ *  _Private_ utility function to backslash escape and quote strings.
+ *
+ *  Parameters:
+ *    (String) str - The string to be quoted.
+ *
+ *  Returns:
+ *    quoted string
+ */
+
+
+Strophe.SASLMD5.prototype._quote = function (str) {
+  return '"' + str.replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"'; //" end string workaround for emacs
+};
+
+Strophe.SASLMD5.prototype.onChallenge = function (connection, challenge, test_cnonce) {
+  var attribMatch = /([a-z]+)=("[^"]+"|[^,"]+)(?:,|$)/;
+  var cnonce = test_cnonce || md5__WEBPACK_IMPORTED_MODULE_0__["default"].hexdigest("" + Math.random() * 1234567890);
+  var realm = "";
+  var host = null;
+  var nonce = "";
+  var qop = "";
+
+  while (challenge.match(attribMatch)) {
+    var matches = challenge.match(attribMatch);
+    challenge = challenge.replace(matches[0], "");
+    matches[2] = matches[2].replace(/^"(.+)"$/, "$1");
+
+    switch (matches[1]) {
+      case "realm":
+        realm = matches[2];
+        break;
+
+      case "nonce":
+        nonce = matches[2];
+        break;
+
+      case "qop":
+        qop = matches[2];
+        break;
+
+      case "host":
+        host = matches[2];
+        break;
+    }
+  }
+
+  var digest_uri = connection.servtype + "/" + connection.domain;
+
+  if (host !== null) {
+    digest_uri = digest_uri + "/" + host;
+  }
+
+  var cred = utils__WEBPACK_IMPORTED_MODULE_2__["default"].utf16to8(connection.authcid + ":" + realm + ":" + this._connection.pass);
+  var A1 = md5__WEBPACK_IMPORTED_MODULE_0__["default"].hash(cred) + ":" + nonce + ":" + cnonce;
+  var A2 = 'AUTHENTICATE:' + digest_uri;
+  var responseText = "";
+  responseText += 'charset=utf-8,';
+  responseText += 'username=' + this._quote(utils__WEBPACK_IMPORTED_MODULE_2__["default"].utf16to8(connection.authcid)) + ',';
+  responseText += 'realm=' + this._quote(realm) + ',';
+  responseText += 'nonce=' + this._quote(nonce) + ',';
+  responseText += 'nc=00000001,';
+  responseText += 'cnonce=' + this._quote(cnonce) + ',';
+  responseText += 'digest-uri=' + this._quote(digest_uri) + ',';
+  responseText += 'response=' + md5__WEBPACK_IMPORTED_MODULE_0__["default"].hexdigest(md5__WEBPACK_IMPORTED_MODULE_0__["default"].hexdigest(A1) + ":" + nonce + ":00000001:" + cnonce + ":auth:" + md5__WEBPACK_IMPORTED_MODULE_0__["default"].hexdigest(A2)) + ",";
+  responseText += 'qop=auth';
+
+  this.onChallenge = function () {
+    return "";
+  };
+
+  return responseText;
+};
+/** PrivateConstructor: SASLOAuthBearer
+ *  SASL OAuth Bearer authentication.
+ */
+
+
+Strophe.SASLOAuthBearer = function () {};
+
+Strophe.SASLOAuthBearer.prototype = new Strophe.SASLMechanism("OAUTHBEARER", true, 40);
+
+Strophe.SASLOAuthBearer.prototype.test = function (connection) {
+  return connection.pass !== null;
+};
+
+Strophe.SASLOAuthBearer.prototype.onChallenge = function (connection) {
+  var auth_str = 'n,';
+
+  if (connection.authcid !== null) {
+    auth_str = auth_str + 'a=' + connection.authzid;
+  }
+
+  auth_str = auth_str + ',';
+  auth_str = auth_str + "\x01";
+  auth_str = auth_str + 'auth=Bearer ';
+  auth_str = auth_str + connection.pass;
+  auth_str = auth_str + "\x01";
+  auth_str = auth_str + "\x01";
+  return utils__WEBPACK_IMPORTED_MODULE_2__["default"].utf16to8(auth_str);
+};
+/** PrivateConstructor: SASLExternal
+ *  SASL EXTERNAL authentication.
+ *
+ *  The EXTERNAL mechanism allows a client to request the server to use
+ *  credentials established by means external to the mechanism to
+ *  authenticate the client. The external means may be, for instance,
+ *  TLS services.
+ */
+
+
+Strophe.SASLExternal = function () {};
+
+Strophe.SASLExternal.prototype = new Strophe.SASLMechanism("EXTERNAL", true, 10);
+
+Strophe.SASLExternal.prototype.onChallenge = function (connection) {
+  /** According to XEP-178, an authzid SHOULD NOT be presented when the
+   * authcid contained or implied in the client certificate is the JID (i.e.
+   * authzid) with which the user wants to log in as.
+   *
+   * To NOT send the authzid, the user should therefore set the authcid equal
+   * to the JID when instantiating a new Strophe.Connection object.
+   */
+  return connection.authcid === connection.authzid ? '' : connection.authzid;
+};
+/** PrivateConstructor: SASLXOAuth2
+ *  SASL X-OAuth2 authentication.
+ */
+
+
+Strophe.SASLXOAuth2 = function () {};
+
+Strophe.SASLXOAuth2.prototype = new Strophe.SASLMechanism("X-OAUTH2", true, 30);
+
+Strophe.SASLXOAuth2.prototype.test = function (connection) {
+  return connection.pass !== null;
+};
+
+Strophe.SASLXOAuth2.prototype.onChallenge = function (connection) {
+  var auth_str = "\0";
+
+  if (connection.authcid !== null) {
+    auth_str = auth_str + connection.authzid;
+  }
+
+  auth_str = auth_str + "\0";
+  auth_str = auth_str + connection.pass;
+  return utils__WEBPACK_IMPORTED_MODULE_2__["default"].utf16to8(auth_str);
+};
+
+
+/* harmony default export */ __webpack_exports__["default"] = ({
+  'Strophe': Strophe,
+  '$build': $build,
+  '$iq': $iq,
+  '$msg': $msg,
+  '$pres': $pres,
+  'SHA1': sha1__WEBPACK_IMPORTED_MODULE_1__["default"],
+  'MD5': md5__WEBPACK_IMPORTED_MODULE_0__["default"],
+  'b64_hmac_sha1': sha1__WEBPACK_IMPORTED_MODULE_1__["default"].b64_hmac_sha1,
+  'b64_sha1': sha1__WEBPACK_IMPORTED_MODULE_1__["default"].b64_sha1,
+  'str_hmac_sha1': sha1__WEBPACK_IMPORTED_MODULE_1__["default"].str_hmac_sha1,
+  'str_sha1': sha1__WEBPACK_IMPORTED_MODULE_1__["default"].str_sha1
+});
+
+/***/ }),
+
+/***/ "./src/md5.js":
+/*!********************!*\
+  !*** ./src/md5.js ***!
+  \********************/
+/*! exports provided: default */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return MD5; });
+/*
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
+ * Digest Algorithm, as defined in RFC 1321.
+ * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * See http://pajhome.org.uk/crypt/md5 for more info.
+ */
+
+/*
+ * Everything that isn't used by Strophe has been stripped here!
+ */
+
+/*
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+ * to work around bugs in some JS interpreters.
+ */
+var safe_add = function safe_add(x, y) {
+  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+  return msw << 16 | lsw & 0xFFFF;
+};
+/*
+ * Bitwise rotate a 32-bit number to the left.
+ */
+
+
+var bit_rol = function bit_rol(num, cnt) {
+  return num << cnt | num >>> 32 - cnt;
+};
+/*
+ * Convert a string to an array of little-endian words
+ */
+
+
+var str2binl = function str2binl(str) {
+  var bin = [];
+
+  for (var i = 0; i < str.length * 8; i += 8) {
+    bin[i >> 5] |= (str.charCodeAt(i / 8) & 255) << i % 32;
+  }
+
+  return bin;
+};
+/*
+ * Convert an array of little-endian words to a string
+ */
+
+
+var binl2str = function binl2str(bin) {
+  var str = "";
+
+  for (var i = 0; i < bin.length * 32; i += 8) {
+    str += String.fromCharCode(bin[i >> 5] >>> i % 32 & 255);
+  }
+
+  return str;
+};
+/*
+ * Convert an array of little-endian words to a hex string.
+ */
+
+
+var binl2hex = function binl2hex(binarray) {
+  var hex_tab = "0123456789abcdef";
+  var str = "";
+
+  for (var i = 0; i < binarray.length * 4; i++) {
+    str += hex_tab.charAt(binarray[i >> 2] >> i % 4 * 8 + 4 & 0xF) + hex_tab.charAt(binarray[i >> 2] >> i % 4 * 8 & 0xF);
+  }
+
+  return str;
+};
+/*
+ * These functions implement the four basic operations the algorithm uses.
+ */
+
+
+var md5_cmn = function md5_cmn(q, a, b, x, s, t) {
+  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
+};
+
+var md5_ff = function md5_ff(a, b, c, d, x, s, t) {
+  return md5_cmn(b & c | ~b & d, a, b, x, s, t);
+};
+
+var md5_gg = function md5_gg(a, b, c, d, x, s, t) {
+  return md5_cmn(b & d | c & ~d, a, b, x, s, t);
+};
+
+var md5_hh = function md5_hh(a, b, c, d, x, s, t) {
+  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
+};
+
+var md5_ii = function md5_ii(a, b, c, d, x, s, t) {
+  return md5_cmn(c ^ (b | ~d), a, b, x, s, t);
+};
+/*
+ * Calculate the MD5 of an array of little-endian words, and a bit length
+ */
+
+
+var core_md5 = function core_md5(x, len) {
+  /* append padding */
+  x[len >> 5] |= 0x80 << len % 32;
+  x[(len + 64 >>> 9 << 4) + 14] = len;
+  var a = 1732584193;
+  var b = -271733879;
+  var c = -1732584194;
+  var d = 271733878;
+  var olda, oldb, oldc, oldd;
+
+  for (var i = 0; i < x.length; i += 16) {
+    olda = a;
+    oldb = b;
+    oldc = c;
+    oldd = d;
+    a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
+    d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
+    c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
+    b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
+    a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
+    d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
+    c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
+    b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
+    a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
+    d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
+    c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
+    b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
+    a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
+    d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
+    c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
+    b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
+    a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
+    d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
+    c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
+    b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
+    a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
+    d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
+    c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
+    b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
+    a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
+    d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
+    c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
+    b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
+    a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
+    d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
+    c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
+    b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
+    a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
+    d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
+    c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
+    b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
+    a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
+    d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
+    c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
+    b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
+    a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
+    d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
+    c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
+    b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
+    a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
+    d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
+    c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
+    b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
+    a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
+    d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
+    c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
+    b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
+    a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
+    d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
+    c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
+    b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
+    a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
+    d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
+    c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
+    b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
+    a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
+    d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
+    c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
+    b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
+    a = safe_add(a, olda);
+    b = safe_add(b, oldb);
+    c = safe_add(c, oldc);
+    d = safe_add(d, oldd);
+  }
+
+  return [a, b, c, d];
+};
+/*
+ * These are the functions you'll usually want to call.
+ * They take string arguments and return either hex or base-64 encoded
+ * strings.
+ */
+
+
+var MD5 = {
+  hexdigest: function hexdigest(s) {
+    return binl2hex(core_md5(str2binl(s), s.length * 8));
+  },
+  hash: function hash(s) {
+    return binl2str(core_md5(str2binl(s), s.length * 8));
+  }
+};
+
+
+/***/ }),
+
+/***/ "./src/sha1.js":
+/*!*********************!*\
+  !*** ./src/sha1.js ***!
+  \*********************/
+/*! exports provided: default */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return SHA1; });
+/*
+ * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
+ * in FIPS PUB 180-1
+ * Version 2.1a Copyright Paul Johnston 2000 - 2002.
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * See http://pajhome.org.uk/crypt/md5 for details.
+ */
+
+/* jshint undef: true, unused: true:, noarg: true, latedef: false */
+
+/* global define */
+
+/* Some functions and variables have been stripped for use with Strophe */
+
+/*
+ * Calculate the SHA-1 of an array of big-endian words, and a bit length
+ */
+function core_sha1(x, len) {
+  /* append padding */
+  x[len >> 5] |= 0x80 << 24 - len % 32;
+  x[(len + 64 >> 9 << 4) + 15] = len;
+  var w = new Array(80);
+  var a = 1732584193;
+  var b = -271733879;
+  var c = -1732584194;
+  var d = 271733878;
+  var e = -1009589776;
+  var i, j, t, olda, oldb, oldc, oldd, olde;
+
+  for (i = 0; i < x.length; i += 16) {
+    olda = a;
+    oldb = b;
+    oldc = c;
+    oldd = d;
+    olde = e;
+
+    for (j = 0; j < 80; j++) {
+      if (j < 16) {
+        w[j] = x[i + j];
+      } else {
+        w[j] = rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
+      }
+
+      t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), safe_add(safe_add(e, w[j]), sha1_kt(j)));
+      e = d;
+      d = c;
+      c = rol(b, 30);
+      b = a;
+      a = t;
+    }
+
+    a = safe_add(a, olda);
+    b = safe_add(b, oldb);
+    c = safe_add(c, oldc);
+    d = safe_add(d, oldd);
+    e = safe_add(e, olde);
+  }
+
+  return [a, b, c, d, e];
+}
+/*
+ * Perform the appropriate triplet combination function for the current
+ * iteration
+ */
+
+
+function sha1_ft(t, b, c, d) {
+  if (t < 20) {
+    return b & c | ~b & d;
+  }
+
+  if (t < 40) {
+    return b ^ c ^ d;
+  }
+
+  if (t < 60) {
+    return b & c | b & d | c & d;
+  }
+
+  return b ^ c ^ d;
+}
+/*
+ * Determine the appropriate additive constant for the current iteration
+ */
+
+
+function sha1_kt(t) {
+  return t < 20 ? 1518500249 : t < 40 ? 1859775393 : t < 60 ? -1894007588 : -899497514;
+}
+/*
+ * Calculate the HMAC-SHA1 of a key and some data
+ */
+
+
+function core_hmac_sha1(key, data) {
+  var bkey = str2binb(key);
+
+  if (bkey.length > 16) {
+    bkey = core_sha1(bkey, key.length * 8);
+  }
+
+  var ipad = new Array(16),
+      opad = new Array(16);
+
+  for (var i = 0; i < 16; i++) {
+    ipad[i] = bkey[i] ^ 0x36363636;
+    opad[i] = bkey[i] ^ 0x5C5C5C5C;
+  }
+
+  var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * 8);
+  return core_sha1(opad.concat(hash), 512 + 160);
+}
+/*
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+ * to work around bugs in some JS interpreters.
+ */
+
+
+function safe_add(x, y) {
+  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+  return msw << 16 | lsw & 0xFFFF;
+}
+/*
+ * Bitwise rotate a 32-bit number to the left.
+ */
+
+
+function rol(num, cnt) {
+  return num << cnt | num >>> 32 - cnt;
+}
+/*
+ * Convert an 8-bit or 16-bit string to an array of big-endian words
+ * In 8-bit function, characters >255 have their hi-byte silently ignored.
+ */
+
+
+function str2binb(str) {
+  var bin = [];
+  var mask = 255;
+
+  for (var i = 0; i < str.length * 8; i += 8) {
+    bin[i >> 5] |= (str.charCodeAt(i / 8) & mask) << 24 - i % 32;
+  }
+
+  return bin;
+}
+/*
+ * Convert an array of big-endian words to a base-64 string
+ */
+
+
+function binb2b64(binarray) {
+  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  var str = "";
+  var triplet, j;
+
+  for (var i = 0; i < binarray.length * 4; i += 3) {
+    triplet = (binarray[i >> 2] >> 8 * (3 - i % 4) & 0xFF) << 16 | (binarray[i + 1 >> 2] >> 8 * (3 - (i + 1) % 4) & 0xFF) << 8 | binarray[i + 2 >> 2] >> 8 * (3 - (i + 2) % 4) & 0xFF;
+
+    for (j = 0; j < 4; j++) {
+      if (i * 8 + j * 6 > binarray.length * 32) {
+        str += "=";
+      } else {
+        str += tab.charAt(triplet >> 6 * (3 - j) & 0x3F);
+      }
+    }
+  }
+
+  return str;
+}
+/*
+ * Convert an array of big-endian words to a string
+ */
+
+
+function binb2str(bin) {
+  var str = "";
+  var mask = 255;
+
+  for (var i = 0; i < bin.length * 32; i += 8) {
+    str += String.fromCharCode(bin[i >> 5] >>> 24 - i % 32 & mask);
+  }
+
+  return str;
+}
+/*
+ * These are the functions you'll usually want to call
+ * They take string arguments and return either hex or base-64 encoded strings
+ */
+
+
+var SHA1 = {
+  b64_hmac_sha1: function b64_hmac_sha1(key, data) {
+    return binb2b64(core_hmac_sha1(key, data));
+  },
+  b64_sha1: function b64_sha1(s) {
+    return binb2b64(core_sha1(str2binb(s), s.length * 8));
+  },
+  binb2str: binb2str,
+  core_hmac_sha1: core_hmac_sha1,
+  str_hmac_sha1: function str_hmac_sha1(key, data) {
+    return binb2str(core_hmac_sha1(key, data));
+  },
+  str_sha1: function str_sha1(s) {
+    return binb2str(core_sha1(str2binb(s), s.length * 8));
+  }
+};
+
+
+/***/ }),
+
+/***/ "./src/strophe.js":
+/*!************************!*\
+  !*** ./src/strophe.js ***!
+  \************************/
+/*! exports provided: default */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* WEBPACK VAR INJECTION */(function(global) {/* harmony import */ var core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! core */ "./src/core.js");
+/* harmony import */ var bosh__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! bosh */ "./src/bosh.js");
+/* harmony import */ var websocket__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! websocket */ "./src/websocket.js");
+/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "default", function() { return core__WEBPACK_IMPORTED_MODULE_0__["default"]; });
+
+/*global global*/
+
+
+
+global.Strophe = core__WEBPACK_IMPORTED_MODULE_0__["default"].Strophe;
+global.$build = core__WEBPACK_IMPORTED_MODULE_0__["default"].$build;
+global.$iq = core__WEBPACK_IMPORTED_MODULE_0__["default"].$iq;
+global.$msg = core__WEBPACK_IMPORTED_MODULE_0__["default"].$msg;
+global.$pres = core__WEBPACK_IMPORTED_MODULE_0__["default"].$pres;
+
+/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../node_modules/webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))
+
+/***/ }),
+
+/***/ "./src/utils.js":
+/*!**********************!*\
+  !*** ./src/utils.js ***!
+  \**********************/
+/*! exports provided: default */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return utils; });
+function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
+
+var utils = {
+  utf16to8: function utf16to8(str) {
+    var i, c;
+    var out = "";
+    var len = str.length;
+
+    for (i = 0; i < len; i++) {
+      c = str.charCodeAt(i);
+
+      if (c >= 0x0000 && c <= 0x007F) {
+        out += str.charAt(i);
+      } else if (c > 0x07FF) {
+        out += String.fromCharCode(0xE0 | c >> 12 & 0x0F);
+        out += String.fromCharCode(0x80 | c >> 6 & 0x3F);
+        out += String.fromCharCode(0x80 | c >> 0 & 0x3F);
+      } else {
+        out += String.fromCharCode(0xC0 | c >> 6 & 0x1F);
+        out += String.fromCharCode(0x80 | c >> 0 & 0x3F);
+      }
+    }
+
+    return out;
+  },
+  addCookies: function addCookies(cookies) {
+    /* Parameters:
+     *  (Object) cookies - either a map of cookie names
+     *    to string values or to maps of cookie values.
+     *
+     * For example:
+     * { "myCookie": "1234" }
+     *
+     * or:
+     * { "myCookie": {
+     *      "value": "1234",
+     *      "domain": ".example.org",
+     *      "path": "/",
+     *      "expires": expirationDate
+     *      }
+     *  }
+     *
+     *  These values get passed to Strophe.Connection via
+     *   options.cookies
+     */
+    cookies = cookies || {};
+
+    for (var cookieName in cookies) {
+      if (Object.prototype.hasOwnProperty.call(cookies, cookieName)) {
+        var expires = '';
+        var domain = '';
+        var path = '';
+        var cookieObj = cookies[cookieName];
+        var isObj = _typeof(cookieObj) === "object";
+        var cookieValue = escape(unescape(isObj ? cookieObj.value : cookieObj));
+
+        if (isObj) {
+          expires = cookieObj.expires ? ";expires=" + cookieObj.expires : '';
+          domain = cookieObj.domain ? ";domain=" + cookieObj.domain : '';
+          path = cookieObj.path ? ";path=" + cookieObj.path : '';
+        }
+
+        document.cookie = cookieName + '=' + cookieValue + expires + domain + path;
+      }
+    }
+  }
+};
+
+
+/***/ }),
+
+/***/ "./src/websocket.js":
+/*!**************************!*\
+  !*** ./src/websocket.js ***!
+  \**************************/
+/*! no exports provided */
+/***/ (function(module, __webpack_exports__, __webpack_require__) {
+
+"use strict";
+__webpack_require__.r(__webpack_exports__);
+/* harmony import */ var core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! core */ "./src/core.js");
+/*
+    This program is distributed under the terms of the MIT license.
+    Please see the LICENSE file for details.
+
+    Copyright 2006-2008, OGG, LLC
+*/
+
+/* global window, clearTimeout, WebSocket, DOMParser */
+
+var Strophe = core__WEBPACK_IMPORTED_MODULE_0__["default"].Strophe;
+var $build = core__WEBPACK_IMPORTED_MODULE_0__["default"].$build;
+/** Class: Strophe.WebSocket
+ *  _Private_ helper class that handles WebSocket Connections
+ *
+ *  The Strophe.WebSocket class is used internally by Strophe.Connection
+ *  to encapsulate WebSocket sessions. It is not meant to be used from user's code.
+ */
+
+/** File: websocket.js
+ *  A JavaScript library to enable XMPP over Websocket in Strophejs.
+ *
+ *  This file implements XMPP over WebSockets for Strophejs.
+ *  If a Connection is established with a Websocket url (ws://...)
+ *  Strophe will use WebSockets.
+ *  For more information on XMPP-over-WebSocket see RFC 7395:
+ *  http://tools.ietf.org/html/rfc7395
+ *
+ *  WebSocket support implemented by Andreas Guth (andreas.guth@rwth-aachen.de)
+ */
+
+/** PrivateConstructor: Strophe.Websocket
+ *  Create and initialize a Strophe.WebSocket object.
+ *  Currently only sets the connection Object.
+ *
+ *  Parameters:
+ *    (Strophe.Connection) connection - The Strophe.Connection that will use WebSockets.
+ *
+ *  Returns:
+ *    A new Strophe.WebSocket object.
+ */
+
+Strophe.Websocket = function (connection) {
+  this._conn = connection;
+  this.strip = "wrapper";
+  var service = connection.service;
+
+  if (service.indexOf("ws:") !== 0 && service.indexOf("wss:") !== 0) {
+    // If the service is not an absolute URL, assume it is a path and put the absolute
+    // URL together from options, current URL and the path.
+    var new_service = "";
+
+    if (connection.options.protocol === "ws" && window.location.protocol !== "https:") {
+      new_service += "ws";
+    } else {
+      new_service += "wss";
+    }
+
+    new_service += "://" + window.location.host;
+
+    if (service.indexOf("/") !== 0) {
+      new_service += window.location.pathname + service;
+    } else {
+      new_service += service;
+    }
+
+    connection.service = new_service;
+  }
+};
+
+Strophe.Websocket.prototype = {
+  /** PrivateFunction: _buildStream
+   *  _Private_ helper function to generate the <stream> start tag for WebSockets
+   *
+   *  Returns:
+   *    A Strophe.Builder with a <stream> element.
+   */
+  _buildStream: function _buildStream() {
+    return $build("open", {
+      "xmlns": Strophe.NS.FRAMING,
+      "to": this._conn.domain,
+      "version": '1.0'
+    });
+  },
+
+  /** PrivateFunction: _check_streamerror
+   * _Private_ checks a message for stream:error
+   *
+   *  Parameters:
+   *    (Strophe.Request) bodyWrap - The received stanza.
+   *    connectstatus - The ConnectStatus that will be set on error.
+   *  Returns:
+   *     true if there was a streamerror, false otherwise.
+   */
+  _check_streamerror: function _check_streamerror(bodyWrap, connectstatus) {
+    var errors;
+
+    if (bodyWrap.getElementsByTagNameNS) {
+      errors = bodyWrap.getElementsByTagNameNS(Strophe.NS.STREAM, "error");
+    } else {
+      errors = bodyWrap.getElementsByTagName("stream:error");
+    }
+
+    if (errors.length === 0) {
+      return false;
+    }
+
+    var error = errors[0];
+    var condition = "";
+    var text = "";
+    var ns = "urn:ietf:params:xml:ns:xmpp-streams";
+
+    for (var i = 0; i < error.childNodes.length; i++) {
+      var e = error.childNodes[i];
+
+      if (e.getAttribute("xmlns") !== ns) {
+        break;
+      }
+
+      if (e.nodeName === "text") {
+        text = e.textContent;
+      } else {
+        condition = e.nodeName;
+      }
+    }
+
+    var errorString = "WebSocket stream error: ";
+
+    if (condition) {
+      errorString += condition;
+    } else {
+      errorString += "unknown";
+    }
+
+    if (text) {
+      errorString += " - " + text;
+    }
+
+    Strophe.error(errorString); // close the connection on stream_error
+
+    this._conn._changeConnectStatus(connectstatus, condition);
+
+    this._conn._doDisconnect();
+
+    return true;
+  },
+
+  /** PrivateFunction: _reset
+   *  Reset the connection.
+   *
+   *  This function is called by the reset function of the Strophe Connection.
+   *  Is not needed by WebSockets.
+   */
+  _reset: function _reset() {
+    return;
+  },
+
+  /** PrivateFunction: _connect
+   *  _Private_ function called by Strophe.Connection.connect
+   *
+   *  Creates a WebSocket for a connection and assigns Callbacks to it.
+   *  Does nothing if there already is a WebSocket.
+   */
+  _connect: function _connect() {
+    // Ensure that there is no open WebSocket from a previous Connection.
+    this._closeSocket(); // Create the new WobSocket
+
+
+    this.socket = new WebSocket(this._conn.service, "xmpp");
+    this.socket.onopen = this._onOpen.bind(this);
+    this.socket.onerror = this._onError.bind(this);
+    this.socket.onclose = this._onClose.bind(this);
+    this.socket.onmessage = this._connect_cb_wrapper.bind(this);
+  },
+
+  /** PrivateFunction: _connect_cb
+   *  _Private_ function called by Strophe.Connection._connect_cb
+   *
+   * checks for stream:error
+   *
+   *  Parameters:
+   *    (Strophe.Request) bodyWrap - The received stanza.
+   */
+  _connect_cb: function _connect_cb(bodyWrap) {
+    var error = this._check_streamerror(bodyWrap, Strophe.Status.CONNFAIL);
+
+    if (error) {
+      return Strophe.Status.CONNFAIL;
+    }
+  },
+
+  /** PrivateFunction: _handleStreamStart
+   * _Private_ function that checks the opening <open /> tag for errors.
+   *
+   * Disconnects if there is an error and returns false, true otherwise.
+   *
+   *  Parameters:
+   *    (Node) message - Stanza containing the <open /> tag.
+   */
+  _handleStreamStart: function _handleStreamStart(message) {
+    var error = false; // Check for errors in the <open /> tag
+
+    var ns = message.getAttribute("xmlns");
+
+    if (typeof ns !== "string") {
+      error = "Missing xmlns in <open />";
+    } else if (ns !== Strophe.NS.FRAMING) {
+      error = "Wrong xmlns in <open />: " + ns;
+    }
+
+    var ver = message.getAttribute("version");
+
+    if (typeof ver !== "string") {
+      error = "Missing version in <open />";
+    } else if (ver !== "1.0") {
+      error = "Wrong version in <open />: " + ver;
+    }
+
+    if (error) {
+      this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, error);
+
+      this._conn._doDisconnect();
+
+      return false;
+    }
+
+    return true;
+  },
+
+  /** PrivateFunction: _connect_cb_wrapper
+   * _Private_ function that handles the first connection messages.
+   *
+   * On receiving an opening stream tag this callback replaces itself with the real
+   * message handler. On receiving a stream error the connection is terminated.
+   */
+  _connect_cb_wrapper: function _connect_cb_wrapper(message) {
+    if (message.data.indexOf("<open ") === 0 || message.data.indexOf("<?xml") === 0) {
+      // Strip the XML Declaration, if there is one
+      var data = message.data.replace(/^(<\?.*?\?>\s*)*/, "");
+      if (data === '') return;
+      var streamStart = new DOMParser().parseFromString(data, "text/xml").documentElement;
+
+      this._conn.xmlInput(streamStart);
+
+      this._conn.rawInput(message.data); //_handleStreamSteart will check for XML errors and disconnect on error
+
+
+      if (this._handleStreamStart(streamStart)) {
+        //_connect_cb will check for stream:error and disconnect on error
+        this._connect_cb(streamStart);
+      }
+    } else if (message.data.indexOf("<close ") === 0) {
+      // <close xmlns="urn:ietf:params:xml:ns:xmpp-framing />
+      // Parse the raw string to an XML element
+      var parsedMessage = new DOMParser().parseFromString(message.data, "text/xml").documentElement; // Report this input to the raw and xml handlers
+
+      this._conn.xmlInput(parsedMessage);
+
+      this._conn.rawInput(message.data);
+
+      var see_uri = parsedMessage.getAttribute("see-other-uri");
+
+      if (see_uri) {
+        var service = this._conn.service; // Valid scenarios: WSS->WSS, WS->ANY
+
+        var isSecureRedirect = service.indexOf("wss:") >= 0 && see_uri.indexOf("wss:") >= 0 || service.indexOf("ws:") >= 0;
+
+        if (isSecureRedirect) {
+          this._conn._changeConnectStatus(Strophe.Status.REDIRECT, "Received see-other-uri, resetting connection");
+
+          this._conn.reset();
+
+          this._conn.service = see_uri;
+
+          this._connect();
+        }
+      } else {
+        this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "Received closing stream");
+
+        this._conn._doDisconnect();
+      }
+    } else {
+      var string = this._streamWrap(message.data);
+
+      var elem = new DOMParser().parseFromString(string, "text/xml").documentElement;
+      this.socket.onmessage = this._onMessage.bind(this);
+
+      this._conn._connect_cb(elem, null, message.data);
+    }
+  },
+
+  /** PrivateFunction: _disconnect
+   *  _Private_ function called by Strophe.Connection.disconnect
+   *
+   *  Disconnects and sends a last stanza if one is given
+   *
+   *  Parameters:
+   *    (Request) pres - This stanza will be sent before disconnecting.
+   */
+  _disconnect: function _disconnect(pres) {
+    if (this.socket && this.socket.readyState !== WebSocket.CLOSED) {
+      if (pres) {
+        this._conn.send(pres);
+      }
+
+      var close = $build("close", {
+        "xmlns": Strophe.NS.FRAMING
+      });
+
+      this._conn.xmlOutput(close.tree());
+
+      var closeString = Strophe.serialize(close);
+
+      this._conn.rawOutput(closeString);
+
+      try {
+        this.socket.send(closeString);
+      } catch (e) {
+        Strophe.info("Couldn't send <close /> tag.");
+      }
+    }
+
+    this._conn._doDisconnect();
+  },
+
+  /** PrivateFunction: _doDisconnect
+   *  _Private_ function to disconnect.
+   *
+   *  Just closes the Socket for WebSockets
+   */
+  _doDisconnect: function _doDisconnect() {
+    Strophe.info("WebSockets _doDisconnect was called");
+
+    this._closeSocket();
+  },
+
+  /** PrivateFunction _streamWrap
+   *  _Private_ helper function to wrap a stanza in a <stream> tag.
+   *  This is used so Strophe can process stanzas from WebSockets like BOSH
+   */
+  _streamWrap: function _streamWrap(stanza) {
+    return "<wrapper>" + stanza + '</wrapper>';
+  },
+
+  /** PrivateFunction: _closeSocket
+   *  _Private_ function to close the WebSocket.
+   *
+   *  Closes the socket if it is still open and deletes it
+   */
+  _closeSocket: function _closeSocket() {
+    if (this.socket) {
+      try {
+        this.socket.onerror = null;
+        this.socket.close();
+      } catch (e) {
+        Strophe.debug(e.message);
+      }
+    }
+
+    this.socket = null;
+  },
+
+  /** PrivateFunction: _emptyQueue
+   * _Private_ function to check if the message queue is empty.
+   *
+   *  Returns:
+   *    True, because WebSocket messages are send immediately after queueing.
+   */
+  _emptyQueue: function _emptyQueue() {
+    return true;
+  },
+
+  /** PrivateFunction: _onClose
+   * _Private_ function to handle websockets closing.
+   *
+   * Nothing to do here for WebSockets
+   */
+  _onClose: function _onClose(e) {
+    if (this._conn.connected && !this._conn.disconnecting) {
+      Strophe.error("Websocket closed unexpectedly");
+
+      this._conn._doDisconnect();
+    } else if (e && e.code === 1006 && !this._conn.connected && this.socket) {
+      // in case the onError callback was not called (Safari 10 does not
+      // call onerror when the initial connection fails) we need to
+      // dispatch a CONNFAIL status update to be consistent with the
+      // behavior on other browsers.
+      Strophe.error("Websocket closed unexcectedly");
+
+      this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "The WebSocket connection could not be established or was disconnected.");
+
+      this._conn._doDisconnect();
+    } else {
+      Strophe.info("Websocket closed");
+    }
+  },
+
+  /** PrivateFunction: _no_auth_received
+   *
+   * Called on stream start/restart when no stream:features
+   * has been received.
+   */
+  _no_auth_received: function _no_auth_received(callback) {
+    Strophe.error("Server did not offer a supported authentication mechanism");
+
+    this._changeConnectStatus(Strophe.Status.CONNFAIL, Strophe.ErrorCondition.NO_AUTH_MECH);
+
+    if (callback) {
+      callback.call(this._conn);
+    }
+
+    this._conn._doDisconnect();
+  },
+
+  /** PrivateFunction: _onDisconnectTimeout
+   *  _Private_ timeout handler for handling non-graceful disconnection.
+   *
+   *  This does nothing for WebSockets
+   */
+  _onDisconnectTimeout: function _onDisconnectTimeout() {},
+
+  /** PrivateFunction: _abortAllRequests
+   *  _Private_ helper function that makes sure all pending requests are aborted.
+   */
+  _abortAllRequests: function _abortAllRequests() {},
+
+  /** PrivateFunction: _onError
+   * _Private_ function to handle websockets errors.
+   *
+   * Parameters:
+   * (Object) error - The websocket error.
+   */
+  _onError: function _onError(error) {
+    Strophe.error("Websocket error " + error);
+
+    this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "The WebSocket connection could not be established or was disconnected.");
+
+    this._disconnect();
+  },
+
+  /** PrivateFunction: _onIdle
+   *  _Private_ function called by Strophe.Connection._onIdle
+   *
+   *  sends all queued stanzas
+   */
+  _onIdle: function _onIdle() {
+    var data = this._conn._data;
+
+    if (data.length > 0 && !this._conn.paused) {
+      for (var i = 0; i < data.length; i++) {
+        if (data[i] !== null) {
+          var stanza = void 0;
+
+          if (data[i] === "restart") {
+            stanza = this._buildStream().tree();
+          } else {
+            stanza = data[i];
+          }
+
+          var rawStanza = Strophe.serialize(stanza);
+
+          this._conn.xmlOutput(stanza);
+
+          this._conn.rawOutput(rawStanza);
+
+          this.socket.send(rawStanza);
+        }
+      }
+
+      this._conn._data = [];
+    }
+  },
+
+  /** PrivateFunction: _onMessage
+   * _Private_ function to handle websockets messages.
+   *
+   * This function parses each of the messages as if they are full documents.
+   * [TODO : We may actually want to use a SAX Push parser].
+   *
+   * Since all XMPP traffic starts with
+   *  <stream:stream version='1.0'
+   *                 xml:lang='en'
+   *                 xmlns='jabber:client'
+   *                 xmlns:stream='http://etherx.jabber.org/streams'
+   *                 id='3697395463'
+   *                 from='SERVER'>
+   *
+   * The first stanza will always fail to be parsed.
+   *
+   * Additionally, the seconds stanza will always be <stream:features> with
+   * the stream NS defined in the previous stanza, so we need to 'force'
+   * the inclusion of the NS in this stanza.
+   *
+   * Parameters:
+   * (string) message - The websocket message.
+   */
+  _onMessage: function _onMessage(message) {
+    var elem; // check for closing stream
+
+    var close = '<close xmlns="urn:ietf:params:xml:ns:xmpp-framing" />';
+
+    if (message.data === close) {
+      this._conn.rawInput(close);
+
+      this._conn.xmlInput(message);
+
+      if (!this._conn.disconnecting) {
+        this._conn._doDisconnect();
+      }
+
+      return;
+    } else if (message.data.search("<open ") === 0) {
+      // This handles stream restarts
+      elem = new DOMParser().parseFromString(message.data, "text/xml").documentElement;
+
+      if (!this._handleStreamStart(elem)) {
+        return;
+      }
+    } else {
+      var data = this._streamWrap(message.data);
+
+      elem = new DOMParser().parseFromString(data, "text/xml").documentElement;
+    }
+
+    if (this._check_streamerror(elem, Strophe.Status.ERROR)) {
+      return;
+    } //handle unavailable presence stanza before disconnecting
+
+
+    if (this._conn.disconnecting && elem.firstChild.nodeName === "presence" && elem.firstChild.getAttribute("type") === "unavailable") {
+      this._conn.xmlInput(elem);
+
+      this._conn.rawInput(Strophe.serialize(elem)); // if we are already disconnecting we will ignore the unavailable stanza and
+      // wait for the </stream:stream> tag before we close the connection
+
+
+      return;
+    }
+
+    this._conn._dataRecv(elem, message.data);
+  },
+
+  /** PrivateFunction: _onOpen
+   * _Private_ function to handle websockets connection setup.
+   *
+   * The opening stream tag is sent here.
+   */
+  _onOpen: function _onOpen() {
+    Strophe.info("Websocket open");
+
+    var start = this._buildStream();
+
+    this._conn.xmlOutput(start.tree());
+
+    var startString = Strophe.serialize(start);
+
+    this._conn.rawOutput(startString);
+
+    this.socket.send(startString);
+  },
+
+  /** PrivateFunction: _reqToData
+   * _Private_ function to get a stanza out of a request.
+   *
+   * WebSockets don't use requests, so the passed argument is just returned.
+   *
+   *  Parameters:
+   *    (Object) stanza - The stanza.
+   *
+   *  Returns:
+   *    The stanza that was passed.
+   */
+  _reqToData: function _reqToData(stanza) {
+    return stanza;
+  },
+
+  /** PrivateFunction: _send
+   *  _Private_ part of the Connection.send function for WebSocket
+   *
+   * Just flushes the messages that are in the queue
+   */
+  _send: function _send() {
+    this._conn.flush();
+  },
+
+  /** PrivateFunction: _sendRestart
+   *
+   *  Send an xmpp:restart stanza.
+   */
+  _sendRestart: function _sendRestart() {
+    clearTimeout(this._conn._idleTimeout);
+
+    this._conn._onIdle.bind(this._conn)();
+  }
+};
+
+/***/ })
+
+/******/ })["default"];
+});
+//# sourceMappingURL=strophe.js.map
\ No newline at end of file