/**
 *  Copyright (C) 2021  Double Bastion LLC
 *
 *  This file is part of Roundpin, which is licensed under the
 *  GNU Affero General Public License Version 3.0. The license terms
 *  are detailed in the "LICENSE.txt" file located in the root directory.
 *
 *  The file content from below is identical with that of the
 *  original file "jssip-3.9.1.js". The copyright notice for the
 *  original content follows:

 * JsSIP v3.9.1
 * the Javascript SIP library
 * Copyright: 2012-2022 
 * Homepage: https://jssip.net
 * License: MIT
 */

(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.JsSIP = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict";

function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

var Utils = require('./Utils');

var JsSIP_C = require('./Constants');

var Grammar = require('./Grammar');

var URI = require('./URI');

var Socket = require('./Socket');

var Exceptions = require('./Exceptions'); // Default settings.


exports.settings = {
  // SIP authentication.
  authorization_user: null,
  password: null,
  realm: null,
  ha1: null,
  authorization_jwt: null,
  // SIP account.
  display_name: null,
  uri: null,
  contact_uri: null,
  // SIP instance id (GRUU).
  instance_id: null,
  // Preloaded SIP Route header field.
  use_preloaded_route: false,
  // Session parameters.
  session_timers: true,
  session_timers_refresh_method: JsSIP_C.UPDATE,
  session_timers_force_refresher: false,
  no_answer_timeout: 60,
  // Registration parameters.
  register: true,
  register_expires: 600,
  registrar_server: null,
  // Connection options.
  sockets: null,
  connection_recovery_max_interval: JsSIP_C.CONNECTION_RECOVERY_MAX_INTERVAL,
  connection_recovery_min_interval: JsSIP_C.CONNECTION_RECOVERY_MIN_INTERVAL,

  /*
   * Host address.
   * Value to be set in Via sent_by and host part of Contact FQDN.
  */
  via_host: "".concat(Utils.createRandomToken(12), ".invalid")
}; // Configuration checks.

var checks = {
  mandatory: {
    sockets: function sockets(_sockets2) {
      /* Allow defining sockets parameter as:
       *  Socket: socket
       *  Array of Socket: [socket1, socket2]
       *  Array of Objects: [{socket: socket1, weight:1}, {socket: Socket2, weight:0}]
       *  Array of Objects and Socket: [{socket: socket1}, socket2]
       */
      var _sockets = [];

      if (Socket.isSocket(_sockets2)) {
        _sockets.push({
          socket: _sockets2
        });
      } else if (Array.isArray(_sockets2) && _sockets2.length) {
        var _iterator = _createForOfIteratorHelper(_sockets2),
            _step;

        try {
          for (_iterator.s(); !(_step = _iterator.n()).done;) {
            var socket = _step.value;

            if (Object.prototype.hasOwnProperty.call(socket, 'socket') && Socket.isSocket(socket.socket)) {
              _sockets.push(socket);
            } else if (Socket.isSocket(socket)) {
              _sockets.push({
                socket: socket
              });
            }
          }
        } catch (err) {
          _iterator.e(err);
        } finally {
          _iterator.f();
        }
      } else {
        return;
      }

      return _sockets;
    },
    uri: function uri(_uri) {
      if (!/^sip:/i.test(_uri)) {
        _uri = "".concat(JsSIP_C.SIP, ":").concat(_uri);
      }

      var parsed = URI.parse(_uri);

      if (!parsed) {
        return;
      } else if (!parsed.user) {
        return;
      } else {
        return parsed;
      }
    }
  },
  optional: {
    authorization_user: function authorization_user(_authorization_user) {
      if (Grammar.parse("\"".concat(_authorization_user, "\""), 'quoted_string') === -1) {
        return;
      } else {
        return _authorization_user;
      }
    },
    authorization_jwt: function authorization_jwt(_authorization_jwt) {
      if (typeof _authorization_jwt === 'string') {
        return _authorization_jwt;
      }
    },
    user_agent: function user_agent(_user_agent) {
      if (typeof _user_agent === 'string') {
        return _user_agent;
      }
    },
    connection_recovery_max_interval: function connection_recovery_max_interval(_connection_recovery_max_interval) {
      if (Utils.isDecimal(_connection_recovery_max_interval)) {
        var value = Number(_connection_recovery_max_interval);

        if (value > 0) {
          return value;
        }
      }
    },
    connection_recovery_min_interval: function connection_recovery_min_interval(_connection_recovery_min_interval) {
      if (Utils.isDecimal(_connection_recovery_min_interval)) {
        var value = Number(_connection_recovery_min_interval);

        if (value > 0) {
          return value;
        }
      }
    },
    contact_uri: function contact_uri(_contact_uri) {
      if (typeof _contact_uri === 'string') {
        var uri = Grammar.parse(_contact_uri, 'SIP_URI');

        if (uri !== -1) {
          return uri;
        }
      }
    },
    display_name: function display_name(_display_name) {
      return _display_name;
    },
    instance_id: function instance_id(_instance_id) {
      if (/^uuid:/i.test(_instance_id)) {
        _instance_id = _instance_id.substr(5);
      }

      if (Grammar.parse(_instance_id, 'uuid') === -1) {
        return;
      } else {
        return _instance_id;
      }
    },
    no_answer_timeout: function no_answer_timeout(_no_answer_timeout) {
      if (Utils.isDecimal(_no_answer_timeout)) {
        var value = Number(_no_answer_timeout);

        if (value > 0) {
          return value;
        }
      }
    },
    session_timers: function session_timers(_session_timers) {
      if (typeof _session_timers === 'boolean') {
        return _session_timers;
      }
    },
    session_timers_refresh_method: function session_timers_refresh_method(method) {
      if (typeof method === 'string') {
        method = method.toUpperCase();

        if (method === JsSIP_C.INVITE || method === JsSIP_C.UPDATE) {
          return method;
        }
      }
    },
    session_timers_force_refresher: function session_timers_force_refresher(_session_timers_force_refresher) {
      if (typeof _session_timers_force_refresher === 'boolean') {
        return _session_timers_force_refresher;
      }
    },
    password: function password(_password) {
      return String(_password);
    },
    realm: function realm(_realm) {
      return String(_realm);
    },
    ha1: function ha1(_ha) {
      return String(_ha);
    },
    register: function register(_register) {
      if (typeof _register === 'boolean') {
        return _register;
      }
    },
    register_expires: function register_expires(_register_expires) {
      if (Utils.isDecimal(_register_expires)) {
        var value = Number(_register_expires);

        if (value > 0) {
          return value;
        }
      }
    },
    registrar_server: function registrar_server(_registrar_server) {
      if (!/^sip:/i.test(_registrar_server)) {
        _registrar_server = "".concat(JsSIP_C.SIP, ":").concat(_registrar_server);
      }

      var parsed = URI.parse(_registrar_server);

      if (!parsed) {
        return;
      } else if (parsed.user) {
        return;
      } else {
        return parsed;
      }
    },
    use_preloaded_route: function use_preloaded_route(_use_preloaded_route) {
      if (typeof _use_preloaded_route === 'boolean') {
        return _use_preloaded_route;
      }
    }
  }
};

exports.load = function (dst, src) {
  // Check Mandatory parameters.
  for (var parameter in checks.mandatory) {
    if (!src.hasOwnProperty(parameter)) {
      throw new Exceptions.ConfigurationError(parameter);
    } else {
      var value = src[parameter];
      var checked_value = checks.mandatory[parameter](value);

      if (checked_value !== undefined) {
        dst[parameter] = checked_value;
      } else {
        throw new Exceptions.ConfigurationError(parameter, value);
      }
    }
  } // Check Optional parameters.


  for (var _parameter in checks.optional) {
    if (src.hasOwnProperty(_parameter)) {
      var _value = src[_parameter];
      /* If the parameter value is null, empty string, undefined, empty array
       * or it's a number with NaN value, then apply its default value.
       */

      if (Utils.isEmpty(_value)) {
        continue;
      }

      var _checked_value = checks.optional[_parameter](_value);

      if (_checked_value !== undefined) {
        dst[_parameter] = _checked_value;
      } else {
        throw new Exceptions.ConfigurationError(_parameter, _value);
      }
    }
  }
};
},{"./Constants":2,"./Exceptions":6,"./Grammar":7,"./Socket":22,"./URI":27,"./Utils":28}],2:[function(require,module,exports){
"use strict";

var pkg = require('../package.json');

module.exports = {
  USER_AGENT: "".concat(pkg.title, " ").concat(pkg.version),
  // SIP scheme.
  SIP: 'sip',
  SIPS: 'sips',
  // End and Failure causes.
  causes: {
    // Generic error causes.
    CONNECTION_ERROR: 'Connection Error',
    REQUEST_TIMEOUT: 'Request Timeout',
    SIP_FAILURE_CODE: 'SIP Failure Code',
    INTERNAL_ERROR: 'Internal Error',
    // SIP error causes.
    BUSY: 'Busy',
    REJECTED: 'Rejected',
    REDIRECTED: 'Redirected',
    UNAVAILABLE: 'Unavailable',
    NOT_FOUND: 'Not Found',
    ADDRESS_INCOMPLETE: 'Address Incomplete',
    INCOMPATIBLE_SDP: 'Incompatible SDP',
    MISSING_SDP: 'Missing SDP',
    AUTHENTICATION_ERROR: 'Authentication Error',
    // Session error causes.
    BYE: 'Terminated',
    WEBRTC_ERROR: 'WebRTC Error',
    CANCELED: 'Canceled',
    NO_ANSWER: 'No Answer',
    EXPIRES: 'Expires',
    NO_ACK: 'No ACK',
    DIALOG_ERROR: 'Dialog Error',
    USER_DENIED_MEDIA_ACCESS: 'User Denied Media Access',
    BAD_MEDIA_DESCRIPTION: 'Bad Media Description',
    RTP_TIMEOUT: 'RTP Timeout'
  },
  SIP_ERROR_CAUSES: {
    REDIRECTED: [300, 301, 302, 305, 380],
    BUSY: [486, 600],
    REJECTED: [403, 603],
    NOT_FOUND: [404, 604],
    UNAVAILABLE: [480, 410, 408, 430],
    ADDRESS_INCOMPLETE: [484, 424],
    INCOMPATIBLE_SDP: [488, 606],
    AUTHENTICATION_ERROR: [401, 407]
  },
  // SIP Methods.
  ACK: 'ACK',
  BYE: 'BYE',
  CANCEL: 'CANCEL',
  INFO: 'INFO',
  INVITE: 'INVITE',
  MESSAGE: 'MESSAGE',
  NOTIFY: 'NOTIFY',
  OPTIONS: 'OPTIONS',
  REGISTER: 'REGISTER',
  REFER: 'REFER',
  UPDATE: 'UPDATE',
  SUBSCRIBE: 'SUBSCRIBE',
  // DTMF transport methods.
  DTMF_TRANSPORT: {
    INFO: 'INFO',
    RFC2833: 'RFC2833'
  },

  /* SIP Response Reasons
   * DOC: https://www.iana.org/assignments/sip-parameters
   * Copied from https://github.com/versatica/OverSIP/blob/master/lib/oversip/sip/constants.rb#L7
   */
  REASON_PHRASE: {
    100: 'Trying',
    180: 'Ringing',
    181: 'Call Is Being Forwarded',
    182: 'Queued',
    183: 'Session Progress',
    199: 'Early Dialog Terminated',
    // draft-ietf-sipcore-199
    200: 'OK',
    202: 'Accepted',
    // RFC 3265
    204: 'No Notification',
    // RFC 5839
    300: 'Multiple Choices',
    301: 'Moved Permanently',
    302: 'Moved Temporarily',
    305: 'Use Proxy',
    380: 'Alternative Service',
    400: 'Bad Request',
    401: 'Unauthorized',
    402: 'Payment Required',
    403: 'Forbidden',
    404: 'Not Found',
    405: 'Method Not Allowed',
    406: 'Not Acceptable',
    407: 'Proxy Authentication Required',
    408: 'Request Timeout',
    410: 'Gone',
    412: 'Conditional Request Failed',
    // RFC 3903
    413: 'Request Entity Too Large',
    414: 'Request-URI Too Long',
    415: 'Unsupported Media Type',
    416: 'Unsupported URI Scheme',
    417: 'Unknown Resource-Priority',
    // RFC 4412
    420: 'Bad Extension',
    421: 'Extension Required',
    422: 'Session Interval Too Small',
    // RFC 4028
    423: 'Interval Too Brief',
    424: 'Bad Location Information',
    // RFC 6442
    428: 'Use Identity Header',
    // RFC 4474
    429: 'Provide Referrer Identity',
    // RFC 3892
    430: 'Flow Failed',
    // RFC 5626
    433: 'Anonymity Disallowed',
    // RFC 5079
    436: 'Bad Identity-Info',
    // RFC 4474
    437: 'Unsupported Certificate',
    // RFC 4744
    438: 'Invalid Identity Header',
    // RFC 4744
    439: 'First Hop Lacks Outbound Support',
    // RFC 5626
    440: 'Max-Breadth Exceeded',
    // RFC 5393
    469: 'Bad Info Package',
    // draft-ietf-sipcore-info-events
    470: 'Consent Needed',
    // RFC 5360
    478: 'Unresolvable Destination',
    // Custom code copied from Kamailio.
    480: 'Temporarily Unavailable',
    481: 'Call/Transaction Does Not Exist',
    482: 'Loop Detected',
    483: 'Too Many Hops',
    484: 'Address Incomplete',
    485: 'Ambiguous',
    486: 'Busy Here',
    487: 'Request Terminated',
    488: 'Not Acceptable Here',
    489: 'Bad Event',
    // RFC 3265
    491: 'Request Pending',
    493: 'Undecipherable',
    494: 'Security Agreement Required',
    // RFC 3329
    500: 'JsSIP Internal Error',
    501: 'Not Implemented',
    502: 'Bad Gateway',
    503: 'Service Unavailable',
    504: 'Server Time-out',
    505: 'Version Not Supported',
    513: 'Message Too Large',
    580: 'Precondition Failure',
    // RFC 3312
    600: 'Busy Everywhere',
    603: 'Decline',
    604: 'Does Not Exist Anywhere',
    606: 'Not Acceptable'
  },
  ALLOWED_METHODS: 'INVITE,ACK,CANCEL,BYE,UPDATE,MESSAGE,OPTIONS,REFER,INFO,NOTIFY',
  ACCEPTED_BODY_TYPES: 'application/sdp, application/dtmf-relay',
  MAX_FORWARDS: 69,
  SESSION_EXPIRES: 90,
  MIN_SESSION_EXPIRES: 60,
  CONNECTION_RECOVERY_MAX_INTERVAL: 30,
  CONNECTION_RECOVERY_MIN_INTERVAL: 2
};
},{"../package.json":40}],3:[function(require,module,exports){
"use strict";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var Logger = require('./Logger');

var SIPMessage = require('./SIPMessage');

var JsSIP_C = require('./Constants');

var Transactions = require('./Transactions');

var Dialog_RequestSender = require('./Dialog/RequestSender');

var Utils = require('./Utils');

var logger = new Logger('Dialog');
var C = {
  // Dialog states.
  STATUS_EARLY: 1,
  STATUS_CONFIRMED: 2
}; // RFC 3261 12.1.

module.exports = /*#__PURE__*/function () {
  _createClass(Dialog, null, [{
    key: "C",
    // Expose C object.
    get: function get() {
      return C;
    }
  }]);

  function Dialog(owner, message, type) {
    var state = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : C.STATUS_CONFIRMED;

    _classCallCheck(this, Dialog);

    this._owner = owner;
    this._ua = owner._ua;
    this._uac_pending_reply = false;
    this._uas_pending_reply = false;

    if (!message.hasHeader('contact')) {
      return {
        error: 'unable to create a Dialog without Contact header field'
      };
    }

    if (message instanceof SIPMessage.IncomingResponse) {
      state = message.status_code < 200 ? C.STATUS_EARLY : C.STATUS_CONFIRMED;
    }

    var contact = message.parseHeader('contact'); // RFC 3261 12.1.1.

    if (type === 'UAS') {
      this._id = {
        call_id: message.call_id,
        local_tag: message.to_tag,
        remote_tag: message.from_tag,
        toString: function toString() {
          return this.call_id + this.local_tag + this.remote_tag;
        }
      };
      this._state = state;
      this._remote_seqnum = message.cseq;
      this._local_uri = message.parseHeader('to').uri;
      this._remote_uri = message.parseHeader('from').uri;
      this._remote_target = contact.uri;
      this._route_set = message.getHeaders('record-route');
      this._ack_seqnum = this._remote_seqnum;
    } // RFC 3261 12.1.2.
    else if (type === 'UAC') {
        this._id = {
          call_id: message.call_id,
          local_tag: message.from_tag,
          remote_tag: message.to_tag,
          toString: function toString() {
            return this.call_id + this.local_tag + this.remote_tag;
          }
        };
        this._state = state;
        this._local_seqnum = message.cseq;
        this._local_uri = message.parseHeader('from').uri;
        this._remote_uri = message.parseHeader('to').uri;
        this._remote_target = contact.uri;
        this._route_set = message.getHeaders('record-route').reverse();
        this._ack_seqnum = null;
      }

    this._ua.newDialog(this);

    logger.debug("new ".concat(type, " dialog created with status ").concat(this._state === C.STATUS_EARLY ? 'EARLY' : 'CONFIRMED'));
  }

  _createClass(Dialog, [{
    key: "update",
    value: function update(message, type) {
      this._state = C.STATUS_CONFIRMED;
      logger.debug("dialog ".concat(this._id.toString(), "  changed to CONFIRMED state"));

      if (type === 'UAC') {
        // RFC 3261 13.2.2.4.
        this._route_set = message.getHeaders('record-route').reverse();
      }
    }
  }, {
    key: "terminate",
    value: function terminate() {
      logger.debug("dialog ".concat(this._id.toString(), " deleted"));

      this._ua.destroyDialog(this);
    }
  }, {
    key: "sendRequest",
    value: function sendRequest(method) {
      var _this = this;

      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      var extraHeaders = Utils.cloneArray(options.extraHeaders);
      var eventHandlers = Utils.cloneObject(options.eventHandlers);
      var body = options.body || null;

      var request = this._createRequest(method, extraHeaders, body); // Increase the local CSeq on authentication.


      eventHandlers.onAuthenticated = function () {
        _this._local_seqnum += 1;
      };

      var request_sender = new Dialog_RequestSender(this, request, eventHandlers);
      request_sender.send(); // Return the instance of OutgoingRequest.

      return request;
    }
  }, {
    key: "receiveRequest",
    value: function receiveRequest(request) {
      // Check in-dialog request.
      if (!this._checkInDialogRequest(request)) {
        return;
      } // ACK received. Cleanup this._ack_seqnum.


      if (request.method === JsSIP_C.ACK && this._ack_seqnum !== null) {
        this._ack_seqnum = null;
      } // INVITE received. Set this._ack_seqnum.
      else if (request.method === JsSIP_C.INVITE) {
          this._ack_seqnum = request.cseq;
        }

      this._owner.receiveRequest(request);
    } // RFC 3261 12.2.1.1.

  }, {
    key: "_createRequest",
    value: function _createRequest(method, extraHeaders, body) {
      extraHeaders = Utils.cloneArray(extraHeaders);

      if (!this._local_seqnum) {
        this._local_seqnum = Math.floor(Math.random() * 10000);
      }

      var cseq = method === JsSIP_C.CANCEL || method === JsSIP_C.ACK ? this._local_seqnum : this._local_seqnum += 1;
      var request = new SIPMessage.OutgoingRequest(method, this._remote_target, this._ua, {
        'cseq': cseq,
        'call_id': this._id.call_id,
        'from_uri': this._local_uri,
        'from_tag': this._id.local_tag,
        'to_uri': this._remote_uri,
        'to_tag': this._id.remote_tag,
        'route_set': this._route_set
      }, extraHeaders, body);
      return request;
    } // RFC 3261 12.2.2.

  }, {
    key: "_checkInDialogRequest",
    value: function _checkInDialogRequest(request) {
      var _this2 = this;

      if (!this._remote_seqnum) {
        this._remote_seqnum = request.cseq;
      } else if (request.cseq < this._remote_seqnum) {
        if (request.method === JsSIP_C.ACK) {
          // We are not expecting any ACK with lower seqnum than the current one.
          // Or this is not the ACK we are waiting for.
          if (this._ack_seqnum === null || request.cseq !== this._ack_seqnum) {
            return false;
          }
        } else {
          request.reply(500);
          return false;
        }
      } else if (request.cseq > this._remote_seqnum) {
        this._remote_seqnum = request.cseq;
      } // RFC3261 14.2 Modifying an Existing Session -UAS BEHAVIOR-.


      if (request.method === JsSIP_C.INVITE || request.method === JsSIP_C.UPDATE && request.body) {
        if (this._uac_pending_reply === true) {
          request.reply(491);
        } else if (this._uas_pending_reply === true) {
          var retryAfter = (Math.random() * 10 | 0) + 1;
          request.reply(500, null, ["Retry-After:".concat(retryAfter)]);
          return false;
        } else {
          this._uas_pending_reply = true;

          var stateChanged = function stateChanged() {
            if (request.server_transaction.state === Transactions.C.STATUS_ACCEPTED || request.server_transaction.state === Transactions.C.STATUS_COMPLETED || request.server_transaction.state === Transactions.C.STATUS_TERMINATED) {
              request.server_transaction.removeListener('stateChanged', stateChanged);
              _this2._uas_pending_reply = false;
            }
          };

          request.server_transaction.on('stateChanged', stateChanged);
        } // RFC3261 12.2.2 Replace the dialog`s remote target URI if the request is accepted.


        if (request.hasHeader('contact')) {
          request.server_transaction.on('stateChanged', function () {
            if (request.server_transaction.state === Transactions.C.STATUS_ACCEPTED) {
              _this2._remote_target = request.parseHeader('contact').uri;
            }
          });
        }
      } else if (request.method === JsSIP_C.NOTIFY) {
        // RFC6665 3.2 Replace the dialog`s remote target URI if the request is accepted.
        if (request.hasHeader('contact')) {
          request.server_transaction.on('stateChanged', function () {
            if (request.server_transaction.state === Transactions.C.STATUS_COMPLETED) {
              _this2._remote_target = request.parseHeader('contact').uri;
            }
          });
        }
      }

      return true;
    }
  }, {
    key: "id",
    get: function get() {
      return this._id;
    }
  }, {
    key: "local_seqnum",
    get: function get() {
      return this._local_seqnum;
    },
    set: function set(num) {
      this._local_seqnum = num;
    }
  }, {
    key: "owner",
    get: function get() {
      return this._owner;
    }
  }, {
    key: "uac_pending_reply",
    get: function get() {
      return this._uac_pending_reply;
    },
    set: function set(pending) {
      this._uac_pending_reply = pending;
    }
  }, {
    key: "uas_pending_reply",
    get: function get() {
      return this._uas_pending_reply;
    }
  }]);

  return Dialog;
}();
},{"./Constants":2,"./Dialog/RequestSender":4,"./Logger":9,"./SIPMessage":21,"./Transactions":24,"./Utils":28}],4:[function(require,module,exports){
"use strict";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var JsSIP_C = require('../Constants');

var Transactions = require('../Transactions');

var RTCSession = require('../RTCSession');

var RequestSender = require('../RequestSender'); // Default event handlers.


var EventHandlers = {
  onRequestTimeout: function onRequestTimeout() {},
  onTransportError: function onTransportError() {},
  onSuccessResponse: function onSuccessResponse() {},
  onErrorResponse: function onErrorResponse() {},
  onAuthenticated: function onAuthenticated() {},
  onDialogError: function onDialogError() {}
};

module.exports = /*#__PURE__*/function () {
  function DialogRequestSender(dialog, request, eventHandlers) {
    _classCallCheck(this, DialogRequestSender);

    this._dialog = dialog;
    this._ua = dialog._ua;
    this._request = request;
    this._eventHandlers = eventHandlers; // RFC3261 14.1 Modifying an Existing Session. UAC Behavior.

    this._reattempt = false;
    this._reattemptTimer = null; // Define the undefined handlers.

    for (var handler in EventHandlers) {
      if (Object.prototype.hasOwnProperty.call(EventHandlers, handler)) {
        if (!this._eventHandlers[handler]) {
          this._eventHandlers[handler] = EventHandlers[handler];
        }
      }
    }
  }

  _createClass(DialogRequestSender, [{
    key: "send",
    value: function send() {
      var _this = this;

      var request_sender = new RequestSender(this._ua, this._request, {
        onRequestTimeout: function onRequestTimeout() {
          _this._eventHandlers.onRequestTimeout();
        },
        onTransportError: function onTransportError() {
          _this._eventHandlers.onTransportError();
        },
        onAuthenticated: function onAuthenticated(request) {
          _this._eventHandlers.onAuthenticated(request);
        },
        onReceiveResponse: function onReceiveResponse(response) {
          _this._receiveResponse(response);
        }
      });
      request_sender.send(); // RFC3261 14.2 Modifying an Existing Session -UAC BEHAVIOR-.

      if ((this._request.method === JsSIP_C.INVITE || this._request.method === JsSIP_C.UPDATE && this._request.body) && request_sender.clientTransaction.state !== Transactions.C.STATUS_TERMINATED) {
        this._dialog.uac_pending_reply = true;

        var stateChanged = function stateChanged() {
          if (request_sender.clientTransaction.state === Transactions.C.STATUS_ACCEPTED || request_sender.clientTransaction.state === Transactions.C.STATUS_COMPLETED || request_sender.clientTransaction.state === Transactions.C.STATUS_TERMINATED) {
            request_sender.clientTransaction.removeListener('stateChanged', stateChanged);
            _this._dialog.uac_pending_reply = false;
          }
        };

        request_sender.clientTransaction.on('stateChanged', stateChanged);
      }
    }
  }, {
    key: "_receiveResponse",
    value: function _receiveResponse(response) {
      var _this2 = this;

      // RFC3261 12.2.1.2 408 or 481 is received for a request within a dialog.
      if (response.status_code === 408 || response.status_code === 481) {
        this._eventHandlers.onDialogError(response);
      } else if (response.method === JsSIP_C.INVITE && response.status_code === 491) {
        if (this._reattempt) {
          if (response.status_code >= 200 && response.status_code < 300) {
            this._eventHandlers.onSuccessResponse(response);
          } else if (response.status_code >= 300) {
            this._eventHandlers.onErrorResponse(response);
          }
        } else {
          this._request.cseq = this._dialog.local_seqnum += 1;
          this._reattemptTimer = setTimeout(function () {
            // TODO: look at dialog state instead.
            if (_this2._dialog.owner.status !== RTCSession.C.STATUS_TERMINATED) {
              _this2._reattempt = true;

              _this2._request_sender.send();
            }
          }, 1000);
        }
      } else if (response.status_code >= 200 && response.status_code < 300) {
        this._eventHandlers.onSuccessResponse(response);
      } else if (response.status_code >= 300) {
        this._eventHandlers.onErrorResponse(response);
      }
    }
  }, {
    key: "request",
    get: function get() {
      return this._request;
    }
  }]);

  return DialogRequestSender;
}();
},{"../Constants":2,"../RTCSession":14,"../RequestSender":20,"../Transactions":24}],5:[function(require,module,exports){
"use strict";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var Logger = require('./Logger');

var Utils = require('./Utils');

var logger = new Logger('DigestAuthentication');

module.exports = /*#__PURE__*/function () {
  function DigestAuthentication(credentials) {
    _classCallCheck(this, DigestAuthentication);

    this._credentials = credentials;
    this._cnonce = null;
    this._nc = 0;
    this._ncHex = '00000000';
    this._algorithm = null;
    this._realm = null;
    this._nonce = null;
    this._opaque = null;
    this._stale = null;
    this._qop = null;
    this._method = null;
    this._uri = null;
    this._ha1 = null;
    this._response = null;
  }

  _createClass(DigestAuthentication, [{
    key: "get",
    value: function get(parameter) {
      switch (parameter) {
        case 'realm':
          return this._realm;

        case 'ha1':
          return this._ha1;

        default:
          logger.warn('get() | cannot get "%s" parameter', parameter);
          return undefined;
      }
    }
    /**
    * Performs Digest authentication given a SIP request and the challenge
    * received in a response to that request.
    * Returns true if auth was successfully generated, false otherwise.
    */

  }, {
    key: "authenticate",
    value: function authenticate(_ref, challenge)
    /* test interface */
    {
      var method = _ref.method,
          ruri = _ref.ruri,
          body = _ref.body;
      var cnonce = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
      this._algorithm = challenge.algorithm;
      this._realm = challenge.realm;
      this._nonce = challenge.nonce;
      this._opaque = challenge.opaque;
      this._stale = challenge.stale;

      if (this._algorithm) {
        if (this._algorithm !== 'MD5') {
          logger.warn('authenticate() | challenge with Digest algorithm different than "MD5", authentication aborted');
          return false;
        }
      } else {
        this._algorithm = 'MD5';
      }

      if (!this._nonce) {
        logger.warn('authenticate() | challenge without Digest nonce, authentication aborted');
        return false;
      }

      if (!this._realm) {
        logger.warn('authenticate() | challenge without Digest realm, authentication aborted');
        return false;
      } // If no plain SIP password is provided.


      if (!this._credentials.password) {
        // If ha1 is not provided we cannot authenticate.
        if (!this._credentials.ha1) {
          logger.warn('authenticate() | no plain SIP password nor ha1 provided, authentication aborted');
          return false;
        } // If the realm does not match the stored realm we cannot authenticate.


        if (this._credentials.realm !== this._realm) {
          logger.warn('authenticate() | no plain SIP password, and stored `realm` does not match the given `realm`, cannot authenticate [stored:"%s", given:"%s"]', this._credentials.realm, this._realm);
          return false;
        }
      } // 'qop' can contain a list of values (Array). Let's choose just one.


      if (challenge.qop) {
        if (challenge.qop.indexOf('auth-int') > -1) {
          this._qop = 'auth-int';
        } else if (challenge.qop.indexOf('auth') > -1) {
          this._qop = 'auth';
        } else {
          // Otherwise 'qop' is present but does not contain 'auth' or 'auth-int', so abort here.
          logger.warn('authenticate() | challenge without Digest qop different than "auth" or "auth-int", authentication aborted');
          return false;
        }
      } else {
        this._qop = null;
      } // Fill other attributes.


      this._method = method;
      this._uri = ruri;
      this._cnonce = cnonce || Utils.createRandomToken(12);
      this._nc += 1;
      var hex = Number(this._nc).toString(16);
      this._ncHex = '00000000'.substr(0, 8 - hex.length) + hex; // Nc-value = 8LHEX. Max value = 'FFFFFFFF'.

      if (this._nc === 4294967296) {
        this._nc = 1;
        this._ncHex = '00000001';
      } // Calculate the Digest "response" value.
      // If we have plain SIP password then regenerate ha1.


      if (this._credentials.password) {
        // HA1 = MD5(A1) = MD5(username:realm:password).
        this._ha1 = Utils.calculateMD5("".concat(this._credentials.username, ":").concat(this._realm, ":").concat(this._credentials.password));
      } // Otherwise reuse the stored ha1.
      else {
          this._ha1 = this._credentials.ha1;
        }

      var a2;
      var ha2;

      if (this._qop === 'auth') {
        // HA2 = MD5(A2) = MD5(method:digestURI).
        a2 = "".concat(this._method, ":").concat(this._uri);
        ha2 = Utils.calculateMD5(a2);
        logger.debug('authenticate() | using qop=auth [a2:"%s"]', a2); // Response = MD5(HA1:nonce:nonceCount:credentialsNonce:qop:HA2).

        this._response = Utils.calculateMD5("".concat(this._ha1, ":").concat(this._nonce, ":").concat(this._ncHex, ":").concat(this._cnonce, ":auth:").concat(ha2));
      } else if (this._qop === 'auth-int') {
        // HA2 = MD5(A2) = MD5(method:digestURI:MD5(entityBody)).
        a2 = "".concat(this._method, ":").concat(this._uri, ":").concat(Utils.calculateMD5(body ? body : ''));
        ha2 = Utils.calculateMD5(a2);
        logger.debug('authenticate() | using qop=auth-int [a2:"%s"]', a2); // Response = MD5(HA1:nonce:nonceCount:credentialsNonce:qop:HA2).

        this._response = Utils.calculateMD5("".concat(this._ha1, ":").concat(this._nonce, ":").concat(this._ncHex, ":").concat(this._cnonce, ":auth-int:").concat(ha2));
      } else if (this._qop === null) {
        // HA2 = MD5(A2) = MD5(method:digestURI).
        a2 = "".concat(this._method, ":").concat(this._uri);
        ha2 = Utils.calculateMD5(a2);
        logger.debug('authenticate() | using qop=null [a2:"%s"]', a2); // Response = MD5(HA1:nonce:HA2).

        this._response = Utils.calculateMD5("".concat(this._ha1, ":").concat(this._nonce, ":").concat(ha2));
      }

      logger.debug('authenticate() | response generated');
      return true;
    }
    /**
    * Return the Proxy-Authorization or WWW-Authorization header value.
    */

  }, {
    key: "toString",
    value: function toString() {
      var auth_params = [];

      if (!this._response) {
        throw new Error('response field does not exist, cannot generate Authorization header');
      }

      auth_params.push("algorithm=".concat(this._algorithm));
      auth_params.push("username=\"".concat(this._credentials.username, "\""));
      auth_params.push("realm=\"".concat(this._realm, "\""));
      auth_params.push("nonce=\"".concat(this._nonce, "\""));
      auth_params.push("uri=\"".concat(this._uri, "\""));
      auth_params.push("response=\"".concat(this._response, "\""));

      if (this._opaque) {
        auth_params.push("opaque=\"".concat(this._opaque, "\""));
      }

      if (this._qop) {
        auth_params.push("qop=".concat(this._qop));
        auth_params.push("cnonce=\"".concat(this._cnonce, "\""));
        auth_params.push("nc=".concat(this._ncHex));
      }

      return "Digest ".concat(auth_params.join(', '));
    }
  }]);

  return DigestAuthentication;
}();
},{"./Logger":9,"./Utils":28}],6:[function(require,module,exports){
"use strict";

function _typeof(obj) { "@babel/helpers - typeof"; 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); }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }

function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

function _wrapNativeSuper(Class) { var _cache = typeof Map === "function" ? new Map() : undefined; _wrapNativeSuper = function _wrapNativeSuper(Class) { if (Class === null || !_isNativeFunction(Class)) return Class; if (typeof Class !== "function") { throw new TypeError("Super expression must either be null or a function"); } if (typeof _cache !== "undefined") { if (_cache.has(Class)) return _cache.get(Class); _cache.set(Class, Wrapper); } function Wrapper() { return _construct(Class, arguments, _getPrototypeOf(this).constructor); } Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }); return _setPrototypeOf(Wrapper, Class); }; return _wrapNativeSuper(Class); }

function _construct(Parent, args, Class) { if (_isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); }

function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }

function _isNativeFunction(fn) { return Function.toString.call(fn).indexOf("[native code]") !== -1; }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

var ConfigurationError = /*#__PURE__*/function (_Error) {
  _inherits(ConfigurationError, _Error);

  var _super = _createSuper(ConfigurationError);

  function ConfigurationError(parameter, value) {
    var _this;

    _classCallCheck(this, ConfigurationError);

    _this = _super.call(this);
    _this.code = 1;
    _this.name = 'CONFIGURATION_ERROR';
    _this.parameter = parameter;
    _this.value = value;
    _this.message = !_this.value ? "Missing parameter: ".concat(_this.parameter) : "Invalid value ".concat(JSON.stringify(_this.value), " for parameter \"").concat(_this.parameter, "\"");
    return _this;
  }

  return ConfigurationError;
}( /*#__PURE__*/_wrapNativeSuper(Error));

var InvalidStateError = /*#__PURE__*/function (_Error2) {
  _inherits(InvalidStateError, _Error2);

  var _super2 = _createSuper(InvalidStateError);

  function InvalidStateError(status) {
    var _this2;

    _classCallCheck(this, InvalidStateError);

    _this2 = _super2.call(this);
    _this2.code = 2;
    _this2.name = 'INVALID_STATE_ERROR';
    _this2.status = status;
    _this2.message = "Invalid status: ".concat(status);
    return _this2;
  }

  return InvalidStateError;
}( /*#__PURE__*/_wrapNativeSuper(Error));

var NotSupportedError = /*#__PURE__*/function (_Error3) {
  _inherits(NotSupportedError, _Error3);

  var _super3 = _createSuper(NotSupportedError);

  function NotSupportedError(message) {
    var _this3;

    _classCallCheck(this, NotSupportedError);

    _this3 = _super3.call(this);
    _this3.code = 3;
    _this3.name = 'NOT_SUPPORTED_ERROR';
    _this3.message = message;
    return _this3;
  }

  return NotSupportedError;
}( /*#__PURE__*/_wrapNativeSuper(Error));

var NotReadyError = /*#__PURE__*/function (_Error4) {
  _inherits(NotReadyError, _Error4);

  var _super4 = _createSuper(NotReadyError);

  function NotReadyError(message) {
    var _this4;

    _classCallCheck(this, NotReadyError);

    _this4 = _super4.call(this);
    _this4.code = 4;
    _this4.name = 'NOT_READY_ERROR';
    _this4.message = message;
    return _this4;
  }

  return NotReadyError;
}( /*#__PURE__*/_wrapNativeSuper(Error));

module.exports = {
  ConfigurationError: ConfigurationError,
  InvalidStateError: InvalidStateError,
  NotSupportedError: NotSupportedError,
  NotReadyError: NotReadyError
};
},{}],7:[function(require,module,exports){
"use strict";

module.exports = function () {
  /*
   * Generated by PEG.js 0.7.0.
   *
   * http://pegjs.majda.cz/
   */
  function quote(s) {
    /*
     * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a
     * string literal except for the closing quote character, backslash,
     * carriage return, line separator, paragraph separator, and line feed.
     * Any character may appear in the form of an escape sequence.
     *
     * For portability, we also escape escape all control and non-ASCII
     * characters. Note that "\0" and "\v" escape sequences are not used
     * because JSHint does not like the first and IE the second.
     */
    return '"' + s.replace(/\\/g, '\\\\') // backslash
    .replace(/"/g, '\\"') // closing quote character
    .replace(/\x08/g, '\\b') // backspace
    .replace(/\t/g, '\\t') // horizontal tab
    .replace(/\n/g, '\\n') // line feed
    .replace(/\f/g, '\\f') // form feed
    .replace(/\r/g, '\\r') // carriage return
    .replace(/[\x00-\x07\x0B\x0E-\x1F\x80-\uFFFF]/g, escape) + '"';
  }

  var result = {
    /*
     * Parses the input with a generated parser. If the parsing is successfull,
     * returns a value explicitly or implicitly specified by the grammar from
     * which the parser was generated (see |PEG.buildParser|). If the parsing is
     * unsuccessful, throws |PEG.parser.SyntaxError| describing the error.
     */
    parse: function parse(input, startRule) {
      var parseFunctions = {
        "CRLF": parse_CRLF,
        "DIGIT": parse_DIGIT,
        "ALPHA": parse_ALPHA,
        "HEXDIG": parse_HEXDIG,
        "WSP": parse_WSP,
        "OCTET": parse_OCTET,
        "DQUOTE": parse_DQUOTE,
        "SP": parse_SP,
        "HTAB": parse_HTAB,
        "alphanum": parse_alphanum,
        "reserved": parse_reserved,
        "unreserved": parse_unreserved,
        "mark": parse_mark,
        "escaped": parse_escaped,
        "LWS": parse_LWS,
        "SWS": parse_SWS,
        "HCOLON": parse_HCOLON,
        "TEXT_UTF8_TRIM": parse_TEXT_UTF8_TRIM,
        "TEXT_UTF8char": parse_TEXT_UTF8char,
        "UTF8_NONASCII": parse_UTF8_NONASCII,
        "UTF8_CONT": parse_UTF8_CONT,
        "LHEX": parse_LHEX,
        "token": parse_token,
        "token_nodot": parse_token_nodot,
        "separators": parse_separators,
        "word": parse_word,
        "STAR": parse_STAR,
        "SLASH": parse_SLASH,
        "EQUAL": parse_EQUAL,
        "LPAREN": parse_LPAREN,
        "RPAREN": parse_RPAREN,
        "RAQUOT": parse_RAQUOT,
        "LAQUOT": parse_LAQUOT,
        "COMMA": parse_COMMA,
        "SEMI": parse_SEMI,
        "COLON": parse_COLON,
        "LDQUOT": parse_LDQUOT,
        "RDQUOT": parse_RDQUOT,
        "comment": parse_comment,
        "ctext": parse_ctext,
        "quoted_string": parse_quoted_string,
        "quoted_string_clean": parse_quoted_string_clean,
        "qdtext": parse_qdtext,
        "quoted_pair": parse_quoted_pair,
        "SIP_URI_noparams": parse_SIP_URI_noparams,
        "SIP_URI": parse_SIP_URI,
        "uri_scheme": parse_uri_scheme,
        "uri_scheme_sips": parse_uri_scheme_sips,
        "uri_scheme_sip": parse_uri_scheme_sip,
        "userinfo": parse_userinfo,
        "user": parse_user,
        "user_unreserved": parse_user_unreserved,
        "password": parse_password,
        "hostport": parse_hostport,
        "host": parse_host,
        "hostname": parse_hostname,
        "domainlabel": parse_domainlabel,
        "toplabel": parse_toplabel,
        "IPv6reference": parse_IPv6reference,
        "IPv6address": parse_IPv6address,
        "h16": parse_h16,
        "ls32": parse_ls32,
        "IPv4address": parse_IPv4address,
        "dec_octet": parse_dec_octet,
        "port": parse_port,
        "uri_parameters": parse_uri_parameters,
        "uri_parameter": parse_uri_parameter,
        "transport_param": parse_transport_param,
        "user_param": parse_user_param,
        "method_param": parse_method_param,
        "ttl_param": parse_ttl_param,
        "maddr_param": parse_maddr_param,
        "lr_param": parse_lr_param,
        "other_param": parse_other_param,
        "pname": parse_pname,
        "pvalue": parse_pvalue,
        "paramchar": parse_paramchar,
        "param_unreserved": parse_param_unreserved,
        "headers": parse_headers,
        "header": parse_header,
        "hname": parse_hname,
        "hvalue": parse_hvalue,
        "hnv_unreserved": parse_hnv_unreserved,
        "Request_Response": parse_Request_Response,
        "Request_Line": parse_Request_Line,
        "Request_URI": parse_Request_URI,
        "absoluteURI": parse_absoluteURI,
        "hier_part": parse_hier_part,
        "net_path": parse_net_path,
        "abs_path": parse_abs_path,
        "opaque_part": parse_opaque_part,
        "uric": parse_uric,
        "uric_no_slash": parse_uric_no_slash,
        "path_segments": parse_path_segments,
        "segment": parse_segment,
        "param": parse_param,
        "pchar": parse_pchar,
        "scheme": parse_scheme,
        "authority": parse_authority,
        "srvr": parse_srvr,
        "reg_name": parse_reg_name,
        "query": parse_query,
        "SIP_Version": parse_SIP_Version,
        "INVITEm": parse_INVITEm,
        "ACKm": parse_ACKm,
        "OPTIONSm": parse_OPTIONSm,
        "BYEm": parse_BYEm,
        "CANCELm": parse_CANCELm,
        "REGISTERm": parse_REGISTERm,
        "SUBSCRIBEm": parse_SUBSCRIBEm,
        "NOTIFYm": parse_NOTIFYm,
        "REFERm": parse_REFERm,
        "Method": parse_Method,
        "Status_Line": parse_Status_Line,
        "Status_Code": parse_Status_Code,
        "extension_code": parse_extension_code,
        "Reason_Phrase": parse_Reason_Phrase,
        "Allow_Events": parse_Allow_Events,
        "Call_ID": parse_Call_ID,
        "Contact": parse_Contact,
        "contact_param": parse_contact_param,
        "name_addr": parse_name_addr,
        "display_name": parse_display_name,
        "contact_params": parse_contact_params,
        "c_p_q": parse_c_p_q,
        "c_p_expires": parse_c_p_expires,
        "delta_seconds": parse_delta_seconds,
        "qvalue": parse_qvalue,
        "generic_param": parse_generic_param,
        "gen_value": parse_gen_value,
        "Content_Disposition": parse_Content_Disposition,
        "disp_type": parse_disp_type,
        "disp_param": parse_disp_param,
        "handling_param": parse_handling_param,
        "Content_Encoding": parse_Content_Encoding,
        "Content_Length": parse_Content_Length,
        "Content_Type": parse_Content_Type,
        "media_type": parse_media_type,
        "m_type": parse_m_type,
        "discrete_type": parse_discrete_type,
        "composite_type": parse_composite_type,
        "extension_token": parse_extension_token,
        "x_token": parse_x_token,
        "m_subtype": parse_m_subtype,
        "m_parameter": parse_m_parameter,
        "m_value": parse_m_value,
        "CSeq": parse_CSeq,
        "CSeq_value": parse_CSeq_value,
        "Expires": parse_Expires,
        "Event": parse_Event,
        "event_type": parse_event_type,
        "From": parse_From,
        "from_param": parse_from_param,
        "tag_param": parse_tag_param,
        "Max_Forwards": parse_Max_Forwards,
        "Min_Expires": parse_Min_Expires,
        "Name_Addr_Header": parse_Name_Addr_Header,
        "Proxy_Authenticate": parse_Proxy_Authenticate,
        "challenge": parse_challenge,
        "other_challenge": parse_other_challenge,
        "auth_param": parse_auth_param,
        "digest_cln": parse_digest_cln,
        "realm": parse_realm,
        "realm_value": parse_realm_value,
        "domain": parse_domain,
        "URI": parse_URI,
        "nonce": parse_nonce,
        "nonce_value": parse_nonce_value,
        "opaque": parse_opaque,
        "stale": parse_stale,
        "algorithm": parse_algorithm,
        "qop_options": parse_qop_options,
        "qop_value": parse_qop_value,
        "Proxy_Require": parse_Proxy_Require,
        "Record_Route": parse_Record_Route,
        "rec_route": parse_rec_route,
        "Reason": parse_Reason,
        "reason_param": parse_reason_param,
        "reason_cause": parse_reason_cause,
        "Require": parse_Require,
        "Route": parse_Route,
        "route_param": parse_route_param,
        "Subscription_State": parse_Subscription_State,
        "substate_value": parse_substate_value,
        "subexp_params": parse_subexp_params,
        "event_reason_value": parse_event_reason_value,
        "Subject": parse_Subject,
        "Supported": parse_Supported,
        "To": parse_To,
        "to_param": parse_to_param,
        "Via": parse_Via,
        "via_param": parse_via_param,
        "via_params": parse_via_params,
        "via_ttl": parse_via_ttl,
        "via_maddr": parse_via_maddr,
        "via_received": parse_via_received,
        "via_branch": parse_via_branch,
        "response_port": parse_response_port,
        "rport": parse_rport,
        "sent_protocol": parse_sent_protocol,
        "protocol_name": parse_protocol_name,
        "transport": parse_transport,
        "sent_by": parse_sent_by,
        "via_host": parse_via_host,
        "via_port": parse_via_port,
        "ttl": parse_ttl,
        "WWW_Authenticate": parse_WWW_Authenticate,
        "Session_Expires": parse_Session_Expires,
        "s_e_expires": parse_s_e_expires,
        "s_e_params": parse_s_e_params,
        "s_e_refresher": parse_s_e_refresher,
        "extension_header": parse_extension_header,
        "header_value": parse_header_value,
        "message_body": parse_message_body,
        "uuid_URI": parse_uuid_URI,
        "uuid": parse_uuid,
        "hex4": parse_hex4,
        "hex8": parse_hex8,
        "hex12": parse_hex12,
        "Refer_To": parse_Refer_To,
        "Replaces": parse_Replaces,
        "call_id": parse_call_id,
        "replaces_param": parse_replaces_param,
        "to_tag": parse_to_tag,
        "from_tag": parse_from_tag,
        "early_flag": parse_early_flag
      };

      if (startRule !== undefined) {
        if (parseFunctions[startRule] === undefined) {
          throw new Error("Invalid rule name: " + quote(startRule) + ".");
        }
      } else {
        startRule = "CRLF";
      }

      var pos = 0;
      var reportFailures = 0;
      var rightmostFailuresPos = 0;
      var rightmostFailuresExpected = [];

      function padLeft(input, padding, length) {
        var result = input;
        var padLength = length - input.length;

        for (var i = 0; i < padLength; i++) {
          result = padding + result;
        }

        return result;
      }

      function escape(ch) {
        var charCode = ch.charCodeAt(0);
        var escapeChar;
        var length;

        if (charCode <= 0xFF) {
          escapeChar = 'x';
          length = 2;
        } else {
          escapeChar = 'u';
          length = 4;
        }

        return '\\' + escapeChar + padLeft(charCode.toString(16).toUpperCase(), '0', length);
      }

      function matchFailed(failure) {
        if (pos < rightmostFailuresPos) {
          return;
        }

        if (pos > rightmostFailuresPos) {
          rightmostFailuresPos = pos;
          rightmostFailuresExpected = [];
        }

        rightmostFailuresExpected.push(failure);
      }

      function parse_CRLF() {
        var result0;

        if (input.substr(pos, 2) === "\r\n") {
          result0 = "\r\n";
          pos += 2;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"\\r\\n\"");
          }
        }

        return result0;
      }

      function parse_DIGIT() {
        var result0;

        if (/^[0-9]/.test(input.charAt(pos))) {
          result0 = input.charAt(pos);
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("[0-9]");
          }
        }

        return result0;
      }

      function parse_ALPHA() {
        var result0;

        if (/^[a-zA-Z]/.test(input.charAt(pos))) {
          result0 = input.charAt(pos);
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("[a-zA-Z]");
          }
        }

        return result0;
      }

      function parse_HEXDIG() {
        var result0;

        if (/^[0-9a-fA-F]/.test(input.charAt(pos))) {
          result0 = input.charAt(pos);
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("[0-9a-fA-F]");
          }
        }

        return result0;
      }

      function parse_WSP() {
        var result0;
        result0 = parse_SP();

        if (result0 === null) {
          result0 = parse_HTAB();
        }

        return result0;
      }

      function parse_OCTET() {
        var result0;

        if (/^[\0-\xFF]/.test(input.charAt(pos))) {
          result0 = input.charAt(pos);
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("[\\0-\\xFF]");
          }
        }

        return result0;
      }

      function parse_DQUOTE() {
        var result0;

        if (/^["]/.test(input.charAt(pos))) {
          result0 = input.charAt(pos);
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("[\"]");
          }
        }

        return result0;
      }

      function parse_SP() {
        var result0;

        if (input.charCodeAt(pos) === 32) {
          result0 = " ";
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\" \"");
          }
        }

        return result0;
      }

      function parse_HTAB() {
        var result0;

        if (input.charCodeAt(pos) === 9) {
          result0 = "\t";
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"\\t\"");
          }
        }

        return result0;
      }

      function parse_alphanum() {
        var result0;

        if (/^[a-zA-Z0-9]/.test(input.charAt(pos))) {
          result0 = input.charAt(pos);
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("[a-zA-Z0-9]");
          }
        }

        return result0;
      }

      function parse_reserved() {
        var result0;

        if (input.charCodeAt(pos) === 59) {
          result0 = ";";
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\";\"");
          }
        }

        if (result0 === null) {
          if (input.charCodeAt(pos) === 47) {
            result0 = "/";
            pos++;
          } else {
            result0 = null;

            if (reportFailures === 0) {
              matchFailed("\"/\"");
            }
          }

          if (result0 === null) {
            if (input.charCodeAt(pos) === 63) {
              result0 = "?";
              pos++;
            } else {
              result0 = null;

              if (reportFailures === 0) {
                matchFailed("\"?\"");
              }
            }

            if (result0 === null) {
              if (input.charCodeAt(pos) === 58) {
                result0 = ":";
                pos++;
              } else {
                result0 = null;

                if (reportFailures === 0) {
                  matchFailed("\":\"");
                }
              }

              if (result0 === null) {
                if (input.charCodeAt(pos) === 64) {
                  result0 = "@";
                  pos++;
                } else {
                  result0 = null;

                  if (reportFailures === 0) {
                    matchFailed("\"@\"");
                  }
                }

                if (result0 === null) {
                  if (input.charCodeAt(pos) === 38) {
                    result0 = "&";
                    pos++;
                  } else {
                    result0 = null;

                    if (reportFailures === 0) {
                      matchFailed("\"&\"");
                    }
                  }

                  if (result0 === null) {
                    if (input.charCodeAt(pos) === 61) {
                      result0 = "=";
                      pos++;
                    } else {
                      result0 = null;

                      if (reportFailures === 0) {
                        matchFailed("\"=\"");
                      }
                    }

                    if (result0 === null) {
                      if (input.charCodeAt(pos) === 43) {
                        result0 = "+";
                        pos++;
                      } else {
                        result0 = null;

                        if (reportFailures === 0) {
                          matchFailed("\"+\"");
                        }
                      }

                      if (result0 === null) {
                        if (input.charCodeAt(pos) === 36) {
                          result0 = "$";
                          pos++;
                        } else {
                          result0 = null;

                          if (reportFailures === 0) {
                            matchFailed("\"$\"");
                          }
                        }

                        if (result0 === null) {
                          if (input.charCodeAt(pos) === 44) {
                            result0 = ",";
                            pos++;
                          } else {
                            result0 = null;

                            if (reportFailures === 0) {
                              matchFailed("\",\"");
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }

        return result0;
      }

      function parse_unreserved() {
        var result0;
        result0 = parse_alphanum();

        if (result0 === null) {
          result0 = parse_mark();
        }

        return result0;
      }

      function parse_mark() {
        var result0;

        if (input.charCodeAt(pos) === 45) {
          result0 = "-";
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"-\"");
          }
        }

        if (result0 === null) {
          if (input.charCodeAt(pos) === 95) {
            result0 = "_";
            pos++;
          } else {
            result0 = null;

            if (reportFailures === 0) {
              matchFailed("\"_\"");
            }
          }

          if (result0 === null) {
            if (input.charCodeAt(pos) === 46) {
              result0 = ".";
              pos++;
            } else {
              result0 = null;

              if (reportFailures === 0) {
                matchFailed("\".\"");
              }
            }

            if (result0 === null) {
              if (input.charCodeAt(pos) === 33) {
                result0 = "!";
                pos++;
              } else {
                result0 = null;

                if (reportFailures === 0) {
                  matchFailed("\"!\"");
                }
              }

              if (result0 === null) {
                if (input.charCodeAt(pos) === 126) {
                  result0 = "~";
                  pos++;
                } else {
                  result0 = null;

                  if (reportFailures === 0) {
                    matchFailed("\"~\"");
                  }
                }

                if (result0 === null) {
                  if (input.charCodeAt(pos) === 42) {
                    result0 = "*";
                    pos++;
                  } else {
                    result0 = null;

                    if (reportFailures === 0) {
                      matchFailed("\"*\"");
                    }
                  }

                  if (result0 === null) {
                    if (input.charCodeAt(pos) === 39) {
                      result0 = "'";
                      pos++;
                    } else {
                      result0 = null;

                      if (reportFailures === 0) {
                        matchFailed("\"'\"");
                      }
                    }

                    if (result0 === null) {
                      if (input.charCodeAt(pos) === 40) {
                        result0 = "(";
                        pos++;
                      } else {
                        result0 = null;

                        if (reportFailures === 0) {
                          matchFailed("\"(\"");
                        }
                      }

                      if (result0 === null) {
                        if (input.charCodeAt(pos) === 41) {
                          result0 = ")";
                          pos++;
                        } else {
                          result0 = null;

                          if (reportFailures === 0) {
                            matchFailed("\")\"");
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }

        return result0;
      }

      function parse_escaped() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.charCodeAt(pos) === 37) {
          result0 = "%";
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"%\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_HEXDIG();

          if (result1 !== null) {
            result2 = parse_HEXDIG();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, escaped) {
            return escaped.join('');
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_LWS() {
        var result0, result1, result2;
        var pos0, pos1, pos2;
        pos0 = pos;
        pos1 = pos;
        pos2 = pos;
        result0 = [];
        result1 = parse_WSP();

        while (result1 !== null) {
          result0.push(result1);
          result1 = parse_WSP();
        }

        if (result0 !== null) {
          result1 = parse_CRLF();

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos2;
          }
        } else {
          result0 = null;
          pos = pos2;
        }

        result0 = result0 !== null ? result0 : "";

        if (result0 !== null) {
          result2 = parse_WSP();

          if (result2 !== null) {
            result1 = [];

            while (result2 !== null) {
              result1.push(result2);
              result2 = parse_WSP();
            }
          } else {
            result1 = null;
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            return " ";
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_SWS() {
        var result0;
        result0 = parse_LWS();
        result0 = result0 !== null ? result0 : "";
        return result0;
      }

      function parse_HCOLON() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = [];
        result1 = parse_SP();

        if (result1 === null) {
          result1 = parse_HTAB();
        }

        while (result1 !== null) {
          result0.push(result1);
          result1 = parse_SP();

          if (result1 === null) {
            result1 = parse_HTAB();
          }
        }

        if (result0 !== null) {
          if (input.charCodeAt(pos) === 58) {
            result1 = ":";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\":\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_SWS();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            return ':';
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_TEXT_UTF8_TRIM() {
        var result0, result1, result2, result3;
        var pos0, pos1, pos2;
        pos0 = pos;
        pos1 = pos;
        result1 = parse_TEXT_UTF8char();

        if (result1 !== null) {
          result0 = [];

          while (result1 !== null) {
            result0.push(result1);
            result1 = parse_TEXT_UTF8char();
          }
        } else {
          result0 = null;
        }

        if (result0 !== null) {
          result1 = [];
          pos2 = pos;
          result2 = [];
          result3 = parse_LWS();

          while (result3 !== null) {
            result2.push(result3);
            result3 = parse_LWS();
          }

          if (result2 !== null) {
            result3 = parse_TEXT_UTF8char();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos2;
            }
          } else {
            result2 = null;
            pos = pos2;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos2 = pos;
            result2 = [];
            result3 = parse_LWS();

            while (result3 !== null) {
              result2.push(result3);
              result3 = parse_LWS();
            }

            if (result2 !== null) {
              result3 = parse_TEXT_UTF8char();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos2;
              }
            } else {
              result2 = null;
              pos = pos2;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            return input.substring(pos, offset);
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_TEXT_UTF8char() {
        var result0;

        if (/^[!-~]/.test(input.charAt(pos))) {
          result0 = input.charAt(pos);
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("[!-~]");
          }
        }

        if (result0 === null) {
          result0 = parse_UTF8_NONASCII();
        }

        return result0;
      }

      function parse_UTF8_NONASCII() {
        var result0;

        if (/^[\x80-\uFFFF]/.test(input.charAt(pos))) {
          result0 = input.charAt(pos);
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("[\\x80-\\uFFFF]");
          }
        }

        return result0;
      }

      function parse_UTF8_CONT() {
        var result0;

        if (/^[\x80-\xBF]/.test(input.charAt(pos))) {
          result0 = input.charAt(pos);
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("[\\x80-\\xBF]");
          }
        }

        return result0;
      }

      function parse_LHEX() {
        var result0;
        result0 = parse_DIGIT();

        if (result0 === null) {
          if (/^[a-f]/.test(input.charAt(pos))) {
            result0 = input.charAt(pos);
            pos++;
          } else {
            result0 = null;

            if (reportFailures === 0) {
              matchFailed("[a-f]");
            }
          }
        }

        return result0;
      }

      function parse_token() {
        var result0, result1;
        var pos0;
        pos0 = pos;
        result1 = parse_alphanum();

        if (result1 === null) {
          if (input.charCodeAt(pos) === 45) {
            result1 = "-";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\"-\"");
            }
          }

          if (result1 === null) {
            if (input.charCodeAt(pos) === 46) {
              result1 = ".";
              pos++;
            } else {
              result1 = null;

              if (reportFailures === 0) {
                matchFailed("\".\"");
              }
            }

            if (result1 === null) {
              if (input.charCodeAt(pos) === 33) {
                result1 = "!";
                pos++;
              } else {
                result1 = null;

                if (reportFailures === 0) {
                  matchFailed("\"!\"");
                }
              }

              if (result1 === null) {
                if (input.charCodeAt(pos) === 37) {
                  result1 = "%";
                  pos++;
                } else {
                  result1 = null;

                  if (reportFailures === 0) {
                    matchFailed("\"%\"");
                  }
                }

                if (result1 === null) {
                  if (input.charCodeAt(pos) === 42) {
                    result1 = "*";
                    pos++;
                  } else {
                    result1 = null;

                    if (reportFailures === 0) {
                      matchFailed("\"*\"");
                    }
                  }

                  if (result1 === null) {
                    if (input.charCodeAt(pos) === 95) {
                      result1 = "_";
                      pos++;
                    } else {
                      result1 = null;

                      if (reportFailures === 0) {
                        matchFailed("\"_\"");
                      }
                    }

                    if (result1 === null) {
                      if (input.charCodeAt(pos) === 43) {
                        result1 = "+";
                        pos++;
                      } else {
                        result1 = null;

                        if (reportFailures === 0) {
                          matchFailed("\"+\"");
                        }
                      }

                      if (result1 === null) {
                        if (input.charCodeAt(pos) === 96) {
                          result1 = "`";
                          pos++;
                        } else {
                          result1 = null;

                          if (reportFailures === 0) {
                            matchFailed("\"`\"");
                          }
                        }

                        if (result1 === null) {
                          if (input.charCodeAt(pos) === 39) {
                            result1 = "'";
                            pos++;
                          } else {
                            result1 = null;

                            if (reportFailures === 0) {
                              matchFailed("\"'\"");
                            }
                          }

                          if (result1 === null) {
                            if (input.charCodeAt(pos) === 126) {
                              result1 = "~";
                              pos++;
                            } else {
                              result1 = null;

                              if (reportFailures === 0) {
                                matchFailed("\"~\"");
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }

        if (result1 !== null) {
          result0 = [];

          while (result1 !== null) {
            result0.push(result1);
            result1 = parse_alphanum();

            if (result1 === null) {
              if (input.charCodeAt(pos) === 45) {
                result1 = "-";
                pos++;
              } else {
                result1 = null;

                if (reportFailures === 0) {
                  matchFailed("\"-\"");
                }
              }

              if (result1 === null) {
                if (input.charCodeAt(pos) === 46) {
                  result1 = ".";
                  pos++;
                } else {
                  result1 = null;

                  if (reportFailures === 0) {
                    matchFailed("\".\"");
                  }
                }

                if (result1 === null) {
                  if (input.charCodeAt(pos) === 33) {
                    result1 = "!";
                    pos++;
                  } else {
                    result1 = null;

                    if (reportFailures === 0) {
                      matchFailed("\"!\"");
                    }
                  }

                  if (result1 === null) {
                    if (input.charCodeAt(pos) === 37) {
                      result1 = "%";
                      pos++;
                    } else {
                      result1 = null;

                      if (reportFailures === 0) {
                        matchFailed("\"%\"");
                      }
                    }

                    if (result1 === null) {
                      if (input.charCodeAt(pos) === 42) {
                        result1 = "*";
                        pos++;
                      } else {
                        result1 = null;

                        if (reportFailures === 0) {
                          matchFailed("\"*\"");
                        }
                      }

                      if (result1 === null) {
                        if (input.charCodeAt(pos) === 95) {
                          result1 = "_";
                          pos++;
                        } else {
                          result1 = null;

                          if (reportFailures === 0) {
                            matchFailed("\"_\"");
                          }
                        }

                        if (result1 === null) {
                          if (input.charCodeAt(pos) === 43) {
                            result1 = "+";
                            pos++;
                          } else {
                            result1 = null;

                            if (reportFailures === 0) {
                              matchFailed("\"+\"");
                            }
                          }

                          if (result1 === null) {
                            if (input.charCodeAt(pos) === 96) {
                              result1 = "`";
                              pos++;
                            } else {
                              result1 = null;

                              if (reportFailures === 0) {
                                matchFailed("\"`\"");
                              }
                            }

                            if (result1 === null) {
                              if (input.charCodeAt(pos) === 39) {
                                result1 = "'";
                                pos++;
                              } else {
                                result1 = null;

                                if (reportFailures === 0) {
                                  matchFailed("\"'\"");
                                }
                              }

                              if (result1 === null) {
                                if (input.charCodeAt(pos) === 126) {
                                  result1 = "~";
                                  pos++;
                                } else {
                                  result1 = null;

                                  if (reportFailures === 0) {
                                    matchFailed("\"~\"");
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        } else {
          result0 = null;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            return input.substring(pos, offset);
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_token_nodot() {
        var result0, result1;
        var pos0;
        pos0 = pos;
        result1 = parse_alphanum();

        if (result1 === null) {
          if (input.charCodeAt(pos) === 45) {
            result1 = "-";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\"-\"");
            }
          }

          if (result1 === null) {
            if (input.charCodeAt(pos) === 33) {
              result1 = "!";
              pos++;
            } else {
              result1 = null;

              if (reportFailures === 0) {
                matchFailed("\"!\"");
              }
            }

            if (result1 === null) {
              if (input.charCodeAt(pos) === 37) {
                result1 = "%";
                pos++;
              } else {
                result1 = null;

                if (reportFailures === 0) {
                  matchFailed("\"%\"");
                }
              }

              if (result1 === null) {
                if (input.charCodeAt(pos) === 42) {
                  result1 = "*";
                  pos++;
                } else {
                  result1 = null;

                  if (reportFailures === 0) {
                    matchFailed("\"*\"");
                  }
                }

                if (result1 === null) {
                  if (input.charCodeAt(pos) === 95) {
                    result1 = "_";
                    pos++;
                  } else {
                    result1 = null;

                    if (reportFailures === 0) {
                      matchFailed("\"_\"");
                    }
                  }

                  if (result1 === null) {
                    if (input.charCodeAt(pos) === 43) {
                      result1 = "+";
                      pos++;
                    } else {
                      result1 = null;

                      if (reportFailures === 0) {
                        matchFailed("\"+\"");
                      }
                    }

                    if (result1 === null) {
                      if (input.charCodeAt(pos) === 96) {
                        result1 = "`";
                        pos++;
                      } else {
                        result1 = null;

                        if (reportFailures === 0) {
                          matchFailed("\"`\"");
                        }
                      }

                      if (result1 === null) {
                        if (input.charCodeAt(pos) === 39) {
                          result1 = "'";
                          pos++;
                        } else {
                          result1 = null;

                          if (reportFailures === 0) {
                            matchFailed("\"'\"");
                          }
                        }

                        if (result1 === null) {
                          if (input.charCodeAt(pos) === 126) {
                            result1 = "~";
                            pos++;
                          } else {
                            result1 = null;

                            if (reportFailures === 0) {
                              matchFailed("\"~\"");
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }

        if (result1 !== null) {
          result0 = [];

          while (result1 !== null) {
            result0.push(result1);
            result1 = parse_alphanum();

            if (result1 === null) {
              if (input.charCodeAt(pos) === 45) {
                result1 = "-";
                pos++;
              } else {
                result1 = null;

                if (reportFailures === 0) {
                  matchFailed("\"-\"");
                }
              }

              if (result1 === null) {
                if (input.charCodeAt(pos) === 33) {
                  result1 = "!";
                  pos++;
                } else {
                  result1 = null;

                  if (reportFailures === 0) {
                    matchFailed("\"!\"");
                  }
                }

                if (result1 === null) {
                  if (input.charCodeAt(pos) === 37) {
                    result1 = "%";
                    pos++;
                  } else {
                    result1 = null;

                    if (reportFailures === 0) {
                      matchFailed("\"%\"");
                    }
                  }

                  if (result1 === null) {
                    if (input.charCodeAt(pos) === 42) {
                      result1 = "*";
                      pos++;
                    } else {
                      result1 = null;

                      if (reportFailures === 0) {
                        matchFailed("\"*\"");
                      }
                    }

                    if (result1 === null) {
                      if (input.charCodeAt(pos) === 95) {
                        result1 = "_";
                        pos++;
                      } else {
                        result1 = null;

                        if (reportFailures === 0) {
                          matchFailed("\"_\"");
                        }
                      }

                      if (result1 === null) {
                        if (input.charCodeAt(pos) === 43) {
                          result1 = "+";
                          pos++;
                        } else {
                          result1 = null;

                          if (reportFailures === 0) {
                            matchFailed("\"+\"");
                          }
                        }

                        if (result1 === null) {
                          if (input.charCodeAt(pos) === 96) {
                            result1 = "`";
                            pos++;
                          } else {
                            result1 = null;

                            if (reportFailures === 0) {
                              matchFailed("\"`\"");
                            }
                          }

                          if (result1 === null) {
                            if (input.charCodeAt(pos) === 39) {
                              result1 = "'";
                              pos++;
                            } else {
                              result1 = null;

                              if (reportFailures === 0) {
                                matchFailed("\"'\"");
                              }
                            }

                            if (result1 === null) {
                              if (input.charCodeAt(pos) === 126) {
                                result1 = "~";
                                pos++;
                              } else {
                                result1 = null;

                                if (reportFailures === 0) {
                                  matchFailed("\"~\"");
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        } else {
          result0 = null;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            return input.substring(pos, offset);
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_separators() {
        var result0;

        if (input.charCodeAt(pos) === 40) {
          result0 = "(";
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"(\"");
          }
        }

        if (result0 === null) {
          if (input.charCodeAt(pos) === 41) {
            result0 = ")";
            pos++;
          } else {
            result0 = null;

            if (reportFailures === 0) {
              matchFailed("\")\"");
            }
          }

          if (result0 === null) {
            if (input.charCodeAt(pos) === 60) {
              result0 = "<";
              pos++;
            } else {
              result0 = null;

              if (reportFailures === 0) {
                matchFailed("\"<\"");
              }
            }

            if (result0 === null) {
              if (input.charCodeAt(pos) === 62) {
                result0 = ">";
                pos++;
              } else {
                result0 = null;

                if (reportFailures === 0) {
                  matchFailed("\">\"");
                }
              }

              if (result0 === null) {
                if (input.charCodeAt(pos) === 64) {
                  result0 = "@";
                  pos++;
                } else {
                  result0 = null;

                  if (reportFailures === 0) {
                    matchFailed("\"@\"");
                  }
                }

                if (result0 === null) {
                  if (input.charCodeAt(pos) === 44) {
                    result0 = ",";
                    pos++;
                  } else {
                    result0 = null;

                    if (reportFailures === 0) {
                      matchFailed("\",\"");
                    }
                  }

                  if (result0 === null) {
                    if (input.charCodeAt(pos) === 59) {
                      result0 = ";";
                      pos++;
                    } else {
                      result0 = null;

                      if (reportFailures === 0) {
                        matchFailed("\";\"");
                      }
                    }

                    if (result0 === null) {
                      if (input.charCodeAt(pos) === 58) {
                        result0 = ":";
                        pos++;
                      } else {
                        result0 = null;

                        if (reportFailures === 0) {
                          matchFailed("\":\"");
                        }
                      }

                      if (result0 === null) {
                        if (input.charCodeAt(pos) === 92) {
                          result0 = "\\";
                          pos++;
                        } else {
                          result0 = null;

                          if (reportFailures === 0) {
                            matchFailed("\"\\\\\"");
                          }
                        }

                        if (result0 === null) {
                          result0 = parse_DQUOTE();

                          if (result0 === null) {
                            if (input.charCodeAt(pos) === 47) {
                              result0 = "/";
                              pos++;
                            } else {
                              result0 = null;

                              if (reportFailures === 0) {
                                matchFailed("\"/\"");
                              }
                            }

                            if (result0 === null) {
                              if (input.charCodeAt(pos) === 91) {
                                result0 = "[";
                                pos++;
                              } else {
                                result0 = null;

                                if (reportFailures === 0) {
                                  matchFailed("\"[\"");
                                }
                              }

                              if (result0 === null) {
                                if (input.charCodeAt(pos) === 93) {
                                  result0 = "]";
                                  pos++;
                                } else {
                                  result0 = null;

                                  if (reportFailures === 0) {
                                    matchFailed("\"]\"");
                                  }
                                }

                                if (result0 === null) {
                                  if (input.charCodeAt(pos) === 63) {
                                    result0 = "?";
                                    pos++;
                                  } else {
                                    result0 = null;

                                    if (reportFailures === 0) {
                                      matchFailed("\"?\"");
                                    }
                                  }

                                  if (result0 === null) {
                                    if (input.charCodeAt(pos) === 61) {
                                      result0 = "=";
                                      pos++;
                                    } else {
                                      result0 = null;

                                      if (reportFailures === 0) {
                                        matchFailed("\"=\"");
                                      }
                                    }

                                    if (result0 === null) {
                                      if (input.charCodeAt(pos) === 123) {
                                        result0 = "{";
                                        pos++;
                                      } else {
                                        result0 = null;

                                        if (reportFailures === 0) {
                                          matchFailed("\"{\"");
                                        }
                                      }

                                      if (result0 === null) {
                                        if (input.charCodeAt(pos) === 125) {
                                          result0 = "}";
                                          pos++;
                                        } else {
                                          result0 = null;

                                          if (reportFailures === 0) {
                                            matchFailed("\"}\"");
                                          }
                                        }

                                        if (result0 === null) {
                                          result0 = parse_SP();

                                          if (result0 === null) {
                                            result0 = parse_HTAB();
                                          }
                                        }
                                      }
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }

        return result0;
      }

      function parse_word() {
        var result0, result1;
        var pos0;
        pos0 = pos;
        result1 = parse_alphanum();

        if (result1 === null) {
          if (input.charCodeAt(pos) === 45) {
            result1 = "-";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\"-\"");
            }
          }

          if (result1 === null) {
            if (input.charCodeAt(pos) === 46) {
              result1 = ".";
              pos++;
            } else {
              result1 = null;

              if (reportFailures === 0) {
                matchFailed("\".\"");
              }
            }

            if (result1 === null) {
              if (input.charCodeAt(pos) === 33) {
                result1 = "!";
                pos++;
              } else {
                result1 = null;

                if (reportFailures === 0) {
                  matchFailed("\"!\"");
                }
              }

              if (result1 === null) {
                if (input.charCodeAt(pos) === 37) {
                  result1 = "%";
                  pos++;
                } else {
                  result1 = null;

                  if (reportFailures === 0) {
                    matchFailed("\"%\"");
                  }
                }

                if (result1 === null) {
                  if (input.charCodeAt(pos) === 42) {
                    result1 = "*";
                    pos++;
                  } else {
                    result1 = null;

                    if (reportFailures === 0) {
                      matchFailed("\"*\"");
                    }
                  }

                  if (result1 === null) {
                    if (input.charCodeAt(pos) === 95) {
                      result1 = "_";
                      pos++;
                    } else {
                      result1 = null;

                      if (reportFailures === 0) {
                        matchFailed("\"_\"");
                      }
                    }

                    if (result1 === null) {
                      if (input.charCodeAt(pos) === 43) {
                        result1 = "+";
                        pos++;
                      } else {
                        result1 = null;

                        if (reportFailures === 0) {
                          matchFailed("\"+\"");
                        }
                      }

                      if (result1 === null) {
                        if (input.charCodeAt(pos) === 96) {
                          result1 = "`";
                          pos++;
                        } else {
                          result1 = null;

                          if (reportFailures === 0) {
                            matchFailed("\"`\"");
                          }
                        }

                        if (result1 === null) {
                          if (input.charCodeAt(pos) === 39) {
                            result1 = "'";
                            pos++;
                          } else {
                            result1 = null;

                            if (reportFailures === 0) {
                              matchFailed("\"'\"");
                            }
                          }

                          if (result1 === null) {
                            if (input.charCodeAt(pos) === 126) {
                              result1 = "~";
                              pos++;
                            } else {
                              result1 = null;

                              if (reportFailures === 0) {
                                matchFailed("\"~\"");
                              }
                            }

                            if (result1 === null) {
                              if (input.charCodeAt(pos) === 40) {
                                result1 = "(";
                                pos++;
                              } else {
                                result1 = null;

                                if (reportFailures === 0) {
                                  matchFailed("\"(\"");
                                }
                              }

                              if (result1 === null) {
                                if (input.charCodeAt(pos) === 41) {
                                  result1 = ")";
                                  pos++;
                                } else {
                                  result1 = null;

                                  if (reportFailures === 0) {
                                    matchFailed("\")\"");
                                  }
                                }

                                if (result1 === null) {
                                  if (input.charCodeAt(pos) === 60) {
                                    result1 = "<";
                                    pos++;
                                  } else {
                                    result1 = null;

                                    if (reportFailures === 0) {
                                      matchFailed("\"<\"");
                                    }
                                  }

                                  if (result1 === null) {
                                    if (input.charCodeAt(pos) === 62) {
                                      result1 = ">";
                                      pos++;
                                    } else {
                                      result1 = null;

                                      if (reportFailures === 0) {
                                        matchFailed("\">\"");
                                      }
                                    }

                                    if (result1 === null) {
                                      if (input.charCodeAt(pos) === 58) {
                                        result1 = ":";
                                        pos++;
                                      } else {
                                        result1 = null;

                                        if (reportFailures === 0) {
                                          matchFailed("\":\"");
                                        }
                                      }

                                      if (result1 === null) {
                                        if (input.charCodeAt(pos) === 92) {
                                          result1 = "\\";
                                          pos++;
                                        } else {
                                          result1 = null;

                                          if (reportFailures === 0) {
                                            matchFailed("\"\\\\\"");
                                          }
                                        }

                                        if (result1 === null) {
                                          result1 = parse_DQUOTE();

                                          if (result1 === null) {
                                            if (input.charCodeAt(pos) === 47) {
                                              result1 = "/";
                                              pos++;
                                            } else {
                                              result1 = null;

                                              if (reportFailures === 0) {
                                                matchFailed("\"/\"");
                                              }
                                            }

                                            if (result1 === null) {
                                              if (input.charCodeAt(pos) === 91) {
                                                result1 = "[";
                                                pos++;
                                              } else {
                                                result1 = null;

                                                if (reportFailures === 0) {
                                                  matchFailed("\"[\"");
                                                }
                                              }

                                              if (result1 === null) {
                                                if (input.charCodeAt(pos) === 93) {
                                                  result1 = "]";
                                                  pos++;
                                                } else {
                                                  result1 = null;

                                                  if (reportFailures === 0) {
                                                    matchFailed("\"]\"");
                                                  }
                                                }

                                                if (result1 === null) {
                                                  if (input.charCodeAt(pos) === 63) {
                                                    result1 = "?";
                                                    pos++;
                                                  } else {
                                                    result1 = null;

                                                    if (reportFailures === 0) {
                                                      matchFailed("\"?\"");
                                                    }
                                                  }

                                                  if (result1 === null) {
                                                    if (input.charCodeAt(pos) === 123) {
                                                      result1 = "{";
                                                      pos++;
                                                    } else {
                                                      result1 = null;

                                                      if (reportFailures === 0) {
                                                        matchFailed("\"{\"");
                                                      }
                                                    }

                                                    if (result1 === null) {
                                                      if (input.charCodeAt(pos) === 125) {
                                                        result1 = "}";
                                                        pos++;
                                                      } else {
                                                        result1 = null;

                                                        if (reportFailures === 0) {
                                                          matchFailed("\"}\"");
                                                        }
                                                      }
                                                    }
                                                  }
                                                }
                                              }
                                            }
                                          }
                                        }
                                      }
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }

        if (result1 !== null) {
          result0 = [];

          while (result1 !== null) {
            result0.push(result1);
            result1 = parse_alphanum();

            if (result1 === null) {
              if (input.charCodeAt(pos) === 45) {
                result1 = "-";
                pos++;
              } else {
                result1 = null;

                if (reportFailures === 0) {
                  matchFailed("\"-\"");
                }
              }

              if (result1 === null) {
                if (input.charCodeAt(pos) === 46) {
                  result1 = ".";
                  pos++;
                } else {
                  result1 = null;

                  if (reportFailures === 0) {
                    matchFailed("\".\"");
                  }
                }

                if (result1 === null) {
                  if (input.charCodeAt(pos) === 33) {
                    result1 = "!";
                    pos++;
                  } else {
                    result1 = null;

                    if (reportFailures === 0) {
                      matchFailed("\"!\"");
                    }
                  }

                  if (result1 === null) {
                    if (input.charCodeAt(pos) === 37) {
                      result1 = "%";
                      pos++;
                    } else {
                      result1 = null;

                      if (reportFailures === 0) {
                        matchFailed("\"%\"");
                      }
                    }

                    if (result1 === null) {
                      if (input.charCodeAt(pos) === 42) {
                        result1 = "*";
                        pos++;
                      } else {
                        result1 = null;

                        if (reportFailures === 0) {
                          matchFailed("\"*\"");
                        }
                      }

                      if (result1 === null) {
                        if (input.charCodeAt(pos) === 95) {
                          result1 = "_";
                          pos++;
                        } else {
                          result1 = null;

                          if (reportFailures === 0) {
                            matchFailed("\"_\"");
                          }
                        }

                        if (result1 === null) {
                          if (input.charCodeAt(pos) === 43) {
                            result1 = "+";
                            pos++;
                          } else {
                            result1 = null;

                            if (reportFailures === 0) {
                              matchFailed("\"+\"");
                            }
                          }

                          if (result1 === null) {
                            if (input.charCodeAt(pos) === 96) {
                              result1 = "`";
                              pos++;
                            } else {
                              result1 = null;

                              if (reportFailures === 0) {
                                matchFailed("\"`\"");
                              }
                            }

                            if (result1 === null) {
                              if (input.charCodeAt(pos) === 39) {
                                result1 = "'";
                                pos++;
                              } else {
                                result1 = null;

                                if (reportFailures === 0) {
                                  matchFailed("\"'\"");
                                }
                              }

                              if (result1 === null) {
                                if (input.charCodeAt(pos) === 126) {
                                  result1 = "~";
                                  pos++;
                                } else {
                                  result1 = null;

                                  if (reportFailures === 0) {
                                    matchFailed("\"~\"");
                                  }
                                }

                                if (result1 === null) {
                                  if (input.charCodeAt(pos) === 40) {
                                    result1 = "(";
                                    pos++;
                                  } else {
                                    result1 = null;

                                    if (reportFailures === 0) {
                                      matchFailed("\"(\"");
                                    }
                                  }

                                  if (result1 === null) {
                                    if (input.charCodeAt(pos) === 41) {
                                      result1 = ")";
                                      pos++;
                                    } else {
                                      result1 = null;

                                      if (reportFailures === 0) {
                                        matchFailed("\")\"");
                                      }
                                    }

                                    if (result1 === null) {
                                      if (input.charCodeAt(pos) === 60) {
                                        result1 = "<";
                                        pos++;
                                      } else {
                                        result1 = null;

                                        if (reportFailures === 0) {
                                          matchFailed("\"<\"");
                                        }
                                      }

                                      if (result1 === null) {
                                        if (input.charCodeAt(pos) === 62) {
                                          result1 = ">";
                                          pos++;
                                        } else {
                                          result1 = null;

                                          if (reportFailures === 0) {
                                            matchFailed("\">\"");
                                          }
                                        }

                                        if (result1 === null) {
                                          if (input.charCodeAt(pos) === 58) {
                                            result1 = ":";
                                            pos++;
                                          } else {
                                            result1 = null;

                                            if (reportFailures === 0) {
                                              matchFailed("\":\"");
                                            }
                                          }

                                          if (result1 === null) {
                                            if (input.charCodeAt(pos) === 92) {
                                              result1 = "\\";
                                              pos++;
                                            } else {
                                              result1 = null;

                                              if (reportFailures === 0) {
                                                matchFailed("\"\\\\\"");
                                              }
                                            }

                                            if (result1 === null) {
                                              result1 = parse_DQUOTE();

                                              if (result1 === null) {
                                                if (input.charCodeAt(pos) === 47) {
                                                  result1 = "/";
                                                  pos++;
                                                } else {
                                                  result1 = null;

                                                  if (reportFailures === 0) {
                                                    matchFailed("\"/\"");
                                                  }
                                                }

                                                if (result1 === null) {
                                                  if (input.charCodeAt(pos) === 91) {
                                                    result1 = "[";
                                                    pos++;
                                                  } else {
                                                    result1 = null;

                                                    if (reportFailures === 0) {
                                                      matchFailed("\"[\"");
                                                    }
                                                  }

                                                  if (result1 === null) {
                                                    if (input.charCodeAt(pos) === 93) {
                                                      result1 = "]";
                                                      pos++;
                                                    } else {
                                                      result1 = null;

                                                      if (reportFailures === 0) {
                                                        matchFailed("\"]\"");
                                                      }
                                                    }

                                                    if (result1 === null) {
                                                      if (input.charCodeAt(pos) === 63) {
                                                        result1 = "?";
                                                        pos++;
                                                      } else {
                                                        result1 = null;

                                                        if (reportFailures === 0) {
                                                          matchFailed("\"?\"");
                                                        }
                                                      }

                                                      if (result1 === null) {
                                                        if (input.charCodeAt(pos) === 123) {
                                                          result1 = "{";
                                                          pos++;
                                                        } else {
                                                          result1 = null;

                                                          if (reportFailures === 0) {
                                                            matchFailed("\"{\"");
                                                          }
                                                        }

                                                        if (result1 === null) {
                                                          if (input.charCodeAt(pos) === 125) {
                                                            result1 = "}";
                                                            pos++;
                                                          } else {
                                                            result1 = null;

                                                            if (reportFailures === 0) {
                                                              matchFailed("\"}\"");
                                                            }
                                                          }
                                                        }
                                                      }
                                                    }
                                                  }
                                                }
                                              }
                                            }
                                          }
                                        }
                                      }
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        } else {
          result0 = null;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            return input.substring(pos, offset);
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_STAR() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_SWS();

        if (result0 !== null) {
          if (input.charCodeAt(pos) === 42) {
            result1 = "*";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\"*\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_SWS();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            return "*";
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_SLASH() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_SWS();

        if (result0 !== null) {
          if (input.charCodeAt(pos) === 47) {
            result1 = "/";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\"/\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_SWS();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            return "/";
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_EQUAL() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_SWS();

        if (result0 !== null) {
          if (input.charCodeAt(pos) === 61) {
            result1 = "=";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\"=\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_SWS();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            return "=";
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_LPAREN() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_SWS();

        if (result0 !== null) {
          if (input.charCodeAt(pos) === 40) {
            result1 = "(";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\"(\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_SWS();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            return "(";
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_RPAREN() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_SWS();

        if (result0 !== null) {
          if (input.charCodeAt(pos) === 41) {
            result1 = ")";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\")\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_SWS();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            return ")";
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_RAQUOT() {
        var result0, result1;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.charCodeAt(pos) === 62) {
          result0 = ">";
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\">\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_SWS();

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            return ">";
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_LAQUOT() {
        var result0, result1;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_SWS();

        if (result0 !== null) {
          if (input.charCodeAt(pos) === 60) {
            result1 = "<";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\"<\"");
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            return "<";
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_COMMA() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_SWS();

        if (result0 !== null) {
          if (input.charCodeAt(pos) === 44) {
            result1 = ",";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\",\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_SWS();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            return ",";
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_SEMI() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_SWS();

        if (result0 !== null) {
          if (input.charCodeAt(pos) === 59) {
            result1 = ";";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\";\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_SWS();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            return ";";
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_COLON() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_SWS();

        if (result0 !== null) {
          if (input.charCodeAt(pos) === 58) {
            result1 = ":";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\":\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_SWS();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            return ":";
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_LDQUOT() {
        var result0, result1;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_SWS();

        if (result0 !== null) {
          result1 = parse_DQUOTE();

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            return "\"";
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_RDQUOT() {
        var result0, result1;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_DQUOTE();

        if (result0 !== null) {
          result1 = parse_SWS();

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            return "\"";
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_comment() {
        var result0, result1, result2;
        var pos0;
        pos0 = pos;
        result0 = parse_LPAREN();

        if (result0 !== null) {
          result1 = [];
          result2 = parse_ctext();

          if (result2 === null) {
            result2 = parse_quoted_pair();

            if (result2 === null) {
              result2 = parse_comment();
            }
          }

          while (result2 !== null) {
            result1.push(result2);
            result2 = parse_ctext();

            if (result2 === null) {
              result2 = parse_quoted_pair();

              if (result2 === null) {
                result2 = parse_comment();
              }
            }
          }

          if (result1 !== null) {
            result2 = parse_RPAREN();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_ctext() {
        var result0;

        if (/^[!-']/.test(input.charAt(pos))) {
          result0 = input.charAt(pos);
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("[!-']");
          }
        }

        if (result0 === null) {
          if (/^[*-[]/.test(input.charAt(pos))) {
            result0 = input.charAt(pos);
            pos++;
          } else {
            result0 = null;

            if (reportFailures === 0) {
              matchFailed("[*-[]");
            }
          }

          if (result0 === null) {
            if (/^[\]-~]/.test(input.charAt(pos))) {
              result0 = input.charAt(pos);
              pos++;
            } else {
              result0 = null;

              if (reportFailures === 0) {
                matchFailed("[\\]-~]");
              }
            }

            if (result0 === null) {
              result0 = parse_UTF8_NONASCII();

              if (result0 === null) {
                result0 = parse_LWS();
              }
            }
          }
        }

        return result0;
      }

      function parse_quoted_string() {
        var result0, result1, result2, result3;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_SWS();

        if (result0 !== null) {
          result1 = parse_DQUOTE();

          if (result1 !== null) {
            result2 = [];
            result3 = parse_qdtext();

            if (result3 === null) {
              result3 = parse_quoted_pair();
            }

            while (result3 !== null) {
              result2.push(result3);
              result3 = parse_qdtext();

              if (result3 === null) {
                result3 = parse_quoted_pair();
              }
            }

            if (result2 !== null) {
              result3 = parse_DQUOTE();

              if (result3 !== null) {
                result0 = [result0, result1, result2, result3];
              } else {
                result0 = null;
                pos = pos1;
              }
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            return input.substring(pos, offset);
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_quoted_string_clean() {
        var result0, result1, result2, result3;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_SWS();

        if (result0 !== null) {
          result1 = parse_DQUOTE();

          if (result1 !== null) {
            result2 = [];
            result3 = parse_qdtext();

            if (result3 === null) {
              result3 = parse_quoted_pair();
            }

            while (result3 !== null) {
              result2.push(result3);
              result3 = parse_qdtext();

              if (result3 === null) {
                result3 = parse_quoted_pair();
              }
            }

            if (result2 !== null) {
              result3 = parse_DQUOTE();

              if (result3 !== null) {
                result0 = [result0, result1, result2, result3];
              } else {
                result0 = null;
                pos = pos1;
              }
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            var trimmed = input.substring(pos, offset).trim();
            return trimmed.substring(1, trimmed.length - 1) // remove outer quotes
            .replace(/\\([\x00-\x09\x0b-\x0c\x0e-\x7f])/g, '$1');
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_qdtext() {
        var result0;
        result0 = parse_LWS();

        if (result0 === null) {
          if (input.charCodeAt(pos) === 33) {
            result0 = "!";
            pos++;
          } else {
            result0 = null;

            if (reportFailures === 0) {
              matchFailed("\"!\"");
            }
          }

          if (result0 === null) {
            if (/^[#-[]/.test(input.charAt(pos))) {
              result0 = input.charAt(pos);
              pos++;
            } else {
              result0 = null;

              if (reportFailures === 0) {
                matchFailed("[#-[]");
              }
            }

            if (result0 === null) {
              if (/^[\]-~]/.test(input.charAt(pos))) {
                result0 = input.charAt(pos);
                pos++;
              } else {
                result0 = null;

                if (reportFailures === 0) {
                  matchFailed("[\\]-~]");
                }
              }

              if (result0 === null) {
                result0 = parse_UTF8_NONASCII();
              }
            }
          }
        }

        return result0;
      }

      function parse_quoted_pair() {
        var result0, result1;
        var pos0;
        pos0 = pos;

        if (input.charCodeAt(pos) === 92) {
          result0 = "\\";
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"\\\\\"");
          }
        }

        if (result0 !== null) {
          if (/^[\0-\t]/.test(input.charAt(pos))) {
            result1 = input.charAt(pos);
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("[\\0-\\t]");
            }
          }

          if (result1 === null) {
            if (/^[\x0B-\f]/.test(input.charAt(pos))) {
              result1 = input.charAt(pos);
              pos++;
            } else {
              result1 = null;

              if (reportFailures === 0) {
                matchFailed("[\\x0B-\\f]");
              }
            }

            if (result1 === null) {
              if (/^[\x0E-]/.test(input.charAt(pos))) {
                result1 = input.charAt(pos);
                pos++;
              } else {
                result1 = null;

                if (reportFailures === 0) {
                  matchFailed("[\\x0E-]");
                }
              }
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_SIP_URI_noparams() {
        var result0, result1, result2, result3;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_uri_scheme();

        if (result0 !== null) {
          if (input.charCodeAt(pos) === 58) {
            result1 = ":";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\":\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_userinfo();
            result2 = result2 !== null ? result2 : "";

            if (result2 !== null) {
              result3 = parse_hostport();

              if (result3 !== null) {
                result0 = [result0, result1, result2, result3];
              } else {
                result0 = null;
                pos = pos1;
              }
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            try {
              data.uri = new URI(data.scheme, data.user, data.host, data.port);
              delete data.scheme;
              delete data.user;
              delete data.host;
              delete data.host_type;
              delete data.port;
            } catch (e) {
              data = -1;
            }
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_SIP_URI() {
        var result0, result1, result2, result3, result4, result5;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_uri_scheme();

        if (result0 !== null) {
          if (input.charCodeAt(pos) === 58) {
            result1 = ":";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\":\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_userinfo();
            result2 = result2 !== null ? result2 : "";

            if (result2 !== null) {
              result3 = parse_hostport();

              if (result3 !== null) {
                result4 = parse_uri_parameters();

                if (result4 !== null) {
                  result5 = parse_headers();
                  result5 = result5 !== null ? result5 : "";

                  if (result5 !== null) {
                    result0 = [result0, result1, result2, result3, result4, result5];
                  } else {
                    result0 = null;
                    pos = pos1;
                  }
                } else {
                  result0 = null;
                  pos = pos1;
                }
              } else {
                result0 = null;
                pos = pos1;
              }
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            var header;

            try {
              data.uri = new URI(data.scheme, data.user, data.host, data.port, data.uri_params, data.uri_headers);
              delete data.scheme;
              delete data.user;
              delete data.host;
              delete data.host_type;
              delete data.port;
              delete data.uri_params;

              if (startRule === 'SIP_URI') {
                data = data.uri;
              }
            } catch (e) {
              data = -1;
            }
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_uri_scheme() {
        var result0;
        result0 = parse_uri_scheme_sips();

        if (result0 === null) {
          result0 = parse_uri_scheme_sip();
        }

        return result0;
      }

      function parse_uri_scheme_sips() {
        var result0;
        var pos0;
        pos0 = pos;

        if (input.substr(pos, 4).toLowerCase() === "sips") {
          result0 = input.substr(pos, 4);
          pos += 4;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"sips\"");
          }
        }

        if (result0 !== null) {
          result0 = function (offset, scheme) {
            data.scheme = scheme.toLowerCase();
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_uri_scheme_sip() {
        var result0;
        var pos0;
        pos0 = pos;

        if (input.substr(pos, 3).toLowerCase() === "sip") {
          result0 = input.substr(pos, 3);
          pos += 3;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"sip\"");
          }
        }

        if (result0 !== null) {
          result0 = function (offset, scheme) {
            data.scheme = scheme.toLowerCase();
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_userinfo() {
        var result0, result1, result2;
        var pos0, pos1, pos2;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_user();

        if (result0 !== null) {
          pos2 = pos;

          if (input.charCodeAt(pos) === 58) {
            result1 = ":";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\":\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_password();

            if (result2 !== null) {
              result1 = [result1, result2];
            } else {
              result1 = null;
              pos = pos2;
            }
          } else {
            result1 = null;
            pos = pos2;
          }

          result1 = result1 !== null ? result1 : "";

          if (result1 !== null) {
            if (input.charCodeAt(pos) === 64) {
              result2 = "@";
              pos++;
            } else {
              result2 = null;

              if (reportFailures === 0) {
                matchFailed("\"@\"");
              }
            }

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            data.user = decodeURIComponent(input.substring(pos - 1, offset));
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_user() {
        var result0, result1;
        result1 = parse_unreserved();

        if (result1 === null) {
          result1 = parse_escaped();

          if (result1 === null) {
            result1 = parse_user_unreserved();
          }
        }

        if (result1 !== null) {
          result0 = [];

          while (result1 !== null) {
            result0.push(result1);
            result1 = parse_unreserved();

            if (result1 === null) {
              result1 = parse_escaped();

              if (result1 === null) {
                result1 = parse_user_unreserved();
              }
            }
          }
        } else {
          result0 = null;
        }

        return result0;
      }

      function parse_user_unreserved() {
        var result0;

        if (input.charCodeAt(pos) === 38) {
          result0 = "&";
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"&\"");
          }
        }

        if (result0 === null) {
          if (input.charCodeAt(pos) === 61) {
            result0 = "=";
            pos++;
          } else {
            result0 = null;

            if (reportFailures === 0) {
              matchFailed("\"=\"");
            }
          }

          if (result0 === null) {
            if (input.charCodeAt(pos) === 43) {
              result0 = "+";
              pos++;
            } else {
              result0 = null;

              if (reportFailures === 0) {
                matchFailed("\"+\"");
              }
            }

            if (result0 === null) {
              if (input.charCodeAt(pos) === 36) {
                result0 = "$";
                pos++;
              } else {
                result0 = null;

                if (reportFailures === 0) {
                  matchFailed("\"$\"");
                }
              }

              if (result0 === null) {
                if (input.charCodeAt(pos) === 44) {
                  result0 = ",";
                  pos++;
                } else {
                  result0 = null;

                  if (reportFailures === 0) {
                    matchFailed("\",\"");
                  }
                }

                if (result0 === null) {
                  if (input.charCodeAt(pos) === 59) {
                    result0 = ";";
                    pos++;
                  } else {
                    result0 = null;

                    if (reportFailures === 0) {
                      matchFailed("\";\"");
                    }
                  }

                  if (result0 === null) {
                    if (input.charCodeAt(pos) === 63) {
                      result0 = "?";
                      pos++;
                    } else {
                      result0 = null;

                      if (reportFailures === 0) {
                        matchFailed("\"?\"");
                      }
                    }

                    if (result0 === null) {
                      if (input.charCodeAt(pos) === 47) {
                        result0 = "/";
                        pos++;
                      } else {
                        result0 = null;

                        if (reportFailures === 0) {
                          matchFailed("\"/\"");
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }

        return result0;
      }

      function parse_password() {
        var result0, result1;
        var pos0;
        pos0 = pos;
        result0 = [];
        result1 = parse_unreserved();

        if (result1 === null) {
          result1 = parse_escaped();

          if (result1 === null) {
            if (input.charCodeAt(pos) === 38) {
              result1 = "&";
              pos++;
            } else {
              result1 = null;

              if (reportFailures === 0) {
                matchFailed("\"&\"");
              }
            }

            if (result1 === null) {
              if (input.charCodeAt(pos) === 61) {
                result1 = "=";
                pos++;
              } else {
                result1 = null;

                if (reportFailures === 0) {
                  matchFailed("\"=\"");
                }
              }

              if (result1 === null) {
                if (input.charCodeAt(pos) === 43) {
                  result1 = "+";
                  pos++;
                } else {
                  result1 = null;

                  if (reportFailures === 0) {
                    matchFailed("\"+\"");
                  }
                }

                if (result1 === null) {
                  if (input.charCodeAt(pos) === 36) {
                    result1 = "$";
                    pos++;
                  } else {
                    result1 = null;

                    if (reportFailures === 0) {
                      matchFailed("\"$\"");
                    }
                  }

                  if (result1 === null) {
                    if (input.charCodeAt(pos) === 44) {
                      result1 = ",";
                      pos++;
                    } else {
                      result1 = null;

                      if (reportFailures === 0) {
                        matchFailed("\",\"");
                      }
                    }
                  }
                }
              }
            }
          }
        }

        while (result1 !== null) {
          result0.push(result1);
          result1 = parse_unreserved();

          if (result1 === null) {
            result1 = parse_escaped();

            if (result1 === null) {
              if (input.charCodeAt(pos) === 38) {
                result1 = "&";
                pos++;
              } else {
                result1 = null;

                if (reportFailures === 0) {
                  matchFailed("\"&\"");
                }
              }

              if (result1 === null) {
                if (input.charCodeAt(pos) === 61) {
                  result1 = "=";
                  pos++;
                } else {
                  result1 = null;

                  if (reportFailures === 0) {
                    matchFailed("\"=\"");
                  }
                }

                if (result1 === null) {
                  if (input.charCodeAt(pos) === 43) {
                    result1 = "+";
                    pos++;
                  } else {
                    result1 = null;

                    if (reportFailures === 0) {
                      matchFailed("\"+\"");
                    }
                  }

                  if (result1 === null) {
                    if (input.charCodeAt(pos) === 36) {
                      result1 = "$";
                      pos++;
                    } else {
                      result1 = null;

                      if (reportFailures === 0) {
                        matchFailed("\"$\"");
                      }
                    }

                    if (result1 === null) {
                      if (input.charCodeAt(pos) === 44) {
                        result1 = ",";
                        pos++;
                      } else {
                        result1 = null;

                        if (reportFailures === 0) {
                          matchFailed("\",\"");
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }

        if (result0 !== null) {
          result0 = function (offset) {
            data.password = input.substring(pos, offset);
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_hostport() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        result0 = parse_host();

        if (result0 !== null) {
          pos1 = pos;

          if (input.charCodeAt(pos) === 58) {
            result1 = ":";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\":\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_port();

            if (result2 !== null) {
              result1 = [result1, result2];
            } else {
              result1 = null;
              pos = pos1;
            }
          } else {
            result1 = null;
            pos = pos1;
          }

          result1 = result1 !== null ? result1 : "";

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_host() {
        var result0;
        var pos0;
        pos0 = pos;
        result0 = parse_hostname();

        if (result0 === null) {
          result0 = parse_IPv4address();

          if (result0 === null) {
            result0 = parse_IPv6reference();
          }
        }

        if (result0 !== null) {
          result0 = function (offset) {
            data.host = input.substring(pos, offset).toLowerCase();
            return data.host;
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_hostname() {
        var result0, result1, result2;
        var pos0, pos1, pos2;
        pos0 = pos;
        pos1 = pos;
        result0 = [];
        pos2 = pos;
        result1 = parse_domainlabel();

        if (result1 !== null) {
          if (input.charCodeAt(pos) === 46) {
            result2 = ".";
            pos++;
          } else {
            result2 = null;

            if (reportFailures === 0) {
              matchFailed("\".\"");
            }
          }

          if (result2 !== null) {
            result1 = [result1, result2];
          } else {
            result1 = null;
            pos = pos2;
          }
        } else {
          result1 = null;
          pos = pos2;
        }

        while (result1 !== null) {
          result0.push(result1);
          pos2 = pos;
          result1 = parse_domainlabel();

          if (result1 !== null) {
            if (input.charCodeAt(pos) === 46) {
              result2 = ".";
              pos++;
            } else {
              result2 = null;

              if (reportFailures === 0) {
                matchFailed("\".\"");
              }
            }

            if (result2 !== null) {
              result1 = [result1, result2];
            } else {
              result1 = null;
              pos = pos2;
            }
          } else {
            result1 = null;
            pos = pos2;
          }
        }

        if (result0 !== null) {
          result1 = parse_toplabel();

          if (result1 !== null) {
            if (input.charCodeAt(pos) === 46) {
              result2 = ".";
              pos++;
            } else {
              result2 = null;

              if (reportFailures === 0) {
                matchFailed("\".\"");
              }
            }

            result2 = result2 !== null ? result2 : "";

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            data.host_type = 'domain';
            return input.substring(pos, offset);
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_domainlabel() {
        var result0, result1, result2;
        var pos0;
        pos0 = pos;
        result0 = parse_alphanum();

        if (result0 !== null) {
          result1 = [];
          result2 = parse_alphanum();

          if (result2 === null) {
            if (input.charCodeAt(pos) === 45) {
              result2 = "-";
              pos++;
            } else {
              result2 = null;

              if (reportFailures === 0) {
                matchFailed("\"-\"");
              }
            }

            if (result2 === null) {
              if (input.charCodeAt(pos) === 95) {
                result2 = "_";
                pos++;
              } else {
                result2 = null;

                if (reportFailures === 0) {
                  matchFailed("\"_\"");
                }
              }
            }
          }

          while (result2 !== null) {
            result1.push(result2);
            result2 = parse_alphanum();

            if (result2 === null) {
              if (input.charCodeAt(pos) === 45) {
                result2 = "-";
                pos++;
              } else {
                result2 = null;

                if (reportFailures === 0) {
                  matchFailed("\"-\"");
                }
              }

              if (result2 === null) {
                if (input.charCodeAt(pos) === 95) {
                  result2 = "_";
                  pos++;
                } else {
                  result2 = null;

                  if (reportFailures === 0) {
                    matchFailed("\"_\"");
                  }
                }
              }
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_toplabel() {
        var result0, result1, result2;
        var pos0;
        pos0 = pos;
        result0 = parse_ALPHA();

        if (result0 !== null) {
          result1 = [];
          result2 = parse_alphanum();

          if (result2 === null) {
            if (input.charCodeAt(pos) === 45) {
              result2 = "-";
              pos++;
            } else {
              result2 = null;

              if (reportFailures === 0) {
                matchFailed("\"-\"");
              }
            }

            if (result2 === null) {
              if (input.charCodeAt(pos) === 95) {
                result2 = "_";
                pos++;
              } else {
                result2 = null;

                if (reportFailures === 0) {
                  matchFailed("\"_\"");
                }
              }
            }
          }

          while (result2 !== null) {
            result1.push(result2);
            result2 = parse_alphanum();

            if (result2 === null) {
              if (input.charCodeAt(pos) === 45) {
                result2 = "-";
                pos++;
              } else {
                result2 = null;

                if (reportFailures === 0) {
                  matchFailed("\"-\"");
                }
              }

              if (result2 === null) {
                if (input.charCodeAt(pos) === 95) {
                  result2 = "_";
                  pos++;
                } else {
                  result2 = null;

                  if (reportFailures === 0) {
                    matchFailed("\"_\"");
                  }
                }
              }
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_IPv6reference() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.charCodeAt(pos) === 91) {
          result0 = "[";
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"[\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_IPv6address();

          if (result1 !== null) {
            if (input.charCodeAt(pos) === 93) {
              result2 = "]";
              pos++;
            } else {
              result2 = null;

              if (reportFailures === 0) {
                matchFailed("\"]\"");
              }
            }

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            data.host_type = 'IPv6';
            return input.substring(pos, offset);
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_IPv6address() {
        var result0, result1, result2, result3, result4, result5, result6, result7, result8, result9, result10, result11, result12;
        var pos0, pos1, pos2;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_h16();

        if (result0 !== null) {
          if (input.charCodeAt(pos) === 58) {
            result1 = ":";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\":\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_h16();

            if (result2 !== null) {
              if (input.charCodeAt(pos) === 58) {
                result3 = ":";
                pos++;
              } else {
                result3 = null;

                if (reportFailures === 0) {
                  matchFailed("\":\"");
                }
              }

              if (result3 !== null) {
                result4 = parse_h16();

                if (result4 !== null) {
                  if (input.charCodeAt(pos) === 58) {
                    result5 = ":";
                    pos++;
                  } else {
                    result5 = null;

                    if (reportFailures === 0) {
                      matchFailed("\":\"");
                    }
                  }

                  if (result5 !== null) {
                    result6 = parse_h16();

                    if (result6 !== null) {
                      if (input.charCodeAt(pos) === 58) {
                        result7 = ":";
                        pos++;
                      } else {
                        result7 = null;

                        if (reportFailures === 0) {
                          matchFailed("\":\"");
                        }
                      }

                      if (result7 !== null) {
                        result8 = parse_h16();

                        if (result8 !== null) {
                          if (input.charCodeAt(pos) === 58) {
                            result9 = ":";
                            pos++;
                          } else {
                            result9 = null;

                            if (reportFailures === 0) {
                              matchFailed("\":\"");
                            }
                          }

                          if (result9 !== null) {
                            result10 = parse_h16();

                            if (result10 !== null) {
                              if (input.charCodeAt(pos) === 58) {
                                result11 = ":";
                                pos++;
                              } else {
                                result11 = null;

                                if (reportFailures === 0) {
                                  matchFailed("\":\"");
                                }
                              }

                              if (result11 !== null) {
                                result12 = parse_ls32();

                                if (result12 !== null) {
                                  result0 = [result0, result1, result2, result3, result4, result5, result6, result7, result8, result9, result10, result11, result12];
                                } else {
                                  result0 = null;
                                  pos = pos1;
                                }
                              } else {
                                result0 = null;
                                pos = pos1;
                              }
                            } else {
                              result0 = null;
                              pos = pos1;
                            }
                          } else {
                            result0 = null;
                            pos = pos1;
                          }
                        } else {
                          result0 = null;
                          pos = pos1;
                        }
                      } else {
                        result0 = null;
                        pos = pos1;
                      }
                    } else {
                      result0 = null;
                      pos = pos1;
                    }
                  } else {
                    result0 = null;
                    pos = pos1;
                  }
                } else {
                  result0 = null;
                  pos = pos1;
                }
              } else {
                result0 = null;
                pos = pos1;
              }
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 === null) {
          pos1 = pos;

          if (input.substr(pos, 2) === "::") {
            result0 = "::";
            pos += 2;
          } else {
            result0 = null;

            if (reportFailures === 0) {
              matchFailed("\"::\"");
            }
          }

          if (result0 !== null) {
            result1 = parse_h16();

            if (result1 !== null) {
              if (input.charCodeAt(pos) === 58) {
                result2 = ":";
                pos++;
              } else {
                result2 = null;

                if (reportFailures === 0) {
                  matchFailed("\":\"");
                }
              }

              if (result2 !== null) {
                result3 = parse_h16();

                if (result3 !== null) {
                  if (input.charCodeAt(pos) === 58) {
                    result4 = ":";
                    pos++;
                  } else {
                    result4 = null;

                    if (reportFailures === 0) {
                      matchFailed("\":\"");
                    }
                  }

                  if (result4 !== null) {
                    result5 = parse_h16();

                    if (result5 !== null) {
                      if (input.charCodeAt(pos) === 58) {
                        result6 = ":";
                        pos++;
                      } else {
                        result6 = null;

                        if (reportFailures === 0) {
                          matchFailed("\":\"");
                        }
                      }

                      if (result6 !== null) {
                        result7 = parse_h16();

                        if (result7 !== null) {
                          if (input.charCodeAt(pos) === 58) {
                            result8 = ":";
                            pos++;
                          } else {
                            result8 = null;

                            if (reportFailures === 0) {
                              matchFailed("\":\"");
                            }
                          }

                          if (result8 !== null) {
                            result9 = parse_h16();

                            if (result9 !== null) {
                              if (input.charCodeAt(pos) === 58) {
                                result10 = ":";
                                pos++;
                              } else {
                                result10 = null;

                                if (reportFailures === 0) {
                                  matchFailed("\":\"");
                                }
                              }

                              if (result10 !== null) {
                                result11 = parse_ls32();

                                if (result11 !== null) {
                                  result0 = [result0, result1, result2, result3, result4, result5, result6, result7, result8, result9, result10, result11];
                                } else {
                                  result0 = null;
                                  pos = pos1;
                                }
                              } else {
                                result0 = null;
                                pos = pos1;
                              }
                            } else {
                              result0 = null;
                              pos = pos1;
                            }
                          } else {
                            result0 = null;
                            pos = pos1;
                          }
                        } else {
                          result0 = null;
                          pos = pos1;
                        }
                      } else {
                        result0 = null;
                        pos = pos1;
                      }
                    } else {
                      result0 = null;
                      pos = pos1;
                    }
                  } else {
                    result0 = null;
                    pos = pos1;
                  }
                } else {
                  result0 = null;
                  pos = pos1;
                }
              } else {
                result0 = null;
                pos = pos1;
              }
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }

          if (result0 === null) {
            pos1 = pos;

            if (input.substr(pos, 2) === "::") {
              result0 = "::";
              pos += 2;
            } else {
              result0 = null;

              if (reportFailures === 0) {
                matchFailed("\"::\"");
              }
            }

            if (result0 !== null) {
              result1 = parse_h16();

              if (result1 !== null) {
                if (input.charCodeAt(pos) === 58) {
                  result2 = ":";
                  pos++;
                } else {
                  result2 = null;

                  if (reportFailures === 0) {
                    matchFailed("\":\"");
                  }
                }

                if (result2 !== null) {
                  result3 = parse_h16();

                  if (result3 !== null) {
                    if (input.charCodeAt(pos) === 58) {
                      result4 = ":";
                      pos++;
                    } else {
                      result4 = null;

                      if (reportFailures === 0) {
                        matchFailed("\":\"");
                      }
                    }

                    if (result4 !== null) {
                      result5 = parse_h16();

                      if (result5 !== null) {
                        if (input.charCodeAt(pos) === 58) {
                          result6 = ":";
                          pos++;
                        } else {
                          result6 = null;

                          if (reportFailures === 0) {
                            matchFailed("\":\"");
                          }
                        }

                        if (result6 !== null) {
                          result7 = parse_h16();

                          if (result7 !== null) {
                            if (input.charCodeAt(pos) === 58) {
                              result8 = ":";
                              pos++;
                            } else {
                              result8 = null;

                              if (reportFailures === 0) {
                                matchFailed("\":\"");
                              }
                            }

                            if (result8 !== null) {
                              result9 = parse_ls32();

                              if (result9 !== null) {
                                result0 = [result0, result1, result2, result3, result4, result5, result6, result7, result8, result9];
                              } else {
                                result0 = null;
                                pos = pos1;
                              }
                            } else {
                              result0 = null;
                              pos = pos1;
                            }
                          } else {
                            result0 = null;
                            pos = pos1;
                          }
                        } else {
                          result0 = null;
                          pos = pos1;
                        }
                      } else {
                        result0 = null;
                        pos = pos1;
                      }
                    } else {
                      result0 = null;
                      pos = pos1;
                    }
                  } else {
                    result0 = null;
                    pos = pos1;
                  }
                } else {
                  result0 = null;
                  pos = pos1;
                }
              } else {
                result0 = null;
                pos = pos1;
              }
            } else {
              result0 = null;
              pos = pos1;
            }

            if (result0 === null) {
              pos1 = pos;

              if (input.substr(pos, 2) === "::") {
                result0 = "::";
                pos += 2;
              } else {
                result0 = null;

                if (reportFailures === 0) {
                  matchFailed("\"::\"");
                }
              }

              if (result0 !== null) {
                result1 = parse_h16();

                if (result1 !== null) {
                  if (input.charCodeAt(pos) === 58) {
                    result2 = ":";
                    pos++;
                  } else {
                    result2 = null;

                    if (reportFailures === 0) {
                      matchFailed("\":\"");
                    }
                  }

                  if (result2 !== null) {
                    result3 = parse_h16();

                    if (result3 !== null) {
                      if (input.charCodeAt(pos) === 58) {
                        result4 = ":";
                        pos++;
                      } else {
                        result4 = null;

                        if (reportFailures === 0) {
                          matchFailed("\":\"");
                        }
                      }

                      if (result4 !== null) {
                        result5 = parse_h16();

                        if (result5 !== null) {
                          if (input.charCodeAt(pos) === 58) {
                            result6 = ":";
                            pos++;
                          } else {
                            result6 = null;

                            if (reportFailures === 0) {
                              matchFailed("\":\"");
                            }
                          }

                          if (result6 !== null) {
                            result7 = parse_ls32();

                            if (result7 !== null) {
                              result0 = [result0, result1, result2, result3, result4, result5, result6, result7];
                            } else {
                              result0 = null;
                              pos = pos1;
                            }
                          } else {
                            result0 = null;
                            pos = pos1;
                          }
                        } else {
                          result0 = null;
                          pos = pos1;
                        }
                      } else {
                        result0 = null;
                        pos = pos1;
                      }
                    } else {
                      result0 = null;
                      pos = pos1;
                    }
                  } else {
                    result0 = null;
                    pos = pos1;
                  }
                } else {
                  result0 = null;
                  pos = pos1;
                }
              } else {
                result0 = null;
                pos = pos1;
              }

              if (result0 === null) {
                pos1 = pos;

                if (input.substr(pos, 2) === "::") {
                  result0 = "::";
                  pos += 2;
                } else {
                  result0 = null;

                  if (reportFailures === 0) {
                    matchFailed("\"::\"");
                  }
                }

                if (result0 !== null) {
                  result1 = parse_h16();

                  if (result1 !== null) {
                    if (input.charCodeAt(pos) === 58) {
                      result2 = ":";
                      pos++;
                    } else {
                      result2 = null;

                      if (reportFailures === 0) {
                        matchFailed("\":\"");
                      }
                    }

                    if (result2 !== null) {
                      result3 = parse_h16();

                      if (result3 !== null) {
                        if (input.charCodeAt(pos) === 58) {
                          result4 = ":";
                          pos++;
                        } else {
                          result4 = null;

                          if (reportFailures === 0) {
                            matchFailed("\":\"");
                          }
                        }

                        if (result4 !== null) {
                          result5 = parse_ls32();

                          if (result5 !== null) {
                            result0 = [result0, result1, result2, result3, result4, result5];
                          } else {
                            result0 = null;
                            pos = pos1;
                          }
                        } else {
                          result0 = null;
                          pos = pos1;
                        }
                      } else {
                        result0 = null;
                        pos = pos1;
                      }
                    } else {
                      result0 = null;
                      pos = pos1;
                    }
                  } else {
                    result0 = null;
                    pos = pos1;
                  }
                } else {
                  result0 = null;
                  pos = pos1;
                }

                if (result0 === null) {
                  pos1 = pos;

                  if (input.substr(pos, 2) === "::") {
                    result0 = "::";
                    pos += 2;
                  } else {
                    result0 = null;

                    if (reportFailures === 0) {
                      matchFailed("\"::\"");
                    }
                  }

                  if (result0 !== null) {
                    result1 = parse_h16();

                    if (result1 !== null) {
                      if (input.charCodeAt(pos) === 58) {
                        result2 = ":";
                        pos++;
                      } else {
                        result2 = null;

                        if (reportFailures === 0) {
                          matchFailed("\":\"");
                        }
                      }

                      if (result2 !== null) {
                        result3 = parse_ls32();

                        if (result3 !== null) {
                          result0 = [result0, result1, result2, result3];
                        } else {
                          result0 = null;
                          pos = pos1;
                        }
                      } else {
                        result0 = null;
                        pos = pos1;
                      }
                    } else {
                      result0 = null;
                      pos = pos1;
                    }
                  } else {
                    result0 = null;
                    pos = pos1;
                  }

                  if (result0 === null) {
                    pos1 = pos;

                    if (input.substr(pos, 2) === "::") {
                      result0 = "::";
                      pos += 2;
                    } else {
                      result0 = null;

                      if (reportFailures === 0) {
                        matchFailed("\"::\"");
                      }
                    }

                    if (result0 !== null) {
                      result1 = parse_ls32();

                      if (result1 !== null) {
                        result0 = [result0, result1];
                      } else {
                        result0 = null;
                        pos = pos1;
                      }
                    } else {
                      result0 = null;
                      pos = pos1;
                    }

                    if (result0 === null) {
                      pos1 = pos;

                      if (input.substr(pos, 2) === "::") {
                        result0 = "::";
                        pos += 2;
                      } else {
                        result0 = null;

                        if (reportFailures === 0) {
                          matchFailed("\"::\"");
                        }
                      }

                      if (result0 !== null) {
                        result1 = parse_h16();

                        if (result1 !== null) {
                          result0 = [result0, result1];
                        } else {
                          result0 = null;
                          pos = pos1;
                        }
                      } else {
                        result0 = null;
                        pos = pos1;
                      }

                      if (result0 === null) {
                        pos1 = pos;
                        result0 = parse_h16();

                        if (result0 !== null) {
                          if (input.substr(pos, 2) === "::") {
                            result1 = "::";
                            pos += 2;
                          } else {
                            result1 = null;

                            if (reportFailures === 0) {
                              matchFailed("\"::\"");
                            }
                          }

                          if (result1 !== null) {
                            result2 = parse_h16();

                            if (result2 !== null) {
                              if (input.charCodeAt(pos) === 58) {
                                result3 = ":";
                                pos++;
                              } else {
                                result3 = null;

                                if (reportFailures === 0) {
                                  matchFailed("\":\"");
                                }
                              }

                              if (result3 !== null) {
                                result4 = parse_h16();

                                if (result4 !== null) {
                                  if (input.charCodeAt(pos) === 58) {
                                    result5 = ":";
                                    pos++;
                                  } else {
                                    result5 = null;

                                    if (reportFailures === 0) {
                                      matchFailed("\":\"");
                                    }
                                  }

                                  if (result5 !== null) {
                                    result6 = parse_h16();

                                    if (result6 !== null) {
                                      if (input.charCodeAt(pos) === 58) {
                                        result7 = ":";
                                        pos++;
                                      } else {
                                        result7 = null;

                                        if (reportFailures === 0) {
                                          matchFailed("\":\"");
                                        }
                                      }

                                      if (result7 !== null) {
                                        result8 = parse_h16();

                                        if (result8 !== null) {
                                          if (input.charCodeAt(pos) === 58) {
                                            result9 = ":";
                                            pos++;
                                          } else {
                                            result9 = null;

                                            if (reportFailures === 0) {
                                              matchFailed("\":\"");
                                            }
                                          }

                                          if (result9 !== null) {
                                            result10 = parse_ls32();

                                            if (result10 !== null) {
                                              result0 = [result0, result1, result2, result3, result4, result5, result6, result7, result8, result9, result10];
                                            } else {
                                              result0 = null;
                                              pos = pos1;
                                            }
                                          } else {
                                            result0 = null;
                                            pos = pos1;
                                          }
                                        } else {
                                          result0 = null;
                                          pos = pos1;
                                        }
                                      } else {
                                        result0 = null;
                                        pos = pos1;
                                      }
                                    } else {
                                      result0 = null;
                                      pos = pos1;
                                    }
                                  } else {
                                    result0 = null;
                                    pos = pos1;
                                  }
                                } else {
                                  result0 = null;
                                  pos = pos1;
                                }
                              } else {
                                result0 = null;
                                pos = pos1;
                              }
                            } else {
                              result0 = null;
                              pos = pos1;
                            }
                          } else {
                            result0 = null;
                            pos = pos1;
                          }
                        } else {
                          result0 = null;
                          pos = pos1;
                        }

                        if (result0 === null) {
                          pos1 = pos;
                          result0 = parse_h16();

                          if (result0 !== null) {
                            pos2 = pos;

                            if (input.charCodeAt(pos) === 58) {
                              result1 = ":";
                              pos++;
                            } else {
                              result1 = null;

                              if (reportFailures === 0) {
                                matchFailed("\":\"");
                              }
                            }

                            if (result1 !== null) {
                              result2 = parse_h16();

                              if (result2 !== null) {
                                result1 = [result1, result2];
                              } else {
                                result1 = null;
                                pos = pos2;
                              }
                            } else {
                              result1 = null;
                              pos = pos2;
                            }

                            result1 = result1 !== null ? result1 : "";

                            if (result1 !== null) {
                              if (input.substr(pos, 2) === "::") {
                                result2 = "::";
                                pos += 2;
                              } else {
                                result2 = null;

                                if (reportFailures === 0) {
                                  matchFailed("\"::\"");
                                }
                              }

                              if (result2 !== null) {
                                result3 = parse_h16();

                                if (result3 !== null) {
                                  if (input.charCodeAt(pos) === 58) {
                                    result4 = ":";
                                    pos++;
                                  } else {
                                    result4 = null;

                                    if (reportFailures === 0) {
                                      matchFailed("\":\"");
                                    }
                                  }

                                  if (result4 !== null) {
                                    result5 = parse_h16();

                                    if (result5 !== null) {
                                      if (input.charCodeAt(pos) === 58) {
                                        result6 = ":";
                                        pos++;
                                      } else {
                                        result6 = null;

                                        if (reportFailures === 0) {
                                          matchFailed("\":\"");
                                        }
                                      }

                                      if (result6 !== null) {
                                        result7 = parse_h16();

                                        if (result7 !== null) {
                                          if (input.charCodeAt(pos) === 58) {
                                            result8 = ":";
                                            pos++;
                                          } else {
                                            result8 = null;

                                            if (reportFailures === 0) {
                                              matchFailed("\":\"");
                                            }
                                          }

                                          if (result8 !== null) {
                                            result9 = parse_ls32();

                                            if (result9 !== null) {
                                              result0 = [result0, result1, result2, result3, result4, result5, result6, result7, result8, result9];
                                            } else {
                                              result0 = null;
                                              pos = pos1;
                                            }
                                          } else {
                                            result0 = null;
                                            pos = pos1;
                                          }
                                        } else {
                                          result0 = null;
                                          pos = pos1;
                                        }
                                      } else {
                                        result0 = null;
                                        pos = pos1;
                                      }
                                    } else {
                                      result0 = null;
                                      pos = pos1;
                                    }
                                  } else {
                                    result0 = null;
                                    pos = pos1;
                                  }
                                } else {
                                  result0 = null;
                                  pos = pos1;
                                }
                              } else {
                                result0 = null;
                                pos = pos1;
                              }
                            } else {
                              result0 = null;
                              pos = pos1;
                            }
                          } else {
                            result0 = null;
                            pos = pos1;
                          }

                          if (result0 === null) {
                            pos1 = pos;
                            result0 = parse_h16();

                            if (result0 !== null) {
                              pos2 = pos;

                              if (input.charCodeAt(pos) === 58) {
                                result1 = ":";
                                pos++;
                              } else {
                                result1 = null;

                                if (reportFailures === 0) {
                                  matchFailed("\":\"");
                                }
                              }

                              if (result1 !== null) {
                                result2 = parse_h16();

                                if (result2 !== null) {
                                  result1 = [result1, result2];
                                } else {
                                  result1 = null;
                                  pos = pos2;
                                }
                              } else {
                                result1 = null;
                                pos = pos2;
                              }

                              result1 = result1 !== null ? result1 : "";

                              if (result1 !== null) {
                                pos2 = pos;

                                if (input.charCodeAt(pos) === 58) {
                                  result2 = ":";
                                  pos++;
                                } else {
                                  result2 = null;

                                  if (reportFailures === 0) {
                                    matchFailed("\":\"");
                                  }
                                }

                                if (result2 !== null) {
                                  result3 = parse_h16();

                                  if (result3 !== null) {
                                    result2 = [result2, result3];
                                  } else {
                                    result2 = null;
                                    pos = pos2;
                                  }
                                } else {
                                  result2 = null;
                                  pos = pos2;
                                }

                                result2 = result2 !== null ? result2 : "";

                                if (result2 !== null) {
                                  if (input.substr(pos, 2) === "::") {
                                    result3 = "::";
                                    pos += 2;
                                  } else {
                                    result3 = null;

                                    if (reportFailures === 0) {
                                      matchFailed("\"::\"");
                                    }
                                  }

                                  if (result3 !== null) {
                                    result4 = parse_h16();

                                    if (result4 !== null) {
                                      if (input.charCodeAt(pos) === 58) {
                                        result5 = ":";
                                        pos++;
                                      } else {
                                        result5 = null;

                                        if (reportFailures === 0) {
                                          matchFailed("\":\"");
                                        }
                                      }

                                      if (result5 !== null) {
                                        result6 = parse_h16();

                                        if (result6 !== null) {
                                          if (input.charCodeAt(pos) === 58) {
                                            result7 = ":";
                                            pos++;
                                          } else {
                                            result7 = null;

                                            if (reportFailures === 0) {
                                              matchFailed("\":\"");
                                            }
                                          }

                                          if (result7 !== null) {
                                            result8 = parse_ls32();

                                            if (result8 !== null) {
                                              result0 = [result0, result1, result2, result3, result4, result5, result6, result7, result8];
                                            } else {
                                              result0 = null;
                                              pos = pos1;
                                            }
                                          } else {
                                            result0 = null;
                                            pos = pos1;
                                          }
                                        } else {
                                          result0 = null;
                                          pos = pos1;
                                        }
                                      } else {
                                        result0 = null;
                                        pos = pos1;
                                      }
                                    } else {
                                      result0 = null;
                                      pos = pos1;
                                    }
                                  } else {
                                    result0 = null;
                                    pos = pos1;
                                  }
                                } else {
                                  result0 = null;
                                  pos = pos1;
                                }
                              } else {
                                result0 = null;
                                pos = pos1;
                              }
                            } else {
                              result0 = null;
                              pos = pos1;
                            }

                            if (result0 === null) {
                              pos1 = pos;
                              result0 = parse_h16();

                              if (result0 !== null) {
                                pos2 = pos;

                                if (input.charCodeAt(pos) === 58) {
                                  result1 = ":";
                                  pos++;
                                } else {
                                  result1 = null;

                                  if (reportFailures === 0) {
                                    matchFailed("\":\"");
                                  }
                                }

                                if (result1 !== null) {
                                  result2 = parse_h16();

                                  if (result2 !== null) {
                                    result1 = [result1, result2];
                                  } else {
                                    result1 = null;
                                    pos = pos2;
                                  }
                                } else {
                                  result1 = null;
                                  pos = pos2;
                                }

                                result1 = result1 !== null ? result1 : "";

                                if (result1 !== null) {
                                  pos2 = pos;

                                  if (input.charCodeAt(pos) === 58) {
                                    result2 = ":";
                                    pos++;
                                  } else {
                                    result2 = null;

                                    if (reportFailures === 0) {
                                      matchFailed("\":\"");
                                    }
                                  }

                                  if (result2 !== null) {
                                    result3 = parse_h16();

                                    if (result3 !== null) {
                                      result2 = [result2, result3];
                                    } else {
                                      result2 = null;
                                      pos = pos2;
                                    }
                                  } else {
                                    result2 = null;
                                    pos = pos2;
                                  }

                                  result2 = result2 !== null ? result2 : "";

                                  if (result2 !== null) {
                                    pos2 = pos;

                                    if (input.charCodeAt(pos) === 58) {
                                      result3 = ":";
                                      pos++;
                                    } else {
                                      result3 = null;

                                      if (reportFailures === 0) {
                                        matchFailed("\":\"");
                                      }
                                    }

                                    if (result3 !== null) {
                                      result4 = parse_h16();

                                      if (result4 !== null) {
                                        result3 = [result3, result4];
                                      } else {
                                        result3 = null;
                                        pos = pos2;
                                      }
                                    } else {
                                      result3 = null;
                                      pos = pos2;
                                    }

                                    result3 = result3 !== null ? result3 : "";

                                    if (result3 !== null) {
                                      if (input.substr(pos, 2) === "::") {
                                        result4 = "::";
                                        pos += 2;
                                      } else {
                                        result4 = null;

                                        if (reportFailures === 0) {
                                          matchFailed("\"::\"");
                                        }
                                      }

                                      if (result4 !== null) {
                                        result5 = parse_h16();

                                        if (result5 !== null) {
                                          if (input.charCodeAt(pos) === 58) {
                                            result6 = ":";
                                            pos++;
                                          } else {
                                            result6 = null;

                                            if (reportFailures === 0) {
                                              matchFailed("\":\"");
                                            }
                                          }

                                          if (result6 !== null) {
                                            result7 = parse_ls32();

                                            if (result7 !== null) {
                                              result0 = [result0, result1, result2, result3, result4, result5, result6, result7];
                                            } else {
                                              result0 = null;
                                              pos = pos1;
                                            }
                                          } else {
                                            result0 = null;
                                            pos = pos1;
                                          }
                                        } else {
                                          result0 = null;
                                          pos = pos1;
                                        }
                                      } else {
                                        result0 = null;
                                        pos = pos1;
                                      }
                                    } else {
                                      result0 = null;
                                      pos = pos1;
                                    }
                                  } else {
                                    result0 = null;
                                    pos = pos1;
                                  }
                                } else {
                                  result0 = null;
                                  pos = pos1;
                                }
                              } else {
                                result0 = null;
                                pos = pos1;
                              }

                              if (result0 === null) {
                                pos1 = pos;
                                result0 = parse_h16();

                                if (result0 !== null) {
                                  pos2 = pos;

                                  if (input.charCodeAt(pos) === 58) {
                                    result1 = ":";
                                    pos++;
                                  } else {
                                    result1 = null;

                                    if (reportFailures === 0) {
                                      matchFailed("\":\"");
                                    }
                                  }

                                  if (result1 !== null) {
                                    result2 = parse_h16();

                                    if (result2 !== null) {
                                      result1 = [result1, result2];
                                    } else {
                                      result1 = null;
                                      pos = pos2;
                                    }
                                  } else {
                                    result1 = null;
                                    pos = pos2;
                                  }

                                  result1 = result1 !== null ? result1 : "";

                                  if (result1 !== null) {
                                    pos2 = pos;

                                    if (input.charCodeAt(pos) === 58) {
                                      result2 = ":";
                                      pos++;
                                    } else {
                                      result2 = null;

                                      if (reportFailures === 0) {
                                        matchFailed("\":\"");
                                      }
                                    }

                                    if (result2 !== null) {
                                      result3 = parse_h16();

                                      if (result3 !== null) {
                                        result2 = [result2, result3];
                                      } else {
                                        result2 = null;
                                        pos = pos2;
                                      }
                                    } else {
                                      result2 = null;
                                      pos = pos2;
                                    }

                                    result2 = result2 !== null ? result2 : "";

                                    if (result2 !== null) {
                                      pos2 = pos;

                                      if (input.charCodeAt(pos) === 58) {
                                        result3 = ":";
                                        pos++;
                                      } else {
                                        result3 = null;

                                        if (reportFailures === 0) {
                                          matchFailed("\":\"");
                                        }
                                      }

                                      if (result3 !== null) {
                                        result4 = parse_h16();

                                        if (result4 !== null) {
                                          result3 = [result3, result4];
                                        } else {
                                          result3 = null;
                                          pos = pos2;
                                        }
                                      } else {
                                        result3 = null;
                                        pos = pos2;
                                      }

                                      result3 = result3 !== null ? result3 : "";

                                      if (result3 !== null) {
                                        pos2 = pos;

                                        if (input.charCodeAt(pos) === 58) {
                                          result4 = ":";
                                          pos++;
                                        } else {
                                          result4 = null;

                                          if (reportFailures === 0) {
                                            matchFailed("\":\"");
                                          }
                                        }

                                        if (result4 !== null) {
                                          result5 = parse_h16();

                                          if (result5 !== null) {
                                            result4 = [result4, result5];
                                          } else {
                                            result4 = null;
                                            pos = pos2;
                                          }
                                        } else {
                                          result4 = null;
                                          pos = pos2;
                                        }

                                        result4 = result4 !== null ? result4 : "";

                                        if (result4 !== null) {
                                          if (input.substr(pos, 2) === "::") {
                                            result5 = "::";
                                            pos += 2;
                                          } else {
                                            result5 = null;

                                            if (reportFailures === 0) {
                                              matchFailed("\"::\"");
                                            }
                                          }

                                          if (result5 !== null) {
                                            result6 = parse_ls32();

                                            if (result6 !== null) {
                                              result0 = [result0, result1, result2, result3, result4, result5, result6];
                                            } else {
                                              result0 = null;
                                              pos = pos1;
                                            }
                                          } else {
                                            result0 = null;
                                            pos = pos1;
                                          }
                                        } else {
                                          result0 = null;
                                          pos = pos1;
                                        }
                                      } else {
                                        result0 = null;
                                        pos = pos1;
                                      }
                                    } else {
                                      result0 = null;
                                      pos = pos1;
                                    }
                                  } else {
                                    result0 = null;
                                    pos = pos1;
                                  }
                                } else {
                                  result0 = null;
                                  pos = pos1;
                                }

                                if (result0 === null) {
                                  pos1 = pos;
                                  result0 = parse_h16();

                                  if (result0 !== null) {
                                    pos2 = pos;

                                    if (input.charCodeAt(pos) === 58) {
                                      result1 = ":";
                                      pos++;
                                    } else {
                                      result1 = null;

                                      if (reportFailures === 0) {
                                        matchFailed("\":\"");
                                      }
                                    }

                                    if (result1 !== null) {
                                      result2 = parse_h16();

                                      if (result2 !== null) {
                                        result1 = [result1, result2];
                                      } else {
                                        result1 = null;
                                        pos = pos2;
                                      }
                                    } else {
                                      result1 = null;
                                      pos = pos2;
                                    }

                                    result1 = result1 !== null ? result1 : "";

                                    if (result1 !== null) {
                                      pos2 = pos;

                                      if (input.charCodeAt(pos) === 58) {
                                        result2 = ":";
                                        pos++;
                                      } else {
                                        result2 = null;

                                        if (reportFailures === 0) {
                                          matchFailed("\":\"");
                                        }
                                      }

                                      if (result2 !== null) {
                                        result3 = parse_h16();

                                        if (result3 !== null) {
                                          result2 = [result2, result3];
                                        } else {
                                          result2 = null;
                                          pos = pos2;
                                        }
                                      } else {
                                        result2 = null;
                                        pos = pos2;
                                      }

                                      result2 = result2 !== null ? result2 : "";

                                      if (result2 !== null) {
                                        pos2 = pos;

                                        if (input.charCodeAt(pos) === 58) {
                                          result3 = ":";
                                          pos++;
                                        } else {
                                          result3 = null;

                                          if (reportFailures === 0) {
                                            matchFailed("\":\"");
                                          }
                                        }

                                        if (result3 !== null) {
                                          result4 = parse_h16();

                                          if (result4 !== null) {
                                            result3 = [result3, result4];
                                          } else {
                                            result3 = null;
                                            pos = pos2;
                                          }
                                        } else {
                                          result3 = null;
                                          pos = pos2;
                                        }

                                        result3 = result3 !== null ? result3 : "";

                                        if (result3 !== null) {
                                          pos2 = pos;

                                          if (input.charCodeAt(pos) === 58) {
                                            result4 = ":";
                                            pos++;
                                          } else {
                                            result4 = null;

                                            if (reportFailures === 0) {
                                              matchFailed("\":\"");
                                            }
                                          }

                                          if (result4 !== null) {
                                            result5 = parse_h16();

                                            if (result5 !== null) {
                                              result4 = [result4, result5];
                                            } else {
                                              result4 = null;
                                              pos = pos2;
                                            }
                                          } else {
                                            result4 = null;
                                            pos = pos2;
                                          }

                                          result4 = result4 !== null ? result4 : "";

                                          if (result4 !== null) {
                                            pos2 = pos;

                                            if (input.charCodeAt(pos) === 58) {
                                              result5 = ":";
                                              pos++;
                                            } else {
                                              result5 = null;

                                              if (reportFailures === 0) {
                                                matchFailed("\":\"");
                                              }
                                            }

                                            if (result5 !== null) {
                                              result6 = parse_h16();

                                              if (result6 !== null) {
                                                result5 = [result5, result6];
                                              } else {
                                                result5 = null;
                                                pos = pos2;
                                              }
                                            } else {
                                              result5 = null;
                                              pos = pos2;
                                            }

                                            result5 = result5 !== null ? result5 : "";

                                            if (result5 !== null) {
                                              if (input.substr(pos, 2) === "::") {
                                                result6 = "::";
                                                pos += 2;
                                              } else {
                                                result6 = null;

                                                if (reportFailures === 0) {
                                                  matchFailed("\"::\"");
                                                }
                                              }

                                              if (result6 !== null) {
                                                result7 = parse_h16();

                                                if (result7 !== null) {
                                                  result0 = [result0, result1, result2, result3, result4, result5, result6, result7];
                                                } else {
                                                  result0 = null;
                                                  pos = pos1;
                                                }
                                              } else {
                                                result0 = null;
                                                pos = pos1;
                                              }
                                            } else {
                                              result0 = null;
                                              pos = pos1;
                                            }
                                          } else {
                                            result0 = null;
                                            pos = pos1;
                                          }
                                        } else {
                                          result0 = null;
                                          pos = pos1;
                                        }
                                      } else {
                                        result0 = null;
                                        pos = pos1;
                                      }
                                    } else {
                                      result0 = null;
                                      pos = pos1;
                                    }
                                  } else {
                                    result0 = null;
                                    pos = pos1;
                                  }

                                  if (result0 === null) {
                                    pos1 = pos;
                                    result0 = parse_h16();

                                    if (result0 !== null) {
                                      pos2 = pos;

                                      if (input.charCodeAt(pos) === 58) {
                                        result1 = ":";
                                        pos++;
                                      } else {
                                        result1 = null;

                                        if (reportFailures === 0) {
                                          matchFailed("\":\"");
                                        }
                                      }

                                      if (result1 !== null) {
                                        result2 = parse_h16();

                                        if (result2 !== null) {
                                          result1 = [result1, result2];
                                        } else {
                                          result1 = null;
                                          pos = pos2;
                                        }
                                      } else {
                                        result1 = null;
                                        pos = pos2;
                                      }

                                      result1 = result1 !== null ? result1 : "";

                                      if (result1 !== null) {
                                        pos2 = pos;

                                        if (input.charCodeAt(pos) === 58) {
                                          result2 = ":";
                                          pos++;
                                        } else {
                                          result2 = null;

                                          if (reportFailures === 0) {
                                            matchFailed("\":\"");
                                          }
                                        }

                                        if (result2 !== null) {
                                          result3 = parse_h16();

                                          if (result3 !== null) {
                                            result2 = [result2, result3];
                                          } else {
                                            result2 = null;
                                            pos = pos2;
                                          }
                                        } else {
                                          result2 = null;
                                          pos = pos2;
                                        }

                                        result2 = result2 !== null ? result2 : "";

                                        if (result2 !== null) {
                                          pos2 = pos;

                                          if (input.charCodeAt(pos) === 58) {
                                            result3 = ":";
                                            pos++;
                                          } else {
                                            result3 = null;

                                            if (reportFailures === 0) {
                                              matchFailed("\":\"");
                                            }
                                          }

                                          if (result3 !== null) {
                                            result4 = parse_h16();

                                            if (result4 !== null) {
                                              result3 = [result3, result4];
                                            } else {
                                              result3 = null;
                                              pos = pos2;
                                            }
                                          } else {
                                            result3 = null;
                                            pos = pos2;
                                          }

                                          result3 = result3 !== null ? result3 : "";

                                          if (result3 !== null) {
                                            pos2 = pos;

                                            if (input.charCodeAt(pos) === 58) {
                                              result4 = ":";
                                              pos++;
                                            } else {
                                              result4 = null;

                                              if (reportFailures === 0) {
                                                matchFailed("\":\"");
                                              }
                                            }

                                            if (result4 !== null) {
                                              result5 = parse_h16();

                                              if (result5 !== null) {
                                                result4 = [result4, result5];
                                              } else {
                                                result4 = null;
                                                pos = pos2;
                                              }
                                            } else {
                                              result4 = null;
                                              pos = pos2;
                                            }

                                            result4 = result4 !== null ? result4 : "";

                                            if (result4 !== null) {
                                              pos2 = pos;

                                              if (input.charCodeAt(pos) === 58) {
                                                result5 = ":";
                                                pos++;
                                              } else {
                                                result5 = null;

                                                if (reportFailures === 0) {
                                                  matchFailed("\":\"");
                                                }
                                              }

                                              if (result5 !== null) {
                                                result6 = parse_h16();

                                                if (result6 !== null) {
                                                  result5 = [result5, result6];
                                                } else {
                                                  result5 = null;
                                                  pos = pos2;
                                                }
                                              } else {
                                                result5 = null;
                                                pos = pos2;
                                              }

                                              result5 = result5 !== null ? result5 : "";

                                              if (result5 !== null) {
                                                pos2 = pos;

                                                if (input.charCodeAt(pos) === 58) {
                                                  result6 = ":";
                                                  pos++;
                                                } else {
                                                  result6 = null;

                                                  if (reportFailures === 0) {
                                                    matchFailed("\":\"");
                                                  }
                                                }

                                                if (result6 !== null) {
                                                  result7 = parse_h16();

                                                  if (result7 !== null) {
                                                    result6 = [result6, result7];
                                                  } else {
                                                    result6 = null;
                                                    pos = pos2;
                                                  }
                                                } else {
                                                  result6 = null;
                                                  pos = pos2;
                                                }

                                                result6 = result6 !== null ? result6 : "";

                                                if (result6 !== null) {
                                                  if (input.substr(pos, 2) === "::") {
                                                    result7 = "::";
                                                    pos += 2;
                                                  } else {
                                                    result7 = null;

                                                    if (reportFailures === 0) {
                                                      matchFailed("\"::\"");
                                                    }
                                                  }

                                                  if (result7 !== null) {
                                                    result0 = [result0, result1, result2, result3, result4, result5, result6, result7];
                                                  } else {
                                                    result0 = null;
                                                    pos = pos1;
                                                  }
                                                } else {
                                                  result0 = null;
                                                  pos = pos1;
                                                }
                                              } else {
                                                result0 = null;
                                                pos = pos1;
                                              }
                                            } else {
                                              result0 = null;
                                              pos = pos1;
                                            }
                                          } else {
                                            result0 = null;
                                            pos = pos1;
                                          }
                                        } else {
                                          result0 = null;
                                          pos = pos1;
                                        }
                                      } else {
                                        result0 = null;
                                        pos = pos1;
                                      }
                                    } else {
                                      result0 = null;
                                      pos = pos1;
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }

        if (result0 !== null) {
          result0 = function (offset) {
            data.host_type = 'IPv6';
            return input.substring(pos, offset);
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_h16() {
        var result0, result1, result2, result3;
        var pos0;
        pos0 = pos;
        result0 = parse_HEXDIG();

        if (result0 !== null) {
          result1 = parse_HEXDIG();
          result1 = result1 !== null ? result1 : "";

          if (result1 !== null) {
            result2 = parse_HEXDIG();
            result2 = result2 !== null ? result2 : "";

            if (result2 !== null) {
              result3 = parse_HEXDIG();
              result3 = result3 !== null ? result3 : "";

              if (result3 !== null) {
                result0 = [result0, result1, result2, result3];
              } else {
                result0 = null;
                pos = pos0;
              }
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_ls32() {
        var result0, result1, result2;
        var pos0;
        pos0 = pos;
        result0 = parse_h16();

        if (result0 !== null) {
          if (input.charCodeAt(pos) === 58) {
            result1 = ":";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\":\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_h16();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        if (result0 === null) {
          result0 = parse_IPv4address();
        }

        return result0;
      }

      function parse_IPv4address() {
        var result0, result1, result2, result3, result4, result5, result6;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_dec_octet();

        if (result0 !== null) {
          if (input.charCodeAt(pos) === 46) {
            result1 = ".";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\".\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_dec_octet();

            if (result2 !== null) {
              if (input.charCodeAt(pos) === 46) {
                result3 = ".";
                pos++;
              } else {
                result3 = null;

                if (reportFailures === 0) {
                  matchFailed("\".\"");
                }
              }

              if (result3 !== null) {
                result4 = parse_dec_octet();

                if (result4 !== null) {
                  if (input.charCodeAt(pos) === 46) {
                    result5 = ".";
                    pos++;
                  } else {
                    result5 = null;

                    if (reportFailures === 0) {
                      matchFailed("\".\"");
                    }
                  }

                  if (result5 !== null) {
                    result6 = parse_dec_octet();

                    if (result6 !== null) {
                      result0 = [result0, result1, result2, result3, result4, result5, result6];
                    } else {
                      result0 = null;
                      pos = pos1;
                    }
                  } else {
                    result0 = null;
                    pos = pos1;
                  }
                } else {
                  result0 = null;
                  pos = pos1;
                }
              } else {
                result0 = null;
                pos = pos1;
              }
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            data.host_type = 'IPv4';
            return input.substring(pos, offset);
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_dec_octet() {
        var result0, result1, result2;
        var pos0;
        pos0 = pos;

        if (input.substr(pos, 2) === "25") {
          result0 = "25";
          pos += 2;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"25\"");
          }
        }

        if (result0 !== null) {
          if (/^[0-5]/.test(input.charAt(pos))) {
            result1 = input.charAt(pos);
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("[0-5]");
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        if (result0 === null) {
          pos0 = pos;

          if (input.charCodeAt(pos) === 50) {
            result0 = "2";
            pos++;
          } else {
            result0 = null;

            if (reportFailures === 0) {
              matchFailed("\"2\"");
            }
          }

          if (result0 !== null) {
            if (/^[0-4]/.test(input.charAt(pos))) {
              result1 = input.charAt(pos);
              pos++;
            } else {
              result1 = null;

              if (reportFailures === 0) {
                matchFailed("[0-4]");
              }
            }

            if (result1 !== null) {
              result2 = parse_DIGIT();

              if (result2 !== null) {
                result0 = [result0, result1, result2];
              } else {
                result0 = null;
                pos = pos0;
              }
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }

          if (result0 === null) {
            pos0 = pos;

            if (input.charCodeAt(pos) === 49) {
              result0 = "1";
              pos++;
            } else {
              result0 = null;

              if (reportFailures === 0) {
                matchFailed("\"1\"");
              }
            }

            if (result0 !== null) {
              result1 = parse_DIGIT();

              if (result1 !== null) {
                result2 = parse_DIGIT();

                if (result2 !== null) {
                  result0 = [result0, result1, result2];
                } else {
                  result0 = null;
                  pos = pos0;
                }
              } else {
                result0 = null;
                pos = pos0;
              }
            } else {
              result0 = null;
              pos = pos0;
            }

            if (result0 === null) {
              pos0 = pos;

              if (/^[1-9]/.test(input.charAt(pos))) {
                result0 = input.charAt(pos);
                pos++;
              } else {
                result0 = null;

                if (reportFailures === 0) {
                  matchFailed("[1-9]");
                }
              }

              if (result0 !== null) {
                result1 = parse_DIGIT();

                if (result1 !== null) {
                  result0 = [result0, result1];
                } else {
                  result0 = null;
                  pos = pos0;
                }
              } else {
                result0 = null;
                pos = pos0;
              }

              if (result0 === null) {
                result0 = parse_DIGIT();
              }
            }
          }
        }

        return result0;
      }

      function parse_port() {
        var result0, result1, result2, result3, result4;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_DIGIT();
        result0 = result0 !== null ? result0 : "";

        if (result0 !== null) {
          result1 = parse_DIGIT();
          result1 = result1 !== null ? result1 : "";

          if (result1 !== null) {
            result2 = parse_DIGIT();
            result2 = result2 !== null ? result2 : "";

            if (result2 !== null) {
              result3 = parse_DIGIT();
              result3 = result3 !== null ? result3 : "";

              if (result3 !== null) {
                result4 = parse_DIGIT();
                result4 = result4 !== null ? result4 : "";

                if (result4 !== null) {
                  result0 = [result0, result1, result2, result3, result4];
                } else {
                  result0 = null;
                  pos = pos1;
                }
              } else {
                result0 = null;
                pos = pos1;
              }
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, port) {
            port = parseInt(port.join(''));
            data.port = port;
            return port;
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_uri_parameters() {
        var result0, result1, result2;
        var pos0;
        result0 = [];
        pos0 = pos;

        if (input.charCodeAt(pos) === 59) {
          result1 = ";";
          pos++;
        } else {
          result1 = null;

          if (reportFailures === 0) {
            matchFailed("\";\"");
          }
        }

        if (result1 !== null) {
          result2 = parse_uri_parameter();

          if (result2 !== null) {
            result1 = [result1, result2];
          } else {
            result1 = null;
            pos = pos0;
          }
        } else {
          result1 = null;
          pos = pos0;
        }

        while (result1 !== null) {
          result0.push(result1);
          pos0 = pos;

          if (input.charCodeAt(pos) === 59) {
            result1 = ";";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\";\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_uri_parameter();

            if (result2 !== null) {
              result1 = [result1, result2];
            } else {
              result1 = null;
              pos = pos0;
            }
          } else {
            result1 = null;
            pos = pos0;
          }
        }

        return result0;
      }

      function parse_uri_parameter() {
        var result0;
        result0 = parse_transport_param();

        if (result0 === null) {
          result0 = parse_user_param();

          if (result0 === null) {
            result0 = parse_method_param();

            if (result0 === null) {
              result0 = parse_ttl_param();

              if (result0 === null) {
                result0 = parse_maddr_param();

                if (result0 === null) {
                  result0 = parse_lr_param();

                  if (result0 === null) {
                    result0 = parse_other_param();
                  }
                }
              }
            }
          }
        }

        return result0;
      }

      function parse_transport_param() {
        var result0, result1;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 10).toLowerCase() === "transport=") {
          result0 = input.substr(pos, 10);
          pos += 10;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"transport=\"");
          }
        }

        if (result0 !== null) {
          if (input.substr(pos, 3).toLowerCase() === "udp") {
            result1 = input.substr(pos, 3);
            pos += 3;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\"udp\"");
            }
          }

          if (result1 === null) {
            if (input.substr(pos, 3).toLowerCase() === "tcp") {
              result1 = input.substr(pos, 3);
              pos += 3;
            } else {
              result1 = null;

              if (reportFailures === 0) {
                matchFailed("\"tcp\"");
              }
            }

            if (result1 === null) {
              if (input.substr(pos, 4).toLowerCase() === "sctp") {
                result1 = input.substr(pos, 4);
                pos += 4;
              } else {
                result1 = null;

                if (reportFailures === 0) {
                  matchFailed("\"sctp\"");
                }
              }

              if (result1 === null) {
                if (input.substr(pos, 3).toLowerCase() === "tls") {
                  result1 = input.substr(pos, 3);
                  pos += 3;
                } else {
                  result1 = null;

                  if (reportFailures === 0) {
                    matchFailed("\"tls\"");
                  }
                }

                if (result1 === null) {
                  result1 = parse_token();
                }
              }
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, transport) {
            if (!data.uri_params) data.uri_params = {};
            data.uri_params['transport'] = transport.toLowerCase();
          }(pos0, result0[1]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_user_param() {
        var result0, result1;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 5).toLowerCase() === "user=") {
          result0 = input.substr(pos, 5);
          pos += 5;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"user=\"");
          }
        }

        if (result0 !== null) {
          if (input.substr(pos, 5).toLowerCase() === "phone") {
            result1 = input.substr(pos, 5);
            pos += 5;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\"phone\"");
            }
          }

          if (result1 === null) {
            if (input.substr(pos, 2).toLowerCase() === "ip") {
              result1 = input.substr(pos, 2);
              pos += 2;
            } else {
              result1 = null;

              if (reportFailures === 0) {
                matchFailed("\"ip\"");
              }
            }

            if (result1 === null) {
              result1 = parse_token();
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, user) {
            if (!data.uri_params) data.uri_params = {};
            data.uri_params['user'] = user.toLowerCase();
          }(pos0, result0[1]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_method_param() {
        var result0, result1;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 7).toLowerCase() === "method=") {
          result0 = input.substr(pos, 7);
          pos += 7;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"method=\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_Method();

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, method) {
            if (!data.uri_params) data.uri_params = {};
            data.uri_params['method'] = method;
          }(pos0, result0[1]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_ttl_param() {
        var result0, result1;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 4).toLowerCase() === "ttl=") {
          result0 = input.substr(pos, 4);
          pos += 4;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"ttl=\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_ttl();

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, ttl) {
            if (!data.params) data.params = {};
            data.params['ttl'] = ttl;
          }(pos0, result0[1]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_maddr_param() {
        var result0, result1;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 6).toLowerCase() === "maddr=") {
          result0 = input.substr(pos, 6);
          pos += 6;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"maddr=\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_host();

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, maddr) {
            if (!data.uri_params) data.uri_params = {};
            data.uri_params['maddr'] = maddr;
          }(pos0, result0[1]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_lr_param() {
        var result0, result1, result2;
        var pos0, pos1, pos2;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 2).toLowerCase() === "lr") {
          result0 = input.substr(pos, 2);
          pos += 2;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"lr\"");
          }
        }

        if (result0 !== null) {
          pos2 = pos;

          if (input.charCodeAt(pos) === 61) {
            result1 = "=";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\"=\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_token();

            if (result2 !== null) {
              result1 = [result1, result2];
            } else {
              result1 = null;
              pos = pos2;
            }
          } else {
            result1 = null;
            pos = pos2;
          }

          result1 = result1 !== null ? result1 : "";

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            if (!data.uri_params) data.uri_params = {};
            data.uri_params['lr'] = undefined;
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_other_param() {
        var result0, result1, result2;
        var pos0, pos1, pos2;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_pname();

        if (result0 !== null) {
          pos2 = pos;

          if (input.charCodeAt(pos) === 61) {
            result1 = "=";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\"=\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_pvalue();

            if (result2 !== null) {
              result1 = [result1, result2];
            } else {
              result1 = null;
              pos = pos2;
            }
          } else {
            result1 = null;
            pos = pos2;
          }

          result1 = result1 !== null ? result1 : "";

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, param, value) {
            if (!data.uri_params) data.uri_params = {};

            if (typeof value === 'undefined') {
              value = undefined;
            } else {
              value = value[1];
            }

            data.uri_params[param.toLowerCase()] = value;
          }(pos0, result0[0], result0[1]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_pname() {
        var result0, result1;
        var pos0;
        pos0 = pos;
        result1 = parse_paramchar();

        if (result1 !== null) {
          result0 = [];

          while (result1 !== null) {
            result0.push(result1);
            result1 = parse_paramchar();
          }
        } else {
          result0 = null;
        }

        if (result0 !== null) {
          result0 = function (offset, pname) {
            return pname.join('');
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_pvalue() {
        var result0, result1;
        var pos0;
        pos0 = pos;
        result1 = parse_paramchar();

        if (result1 !== null) {
          result0 = [];

          while (result1 !== null) {
            result0.push(result1);
            result1 = parse_paramchar();
          }
        } else {
          result0 = null;
        }

        if (result0 !== null) {
          result0 = function (offset, pvalue) {
            return pvalue.join('');
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_paramchar() {
        var result0;
        result0 = parse_param_unreserved();

        if (result0 === null) {
          result0 = parse_unreserved();

          if (result0 === null) {
            result0 = parse_escaped();
          }
        }

        return result0;
      }

      function parse_param_unreserved() {
        var result0;

        if (input.charCodeAt(pos) === 91) {
          result0 = "[";
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"[\"");
          }
        }

        if (result0 === null) {
          if (input.charCodeAt(pos) === 93) {
            result0 = "]";
            pos++;
          } else {
            result0 = null;

            if (reportFailures === 0) {
              matchFailed("\"]\"");
            }
          }

          if (result0 === null) {
            if (input.charCodeAt(pos) === 47) {
              result0 = "/";
              pos++;
            } else {
              result0 = null;

              if (reportFailures === 0) {
                matchFailed("\"/\"");
              }
            }

            if (result0 === null) {
              if (input.charCodeAt(pos) === 58) {
                result0 = ":";
                pos++;
              } else {
                result0 = null;

                if (reportFailures === 0) {
                  matchFailed("\":\"");
                }
              }

              if (result0 === null) {
                if (input.charCodeAt(pos) === 38) {
                  result0 = "&";
                  pos++;
                } else {
                  result0 = null;

                  if (reportFailures === 0) {
                    matchFailed("\"&\"");
                  }
                }

                if (result0 === null) {
                  if (input.charCodeAt(pos) === 43) {
                    result0 = "+";
                    pos++;
                  } else {
                    result0 = null;

                    if (reportFailures === 0) {
                      matchFailed("\"+\"");
                    }
                  }

                  if (result0 === null) {
                    if (input.charCodeAt(pos) === 36) {
                      result0 = "$";
                      pos++;
                    } else {
                      result0 = null;

                      if (reportFailures === 0) {
                        matchFailed("\"$\"");
                      }
                    }
                  }
                }
              }
            }
          }
        }

        return result0;
      }

      function parse_headers() {
        var result0, result1, result2, result3, result4;
        var pos0, pos1;
        pos0 = pos;

        if (input.charCodeAt(pos) === 63) {
          result0 = "?";
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"?\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_header();

          if (result1 !== null) {
            result2 = [];
            pos1 = pos;

            if (input.charCodeAt(pos) === 38) {
              result3 = "&";
              pos++;
            } else {
              result3 = null;

              if (reportFailures === 0) {
                matchFailed("\"&\"");
              }
            }

            if (result3 !== null) {
              result4 = parse_header();

              if (result4 !== null) {
                result3 = [result3, result4];
              } else {
                result3 = null;
                pos = pos1;
              }
            } else {
              result3 = null;
              pos = pos1;
            }

            while (result3 !== null) {
              result2.push(result3);
              pos1 = pos;

              if (input.charCodeAt(pos) === 38) {
                result3 = "&";
                pos++;
              } else {
                result3 = null;

                if (reportFailures === 0) {
                  matchFailed("\"&\"");
                }
              }

              if (result3 !== null) {
                result4 = parse_header();

                if (result4 !== null) {
                  result3 = [result3, result4];
                } else {
                  result3 = null;
                  pos = pos1;
                }
              } else {
                result3 = null;
                pos = pos1;
              }
            }

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_header() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_hname();

        if (result0 !== null) {
          if (input.charCodeAt(pos) === 61) {
            result1 = "=";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\"=\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_hvalue();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, hname, hvalue) {
            hname = hname.join('').toLowerCase();
            hvalue = hvalue.join('');
            if (!data.uri_headers) data.uri_headers = {};

            if (!data.uri_headers[hname]) {
              data.uri_headers[hname] = [hvalue];
            } else {
              data.uri_headers[hname].push(hvalue);
            }
          }(pos0, result0[0], result0[2]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_hname() {
        var result0, result1;
        result1 = parse_hnv_unreserved();

        if (result1 === null) {
          result1 = parse_unreserved();

          if (result1 === null) {
            result1 = parse_escaped();
          }
        }

        if (result1 !== null) {
          result0 = [];

          while (result1 !== null) {
            result0.push(result1);
            result1 = parse_hnv_unreserved();

            if (result1 === null) {
              result1 = parse_unreserved();

              if (result1 === null) {
                result1 = parse_escaped();
              }
            }
          }
        } else {
          result0 = null;
        }

        return result0;
      }

      function parse_hvalue() {
        var result0, result1;
        result0 = [];
        result1 = parse_hnv_unreserved();

        if (result1 === null) {
          result1 = parse_unreserved();

          if (result1 === null) {
            result1 = parse_escaped();
          }
        }

        while (result1 !== null) {
          result0.push(result1);
          result1 = parse_hnv_unreserved();

          if (result1 === null) {
            result1 = parse_unreserved();

            if (result1 === null) {
              result1 = parse_escaped();
            }
          }
        }

        return result0;
      }

      function parse_hnv_unreserved() {
        var result0;

        if (input.charCodeAt(pos) === 91) {
          result0 = "[";
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"[\"");
          }
        }

        if (result0 === null) {
          if (input.charCodeAt(pos) === 93) {
            result0 = "]";
            pos++;
          } else {
            result0 = null;

            if (reportFailures === 0) {
              matchFailed("\"]\"");
            }
          }

          if (result0 === null) {
            if (input.charCodeAt(pos) === 47) {
              result0 = "/";
              pos++;
            } else {
              result0 = null;

              if (reportFailures === 0) {
                matchFailed("\"/\"");
              }
            }

            if (result0 === null) {
              if (input.charCodeAt(pos) === 63) {
                result0 = "?";
                pos++;
              } else {
                result0 = null;

                if (reportFailures === 0) {
                  matchFailed("\"?\"");
                }
              }

              if (result0 === null) {
                if (input.charCodeAt(pos) === 58) {
                  result0 = ":";
                  pos++;
                } else {
                  result0 = null;

                  if (reportFailures === 0) {
                    matchFailed("\":\"");
                  }
                }

                if (result0 === null) {
                  if (input.charCodeAt(pos) === 43) {
                    result0 = "+";
                    pos++;
                  } else {
                    result0 = null;

                    if (reportFailures === 0) {
                      matchFailed("\"+\"");
                    }
                  }

                  if (result0 === null) {
                    if (input.charCodeAt(pos) === 36) {
                      result0 = "$";
                      pos++;
                    } else {
                      result0 = null;

                      if (reportFailures === 0) {
                        matchFailed("\"$\"");
                      }
                    }
                  }
                }
              }
            }
          }
        }

        return result0;
      }

      function parse_Request_Response() {
        var result0;
        result0 = parse_Status_Line();

        if (result0 === null) {
          result0 = parse_Request_Line();
        }

        return result0;
      }

      function parse_Request_Line() {
        var result0, result1, result2, result3, result4;
        var pos0;
        pos0 = pos;
        result0 = parse_Method();

        if (result0 !== null) {
          result1 = parse_SP();

          if (result1 !== null) {
            result2 = parse_Request_URI();

            if (result2 !== null) {
              result3 = parse_SP();

              if (result3 !== null) {
                result4 = parse_SIP_Version();

                if (result4 !== null) {
                  result0 = [result0, result1, result2, result3, result4];
                } else {
                  result0 = null;
                  pos = pos0;
                }
              } else {
                result0 = null;
                pos = pos0;
              }
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_Request_URI() {
        var result0;
        result0 = parse_SIP_URI();

        if (result0 === null) {
          result0 = parse_absoluteURI();
        }

        return result0;
      }

      function parse_absoluteURI() {
        var result0, result1, result2;
        var pos0;
        pos0 = pos;
        result0 = parse_scheme();

        if (result0 !== null) {
          if (input.charCodeAt(pos) === 58) {
            result1 = ":";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\":\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_hier_part();

            if (result2 === null) {
              result2 = parse_opaque_part();
            }

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_hier_part() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        result0 = parse_net_path();

        if (result0 === null) {
          result0 = parse_abs_path();
        }

        if (result0 !== null) {
          pos1 = pos;

          if (input.charCodeAt(pos) === 63) {
            result1 = "?";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\"?\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_query();

            if (result2 !== null) {
              result1 = [result1, result2];
            } else {
              result1 = null;
              pos = pos1;
            }
          } else {
            result1 = null;
            pos = pos1;
          }

          result1 = result1 !== null ? result1 : "";

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_net_path() {
        var result0, result1, result2;
        var pos0;
        pos0 = pos;

        if (input.substr(pos, 2) === "//") {
          result0 = "//";
          pos += 2;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"//\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_authority();

          if (result1 !== null) {
            result2 = parse_abs_path();
            result2 = result2 !== null ? result2 : "";

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_abs_path() {
        var result0, result1;
        var pos0;
        pos0 = pos;

        if (input.charCodeAt(pos) === 47) {
          result0 = "/";
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"/\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_path_segments();

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_opaque_part() {
        var result0, result1, result2;
        var pos0;
        pos0 = pos;
        result0 = parse_uric_no_slash();

        if (result0 !== null) {
          result1 = [];
          result2 = parse_uric();

          while (result2 !== null) {
            result1.push(result2);
            result2 = parse_uric();
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_uric() {
        var result0;
        result0 = parse_reserved();

        if (result0 === null) {
          result0 = parse_unreserved();

          if (result0 === null) {
            result0 = parse_escaped();
          }
        }

        return result0;
      }

      function parse_uric_no_slash() {
        var result0;
        result0 = parse_unreserved();

        if (result0 === null) {
          result0 = parse_escaped();

          if (result0 === null) {
            if (input.charCodeAt(pos) === 59) {
              result0 = ";";
              pos++;
            } else {
              result0 = null;

              if (reportFailures === 0) {
                matchFailed("\";\"");
              }
            }

            if (result0 === null) {
              if (input.charCodeAt(pos) === 63) {
                result0 = "?";
                pos++;
              } else {
                result0 = null;

                if (reportFailures === 0) {
                  matchFailed("\"?\"");
                }
              }

              if (result0 === null) {
                if (input.charCodeAt(pos) === 58) {
                  result0 = ":";
                  pos++;
                } else {
                  result0 = null;

                  if (reportFailures === 0) {
                    matchFailed("\":\"");
                  }
                }

                if (result0 === null) {
                  if (input.charCodeAt(pos) === 64) {
                    result0 = "@";
                    pos++;
                  } else {
                    result0 = null;

                    if (reportFailures === 0) {
                      matchFailed("\"@\"");
                    }
                  }

                  if (result0 === null) {
                    if (input.charCodeAt(pos) === 38) {
                      result0 = "&";
                      pos++;
                    } else {
                      result0 = null;

                      if (reportFailures === 0) {
                        matchFailed("\"&\"");
                      }
                    }

                    if (result0 === null) {
                      if (input.charCodeAt(pos) === 61) {
                        result0 = "=";
                        pos++;
                      } else {
                        result0 = null;

                        if (reportFailures === 0) {
                          matchFailed("\"=\"");
                        }
                      }

                      if (result0 === null) {
                        if (input.charCodeAt(pos) === 43) {
                          result0 = "+";
                          pos++;
                        } else {
                          result0 = null;

                          if (reportFailures === 0) {
                            matchFailed("\"+\"");
                          }
                        }

                        if (result0 === null) {
                          if (input.charCodeAt(pos) === 36) {
                            result0 = "$";
                            pos++;
                          } else {
                            result0 = null;

                            if (reportFailures === 0) {
                              matchFailed("\"$\"");
                            }
                          }

                          if (result0 === null) {
                            if (input.charCodeAt(pos) === 44) {
                              result0 = ",";
                              pos++;
                            } else {
                              result0 = null;

                              if (reportFailures === 0) {
                                matchFailed("\",\"");
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }

        return result0;
      }

      function parse_path_segments() {
        var result0, result1, result2, result3;
        var pos0, pos1;
        pos0 = pos;
        result0 = parse_segment();

        if (result0 !== null) {
          result1 = [];
          pos1 = pos;

          if (input.charCodeAt(pos) === 47) {
            result2 = "/";
            pos++;
          } else {
            result2 = null;

            if (reportFailures === 0) {
              matchFailed("\"/\"");
            }
          }

          if (result2 !== null) {
            result3 = parse_segment();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos1;
            }
          } else {
            result2 = null;
            pos = pos1;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos1 = pos;

            if (input.charCodeAt(pos) === 47) {
              result2 = "/";
              pos++;
            } else {
              result2 = null;

              if (reportFailures === 0) {
                matchFailed("\"/\"");
              }
            }

            if (result2 !== null) {
              result3 = parse_segment();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos1;
              }
            } else {
              result2 = null;
              pos = pos1;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_segment() {
        var result0, result1, result2, result3;
        var pos0, pos1;
        pos0 = pos;
        result0 = [];
        result1 = parse_pchar();

        while (result1 !== null) {
          result0.push(result1);
          result1 = parse_pchar();
        }

        if (result0 !== null) {
          result1 = [];
          pos1 = pos;

          if (input.charCodeAt(pos) === 59) {
            result2 = ";";
            pos++;
          } else {
            result2 = null;

            if (reportFailures === 0) {
              matchFailed("\";\"");
            }
          }

          if (result2 !== null) {
            result3 = parse_param();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos1;
            }
          } else {
            result2 = null;
            pos = pos1;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos1 = pos;

            if (input.charCodeAt(pos) === 59) {
              result2 = ";";
              pos++;
            } else {
              result2 = null;

              if (reportFailures === 0) {
                matchFailed("\";\"");
              }
            }

            if (result2 !== null) {
              result3 = parse_param();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos1;
              }
            } else {
              result2 = null;
              pos = pos1;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_param() {
        var result0, result1;
        result0 = [];
        result1 = parse_pchar();

        while (result1 !== null) {
          result0.push(result1);
          result1 = parse_pchar();
        }

        return result0;
      }

      function parse_pchar() {
        var result0;
        result0 = parse_unreserved();

        if (result0 === null) {
          result0 = parse_escaped();

          if (result0 === null) {
            if (input.charCodeAt(pos) === 58) {
              result0 = ":";
              pos++;
            } else {
              result0 = null;

              if (reportFailures === 0) {
                matchFailed("\":\"");
              }
            }

            if (result0 === null) {
              if (input.charCodeAt(pos) === 64) {
                result0 = "@";
                pos++;
              } else {
                result0 = null;

                if (reportFailures === 0) {
                  matchFailed("\"@\"");
                }
              }

              if (result0 === null) {
                if (input.charCodeAt(pos) === 38) {
                  result0 = "&";
                  pos++;
                } else {
                  result0 = null;

                  if (reportFailures === 0) {
                    matchFailed("\"&\"");
                  }
                }

                if (result0 === null) {
                  if (input.charCodeAt(pos) === 61) {
                    result0 = "=";
                    pos++;
                  } else {
                    result0 = null;

                    if (reportFailures === 0) {
                      matchFailed("\"=\"");
                    }
                  }

                  if (result0 === null) {
                    if (input.charCodeAt(pos) === 43) {
                      result0 = "+";
                      pos++;
                    } else {
                      result0 = null;

                      if (reportFailures === 0) {
                        matchFailed("\"+\"");
                      }
                    }

                    if (result0 === null) {
                      if (input.charCodeAt(pos) === 36) {
                        result0 = "$";
                        pos++;
                      } else {
                        result0 = null;

                        if (reportFailures === 0) {
                          matchFailed("\"$\"");
                        }
                      }

                      if (result0 === null) {
                        if (input.charCodeAt(pos) === 44) {
                          result0 = ",";
                          pos++;
                        } else {
                          result0 = null;

                          if (reportFailures === 0) {
                            matchFailed("\",\"");
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }

        return result0;
      }

      function parse_scheme() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_ALPHA();

        if (result0 !== null) {
          result1 = [];
          result2 = parse_ALPHA();

          if (result2 === null) {
            result2 = parse_DIGIT();

            if (result2 === null) {
              if (input.charCodeAt(pos) === 43) {
                result2 = "+";
                pos++;
              } else {
                result2 = null;

                if (reportFailures === 0) {
                  matchFailed("\"+\"");
                }
              }

              if (result2 === null) {
                if (input.charCodeAt(pos) === 45) {
                  result2 = "-";
                  pos++;
                } else {
                  result2 = null;

                  if (reportFailures === 0) {
                    matchFailed("\"-\"");
                  }
                }

                if (result2 === null) {
                  if (input.charCodeAt(pos) === 46) {
                    result2 = ".";
                    pos++;
                  } else {
                    result2 = null;

                    if (reportFailures === 0) {
                      matchFailed("\".\"");
                    }
                  }
                }
              }
            }
          }

          while (result2 !== null) {
            result1.push(result2);
            result2 = parse_ALPHA();

            if (result2 === null) {
              result2 = parse_DIGIT();

              if (result2 === null) {
                if (input.charCodeAt(pos) === 43) {
                  result2 = "+";
                  pos++;
                } else {
                  result2 = null;

                  if (reportFailures === 0) {
                    matchFailed("\"+\"");
                  }
                }

                if (result2 === null) {
                  if (input.charCodeAt(pos) === 45) {
                    result2 = "-";
                    pos++;
                  } else {
                    result2 = null;

                    if (reportFailures === 0) {
                      matchFailed("\"-\"");
                    }
                  }

                  if (result2 === null) {
                    if (input.charCodeAt(pos) === 46) {
                      result2 = ".";
                      pos++;
                    } else {
                      result2 = null;

                      if (reportFailures === 0) {
                        matchFailed("\".\"");
                      }
                    }
                  }
                }
              }
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            data.scheme = input.substring(pos, offset);
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_authority() {
        var result0;
        result0 = parse_srvr();

        if (result0 === null) {
          result0 = parse_reg_name();
        }

        return result0;
      }

      function parse_srvr() {
        var result0, result1;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_userinfo();

        if (result0 !== null) {
          if (input.charCodeAt(pos) === 64) {
            result1 = "@";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\"@\"");
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        result0 = result0 !== null ? result0 : "";

        if (result0 !== null) {
          result1 = parse_hostport();

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        result0 = result0 !== null ? result0 : "";
        return result0;
      }

      function parse_reg_name() {
        var result0, result1;
        result1 = parse_unreserved();

        if (result1 === null) {
          result1 = parse_escaped();

          if (result1 === null) {
            if (input.charCodeAt(pos) === 36) {
              result1 = "$";
              pos++;
            } else {
              result1 = null;

              if (reportFailures === 0) {
                matchFailed("\"$\"");
              }
            }

            if (result1 === null) {
              if (input.charCodeAt(pos) === 44) {
                result1 = ",";
                pos++;
              } else {
                result1 = null;

                if (reportFailures === 0) {
                  matchFailed("\",\"");
                }
              }

              if (result1 === null) {
                if (input.charCodeAt(pos) === 59) {
                  result1 = ";";
                  pos++;
                } else {
                  result1 = null;

                  if (reportFailures === 0) {
                    matchFailed("\";\"");
                  }
                }

                if (result1 === null) {
                  if (input.charCodeAt(pos) === 58) {
                    result1 = ":";
                    pos++;
                  } else {
                    result1 = null;

                    if (reportFailures === 0) {
                      matchFailed("\":\"");
                    }
                  }

                  if (result1 === null) {
                    if (input.charCodeAt(pos) === 64) {
                      result1 = "@";
                      pos++;
                    } else {
                      result1 = null;

                      if (reportFailures === 0) {
                        matchFailed("\"@\"");
                      }
                    }

                    if (result1 === null) {
                      if (input.charCodeAt(pos) === 38) {
                        result1 = "&";
                        pos++;
                      } else {
                        result1 = null;

                        if (reportFailures === 0) {
                          matchFailed("\"&\"");
                        }
                      }

                      if (result1 === null) {
                        if (input.charCodeAt(pos) === 61) {
                          result1 = "=";
                          pos++;
                        } else {
                          result1 = null;

                          if (reportFailures === 0) {
                            matchFailed("\"=\"");
                          }
                        }

                        if (result1 === null) {
                          if (input.charCodeAt(pos) === 43) {
                            result1 = "+";
                            pos++;
                          } else {
                            result1 = null;

                            if (reportFailures === 0) {
                              matchFailed("\"+\"");
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }

        if (result1 !== null) {
          result0 = [];

          while (result1 !== null) {
            result0.push(result1);
            result1 = parse_unreserved();

            if (result1 === null) {
              result1 = parse_escaped();

              if (result1 === null) {
                if (input.charCodeAt(pos) === 36) {
                  result1 = "$";
                  pos++;
                } else {
                  result1 = null;

                  if (reportFailures === 0) {
                    matchFailed("\"$\"");
                  }
                }

                if (result1 === null) {
                  if (input.charCodeAt(pos) === 44) {
                    result1 = ",";
                    pos++;
                  } else {
                    result1 = null;

                    if (reportFailures === 0) {
                      matchFailed("\",\"");
                    }
                  }

                  if (result1 === null) {
                    if (input.charCodeAt(pos) === 59) {
                      result1 = ";";
                      pos++;
                    } else {
                      result1 = null;

                      if (reportFailures === 0) {
                        matchFailed("\";\"");
                      }
                    }

                    if (result1 === null) {
                      if (input.charCodeAt(pos) === 58) {
                        result1 = ":";
                        pos++;
                      } else {
                        result1 = null;

                        if (reportFailures === 0) {
                          matchFailed("\":\"");
                        }
                      }

                      if (result1 === null) {
                        if (input.charCodeAt(pos) === 64) {
                          result1 = "@";
                          pos++;
                        } else {
                          result1 = null;

                          if (reportFailures === 0) {
                            matchFailed("\"@\"");
                          }
                        }

                        if (result1 === null) {
                          if (input.charCodeAt(pos) === 38) {
                            result1 = "&";
                            pos++;
                          } else {
                            result1 = null;

                            if (reportFailures === 0) {
                              matchFailed("\"&\"");
                            }
                          }

                          if (result1 === null) {
                            if (input.charCodeAt(pos) === 61) {
                              result1 = "=";
                              pos++;
                            } else {
                              result1 = null;

                              if (reportFailures === 0) {
                                matchFailed("\"=\"");
                              }
                            }

                            if (result1 === null) {
                              if (input.charCodeAt(pos) === 43) {
                                result1 = "+";
                                pos++;
                              } else {
                                result1 = null;

                                if (reportFailures === 0) {
                                  matchFailed("\"+\"");
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        } else {
          result0 = null;
        }

        return result0;
      }

      function parse_query() {
        var result0, result1;
        result0 = [];
        result1 = parse_uric();

        while (result1 !== null) {
          result0.push(result1);
          result1 = parse_uric();
        }

        return result0;
      }

      function parse_SIP_Version() {
        var result0, result1, result2, result3, result4, result5;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 3).toLowerCase() === "sip") {
          result0 = input.substr(pos, 3);
          pos += 3;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"SIP\"");
          }
        }

        if (result0 !== null) {
          if (input.charCodeAt(pos) === 47) {
            result1 = "/";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\"/\"");
            }
          }

          if (result1 !== null) {
            result3 = parse_DIGIT();

            if (result3 !== null) {
              result2 = [];

              while (result3 !== null) {
                result2.push(result3);
                result3 = parse_DIGIT();
              }
            } else {
              result2 = null;
            }

            if (result2 !== null) {
              if (input.charCodeAt(pos) === 46) {
                result3 = ".";
                pos++;
              } else {
                result3 = null;

                if (reportFailures === 0) {
                  matchFailed("\".\"");
                }
              }

              if (result3 !== null) {
                result5 = parse_DIGIT();

                if (result5 !== null) {
                  result4 = [];

                  while (result5 !== null) {
                    result4.push(result5);
                    result5 = parse_DIGIT();
                  }
                } else {
                  result4 = null;
                }

                if (result4 !== null) {
                  result0 = [result0, result1, result2, result3, result4];
                } else {
                  result0 = null;
                  pos = pos1;
                }
              } else {
                result0 = null;
                pos = pos1;
              }
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            data.sip_version = input.substring(pos, offset);
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_INVITEm() {
        var result0;

        if (input.substr(pos, 6) === "INVITE") {
          result0 = "INVITE";
          pos += 6;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"INVITE\"");
          }
        }

        return result0;
      }

      function parse_ACKm() {
        var result0;

        if (input.substr(pos, 3) === "ACK") {
          result0 = "ACK";
          pos += 3;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"ACK\"");
          }
        }

        return result0;
      }

      function parse_OPTIONSm() {
        var result0;

        if (input.substr(pos, 7) === "OPTIONS") {
          result0 = "OPTIONS";
          pos += 7;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"OPTIONS\"");
          }
        }

        return result0;
      }

      function parse_BYEm() {
        var result0;

        if (input.substr(pos, 3) === "BYE") {
          result0 = "BYE";
          pos += 3;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"BYE\"");
          }
        }

        return result0;
      }

      function parse_CANCELm() {
        var result0;

        if (input.substr(pos, 6) === "CANCEL") {
          result0 = "CANCEL";
          pos += 6;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"CANCEL\"");
          }
        }

        return result0;
      }

      function parse_REGISTERm() {
        var result0;

        if (input.substr(pos, 8) === "REGISTER") {
          result0 = "REGISTER";
          pos += 8;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"REGISTER\"");
          }
        }

        return result0;
      }

      function parse_SUBSCRIBEm() {
        var result0;

        if (input.substr(pos, 9) === "SUBSCRIBE") {
          result0 = "SUBSCRIBE";
          pos += 9;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"SUBSCRIBE\"");
          }
        }

        return result0;
      }

      function parse_NOTIFYm() {
        var result0;

        if (input.substr(pos, 6) === "NOTIFY") {
          result0 = "NOTIFY";
          pos += 6;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"NOTIFY\"");
          }
        }

        return result0;
      }

      function parse_REFERm() {
        var result0;

        if (input.substr(pos, 5) === "REFER") {
          result0 = "REFER";
          pos += 5;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"REFER\"");
          }
        }

        return result0;
      }

      function parse_Method() {
        var result0;
        var pos0;
        pos0 = pos;
        result0 = parse_INVITEm();

        if (result0 === null) {
          result0 = parse_ACKm();

          if (result0 === null) {
            result0 = parse_OPTIONSm();

            if (result0 === null) {
              result0 = parse_BYEm();

              if (result0 === null) {
                result0 = parse_CANCELm();

                if (result0 === null) {
                  result0 = parse_REGISTERm();

                  if (result0 === null) {
                    result0 = parse_SUBSCRIBEm();

                    if (result0 === null) {
                      result0 = parse_NOTIFYm();

                      if (result0 === null) {
                        result0 = parse_REFERm();

                        if (result0 === null) {
                          result0 = parse_token();
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }

        if (result0 !== null) {
          result0 = function (offset) {
            data.method = input.substring(pos, offset);
            return data.method;
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_Status_Line() {
        var result0, result1, result2, result3, result4;
        var pos0;
        pos0 = pos;
        result0 = parse_SIP_Version();

        if (result0 !== null) {
          result1 = parse_SP();

          if (result1 !== null) {
            result2 = parse_Status_Code();

            if (result2 !== null) {
              result3 = parse_SP();

              if (result3 !== null) {
                result4 = parse_Reason_Phrase();

                if (result4 !== null) {
                  result0 = [result0, result1, result2, result3, result4];
                } else {
                  result0 = null;
                  pos = pos0;
                }
              } else {
                result0 = null;
                pos = pos0;
              }
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_Status_Code() {
        var result0;
        var pos0;
        pos0 = pos;
        result0 = parse_extension_code();

        if (result0 !== null) {
          result0 = function (offset, status_code) {
            data.status_code = parseInt(status_code.join(''));
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_extension_code() {
        var result0, result1, result2;
        var pos0;
        pos0 = pos;
        result0 = parse_DIGIT();

        if (result0 !== null) {
          result1 = parse_DIGIT();

          if (result1 !== null) {
            result2 = parse_DIGIT();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_Reason_Phrase() {
        var result0, result1;
        var pos0;
        pos0 = pos;
        result0 = [];
        result1 = parse_reserved();

        if (result1 === null) {
          result1 = parse_unreserved();

          if (result1 === null) {
            result1 = parse_escaped();

            if (result1 === null) {
              result1 = parse_UTF8_NONASCII();

              if (result1 === null) {
                result1 = parse_UTF8_CONT();

                if (result1 === null) {
                  result1 = parse_SP();

                  if (result1 === null) {
                    result1 = parse_HTAB();
                  }
                }
              }
            }
          }
        }

        while (result1 !== null) {
          result0.push(result1);
          result1 = parse_reserved();

          if (result1 === null) {
            result1 = parse_unreserved();

            if (result1 === null) {
              result1 = parse_escaped();

              if (result1 === null) {
                result1 = parse_UTF8_NONASCII();

                if (result1 === null) {
                  result1 = parse_UTF8_CONT();

                  if (result1 === null) {
                    result1 = parse_SP();

                    if (result1 === null) {
                      result1 = parse_HTAB();
                    }
                  }
                }
              }
            }
          }
        }

        if (result0 !== null) {
          result0 = function (offset) {
            data.reason_phrase = input.substring(pos, offset);
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_Allow_Events() {
        var result0, result1, result2, result3;
        var pos0, pos1;
        pos0 = pos;
        result0 = parse_event_type();

        if (result0 !== null) {
          result1 = [];
          pos1 = pos;
          result2 = parse_COMMA();

          if (result2 !== null) {
            result3 = parse_event_type();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos1;
            }
          } else {
            result2 = null;
            pos = pos1;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos1 = pos;
            result2 = parse_COMMA();

            if (result2 !== null) {
              result3 = parse_event_type();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos1;
              }
            } else {
              result2 = null;
              pos = pos1;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_Call_ID() {
        var result0, result1, result2;
        var pos0, pos1, pos2;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_word();

        if (result0 !== null) {
          pos2 = pos;

          if (input.charCodeAt(pos) === 64) {
            result1 = "@";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\"@\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_word();

            if (result2 !== null) {
              result1 = [result1, result2];
            } else {
              result1 = null;
              pos = pos2;
            }
          } else {
            result1 = null;
            pos = pos2;
          }

          result1 = result1 !== null ? result1 : "";

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            data = input.substring(pos, offset);
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_Contact() {
        var result0, result1, result2, result3;
        var pos0, pos1, pos2;
        pos0 = pos;
        result0 = parse_STAR();

        if (result0 === null) {
          pos1 = pos;
          result0 = parse_contact_param();

          if (result0 !== null) {
            result1 = [];
            pos2 = pos;
            result2 = parse_COMMA();

            if (result2 !== null) {
              result3 = parse_contact_param();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos2;
              }
            } else {
              result2 = null;
              pos = pos2;
            }

            while (result2 !== null) {
              result1.push(result2);
              pos2 = pos;
              result2 = parse_COMMA();

              if (result2 !== null) {
                result3 = parse_contact_param();

                if (result3 !== null) {
                  result2 = [result2, result3];
                } else {
                  result2 = null;
                  pos = pos2;
                }
              } else {
                result2 = null;
                pos = pos2;
              }
            }

            if (result1 !== null) {
              result0 = [result0, result1];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        }

        if (result0 !== null) {
          result0 = function (offset) {
            var idx, length;
            length = data.multi_header.length;

            for (idx = 0; idx < length; idx++) {
              if (data.multi_header[idx].parsed === null) {
                data = null;
                break;
              }
            }

            if (data !== null) {
              data = data.multi_header;
            } else {
              data = -1;
            }
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_contact_param() {
        var result0, result1, result2, result3;
        var pos0, pos1, pos2;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_SIP_URI_noparams();

        if (result0 === null) {
          result0 = parse_name_addr();
        }

        if (result0 !== null) {
          result1 = [];
          pos2 = pos;
          result2 = parse_SEMI();

          if (result2 !== null) {
            result3 = parse_contact_params();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos2;
            }
          } else {
            result2 = null;
            pos = pos2;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos2 = pos;
            result2 = parse_SEMI();

            if (result2 !== null) {
              result3 = parse_contact_params();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos2;
              }
            } else {
              result2 = null;
              pos = pos2;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            var header;
            if (!data.multi_header) data.multi_header = [];

            try {
              header = new NameAddrHeader(data.uri, data.display_name, data.params);
              delete data.uri;
              delete data.display_name;
              delete data.params;
            } catch (e) {
              header = null;
            }

            data.multi_header.push({
              'possition': pos,
              'offset': offset,
              'parsed': header
            });
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_name_addr() {
        var result0, result1, result2, result3;
        var pos0;
        pos0 = pos;
        result0 = parse_display_name();
        result0 = result0 !== null ? result0 : "";

        if (result0 !== null) {
          result1 = parse_LAQUOT();

          if (result1 !== null) {
            result2 = parse_SIP_URI();

            if (result2 !== null) {
              result3 = parse_RAQUOT();

              if (result3 !== null) {
                result0 = [result0, result1, result2, result3];
              } else {
                result0 = null;
                pos = pos0;
              }
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_display_name() {
        var result0, result1, result2, result3;
        var pos0, pos1, pos2;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_token();

        if (result0 !== null) {
          result1 = [];
          pos2 = pos;
          result2 = parse_LWS();

          if (result2 !== null) {
            result3 = parse_token();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos2;
            }
          } else {
            result2 = null;
            pos = pos2;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos2 = pos;
            result2 = parse_LWS();

            if (result2 !== null) {
              result3 = parse_token();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos2;
              }
            } else {
              result2 = null;
              pos = pos2;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 === null) {
          result0 = parse_quoted_string_clean();
        }

        if (result0 !== null) {
          result0 = function (offset, display_name) {
            if (typeof display_name === 'string') {
              // quoted_string_clean
              data.display_name = display_name;
            } else {
              // token ( LWS token )*
              data.display_name = display_name[1].reduce(function (acc, cur) {
                return acc + cur[0] + cur[1];
              }, display_name[0]);
            }
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_contact_params() {
        var result0;
        result0 = parse_c_p_q();

        if (result0 === null) {
          result0 = parse_c_p_expires();

          if (result0 === null) {
            result0 = parse_generic_param();
          }
        }

        return result0;
      }

      function parse_c_p_q() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 1).toLowerCase() === "q") {
          result0 = input.substr(pos, 1);
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"q\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            result2 = parse_qvalue();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, q) {
            if (!data.params) data.params = {};
            data.params['q'] = q;
          }(pos0, result0[2]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_c_p_expires() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 7).toLowerCase() === "expires") {
          result0 = input.substr(pos, 7);
          pos += 7;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"expires\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            result2 = parse_delta_seconds();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, expires) {
            if (!data.params) data.params = {};
            data.params['expires'] = expires;
          }(pos0, result0[2]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_delta_seconds() {
        var result0, result1;
        var pos0;
        pos0 = pos;
        result1 = parse_DIGIT();

        if (result1 !== null) {
          result0 = [];

          while (result1 !== null) {
            result0.push(result1);
            result1 = parse_DIGIT();
          }
        } else {
          result0 = null;
        }

        if (result0 !== null) {
          result0 = function (offset, delta_seconds) {
            return parseInt(delta_seconds.join(''));
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_qvalue() {
        var result0, result1, result2, result3, result4;
        var pos0, pos1, pos2;
        pos0 = pos;
        pos1 = pos;

        if (input.charCodeAt(pos) === 48) {
          result0 = "0";
          pos++;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"0\"");
          }
        }

        if (result0 !== null) {
          pos2 = pos;

          if (input.charCodeAt(pos) === 46) {
            result1 = ".";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\".\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_DIGIT();
            result2 = result2 !== null ? result2 : "";

            if (result2 !== null) {
              result3 = parse_DIGIT();
              result3 = result3 !== null ? result3 : "";

              if (result3 !== null) {
                result4 = parse_DIGIT();
                result4 = result4 !== null ? result4 : "";

                if (result4 !== null) {
                  result1 = [result1, result2, result3, result4];
                } else {
                  result1 = null;
                  pos = pos2;
                }
              } else {
                result1 = null;
                pos = pos2;
              }
            } else {
              result1 = null;
              pos = pos2;
            }
          } else {
            result1 = null;
            pos = pos2;
          }

          result1 = result1 !== null ? result1 : "";

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            return parseFloat(input.substring(pos, offset));
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_generic_param() {
        var result0, result1, result2;
        var pos0, pos1, pos2;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_token();

        if (result0 !== null) {
          pos2 = pos;
          result1 = parse_EQUAL();

          if (result1 !== null) {
            result2 = parse_gen_value();

            if (result2 !== null) {
              result1 = [result1, result2];
            } else {
              result1 = null;
              pos = pos2;
            }
          } else {
            result1 = null;
            pos = pos2;
          }

          result1 = result1 !== null ? result1 : "";

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, param, value) {
            if (!data.params) data.params = {};

            if (typeof value === 'undefined') {
              value = undefined;
            } else {
              value = value[1];
            }

            data.params[param.toLowerCase()] = value;
          }(pos0, result0[0], result0[1]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_gen_value() {
        var result0;
        result0 = parse_token();

        if (result0 === null) {
          result0 = parse_host();

          if (result0 === null) {
            result0 = parse_quoted_string();
          }
        }

        return result0;
      }

      function parse_Content_Disposition() {
        var result0, result1, result2, result3;
        var pos0, pos1;
        pos0 = pos;
        result0 = parse_disp_type();

        if (result0 !== null) {
          result1 = [];
          pos1 = pos;
          result2 = parse_SEMI();

          if (result2 !== null) {
            result3 = parse_disp_param();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos1;
            }
          } else {
            result2 = null;
            pos = pos1;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos1 = pos;
            result2 = parse_SEMI();

            if (result2 !== null) {
              result3 = parse_disp_param();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos1;
              }
            } else {
              result2 = null;
              pos = pos1;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_disp_type() {
        var result0;

        if (input.substr(pos, 6).toLowerCase() === "render") {
          result0 = input.substr(pos, 6);
          pos += 6;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"render\"");
          }
        }

        if (result0 === null) {
          if (input.substr(pos, 7).toLowerCase() === "session") {
            result0 = input.substr(pos, 7);
            pos += 7;
          } else {
            result0 = null;

            if (reportFailures === 0) {
              matchFailed("\"session\"");
            }
          }

          if (result0 === null) {
            if (input.substr(pos, 4).toLowerCase() === "icon") {
              result0 = input.substr(pos, 4);
              pos += 4;
            } else {
              result0 = null;

              if (reportFailures === 0) {
                matchFailed("\"icon\"");
              }
            }

            if (result0 === null) {
              if (input.substr(pos, 5).toLowerCase() === "alert") {
                result0 = input.substr(pos, 5);
                pos += 5;
              } else {
                result0 = null;

                if (reportFailures === 0) {
                  matchFailed("\"alert\"");
                }
              }

              if (result0 === null) {
                result0 = parse_token();
              }
            }
          }
        }

        return result0;
      }

      function parse_disp_param() {
        var result0;
        result0 = parse_handling_param();

        if (result0 === null) {
          result0 = parse_generic_param();
        }

        return result0;
      }

      function parse_handling_param() {
        var result0, result1, result2;
        var pos0;
        pos0 = pos;

        if (input.substr(pos, 8).toLowerCase() === "handling") {
          result0 = input.substr(pos, 8);
          pos += 8;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"handling\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            if (input.substr(pos, 8).toLowerCase() === "optional") {
              result2 = input.substr(pos, 8);
              pos += 8;
            } else {
              result2 = null;

              if (reportFailures === 0) {
                matchFailed("\"optional\"");
              }
            }

            if (result2 === null) {
              if (input.substr(pos, 8).toLowerCase() === "required") {
                result2 = input.substr(pos, 8);
                pos += 8;
              } else {
                result2 = null;

                if (reportFailures === 0) {
                  matchFailed("\"required\"");
                }
              }

              if (result2 === null) {
                result2 = parse_token();
              }
            }

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_Content_Encoding() {
        var result0, result1, result2, result3;
        var pos0, pos1;
        pos0 = pos;
        result0 = parse_token();

        if (result0 !== null) {
          result1 = [];
          pos1 = pos;
          result2 = parse_COMMA();

          if (result2 !== null) {
            result3 = parse_token();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos1;
            }
          } else {
            result2 = null;
            pos = pos1;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos1 = pos;
            result2 = parse_COMMA();

            if (result2 !== null) {
              result3 = parse_token();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos1;
              }
            } else {
              result2 = null;
              pos = pos1;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_Content_Length() {
        var result0, result1;
        var pos0;
        pos0 = pos;
        result1 = parse_DIGIT();

        if (result1 !== null) {
          result0 = [];

          while (result1 !== null) {
            result0.push(result1);
            result1 = parse_DIGIT();
          }
        } else {
          result0 = null;
        }

        if (result0 !== null) {
          result0 = function (offset, length) {
            data = parseInt(length.join(''));
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_Content_Type() {
        var result0;
        var pos0;
        pos0 = pos;
        result0 = parse_media_type();

        if (result0 !== null) {
          result0 = function (offset) {
            data = input.substring(pos, offset);
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_media_type() {
        var result0, result1, result2, result3, result4, result5;
        var pos0, pos1;
        pos0 = pos;
        result0 = parse_m_type();

        if (result0 !== null) {
          result1 = parse_SLASH();

          if (result1 !== null) {
            result2 = parse_m_subtype();

            if (result2 !== null) {
              result3 = [];
              pos1 = pos;
              result4 = parse_SEMI();

              if (result4 !== null) {
                result5 = parse_m_parameter();

                if (result5 !== null) {
                  result4 = [result4, result5];
                } else {
                  result4 = null;
                  pos = pos1;
                }
              } else {
                result4 = null;
                pos = pos1;
              }

              while (result4 !== null) {
                result3.push(result4);
                pos1 = pos;
                result4 = parse_SEMI();

                if (result4 !== null) {
                  result5 = parse_m_parameter();

                  if (result5 !== null) {
                    result4 = [result4, result5];
                  } else {
                    result4 = null;
                    pos = pos1;
                  }
                } else {
                  result4 = null;
                  pos = pos1;
                }
              }

              if (result3 !== null) {
                result0 = [result0, result1, result2, result3];
              } else {
                result0 = null;
                pos = pos0;
              }
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_m_type() {
        var result0;
        result0 = parse_discrete_type();

        if (result0 === null) {
          result0 = parse_composite_type();
        }

        return result0;
      }

      function parse_discrete_type() {
        var result0;

        if (input.substr(pos, 4).toLowerCase() === "text") {
          result0 = input.substr(pos, 4);
          pos += 4;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"text\"");
          }
        }

        if (result0 === null) {
          if (input.substr(pos, 5).toLowerCase() === "image") {
            result0 = input.substr(pos, 5);
            pos += 5;
          } else {
            result0 = null;

            if (reportFailures === 0) {
              matchFailed("\"image\"");
            }
          }

          if (result0 === null) {
            if (input.substr(pos, 5).toLowerCase() === "audio") {
              result0 = input.substr(pos, 5);
              pos += 5;
            } else {
              result0 = null;

              if (reportFailures === 0) {
                matchFailed("\"audio\"");
              }
            }

            if (result0 === null) {
              if (input.substr(pos, 5).toLowerCase() === "video") {
                result0 = input.substr(pos, 5);
                pos += 5;
              } else {
                result0 = null;

                if (reportFailures === 0) {
                  matchFailed("\"video\"");
                }
              }

              if (result0 === null) {
                if (input.substr(pos, 11).toLowerCase() === "application") {
                  result0 = input.substr(pos, 11);
                  pos += 11;
                } else {
                  result0 = null;

                  if (reportFailures === 0) {
                    matchFailed("\"application\"");
                  }
                }

                if (result0 === null) {
                  result0 = parse_extension_token();
                }
              }
            }
          }
        }

        return result0;
      }

      function parse_composite_type() {
        var result0;

        if (input.substr(pos, 7).toLowerCase() === "message") {
          result0 = input.substr(pos, 7);
          pos += 7;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"message\"");
          }
        }

        if (result0 === null) {
          if (input.substr(pos, 9).toLowerCase() === "multipart") {
            result0 = input.substr(pos, 9);
            pos += 9;
          } else {
            result0 = null;

            if (reportFailures === 0) {
              matchFailed("\"multipart\"");
            }
          }

          if (result0 === null) {
            result0 = parse_extension_token();
          }
        }

        return result0;
      }

      function parse_extension_token() {
        var result0;
        result0 = parse_token();

        if (result0 === null) {
          result0 = parse_x_token();
        }

        return result0;
      }

      function parse_x_token() {
        var result0, result1;
        var pos0;
        pos0 = pos;

        if (input.substr(pos, 2).toLowerCase() === "x-") {
          result0 = input.substr(pos, 2);
          pos += 2;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"x-\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_token();

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_m_subtype() {
        var result0;
        result0 = parse_extension_token();

        if (result0 === null) {
          result0 = parse_token();
        }

        return result0;
      }

      function parse_m_parameter() {
        var result0, result1, result2;
        var pos0;
        pos0 = pos;
        result0 = parse_token();

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            result2 = parse_m_value();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_m_value() {
        var result0;
        result0 = parse_token();

        if (result0 === null) {
          result0 = parse_quoted_string();
        }

        return result0;
      }

      function parse_CSeq() {
        var result0, result1, result2;
        var pos0;
        pos0 = pos;
        result0 = parse_CSeq_value();

        if (result0 !== null) {
          result1 = parse_LWS();

          if (result1 !== null) {
            result2 = parse_Method();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_CSeq_value() {
        var result0, result1;
        var pos0;
        pos0 = pos;
        result1 = parse_DIGIT();

        if (result1 !== null) {
          result0 = [];

          while (result1 !== null) {
            result0.push(result1);
            result1 = parse_DIGIT();
          }
        } else {
          result0 = null;
        }

        if (result0 !== null) {
          result0 = function (offset, cseq_value) {
            data.value = parseInt(cseq_value.join(''));
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_Expires() {
        var result0;
        var pos0;
        pos0 = pos;
        result0 = parse_delta_seconds();

        if (result0 !== null) {
          result0 = function (offset, expires) {
            data = expires;
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_Event() {
        var result0, result1, result2, result3;
        var pos0, pos1, pos2;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_event_type();

        if (result0 !== null) {
          result1 = [];
          pos2 = pos;
          result2 = parse_SEMI();

          if (result2 !== null) {
            result3 = parse_generic_param();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos2;
            }
          } else {
            result2 = null;
            pos = pos2;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos2 = pos;
            result2 = parse_SEMI();

            if (result2 !== null) {
              result3 = parse_generic_param();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos2;
              }
            } else {
              result2 = null;
              pos = pos2;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, event_type) {
            data.event = event_type.join('').toLowerCase();
          }(pos0, result0[0]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_event_type() {
        var result0, result1, result2, result3;
        var pos0, pos1;
        pos0 = pos;
        result0 = parse_token_nodot();

        if (result0 !== null) {
          result1 = [];
          pos1 = pos;

          if (input.charCodeAt(pos) === 46) {
            result2 = ".";
            pos++;
          } else {
            result2 = null;

            if (reportFailures === 0) {
              matchFailed("\".\"");
            }
          }

          if (result2 !== null) {
            result3 = parse_token_nodot();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos1;
            }
          } else {
            result2 = null;
            pos = pos1;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos1 = pos;

            if (input.charCodeAt(pos) === 46) {
              result2 = ".";
              pos++;
            } else {
              result2 = null;

              if (reportFailures === 0) {
                matchFailed("\".\"");
              }
            }

            if (result2 !== null) {
              result3 = parse_token_nodot();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos1;
              }
            } else {
              result2 = null;
              pos = pos1;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_From() {
        var result0, result1, result2, result3;
        var pos0, pos1, pos2;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_SIP_URI_noparams();

        if (result0 === null) {
          result0 = parse_name_addr();
        }

        if (result0 !== null) {
          result1 = [];
          pos2 = pos;
          result2 = parse_SEMI();

          if (result2 !== null) {
            result3 = parse_from_param();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos2;
            }
          } else {
            result2 = null;
            pos = pos2;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos2 = pos;
            result2 = parse_SEMI();

            if (result2 !== null) {
              result3 = parse_from_param();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos2;
              }
            } else {
              result2 = null;
              pos = pos2;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            var tag = data.tag;

            try {
              data = new NameAddrHeader(data.uri, data.display_name, data.params);

              if (tag) {
                data.setParam('tag', tag);
              }
            } catch (e) {
              data = -1;
            }
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_from_param() {
        var result0;
        result0 = parse_tag_param();

        if (result0 === null) {
          result0 = parse_generic_param();
        }

        return result0;
      }

      function parse_tag_param() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 3).toLowerCase() === "tag") {
          result0 = input.substr(pos, 3);
          pos += 3;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"tag\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            result2 = parse_token();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, tag) {
            data.tag = tag;
          }(pos0, result0[2]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_Max_Forwards() {
        var result0, result1;
        var pos0;
        pos0 = pos;
        result1 = parse_DIGIT();

        if (result1 !== null) {
          result0 = [];

          while (result1 !== null) {
            result0.push(result1);
            result1 = parse_DIGIT();
          }
        } else {
          result0 = null;
        }

        if (result0 !== null) {
          result0 = function (offset, forwards) {
            data = parseInt(forwards.join(''));
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_Min_Expires() {
        var result0;
        var pos0;
        pos0 = pos;
        result0 = parse_delta_seconds();

        if (result0 !== null) {
          result0 = function (offset, min_expires) {
            data = min_expires;
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_Name_Addr_Header() {
        var result0, result1, result2, result3, result4, result5, result6;
        var pos0, pos1, pos2;
        pos0 = pos;
        pos1 = pos;
        result0 = [];
        result1 = parse_display_name();

        while (result1 !== null) {
          result0.push(result1);
          result1 = parse_display_name();
        }

        if (result0 !== null) {
          result1 = parse_LAQUOT();

          if (result1 !== null) {
            result2 = parse_SIP_URI();

            if (result2 !== null) {
              result3 = parse_RAQUOT();

              if (result3 !== null) {
                result4 = [];
                pos2 = pos;
                result5 = parse_SEMI();

                if (result5 !== null) {
                  result6 = parse_generic_param();

                  if (result6 !== null) {
                    result5 = [result5, result6];
                  } else {
                    result5 = null;
                    pos = pos2;
                  }
                } else {
                  result5 = null;
                  pos = pos2;
                }

                while (result5 !== null) {
                  result4.push(result5);
                  pos2 = pos;
                  result5 = parse_SEMI();

                  if (result5 !== null) {
                    result6 = parse_generic_param();

                    if (result6 !== null) {
                      result5 = [result5, result6];
                    } else {
                      result5 = null;
                      pos = pos2;
                    }
                  } else {
                    result5 = null;
                    pos = pos2;
                  }
                }

                if (result4 !== null) {
                  result0 = [result0, result1, result2, result3, result4];
                } else {
                  result0 = null;
                  pos = pos1;
                }
              } else {
                result0 = null;
                pos = pos1;
              }
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            try {
              data = new NameAddrHeader(data.uri, data.display_name, data.params);
            } catch (e) {
              data = -1;
            }
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_Proxy_Authenticate() {
        var result0;
        result0 = parse_challenge();
        return result0;
      }

      function parse_challenge() {
        var result0, result1, result2, result3, result4, result5;
        var pos0, pos1;
        pos0 = pos;

        if (input.substr(pos, 6).toLowerCase() === "digest") {
          result0 = input.substr(pos, 6);
          pos += 6;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"Digest\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_LWS();

          if (result1 !== null) {
            result2 = parse_digest_cln();

            if (result2 !== null) {
              result3 = [];
              pos1 = pos;
              result4 = parse_COMMA();

              if (result4 !== null) {
                result5 = parse_digest_cln();

                if (result5 !== null) {
                  result4 = [result4, result5];
                } else {
                  result4 = null;
                  pos = pos1;
                }
              } else {
                result4 = null;
                pos = pos1;
              }

              while (result4 !== null) {
                result3.push(result4);
                pos1 = pos;
                result4 = parse_COMMA();

                if (result4 !== null) {
                  result5 = parse_digest_cln();

                  if (result5 !== null) {
                    result4 = [result4, result5];
                  } else {
                    result4 = null;
                    pos = pos1;
                  }
                } else {
                  result4 = null;
                  pos = pos1;
                }
              }

              if (result3 !== null) {
                result0 = [result0, result1, result2, result3];
              } else {
                result0 = null;
                pos = pos0;
              }
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        if (result0 === null) {
          result0 = parse_other_challenge();
        }

        return result0;
      }

      function parse_other_challenge() {
        var result0, result1, result2, result3, result4, result5;
        var pos0, pos1;
        pos0 = pos;
        result0 = parse_token();

        if (result0 !== null) {
          result1 = parse_LWS();

          if (result1 !== null) {
            result2 = parse_auth_param();

            if (result2 !== null) {
              result3 = [];
              pos1 = pos;
              result4 = parse_COMMA();

              if (result4 !== null) {
                result5 = parse_auth_param();

                if (result5 !== null) {
                  result4 = [result4, result5];
                } else {
                  result4 = null;
                  pos = pos1;
                }
              } else {
                result4 = null;
                pos = pos1;
              }

              while (result4 !== null) {
                result3.push(result4);
                pos1 = pos;
                result4 = parse_COMMA();

                if (result4 !== null) {
                  result5 = parse_auth_param();

                  if (result5 !== null) {
                    result4 = [result4, result5];
                  } else {
                    result4 = null;
                    pos = pos1;
                  }
                } else {
                  result4 = null;
                  pos = pos1;
                }
              }

              if (result3 !== null) {
                result0 = [result0, result1, result2, result3];
              } else {
                result0 = null;
                pos = pos0;
              }
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_auth_param() {
        var result0, result1, result2;
        var pos0;
        pos0 = pos;
        result0 = parse_token();

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            result2 = parse_token();

            if (result2 === null) {
              result2 = parse_quoted_string();
            }

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_digest_cln() {
        var result0;
        result0 = parse_realm();

        if (result0 === null) {
          result0 = parse_domain();

          if (result0 === null) {
            result0 = parse_nonce();

            if (result0 === null) {
              result0 = parse_opaque();

              if (result0 === null) {
                result0 = parse_stale();

                if (result0 === null) {
                  result0 = parse_algorithm();

                  if (result0 === null) {
                    result0 = parse_qop_options();

                    if (result0 === null) {
                      result0 = parse_auth_param();
                    }
                  }
                }
              }
            }
          }
        }

        return result0;
      }

      function parse_realm() {
        var result0, result1, result2;
        var pos0;
        pos0 = pos;

        if (input.substr(pos, 5).toLowerCase() === "realm") {
          result0 = input.substr(pos, 5);
          pos += 5;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"realm\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            result2 = parse_realm_value();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_realm_value() {
        var result0;
        var pos0;
        pos0 = pos;
        result0 = parse_quoted_string_clean();

        if (result0 !== null) {
          result0 = function (offset, realm) {
            data.realm = realm;
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_domain() {
        var result0, result1, result2, result3, result4, result5, result6;
        var pos0, pos1;
        pos0 = pos;

        if (input.substr(pos, 6).toLowerCase() === "domain") {
          result0 = input.substr(pos, 6);
          pos += 6;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"domain\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            result2 = parse_LDQUOT();

            if (result2 !== null) {
              result3 = parse_URI();

              if (result3 !== null) {
                result4 = [];
                pos1 = pos;
                result6 = parse_SP();

                if (result6 !== null) {
                  result5 = [];

                  while (result6 !== null) {
                    result5.push(result6);
                    result6 = parse_SP();
                  }
                } else {
                  result5 = null;
                }

                if (result5 !== null) {
                  result6 = parse_URI();

                  if (result6 !== null) {
                    result5 = [result5, result6];
                  } else {
                    result5 = null;
                    pos = pos1;
                  }
                } else {
                  result5 = null;
                  pos = pos1;
                }

                while (result5 !== null) {
                  result4.push(result5);
                  pos1 = pos;
                  result6 = parse_SP();

                  if (result6 !== null) {
                    result5 = [];

                    while (result6 !== null) {
                      result5.push(result6);
                      result6 = parse_SP();
                    }
                  } else {
                    result5 = null;
                  }

                  if (result5 !== null) {
                    result6 = parse_URI();

                    if (result6 !== null) {
                      result5 = [result5, result6];
                    } else {
                      result5 = null;
                      pos = pos1;
                    }
                  } else {
                    result5 = null;
                    pos = pos1;
                  }
                }

                if (result4 !== null) {
                  result5 = parse_RDQUOT();

                  if (result5 !== null) {
                    result0 = [result0, result1, result2, result3, result4, result5];
                  } else {
                    result0 = null;
                    pos = pos0;
                  }
                } else {
                  result0 = null;
                  pos = pos0;
                }
              } else {
                result0 = null;
                pos = pos0;
              }
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_URI() {
        var result0;
        result0 = parse_absoluteURI();

        if (result0 === null) {
          result0 = parse_abs_path();
        }

        return result0;
      }

      function parse_nonce() {
        var result0, result1, result2;
        var pos0;
        pos0 = pos;

        if (input.substr(pos, 5).toLowerCase() === "nonce") {
          result0 = input.substr(pos, 5);
          pos += 5;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"nonce\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            result2 = parse_nonce_value();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_nonce_value() {
        var result0;
        var pos0;
        pos0 = pos;
        result0 = parse_quoted_string_clean();

        if (result0 !== null) {
          result0 = function (offset, nonce) {
            data.nonce = nonce;
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_opaque() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 6).toLowerCase() === "opaque") {
          result0 = input.substr(pos, 6);
          pos += 6;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"opaque\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            result2 = parse_quoted_string_clean();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, opaque) {
            data.opaque = opaque;
          }(pos0, result0[2]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_stale() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;

        if (input.substr(pos, 5).toLowerCase() === "stale") {
          result0 = input.substr(pos, 5);
          pos += 5;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"stale\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            pos1 = pos;

            if (input.substr(pos, 4).toLowerCase() === "true") {
              result2 = input.substr(pos, 4);
              pos += 4;
            } else {
              result2 = null;

              if (reportFailures === 0) {
                matchFailed("\"true\"");
              }
            }

            if (result2 !== null) {
              result2 = function (offset) {
                data.stale = true;
              }(pos1);
            }

            if (result2 === null) {
              pos = pos1;
            }

            if (result2 === null) {
              pos1 = pos;

              if (input.substr(pos, 5).toLowerCase() === "false") {
                result2 = input.substr(pos, 5);
                pos += 5;
              } else {
                result2 = null;

                if (reportFailures === 0) {
                  matchFailed("\"false\"");
                }
              }

              if (result2 !== null) {
                result2 = function (offset) {
                  data.stale = false;
                }(pos1);
              }

              if (result2 === null) {
                pos = pos1;
              }
            }

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_algorithm() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 9).toLowerCase() === "algorithm") {
          result0 = input.substr(pos, 9);
          pos += 9;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"algorithm\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            if (input.substr(pos, 3).toLowerCase() === "md5") {
              result2 = input.substr(pos, 3);
              pos += 3;
            } else {
              result2 = null;

              if (reportFailures === 0) {
                matchFailed("\"MD5\"");
              }
            }

            if (result2 === null) {
              if (input.substr(pos, 8).toLowerCase() === "md5-sess") {
                result2 = input.substr(pos, 8);
                pos += 8;
              } else {
                result2 = null;

                if (reportFailures === 0) {
                  matchFailed("\"MD5-sess\"");
                }
              }

              if (result2 === null) {
                result2 = parse_token();
              }
            }

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, algorithm) {
            data.algorithm = algorithm.toUpperCase();
          }(pos0, result0[2]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_qop_options() {
        var result0, result1, result2, result3, result4, result5, result6;
        var pos0, pos1, pos2;
        pos0 = pos;

        if (input.substr(pos, 3).toLowerCase() === "qop") {
          result0 = input.substr(pos, 3);
          pos += 3;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"qop\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            result2 = parse_LDQUOT();

            if (result2 !== null) {
              pos1 = pos;
              result3 = parse_qop_value();

              if (result3 !== null) {
                result4 = [];
                pos2 = pos;

                if (input.charCodeAt(pos) === 44) {
                  result5 = ",";
                  pos++;
                } else {
                  result5 = null;

                  if (reportFailures === 0) {
                    matchFailed("\",\"");
                  }
                }

                if (result5 !== null) {
                  result6 = parse_qop_value();

                  if (result6 !== null) {
                    result5 = [result5, result6];
                  } else {
                    result5 = null;
                    pos = pos2;
                  }
                } else {
                  result5 = null;
                  pos = pos2;
                }

                while (result5 !== null) {
                  result4.push(result5);
                  pos2 = pos;

                  if (input.charCodeAt(pos) === 44) {
                    result5 = ",";
                    pos++;
                  } else {
                    result5 = null;

                    if (reportFailures === 0) {
                      matchFailed("\",\"");
                    }
                  }

                  if (result5 !== null) {
                    result6 = parse_qop_value();

                    if (result6 !== null) {
                      result5 = [result5, result6];
                    } else {
                      result5 = null;
                      pos = pos2;
                    }
                  } else {
                    result5 = null;
                    pos = pos2;
                  }
                }

                if (result4 !== null) {
                  result3 = [result3, result4];
                } else {
                  result3 = null;
                  pos = pos1;
                }
              } else {
                result3 = null;
                pos = pos1;
              }

              if (result3 !== null) {
                result4 = parse_RDQUOT();

                if (result4 !== null) {
                  result0 = [result0, result1, result2, result3, result4];
                } else {
                  result0 = null;
                  pos = pos0;
                }
              } else {
                result0 = null;
                pos = pos0;
              }
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_qop_value() {
        var result0;
        var pos0;
        pos0 = pos;

        if (input.substr(pos, 8).toLowerCase() === "auth-int") {
          result0 = input.substr(pos, 8);
          pos += 8;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"auth-int\"");
          }
        }

        if (result0 === null) {
          if (input.substr(pos, 4).toLowerCase() === "auth") {
            result0 = input.substr(pos, 4);
            pos += 4;
          } else {
            result0 = null;

            if (reportFailures === 0) {
              matchFailed("\"auth\"");
            }
          }

          if (result0 === null) {
            result0 = parse_token();
          }
        }

        if (result0 !== null) {
          result0 = function (offset, qop_value) {
            data.qop || (data.qop = []);
            data.qop.push(qop_value.toLowerCase());
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_Proxy_Require() {
        var result0, result1, result2, result3;
        var pos0, pos1;
        pos0 = pos;
        result0 = parse_token();

        if (result0 !== null) {
          result1 = [];
          pos1 = pos;
          result2 = parse_COMMA();

          if (result2 !== null) {
            result3 = parse_token();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos1;
            }
          } else {
            result2 = null;
            pos = pos1;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos1 = pos;
            result2 = parse_COMMA();

            if (result2 !== null) {
              result3 = parse_token();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos1;
              }
            } else {
              result2 = null;
              pos = pos1;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_Record_Route() {
        var result0, result1, result2, result3;
        var pos0, pos1, pos2;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_rec_route();

        if (result0 !== null) {
          result1 = [];
          pos2 = pos;
          result2 = parse_COMMA();

          if (result2 !== null) {
            result3 = parse_rec_route();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos2;
            }
          } else {
            result2 = null;
            pos = pos2;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos2 = pos;
            result2 = parse_COMMA();

            if (result2 !== null) {
              result3 = parse_rec_route();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos2;
              }
            } else {
              result2 = null;
              pos = pos2;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            var idx, length;
            length = data.multi_header.length;

            for (idx = 0; idx < length; idx++) {
              if (data.multi_header[idx].parsed === null) {
                data = null;
                break;
              }
            }

            if (data !== null) {
              data = data.multi_header;
            } else {
              data = -1;
            }
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_rec_route() {
        var result0, result1, result2, result3;
        var pos0, pos1, pos2;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_name_addr();

        if (result0 !== null) {
          result1 = [];
          pos2 = pos;
          result2 = parse_SEMI();

          if (result2 !== null) {
            result3 = parse_generic_param();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos2;
            }
          } else {
            result2 = null;
            pos = pos2;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos2 = pos;
            result2 = parse_SEMI();

            if (result2 !== null) {
              result3 = parse_generic_param();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos2;
              }
            } else {
              result2 = null;
              pos = pos2;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            var header;
            if (!data.multi_header) data.multi_header = [];

            try {
              header = new NameAddrHeader(data.uri, data.display_name, data.params);
              delete data.uri;
              delete data.display_name;
              delete data.params;
            } catch (e) {
              header = null;
            }

            data.multi_header.push({
              'possition': pos,
              'offset': offset,
              'parsed': header
            });
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_Reason() {
        var result0, result1, result2, result3;
        var pos0, pos1, pos2;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 3).toLowerCase() === "sip") {
          result0 = input.substr(pos, 3);
          pos += 3;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"SIP\"");
          }
        }

        if (result0 === null) {
          result0 = parse_token();
        }

        if (result0 !== null) {
          result1 = [];
          pos2 = pos;
          result2 = parse_SEMI();

          if (result2 !== null) {
            result3 = parse_reason_param();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos2;
            }
          } else {
            result2 = null;
            pos = pos2;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos2 = pos;
            result2 = parse_SEMI();

            if (result2 !== null) {
              result3 = parse_reason_param();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos2;
              }
            } else {
              result2 = null;
              pos = pos2;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, protocol) {
            data.protocol = protocol.toLowerCase();
            if (!data.params) data.params = {};

            if (data.params.text && data.params.text[0] === '"') {
              var text = data.params.text;
              data.text = text.substring(1, text.length - 1);
              delete data.params.text;
            }
          }(pos0, result0[0]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_reason_param() {
        var result0;
        result0 = parse_reason_cause();

        if (result0 === null) {
          result0 = parse_generic_param();
        }

        return result0;
      }

      function parse_reason_cause() {
        var result0, result1, result2, result3;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 5).toLowerCase() === "cause") {
          result0 = input.substr(pos, 5);
          pos += 5;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"cause\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            result3 = parse_DIGIT();

            if (result3 !== null) {
              result2 = [];

              while (result3 !== null) {
                result2.push(result3);
                result3 = parse_DIGIT();
              }
            } else {
              result2 = null;
            }

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, cause) {
            data.cause = parseInt(cause.join(''));
          }(pos0, result0[2]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_Require() {
        var result0, result1, result2, result3;
        var pos0, pos1;
        pos0 = pos;
        result0 = parse_token();

        if (result0 !== null) {
          result1 = [];
          pos1 = pos;
          result2 = parse_COMMA();

          if (result2 !== null) {
            result3 = parse_token();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos1;
            }
          } else {
            result2 = null;
            pos = pos1;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos1 = pos;
            result2 = parse_COMMA();

            if (result2 !== null) {
              result3 = parse_token();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos1;
              }
            } else {
              result2 = null;
              pos = pos1;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_Route() {
        var result0, result1, result2, result3;
        var pos0, pos1;
        pos0 = pos;
        result0 = parse_route_param();

        if (result0 !== null) {
          result1 = [];
          pos1 = pos;
          result2 = parse_COMMA();

          if (result2 !== null) {
            result3 = parse_route_param();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos1;
            }
          } else {
            result2 = null;
            pos = pos1;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos1 = pos;
            result2 = parse_COMMA();

            if (result2 !== null) {
              result3 = parse_route_param();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos1;
              }
            } else {
              result2 = null;
              pos = pos1;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_route_param() {
        var result0, result1, result2, result3;
        var pos0, pos1;
        pos0 = pos;
        result0 = parse_name_addr();

        if (result0 !== null) {
          result1 = [];
          pos1 = pos;
          result2 = parse_SEMI();

          if (result2 !== null) {
            result3 = parse_generic_param();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos1;
            }
          } else {
            result2 = null;
            pos = pos1;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos1 = pos;
            result2 = parse_SEMI();

            if (result2 !== null) {
              result3 = parse_generic_param();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos1;
              }
            } else {
              result2 = null;
              pos = pos1;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_Subscription_State() {
        var result0, result1, result2, result3;
        var pos0, pos1;
        pos0 = pos;
        result0 = parse_substate_value();

        if (result0 !== null) {
          result1 = [];
          pos1 = pos;
          result2 = parse_SEMI();

          if (result2 !== null) {
            result3 = parse_subexp_params();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos1;
            }
          } else {
            result2 = null;
            pos = pos1;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos1 = pos;
            result2 = parse_SEMI();

            if (result2 !== null) {
              result3 = parse_subexp_params();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos1;
              }
            } else {
              result2 = null;
              pos = pos1;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_substate_value() {
        var result0;
        var pos0;
        pos0 = pos;

        if (input.substr(pos, 6).toLowerCase() === "active") {
          result0 = input.substr(pos, 6);
          pos += 6;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"active\"");
          }
        }

        if (result0 === null) {
          if (input.substr(pos, 7).toLowerCase() === "pending") {
            result0 = input.substr(pos, 7);
            pos += 7;
          } else {
            result0 = null;

            if (reportFailures === 0) {
              matchFailed("\"pending\"");
            }
          }

          if (result0 === null) {
            if (input.substr(pos, 10).toLowerCase() === "terminated") {
              result0 = input.substr(pos, 10);
              pos += 10;
            } else {
              result0 = null;

              if (reportFailures === 0) {
                matchFailed("\"terminated\"");
              }
            }

            if (result0 === null) {
              result0 = parse_token();
            }
          }
        }

        if (result0 !== null) {
          result0 = function (offset) {
            data.state = input.substring(pos, offset);
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_subexp_params() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 6).toLowerCase() === "reason") {
          result0 = input.substr(pos, 6);
          pos += 6;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"reason\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            result2 = parse_event_reason_value();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, reason) {
            if (typeof reason !== 'undefined') data.reason = reason;
          }(pos0, result0[2]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        if (result0 === null) {
          pos0 = pos;
          pos1 = pos;

          if (input.substr(pos, 7).toLowerCase() === "expires") {
            result0 = input.substr(pos, 7);
            pos += 7;
          } else {
            result0 = null;

            if (reportFailures === 0) {
              matchFailed("\"expires\"");
            }
          }

          if (result0 !== null) {
            result1 = parse_EQUAL();

            if (result1 !== null) {
              result2 = parse_delta_seconds();

              if (result2 !== null) {
                result0 = [result0, result1, result2];
              } else {
                result0 = null;
                pos = pos1;
              }
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }

          if (result0 !== null) {
            result0 = function (offset, expires) {
              if (typeof expires !== 'undefined') data.expires = expires;
            }(pos0, result0[2]);
          }

          if (result0 === null) {
            pos = pos0;
          }

          if (result0 === null) {
            pos0 = pos;
            pos1 = pos;

            if (input.substr(pos, 11).toLowerCase() === "retry_after") {
              result0 = input.substr(pos, 11);
              pos += 11;
            } else {
              result0 = null;

              if (reportFailures === 0) {
                matchFailed("\"retry_after\"");
              }
            }

            if (result0 !== null) {
              result1 = parse_EQUAL();

              if (result1 !== null) {
                result2 = parse_delta_seconds();

                if (result2 !== null) {
                  result0 = [result0, result1, result2];
                } else {
                  result0 = null;
                  pos = pos1;
                }
              } else {
                result0 = null;
                pos = pos1;
              }
            } else {
              result0 = null;
              pos = pos1;
            }

            if (result0 !== null) {
              result0 = function (offset, retry_after) {
                if (typeof retry_after !== 'undefined') data.retry_after = retry_after;
              }(pos0, result0[2]);
            }

            if (result0 === null) {
              pos = pos0;
            }

            if (result0 === null) {
              result0 = parse_generic_param();
            }
          }
        }

        return result0;
      }

      function parse_event_reason_value() {
        var result0;

        if (input.substr(pos, 11).toLowerCase() === "deactivated") {
          result0 = input.substr(pos, 11);
          pos += 11;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"deactivated\"");
          }
        }

        if (result0 === null) {
          if (input.substr(pos, 9).toLowerCase() === "probation") {
            result0 = input.substr(pos, 9);
            pos += 9;
          } else {
            result0 = null;

            if (reportFailures === 0) {
              matchFailed("\"probation\"");
            }
          }

          if (result0 === null) {
            if (input.substr(pos, 8).toLowerCase() === "rejected") {
              result0 = input.substr(pos, 8);
              pos += 8;
            } else {
              result0 = null;

              if (reportFailures === 0) {
                matchFailed("\"rejected\"");
              }
            }

            if (result0 === null) {
              if (input.substr(pos, 7).toLowerCase() === "timeout") {
                result0 = input.substr(pos, 7);
                pos += 7;
              } else {
                result0 = null;

                if (reportFailures === 0) {
                  matchFailed("\"timeout\"");
                }
              }

              if (result0 === null) {
                if (input.substr(pos, 6).toLowerCase() === "giveup") {
                  result0 = input.substr(pos, 6);
                  pos += 6;
                } else {
                  result0 = null;

                  if (reportFailures === 0) {
                    matchFailed("\"giveup\"");
                  }
                }

                if (result0 === null) {
                  if (input.substr(pos, 10).toLowerCase() === "noresource") {
                    result0 = input.substr(pos, 10);
                    pos += 10;
                  } else {
                    result0 = null;

                    if (reportFailures === 0) {
                      matchFailed("\"noresource\"");
                    }
                  }

                  if (result0 === null) {
                    if (input.substr(pos, 9).toLowerCase() === "invariant") {
                      result0 = input.substr(pos, 9);
                      pos += 9;
                    } else {
                      result0 = null;

                      if (reportFailures === 0) {
                        matchFailed("\"invariant\"");
                      }
                    }

                    if (result0 === null) {
                      result0 = parse_token();
                    }
                  }
                }
              }
            }
          }
        }

        return result0;
      }

      function parse_Subject() {
        var result0;
        result0 = parse_TEXT_UTF8_TRIM();
        result0 = result0 !== null ? result0 : "";
        return result0;
      }

      function parse_Supported() {
        var result0, result1, result2, result3;
        var pos0, pos1;
        pos0 = pos;
        result0 = parse_token();

        if (result0 !== null) {
          result1 = [];
          pos1 = pos;
          result2 = parse_COMMA();

          if (result2 !== null) {
            result3 = parse_token();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos1;
            }
          } else {
            result2 = null;
            pos = pos1;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos1 = pos;
            result2 = parse_COMMA();

            if (result2 !== null) {
              result3 = parse_token();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos1;
              }
            } else {
              result2 = null;
              pos = pos1;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        result0 = result0 !== null ? result0 : "";
        return result0;
      }

      function parse_To() {
        var result0, result1, result2, result3;
        var pos0, pos1, pos2;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_SIP_URI_noparams();

        if (result0 === null) {
          result0 = parse_name_addr();
        }

        if (result0 !== null) {
          result1 = [];
          pos2 = pos;
          result2 = parse_SEMI();

          if (result2 !== null) {
            result3 = parse_to_param();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos2;
            }
          } else {
            result2 = null;
            pos = pos2;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos2 = pos;
            result2 = parse_SEMI();

            if (result2 !== null) {
              result3 = parse_to_param();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos2;
              }
            } else {
              result2 = null;
              pos = pos2;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            var tag = data.tag;

            try {
              data = new NameAddrHeader(data.uri, data.display_name, data.params);

              if (tag) {
                data.setParam('tag', tag);
              }
            } catch (e) {
              data = -1;
            }
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_to_param() {
        var result0;
        result0 = parse_tag_param();

        if (result0 === null) {
          result0 = parse_generic_param();
        }

        return result0;
      }

      function parse_Via() {
        var result0, result1, result2, result3;
        var pos0, pos1;
        pos0 = pos;
        result0 = parse_via_param();

        if (result0 !== null) {
          result1 = [];
          pos1 = pos;
          result2 = parse_COMMA();

          if (result2 !== null) {
            result3 = parse_via_param();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos1;
            }
          } else {
            result2 = null;
            pos = pos1;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos1 = pos;
            result2 = parse_COMMA();

            if (result2 !== null) {
              result3 = parse_via_param();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos1;
              }
            } else {
              result2 = null;
              pos = pos1;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_via_param() {
        var result0, result1, result2, result3, result4, result5;
        var pos0, pos1;
        pos0 = pos;
        result0 = parse_sent_protocol();

        if (result0 !== null) {
          result1 = parse_LWS();

          if (result1 !== null) {
            result2 = parse_sent_by();

            if (result2 !== null) {
              result3 = [];
              pos1 = pos;
              result4 = parse_SEMI();

              if (result4 !== null) {
                result5 = parse_via_params();

                if (result5 !== null) {
                  result4 = [result4, result5];
                } else {
                  result4 = null;
                  pos = pos1;
                }
              } else {
                result4 = null;
                pos = pos1;
              }

              while (result4 !== null) {
                result3.push(result4);
                pos1 = pos;
                result4 = parse_SEMI();

                if (result4 !== null) {
                  result5 = parse_via_params();

                  if (result5 !== null) {
                    result4 = [result4, result5];
                  } else {
                    result4 = null;
                    pos = pos1;
                  }
                } else {
                  result4 = null;
                  pos = pos1;
                }
              }

              if (result3 !== null) {
                result0 = [result0, result1, result2, result3];
              } else {
                result0 = null;
                pos = pos0;
              }
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_via_params() {
        var result0;
        result0 = parse_via_ttl();

        if (result0 === null) {
          result0 = parse_via_maddr();

          if (result0 === null) {
            result0 = parse_via_received();

            if (result0 === null) {
              result0 = parse_via_branch();

              if (result0 === null) {
                result0 = parse_response_port();

                if (result0 === null) {
                  result0 = parse_generic_param();
                }
              }
            }
          }
        }

        return result0;
      }

      function parse_via_ttl() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 3).toLowerCase() === "ttl") {
          result0 = input.substr(pos, 3);
          pos += 3;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"ttl\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            result2 = parse_ttl();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, via_ttl_value) {
            data.ttl = via_ttl_value;
          }(pos0, result0[2]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_via_maddr() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 5).toLowerCase() === "maddr") {
          result0 = input.substr(pos, 5);
          pos += 5;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"maddr\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            result2 = parse_host();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, via_maddr) {
            data.maddr = via_maddr;
          }(pos0, result0[2]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_via_received() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 8).toLowerCase() === "received") {
          result0 = input.substr(pos, 8);
          pos += 8;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"received\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            result2 = parse_IPv4address();

            if (result2 === null) {
              result2 = parse_IPv6address();
            }

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, via_received) {
            data.received = via_received;
          }(pos0, result0[2]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_via_branch() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 6).toLowerCase() === "branch") {
          result0 = input.substr(pos, 6);
          pos += 6;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"branch\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            result2 = parse_token();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, via_branch) {
            data.branch = via_branch;
          }(pos0, result0[2]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_response_port() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;

        if (input.substr(pos, 5).toLowerCase() === "rport") {
          result0 = input.substr(pos, 5);
          pos += 5;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"rport\"");
          }
        }

        if (result0 !== null) {
          pos1 = pos;
          result1 = parse_EQUAL();

          if (result1 !== null) {
            result2 = parse_rport();

            if (result2 !== null) {
              result1 = [result1, result2];
            } else {
              result1 = null;
              pos = pos1;
            }
          } else {
            result1 = null;
            pos = pos1;
          }

          result1 = result1 !== null ? result1 : "";

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_rport() {
        var result0, result1, result2, result3, result4;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_DIGIT();
        result0 = result0 !== null ? result0 : "";

        if (result0 !== null) {
          result1 = parse_DIGIT();
          result1 = result1 !== null ? result1 : "";

          if (result1 !== null) {
            result2 = parse_DIGIT();
            result2 = result2 !== null ? result2 : "";

            if (result2 !== null) {
              result3 = parse_DIGIT();
              result3 = result3 !== null ? result3 : "";

              if (result3 !== null) {
                result4 = parse_DIGIT();
                result4 = result4 !== null ? result4 : "";

                if (result4 !== null) {
                  result0 = [result0, result1, result2, result3, result4];
                } else {
                  result0 = null;
                  pos = pos1;
                }
              } else {
                result0 = null;
                pos = pos1;
              }
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, rport) {
            data.rport = parseInt(rport.join(''));
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_sent_protocol() {
        var result0, result1, result2, result3, result4;
        var pos0;
        pos0 = pos;
        result0 = parse_protocol_name();

        if (result0 !== null) {
          result1 = parse_SLASH();

          if (result1 !== null) {
            result2 = parse_token();

            if (result2 !== null) {
              result3 = parse_SLASH();

              if (result3 !== null) {
                result4 = parse_transport();

                if (result4 !== null) {
                  result0 = [result0, result1, result2, result3, result4];
                } else {
                  result0 = null;
                  pos = pos0;
                }
              } else {
                result0 = null;
                pos = pos0;
              }
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_protocol_name() {
        var result0;
        var pos0;
        pos0 = pos;

        if (input.substr(pos, 3).toLowerCase() === "sip") {
          result0 = input.substr(pos, 3);
          pos += 3;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"SIP\"");
          }
        }

        if (result0 === null) {
          result0 = parse_token();
        }

        if (result0 !== null) {
          result0 = function (offset, via_protocol) {
            data.protocol = via_protocol;
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_transport() {
        var result0;
        var pos0;
        pos0 = pos;

        if (input.substr(pos, 3).toLowerCase() === "udp") {
          result0 = input.substr(pos, 3);
          pos += 3;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"UDP\"");
          }
        }

        if (result0 === null) {
          if (input.substr(pos, 3).toLowerCase() === "tcp") {
            result0 = input.substr(pos, 3);
            pos += 3;
          } else {
            result0 = null;

            if (reportFailures === 0) {
              matchFailed("\"TCP\"");
            }
          }

          if (result0 === null) {
            if (input.substr(pos, 3).toLowerCase() === "tls") {
              result0 = input.substr(pos, 3);
              pos += 3;
            } else {
              result0 = null;

              if (reportFailures === 0) {
                matchFailed("\"TLS\"");
              }
            }

            if (result0 === null) {
              if (input.substr(pos, 4).toLowerCase() === "sctp") {
                result0 = input.substr(pos, 4);
                pos += 4;
              } else {
                result0 = null;

                if (reportFailures === 0) {
                  matchFailed("\"SCTP\"");
                }
              }

              if (result0 === null) {
                result0 = parse_token();
              }
            }
          }
        }

        if (result0 !== null) {
          result0 = function (offset, via_transport) {
            data.transport = via_transport;
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_sent_by() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        result0 = parse_via_host();

        if (result0 !== null) {
          pos1 = pos;
          result1 = parse_COLON();

          if (result1 !== null) {
            result2 = parse_via_port();

            if (result2 !== null) {
              result1 = [result1, result2];
            } else {
              result1 = null;
              pos = pos1;
            }
          } else {
            result1 = null;
            pos = pos1;
          }

          result1 = result1 !== null ? result1 : "";

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_via_host() {
        var result0;
        var pos0;
        pos0 = pos;
        result0 = parse_IPv4address();

        if (result0 === null) {
          result0 = parse_IPv6reference();

          if (result0 === null) {
            result0 = parse_hostname();
          }
        }

        if (result0 !== null) {
          result0 = function (offset) {
            data.host = input.substring(pos, offset);
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_via_port() {
        var result0, result1, result2, result3, result4;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_DIGIT();
        result0 = result0 !== null ? result0 : "";

        if (result0 !== null) {
          result1 = parse_DIGIT();
          result1 = result1 !== null ? result1 : "";

          if (result1 !== null) {
            result2 = parse_DIGIT();
            result2 = result2 !== null ? result2 : "";

            if (result2 !== null) {
              result3 = parse_DIGIT();
              result3 = result3 !== null ? result3 : "";

              if (result3 !== null) {
                result4 = parse_DIGIT();
                result4 = result4 !== null ? result4 : "";

                if (result4 !== null) {
                  result0 = [result0, result1, result2, result3, result4];
                } else {
                  result0 = null;
                  pos = pos1;
                }
              } else {
                result0 = null;
                pos = pos1;
              }
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, via_sent_by_port) {
            data.port = parseInt(via_sent_by_port.join(''));
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_ttl() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_DIGIT();

        if (result0 !== null) {
          result1 = parse_DIGIT();
          result1 = result1 !== null ? result1 : "";

          if (result1 !== null) {
            result2 = parse_DIGIT();
            result2 = result2 !== null ? result2 : "";

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, ttl) {
            return parseInt(ttl.join(''));
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_WWW_Authenticate() {
        var result0;
        result0 = parse_challenge();
        return result0;
      }

      function parse_Session_Expires() {
        var result0, result1, result2, result3;
        var pos0, pos1;
        pos0 = pos;
        result0 = parse_s_e_expires();

        if (result0 !== null) {
          result1 = [];
          pos1 = pos;
          result2 = parse_SEMI();

          if (result2 !== null) {
            result3 = parse_s_e_params();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos1;
            }
          } else {
            result2 = null;
            pos = pos1;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos1 = pos;
            result2 = parse_SEMI();

            if (result2 !== null) {
              result3 = parse_s_e_params();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos1;
              }
            } else {
              result2 = null;
              pos = pos1;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_s_e_expires() {
        var result0;
        var pos0;
        pos0 = pos;
        result0 = parse_delta_seconds();

        if (result0 !== null) {
          result0 = function (offset, expires) {
            data.expires = expires;
          }(pos0, result0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_s_e_params() {
        var result0;
        result0 = parse_s_e_refresher();

        if (result0 === null) {
          result0 = parse_generic_param();
        }

        return result0;
      }

      function parse_s_e_refresher() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 9).toLowerCase() === "refresher") {
          result0 = input.substr(pos, 9);
          pos += 9;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"refresher\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            if (input.substr(pos, 3).toLowerCase() === "uac") {
              result2 = input.substr(pos, 3);
              pos += 3;
            } else {
              result2 = null;

              if (reportFailures === 0) {
                matchFailed("\"uac\"");
              }
            }

            if (result2 === null) {
              if (input.substr(pos, 3).toLowerCase() === "uas") {
                result2 = input.substr(pos, 3);
                pos += 3;
              } else {
                result2 = null;

                if (reportFailures === 0) {
                  matchFailed("\"uas\"");
                }
              }
            }

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, s_e_refresher_value) {
            data.refresher = s_e_refresher_value.toLowerCase();
          }(pos0, result0[2]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_extension_header() {
        var result0, result1, result2;
        var pos0;
        pos0 = pos;
        result0 = parse_token();

        if (result0 !== null) {
          result1 = parse_HCOLON();

          if (result1 !== null) {
            result2 = parse_header_value();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_header_value() {
        var result0, result1;
        result0 = [];
        result1 = parse_TEXT_UTF8char();

        if (result1 === null) {
          result1 = parse_UTF8_CONT();

          if (result1 === null) {
            result1 = parse_LWS();
          }
        }

        while (result1 !== null) {
          result0.push(result1);
          result1 = parse_TEXT_UTF8char();

          if (result1 === null) {
            result1 = parse_UTF8_CONT();

            if (result1 === null) {
              result1 = parse_LWS();
            }
          }
        }

        return result0;
      }

      function parse_message_body() {
        var result0, result1;
        result0 = [];
        result1 = parse_OCTET();

        while (result1 !== null) {
          result0.push(result1);
          result1 = parse_OCTET();
        }

        return result0;
      }

      function parse_uuid_URI() {
        var result0, result1;
        var pos0;
        pos0 = pos;

        if (input.substr(pos, 5) === "uuid:") {
          result0 = "uuid:";
          pos += 5;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"uuid:\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_uuid();

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_uuid() {
        var result0, result1, result2, result3, result4, result5, result6, result7, result8;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_hex8();

        if (result0 !== null) {
          if (input.charCodeAt(pos) === 45) {
            result1 = "-";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\"-\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_hex4();

            if (result2 !== null) {
              if (input.charCodeAt(pos) === 45) {
                result3 = "-";
                pos++;
              } else {
                result3 = null;

                if (reportFailures === 0) {
                  matchFailed("\"-\"");
                }
              }

              if (result3 !== null) {
                result4 = parse_hex4();

                if (result4 !== null) {
                  if (input.charCodeAt(pos) === 45) {
                    result5 = "-";
                    pos++;
                  } else {
                    result5 = null;

                    if (reportFailures === 0) {
                      matchFailed("\"-\"");
                    }
                  }

                  if (result5 !== null) {
                    result6 = parse_hex4();

                    if (result6 !== null) {
                      if (input.charCodeAt(pos) === 45) {
                        result7 = "-";
                        pos++;
                      } else {
                        result7 = null;

                        if (reportFailures === 0) {
                          matchFailed("\"-\"");
                        }
                      }

                      if (result7 !== null) {
                        result8 = parse_hex12();

                        if (result8 !== null) {
                          result0 = [result0, result1, result2, result3, result4, result5, result6, result7, result8];
                        } else {
                          result0 = null;
                          pos = pos1;
                        }
                      } else {
                        result0 = null;
                        pos = pos1;
                      }
                    } else {
                      result0 = null;
                      pos = pos1;
                    }
                  } else {
                    result0 = null;
                    pos = pos1;
                  }
                } else {
                  result0 = null;
                  pos = pos1;
                }
              } else {
                result0 = null;
                pos = pos1;
              }
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, uuid) {
            data = input.substring(pos + 5, offset);
          }(pos0, result0[0]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_hex4() {
        var result0, result1, result2, result3;
        var pos0;
        pos0 = pos;
        result0 = parse_HEXDIG();

        if (result0 !== null) {
          result1 = parse_HEXDIG();

          if (result1 !== null) {
            result2 = parse_HEXDIG();

            if (result2 !== null) {
              result3 = parse_HEXDIG();

              if (result3 !== null) {
                result0 = [result0, result1, result2, result3];
              } else {
                result0 = null;
                pos = pos0;
              }
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_hex8() {
        var result0, result1;
        var pos0;
        pos0 = pos;
        result0 = parse_hex4();

        if (result0 !== null) {
          result1 = parse_hex4();

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_hex12() {
        var result0, result1, result2;
        var pos0;
        pos0 = pos;
        result0 = parse_hex4();

        if (result0 !== null) {
          result1 = parse_hex4();

          if (result1 !== null) {
            result2 = parse_hex4();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos0;
            }
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_Refer_To() {
        var result0, result1, result2, result3;
        var pos0, pos1, pos2;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_SIP_URI_noparams();

        if (result0 === null) {
          result0 = parse_name_addr();
        }

        if (result0 !== null) {
          result1 = [];
          pos2 = pos;
          result2 = parse_SEMI();

          if (result2 !== null) {
            result3 = parse_generic_param();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos2;
            }
          } else {
            result2 = null;
            pos = pos2;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos2 = pos;
            result2 = parse_SEMI();

            if (result2 !== null) {
              result3 = parse_generic_param();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos2;
              }
            } else {
              result2 = null;
              pos = pos2;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            try {
              data = new NameAddrHeader(data.uri, data.display_name, data.params);
            } catch (e) {
              data = -1;
            }
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_Replaces() {
        var result0, result1, result2, result3;
        var pos0, pos1;
        pos0 = pos;
        result0 = parse_call_id();

        if (result0 !== null) {
          result1 = [];
          pos1 = pos;
          result2 = parse_SEMI();

          if (result2 !== null) {
            result3 = parse_replaces_param();

            if (result3 !== null) {
              result2 = [result2, result3];
            } else {
              result2 = null;
              pos = pos1;
            }
          } else {
            result2 = null;
            pos = pos1;
          }

          while (result2 !== null) {
            result1.push(result2);
            pos1 = pos;
            result2 = parse_SEMI();

            if (result2 !== null) {
              result3 = parse_replaces_param();

              if (result3 !== null) {
                result2 = [result2, result3];
              } else {
                result2 = null;
                pos = pos1;
              }
            } else {
              result2 = null;
              pos = pos1;
            }
          }

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos0;
          }
        } else {
          result0 = null;
          pos = pos0;
        }

        return result0;
      }

      function parse_call_id() {
        var result0, result1, result2;
        var pos0, pos1, pos2;
        pos0 = pos;
        pos1 = pos;
        result0 = parse_word();

        if (result0 !== null) {
          pos2 = pos;

          if (input.charCodeAt(pos) === 64) {
            result1 = "@";
            pos++;
          } else {
            result1 = null;

            if (reportFailures === 0) {
              matchFailed("\"@\"");
            }
          }

          if (result1 !== null) {
            result2 = parse_word();

            if (result2 !== null) {
              result1 = [result1, result2];
            } else {
              result1 = null;
              pos = pos2;
            }
          } else {
            result1 = null;
            pos = pos2;
          }

          result1 = result1 !== null ? result1 : "";

          if (result1 !== null) {
            result0 = [result0, result1];
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset) {
            data.call_id = input.substring(pos, offset);
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_replaces_param() {
        var result0;
        result0 = parse_to_tag();

        if (result0 === null) {
          result0 = parse_from_tag();

          if (result0 === null) {
            result0 = parse_early_flag();

            if (result0 === null) {
              result0 = parse_generic_param();
            }
          }
        }

        return result0;
      }

      function parse_to_tag() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 6) === "to-tag") {
          result0 = "to-tag";
          pos += 6;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"to-tag\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            result2 = parse_token();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, to_tag) {
            data.to_tag = to_tag;
          }(pos0, result0[2]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_from_tag() {
        var result0, result1, result2;
        var pos0, pos1;
        pos0 = pos;
        pos1 = pos;

        if (input.substr(pos, 8) === "from-tag") {
          result0 = "from-tag";
          pos += 8;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"from-tag\"");
          }
        }

        if (result0 !== null) {
          result1 = parse_EQUAL();

          if (result1 !== null) {
            result2 = parse_token();

            if (result2 !== null) {
              result0 = [result0, result1, result2];
            } else {
              result0 = null;
              pos = pos1;
            }
          } else {
            result0 = null;
            pos = pos1;
          }
        } else {
          result0 = null;
          pos = pos1;
        }

        if (result0 !== null) {
          result0 = function (offset, from_tag) {
            data.from_tag = from_tag;
          }(pos0, result0[2]);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function parse_early_flag() {
        var result0;
        var pos0;
        pos0 = pos;

        if (input.substr(pos, 10) === "early-only") {
          result0 = "early-only";
          pos += 10;
        } else {
          result0 = null;

          if (reportFailures === 0) {
            matchFailed("\"early-only\"");
          }
        }

        if (result0 !== null) {
          result0 = function (offset) {
            data.early_only = true;
          }(pos0);
        }

        if (result0 === null) {
          pos = pos0;
        }

        return result0;
      }

      function cleanupExpected(expected) {
        expected.sort();
        var lastExpected = null;
        var cleanExpected = [];

        for (var i = 0; i < expected.length; i++) {
          if (expected[i] !== lastExpected) {
            cleanExpected.push(expected[i]);
            lastExpected = expected[i];
          }
        }

        return cleanExpected;
      }

      function computeErrorPosition() {
        /*
         * The first idea was to use |String.split| to break the input up to the
         * error position along newlines and derive the line and column from
         * there. However IE's |split| implementation is so broken that it was
         * enough to prevent it.
         */
        var line = 1;
        var column = 1;
        var seenCR = false;

        for (var i = 0; i < Math.max(pos, rightmostFailuresPos); i++) {
          var ch = input.charAt(i);

          if (ch === "\n") {
            if (!seenCR) {
              line++;
            }

            column = 1;
            seenCR = false;
          } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") {
            line++;
            column = 1;
            seenCR = true;
          } else {
            column++;
            seenCR = false;
          }
        }

        return {
          line: line,
          column: column
        };
      }

      var URI = require('./URI');

      var NameAddrHeader = require('./NameAddrHeader');

      var data = {};
      var result = parseFunctions[startRule]();
      /*
       * The parser is now in one of the following three states:
       *
       * 1. The parser successfully parsed the whole input.
       *
       *    - |result !== null|
       *    - |pos === input.length|
       *    - |rightmostFailuresExpected| may or may not contain something
       *
       * 2. The parser successfully parsed only a part of the input.
       *
       *    - |result !== null|
       *    - |pos < input.length|
       *    - |rightmostFailuresExpected| may or may not contain something
       *
       * 3. The parser did not successfully parse any part of the input.
       *
       *   - |result === null|
       *   - |pos === 0|
       *   - |rightmostFailuresExpected| contains at least one failure
       *
       * All code following this comment (including called functions) must
       * handle these states.
       */

      if (result === null || pos !== input.length) {
        var offset = Math.max(pos, rightmostFailuresPos);
        var found = offset < input.length ? input.charAt(offset) : null;
        var errorPosition = computeErrorPosition();
        new this.SyntaxError(cleanupExpected(rightmostFailuresExpected), found, offset, errorPosition.line, errorPosition.column);
        return -1;
      }

      return data;
    },

    /* Returns the parser source code. */
    toSource: function toSource() {
      return this._source;
    }
  };
  /* Thrown when a parser encounters a syntax error. */

  result.SyntaxError = function (expected, found, offset, line, column) {
    function buildMessage(expected, found) {
      var expectedHumanized, foundHumanized;

      switch (expected.length) {
        case 0:
          expectedHumanized = "end of input";
          break;

        case 1:
          expectedHumanized = expected[0];
          break;

        default:
          expectedHumanized = expected.slice(0, expected.length - 1).join(", ") + " or " + expected[expected.length - 1];
      }

      foundHumanized = found ? quote(found) : "end of input";
      return "Expected " + expectedHumanized + " but " + foundHumanized + " found.";
    }

    this.name = "SyntaxError";
    this.expected = expected;
    this.found = found;
    this.message = buildMessage(expected, found);
    this.offset = offset;
    this.line = line;
    this.column = column;
  };

  result.SyntaxError.prototype = Error.prototype;
  return result;
}();
},{"./NameAddrHeader":11,"./URI":27}],8:[function(require,module,exports){
"use strict";

var pkg = require('../package.json');

var C = require('./Constants');

var Exceptions = require('./Exceptions');

var Utils = require('./Utils');

var UA = require('./UA');

var URI = require('./URI');

var NameAddrHeader = require('./NameAddrHeader');

var Grammar = require('./Grammar');

var WebSocketInterface = require('./WebSocketInterface');

var debug = require('debug')('JsSIP');

debug('version %s', pkg.version);
/**
 * Expose the JsSIP module.
 */

module.exports = {
  C: C,
  Exceptions: Exceptions,
  Utils: Utils,
  UA: UA,
  URI: URI,
  NameAddrHeader: NameAddrHeader,
  WebSocketInterface: WebSocketInterface,
  Grammar: Grammar,
  // Expose the debug module.
  debug: require('debug'),

  get name() {
    return pkg.title;
  },

  get version() {
    return pkg.version;
  }

};
},{"../package.json":40,"./Constants":2,"./Exceptions":6,"./Grammar":7,"./NameAddrHeader":11,"./UA":26,"./URI":27,"./Utils":28,"./WebSocketInterface":29,"debug":32}],9:[function(require,module,exports){
"use strict";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var debug = require('debug');

var APP_NAME = 'JsSIP';

module.exports = /*#__PURE__*/function () {
  function Logger(prefix) {
    _classCallCheck(this, Logger);

    if (prefix) {
      this._debug = debug["default"]("".concat(APP_NAME, ":").concat(prefix));
      this._warn = debug["default"]("".concat(APP_NAME, ":WARN:").concat(prefix));
      this._error = debug["default"]("".concat(APP_NAME, ":ERROR:").concat(prefix));
    } else {
      this._debug = debug["default"](APP_NAME);
      this._warn = debug["default"]("".concat(APP_NAME, ":WARN"));
      this._error = debug["default"]("".concat(APP_NAME, ":ERROR"));
    }
    /* eslint-disable no-console */


    this._debug.log = console.info.bind(console);
    this._warn.log = console.warn.bind(console);
    this._error.log = console.error.bind(console);
    /* eslint-enable no-console */
  }

  _createClass(Logger, [{
    key: "debug",
    get: function get() {
      return this._debug;
    }
  }, {
    key: "warn",
    get: function get() {
      return this._warn;
    }
  }, {
    key: "error",
    get: function get() {
      return this._error;
    }
  }]);

  return Logger;
}();
},{"debug":32}],10:[function(require,module,exports){
"use strict";

function _typeof(obj) { "@babel/helpers - typeof"; 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); }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }

function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }

function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

var EventEmitter = require('events').EventEmitter;

var Logger = require('./Logger');

var JsSIP_C = require('./Constants');

var SIPMessage = require('./SIPMessage');

var Utils = require('./Utils');

var RequestSender = require('./RequestSender');

var Exceptions = require('./Exceptions');

var URI = require('./URI');

var logger = new Logger('Message');

module.exports = /*#__PURE__*/function (_EventEmitter) {
  _inherits(Message, _EventEmitter);

  var _super = _createSuper(Message);

  function Message(ua) {
    var _this;

    _classCallCheck(this, Message);

    _this = _super.call(this);
    _this._ua = ua;
    _this._request = null;
    _this._closed = false;
    _this._direction = null;
    _this._local_identity = null;
    _this._remote_identity = null; // Whether an incoming message has been replied.

    _this._is_replied = false; // Custom message empty object for high level use.

    _this._data = {};
    return _this;
  }

  _createClass(Message, [{
    key: "send",
    value: function send(target, body) {
      var _this2 = this;

      var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
      var originalTarget = target;

      if (target === undefined || body === undefined) {
        throw new TypeError('Not enough arguments');
      } // Check target validity.


      target = this._ua.normalizeTarget(target);

      if (!target) {
        throw new TypeError("Invalid target: ".concat(originalTarget));
      } // Get call options.


      var extraHeaders = Utils.cloneArray(options.extraHeaders);
      var eventHandlers = Utils.cloneObject(options.eventHandlers);
      var contentType = options.contentType || 'text/plain';
      var requestParams = {};

      if (options.fromUserName) {
        requestParams.from_uri = new URI('sip', options.fromUserName, this._ua.configuration.uri.host);
        extraHeaders.push("P-Preferred-Identity: ".concat(this._ua.configuration.uri.toString()));
      }

      if (options.fromDisplayName) {
        requestParams.from_display_name = options.fromDisplayName;
      } // Set event handlers.


      for (var event in eventHandlers) {
        if (Object.prototype.hasOwnProperty.call(eventHandlers, event)) {
          this.on(event, eventHandlers[event]);
        }
      }

      extraHeaders.push("Content-Type: ".concat(contentType));
      this._request = new SIPMessage.OutgoingRequest(JsSIP_C.MESSAGE, target, this._ua, requestParams, extraHeaders);

      if (body) {
        this._request.body = body;
      }

      var request_sender = new RequestSender(this._ua, this._request, {
        onRequestTimeout: function onRequestTimeout() {
          _this2._onRequestTimeout();
        },
        onTransportError: function onTransportError() {
          _this2._onTransportError();
        },
        onReceiveResponse: function onReceiveResponse(response) {
          _this2._receiveResponse(response);
        }
      });

      this._newMessage('local', this._request);

      request_sender.send();
    }
  }, {
    key: "init_incoming",
    value: function init_incoming(request) {
      this._request = request;

      this._newMessage('remote', request); // Reply with a 200 OK if the user didn't reply.


      if (!this._is_replied) {
        this._is_replied = true;
        request.reply(200);
      }

      this._close();
    }
    /**
     * Accept the incoming Message
     * Only valid for incoming Messages
     */

  }, {
    key: "accept",
    value: function accept() {
      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var extraHeaders = Utils.cloneArray(options.extraHeaders);
      var body = options.body;

      if (this._direction !== 'incoming') {
        throw new Exceptions.NotSupportedError('"accept" not supported for outgoing Message');
      }

      if (this._is_replied) {
        throw new Error('incoming Message already replied');
      }

      this._is_replied = true;

      this._request.reply(200, null, extraHeaders, body);
    }
    /**
     * Reject the incoming Message
     * Only valid for incoming Messages
     */

  }, {
    key: "reject",
    value: function reject() {
      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var status_code = options.status_code || 480;
      var reason_phrase = options.reason_phrase;
      var extraHeaders = Utils.cloneArray(options.extraHeaders);
      var body = options.body;

      if (this._direction !== 'incoming') {
        throw new Exceptions.NotSupportedError('"reject" not supported for outgoing Message');
      }

      if (this._is_replied) {
        throw new Error('incoming Message already replied');
      }

      if (status_code < 300 || status_code >= 700) {
        throw new TypeError("Invalid status_code: ".concat(status_code));
      }

      this._is_replied = true;

      this._request.reply(status_code, reason_phrase, extraHeaders, body);
    }
  }, {
    key: "_receiveResponse",
    value: function _receiveResponse(response) {
      if (this._closed) {
        return;
      }

      switch (true) {
        case /^1[0-9]{2}$/.test(response.status_code):
          // Ignore provisional responses.
          break;

        case /^2[0-9]{2}$/.test(response.status_code):
          this._succeeded('remote', response);

          break;

        default:
          {
            var cause = Utils.sipErrorCause(response.status_code);

            this._failed('remote', response, cause);

            break;
          }
      }
    }
  }, {
    key: "_onRequestTimeout",
    value: function _onRequestTimeout() {
      if (this._closed) {
        return;
      }

      this._failed('system', null, JsSIP_C.causes.REQUEST_TIMEOUT);
    }
  }, {
    key: "_onTransportError",
    value: function _onTransportError() {
      if (this._closed) {
        return;
      }

      this._failed('system', null, JsSIP_C.causes.CONNECTION_ERROR);
    }
  }, {
    key: "_close",
    value: function _close() {
      this._closed = true;

      this._ua.destroyMessage(this);
    }
    /**
     * Internal Callbacks
     */

  }, {
    key: "_newMessage",
    value: function _newMessage(originator, request) {
      if (originator === 'remote') {
        this._direction = 'incoming';
        this._local_identity = request.to;
        this._remote_identity = request.from;
      } else if (originator === 'local') {
        this._direction = 'outgoing';
        this._local_identity = request.from;
        this._remote_identity = request.to;
      }

      this._ua.newMessage(this, {
        originator: originator,
        message: this,
        request: request
      });
    }
  }, {
    key: "_failed",
    value: function _failed(originator, response, cause) {
      logger.debug('MESSAGE failed');

      this._close();

      logger.debug('emit "failed"');
      this.emit('failed', {
        originator: originator,
        response: response || null,
        cause: cause
      });
    }
  }, {
    key: "_succeeded",
    value: function _succeeded(originator, response) {
      logger.debug('MESSAGE succeeded');

      this._close();

      logger.debug('emit "succeeded"');
      this.emit('succeeded', {
        originator: originator,
        response: response
      });
    }
  }, {
    key: "direction",
    get: function get() {
      return this._direction;
    }
  }, {
    key: "local_identity",
    get: function get() {
      return this._local_identity;
    }
  }, {
    key: "remote_identity",
    get: function get() {
      return this._remote_identity;
    }
  }]);

  return Message;
}(EventEmitter);
},{"./Constants":2,"./Exceptions":6,"./Logger":9,"./RequestSender":20,"./SIPMessage":21,"./URI":27,"./Utils":28,"events":31}],11:[function(require,module,exports){
"use strict";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var URI = require('./URI');

var Grammar = require('./Grammar');

module.exports = /*#__PURE__*/function () {
  _createClass(NameAddrHeader, null, [{
    key: "parse",

    /**
     * Parse the given string and returns a NameAddrHeader instance or undefined if
     * it is an invalid NameAddrHeader.
     */
    value: function parse(name_addr_header) {
      name_addr_header = Grammar.parse(name_addr_header, 'Name_Addr_Header');

      if (name_addr_header !== -1) {
        return name_addr_header;
      } else {
        return undefined;
      }
    }
  }]);

  function NameAddrHeader(uri, display_name, parameters) {
    _classCallCheck(this, NameAddrHeader);

    // Checks.
    if (!uri || !(uri instanceof URI)) {
      throw new TypeError('missing or invalid "uri" parameter');
    } // Initialize parameters.


    this._uri = uri;
    this._parameters = {};
    this.display_name = display_name;

    for (var param in parameters) {
      if (Object.prototype.hasOwnProperty.call(parameters, param)) {
        this.setParam(param, parameters[param]);
      }
    }
  }

  _createClass(NameAddrHeader, [{
    key: "setParam",
    value: function setParam(key, value) {
      if (key) {
        this._parameters[key.toLowerCase()] = typeof value === 'undefined' || value === null ? null : value.toString();
      }
    }
  }, {
    key: "getParam",
    value: function getParam(key) {
      if (key) {
        return this._parameters[key.toLowerCase()];
      }
    }
  }, {
    key: "hasParam",
    value: function hasParam(key) {
      if (key) {
        return this._parameters.hasOwnProperty(key.toLowerCase()) && true || false;
      }
    }
  }, {
    key: "deleteParam",
    value: function deleteParam(parameter) {
      parameter = parameter.toLowerCase();

      if (this._parameters.hasOwnProperty(parameter)) {
        var value = this._parameters[parameter];
        delete this._parameters[parameter];
        return value;
      }
    }
  }, {
    key: "clearParams",
    value: function clearParams() {
      this._parameters = {};
    }
  }, {
    key: "clone",
    value: function clone() {
      return new NameAddrHeader(this._uri.clone(), this._display_name, JSON.parse(JSON.stringify(this._parameters)));
    }
  }, {
    key: "_quote",
    value: function _quote(str) {
      return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
    }
  }, {
    key: "toString",
    value: function toString() {
      var body = this._display_name ? "\"".concat(this._quote(this._display_name), "\" ") : '';
      body += "<".concat(this._uri.toString(), ">");

      for (var parameter in this._parameters) {
        if (Object.prototype.hasOwnProperty.call(this._parameters, parameter)) {
          body += ";".concat(parameter);

          if (this._parameters[parameter] !== null) {
            body += "=".concat(this._parameters[parameter]);
          }
        }
      }

      return body;
    }
  }, {
    key: "uri",
    get: function get() {
      return this._uri;
    }
  }, {
    key: "display_name",
    get: function get() {
      return this._display_name;
    },
    set: function set(value) {
      this._display_name = value === 0 ? '0' : value;
    }
  }]);

  return NameAddrHeader;
}();
},{"./Grammar":7,"./URI":27}],12:[function(require,module,exports){
"use strict";

function _typeof(obj) { "@babel/helpers - typeof"; 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); }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }

function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }

function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

var EventEmitter = require('events').EventEmitter;

var Logger = require('./Logger');

var JsSIP_C = require('./Constants');

var SIPMessage = require('./SIPMessage');

var Utils = require('./Utils');

var RequestSender = require('./RequestSender');

var Exceptions = require('./Exceptions');

var logger = new Logger('Options');

module.exports = /*#__PURE__*/function (_EventEmitter) {
  _inherits(Options, _EventEmitter);

  var _super = _createSuper(Options);

  function Options(ua) {
    var _this;

    _classCallCheck(this, Options);

    _this = _super.call(this);
    _this._ua = ua;
    _this._request = null;
    _this._closed = false;
    _this._direction = null;
    _this._local_identity = null;
    _this._remote_identity = null; // Whether an incoming message has been replied.

    _this._is_replied = false; // Custom message empty object for high level use.

    _this._data = {};
    return _this;
  }

  _createClass(Options, [{
    key: "send",
    value: function send(target, body) {
      var _this2 = this;

      var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
      var originalTarget = target;

      if (target === undefined) {
        throw new TypeError('A target is required for OPTIONS');
      } // Check target validity.


      target = this._ua.normalizeTarget(target);

      if (!target) {
        throw new TypeError("Invalid target: ".concat(originalTarget));
      } // Get call options.


      var extraHeaders = Utils.cloneArray(options.extraHeaders);
      var eventHandlers = Utils.cloneObject(options.eventHandlers);
      var contentType = options.contentType || 'application/sdp'; // Set event handlers.

      for (var event in eventHandlers) {
        if (Object.prototype.hasOwnProperty.call(eventHandlers, event)) {
          this.on(event, eventHandlers[event]);
        }
      }

      extraHeaders.push("Content-Type: ".concat(contentType));
      this._request = new SIPMessage.OutgoingRequest(JsSIP_C.OPTIONS, target, this._ua, null, extraHeaders);

      if (body) {
        this._request.body = body;
      }

      var request_sender = new RequestSender(this._ua, this._request, {
        onRequestTimeout: function onRequestTimeout() {
          _this2._onRequestTimeout();
        },
        onTransportError: function onTransportError() {
          _this2._onTransportError();
        },
        onReceiveResponse: function onReceiveResponse(response) {
          _this2._receiveResponse(response);
        }
      });

      this._newOptions('local', this._request);

      request_sender.send();
    }
  }, {
    key: "init_incoming",
    value: function init_incoming(request) {
      this._request = request;

      this._newOptions('remote', request); // Reply with a 200 OK if the user didn't reply.


      if (!this._is_replied) {
        this._is_replied = true;
        request.reply(200);
      }

      this._close();
    }
    /**
     * Accept the incoming Options
     * Only valid for incoming Options
     */

  }, {
    key: "accept",
    value: function accept() {
      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var extraHeaders = Utils.cloneArray(options.extraHeaders);
      var body = options.body;

      if (this._direction !== 'incoming') {
        throw new Exceptions.NotSupportedError('"accept" not supported for outgoing Options');
      }

      if (this._is_replied) {
        throw new Error('incoming Options already replied');
      }

      this._is_replied = true;

      this._request.reply(200, null, extraHeaders, body);
    }
    /**
     * Reject the incoming Options
     * Only valid for incoming Options
     */

  }, {
    key: "reject",
    value: function reject() {
      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var status_code = options.status_code || 480;
      var reason_phrase = options.reason_phrase;
      var extraHeaders = Utils.cloneArray(options.extraHeaders);
      var body = options.body;

      if (this._direction !== 'incoming') {
        throw new Exceptions.NotSupportedError('"reject" not supported for outgoing Options');
      }

      if (this._is_replied) {
        throw new Error('incoming Options already replied');
      }

      if (status_code < 300 || status_code >= 700) {
        throw new TypeError("Invalid status_code: ".concat(status_code));
      }

      this._is_replied = true;

      this._request.reply(status_code, reason_phrase, extraHeaders, body);
    }
  }, {
    key: "_receiveResponse",
    value: function _receiveResponse(response) {
      if (this._closed) {
        return;
      }

      switch (true) {
        case /^1[0-9]{2}$/.test(response.status_code):
          // Ignore provisional responses.
          break;

        case /^2[0-9]{2}$/.test(response.status_code):
          this._succeeded('remote', response);

          break;

        default:
          {
            var cause = Utils.sipErrorCause(response.status_code);

            this._failed('remote', response, cause);

            break;
          }
      }
    }
  }, {
    key: "_onRequestTimeout",
    value: function _onRequestTimeout() {
      if (this._closed) {
        return;
      }

      this._failed('system', null, JsSIP_C.causes.REQUEST_TIMEOUT);
    }
  }, {
    key: "_onTransportError",
    value: function _onTransportError() {
      if (this._closed) {
        return;
      }

      this._failed('system', null, JsSIP_C.causes.CONNECTION_ERROR);
    }
  }, {
    key: "_close",
    value: function _close() {
      this._closed = true;

      this._ua.destroyMessage(this);
    }
    /**
     * Internal Callbacks
     */

  }, {
    key: "_newOptions",
    value: function _newOptions(originator, request) {
      if (originator === 'remote') {
        this._direction = 'incoming';
        this._local_identity = request.to;
        this._remote_identity = request.from;
      } else if (originator === 'local') {
        this._direction = 'outgoing';
        this._local_identity = request.from;
        this._remote_identity = request.to;
      }

      this._ua.newOptions(this, {
        originator: originator,
        message: this,
        request: request
      });
    }
  }, {
    key: "_failed",
    value: function _failed(originator, response, cause) {
      logger.debug('OPTIONS failed');

      this._close();

      logger.debug('emit "failed"');
      this.emit('failed', {
        originator: originator,
        response: response || null,
        cause: cause
      });
    }
  }, {
    key: "_succeeded",
    value: function _succeeded(originator, response) {
      logger.debug('OPTIONS succeeded');

      this._close();

      logger.debug('emit "succeeded"');
      this.emit('succeeded', {
        originator: originator,
        response: response
      });
    }
  }, {
    key: "direction",
    get: function get() {
      return this._direction;
    }
  }, {
    key: "local_identity",
    get: function get() {
      return this._local_identity;
    }
  }, {
    key: "remote_identity",
    get: function get() {
      return this._remote_identity;
    }
  }]);

  return Options;
}(EventEmitter);
},{"./Constants":2,"./Exceptions":6,"./Logger":9,"./RequestSender":20,"./SIPMessage":21,"./Utils":28,"events":31}],13:[function(require,module,exports){
"use strict";

function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

var Logger = require('./Logger');

var Grammar = require('./Grammar');

var SIPMessage = require('./SIPMessage');

var logger = new Logger('Parser');
/**
 * Parse SIP Message
 */

exports.parseMessage = function (data, ua) {
  var message;
  var bodyStart;
  var headerEnd = data.indexOf('\r\n');

  if (headerEnd === -1) {
    logger.warn('parseMessage() | no CRLF found, not a SIP message');
    return;
  } // Parse first line. Check if it is a Request or a Reply.


  var firstLine = data.substring(0, headerEnd);
  var parsed = Grammar.parse(firstLine, 'Request_Response');

  if (parsed === -1) {
    logger.warn("parseMessage() | error parsing first line of SIP message: \"".concat(firstLine, "\""));
    return;
  } else if (!parsed.status_code) {
    message = new SIPMessage.IncomingRequest(ua);
    message.method = parsed.method;
    message.ruri = parsed.uri;
  } else {
    message = new SIPMessage.IncomingResponse();
    message.status_code = parsed.status_code;
    message.reason_phrase = parsed.reason_phrase;
  }

  message.data = data;
  var headerStart = headerEnd + 2;
  /* Loop over every line in data. Detect the end of each header and parse
  * it or simply add to the headers collection.
  */

  while (true) {
    headerEnd = getHeader(data, headerStart); // The SIP message has normally finished.

    if (headerEnd === -2) {
      bodyStart = headerStart + 2;
      break;
    } // Data.indexOf returned -1 due to a malformed message.
    else if (headerEnd === -1) {
        logger.warn('parseMessage() | malformed message');
        return;
      }

    parsed = parseHeader(message, data, headerStart, headerEnd);

    if (parsed !== true) {
      logger.warn('parseMessage() |', parsed.error);
      return;
    }

    headerStart = headerEnd + 2;
  }
  /* RFC3261 18.3.
   * If there are additional bytes in the transport packet
   * beyond the end of the body, they MUST be discarded.
   */


  if (message.hasHeader('content-length')) {
    var contentLength = message.getHeader('content-length');
    message.body = data.substr(bodyStart, contentLength);
  } else {
    message.body = data.substring(bodyStart);
  }

  return message;
};
/**
 * Extract and parse every header of a SIP message.
 */


function getHeader(data, headerStart) {
  // 'start' position of the header.
  var start = headerStart; // 'end' position of the header.

  var end = 0; // 'partial end' position of the header.

  var partialEnd = 0; // End of message.

  if (data.substring(start, start + 2).match(/(^\r\n)/)) {
    return -2;
  }

  while (end === 0) {
    // Partial End of Header.
    partialEnd = data.indexOf('\r\n', start); // 'indexOf' returns -1 if the value to be found never occurs.

    if (partialEnd === -1) {
      return partialEnd;
    }

    if (!data.substring(partialEnd + 2, partialEnd + 4).match(/(^\r\n)/) && data.charAt(partialEnd + 2).match(/(^\s+)/)) {
      // Not the end of the message. Continue from the next position.
      start = partialEnd + 2;
    } else {
      end = partialEnd;
    }
  }

  return end;
}

function parseHeader(message, data, headerStart, headerEnd) {
  var parsed;
  var hcolonIndex = data.indexOf(':', headerStart);
  var headerName = data.substring(headerStart, hcolonIndex).trim();
  var headerValue = data.substring(hcolonIndex + 1, headerEnd).trim(); // If header-field is well-known, parse it.

  switch (headerName.toLowerCase()) {
    case 'via':
    case 'v':
      message.addHeader('via', headerValue);

      if (message.getHeaders('via').length === 1) {
        parsed = message.parseHeader('Via');

        if (parsed) {
          message.via = parsed;
          message.via_branch = parsed.branch;
        }
      } else {
        parsed = 0;
      }

      break;

    case 'from':
    case 'f':
      message.setHeader('from', headerValue);
      parsed = message.parseHeader('from');

      if (parsed) {
        message.from = parsed;
        message.from_tag = parsed.getParam('tag');
      }

      break;

    case 'to':
    case 't':
      message.setHeader('to', headerValue);
      parsed = message.parseHeader('to');

      if (parsed) {
        message.to = parsed;
        message.to_tag = parsed.getParam('tag');
      }

      break;

    case 'record-route':
      parsed = Grammar.parse(headerValue, 'Record_Route');

      if (parsed === -1) {
        parsed = undefined;
      } else {
        var _iterator = _createForOfIteratorHelper(parsed),
            _step;

        try {
          for (_iterator.s(); !(_step = _iterator.n()).done;) {
            var header = _step.value;
            message.addHeader('record-route', headerValue.substring(header.possition, header.offset));
            message.headers['Record-Route'][message.getHeaders('record-route').length - 1].parsed = header.parsed;
          }
        } catch (err) {
          _iterator.e(err);
        } finally {
          _iterator.f();
        }
      }

      break;

    case 'call-id':
    case 'i':
      message.setHeader('call-id', headerValue);
      parsed = message.parseHeader('call-id');

      if (parsed) {
        message.call_id = headerValue;
      }

      break;

    case 'contact':
    case 'm':
      parsed = Grammar.parse(headerValue, 'Contact');

      if (parsed === -1) {
        parsed = undefined;
      } else {
        var _iterator2 = _createForOfIteratorHelper(parsed),
            _step2;

        try {
          for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
            var _header = _step2.value;
            message.addHeader('contact', headerValue.substring(_header.possition, _header.offset));
            message.headers.Contact[message.getHeaders('contact').length - 1].parsed = _header.parsed;
          }
        } catch (err) {
          _iterator2.e(err);
        } finally {
          _iterator2.f();
        }
      }

      break;

    case 'content-length':
    case 'l':
      message.setHeader('content-length', headerValue);
      parsed = message.parseHeader('content-length');
      break;

    case 'content-type':
    case 'c':
      message.setHeader('content-type', headerValue);
      parsed = message.parseHeader('content-type');
      break;

    case 'cseq':
      message.setHeader('cseq', headerValue);
      parsed = message.parseHeader('cseq');

      if (parsed) {
        message.cseq = parsed.value;
      }

      if (message instanceof SIPMessage.IncomingResponse) {
        message.method = parsed.method;
      }

      break;

    case 'max-forwards':
      message.setHeader('max-forwards', headerValue);
      parsed = message.parseHeader('max-forwards');
      break;

    case 'www-authenticate':
      message.setHeader('www-authenticate', headerValue);
      parsed = message.parseHeader('www-authenticate');
      break;

    case 'proxy-authenticate':
      message.setHeader('proxy-authenticate', headerValue);
      parsed = message.parseHeader('proxy-authenticate');
      break;

    case 'session-expires':
    case 'x':
      message.setHeader('session-expires', headerValue);
      parsed = message.parseHeader('session-expires');

      if (parsed) {
        message.session_expires = parsed.expires;
        message.session_expires_refresher = parsed.refresher;
      }

      break;

    case 'refer-to':
    case 'r':
      message.setHeader('refer-to', headerValue);
      parsed = message.parseHeader('refer-to');

      if (parsed) {
        message.refer_to = parsed;
      }

      break;

    case 'replaces':
      message.setHeader('replaces', headerValue);
      parsed = message.parseHeader('replaces');

      if (parsed) {
        message.replaces = parsed;
      }

      break;

    case 'event':
    case 'o':
      message.setHeader('event', headerValue);
      parsed = message.parseHeader('event');

      if (parsed) {
        message.event = parsed;
      }

      break;

    default:
      // Do not parse this header.
      message.addHeader(headerName, headerValue);
      parsed = 0;
  }

  if (parsed === undefined) {
    return {
      error: "error parsing header \"".concat(headerName, "\"")
    };
  } else {
    return true;
  }
}
},{"./Grammar":7,"./Logger":9,"./SIPMessage":21}],14:[function(require,module,exports){
"use strict";

function _typeof(obj) { "@babel/helpers - typeof"; 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); }

function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }

function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }

function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

/* globals RTCPeerConnection: false, RTCSessionDescription: false */
var EventEmitter = require('events').EventEmitter;

var sdp_transform = require('sdp-transform');

var Logger = require('./Logger');

var JsSIP_C = require('./Constants');

var Exceptions = require('./Exceptions');

var Transactions = require('./Transactions');

var Utils = require('./Utils');

var Timers = require('./Timers');

var SIPMessage = require('./SIPMessage');

var Dialog = require('./Dialog');

var RequestSender = require('./RequestSender');

var RTCSession_DTMF = require('./RTCSession/DTMF');

var RTCSession_Info = require('./RTCSession/Info');

var RTCSession_ReferNotifier = require('./RTCSession/ReferNotifier');

var RTCSession_ReferSubscriber = require('./RTCSession/ReferSubscriber');

var URI = require('./URI');

var logger = new Logger('RTCSession');
var C = {
  // RTCSession states.
  STATUS_NULL: 0,
  STATUS_INVITE_SENT: 1,
  STATUS_1XX_RECEIVED: 2,
  STATUS_INVITE_RECEIVED: 3,
  STATUS_WAITING_FOR_ANSWER: 4,
  STATUS_ANSWERED: 5,
  STATUS_WAITING_FOR_ACK: 6,
  STATUS_CANCELED: 7,
  STATUS_TERMINATED: 8,
  STATUS_CONFIRMED: 9
};
/**
 * Local variables.
 */

var holdMediaTypes = ['audio', 'video'];

module.exports = /*#__PURE__*/function (_EventEmitter) {
  _inherits(RTCSession, _EventEmitter);

  var _super = _createSuper(RTCSession);

  _createClass(RTCSession, null, [{
    key: "C",

    /**
     * Expose C object.
     */
    get: function get() {
      return C;
    }
  }]);

  function RTCSession(ua) {
    var _this;

    _classCallCheck(this, RTCSession);

    logger.debug('new');
    _this = _super.call(this);
    _this._id = null;
    _this._ua = ua;
    _this._status = C.STATUS_NULL;
    _this._dialog = null;
    _this._earlyDialogs = {};
    _this._contact = null;
    _this._from_tag = null;
    _this._to_tag = null; // The RTCPeerConnection instance (public attribute).

    _this._connection = null; // Prevent races on serial PeerConnction operations.

    _this._connectionPromiseQueue = Promise.resolve(); // Incoming/Outgoing request being currently processed.

    _this._request = null; // Cancel state for initial outgoing request.

    _this._is_canceled = false;
    _this._cancel_reason = ''; // RTCSession confirmation flag.

    _this._is_confirmed = false; // Is late SDP being negotiated.

    _this._late_sdp = false; // Default rtcOfferConstraints and rtcAnswerConstrainsts (passed in connect() or answer()).

    _this._rtcOfferConstraints = null;
    _this._rtcAnswerConstraints = null; // Local MediaStream.

    _this._localMediaStream = null;
    _this._localMediaStreamLocallyGenerated = false; // Flag to indicate PeerConnection ready for new actions.

    _this._rtcReady = true; // Flag to indicate ICE candidate gathering is finished even if iceGatheringState is not yet 'complete'.

    _this._iceReady = false; // SIP Timers.

    _this._timers = {
      ackTimer: null,
      expiresTimer: null,
      invite2xxTimer: null,
      userNoAnswerTimer: null
    }; // Session info.

    _this._direction = null;
    _this._local_identity = null;
    _this._remote_identity = null;
    _this._start_time = null;
    _this._end_time = null;
    _this._tones = null; // Mute/Hold state.

    _this._audioMuted = false;
    _this._videoMuted = false;
    _this._localHold = false;
    _this._remoteHold = false; // Session Timers (RFC 4028).

    _this._sessionTimers = {
      enabled: _this._ua.configuration.session_timers,
      refreshMethod: _this._ua.configuration.session_timers_refresh_method,
      defaultExpires: JsSIP_C.SESSION_EXPIRES,
      currentExpires: null,
      running: false,
      refresher: false,
      timer: null // A setTimeout.

    }; // Map of ReferSubscriber instances indexed by the REFER's CSeq number.

    _this._referSubscribers = {}; // Custom session empty object for high level use.

    _this._data = {};
    return _this;
  }
  /**
   * User API
   */
  // Expose RTCSession constants as a property of the RTCSession instance.


  _createClass(RTCSession, [{
    key: "isInProgress",
    value: function isInProgress() {
      switch (this._status) {
        case C.STATUS_NULL:
        case C.STATUS_INVITE_SENT:
        case C.STATUS_1XX_RECEIVED:
        case C.STATUS_INVITE_RECEIVED:
        case C.STATUS_WAITING_FOR_ANSWER:
          return true;

        default:
          return false;
      }
    }
  }, {
    key: "isEstablished",
    value: function isEstablished() {
      switch (this._status) {
        case C.STATUS_ANSWERED:
        case C.STATUS_WAITING_FOR_ACK:
        case C.STATUS_CONFIRMED:
          return true;

        default:
          return false;
      }
    }
  }, {
    key: "isEnded",
    value: function isEnded() {
      switch (this._status) {
        case C.STATUS_CANCELED:
        case C.STATUS_TERMINATED:
          return true;

        default:
          return false;
      }
    }
  }, {
    key: "isMuted",
    value: function isMuted() {
      return {
        audio: this._audioMuted,
        video: this._videoMuted
      };
    }
  }, {
    key: "isOnHold",
    value: function isOnHold() {
      return {
        local: this._localHold,
        remote: this._remoteHold
      };
    }
  }, {
    key: "connect",
    value: function connect(target) {
      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      var initCallback = arguments.length > 2 ? arguments[2] : undefined;
      logger.debug('connect()');
      var originalTarget = target;
      var eventHandlers = Utils.cloneObject(options.eventHandlers);
      var extraHeaders = Utils.cloneArray(options.extraHeaders);
      var mediaConstraints = Utils.cloneObject(options.mediaConstraints, {
        audio: true,
        video: true
      });
      var mediaStream = options.mediaStream || null;
      var pcConfig = Utils.cloneObject(options.pcConfig, {
        iceServers: []
      });
      var rtcConstraints = options.rtcConstraints || null;
      var rtcOfferConstraints = options.rtcOfferConstraints || null;
      this._rtcOfferConstraints = rtcOfferConstraints;
      this._rtcAnswerConstraints = options.rtcAnswerConstraints || null;
      this._data = options.data || this._data; // Check target.

      if (target === undefined) {
        throw new TypeError('Not enough arguments');
      } // Check Session Status.


      if (this._status !== C.STATUS_NULL) {
        throw new Exceptions.InvalidStateError(this._status);
      } // Check WebRTC support.


      if (!window.RTCPeerConnection) {
        throw new Exceptions.NotSupportedError('WebRTC not supported');
      } // Check target validity.


      target = this._ua.normalizeTarget(target);

      if (!target) {
        throw new TypeError("Invalid target: ".concat(originalTarget));
      } // Session Timers.


      if (this._sessionTimers.enabled) {
        if (Utils.isDecimal(options.sessionTimersExpires)) {
          if (options.sessionTimersExpires >= JsSIP_C.MIN_SESSION_EXPIRES) {
            this._sessionTimers.defaultExpires = options.sessionTimersExpires;
          } else {
            this._sessionTimers.defaultExpires = JsSIP_C.SESSION_EXPIRES;
          }
        }
      } // Set event handlers.


      for (var event in eventHandlers) {
        if (Object.prototype.hasOwnProperty.call(eventHandlers, event)) {
          this.on(event, eventHandlers[event]);
        }
      } // Session parameter initialization.


      this._from_tag = Utils.newTag(); // Set anonymous property.

      var anonymous = options.anonymous || false;
      var requestParams = {
        from_tag: this._from_tag
      };
      this._contact = this._ua.contact.toString({
        anonymous: anonymous,
        outbound: true
      });

      if (anonymous) {
        requestParams.from_display_name = 'Anonymous';
        requestParams.from_uri = new URI('sip', 'anonymous', 'anonymous.invalid');
        extraHeaders.push("P-Preferred-Identity: ".concat(this._ua.configuration.uri.toString()));
        extraHeaders.push('Privacy: id');
      } else if (options.fromUserName) {
        requestParams.from_uri = new URI('sip', options.fromUserName, this._ua.configuration.uri.host);
        extraHeaders.push("P-Preferred-Identity: ".concat(this._ua.configuration.uri.toString()));
      }

      if (options.fromDisplayName) {
        requestParams.from_display_name = options.fromDisplayName;
      }

      extraHeaders.push("Contact: ".concat(this._contact));
      extraHeaders.push('Content-Type: application/sdp');

      if (this._sessionTimers.enabled) {
        extraHeaders.push("Session-Expires: ".concat(this._sessionTimers.defaultExpires).concat(this._ua.configuration.session_timers_force_refresher ? ';refresher=uac' : ''));
      }

      this._request = new SIPMessage.InitialOutgoingInviteRequest(target, this._ua, requestParams, extraHeaders);
      this._id = this._request.call_id + this._from_tag; // Create a new RTCPeerConnection instance.

      this._createRTCConnection(pcConfig, rtcConstraints); // Set internal properties.


      this._direction = 'outgoing';
      this._local_identity = this._request.from;
      this._remote_identity = this._request.to; // User explicitly provided a newRTCSession callback for this session.

      if (initCallback) {
        initCallback(this);
      }

      this._newRTCSession('local', this._request);

      this._sendInitialRequest(mediaConstraints, rtcOfferConstraints, mediaStream);
    }
  }, {
    key: "init_incoming",
    value: function init_incoming(request, initCallback) {
      var _this2 = this;

      logger.debug('init_incoming()');
      var expires;
      var contentType = request.hasHeader('Content-Type') ? request.getHeader('Content-Type').toLowerCase() : undefined; // Check body and content type.

      if (request.body && contentType !== 'application/sdp') {
        request.reply(415);
        return;
      } // Session parameter initialization.


      this._status = C.STATUS_INVITE_RECEIVED;
      this._from_tag = request.from_tag;
      this._id = request.call_id + this._from_tag;
      this._request = request;
      this._contact = this._ua.contact.toString(); // Get the Expires header value if exists.

      if (request.hasHeader('expires')) {
        expires = request.getHeader('expires') * 1000;
      }
      /* Set the to_tag before
       * replying a response code that will create a dialog.
       */


      request.to_tag = Utils.newTag(); // An error on dialog creation will fire 'failed' event.

      if (!this._createDialog(request, 'UAS', true)) {
        request.reply(500, 'Missing Contact header field');
        return;
      }

      if (request.body) {
        this._late_sdp = false;
      } else {
        this._late_sdp = true;
      }

      this._status = C.STATUS_WAITING_FOR_ANSWER; // Set userNoAnswerTimer.

      this._timers.userNoAnswerTimer = setTimeout(function () {
        request.reply(408);

        _this2._failed('local', null, JsSIP_C.causes.NO_ANSWER);
      }, this._ua.configuration.no_answer_timeout);
      /* Set expiresTimer
       * RFC3261 13.3.1
       */

      if (expires) {
        this._timers.expiresTimer = setTimeout(function () {
          if (_this2._status === C.STATUS_WAITING_FOR_ANSWER) {
            request.reply(487);

            _this2._failed('system', null, JsSIP_C.causes.EXPIRES);
          }
        }, expires);
      } // Set internal properties.


      this._direction = 'incoming';
      this._local_identity = request.to;
      this._remote_identity = request.from; // A init callback was specifically defined.

      if (initCallback) {
        initCallback(this);
      } // Fire 'newRTCSession' event.


      this._newRTCSession('remote', request); // The user may have rejected the call in the 'newRTCSession' event.


      if (this._status === C.STATUS_TERMINATED) {
        return;
      } // Reply 180.


      request.reply(180, null, ["Contact: ".concat(this._contact)]); // Fire 'progress' event.
      // TODO: Document that 'response' field in 'progress' event is null for incoming calls.

      this._progress('local', null);
    }
    /**
     * Answer the call.
     */

  }, {
    key: "answer",
    value: function answer() {
      var _this3 = this;

      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      logger.debug('answer()');
      var request = this._request;
      var extraHeaders = Utils.cloneArray(options.extraHeaders);
      var mediaConstraints = Utils.cloneObject(options.mediaConstraints);
      var mediaStream = options.mediaStream || null;
      var pcConfig = Utils.cloneObject(options.pcConfig, {
        iceServers: []
      });
      var rtcConstraints = options.rtcConstraints || null;
      var rtcAnswerConstraints = options.rtcAnswerConstraints || null;
      var rtcOfferConstraints = Utils.cloneObject(options.rtcOfferConstraints);
      var tracks;
      var peerHasAudioLine = false;
      var peerHasVideoLine = false;
      var peerOffersFullAudio = false;
      var peerOffersFullVideo = false;
      this._rtcAnswerConstraints = rtcAnswerConstraints;
      this._rtcOfferConstraints = options.rtcOfferConstraints || null;
      this._data = options.data || this._data; // Check Session Direction and Status.

      if (this._direction !== 'incoming') {
        throw new Exceptions.NotSupportedError('"answer" not supported for outgoing RTCSession');
      } // Check Session status.


      if (this._status !== C.STATUS_WAITING_FOR_ANSWER) {
        throw new Exceptions.InvalidStateError(this._status);
      } // Session Timers.


      if (this._sessionTimers.enabled) {
        if (Utils.isDecimal(options.sessionTimersExpires)) {
          if (options.sessionTimersExpires >= JsSIP_C.MIN_SESSION_EXPIRES) {
            this._sessionTimers.defaultExpires = options.sessionTimersExpires;
          } else {
            this._sessionTimers.defaultExpires = JsSIP_C.SESSION_EXPIRES;
          }
        }
      }

      this._status = C.STATUS_ANSWERED; // An error on dialog creation will fire 'failed' event.

      if (!this._createDialog(request, 'UAS')) {
        request.reply(500, 'Error creating dialog');
        return;
      }

      clearTimeout(this._timers.userNoAnswerTimer);
      extraHeaders.unshift("Contact: ".concat(this._contact)); // Determine incoming media from incoming SDP offer (if any).

      var sdp = request.parseSDP(); // Make sure sdp.media is an array, not the case if there is only one media.

      if (!Array.isArray(sdp.media)) {
        sdp.media = [sdp.media];
      } // Go through all medias in SDP to find offered capabilities to answer with.


      var _iterator = _createForOfIteratorHelper(sdp.media),
          _step;

      try {
        for (_iterator.s(); !(_step = _iterator.n()).done;) {
          var m = _step.value;

          if (m.type === 'audio') {
            peerHasAudioLine = true;

            if (!m.direction || m.direction === 'sendrecv') {
              peerOffersFullAudio = true;
            }
          }

          if (m.type === 'video') {
            peerHasVideoLine = true;

            if (!m.direction || m.direction === 'sendrecv') {
              peerOffersFullVideo = true;
            }
          }
        } // Remove audio from mediaStream if suggested by mediaConstraints.

      } catch (err) {
        _iterator.e(err);
      } finally {
        _iterator.f();
      }

      if (mediaStream && mediaConstraints.audio === false) {
        tracks = mediaStream.getAudioTracks();

        var _iterator2 = _createForOfIteratorHelper(tracks),
            _step2;

        try {
          for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
            var track = _step2.value;
            mediaStream.removeTrack(track);
          }
        } catch (err) {
          _iterator2.e(err);
        } finally {
          _iterator2.f();
        }
      } // Remove video from mediaStream if suggested by mediaConstraints.


      if (mediaStream && mediaConstraints.video === false) {
        tracks = mediaStream.getVideoTracks();

        var _iterator3 = _createForOfIteratorHelper(tracks),
            _step3;

        try {
          for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
            var _track = _step3.value;
            mediaStream.removeTrack(_track);
          }
        } catch (err) {
          _iterator3.e(err);
        } finally {
          _iterator3.f();
        }
      } // Set audio constraints based on incoming stream if not supplied.


      if (!mediaStream && mediaConstraints.audio === undefined) {
        mediaConstraints.audio = peerOffersFullAudio;
      } // Set video constraints based on incoming stream if not supplied.


      if (!mediaStream && mediaConstraints.video === undefined) {
        mediaConstraints.video = peerOffersFullVideo;
      } // Don't ask for audio if the incoming offer has no audio section.


      if (!mediaStream && !peerHasAudioLine && !rtcOfferConstraints.offerToReceiveAudio) {
        mediaConstraints.audio = false;
      } // Don't ask for video if the incoming offer has no video section.


      if (!mediaStream && !peerHasVideoLine && !rtcOfferConstraints.offerToReceiveVideo) {
        mediaConstraints.video = false;
      } // Create a new RTCPeerConnection instance.
      // TODO: This may throw an error, should react.


      this._createRTCConnection(pcConfig, rtcConstraints);

      Promise.resolve() // Handle local MediaStream.
      .then(function () {
        // A local MediaStream is given, use it.
        if (mediaStream) {
          return mediaStream;
        } // Audio and/or video requested, prompt getUserMedia.
        else if (mediaConstraints.audio || mediaConstraints.video) {
            _this3._localMediaStreamLocallyGenerated = true;
            return navigator.mediaDevices.getUserMedia(mediaConstraints)["catch"](function (error) {
              if (_this3._status === C.STATUS_TERMINATED) {
                throw new Error('terminated');
              }

              request.reply(480);

              _this3._failed('local', null, JsSIP_C.causes.USER_DENIED_MEDIA_ACCESS);

              logger.warn('emit "getusermediafailed" [error:%o]', error);

              _this3.emit('getusermediafailed', error);

              throw new Error('getUserMedia() failed');
            });
          }
      }) // Attach MediaStream to RTCPeerconnection.
      .then(function (stream) {
        if (_this3._status === C.STATUS_TERMINATED) {
          throw new Error('terminated');
        }

        _this3._localMediaStream = stream;

        if (stream) {
          stream.getTracks().forEach(function (track) {
            _this3._connection.addTrack(track, stream);
          });
        }
      }) // Set remote description.
      .then(function () {
        if (_this3._late_sdp) {
          return;
        }

        var e = {
          originator: 'remote',
          type: 'offer',
          sdp: request.body
        };
        logger.debug('emit "sdp"');

        _this3.emit('sdp', e);

        var offer = new RTCSessionDescription({
          type: 'offer',
          sdp: e.sdp
        });
        _this3._connectionPromiseQueue = _this3._connectionPromiseQueue.then(function () {
          return _this3._connection.setRemoteDescription(offer);
        })["catch"](function (error) {
          request.reply(488);

          _this3._failed('system', null, JsSIP_C.causes.WEBRTC_ERROR);

          logger.warn('emit "peerconnection:setremotedescriptionfailed" [error:%o]', error);

          _this3.emit('peerconnection:setremotedescriptionfailed', error);

          throw new Error('peerconnection.setRemoteDescription() failed');
        });
        return _this3._connectionPromiseQueue;
      }) // Create local description.
      .then(function () {
        if (_this3._status === C.STATUS_TERMINATED) {
          throw new Error('terminated');
        } // TODO: Is this event already useful?


        _this3._connecting(request);

        if (!_this3._late_sdp) {
          return _this3._createLocalDescription('answer', rtcAnswerConstraints)["catch"](function () {
            request.reply(500);
            throw new Error('_createLocalDescription() failed');
          });
        } else {
          return _this3._createLocalDescription('offer', _this3._rtcOfferConstraints)["catch"](function () {
            request.reply(500);
            throw new Error('_createLocalDescription() failed');
          });
        }
      }) // Send reply.
      .then(function (desc) {
        if (_this3._status === C.STATUS_TERMINATED) {
          throw new Error('terminated');
        }

        _this3._handleSessionTimersInIncomingRequest(request, extraHeaders);

        request.reply(200, null, extraHeaders, desc, function () {
          _this3._status = C.STATUS_WAITING_FOR_ACK;

          _this3._setInvite2xxTimer(request, desc);

          _this3._setACKTimer();

          _this3._accepted('local');
        }, function () {
          _this3._failed('system', null, JsSIP_C.causes.CONNECTION_ERROR);
        });
      })["catch"](function (error) {
        if (_this3._status === C.STATUS_TERMINATED) {
          return;
        }

        logger.warn(error);
      });
    }
    /**
     * Terminate the call.
     */

  }, {
    key: "terminate",
    value: function terminate() {
      var _this4 = this;

      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      logger.debug('terminate()');
      var cause = options.cause || JsSIP_C.causes.BYE;
      var extraHeaders = Utils.cloneArray(options.extraHeaders);
      var body = options.body;
      var cancel_reason;
      var status_code = options.status_code;
      var reason_phrase = options.reason_phrase; // Check Session Status.

      if (this._status === C.STATUS_TERMINATED) {
        throw new Exceptions.InvalidStateError(this._status);
      }

      switch (this._status) {
        // - UAC -
        case C.STATUS_NULL:
        case C.STATUS_INVITE_SENT:
        case C.STATUS_1XX_RECEIVED:
          logger.debug('canceling session');

          if (status_code && (status_code < 200 || status_code >= 700)) {
            throw new TypeError("Invalid status_code: ".concat(status_code));
          } else if (status_code) {
            reason_phrase = reason_phrase || JsSIP_C.REASON_PHRASE[status_code] || '';
            cancel_reason = "SIP ;cause=".concat(status_code, " ;text=\"").concat(reason_phrase, "\"");
          } // Check Session Status.


          if (this._status === C.STATUS_NULL || this._status === C.STATUS_INVITE_SENT) {
            this._is_canceled = true;
            this._cancel_reason = cancel_reason;
          } else if (this._status === C.STATUS_1XX_RECEIVED) {
            this._request.cancel(cancel_reason);
          }

          this._status = C.STATUS_CANCELED;

          this._failed('local', null, JsSIP_C.causes.CANCELED);

          break;
        // - UAS -

        case C.STATUS_WAITING_FOR_ANSWER:
        case C.STATUS_ANSWERED:
          logger.debug('rejecting session');
          status_code = status_code || 480;

          if (status_code < 300 || status_code >= 700) {
            throw new TypeError("Invalid status_code: ".concat(status_code));
          }

          this._request.reply(status_code, reason_phrase, extraHeaders, body);

          this._failed('local', null, JsSIP_C.causes.REJECTED);

          break;

        case C.STATUS_WAITING_FOR_ACK:
        case C.STATUS_CONFIRMED:
          logger.debug('terminating session');
          reason_phrase = options.reason_phrase || JsSIP_C.REASON_PHRASE[status_code] || '';

          if (status_code && (status_code < 200 || status_code >= 700)) {
            throw new TypeError("Invalid status_code: ".concat(status_code));
          } else if (status_code) {
            extraHeaders.push("Reason: SIP ;cause=".concat(status_code, "; text=\"").concat(reason_phrase, "\""));
          }
          /* RFC 3261 section 15 (Terminating a session):
            *
            * "...the callee's UA MUST NOT send a BYE on a confirmed dialog
            * until it has received an ACK for its 2xx response or until the server
            * transaction times out."
            */


          if (this._status === C.STATUS_WAITING_FOR_ACK && this._direction === 'incoming' && this._request.server_transaction.state !== Transactions.C.STATUS_TERMINATED) {
            // Save the dialog for later restoration.
            var dialog = this._dialog; // Send the BYE as soon as the ACK is received...

            this.receiveRequest = function (_ref) {
              var method = _ref.method;

              if (method === JsSIP_C.ACK) {
                _this4.sendRequest(JsSIP_C.BYE, {
                  extraHeaders: extraHeaders,
                  body: body
                });

                dialog.terminate();
              }
            }; // .., or when the INVITE transaction times out


            this._request.server_transaction.on('stateChanged', function () {
              if (_this4._request.server_transaction.state === Transactions.C.STATUS_TERMINATED) {
                _this4.sendRequest(JsSIP_C.BYE, {
                  extraHeaders: extraHeaders,
                  body: body
                });

                dialog.terminate();
              }
            });

            this._ended('local', null, cause); // Restore the dialog into 'this' in order to be able to send the in-dialog BYE :-).


            this._dialog = dialog; // Restore the dialog into 'ua' so the ACK can reach 'this' session.

            this._ua.newDialog(dialog);
          } else {
            this.sendRequest(JsSIP_C.BYE, {
              extraHeaders: extraHeaders,
              body: body
            });

            this._ended('local', null, cause);
          }

      }
    }
  }, {
    key: "sendDTMF",
    value: function sendDTMF(tones) {
      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      logger.debug('sendDTMF() | tones: %s', tones);
      var position = 0;
      var duration = options.duration || null;
      var interToneGap = options.interToneGap || null;
      var transportType = options.transportType || JsSIP_C.DTMF_TRANSPORT.INFO;

      if (tones === undefined) {
        throw new TypeError('Not enough arguments');
      } // Check Session Status.


      if (this._status !== C.STATUS_CONFIRMED && this._status !== C.STATUS_WAITING_FOR_ACK && this._status !== C.STATUS_1XX_RECEIVED) {
        throw new Exceptions.InvalidStateError(this._status);
      } // Check Transport type.


      if (transportType !== JsSIP_C.DTMF_TRANSPORT.INFO && transportType !== JsSIP_C.DTMF_TRANSPORT.RFC2833) {
        throw new TypeError("invalid transportType: ".concat(transportType));
      } // Convert to string.


      if (typeof tones === 'number') {
        tones = tones.toString();
      } // Check tones.


      if (!tones || typeof tones !== 'string' || !tones.match(/^[0-9A-DR#*,]+$/i)) {
        throw new TypeError("Invalid tones: ".concat(tones));
      } // Check duration.


      if (duration && !Utils.isDecimal(duration)) {
        throw new TypeError("Invalid tone duration: ".concat(duration));
      } else if (!duration) {
        duration = RTCSession_DTMF.C.DEFAULT_DURATION;
      } else if (duration < RTCSession_DTMF.C.MIN_DURATION) {
        logger.debug("\"duration\" value is lower than the minimum allowed, setting it to ".concat(RTCSession_DTMF.C.MIN_DURATION, " milliseconds"));
        duration = RTCSession_DTMF.C.MIN_DURATION;
      } else if (duration > RTCSession_DTMF.C.MAX_DURATION) {
        logger.debug("\"duration\" value is greater than the maximum allowed, setting it to ".concat(RTCSession_DTMF.C.MAX_DURATION, " milliseconds"));
        duration = RTCSession_DTMF.C.MAX_DURATION;
      } else {
        duration = Math.abs(duration);
      }

      options.duration = duration; // Check interToneGap.

      if (interToneGap && !Utils.isDecimal(interToneGap)) {
        throw new TypeError("Invalid interToneGap: ".concat(interToneGap));
      } else if (!interToneGap) {
        interToneGap = RTCSession_DTMF.C.DEFAULT_INTER_TONE_GAP;
      } else if (interToneGap < RTCSession_DTMF.C.MIN_INTER_TONE_GAP) {
        logger.debug("\"interToneGap\" value is lower than the minimum allowed, setting it to ".concat(RTCSession_DTMF.C.MIN_INTER_TONE_GAP, " milliseconds"));
        interToneGap = RTCSession_DTMF.C.MIN_INTER_TONE_GAP;
      } else {
        interToneGap = Math.abs(interToneGap);
      } // RFC2833. Let RTCDTMFSender enqueue the DTMFs.


      if (transportType === JsSIP_C.DTMF_TRANSPORT.RFC2833) {
        // Send DTMF in current audio RTP stream.
        var sender = this._getDTMFRTPSender();

        if (sender) {
          // Add remaining buffered tones.
          tones = sender.toneBuffer + tones; // Insert tones.

          sender.insertDTMF(tones, duration, interToneGap);
        }

        return;
      }

      if (this._tones) {
        // Tones are already queued, just add to the queue.
        this._tones += tones;
        return;
      }

      this._tones = tones; // Send the first tone.

      _sendDTMF.call(this);

      function _sendDTMF() {
        var _this5 = this;

        var timeout;

        if (this._status === C.STATUS_TERMINATED || !this._tones || position >= this._tones.length) {
          // Stop sending DTMF.
          this._tones = null;
          return;
        }

        var tone = this._tones[position];
        position += 1;

        if (tone === ',') {
          timeout = 2000;
        } else {
          // Send DTMF via SIP INFO messages.
          var dtmf = new RTCSession_DTMF(this);
          options.eventHandlers = {
            onFailed: function onFailed() {
              _this5._tones = null;
            }
          };
          dtmf.send(tone, options);
          timeout = duration + interToneGap;
        } // Set timeout for the next tone.


        setTimeout(_sendDTMF.bind(this), timeout);
      }
    }
  }, {
    key: "sendInfo",
    value: function sendInfo(contentType, body) {
      var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
      logger.debug('sendInfo()'); // Check Session Status.

      if (this._status !== C.STATUS_CONFIRMED && this._status !== C.STATUS_WAITING_FOR_ACK && this._status !== C.STATUS_1XX_RECEIVED) {
        throw new Exceptions.InvalidStateError(this._status);
      }

      var info = new RTCSession_Info(this);
      info.send(contentType, body, options);
    }
    /**
     * Mute
     */

  }, {
    key: "mute",
    value: function mute() {
      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
        audio: true,
        video: false
      };
      logger.debug('mute()');
      var audioMuted = false,
          videoMuted = false;

      if (this._audioMuted === false && options.audio) {
        audioMuted = true;
        this._audioMuted = true;

        this._toggleMuteAudio(true);
      }

      if (this._videoMuted === false && options.video) {
        videoMuted = true;
        this._videoMuted = true;

        this._toggleMuteVideo(true);
      }

      if (audioMuted === true || videoMuted === true) {
        this._onmute({
          audio: audioMuted,
          video: videoMuted
        });
      }
    }
    /**
     * Unmute
     */

  }, {
    key: "unmute",
    value: function unmute() {
      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
        audio: true,
        video: true
      };
      logger.debug('unmute()');
      var audioUnMuted = false,
          videoUnMuted = false;

      if (this._audioMuted === true && options.audio) {
        audioUnMuted = true;
        this._audioMuted = false;

        if (this._localHold === false) {
          this._toggleMuteAudio(false);
        }
      }

      if (this._videoMuted === true && options.video) {
        videoUnMuted = true;
        this._videoMuted = false;

        if (this._localHold === false) {
          this._toggleMuteVideo(false);
        }
      }

      if (audioUnMuted === true || videoUnMuted === true) {
        this._onunmute({
          audio: audioUnMuted,
          video: videoUnMuted
        });
      }
    }
    /**
     * Hold
     */

  }, {
    key: "hold",
    value: function hold() {
      var _this6 = this;

      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var done = arguments.length > 1 ? arguments[1] : undefined;
      logger.debug('hold()');

      if (this._status !== C.STATUS_WAITING_FOR_ACK && this._status !== C.STATUS_CONFIRMED) {
        return false;
      }

      if (this._localHold === true) {
        return false;
      }

      if (!this._isReadyToReOffer()) {
        return false;
      }

      this._localHold = true;

      this._onhold('local');

      var eventHandlers = {
        succeeded: function succeeded() {
          if (done) {
            done();
          }
        },
        failed: function failed() {
          _this6.terminate({
            cause: JsSIP_C.causes.WEBRTC_ERROR,
            status_code: 500,
            reason_phrase: 'Hold Failed'
          });
        }
      };

      if (options.useUpdate) {
        this._sendUpdate({
          sdpOffer: true,
          eventHandlers: eventHandlers,
          extraHeaders: options.extraHeaders
        });
      } else {
        this._sendReinvite({
          eventHandlers: eventHandlers,
          extraHeaders: options.extraHeaders
        });
      }

      return true;
    }
  }, {
    key: "unhold",
    value: function unhold() {
      var _this7 = this;

      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var done = arguments.length > 1 ? arguments[1] : undefined;
      logger.debug('unhold()');

      if (this._status !== C.STATUS_WAITING_FOR_ACK && this._status !== C.STATUS_CONFIRMED) {
        return false;
      }

      if (this._localHold === false) {
        return false;
      }

      if (!this._isReadyToReOffer()) {
        return false;
      }

      this._localHold = false;

      this._onunhold('local');

      var eventHandlers = {
        succeeded: function succeeded() {
          if (done) {
            done();
          }
        },
        failed: function failed() {
          _this7.terminate({
            cause: JsSIP_C.causes.WEBRTC_ERROR,
            status_code: 500,
            reason_phrase: 'Unhold Failed'
          });
        }
      };

      if (options.useUpdate) {
        this._sendUpdate({
          sdpOffer: true,
          eventHandlers: eventHandlers,
          extraHeaders: options.extraHeaders
        });
      } else {
        this._sendReinvite({
          eventHandlers: eventHandlers,
          extraHeaders: options.extraHeaders
        });
      }

      return true;
    }
  }, {
    key: "renegotiate",
    value: function renegotiate() {
      var _this8 = this;

      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var done = arguments.length > 1 ? arguments[1] : undefined;
      logger.debug('renegotiate()');
      var rtcOfferConstraints = options.rtcOfferConstraints || null;

      if (this._status !== C.STATUS_WAITING_FOR_ACK && this._status !== C.STATUS_CONFIRMED) {
        return false;
      }

      if (!this._isReadyToReOffer()) {
        return false;
      }

      var eventHandlers = {
        succeeded: function succeeded() {
          if (done) {
            done();
          }
        },
        failed: function failed() {
          _this8.terminate({
            cause: JsSIP_C.causes.WEBRTC_ERROR,
            status_code: 500,
            reason_phrase: 'Media Renegotiation Failed'
          });
        }
      };

      this._setLocalMediaStatus();

      if (options.useUpdate) {
        this._sendUpdate({
          sdpOffer: true,
          eventHandlers: eventHandlers,
          rtcOfferConstraints: rtcOfferConstraints,
          extraHeaders: options.extraHeaders
        });
      } else {
        this._sendReinvite({
          eventHandlers: eventHandlers,
          rtcOfferConstraints: rtcOfferConstraints,
          extraHeaders: options.extraHeaders
        });
      }

      return true;
    }
    /**
     * Refer
     */

  }, {
    key: "refer",
    value: function refer(target, options) {
      var _this9 = this;

      logger.debug('refer()');
      var originalTarget = target;

      if (this._status !== C.STATUS_WAITING_FOR_ACK && this._status !== C.STATUS_CONFIRMED) {
        return false;
      } // Check target validity.


      target = this._ua.normalizeTarget(target);

      if (!target) {
        throw new TypeError("Invalid target: ".concat(originalTarget));
      }

      var referSubscriber = new RTCSession_ReferSubscriber(this);
      referSubscriber.sendRefer(target, options); // Store in the map.

      var id = referSubscriber.id;
      this._referSubscribers[id] = referSubscriber; // Listen for ending events so we can remove it from the map.

      referSubscriber.on('requestFailed', function () {
        delete _this9._referSubscribers[id];
      });
      referSubscriber.on('accepted', function () {
        delete _this9._referSubscribers[id];
      });
      referSubscriber.on('failed', function () {
        delete _this9._referSubscribers[id];
      });
      return referSubscriber;
    }
    /**
     * Send a generic in-dialog Request
     */

  }, {
    key: "sendRequest",
    value: function sendRequest(method, options) {
      logger.debug('sendRequest()');
      return this._dialog.sendRequest(method, options);
    }
    /**
     * In dialog Request Reception
     */

  }, {
    key: "receiveRequest",
    value: function receiveRequest(request) {
      var _this10 = this;

      logger.debug('receiveRequest()');

      if (request.method === JsSIP_C.CANCEL) {
        /* RFC3261 15 States that a UAS may have accepted an invitation while a CANCEL
        * was in progress and that the UAC MAY continue with the session established by
        * any 2xx response, or MAY terminate with BYE. JsSIP does continue with the
        * established session. So the CANCEL is processed only if the session is not yet
        * established.
        */

        /*
        * Terminate the whole session in case the user didn't accept (or yet send the answer)
        * nor reject the request opening the session.
        */
        if (this._status === C.STATUS_WAITING_FOR_ANSWER || this._status === C.STATUS_ANSWERED) {
          this._status = C.STATUS_CANCELED;

          this._request.reply(487);

          this._failed('remote', request, JsSIP_C.causes.CANCELED);
        }
      } else {
        // Requests arriving here are in-dialog requests.
        switch (request.method) {
          case JsSIP_C.ACK:
            if (this._status !== C.STATUS_WAITING_FOR_ACK) {
              return;
            } // Update signaling status.


            this._status = C.STATUS_CONFIRMED;
            clearTimeout(this._timers.ackTimer);
            clearTimeout(this._timers.invite2xxTimer);

            if (this._late_sdp) {
              if (!request.body) {
                this.terminate({
                  cause: JsSIP_C.causes.MISSING_SDP,
                  status_code: 400
                });
                break;
              }

              var e = {
                originator: 'remote',
                type: 'answer',
                sdp: request.body
              };
              logger.debug('emit "sdp"');
              this.emit('sdp', e);
              var answer = new RTCSessionDescription({
                type: 'answer',
                sdp: e.sdp
              });
              this._connectionPromiseQueue = this._connectionPromiseQueue.then(function () {
                return _this10._connection.setRemoteDescription(answer);
              }).then(function () {
                if (!_this10._is_confirmed) {
                  _this10._confirmed('remote', request);
                }
              })["catch"](function (error) {
                _this10.terminate({
                  cause: JsSIP_C.causes.BAD_MEDIA_DESCRIPTION,
                  status_code: 488
                });

                logger.warn('emit "peerconnection:setremotedescriptionfailed" [error:%o]', error);

                _this10.emit('peerconnection:setremotedescriptionfailed', error);
              });
            } else if (!this._is_confirmed) {
              this._confirmed('remote', request);
            }

            break;

          case JsSIP_C.BYE:
            if (this._status === C.STATUS_CONFIRMED || this._status === C.STATUS_WAITING_FOR_ACK) {
              request.reply(200);

              this._ended('remote', request, JsSIP_C.causes.BYE);
            } else if (this._status === C.STATUS_INVITE_RECEIVED || this._status === C.STATUS_WAITING_FOR_ANSWER) {
              request.reply(200);

              this._request.reply(487, 'BYE Received');

              this._ended('remote', request, JsSIP_C.causes.BYE);
            } else {
              request.reply(403, 'Wrong Status');
            }

            break;

          case JsSIP_C.INVITE:
            if (this._status === C.STATUS_CONFIRMED) {
              if (request.hasHeader('replaces')) {
                this._receiveReplaces(request);
              } else {
                this._receiveReinvite(request);
              }
            } else {
              request.reply(403, 'Wrong Status');
            }

            break;

          case JsSIP_C.INFO:
            if (this._status === C.STATUS_1XX_RECEIVED || this._status === C.STATUS_WAITING_FOR_ANSWER || this._status === C.STATUS_ANSWERED || this._status === C.STATUS_WAITING_FOR_ACK || this._status === C.STATUS_CONFIRMED) {
              var contentType = request.hasHeader('Content-Type') ? request.getHeader('Content-Type').toLowerCase() : undefined;

              if (contentType && contentType.match(/^application\/dtmf-relay/i)) {
                new RTCSession_DTMF(this).init_incoming(request);
              } else if (contentType !== undefined) {
                new RTCSession_Info(this).init_incoming(request);
              } else {
                request.reply(415);
              }
            } else {
              request.reply(403, 'Wrong Status');
            }

            break;

          case JsSIP_C.UPDATE:
            if (this._status === C.STATUS_CONFIRMED) {
              this._receiveUpdate(request);
            } else {
              request.reply(403, 'Wrong Status');
            }

            break;

          case JsSIP_C.REFER:
            if (this._status === C.STATUS_CONFIRMED) {
              this._receiveRefer(request);
            } else {
              request.reply(403, 'Wrong Status');
            }

            break;

          case JsSIP_C.NOTIFY:
            if (this._status === C.STATUS_CONFIRMED) {
              this._receiveNotify(request);
            } else {
              request.reply(403, 'Wrong Status');
            }

            break;

          default:
            request.reply(501);
        }
      }
    }
    /**
     * Session Callbacks
     */

  }, {
    key: "onTransportError",
    value: function onTransportError() {
      logger.warn('onTransportError()');

      if (this._status !== C.STATUS_TERMINATED) {
        this.terminate({
          status_code: 500,
          reason_phrase: JsSIP_C.causes.CONNECTION_ERROR,
          cause: JsSIP_C.causes.CONNECTION_ERROR
        });
      }
    }
  }, {
    key: "onRequestTimeout",
    value: function onRequestTimeout() {
      logger.warn('onRequestTimeout()');

      if (this._status !== C.STATUS_TERMINATED) {
        this.terminate({
          status_code: 408,
          reason_phrase: JsSIP_C.causes.REQUEST_TIMEOUT,
          cause: JsSIP_C.causes.REQUEST_TIMEOUT
        });
      }
    }
  }, {
    key: "onDialogError",
    value: function onDialogError() {
      logger.warn('onDialogError()');

      if (this._status !== C.STATUS_TERMINATED) {
        this.terminate({
          status_code: 500,
          reason_phrase: JsSIP_C.causes.DIALOG_ERROR,
          cause: JsSIP_C.causes.DIALOG_ERROR
        });
      }
    } // Called from DTMF handler.

  }, {
    key: "newDTMF",
    value: function newDTMF(data) {
      logger.debug('newDTMF()');
      this.emit('newDTMF', data);
    } // Called from Info handler.

  }, {
    key: "newInfo",
    value: function newInfo(data) {
      logger.debug('newInfo()');
      this.emit('newInfo', data);
    }
    /**
     * Check if RTCSession is ready for an outgoing re-INVITE or UPDATE with SDP.
     */

  }, {
    key: "_isReadyToReOffer",
    value: function _isReadyToReOffer() {
      if (!this._rtcReady) {
        logger.debug('_isReadyToReOffer() | internal WebRTC status not ready');
        return false;
      } // No established yet.


      if (!this._dialog) {
        logger.debug('_isReadyToReOffer() | session not established yet');
        return false;
      } // Another INVITE transaction is in progress.


      if (this._dialog.uac_pending_reply === true || this._dialog.uas_pending_reply === true) {
        logger.debug('_isReadyToReOffer() | there is another INVITE/UPDATE transaction in progress');
        return false;
      }

      return true;
    }
  }, {
    key: "_close",
    value: function _close() {
      logger.debug('close()'); // Close local MediaStream if it was not given by the user.

      if (this._localMediaStream && this._localMediaStreamLocallyGenerated) {
        logger.debug('close() | closing local MediaStream');
        Utils.closeMediaStream(this._localMediaStream);
      }

      if (this._status === C.STATUS_TERMINATED) {
        return;
      }

      this._status = C.STATUS_TERMINATED; // Terminate RTC.

      if (this._connection) {
        try {
          this._connection.close();
        } catch (error) {
          logger.warn('close() | error closing the RTCPeerConnection: %o', error);
        }
      } // Terminate signaling.
      // Clear SIP timers.


      for (var timer in this._timers) {
        if (Object.prototype.hasOwnProperty.call(this._timers, timer)) {
          clearTimeout(this._timers[timer]);
        }
      } // Clear Session Timers.


      clearTimeout(this._sessionTimers.timer); // Terminate confirmed dialog.

      if (this._dialog) {
        this._dialog.terminate();

        delete this._dialog;
      } // Terminate early dialogs.


      for (var dialog in this._earlyDialogs) {
        if (Object.prototype.hasOwnProperty.call(this._earlyDialogs, dialog)) {
          this._earlyDialogs[dialog].terminate();

          delete this._earlyDialogs[dialog];
        }
      } // Terminate REFER subscribers.


      for (var subscriber in this._referSubscribers) {
        if (Object.prototype.hasOwnProperty.call(this._referSubscribers, subscriber)) {
          delete this._referSubscribers[subscriber];
        }
      }

      this._ua.destroyRTCSession(this);
    }
    /**
     * Private API.
     */

    /**
     * RFC3261 13.3.1.4
     * Response retransmissions cannot be accomplished by transaction layer
     *  since it is destroyed when receiving the first 2xx answer
     */

  }, {
    key: "_setInvite2xxTimer",
    value: function _setInvite2xxTimer(request, body) {
      var timeout = Timers.T1;

      function invite2xxRetransmission() {
        if (this._status !== C.STATUS_WAITING_FOR_ACK) {
          return;
        }

        request.reply(200, null, ["Contact: ".concat(this._contact)], body);

        if (timeout < Timers.T2) {
          timeout = timeout * 2;

          if (timeout > Timers.T2) {
            timeout = Timers.T2;
          }
        }

        this._timers.invite2xxTimer = setTimeout(invite2xxRetransmission.bind(this), timeout);
      }

      this._timers.invite2xxTimer = setTimeout(invite2xxRetransmission.bind(this), timeout);
    }
    /**
     * RFC3261 14.2
     * If a UAS generates a 2xx response and never receives an ACK,
     *  it SHOULD generate a BYE to terminate the dialog.
     */

  }, {
    key: "_setACKTimer",
    value: function _setACKTimer() {
      var _this11 = this;

      this._timers.ackTimer = setTimeout(function () {
        if (_this11._status === C.STATUS_WAITING_FOR_ACK) {
          logger.debug('no ACK received, terminating the session');
          clearTimeout(_this11._timers.invite2xxTimer);

          _this11.sendRequest(JsSIP_C.BYE);

          _this11._ended('remote', null, JsSIP_C.causes.NO_ACK);
        }
      }, Timers.TIMER_H);
    }
  }, {
    key: "_createRTCConnection",
    value: function _createRTCConnection(pcConfig, rtcConstraints) {
      var _this12 = this;

      this._connection = new RTCPeerConnection(pcConfig, rtcConstraints);

      this._connection.addEventListener('iceconnectionstatechange', function () {
        var state = _this12._connection.iceConnectionState; // TODO: Do more with different states.

        if (state === 'failed') {
          _this12.terminate({
            cause: JsSIP_C.causes.RTP_TIMEOUT,
            status_code: 408,
            reason_phrase: JsSIP_C.causes.RTP_TIMEOUT
          });
        }
      });

      logger.debug('emit "peerconnection"');
      this.emit('peerconnection', {
        peerconnection: this._connection
      });
    }
  }, {
    key: "_createLocalDescription",
    value: function _createLocalDescription(type, constraints) {
      var _this13 = this;

      logger.debug('createLocalDescription()');
      if (type !== 'offer' && type !== 'answer') throw new Error("createLocalDescription() | invalid type \"".concat(type, "\""));
      var connection = this._connection;
      this._rtcReady = false;
      return Promise.resolve() // Create Offer or Answer.
      .then(function () {
        if (type === 'offer') {
          return connection.createOffer(constraints)["catch"](function (error) {
            logger.warn('emit "peerconnection:createofferfailed" [error:%o]', error);

            _this13.emit('peerconnection:createofferfailed', error);

            return Promise.reject(error);
          });
        } else {
          return connection.createAnswer(constraints)["catch"](function (error) {
            logger.warn('emit "peerconnection:createanswerfailed" [error:%o]', error);

            _this13.emit('peerconnection:createanswerfailed', error);

            return Promise.reject(error);
          });
        }
      }) // Set local description.
      .then(function (desc) {
        return connection.setLocalDescription(desc)["catch"](function (error) {
          _this13._rtcReady = true;
          logger.warn('emit "peerconnection:setlocaldescriptionfailed" [error:%o]', error);

          _this13.emit('peerconnection:setlocaldescriptionfailed', error);

          return Promise.reject(error);
        });
      }).then(function () {
        // Resolve right away if 'pc.iceGatheringState' is 'complete'.

        /**
         * Resolve right away if:
         * - 'connection.iceGatheringState' is 'complete' and no 'iceRestart' constraint is set.
         * - 'connection.iceGatheringState' is 'gathering' and 'iceReady' is true.
         */
        var iceRestart = constraints && constraints.iceRestart;

        if (connection.iceGatheringState === 'complete' && !iceRestart || connection.iceGatheringState === 'gathering' && _this13._iceReady) {
          _this13._rtcReady = true;
          var e = {
            originator: 'local',
            type: type,
            sdp: connection.localDescription.sdp
          };
          logger.debug('emit "sdp"');

          _this13.emit('sdp', e);

          return Promise.resolve(e.sdp);
        } // Add 'pc.onicencandidate' event handler to resolve on last candidate.


        return new Promise(function (resolve) {
          var finished = false;
          var iceCandidateListener;
          var iceGatheringStateListener;
          _this13._iceReady = false;

          var ready = function ready() {
            connection.removeEventListener('icecandidate', iceCandidateListener);
            connection.removeEventListener('icegatheringstatechange', iceGatheringStateListener);
            finished = true;
            _this13._rtcReady = true; // connection.iceGatheringState will still indicate 'gathering' and thus be blocking.

            _this13._iceReady = true;
            var e = {
              originator: 'local',
              type: type,
              sdp: connection.localDescription.sdp
            };
            logger.debug('emit "sdp"');

            _this13.emit('sdp', e);

            resolve(e.sdp);
          };

          connection.addEventListener('icecandidate', iceCandidateListener = function iceCandidateListener(event) {
            var candidate = event.candidate;

            if (candidate) {
              _this13.emit('icecandidate', {
                candidate: candidate,
                ready: ready
              });
            } else if (!finished) {
              ready();
            }
          });
          connection.addEventListener('icegatheringstatechange', iceGatheringStateListener = function iceGatheringStateListener() {
            if (connection.iceGatheringState === 'complete' && !finished) {
              ready();
            }
          });
        });
      });
    }
    /**
     * Dialog Management
     */

  }, {
    key: "_createDialog",
    value: function _createDialog(message, type, early) {
      var local_tag = type === 'UAS' ? message.to_tag : message.from_tag;
      var remote_tag = type === 'UAS' ? message.from_tag : message.to_tag;
      var id = message.call_id + local_tag + remote_tag;
      var early_dialog = this._earlyDialogs[id]; // Early Dialog.

      if (early) {
        if (early_dialog) {
          return true;
        } else {
          early_dialog = new Dialog(this, message, type, Dialog.C.STATUS_EARLY); // Dialog has been successfully created.

          if (early_dialog.error) {
            logger.debug(early_dialog.error);

            this._failed('remote', message, JsSIP_C.causes.INTERNAL_ERROR);

            return false;
          } else {
            this._earlyDialogs[id] = early_dialog;
            return true;
          }
        }
      } // Confirmed Dialog.
      else {
          this._from_tag = message.from_tag;
          this._to_tag = message.to_tag; // In case the dialog is in _early_ state, update it.

          if (early_dialog) {
            early_dialog.update(message, type);
            this._dialog = early_dialog;
            delete this._earlyDialogs[id];
            return true;
          } // Otherwise, create a _confirmed_ dialog.


          var dialog = new Dialog(this, message, type);

          if (dialog.error) {
            logger.debug(dialog.error);

            this._failed('remote', message, JsSIP_C.causes.INTERNAL_ERROR);

            return false;
          } else {
            this._dialog = dialog;
            return true;
          }
        }
    }
    /**
     * In dialog INVITE Reception
     */

  }, {
    key: "_receiveReinvite",
    value: function _receiveReinvite(request) {
      var _this14 = this;

      logger.debug('receiveReinvite()');
      var contentType = request.hasHeader('Content-Type') ? request.getHeader('Content-Type').toLowerCase() : undefined;
      var data = {
        request: request,
        callback: undefined,
        reject: reject.bind(this)
      };
      var rejected = false;

      function reject() {
        var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
        rejected = true;
        var status_code = options.status_code || 403;
        var reason_phrase = options.reason_phrase || '';
        var extraHeaders = Utils.cloneArray(options.extraHeaders);

        if (this._status !== C.STATUS_CONFIRMED) {
          return false;
        }

        if (status_code < 300 || status_code >= 700) {
          throw new TypeError("Invalid status_code: ".concat(status_code));
        }

        request.reply(status_code, reason_phrase, extraHeaders);
      } // Emit 'reinvite'.


      this.emit('reinvite', data);

      if (rejected) {
        return;
      }

      this._late_sdp = false; // Request without SDP.

      if (!request.body) {
        this._late_sdp = true;

        if (this._remoteHold) {
          this._remoteHold = false;

          this._onunhold('remote');
        }

        this._connectionPromiseQueue = this._connectionPromiseQueue.then(function () {
          return _this14._createLocalDescription('offer', _this14._rtcOfferConstraints);
        }).then(function (sdp) {
          sendAnswer.call(_this14, sdp);
        })["catch"](function () {
          request.reply(500);
        });
        return;
      } // Request with SDP.


      if (contentType !== 'application/sdp') {
        logger.debug('invalid Content-Type');
        request.reply(415);
        return;
      }

      this._processInDialogSdpOffer(request) // Send answer.
      .then(function (desc) {
        if (_this14._status === C.STATUS_TERMINATED) {
          return;
        }

        sendAnswer.call(_this14, desc);
      })["catch"](function (error) {
        logger.warn(error);
      });

      function sendAnswer(desc) {
        var _this15 = this;

        var extraHeaders = ["Contact: ".concat(this._contact)];

        this._handleSessionTimersInIncomingRequest(request, extraHeaders);

        if (this._late_sdp) {
          desc = this._mangleOffer(desc);
        }

        request.reply(200, null, extraHeaders, desc, function () {
          _this15._status = C.STATUS_WAITING_FOR_ACK;

          _this15._setInvite2xxTimer(request, desc);

          _this15._setACKTimer();
        }); // If callback is given execute it.

        if (typeof data.callback === 'function') {
          data.callback();
        }
      }
    }
    /**
     * In dialog UPDATE Reception
     */

  }, {
    key: "_receiveUpdate",
    value: function _receiveUpdate(request) {
      var _this16 = this;

      logger.debug('receiveUpdate()');
      var contentType = request.hasHeader('Content-Type') ? request.getHeader('Content-Type').toLowerCase() : undefined;
      var data = {
        request: request,
        callback: undefined,
        reject: reject.bind(this)
      };
      var rejected = false;

      function reject() {
        var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
        rejected = true;
        var status_code = options.status_code || 403;
        var reason_phrase = options.reason_phrase || '';
        var extraHeaders = Utils.cloneArray(options.extraHeaders);

        if (this._status !== C.STATUS_CONFIRMED) {
          return false;
        }

        if (status_code < 300 || status_code >= 700) {
          throw new TypeError("Invalid status_code: ".concat(status_code));
        }

        request.reply(status_code, reason_phrase, extraHeaders);
      } // Emit 'update'.


      this.emit('update', data);

      if (rejected) {
        return;
      }

      if (!request.body) {
        sendAnswer.call(this, null);
        return;
      }

      if (contentType !== 'application/sdp') {
        logger.debug('invalid Content-Type');
        request.reply(415);
        return;
      }

      this._processInDialogSdpOffer(request) // Send answer.
      .then(function (desc) {
        if (_this16._status === C.STATUS_TERMINATED) {
          return;
        }

        sendAnswer.call(_this16, desc);
      })["catch"](function (error) {
        logger.warn(error);
      });

      function sendAnswer(desc) {
        var extraHeaders = ["Contact: ".concat(this._contact)];

        this._handleSessionTimersInIncomingRequest(request, extraHeaders);

        request.reply(200, null, extraHeaders, desc); // If callback is given execute it.

        if (typeof data.callback === 'function') {
          data.callback();
        }
      }
    }
  }, {
    key: "_processInDialogSdpOffer",
    value: function _processInDialogSdpOffer(request) {
      var _this17 = this;

      logger.debug('_processInDialogSdpOffer()');
      var sdp = request.parseSDP();
      var hold = false;

      var _iterator4 = _createForOfIteratorHelper(sdp.media),
          _step4;

      try {
        for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
          var m = _step4.value;

          if (holdMediaTypes.indexOf(m.type) === -1) {
            continue;
          }

          var direction = m.direction || sdp.direction || 'sendrecv';

          if (direction === 'sendonly' || direction === 'inactive') {
            hold = true;
          } // If at least one of the streams is active don't emit 'hold'.
          else {
              hold = false;
              break;
            }
        }
      } catch (err) {
        _iterator4.e(err);
      } finally {
        _iterator4.f();
      }

      var e = {
        originator: 'remote',
        type: 'offer',
        sdp: request.body
      };
      logger.debug('emit "sdp"');
      this.emit('sdp', e);
      var offer = new RTCSessionDescription({
        type: 'offer',
        sdp: e.sdp
      });
      this._connectionPromiseQueue = this._connectionPromiseQueue // Set remote description.
      .then(function () {
        if (_this17._status === C.STATUS_TERMINATED) {
          throw new Error('terminated');
        }

        return _this17._connection.setRemoteDescription(offer)["catch"](function (error) {
          request.reply(488);
          logger.warn('emit "peerconnection:setremotedescriptionfailed" [error:%o]', error);

          _this17.emit('peerconnection:setremotedescriptionfailed', error);

          throw error;
        });
      }).then(function () {
        if (_this17._status === C.STATUS_TERMINATED) {
          throw new Error('terminated');
        }

        if (_this17._remoteHold === true && hold === false) {
          _this17._remoteHold = false;

          _this17._onunhold('remote');
        } else if (_this17._remoteHold === false && hold === true) {
          _this17._remoteHold = true;

          _this17._onhold('remote');
        }
      }) // Create local description.
      .then(function () {
        if (_this17._status === C.STATUS_TERMINATED) {
          throw new Error('terminated');
        }

        return _this17._createLocalDescription('answer', _this17._rtcAnswerConstraints)["catch"](function (error) {
          request.reply(500);
          logger.warn('emit "peerconnection:createtelocaldescriptionfailed" [error:%o]', error);
          throw error;
        });
      })["catch"](function (error) {
        logger.warn('_processInDialogSdpOffer() failed [error: %o]', error);
      });
      return this._connectionPromiseQueue;
    }
    /**
     * In dialog Refer Reception
     */

  }, {
    key: "_receiveRefer",
    value: function _receiveRefer(request) {
      var _this18 = this;

      logger.debug('receiveRefer()');

      if (!request.refer_to) {
        logger.debug('no Refer-To header field present in REFER');
        request.reply(400);
        return;
      }

      if (request.refer_to.uri.scheme !== JsSIP_C.SIP) {
        logger.debug('Refer-To header field points to a non-SIP URI scheme');
        request.reply(416);
        return;
      } // Reply before the transaction timer expires.


      request.reply(202);
      var notifier = new RTCSession_ReferNotifier(this, request.cseq);
      logger.debug('emit "refer"'); // Emit 'refer'.

      this.emit('refer', {
        request: request,
        accept: function accept(initCallback, options) {
          _accept.call(_this18, initCallback, options);
        },
        reject: function reject() {
          _reject.call(_this18);
        }
      });

      function _accept(initCallback) {
        var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
        initCallback = typeof initCallback === 'function' ? initCallback : null;

        if (this._status !== C.STATUS_WAITING_FOR_ACK && this._status !== C.STATUS_CONFIRMED) {
          return false;
        }

        var session = new RTCSession(this._ua);
        session.on('progress', function (_ref2) {
          var response = _ref2.response;
          notifier.notify(response.status_code, response.reason_phrase);
        });
        session.on('accepted', function (_ref3) {
          var response = _ref3.response;
          notifier.notify(response.status_code, response.reason_phrase);
        });
        session.on('_failed', function (_ref4) {
          var message = _ref4.message,
              cause = _ref4.cause;

          if (message) {
            notifier.notify(message.status_code, message.reason_phrase);
          } else {
            notifier.notify(487, cause);
          }
        }); // Consider the Replaces header present in the Refer-To URI.

        if (request.refer_to.uri.hasHeader('replaces')) {
          var replaces = decodeURIComponent(request.refer_to.uri.getHeader('replaces'));
          options.extraHeaders = Utils.cloneArray(options.extraHeaders);
          options.extraHeaders.push("Replaces: ".concat(replaces));
        }

        session.connect(request.refer_to.uri.toAor(), options, initCallback);
      }

      function _reject() {
        notifier.notify(603);
      }
    }
    /**
     * In dialog Notify Reception
     */

  }, {
    key: "_receiveNotify",
    value: function _receiveNotify(request) {
      logger.debug('receiveNotify()');

      if (!request.event) {
        request.reply(400);
      }

      switch (request.event.event) {
        case 'refer':
          {
            var id;
            var referSubscriber;

            if (request.event.params && request.event.params.id) {
              id = request.event.params.id;
              referSubscriber = this._referSubscribers[id];
            } else if (Object.keys(this._referSubscribers).length === 1) {
              referSubscriber = this._referSubscribers[Object.keys(this._referSubscribers)[0]];
            } else {
              request.reply(400, 'Missing event id parameter');
              return;
            }

            if (!referSubscriber) {
              request.reply(481, 'Subscription does not exist');
              return;
            }

            referSubscriber.receiveNotify(request);
            request.reply(200);
            break;
          }

        default:
          {
            request.reply(489);
          }
      }
    }
    /**
     * INVITE with Replaces Reception
     */

  }, {
    key: "_receiveReplaces",
    value: function _receiveReplaces(request) {
      var _this20 = this;

      logger.debug('receiveReplaces()');

      function _accept2(initCallback) {
        var _this19 = this;

        if (this._status !== C.STATUS_WAITING_FOR_ACK && this._status !== C.STATUS_CONFIRMED) {
          return false;
        }

        var session = new RTCSession(this._ua); // Terminate the current session when the new one is confirmed.

        session.on('confirmed', function () {
          _this19.terminate();
        });
        session.init_incoming(request, initCallback);
      }

      function _reject2() {
        logger.debug('Replaced INVITE rejected by the user');
        request.reply(486);
      } // Emit 'replace'.


      this.emit('replaces', {
        request: request,
        accept: function accept(initCallback) {
          _accept2.call(_this20, initCallback);
        },
        reject: function reject() {
          _reject2.call(_this20);
        }
      });
    }
    /**
     * Initial Request Sender
     */

  }, {
    key: "_sendInitialRequest",
    value: function _sendInitialRequest(mediaConstraints, rtcOfferConstraints, mediaStream) {
      var _this21 = this;

      var request_sender = new RequestSender(this._ua, this._request, {
        onRequestTimeout: function onRequestTimeout() {
          _this21.onRequestTimeout();
        },
        onTransportError: function onTransportError() {
          _this21.onTransportError();
        },
        // Update the request on authentication.
        onAuthenticated: function onAuthenticated(request) {
          _this21._request = request;
        },
        onReceiveResponse: function onReceiveResponse(response) {
          _this21._receiveInviteResponse(response);
        }
      }); // This Promise is resolved within the next iteration, so the app has now
      // a chance to set events such as 'peerconnection' and 'connecting'.

      Promise.resolve() // Get a stream if required.
      .then(function () {
        // A stream is given, let the app set events such as 'peerconnection' and 'connecting'.
        if (mediaStream) {
          return mediaStream;
        } // Request for user media access.
        else if (mediaConstraints.audio || mediaConstraints.video) {
            _this21._localMediaStreamLocallyGenerated = true;
            return navigator.mediaDevices.getUserMedia(mediaConstraints)["catch"](function (error) {
              if (_this21._status === C.STATUS_TERMINATED) {
                throw new Error('terminated');
              }

              _this21._failed('local', null, JsSIP_C.causes.USER_DENIED_MEDIA_ACCESS);

              logger.warn('emit "getusermediafailed" [error:%o]', error);

              _this21.emit('getusermediafailed', error);

              throw error;
            });
          }
      }).then(function (stream) {
        if (_this21._status === C.STATUS_TERMINATED) {
          throw new Error('terminated');
        }

        _this21._localMediaStream = stream;

        if (stream) {
          stream.getTracks().forEach(function (track) {
            _this21._connection.addTrack(track, stream);
          });
        } // TODO: should this be triggered here?


        _this21._connecting(_this21._request);

        return _this21._createLocalDescription('offer', rtcOfferConstraints)["catch"](function (error) {
          _this21._failed('local', null, JsSIP_C.causes.WEBRTC_ERROR);

          throw error;
        });
      }).then(function (desc) {
        if (_this21._is_canceled || _this21._status === C.STATUS_TERMINATED) {
          throw new Error('terminated');
        }

        _this21._request.body = desc;
        _this21._status = C.STATUS_INVITE_SENT;
        logger.debug('emit "sending" [request:%o]', _this21._request); // Emit 'sending' so the app can mangle the body before the request is sent.

        _this21.emit('sending', {
          request: _this21._request
        });

        request_sender.send();
      })["catch"](function (error) {
        if (_this21._status === C.STATUS_TERMINATED) {
          return;
        }

        logger.warn(error);
      });
    }
    /**
     * Get DTMF RTCRtpSender.
     */

  }, {
    key: "_getDTMFRTPSender",
    value: function _getDTMFRTPSender() {
      var sender = this._connection.getSenders().find(function (rtpSender) {
        return rtpSender.track && rtpSender.track.kind === 'audio';
      });

      if (!(sender && sender.dtmf)) {
        logger.warn('sendDTMF() | no local audio track to send DTMF with');
        return;
      }

      return sender.dtmf;
    }
    /**
     * Reception of Response for Initial INVITE
     */

  }, {
    key: "_receiveInviteResponse",
    value: function _receiveInviteResponse(response) {
      var _this22 = this;

      logger.debug('receiveInviteResponse()'); // Handle 2XX retransmissions and responses from forked requests.

      if (this._dialog && response.status_code >= 200 && response.status_code <= 299) {
        /*
         * If it is a retransmission from the endpoint that established
         * the dialog, send an ACK
         */
        if (this._dialog.id.call_id === response.call_id && this._dialog.id.local_tag === response.from_tag && this._dialog.id.remote_tag === response.to_tag) {
          this.sendRequest(JsSIP_C.ACK);
          return;
        } // If not, send an ACK  and terminate.
        else {
            var dialog = new Dialog(this, response, 'UAC');

            if (dialog.error !== undefined) {
              logger.debug(dialog.error);
              return;
            }

            this.sendRequest(JsSIP_C.ACK);
            this.sendRequest(JsSIP_C.BYE);
            return;
          }
      } // Proceed to cancellation if the user requested.


      if (this._is_canceled) {
        if (response.status_code >= 100 && response.status_code < 200) {
          this._request.cancel(this._cancel_reason);
        } else if (response.status_code >= 200 && response.status_code < 299) {
          this._acceptAndTerminate(response);
        }

        return;
      }

      if (this._status !== C.STATUS_INVITE_SENT && this._status !== C.STATUS_1XX_RECEIVED) {
        return;
      }

      switch (true) {
        case /^100$/.test(response.status_code):
          this._status = C.STATUS_1XX_RECEIVED;
          break;

        case /^1[0-9]{2}$/.test(response.status_code):
          {
            // Do nothing with 1xx responses without To tag.
            if (!response.to_tag) {
              logger.debug('1xx response received without to tag');
              break;
            } // Create Early Dialog if 1XX comes with contact.


            if (response.hasHeader('contact')) {
              // An error on dialog creation will fire 'failed' event.
              if (!this._createDialog(response, 'UAC', true)) {
                break;
              }
            }

            this._status = C.STATUS_1XX_RECEIVED;

            if (!response.body) {
              this._progress('remote', response);

              break;
            }

            var e = {
              originator: 'remote',
              type: 'answer',
              sdp: response.body
            };
            logger.debug('emit "sdp"');
            this.emit('sdp', e);
            var answer = new RTCSessionDescription({
              type: 'answer',
              sdp: e.sdp
            });
            this._connectionPromiseQueue = this._connectionPromiseQueue.then(function () {
              return _this22._connection.setRemoteDescription(answer);
            }).then(function () {
              return _this22._progress('remote', response);
            })["catch"](function (error) {
              logger.warn('emit "peerconnection:setremotedescriptionfailed" [error:%o]', error);

              _this22.emit('peerconnection:setremotedescriptionfailed', error);
            });
            break;
          }

        case /^2[0-9]{2}$/.test(response.status_code):
          {
            this._status = C.STATUS_CONFIRMED;

            if (!response.body) {
              this._acceptAndTerminate(response, 400, JsSIP_C.causes.MISSING_SDP);

              this._failed('remote', response, JsSIP_C.causes.BAD_MEDIA_DESCRIPTION);

              break;
            } // An error on dialog creation will fire 'failed' event.


            if (!this._createDialog(response, 'UAC')) {
              break;
            }

            var _e = {
              originator: 'remote',
              type: 'answer',
              sdp: response.body
            };
            logger.debug('emit "sdp"');
            this.emit('sdp', _e);

            var _answer = new RTCSessionDescription({
              type: 'answer',
              sdp: _e.sdp
            });

            this._connectionPromiseQueue = this._connectionPromiseQueue.then(function () {
              // Be ready for 200 with SDP after a 180/183 with SDP.
              // We created a SDP 'answer' for it, so check the current signaling state.
              if (_this22._connection.signalingState === 'stable') {
                return _this22._connection.createOffer(_this22._rtcOfferConstraints).then(function (offer) {
                  return _this22._connection.setLocalDescription(offer);
                })["catch"](function (error) {
                  _this22._acceptAndTerminate(response, 500, error.toString());

                  _this22._failed('local', response, JsSIP_C.causes.WEBRTC_ERROR);
                });
              }
            }).then(function () {
              _this22._connection.setRemoteDescription(_answer).then(function () {
                // Handle Session Timers.
                _this22._handleSessionTimersInIncomingResponse(response);

                _this22._accepted('remote', response);

                _this22.sendRequest(JsSIP_C.ACK);

                _this22._confirmed('local', null);
              })["catch"](function (error) {
                _this22._acceptAndTerminate(response, 488, 'Not Acceptable Here');

                _this22._failed('remote', response, JsSIP_C.causes.BAD_MEDIA_DESCRIPTION);

                logger.warn('emit "peerconnection:setremotedescriptionfailed" [error:%o]', error);

                _this22.emit('peerconnection:setremotedescriptionfailed', error);
              });
            });
            break;
          }

        default:
          {
            var cause = Utils.sipErrorCause(response.status_code);

            this._failed('remote', response, cause);
          }
      }
    }
    /**
     * Send Re-INVITE
     */

  }, {
    key: "_sendReinvite",
    value: function _sendReinvite() {
      var _this23 = this;

      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      logger.debug('sendReinvite()');
      var extraHeaders = Utils.cloneArray(options.extraHeaders);
      var eventHandlers = Utils.cloneObject(options.eventHandlers);
      var rtcOfferConstraints = options.rtcOfferConstraints || this._rtcOfferConstraints || null;
      var succeeded = false;
      extraHeaders.push("Contact: ".concat(this._contact));
      extraHeaders.push('Content-Type: application/sdp'); // Session Timers.

      if (this._sessionTimers.running) {
        extraHeaders.push("Session-Expires: ".concat(this._sessionTimers.currentExpires, ";refresher=").concat(this._sessionTimers.refresher ? 'uac' : 'uas'));
      }

      this._connectionPromiseQueue = this._connectionPromiseQueue.then(function () {
        return _this23._createLocalDescription('offer', rtcOfferConstraints);
      }).then(function (sdp) {
        sdp = _this23._mangleOffer(sdp);
        var e = {
          originator: 'local',
          type: 'offer',
          sdp: sdp
        };
        logger.debug('emit "sdp"');

        _this23.emit('sdp', e);

        _this23.sendRequest(JsSIP_C.INVITE, {
          extraHeaders: extraHeaders,
          body: sdp,
          eventHandlers: {
            onSuccessResponse: function onSuccessResponse(response) {
              onSucceeded.call(_this23, response);
              succeeded = true;
            },
            onErrorResponse: function onErrorResponse(response) {
              onFailed.call(_this23, response);
            },
            onTransportError: function onTransportError() {
              _this23.onTransportError(); // Do nothing because session ends.

            },
            onRequestTimeout: function onRequestTimeout() {
              _this23.onRequestTimeout(); // Do nothing because session ends.

            },
            onDialogError: function onDialogError() {
              _this23.onDialogError(); // Do nothing because session ends.

            }
          }
        });
      })["catch"](function () {
        onFailed();
      });

      function onSucceeded(response) {
        var _this24 = this;

        if (this._status === C.STATUS_TERMINATED) {
          return;
        }

        this.sendRequest(JsSIP_C.ACK); // If it is a 2XX retransmission exit now.

        if (succeeded) {
          return;
        } // Handle Session Timers.


        this._handleSessionTimersInIncomingResponse(response); // Must have SDP answer.


        if (!response.body) {
          onFailed.call(this);
          return;
        } else if (!response.hasHeader('Content-Type') || response.getHeader('Content-Type').toLowerCase() !== 'application/sdp') {
          onFailed.call(this);
          return;
        }

        var e = {
          originator: 'remote',
          type: 'answer',
          sdp: response.body
        };
        logger.debug('emit "sdp"');
        this.emit('sdp', e);
        var answer = new RTCSessionDescription({
          type: 'answer',
          sdp: e.sdp
        });
        this._connectionPromiseQueue = this._connectionPromiseQueue.then(function () {
          return _this24._connection.setRemoteDescription(answer);
        }).then(function () {
          if (eventHandlers.succeeded) {
            eventHandlers.succeeded(response);
          }
        })["catch"](function (error) {
          onFailed.call(_this24);
          logger.warn('emit "peerconnection:setremotedescriptionfailed" [error:%o]', error);

          _this24.emit('peerconnection:setremotedescriptionfailed', error);
        });
      }

      function onFailed(response) {
        if (eventHandlers.failed) {
          eventHandlers.failed(response);
        }
      }
    }
    /**
     * Send UPDATE
     */

  }, {
    key: "_sendUpdate",
    value: function _sendUpdate() {
      var _this25 = this;

      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      logger.debug('sendUpdate()');
      var extraHeaders = Utils.cloneArray(options.extraHeaders);
      var eventHandlers = Utils.cloneObject(options.eventHandlers);
      var rtcOfferConstraints = options.rtcOfferConstraints || this._rtcOfferConstraints || null;
      var sdpOffer = options.sdpOffer || false;
      var succeeded = false;
      extraHeaders.push("Contact: ".concat(this._contact)); // Session Timers.

      if (this._sessionTimers.running) {
        extraHeaders.push("Session-Expires: ".concat(this._sessionTimers.currentExpires, ";refresher=").concat(this._sessionTimers.refresher ? 'uac' : 'uas'));
      }

      if (sdpOffer) {
        extraHeaders.push('Content-Type: application/sdp');
        this._connectionPromiseQueue = this._connectionPromiseQueue.then(function () {
          return _this25._createLocalDescription('offer', rtcOfferConstraints);
        }).then(function (sdp) {
          sdp = _this25._mangleOffer(sdp);
          var e = {
            originator: 'local',
            type: 'offer',
            sdp: sdp
          };
          logger.debug('emit "sdp"');

          _this25.emit('sdp', e);

          _this25.sendRequest(JsSIP_C.UPDATE, {
            extraHeaders: extraHeaders,
            body: sdp,
            eventHandlers: {
              onSuccessResponse: function onSuccessResponse(response) {
                onSucceeded.call(_this25, response);
                succeeded = true;
              },
              onErrorResponse: function onErrorResponse(response) {
                onFailed.call(_this25, response);
              },
              onTransportError: function onTransportError() {
                _this25.onTransportError(); // Do nothing because session ends.

              },
              onRequestTimeout: function onRequestTimeout() {
                _this25.onRequestTimeout(); // Do nothing because session ends.

              },
              onDialogError: function onDialogError() {
                _this25.onDialogError(); // Do nothing because session ends.

              }
            }
          });
        })["catch"](function () {
          onFailed.call(_this25);
        });
      } // No SDP.
      else {
          this.sendRequest(JsSIP_C.UPDATE, {
            extraHeaders: extraHeaders,
            eventHandlers: {
              onSuccessResponse: function onSuccessResponse(response) {
                onSucceeded.call(_this25, response);
              },
              onErrorResponse: function onErrorResponse(response) {
                onFailed.call(_this25, response);
              },
              onTransportError: function onTransportError() {
                _this25.onTransportError(); // Do nothing because session ends.

              },
              onRequestTimeout: function onRequestTimeout() {
                _this25.onRequestTimeout(); // Do nothing because session ends.

              },
              onDialogError: function onDialogError() {
                _this25.onDialogError(); // Do nothing because session ends.

              }
            }
          });
        }

      function onSucceeded(response) {
        var _this26 = this;

        if (this._status === C.STATUS_TERMINATED) {
          return;
        } // If it is a 2XX retransmission exit now.


        if (succeeded) {
          return;
        } // Handle Session Timers.


        this._handleSessionTimersInIncomingResponse(response); // Must have SDP answer.


        if (sdpOffer) {
          if (!response.body) {
            onFailed.call(this);
            return;
          } else if (!response.hasHeader('Content-Type') || response.getHeader('Content-Type').toLowerCase() !== 'application/sdp') {
            onFailed.call(this);
            return;
          }

          var e = {
            originator: 'remote',
            type: 'answer',
            sdp: response.body
          };
          logger.debug('emit "sdp"');
          this.emit('sdp', e);
          var answer = new RTCSessionDescription({
            type: 'answer',
            sdp: e.sdp
          });
          this._connectionPromiseQueue = this._connectionPromiseQueue.then(function () {
            return _this26._connection.setRemoteDescription(answer);
          }).then(function () {
            if (eventHandlers.succeeded) {
              eventHandlers.succeeded(response);
            }
          })["catch"](function (error) {
            onFailed.call(_this26);
            logger.warn('emit "peerconnection:setremotedescriptionfailed" [error:%o]', error);

            _this26.emit('peerconnection:setremotedescriptionfailed', error);
          });
        } // No SDP answer.
        else if (eventHandlers.succeeded) {
            eventHandlers.succeeded(response);
          }
      }

      function onFailed(response) {
        if (eventHandlers.failed) {
          eventHandlers.failed(response);
        }
      }
    }
  }, {
    key: "_acceptAndTerminate",
    value: function _acceptAndTerminate(response, status_code, reason_phrase) {
      logger.debug('acceptAndTerminate()');
      var extraHeaders = [];

      if (status_code) {
        reason_phrase = reason_phrase || JsSIP_C.REASON_PHRASE[status_code] || '';
        extraHeaders.push("Reason: SIP ;cause=".concat(status_code, "; text=\"").concat(reason_phrase, "\""));
      } // An error on dialog creation will fire 'failed' event.


      if (this._dialog || this._createDialog(response, 'UAC')) {
        this.sendRequest(JsSIP_C.ACK);
        this.sendRequest(JsSIP_C.BYE, {
          extraHeaders: extraHeaders
        });
      } // Update session status.


      this._status = C.STATUS_TERMINATED;
    }
    /**
     * Correctly set the SDP direction attributes if the call is on local hold
     */

  }, {
    key: "_mangleOffer",
    value: function _mangleOffer(sdp) {
      if (!this._localHold && !this._remoteHold) {
        return sdp;
      }

      sdp = sdp_transform.parse(sdp); // Local hold.

      if (this._localHold && !this._remoteHold) {
        logger.debug('mangleOffer() | me on hold, mangling offer');

        var _iterator5 = _createForOfIteratorHelper(sdp.media),
            _step5;

        try {
          for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
            var m = _step5.value;

            if (holdMediaTypes.indexOf(m.type) === -1) {
              continue;
            }

            if (!m.direction) {
              m.direction = 'sendonly';
            } else if (m.direction === 'sendrecv') {
              m.direction = 'sendonly';
            } else if (m.direction === 'recvonly') {
              m.direction = 'inactive';
            }
          }
        } catch (err) {
          _iterator5.e(err);
        } finally {
          _iterator5.f();
        }
      } // Local and remote hold.
      else if (this._localHold && this._remoteHold) {
          logger.debug('mangleOffer() | both on hold, mangling offer');

          var _iterator6 = _createForOfIteratorHelper(sdp.media),
              _step6;

          try {
            for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
              var _m = _step6.value;

              if (holdMediaTypes.indexOf(_m.type) === -1) {
                continue;
              }

              _m.direction = 'inactive';
            }
          } catch (err) {
            _iterator6.e(err);
          } finally {
            _iterator6.f();
          }
        } // Remote hold.
        else if (this._remoteHold) {
            logger.debug('mangleOffer() | remote on hold, mangling offer');

            var _iterator7 = _createForOfIteratorHelper(sdp.media),
                _step7;

            try {
              for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
                var _m2 = _step7.value;

                if (holdMediaTypes.indexOf(_m2.type) === -1) {
                  continue;
                }

                if (!_m2.direction) {
                  _m2.direction = 'recvonly';
                } else if (_m2.direction === 'sendrecv') {
                  _m2.direction = 'recvonly';
                } else if (_m2.direction === 'recvonly') {
                  _m2.direction = 'inactive';
                }
              }
            } catch (err) {
              _iterator7.e(err);
            } finally {
              _iterator7.f();
            }
          }

      return sdp_transform.write(sdp);
    }
  }, {
    key: "_setLocalMediaStatus",
    value: function _setLocalMediaStatus() {
      var enableAudio = true,
          enableVideo = true;

      if (this._localHold || this._remoteHold) {
        enableAudio = false;
        enableVideo = false;
      }

      if (this._audioMuted) {
        enableAudio = false;
      }

      if (this._videoMuted) {
        enableVideo = false;
      }

      this._toggleMuteAudio(!enableAudio);

      this._toggleMuteVideo(!enableVideo);
    }
    /**
     * Handle SessionTimers for an incoming INVITE or UPDATE.
     * @param  {IncomingRequest} request
     * @param  {Array} responseExtraHeaders  Extra headers for the 200 response.
     */

  }, {
    key: "_handleSessionTimersInIncomingRequest",
    value: function _handleSessionTimersInIncomingRequest(request, responseExtraHeaders) {
      if (!this._sessionTimers.enabled) {
        return;
      }

      var session_expires_refresher;

      if (request.session_expires && request.session_expires >= JsSIP_C.MIN_SESSION_EXPIRES) {
        this._sessionTimers.currentExpires = request.session_expires;
        session_expires_refresher = request.session_expires_refresher || 'uas';
      } else {
        this._sessionTimers.currentExpires = this._sessionTimers.defaultExpires;
        session_expires_refresher = 'uas';
      }

      responseExtraHeaders.push("Session-Expires: ".concat(this._sessionTimers.currentExpires, ";refresher=").concat(session_expires_refresher));
      this._sessionTimers.refresher = session_expires_refresher === 'uas';

      this._runSessionTimer();
    }
    /**
     * Handle SessionTimers for an incoming response to INVITE or UPDATE.
     * @param  {IncomingResponse} response
     */

  }, {
    key: "_handleSessionTimersInIncomingResponse",
    value: function _handleSessionTimersInIncomingResponse(response) {
      if (!this._sessionTimers.enabled) {
        return;
      }

      var session_expires_refresher;

      if (response.session_expires && response.session_expires >= JsSIP_C.MIN_SESSION_EXPIRES) {
        this._sessionTimers.currentExpires = response.session_expires;
        session_expires_refresher = response.session_expires_refresher || 'uac';
      } else {
        this._sessionTimers.currentExpires = this._sessionTimers.defaultExpires;
        session_expires_refresher = 'uac';
      }

      this._sessionTimers.refresher = session_expires_refresher === 'uac';

      this._runSessionTimer();
    }
  }, {
    key: "_runSessionTimer",
    value: function _runSessionTimer() {
      var _this27 = this;

      var expires = this._sessionTimers.currentExpires;
      this._sessionTimers.running = true;
      clearTimeout(this._sessionTimers.timer); // I'm the refresher.

      if (this._sessionTimers.refresher) {
        this._sessionTimers.timer = setTimeout(function () {
          if (_this27._status === C.STATUS_TERMINATED) {
            return;
          }

          if (!_this27._isReadyToReOffer()) {
            return;
          }

          logger.debug('runSessionTimer() | sending session refresh request');

          if (_this27._sessionTimers.refreshMethod === JsSIP_C.UPDATE) {
            _this27._sendUpdate();
          } else {
            _this27._sendReinvite();
          }
        }, expires * 500); // Half the given interval (as the RFC states).
      } // I'm not the refresher.
      else {
          this._sessionTimers.timer = setTimeout(function () {
            if (_this27._status === C.STATUS_TERMINATED) {
              return;
            }

            logger.warn('runSessionTimer() | timer expired, terminating the session');

            _this27.terminate({
              cause: JsSIP_C.causes.REQUEST_TIMEOUT,
              status_code: 408,
              reason_phrase: 'Session Timer Expired'
            });
          }, expires * 1100);
        }
    }
  }, {
    key: "_toggleMuteAudio",
    value: function _toggleMuteAudio(mute) {
      var senders = this._connection.getSenders().filter(function (sender) {
        return sender.track && sender.track.kind === 'audio';
      });

      var _iterator8 = _createForOfIteratorHelper(senders),
          _step8;

      try {
        for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
          var sender = _step8.value;
          sender.track.enabled = !mute;
        }
      } catch (err) {
        _iterator8.e(err);
      } finally {
        _iterator8.f();
      }
    }
  }, {
    key: "_toggleMuteVideo",
    value: function _toggleMuteVideo(mute) {
      var senders = this._connection.getSenders().filter(function (sender) {
        return sender.track && sender.track.kind === 'video';
      });

      var _iterator9 = _createForOfIteratorHelper(senders),
          _step9;

      try {
        for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
          var sender = _step9.value;
          sender.track.enabled = !mute;
        }
      } catch (err) {
        _iterator9.e(err);
      } finally {
        _iterator9.f();
      }
    }
  }, {
    key: "_newRTCSession",
    value: function _newRTCSession(originator, request) {
      logger.debug('newRTCSession()');

      this._ua.newRTCSession(this, {
        originator: originator,
        session: this,
        request: request
      });
    }
  }, {
    key: "_connecting",
    value: function _connecting(request) {
      logger.debug('session connecting');
      logger.debug('emit "connecting"');
      this.emit('connecting', {
        request: request
      });
    }
  }, {
    key: "_progress",
    value: function _progress(originator, response) {
      logger.debug('session progress');
      logger.debug('emit "progress"');
      this.emit('progress', {
        originator: originator,
        response: response || null
      });
    }
  }, {
    key: "_accepted",
    value: function _accepted(originator, message) {
      logger.debug('session accepted');
      this._start_time = new Date();
      logger.debug('emit "accepted"');
      this.emit('accepted', {
        originator: originator,
        response: message || null
      });
    }
  }, {
    key: "_confirmed",
    value: function _confirmed(originator, ack) {
      logger.debug('session confirmed');
      this._is_confirmed = true;
      logger.debug('emit "confirmed"');
      this.emit('confirmed', {
        originator: originator,
        ack: ack || null
      });
    }
  }, {
    key: "_ended",
    value: function _ended(originator, message, cause) {
      logger.debug('session ended');
      this._end_time = new Date();

      this._close();

      logger.debug('emit "ended"');
      this.emit('ended', {
        originator: originator,
        message: message || null,
        cause: cause
      });
    }
  }, {
    key: "_failed",
    value: function _failed(originator, message, cause) {
      logger.debug('session failed'); // Emit private '_failed' event first.

      logger.debug('emit "_failed"');
      this.emit('_failed', {
        originator: originator,
        message: message || null,
        cause: cause
      });

      this._close();

      logger.debug('emit "failed"');
      this.emit('failed', {
        originator: originator,
        message: message || null,
        cause: cause
      });
    }
  }, {
    key: "_onhold",
    value: function _onhold(originator) {
      logger.debug('session onhold');

      this._setLocalMediaStatus();

      logger.debug('emit "hold"');
      this.emit('hold', {
        originator: originator
      });
    }
  }, {
    key: "_onunhold",
    value: function _onunhold(originator) {
      logger.debug('session onunhold');

      this._setLocalMediaStatus();

      logger.debug('emit "unhold"');
      this.emit('unhold', {
        originator: originator
      });
    }
  }, {
    key: "_onmute",
    value: function _onmute(_ref5) {
      var audio = _ref5.audio,
          video = _ref5.video;
      logger.debug('session onmute');

      this._setLocalMediaStatus();

      logger.debug('emit "muted"');
      this.emit('muted', {
        audio: audio,
        video: video
      });
    }
  }, {
    key: "_onunmute",
    value: function _onunmute(_ref6) {
      var audio = _ref6.audio,
          video = _ref6.video;
      logger.debug('session onunmute');

      this._setLocalMediaStatus();

      logger.debug('emit "unmuted"');
      this.emit('unmuted', {
        audio: audio,
        video: video
      });
    }
  }, {
    key: "C",
    get: function get() {
      return C;
    } // Expose session failed/ended causes as a property of the RTCSession instance.

  }, {
    key: "causes",
    get: function get() {
      return JsSIP_C.causes;
    }
  }, {
    key: "id",
    get: function get() {
      return this._id;
    }
  }, {
    key: "connection",
    get: function get() {
      return this._connection;
    }
  }, {
    key: "contact",
    get: function get() {
      return this._contact;
    }
  }, {
    key: "direction",
    get: function get() {
      return this._direction;
    }
  }, {
    key: "local_identity",
    get: function get() {
      return this._local_identity;
    }
  }, {
    key: "remote_identity",
    get: function get() {
      return this._remote_identity;
    }
  }, {
    key: "start_time",
    get: function get() {
      return this._start_time;
    }
  }, {
    key: "end_time",
    get: function get() {
      return this._end_time;
    }
  }, {
    key: "data",
    get: function get() {
      return this._data;
    },
    set: function set(_data) {
      this._data = _data;
    }
  }, {
    key: "status",
    get: function get() {
      return this._status;
    }
  }]);

  return RTCSession;
}(EventEmitter);
},{"./Constants":2,"./Dialog":3,"./Exceptions":6,"./Logger":9,"./RTCSession/DTMF":15,"./RTCSession/Info":16,"./RTCSession/ReferNotifier":17,"./RTCSession/ReferSubscriber":18,"./RequestSender":20,"./SIPMessage":21,"./Timers":23,"./Transactions":24,"./URI":27,"./Utils":28,"events":31,"sdp-transform":37}],15:[function(require,module,exports){
"use strict";

function _typeof(obj) { "@babel/helpers - typeof"; 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); }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }

function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }

function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

var EventEmitter = require('events').EventEmitter;

var Logger = require('../Logger');

var JsSIP_C = require('../Constants');

var Exceptions = require('../Exceptions');

var Utils = require('../Utils');

var logger = new Logger('RTCSession:DTMF');
var C = {
  MIN_DURATION: 70,
  MAX_DURATION: 6000,
  DEFAULT_DURATION: 100,
  MIN_INTER_TONE_GAP: 50,
  DEFAULT_INTER_TONE_GAP: 500
};

module.exports = /*#__PURE__*/function (_EventEmitter) {
  _inherits(DTMF, _EventEmitter);

  var _super = _createSuper(DTMF);

  function DTMF(session) {
    var _this;

    _classCallCheck(this, DTMF);

    _this = _super.call(this);
    _this._session = session;
    _this._direction = null;
    _this._tone = null;
    _this._duration = null;
    _this._request = null;
    return _this;
  }

  _createClass(DTMF, [{
    key: "send",
    value: function send(tone) {
      var _this2 = this;

      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

      if (tone === undefined) {
        throw new TypeError('Not enough arguments');
      }

      this._direction = 'outgoing'; // Check RTCSession Status.

      if (this._session.status !== this._session.C.STATUS_CONFIRMED && this._session.status !== this._session.C.STATUS_WAITING_FOR_ACK) {
        throw new Exceptions.InvalidStateError(this._session.status);
      }

      var extraHeaders = Utils.cloneArray(options.extraHeaders);
      this.eventHandlers = Utils.cloneObject(options.eventHandlers); // Check tone type.

      if (typeof tone === 'string') {
        tone = tone.toUpperCase();
      } else if (typeof tone === 'number') {
        tone = tone.toString();
      } else {
        throw new TypeError("Invalid tone: ".concat(tone));
      } // Check tone value.


      if (!tone.match(/^[0-9A-DR#*]$/)) {
        throw new TypeError("Invalid tone: ".concat(tone));
      } else {
        this._tone = tone;
      } // Duration is checked/corrected in RTCSession.


      this._duration = options.duration;
      extraHeaders.push('Content-Type: application/dtmf-relay');
      var body = "Signal=".concat(this._tone, "\r\n");
      body += "Duration=".concat(this._duration);

      this._session.newDTMF({
        originator: 'local',
        dtmf: this,
        request: this._request
      });

      this._session.sendRequest(JsSIP_C.INFO, {
        extraHeaders: extraHeaders,
        eventHandlers: {
          onSuccessResponse: function onSuccessResponse(response) {
            _this2.emit('succeeded', {
              originator: 'remote',
              response: response
            });
          },
          onErrorResponse: function onErrorResponse(response) {
            if (_this2.eventHandlers.onFailed) {
              _this2.eventHandlers.onFailed();
            }

            _this2.emit('failed', {
              originator: 'remote',
              response: response
            });
          },
          onRequestTimeout: function onRequestTimeout() {
            _this2._session.onRequestTimeout();
          },
          onTransportError: function onTransportError() {
            _this2._session.onTransportError();
          },
          onDialogError: function onDialogError() {
            _this2._session.onDialogError();
          }
        },
        body: body
      });
    }
  }, {
    key: "init_incoming",
    value: function init_incoming(request) {
      var reg_tone = /^(Signal\s*?=\s*?)([0-9A-D#*]{1})(\s)?.*/;
      var reg_duration = /^(Duration\s?=\s?)([0-9]{1,4})(\s)?.*/;
      this._direction = 'incoming';
      this._request = request;
      request.reply(200);

      if (request.body) {
        var body = request.body.split('\n');

        if (body.length >= 1) {
          if (reg_tone.test(body[0])) {
            this._tone = body[0].replace(reg_tone, '$2');
          }
        }

        if (body.length >= 2) {
          if (reg_duration.test(body[1])) {
            this._duration = parseInt(body[1].replace(reg_duration, '$2'), 10);
          }
        }
      }

      if (!this._duration) {
        this._duration = C.DEFAULT_DURATION;
      }

      if (!this._tone) {
        logger.debug('invalid INFO DTMF received, discarded');
      } else {
        this._session.newDTMF({
          originator: 'remote',
          dtmf: this,
          request: request
        });
      }
    }
  }, {
    key: "tone",
    get: function get() {
      return this._tone;
    }
  }, {
    key: "duration",
    get: function get() {
      return this._duration;
    }
  }]);

  return DTMF;
}(EventEmitter);
/**
 * Expose C object.
 */


module.exports.C = C;
},{"../Constants":2,"../Exceptions":6,"../Logger":9,"../Utils":28,"events":31}],16:[function(require,module,exports){
"use strict";

function _typeof(obj) { "@babel/helpers - typeof"; 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); }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }

function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }

function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

var EventEmitter = require('events').EventEmitter;

var JsSIP_C = require('../Constants');

var Exceptions = require('../Exceptions');

var Utils = require('../Utils');

module.exports = /*#__PURE__*/function (_EventEmitter) {
  _inherits(Info, _EventEmitter);

  var _super = _createSuper(Info);

  function Info(session) {
    var _this;

    _classCallCheck(this, Info);

    _this = _super.call(this);
    _this._session = session;
    _this._direction = null;
    _this._contentType = null;
    _this._body = null;
    return _this;
  }

  _createClass(Info, [{
    key: "send",
    value: function send(contentType, body) {
      var _this2 = this;

      var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
      this._direction = 'outgoing';

      if (contentType === undefined) {
        throw new TypeError('Not enough arguments');
      } // Check RTCSession Status.


      if (this._session.status !== this._session.C.STATUS_CONFIRMED && this._session.status !== this._session.C.STATUS_WAITING_FOR_ACK) {
        throw new Exceptions.InvalidStateError(this._session.status);
      }

      this._contentType = contentType;
      this._body = body;
      var extraHeaders = Utils.cloneArray(options.extraHeaders);
      extraHeaders.push("Content-Type: ".concat(contentType));

      this._session.newInfo({
        originator: 'local',
        info: this,
        request: this.request
      });

      this._session.sendRequest(JsSIP_C.INFO, {
        extraHeaders: extraHeaders,
        eventHandlers: {
          onSuccessResponse: function onSuccessResponse(response) {
            _this2.emit('succeeded', {
              originator: 'remote',
              response: response
            });
          },
          onErrorResponse: function onErrorResponse(response) {
            _this2.emit('failed', {
              originator: 'remote',
              response: response
            });
          },
          onTransportError: function onTransportError() {
            _this2._session.onTransportError();
          },
          onRequestTimeout: function onRequestTimeout() {
            _this2._session.onRequestTimeout();
          },
          onDialogError: function onDialogError() {
            _this2._session.onDialogError();
          }
        },
        body: body
      });
    }
  }, {
    key: "init_incoming",
    value: function init_incoming(request) {
      this._direction = 'incoming';
      this.request = request;
      request.reply(200);
      this._contentType = request.hasHeader('Content-Type') ? request.getHeader('Content-Type').toLowerCase() : undefined;
      this._body = request.body;

      this._session.newInfo({
        originator: 'remote',
        info: this,
        request: request
      });
    }
  }, {
    key: "contentType",
    get: function get() {
      return this._contentType;
    }
  }, {
    key: "body",
    get: function get() {
      return this._body;
    }
  }]);

  return Info;
}(EventEmitter);
},{"../Constants":2,"../Exceptions":6,"../Utils":28,"events":31}],17:[function(require,module,exports){
"use strict";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var Logger = require('../Logger');

var JsSIP_C = require('../Constants');

var logger = new Logger('RTCSession:ReferNotifier');
var C = {
  event_type: 'refer',
  body_type: 'message/sipfrag;version=2.0',
  expires: 300
};

module.exports = /*#__PURE__*/function () {
  function ReferNotifier(session, id, expires) {
    _classCallCheck(this, ReferNotifier);

    this._session = session;
    this._id = id;
    this._expires = expires || C.expires;
    this._active = true; // The creation of a Notifier results in an immediate NOTIFY.

    this.notify(100);
  }

  _createClass(ReferNotifier, [{
    key: "notify",
    value: function notify(code, reason) {
      logger.debug('notify()');

      if (this._active === false) {
        return;
      }

      reason = reason || JsSIP_C.REASON_PHRASE[code] || '';
      var state;

      if (code >= 200) {
        state = 'terminated;reason=noresource';
      } else {
        state = "active;expires=".concat(this._expires);
      } // Put this in a try/catch block.


      this._session.sendRequest(JsSIP_C.NOTIFY, {
        extraHeaders: ["Event: ".concat(C.event_type, ";id=").concat(this._id), "Subscription-State: ".concat(state), "Content-Type: ".concat(C.body_type)],
        body: "SIP/2.0 ".concat(code, " ").concat(reason),
        eventHandlers: {
          // If a negative response is received, subscription is canceled.
          onErrorResponse: function onErrorResponse() {
            this._active = false;
          }
        }
      });
    }
  }]);

  return ReferNotifier;
}();
},{"../Constants":2,"../Logger":9}],18:[function(require,module,exports){
"use strict";

function _typeof(obj) { "@babel/helpers - typeof"; 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); }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }

function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }

function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

var EventEmitter = require('events').EventEmitter;

var Logger = require('../Logger');

var JsSIP_C = require('../Constants');

var Grammar = require('../Grammar');

var Utils = require('../Utils');

var logger = new Logger('RTCSession:ReferSubscriber');

module.exports = /*#__PURE__*/function (_EventEmitter) {
  _inherits(ReferSubscriber, _EventEmitter);

  var _super = _createSuper(ReferSubscriber);

  function ReferSubscriber(session) {
    var _this;

    _classCallCheck(this, ReferSubscriber);

    _this = _super.call(this);
    _this._id = null;
    _this._session = session;
    return _this;
  }

  _createClass(ReferSubscriber, [{
    key: "sendRefer",
    value: function sendRefer(target) {
      var _this2 = this;

      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      logger.debug('sendRefer()');
      var extraHeaders = Utils.cloneArray(options.extraHeaders);
      var eventHandlers = Utils.cloneObject(options.eventHandlers); // Set event handlers.

      for (var event in eventHandlers) {
        if (Object.prototype.hasOwnProperty.call(eventHandlers, event)) {
          this.on(event, eventHandlers[event]);
        }
      } // Replaces URI header field.


      var replaces = null;

      if (options.replaces) {
        replaces = options.replaces._request.call_id;
        replaces += ";to-tag=".concat(options.replaces._to_tag);
        replaces += ";from-tag=".concat(options.replaces._from_tag);
        replaces = encodeURIComponent(replaces);
      } // Refer-To header field.


      var referTo = "Refer-To: <".concat(target).concat(replaces ? "?Replaces=".concat(replaces) : '', ">");
      extraHeaders.push(referTo); // Referred-By header field (if not already present).

      if (!extraHeaders.some(function (header) {
        return header.toLowerCase().startsWith('referred-by:');
      })) {
        var referredBy = "Referred-By: <".concat(this._session._ua._configuration.uri._scheme, ":").concat(this._session._ua._configuration.uri._user, "@").concat(this._session._ua._configuration.uri._host, ">");
        extraHeaders.push(referredBy);
      }

      extraHeaders.push("Contact: ".concat(this._session.contact));

      var request = this._session.sendRequest(JsSIP_C.REFER, {
        extraHeaders: extraHeaders,
        eventHandlers: {
          onSuccessResponse: function onSuccessResponse(response) {
            _this2._requestSucceeded(response);
          },
          onErrorResponse: function onErrorResponse(response) {
            _this2._requestFailed(response, JsSIP_C.causes.REJECTED);
          },
          onTransportError: function onTransportError() {
            _this2._requestFailed(null, JsSIP_C.causes.CONNECTION_ERROR);
          },
          onRequestTimeout: function onRequestTimeout() {
            _this2._requestFailed(null, JsSIP_C.causes.REQUEST_TIMEOUT);
          },
          onDialogError: function onDialogError() {
            _this2._requestFailed(null, JsSIP_C.causes.DIALOG_ERROR);
          }
        }
      });

      this._id = request.cseq;
    }
  }, {
    key: "receiveNotify",
    value: function receiveNotify(request) {
      logger.debug('receiveNotify()');

      if (!request.body) {
        return;
      }

      var status_line = Grammar.parse(request.body.trim(), 'Status_Line');

      if (status_line === -1) {
        logger.debug("receiveNotify() | error parsing NOTIFY body: \"".concat(request.body, "\""));
        return;
      }

      switch (true) {
        case /^100$/.test(status_line.status_code):
          this.emit('trying', {
            request: request,
            status_line: status_line
          });
          break;

        case /^1[0-9]{2}$/.test(status_line.status_code):
          this.emit('progress', {
            request: request,
            status_line: status_line
          });
          break;

        case /^2[0-9]{2}$/.test(status_line.status_code):
          this.emit('accepted', {
            request: request,
            status_line: status_line
          });
          break;

        default:
          this.emit('failed', {
            request: request,
            status_line: status_line
          });
          break;
      }
    }
  }, {
    key: "_requestSucceeded",
    value: function _requestSucceeded(response) {
      logger.debug('REFER succeeded');
      logger.debug('emit "requestSucceeded"');
      this.emit('requestSucceeded', {
        response: response
      });
    }
  }, {
    key: "_requestFailed",
    value: function _requestFailed(response, cause) {
      logger.debug('REFER failed');
      logger.debug('emit "requestFailed"');
      this.emit('requestFailed', {
        response: response || null,
        cause: cause
      });
    }
  }, {
    key: "id",
    get: function get() {
      return this._id;
    }
  }]);

  return ReferSubscriber;
}(EventEmitter);
},{"../Constants":2,"../Grammar":7,"../Logger":9,"../Utils":28,"events":31}],19:[function(require,module,exports){
"use strict";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var Logger = require('./Logger');

var Utils = require('./Utils');

var JsSIP_C = require('./Constants');

var SIPMessage = require('./SIPMessage');

var RequestSender = require('./RequestSender');

var logger = new Logger('Registrator');
var MIN_REGISTER_EXPIRES = 10; // In seconds.

module.exports = /*#__PURE__*/function () {
  function Registrator(ua, transport) {
    _classCallCheck(this, Registrator);

    // Force reg_id to 1.
    this._reg_id = 1;
    this._ua = ua;
    this._transport = transport;
    this._registrar = ua.configuration.registrar_server;
    this._expires = ua.configuration.register_expires; // Call-ID and CSeq values RFC3261 10.2.

    this._call_id = Utils.createRandomToken(22);
    this._cseq = 0;
    this._to_uri = ua.configuration.uri;
    this._registrationTimer = null; // Ongoing Register request.

    this._registering = false; // Set status.

    this._registered = false; // Contact header.

    this._contact = this._ua.contact.toString(); // Sip.ice media feature tag (RFC 5768).

    this._contact += ';+sip.ice'; // Custom headers for REGISTER and un-REGISTER.

    this._extraHeaders = []; // Custom Contact header params for REGISTER and un-REGISTER.

    this._extraContactParams = ''; // Contents of the sip.instance Contact header parameter.

    this._sipInstance = "\"<urn:uuid:".concat(this._ua.configuration.instance_id, ">\"");
    this._contact += ";reg-id=".concat(this._reg_id);
    this._contact += ";+sip.instance=".concat(this._sipInstance);
  }

  _createClass(Registrator, [{
    key: "setExtraHeaders",
    value: function setExtraHeaders(extraHeaders) {
      if (!Array.isArray(extraHeaders)) {
        extraHeaders = [];
      }

      this._extraHeaders = extraHeaders.slice();
    }
  }, {
    key: "setExtraContactParams",
    value: function setExtraContactParams(extraContactParams) {
      if (!(extraContactParams instanceof Object)) {
        extraContactParams = {};
      } // Reset it.


      this._extraContactParams = '';

      for (var param_key in extraContactParams) {
        if (Object.prototype.hasOwnProperty.call(extraContactParams, param_key)) {
          var param_value = extraContactParams[param_key];
          this._extraContactParams += ";".concat(param_key);

          if (param_value) {
            this._extraContactParams += "=".concat(param_value);
          }
        }
      }
    }
  }, {
    key: "register",
    value: function register() {
      var _this = this;

      if (this._registering) {
        logger.debug('Register request in progress...');
        return;
      }

      var extraHeaders = this._extraHeaders.slice();

      extraHeaders.push("Contact: ".concat(this._contact, ";expires=").concat(this._expires).concat(this._extraContactParams));
      extraHeaders.push("Expires: ".concat(this._expires));
      var request = new SIPMessage.OutgoingRequest(JsSIP_C.REGISTER, this._registrar, this._ua, {
        'to_uri': this._to_uri,
        'call_id': this._call_id,
        'cseq': this._cseq += 1
      }, extraHeaders);
      var request_sender = new RequestSender(this._ua, request, {
        onRequestTimeout: function onRequestTimeout() {
          _this._registrationFailure(null, JsSIP_C.causes.REQUEST_TIMEOUT);
        },
        onTransportError: function onTransportError() {
          _this._registrationFailure(null, JsSIP_C.causes.CONNECTION_ERROR);
        },
        // Increase the CSeq on authentication.
        onAuthenticated: function onAuthenticated() {
          _this._cseq += 1;
        },
        onReceiveResponse: function onReceiveResponse(response) {
          // Discard responses to older REGISTER/un-REGISTER requests.
          if (response.cseq !== _this._cseq) {
            return;
          } // Clear registration timer.


          if (_this._registrationTimer !== null) {
            clearTimeout(_this._registrationTimer);
            _this._registrationTimer = null;
          }

          switch (true) {
            case /^1[0-9]{2}$/.test(response.status_code):
              {
                // Ignore provisional responses.
                break;
              }

            case /^2[0-9]{2}$/.test(response.status_code):
              {
                _this._registering = false;

                if (!response.hasHeader('Contact')) {
                  logger.debug('no Contact header in response to REGISTER, response ignored');
                  break;
                }

                var contacts = response.headers['Contact'].reduce(function (a, b) {
                  return a.concat(b.parsed);
                }, []); // Get the Contact pointing to us and update the expires value accordingly.
                // Try to find a matching Contact using sip.instance and reg-id.

                var contact = contacts.find(function (element) {
                  return _this._sipInstance === element.getParam('+sip.instance') && _this._reg_id === parseInt(element.getParam('reg-id'));
                }); // If no match was found using the sip.instance try comparing the URIs.

                if (!contact) {
                  contact = contacts.find(function (element) {
                    return element.uri.user === _this._ua.contact.uri.user;
                  });
                }

                if (!contact) {
                  logger.debug('no Contact header pointing to us, response ignored');
                  break;
                }

                var expires = contact.getParam('expires');

                if (!expires && response.hasHeader('expires')) {
                  expires = response.getHeader('expires');
                }

                if (!expires) {
                  expires = _this._expires;
                }

                expires = Number(expires);
                if (expires < MIN_REGISTER_EXPIRES) expires = MIN_REGISTER_EXPIRES;
                var timeout = expires > 64 ? expires * 1000 / 2 + Math.floor((expires / 2 - 32) * 1000 * Math.random()) : expires * 1000 - 5000; // Re-Register or emit an event before the expiration interval has elapsed.
                // For that, decrease the expires value. ie: 3 seconds.

                _this._registrationTimer = setTimeout(function () {
                  _this._registrationTimer = null; // If there are no listeners for registrationExpiring, renew registration.
                  // If there are listeners, let the function listening do the register call.

                  if (_this._ua.listeners('registrationExpiring').length === 0) {
                    _this.register();
                  } else {
                    _this._ua.emit('registrationExpiring');
                  }
                }, timeout); // Save gruu values.

                if (contact.hasParam('temp-gruu')) {
                  _this._ua.contact.temp_gruu = contact.getParam('temp-gruu').replace(/"/g, '');
                }

                if (contact.hasParam('pub-gruu')) {
                  _this._ua.contact.pub_gruu = contact.getParam('pub-gruu').replace(/"/g, '');
                }

                if (!_this._registered) {
                  _this._registered = true;

                  _this._ua.registered({
                    response: response
                  });
                }

                break;
              }
            // Interval too brief RFC3261 10.2.8.

            case /^423$/.test(response.status_code):
              {
                if (response.hasHeader('min-expires')) {
                  // Increase our registration interval to the suggested minimum.
                  _this._expires = Number(response.getHeader('min-expires'));
                  if (_this._expires < MIN_REGISTER_EXPIRES) _this._expires = MIN_REGISTER_EXPIRES; // Attempt the registration again immediately.

                  _this.register();
                } else {
                  // This response MUST contain a Min-Expires header field.
                  logger.debug('423 response received for REGISTER without Min-Expires');

                  _this._registrationFailure(response, JsSIP_C.causes.SIP_FAILURE_CODE);
                }

                break;
              }

            default:
              {
                var cause = Utils.sipErrorCause(response.status_code);

                _this._registrationFailure(response, cause);
              }
          }
        }
      });
      this._registering = true;
      request_sender.send();
    }
  }, {
    key: "unregister",
    value: function unregister() {
      var _this2 = this;

      var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

      if (!this._registered) {
        logger.debug('already unregistered');
        return;
      }

      this._registered = false; // Clear the registration timer.

      if (this._registrationTimer !== null) {
        clearTimeout(this._registrationTimer);
        this._registrationTimer = null;
      }

      var extraHeaders = this._extraHeaders.slice();

      if (options.all) {
        extraHeaders.push("Contact: *".concat(this._extraContactParams));
      } else {
        extraHeaders.push("Contact: ".concat(this._contact, ";expires=0").concat(this._extraContactParams));
      }

      extraHeaders.push('Expires: 0');
      var request = new SIPMessage.OutgoingRequest(JsSIP_C.REGISTER, this._registrar, this._ua, {
        'to_uri': this._to_uri,
        'call_id': this._call_id,
        'cseq': this._cseq += 1
      }, extraHeaders);
      var request_sender = new RequestSender(this._ua, request, {
        onRequestTimeout: function onRequestTimeout() {
          _this2._unregistered(null, JsSIP_C.causes.REQUEST_TIMEOUT);
        },
        onTransportError: function onTransportError() {
          _this2._unregistered(null, JsSIP_C.causes.CONNECTION_ERROR);
        },
        // Increase the CSeq on authentication.
        onAuthenticated: function onAuthenticated() {
          _this2._cseq += 1;
        },
        onReceiveResponse: function onReceiveResponse(response) {
          switch (true) {
            case /^1[0-9]{2}$/.test(response.status_code):
              // Ignore provisional responses.
              break;

            case /^2[0-9]{2}$/.test(response.status_code):
              _this2._unregistered(response);

              break;

            default:
              {
                var cause = Utils.sipErrorCause(response.status_code);

                _this2._unregistered(response, cause);
              }
          }
        }
      });
      request_sender.send();
    }
  }, {
    key: "close",
    value: function close() {
      if (this._registered) {
        this.unregister();
      }
    }
  }, {
    key: "onTransportClosed",
    value: function onTransportClosed() {
      this._registering = false;

      if (this._registrationTimer !== null) {
        clearTimeout(this._registrationTimer);
        this._registrationTimer = null;
      }

      if (this._registered) {
        this._registered = false;

        this._ua.unregistered({});
      }
    }
  }, {
    key: "_registrationFailure",
    value: function _registrationFailure(response, cause) {
      this._registering = false;

      this._ua.registrationFailed({
        response: response || null,
        cause: cause
      });

      if (this._registered) {
        this._registered = false;

        this._ua.unregistered({
          response: response || null,
          cause: cause
        });
      }
    }
  }, {
    key: "_unregistered",
    value: function _unregistered(response, cause) {
      this._registering = false;
      this._registered = false;

      this._ua.unregistered({
        response: response || null,
        cause: cause || null
      });
    }
  }, {
    key: "registered",
    get: function get() {
      return this._registered;
    }
  }]);

  return Registrator;
}();
},{"./Constants":2,"./Logger":9,"./RequestSender":20,"./SIPMessage":21,"./Utils":28}],20:[function(require,module,exports){
"use strict";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var Logger = require('./Logger');

var JsSIP_C = require('./Constants');

var DigestAuthentication = require('./DigestAuthentication');

var Transactions = require('./Transactions');

var logger = new Logger('RequestSender'); // Default event handlers.

var EventHandlers = {
  onRequestTimeout: function onRequestTimeout() {},
  onTransportError: function onTransportError() {},
  onReceiveResponse: function onReceiveResponse() {},
  onAuthenticated: function onAuthenticated() {}
};

module.exports = /*#__PURE__*/function () {
  function RequestSender(ua, request, eventHandlers) {
    _classCallCheck(this, RequestSender);

    this._ua = ua;
    this._eventHandlers = eventHandlers;
    this._method = request.method;
    this._request = request;
    this._auth = null;
    this._challenged = false;
    this._staled = false; // Define the undefined handlers.

    for (var handler in EventHandlers) {
      if (Object.prototype.hasOwnProperty.call(EventHandlers, handler)) {
        if (!this._eventHandlers[handler]) {
          this._eventHandlers[handler] = EventHandlers[handler];
        }
      }
    } // If ua is in closing process or even closed just allow sending Bye and ACK.


    if (ua.status === ua.C.STATUS_USER_CLOSED && (this._method !== JsSIP_C.BYE || this._method !== JsSIP_C.ACK)) {
      this._eventHandlers.onTransportError();
    }
  }
  /**
  * Create the client transaction and send the message.
  */


  _createClass(RequestSender, [{
    key: "send",
    value: function send() {
      var _this = this;

      var eventHandlers = {
        onRequestTimeout: function onRequestTimeout() {
          _this._eventHandlers.onRequestTimeout();
        },
        onTransportError: function onTransportError() {
          _this._eventHandlers.onTransportError();
        },
        onReceiveResponse: function onReceiveResponse(response) {
          _this._receiveResponse(response);
        }
      };

      switch (this._method) {
        case 'INVITE':
          this.clientTransaction = new Transactions.InviteClientTransaction(this._ua, this._ua.transport, this._request, eventHandlers);
          break;

        case 'ACK':
          this.clientTransaction = new Transactions.AckClientTransaction(this._ua, this._ua.transport, this._request, eventHandlers);
          break;

        default:
          this.clientTransaction = new Transactions.NonInviteClientTransaction(this._ua, this._ua.transport, this._request, eventHandlers);
      } // If authorization JWT is present, use it.


      if (this._ua._configuration.authorization_jwt) {
        this._request.setHeader('Authorization', this._ua._configuration.authorization_jwt);
      }

      this.clientTransaction.send();
    }
    /**
    * Called from client transaction when receiving a correct response to the request.
    * Authenticate request if needed or pass the response back to the applicant.
    */

  }, {
    key: "_receiveResponse",
    value: function _receiveResponse(response) {
      var challenge;
      var authorization_header_name;
      var status_code = response.status_code;
      /*
      * Authentication
      * Authenticate once. _challenged_ flag used to avoid infinite authentications.
      */

      if ((status_code === 401 || status_code === 407) && (this._ua.configuration.password !== null || this._ua.configuration.ha1 !== null)) {
        // Get and parse the appropriate WWW-Authenticate or Proxy-Authenticate header.
        if (response.status_code === 401) {
          challenge = response.parseHeader('www-authenticate');
          authorization_header_name = 'authorization';
        } else {
          challenge = response.parseHeader('proxy-authenticate');
          authorization_header_name = 'proxy-authorization';
        } // Verify it seems a valid challenge.


        if (!challenge) {
          logger.debug("".concat(response.status_code, " with wrong or missing challenge, cannot authenticate"));

          this._eventHandlers.onReceiveResponse(response);

          return;
        }

        if (!this._challenged || !this._staled && challenge.stale === true) {
          if (!this._auth) {
            this._auth = new DigestAuthentication({
              username: this._ua.configuration.authorization_user,
              password: this._ua.configuration.password,
              realm: this._ua.configuration.realm,
              ha1: this._ua.configuration.ha1
            });
          } // Verify that the challenge is really valid.


          if (!this._auth.authenticate(this._request, challenge)) {
            this._eventHandlers.onReceiveResponse(response);

            return;
          }

          this._challenged = true; // Update ha1 and realm in the UA.

          this._ua.set('realm', this._auth.get('realm'));

          this._ua.set('ha1', this._auth.get('ha1'));

          if (challenge.stale) {
            this._staled = true;
          }

          this._request = this._request.clone();
          this._request.cseq += 1;

          this._request.setHeader('cseq', "".concat(this._request.cseq, " ").concat(this._method));

          this._request.setHeader(authorization_header_name, this._auth.toString());

          this._eventHandlers.onAuthenticated(this._request);

          this.send();
        } else {
          this._eventHandlers.onReceiveResponse(response);
        }
      } else {
        this._eventHandlers.onReceiveResponse(response);
      }
    }
  }]);

  return RequestSender;
}();
},{"./Constants":2,"./DigestAuthentication":5,"./Logger":9,"./Transactions":24}],21:[function(require,module,exports){
"use strict";

function _typeof(obj) { "@babel/helpers - typeof"; 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); }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }

function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }

function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var sdp_transform = require('sdp-transform');

var Logger = require('./Logger');

var JsSIP_C = require('./Constants');

var Utils = require('./Utils');

var NameAddrHeader = require('./NameAddrHeader');

var Grammar = require('./Grammar');

var logger = new Logger('SIPMessage');
/**
 * -param {String} method request method
 * -param {String} ruri request uri
 * -param {UA} ua
 * -param {Object} params parameters that will have priority over ua.configuration parameters:
 * <br>
 *  - cseq, call_id, from_tag, from_uri, from_display_name, to_uri, to_tag, route_set
 * -param {Object} [headers] extra headers
 * -param {String} [body]
 */

var OutgoingRequest = /*#__PURE__*/function () {
  function OutgoingRequest(method, ruri, ua, params, extraHeaders, body) {
    _classCallCheck(this, OutgoingRequest);

    // Mandatory parameters check.
    if (!method || !ruri || !ua) {
      return null;
    }

    params = params || {};
    this.ua = ua;
    this.headers = {};
    this.method = method;
    this.ruri = ruri;
    this.body = body;
    this.extraHeaders = Utils.cloneArray(extraHeaders); // Fill the Common SIP Request Headers.
    // Route.

    if (params.route_set) {
      this.setHeader('route', params.route_set);
    } else if (ua.configuration.use_preloaded_route) {
      this.setHeader('route', "<".concat(ua.transport.sip_uri, ";lr>"));
    } // Via.
    // Empty Via header. Will be filled by the client transaction.


    this.setHeader('via', ''); // Max-Forwards.

    this.setHeader('max-forwards', JsSIP_C.MAX_FORWARDS); // To

    var to_uri = params.to_uri || ruri;
    var to_params = params.to_tag ? {
      tag: params.to_tag
    } : null;
    var to_display_name = typeof params.to_display_name !== 'undefined' ? params.to_display_name : null;
    this.to = new NameAddrHeader(to_uri, to_display_name, to_params);
    this.setHeader('to', this.to.toString()); // From.

    var from_uri = params.from_uri || ua.configuration.uri;
    var from_params = {
      tag: params.from_tag || Utils.newTag()
    };
    var display_name;

    if (typeof params.from_display_name !== 'undefined') {
      display_name = params.from_display_name;
    } else if (ua.configuration.display_name) {
      display_name = ua.configuration.display_name;
    } else {
      display_name = null;
    }

    this.from = new NameAddrHeader(from_uri, display_name, from_params);
    this.setHeader('from', this.from.toString()); // Call-ID.

    var call_id = params.call_id || ua.configuration.jssip_id + Utils.createRandomToken(15);
    this.call_id = call_id;
    this.setHeader('call-id', call_id); // CSeq.

    var cseq = params.cseq || Math.floor(Math.random() * 10000);
    this.cseq = cseq;
    this.setHeader('cseq', "".concat(cseq, " ").concat(method));
  }
  /**
   * Replace the the given header by the given value.
   * -param {String} name header name
   * -param {String | Array} value header value
   */


  _createClass(OutgoingRequest, [{
    key: "setHeader",
    value: function setHeader(name, value) {
      // Remove the header from extraHeaders if present.
      var regexp = new RegExp("^\\s*".concat(name, "\\s*:"), 'i');

      for (var idx = 0; idx < this.extraHeaders.length; idx++) {
        if (regexp.test(this.extraHeaders[idx])) {
          this.extraHeaders.splice(idx, 1);
        }
      }

      this.headers[Utils.headerize(name)] = Array.isArray(value) ? value : [value];
    }
    /**
     * Get the value of the given header name at the given position.
     * -param {String} name header name
     * -returns {String|undefined} Returns the specified header, null if header doesn't exist.
     */

  }, {
    key: "getHeader",
    value: function getHeader(name) {
      var headers = this.headers[Utils.headerize(name)];

      if (headers) {
        if (headers[0]) {
          return headers[0];
        }
      } else {
        var regexp = new RegExp("^\\s*".concat(name, "\\s*:"), 'i');

        var _iterator = _createForOfIteratorHelper(this.extraHeaders),
            _step;

        try {
          for (_iterator.s(); !(_step = _iterator.n()).done;) {
            var header = _step.value;

            if (regexp.test(header)) {
              return header.substring(header.indexOf(':') + 1).trim();
            }
          }
        } catch (err) {
          _iterator.e(err);
        } finally {
          _iterator.f();
        }
      }

      return;
    }
    /**
     * Get the header/s of the given name.
     * -param {String} name header name
     * -returns {Array} Array with all the headers of the specified name.
     */

  }, {
    key: "getHeaders",
    value: function getHeaders(name) {
      var headers = this.headers[Utils.headerize(name)];
      var result = [];

      if (headers) {
        var _iterator2 = _createForOfIteratorHelper(headers),
            _step2;

        try {
          for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
            var header = _step2.value;
            result.push(header);
          }
        } catch (err) {
          _iterator2.e(err);
        } finally {
          _iterator2.f();
        }

        return result;
      } else {
        var regexp = new RegExp("^\\s*".concat(name, "\\s*:"), 'i');

        var _iterator3 = _createForOfIteratorHelper(this.extraHeaders),
            _step3;

        try {
          for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
            var _header = _step3.value;

            if (regexp.test(_header)) {
              result.push(_header.substring(_header.indexOf(':') + 1).trim());
            }
          }
        } catch (err) {
          _iterator3.e(err);
        } finally {
          _iterator3.f();
        }

        return result;
      }
    }
    /**
     * Verify the existence of the given header.
     * -param {String} name header name
     * -returns {boolean} true if header with given name exists, false otherwise
     */

  }, {
    key: "hasHeader",
    value: function hasHeader(name) {
      if (this.headers[Utils.headerize(name)]) {
        return true;
      } else {
        var regexp = new RegExp("^\\s*".concat(name, "\\s*:"), 'i');

        var _iterator4 = _createForOfIteratorHelper(this.extraHeaders),
            _step4;

        try {
          for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
            var header = _step4.value;

            if (regexp.test(header)) {
              return true;
            }
          }
        } catch (err) {
          _iterator4.e(err);
        } finally {
          _iterator4.f();
        }
      }

      return false;
    }
    /**
     * Parse the current body as a SDP and store the resulting object
     * into this.sdp.
     * -param {Boolean} force: Parse even if this.sdp already exists.
     *
     * Returns this.sdp.
     */

  }, {
    key: "parseSDP",
    value: function parseSDP(force) {
      if (!force && this.sdp) {
        return this.sdp;
      } else {
        this.sdp = sdp_transform.parse(this.body || '');
        return this.sdp;
      }
    }
  }, {
    key: "toString",
    value: function toString() {
      var msg = "".concat(this.method, " ").concat(this.ruri, " SIP/2.0\r\n");

      for (var headerName in this.headers) {
        if (Object.prototype.hasOwnProperty.call(this.headers, headerName)) {
          var _iterator5 = _createForOfIteratorHelper(this.headers[headerName]),
              _step5;

          try {
            for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
              var headerValue = _step5.value;
              msg += "".concat(headerName, ": ").concat(headerValue, "\r\n");
            }
          } catch (err) {
            _iterator5.e(err);
          } finally {
            _iterator5.f();
          }
        }
      }

      var _iterator6 = _createForOfIteratorHelper(this.extraHeaders),
          _step6;

      try {
        for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
          var header = _step6.value;
          msg += "".concat(header.trim(), "\r\n");
        } // Supported.

      } catch (err) {
        _iterator6.e(err);
      } finally {
        _iterator6.f();
      }

      var supported = [];

      switch (this.method) {
        case JsSIP_C.REGISTER:
          supported.push('path', 'gruu');
          break;

        case JsSIP_C.INVITE:
          if (this.ua.configuration.session_timers) {
            supported.push('timer');
          }

          if (this.ua.contact.pub_gruu || this.ua.contact.temp_gruu) {
            supported.push('gruu');
          }

          supported.push('ice', 'replaces');
          break;

        case JsSIP_C.UPDATE:
          if (this.ua.configuration.session_timers) {
            supported.push('timer');
          }

          supported.push('ice');
          break;
      }

      supported.push('outbound');
      var userAgent = this.ua.configuration.user_agent || JsSIP_C.USER_AGENT; // Allow.

      msg += "Allow: ".concat(JsSIP_C.ALLOWED_METHODS, "\r\n");
      msg += "Supported: ".concat(supported, "\r\n");
      msg += "User-Agent: ".concat(userAgent, "\r\n");

      if (this.body) {
        var length = Utils.str_utf8_length(this.body);
        msg += "Content-Length: ".concat(length, "\r\n\r\n");
        msg += this.body;
      } else {
        msg += 'Content-Length: 0\r\n\r\n';
      }

      return msg;
    }
  }, {
    key: "clone",
    value: function clone() {
      var request = new OutgoingRequest(this.method, this.ruri, this.ua);
      Object.keys(this.headers).forEach(function (name) {
        request.headers[name] = this.headers[name].slice();
      }, this);
      request.body = this.body;
      request.extraHeaders = Utils.cloneArray(this.extraHeaders);
      request.to = this.to;
      request.from = this.from;
      request.call_id = this.call_id;
      request.cseq = this.cseq;
      return request;
    }
  }]);

  return OutgoingRequest;
}();

var InitialOutgoingInviteRequest = /*#__PURE__*/function (_OutgoingRequest) {
  _inherits(InitialOutgoingInviteRequest, _OutgoingRequest);

  var _super = _createSuper(InitialOutgoingInviteRequest);

  function InitialOutgoingInviteRequest(ruri, ua, params, extraHeaders, body) {
    var _this;

    _classCallCheck(this, InitialOutgoingInviteRequest);

    _this = _super.call(this, JsSIP_C.INVITE, ruri, ua, params, extraHeaders, body);
    _this.transaction = null;
    return _this;
  }

  _createClass(InitialOutgoingInviteRequest, [{
    key: "cancel",
    value: function cancel(reason) {
      this.transaction.cancel(reason);
    }
  }, {
    key: "clone",
    value: function clone() {
      var request = new InitialOutgoingInviteRequest(this.ruri, this.ua);
      Object.keys(this.headers).forEach(function (name) {
        request.headers[name] = this.headers[name].slice();
      }, this);
      request.body = this.body;
      request.extraHeaders = Utils.cloneArray(this.extraHeaders);
      request.to = this.to;
      request.from = this.from;
      request.call_id = this.call_id;
      request.cseq = this.cseq;
      request.transaction = this.transaction;
      return request;
    }
  }]);

  return InitialOutgoingInviteRequest;
}(OutgoingRequest);

var IncomingMessage = /*#__PURE__*/function () {
  function IncomingMessage() {
    _classCallCheck(this, IncomingMessage);

    this.data = null;
    this.headers = null;
    this.method = null;
    this.via = null;
    this.via_branch = null;
    this.call_id = null;
    this.cseq = null;
    this.from = null;
    this.from_tag = null;
    this.to = null;
    this.to_tag = null;
    this.body = null;
    this.sdp = null;
  }
  /**
  * Insert a header of the given name and value into the last position of the
  * header array.
  */


  _createClass(IncomingMessage, [{
    key: "addHeader",
    value: function addHeader(name, value) {
      var header = {
        raw: value
      };
      name = Utils.headerize(name);

      if (this.headers[name]) {
        this.headers[name].push(header);
      } else {
        this.headers[name] = [header];
      }
    }
    /**
     * Get the value of the given header name at the given position.
     */

  }, {
    key: "getHeader",
    value: function getHeader(name) {
      var header = this.headers[Utils.headerize(name)];

      if (header) {
        if (header[0]) {
          return header[0].raw;
        }
      } else {
        return;
      }
    }
    /**
     * Get the header/s of the given name.
     */

  }, {
    key: "getHeaders",
    value: function getHeaders(name) {
      var headers = this.headers[Utils.headerize(name)];
      var result = [];

      if (!headers) {
        return [];
      }

      var _iterator7 = _createForOfIteratorHelper(headers),
          _step7;

      try {
        for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
          var header = _step7.value;
          result.push(header.raw);
        }
      } catch (err) {
        _iterator7.e(err);
      } finally {
        _iterator7.f();
      }

      return result;
    }
    /**
     * Verify the existence of the given header.
     */

  }, {
    key: "hasHeader",
    value: function hasHeader(name) {
      return this.headers[Utils.headerize(name)] ? true : false;
    }
    /**
    * Parse the given header on the given index.
    * -param {String} name header name
    * -param {Number} [idx=0] header index
    * -returns {Object|undefined} Parsed header object, undefined if the header
    *  is not present or in case of a parsing error.
    */

  }, {
    key: "parseHeader",
    value: function parseHeader(name) {
      var idx = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
      name = Utils.headerize(name);

      if (!this.headers[name]) {
        logger.debug("header \"".concat(name, "\" not present"));
        return;
      } else if (idx >= this.headers[name].length) {
        logger.debug("not so many \"".concat(name, "\" headers present"));
        return;
      }

      var header = this.headers[name][idx];
      var value = header.raw;

      if (header.parsed) {
        return header.parsed;
      } // Substitute '-' by '_' for grammar rule matching.


      var parsed = Grammar.parse(value, name.replace(/-/g, '_'));

      if (parsed === -1) {
        this.headers[name].splice(idx, 1); // delete from headers

        logger.debug("error parsing \"".concat(name, "\" header field with value \"").concat(value, "\""));
        return;
      } else {
        header.parsed = parsed;
        return parsed;
      }
    }
    /**
     * Message Header attribute selector. Alias of parseHeader.
     * -param {String} name header name
     * -param {Number} [idx=0] header index
     * -returns {Object|undefined} Parsed header object, undefined if the header
     *  is not present or in case of a parsing error.
     *
     * -example
     * message.s('via',3).port
     */

  }, {
    key: "s",
    value: function s(name, idx) {
      return this.parseHeader(name, idx);
    }
    /**
    * Replace the value of the given header by the value.
    * -param {String} name header name
    * -param {String} value header value
    */

  }, {
    key: "setHeader",
    value: function setHeader(name, value) {
      var header = {
        raw: value
      };
      this.headers[Utils.headerize(name)] = [header];
    }
    /**
     * Parse the current body as a SDP and store the resulting object
     * into this.sdp.
     * -param {Boolean} force: Parse even if this.sdp already exists.
     *
     * Returns this.sdp.
     */

  }, {
    key: "parseSDP",
    value: function parseSDP(force) {
      if (!force && this.sdp) {
        return this.sdp;
      } else {
        this.sdp = sdp_transform.parse(this.body || '');
        return this.sdp;
      }
    }
  }, {
    key: "toString",
    value: function toString() {
      return this.data;
    }
  }]);

  return IncomingMessage;
}();

var IncomingRequest = /*#__PURE__*/function (_IncomingMessage) {
  _inherits(IncomingRequest, _IncomingMessage);

  var _super2 = _createSuper(IncomingRequest);

  function IncomingRequest(ua) {
    var _this2;

    _classCallCheck(this, IncomingRequest);

    _this2 = _super2.call(this);
    _this2.ua = ua;
    _this2.headers = {};
    _this2.ruri = null;
    _this2.transport = null;
    _this2.server_transaction = null;
    return _this2;
  }
  /**
  * Stateful reply.
  * -param {Number} code status code
  * -param {String} reason reason phrase
  * -param {Object} headers extra headers
  * -param {String} body body
  * -param {Function} [onSuccess] onSuccess callback
  * -param {Function} [onFailure] onFailure callback
  */


  _createClass(IncomingRequest, [{
    key: "reply",
    value: function reply(code, reason, extraHeaders, body, onSuccess, onFailure) {
      var supported = [];
      var to = this.getHeader('To');
      code = code || null;
      reason = reason || null; // Validate code and reason values.

      if (!code || code < 100 || code > 699) {
        throw new TypeError("Invalid status_code: ".concat(code));
      } else if (reason && typeof reason !== 'string' && !(reason instanceof String)) {
        throw new TypeError("Invalid reason_phrase: ".concat(reason));
      }

      reason = reason || JsSIP_C.REASON_PHRASE[code] || '';
      extraHeaders = Utils.cloneArray(extraHeaders);
      var response = "SIP/2.0 ".concat(code, " ").concat(reason, "\r\n");

      if (this.method === JsSIP_C.INVITE && code > 100 && code <= 200) {
        var headers = this.getHeaders('record-route');

        var _iterator8 = _createForOfIteratorHelper(headers),
            _step8;

        try {
          for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
            var header = _step8.value;
            response += "Record-Route: ".concat(header, "\r\n");
          }
        } catch (err) {
          _iterator8.e(err);
        } finally {
          _iterator8.f();
        }
      }

      var vias = this.getHeaders('via');

      var _iterator9 = _createForOfIteratorHelper(vias),
          _step9;

      try {
        for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
          var via = _step9.value;
          response += "Via: ".concat(via, "\r\n");
        }
      } catch (err) {
        _iterator9.e(err);
      } finally {
        _iterator9.f();
      }

      if (!this.to_tag && code > 100) {
        to += ";tag=".concat(Utils.newTag());
      } else if (this.to_tag && !this.s('to').hasParam('tag')) {
        to += ";tag=".concat(this.to_tag);
      }

      response += "To: ".concat(to, "\r\n");
      response += "From: ".concat(this.getHeader('From'), "\r\n");
      response += "Call-ID: ".concat(this.call_id, "\r\n");
      response += "CSeq: ".concat(this.cseq, " ").concat(this.method, "\r\n");

      var _iterator10 = _createForOfIteratorHelper(extraHeaders),
          _step10;

      try {
        for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) {
          var _header2 = _step10.value;
          response += "".concat(_header2.trim(), "\r\n");
        } // Supported.

      } catch (err) {
        _iterator10.e(err);
      } finally {
        _iterator10.f();
      }

      switch (this.method) {
        case JsSIP_C.INVITE:
          if (this.ua.configuration.session_timers) {
            supported.push('timer');
          }

          if (this.ua.contact.pub_gruu || this.ua.contact.temp_gruu) {
            supported.push('gruu');
          }

          supported.push('ice', 'replaces');
          break;

        case JsSIP_C.UPDATE:
          if (this.ua.configuration.session_timers) {
            supported.push('timer');
          }

          if (body) {
            supported.push('ice');
          }

          supported.push('replaces');
      }

      supported.push('outbound'); // Allow and Accept.

      if (this.method === JsSIP_C.OPTIONS) {
        response += "Allow: ".concat(JsSIP_C.ALLOWED_METHODS, "\r\n");
        response += "Accept: ".concat(JsSIP_C.ACCEPTED_BODY_TYPES, "\r\n");
      } else if (code === 405) {
        response += "Allow: ".concat(JsSIP_C.ALLOWED_METHODS, "\r\n");
      } else if (code === 415) {
        response += "Accept: ".concat(JsSIP_C.ACCEPTED_BODY_TYPES, "\r\n");
      }

      response += "Supported: ".concat(supported, "\r\n");

      if (body) {
        var length = Utils.str_utf8_length(body);
        response += 'Content-Type: application/sdp\r\n';
        response += "Content-Length: ".concat(length, "\r\n\r\n");
        response += body;
      } else {
        response += "Content-Length: ".concat(0, "\r\n\r\n");
      }

      this.server_transaction.receiveResponse(code, response, onSuccess, onFailure);
    }
    /**
    * Stateless reply.
    * -param {Number} code status code
    * -param {String} reason reason phrase
    */

  }, {
    key: "reply_sl",
    value: function reply_sl() {
      var code = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
      var reason = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
      var vias = this.getHeaders('via'); // Validate code and reason values.

      if (!code || code < 100 || code > 699) {
        throw new TypeError("Invalid status_code: ".concat(code));
      } else if (reason && typeof reason !== 'string' && !(reason instanceof String)) {
        throw new TypeError("Invalid reason_phrase: ".concat(reason));
      }

      reason = reason || JsSIP_C.REASON_PHRASE[code] || '';
      var response = "SIP/2.0 ".concat(code, " ").concat(reason, "\r\n");

      var _iterator11 = _createForOfIteratorHelper(vias),
          _step11;

      try {
        for (_iterator11.s(); !(_step11 = _iterator11.n()).done;) {
          var via = _step11.value;
          response += "Via: ".concat(via, "\r\n");
        }
      } catch (err) {
        _iterator11.e(err);
      } finally {
        _iterator11.f();
      }

      var to = this.getHeader('To');

      if (!this.to_tag && code > 100) {
        to += ";tag=".concat(Utils.newTag());
      } else if (this.to_tag && !this.s('to').hasParam('tag')) {
        to += ";tag=".concat(this.to_tag);
      }

      response += "To: ".concat(to, "\r\n");
      response += "From: ".concat(this.getHeader('From'), "\r\n");
      response += "Call-ID: ".concat(this.call_id, "\r\n");
      response += "CSeq: ".concat(this.cseq, " ").concat(this.method, "\r\n");
      response += "Content-Length: ".concat(0, "\r\n\r\n");
      this.transport.send(response);
    }
  }]);

  return IncomingRequest;
}(IncomingMessage);

var IncomingResponse = /*#__PURE__*/function (_IncomingMessage2) {
  _inherits(IncomingResponse, _IncomingMessage2);

  var _super3 = _createSuper(IncomingResponse);

  function IncomingResponse() {
    var _this3;

    _classCallCheck(this, IncomingResponse);

    _this3 = _super3.call(this);
    _this3.headers = {};
    _this3.status_code = null;
    _this3.reason_phrase = null;
    return _this3;
  }

  return IncomingResponse;
}(IncomingMessage);

module.exports = {
  OutgoingRequest: OutgoingRequest,
  InitialOutgoingInviteRequest: InitialOutgoingInviteRequest,
  IncomingRequest: IncomingRequest,
  IncomingResponse: IncomingResponse
};
},{"./Constants":2,"./Grammar":7,"./Logger":9,"./NameAddrHeader":11,"./Utils":28,"sdp-transform":37}],22:[function(require,module,exports){
"use strict";

var Logger = require('./Logger');

var Utils = require('./Utils');

var Grammar = require('./Grammar');

var logger = new Logger('Socket');
/**
 * Interface documentation: https://jssip.net/documentation/$last_version/api/socket/
 *
 * interface Socket {
 *  attribute String via_transport
 *  attribute String url
 *  attribute String sip_uri
 *
 *  method connect();
 *  method disconnect();
 *  method send(data);
 *
 *  attribute EventHandler onconnect
 *  attribute EventHandler ondisconnect
 *  attribute EventHandler ondata
 * }
 *
 */

exports.isSocket = function (socket) {
  // Ignore if an array is given.
  if (Array.isArray(socket)) {
    return false;
  }

  if (typeof socket === 'undefined') {
    logger.warn('undefined JsSIP.Socket instance');
    return false;
  } // Check Properties.


  try {
    if (!Utils.isString(socket.url)) {
      logger.warn('missing or invalid JsSIP.Socket url property');
      throw new Error('Missing or invalid JsSIP.Socket url property');
    }

    if (!Utils.isString(socket.via_transport)) {
      logger.warn('missing or invalid JsSIP.Socket via_transport property');
      throw new Error('Missing or invalid JsSIP.Socket via_transport property');
    }

    if (Grammar.parse(socket.sip_uri, 'SIP_URI') === -1) {
      logger.warn('missing or invalid JsSIP.Socket sip_uri property');
      throw new Error('missing or invalid JsSIP.Socket sip_uri property');
    }
  } catch (e) {
    return false;
  } // Check Methods.


  try {
    ['connect', 'disconnect', 'send'].forEach(function (method) {
      if (!Utils.isFunction(socket[method])) {
        logger.warn("missing or invalid JsSIP.Socket method: ".concat(method));
        throw new Error("Missing or invalid JsSIP.Socket method: ".concat(method));
      }
    });
  } catch (e) {
    return false;
  }

  return true;
};
},{"./Grammar":7,"./Logger":9,"./Utils":28}],23:[function(require,module,exports){
"use strict";

var T1 = 500,
    T2 = 4000,
    T4 = 5000;
module.exports = {
  T1: T1,
  T2: T2,
  T4: T4,
  TIMER_B: 64 * T1,
  TIMER_D: 0 * T1,
  TIMER_F: 64 * T1,
  TIMER_H: 64 * T1,
  TIMER_I: 0 * T1,
  TIMER_J: 0 * T1,
  TIMER_K: 0 * T4,
  TIMER_L: 64 * T1,
  TIMER_M: 64 * T1,
  PROVISIONAL_RESPONSE_INTERVAL: 60000 // See RFC 3261 Section 13.3.1.1

};
},{}],24:[function(require,module,exports){
"use strict";

function _typeof(obj) { "@babel/helpers - typeof"; 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); }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }

function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }

function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

var EventEmitter = require('events').EventEmitter;

var Logger = require('./Logger');

var JsSIP_C = require('./Constants');

var SIPMessage = require('./SIPMessage');

var Timers = require('./Timers');

var loggernict = new Logger('NonInviteClientTransaction');
var loggerict = new Logger('InviteClientTransaction');
var loggeract = new Logger('AckClientTransaction');
var loggernist = new Logger('NonInviteServerTransaction');
var loggerist = new Logger('InviteServerTransaction');
var C = {
  // Transaction states.
  STATUS_TRYING: 1,
  STATUS_PROCEEDING: 2,
  STATUS_CALLING: 3,
  STATUS_ACCEPTED: 4,
  STATUS_COMPLETED: 5,
  STATUS_TERMINATED: 6,
  STATUS_CONFIRMED: 7,
  // Transaction types.
  NON_INVITE_CLIENT: 'nict',
  NON_INVITE_SERVER: 'nist',
  INVITE_CLIENT: 'ict',
  INVITE_SERVER: 'ist'
};

var NonInviteClientTransaction = /*#__PURE__*/function (_EventEmitter) {
  _inherits(NonInviteClientTransaction, _EventEmitter);

  var _super = _createSuper(NonInviteClientTransaction);

  function NonInviteClientTransaction(ua, transport, request, eventHandlers) {
    var _this;

    _classCallCheck(this, NonInviteClientTransaction);

    _this = _super.call(this);
    _this.type = C.NON_INVITE_CLIENT;
    _this.id = "z9hG4bK".concat(Math.floor(Math.random() * 10000000));
    _this.ua = ua;
    _this.transport = transport;
    _this.request = request;
    _this.eventHandlers = eventHandlers;
    var via = "SIP/2.0/".concat(transport.via_transport);
    via += " ".concat(ua.configuration.via_host, ";branch=").concat(_this.id);

    _this.request.setHeader('via', via);

    _this.ua.newTransaction(_assertThisInitialized(_this));

    return _this;
  }

  _createClass(NonInviteClientTransaction, [{
    key: "stateChanged",
    value: function stateChanged(state) {
      this.state = state;
      this.emit('stateChanged');
    }
  }, {
    key: "send",
    value: function send() {
      var _this2 = this;

      this.stateChanged(C.STATUS_TRYING);
      this.F = setTimeout(function () {
        _this2.timer_F();
      }, Timers.TIMER_F);

      if (!this.transport.send(this.request)) {
        this.onTransportError();
      }
    }
  }, {
    key: "onTransportError",
    value: function onTransportError() {
      loggernict.debug("transport error occurred, deleting transaction ".concat(this.id));
      clearTimeout(this.F);
      clearTimeout(this.K);
      this.stateChanged(C.STATUS_TERMINATED);
      this.ua.destroyTransaction(this);
      this.eventHandlers.onTransportError();
    }
  }, {
    key: "timer_F",
    value: function timer_F() {
      loggernict.debug("Timer F expired for transaction ".concat(this.id));
      this.stateChanged(C.STATUS_TERMINATED);
      this.ua.destroyTransaction(this);
      this.eventHandlers.onRequestTimeout();
    }
  }, {
    key: "timer_K",
    value: function timer_K() {
      this.stateChanged(C.STATUS_TERMINATED);
      this.ua.destroyTransaction(this);
    }
  }, {
    key: "receiveResponse",
    value: function receiveResponse(response) {
      var _this3 = this;

      var status_code = response.status_code;

      if (status_code < 200) {
        switch (this.state) {
          case C.STATUS_TRYING:
          case C.STATUS_PROCEEDING:
            this.stateChanged(C.STATUS_PROCEEDING);
            this.eventHandlers.onReceiveResponse(response);
            break;
        }
      } else {
        switch (this.state) {
          case C.STATUS_TRYING:
          case C.STATUS_PROCEEDING:
            this.stateChanged(C.STATUS_COMPLETED);
            clearTimeout(this.F);

            if (status_code === 408) {
              this.eventHandlers.onRequestTimeout();
            } else {
              this.eventHandlers.onReceiveResponse(response);
            }

            this.K = setTimeout(function () {
              _this3.timer_K();
            }, Timers.TIMER_K);
            break;

          case C.STATUS_COMPLETED:
            break;
        }
      }
    }
  }, {
    key: "C",
    get: function get() {
      return C;
    }
  }]);

  return NonInviteClientTransaction;
}(EventEmitter);

var InviteClientTransaction = /*#__PURE__*/function (_EventEmitter2) {
  _inherits(InviteClientTransaction, _EventEmitter2);

  var _super2 = _createSuper(InviteClientTransaction);

  function InviteClientTransaction(ua, transport, request, eventHandlers) {
    var _this4;

    _classCallCheck(this, InviteClientTransaction);

    _this4 = _super2.call(this);
    _this4.type = C.INVITE_CLIENT;
    _this4.id = "z9hG4bK".concat(Math.floor(Math.random() * 10000000));
    _this4.ua = ua;
    _this4.transport = transport;
    _this4.request = request;
    _this4.eventHandlers = eventHandlers;
    request.transaction = _assertThisInitialized(_this4);
    var via = "SIP/2.0/".concat(transport.via_transport);
    via += " ".concat(ua.configuration.via_host, ";branch=").concat(_this4.id);

    _this4.request.setHeader('via', via);

    _this4.ua.newTransaction(_assertThisInitialized(_this4));

    return _this4;
  }

  _createClass(InviteClientTransaction, [{
    key: "stateChanged",
    value: function stateChanged(state) {
      this.state = state;
      this.emit('stateChanged');
    }
  }, {
    key: "send",
    value: function send() {
      var _this5 = this;

      this.stateChanged(C.STATUS_CALLING);
      this.B = setTimeout(function () {
        _this5.timer_B();
      }, Timers.TIMER_B);

      if (!this.transport.send(this.request)) {
        this.onTransportError();
      }
    }
  }, {
    key: "onTransportError",
    value: function onTransportError() {
      clearTimeout(this.B);
      clearTimeout(this.D);
      clearTimeout(this.M);

      if (this.state !== C.STATUS_ACCEPTED) {
        loggerict.debug("transport error occurred, deleting transaction ".concat(this.id));
        this.eventHandlers.onTransportError();
      }

      this.stateChanged(C.STATUS_TERMINATED);
      this.ua.destroyTransaction(this);
    } // RFC 6026 7.2.

  }, {
    key: "timer_M",
    value: function timer_M() {
      loggerict.debug("Timer M expired for transaction ".concat(this.id));

      if (this.state === C.STATUS_ACCEPTED) {
        clearTimeout(this.B);
        this.stateChanged(C.STATUS_TERMINATED);
        this.ua.destroyTransaction(this);
      }
    } // RFC 3261 17.1.1.

  }, {
    key: "timer_B",
    value: function timer_B() {
      loggerict.debug("Timer B expired for transaction ".concat(this.id));

      if (this.state === C.STATUS_CALLING) {
        this.stateChanged(C.STATUS_TERMINATED);
        this.ua.destroyTransaction(this);
        this.eventHandlers.onRequestTimeout();
      }
    }
  }, {
    key: "timer_D",
    value: function timer_D() {
      loggerict.debug("Timer D expired for transaction ".concat(this.id));
      clearTimeout(this.B);
      this.stateChanged(C.STATUS_TERMINATED);
      this.ua.destroyTransaction(this);
    }
  }, {
    key: "sendACK",
    value: function sendACK(response) {
      var _this6 = this;

      var ack = new SIPMessage.OutgoingRequest(JsSIP_C.ACK, this.request.ruri, this.ua, {
        'route_set': this.request.getHeaders('route'),
        'call_id': this.request.getHeader('call-id'),
        'cseq': this.request.cseq
      });
      ack.setHeader('from', this.request.getHeader('from'));
      ack.setHeader('via', this.request.getHeader('via'));
      ack.setHeader('to', response.getHeader('to'));
      this.D = setTimeout(function () {
        _this6.timer_D();
      }, Timers.TIMER_D);
      this.transport.send(ack);
    }
  }, {
    key: "cancel",
    value: function cancel(reason) {
      // Send only if a provisional response (>100) has been received.
      if (this.state !== C.STATUS_PROCEEDING) {
        return;
      }

      var cancel = new SIPMessage.OutgoingRequest(JsSIP_C.CANCEL, this.request.ruri, this.ua, {
        'route_set': this.request.getHeaders('route'),
        'call_id': this.request.getHeader('call-id'),
        'cseq': this.request.cseq
      });
      cancel.setHeader('from', this.request.getHeader('from'));
      cancel.setHeader('via', this.request.getHeader('via'));
      cancel.setHeader('to', this.request.getHeader('to'));

      if (reason) {
        cancel.setHeader('reason', reason);
      }

      this.transport.send(cancel);
    }
  }, {
    key: "receiveResponse",
    value: function receiveResponse(response) {
      var _this7 = this;

      var status_code = response.status_code;

      if (status_code >= 100 && status_code <= 199) {
        switch (this.state) {
          case C.STATUS_CALLING:
            this.stateChanged(C.STATUS_PROCEEDING);
            this.eventHandlers.onReceiveResponse(response);
            break;

          case C.STATUS_PROCEEDING:
            this.eventHandlers.onReceiveResponse(response);
            break;
        }
      } else if (status_code >= 200 && status_code <= 299) {
        switch (this.state) {
          case C.STATUS_CALLING:
          case C.STATUS_PROCEEDING:
            this.stateChanged(C.STATUS_ACCEPTED);
            this.M = setTimeout(function () {
              _this7.timer_M();
            }, Timers.TIMER_M);
            this.eventHandlers.onReceiveResponse(response);
            break;

          case C.STATUS_ACCEPTED:
            this.eventHandlers.onReceiveResponse(response);
            break;
        }
      } else if (status_code >= 300 && status_code <= 699) {
        switch (this.state) {
          case C.STATUS_CALLING:
          case C.STATUS_PROCEEDING:
            this.stateChanged(C.STATUS_COMPLETED);
            this.sendACK(response);
            this.eventHandlers.onReceiveResponse(response);
            break;

          case C.STATUS_COMPLETED:
            this.sendACK(response);
            break;
        }
      }
    }
  }, {
    key: "C",
    get: function get() {
      return C;
    }
  }]);

  return InviteClientTransaction;
}(EventEmitter);

var AckClientTransaction = /*#__PURE__*/function (_EventEmitter3) {
  _inherits(AckClientTransaction, _EventEmitter3);

  var _super3 = _createSuper(AckClientTransaction);

  function AckClientTransaction(ua, transport, request, eventHandlers) {
    var _this8;

    _classCallCheck(this, AckClientTransaction);

    _this8 = _super3.call(this);
    _this8.id = "z9hG4bK".concat(Math.floor(Math.random() * 10000000));
    _this8.transport = transport;
    _this8.request = request;
    _this8.eventHandlers = eventHandlers;
    var via = "SIP/2.0/".concat(transport.via_transport);
    via += " ".concat(ua.configuration.via_host, ";branch=").concat(_this8.id);

    _this8.request.setHeader('via', via);

    return _this8;
  }

  _createClass(AckClientTransaction, [{
    key: "send",
    value: function send() {
      if (!this.transport.send(this.request)) {
        this.onTransportError();
      }
    }
  }, {
    key: "onTransportError",
    value: function onTransportError() {
      loggeract.debug("transport error occurred for transaction ".concat(this.id));
      this.eventHandlers.onTransportError();
    }
  }, {
    key: "C",
    get: function get() {
      return C;
    }
  }]);

  return AckClientTransaction;
}(EventEmitter);

var NonInviteServerTransaction = /*#__PURE__*/function (_EventEmitter4) {
  _inherits(NonInviteServerTransaction, _EventEmitter4);

  var _super4 = _createSuper(NonInviteServerTransaction);

  function NonInviteServerTransaction(ua, transport, request) {
    var _this9;

    _classCallCheck(this, NonInviteServerTransaction);

    _this9 = _super4.call(this);
    _this9.type = C.NON_INVITE_SERVER;
    _this9.id = request.via_branch;
    _this9.ua = ua;
    _this9.transport = transport;
    _this9.request = request;
    _this9.last_response = '';
    request.server_transaction = _assertThisInitialized(_this9);
    _this9.state = C.STATUS_TRYING;
    ua.newTransaction(_assertThisInitialized(_this9));
    return _this9;
  }

  _createClass(NonInviteServerTransaction, [{
    key: "stateChanged",
    value: function stateChanged(state) {
      this.state = state;
      this.emit('stateChanged');
    }
  }, {
    key: "timer_J",
    value: function timer_J() {
      loggernist.debug("Timer J expired for transaction ".concat(this.id));
      this.stateChanged(C.STATUS_TERMINATED);
      this.ua.destroyTransaction(this);
    }
  }, {
    key: "onTransportError",
    value: function onTransportError() {
      if (!this.transportError) {
        this.transportError = true;
        loggernist.debug("transport error occurred, deleting transaction ".concat(this.id));
        clearTimeout(this.J);
        this.stateChanged(C.STATUS_TERMINATED);
        this.ua.destroyTransaction(this);
      }
    }
  }, {
    key: "receiveResponse",
    value: function receiveResponse(status_code, response, onSuccess, onFailure) {
      var _this10 = this;

      if (status_code === 100) {
        /* RFC 4320 4.1
         * 'A SIP element MUST NOT
         * send any provisional response with a
         * Status-Code other than 100 to a non-INVITE request.'
         */
        switch (this.state) {
          case C.STATUS_TRYING:
            this.stateChanged(C.STATUS_PROCEEDING);

            if (!this.transport.send(response)) {
              this.onTransportError();
            }

            break;

          case C.STATUS_PROCEEDING:
            this.last_response = response;

            if (!this.transport.send(response)) {
              this.onTransportError();

              if (onFailure) {
                onFailure();
              }
            } else if (onSuccess) {
              onSuccess();
            }

            break;
        }
      } else if (status_code >= 200 && status_code <= 699) {
        switch (this.state) {
          case C.STATUS_TRYING:
          case C.STATUS_PROCEEDING:
            this.stateChanged(C.STATUS_COMPLETED);
            this.last_response = response;
            this.J = setTimeout(function () {
              _this10.timer_J();
            }, Timers.TIMER_J);

            if (!this.transport.send(response)) {
              this.onTransportError();

              if (onFailure) {
                onFailure();
              }
            } else if (onSuccess) {
              onSuccess();
            }

            break;

          case C.STATUS_COMPLETED:
            break;
        }
      }
    }
  }, {
    key: "C",
    get: function get() {
      return C;
    }
  }]);

  return NonInviteServerTransaction;
}(EventEmitter);

var InviteServerTransaction = /*#__PURE__*/function (_EventEmitter5) {
  _inherits(InviteServerTransaction, _EventEmitter5);

  var _super5 = _createSuper(InviteServerTransaction);

  function InviteServerTransaction(ua, transport, request) {
    var _this11;

    _classCallCheck(this, InviteServerTransaction);

    _this11 = _super5.call(this);
    _this11.type = C.INVITE_SERVER;
    _this11.id = request.via_branch;
    _this11.ua = ua;
    _this11.transport = transport;
    _this11.request = request;
    _this11.last_response = '';
    request.server_transaction = _assertThisInitialized(_this11);
    _this11.state = C.STATUS_PROCEEDING;
    ua.newTransaction(_assertThisInitialized(_this11));
    _this11.resendProvisionalTimer = null;
    request.reply(100);
    return _this11;
  }

  _createClass(InviteServerTransaction, [{
    key: "stateChanged",
    value: function stateChanged(state) {
      this.state = state;
      this.emit('stateChanged');
    }
  }, {
    key: "timer_H",
    value: function timer_H() {
      loggerist.debug("Timer H expired for transaction ".concat(this.id));

      if (this.state === C.STATUS_COMPLETED) {
        loggerist.debug('ACK not received, dialog will be terminated');
      }

      this.stateChanged(C.STATUS_TERMINATED);
      this.ua.destroyTransaction(this);
    }
  }, {
    key: "timer_I",
    value: function timer_I() {
      this.stateChanged(C.STATUS_TERMINATED);
      this.ua.destroyTransaction(this);
    } // RFC 6026 7.1.

  }, {
    key: "timer_L",
    value: function timer_L() {
      loggerist.debug("Timer L expired for transaction ".concat(this.id));

      if (this.state === C.STATUS_ACCEPTED) {
        this.stateChanged(C.STATUS_TERMINATED);
        this.ua.destroyTransaction(this);
      }
    }
  }, {
    key: "onTransportError",
    value: function onTransportError() {
      if (!this.transportError) {
        this.transportError = true;
        loggerist.debug("transport error occurred, deleting transaction ".concat(this.id));

        if (this.resendProvisionalTimer !== null) {
          clearInterval(this.resendProvisionalTimer);
          this.resendProvisionalTimer = null;
        }

        clearTimeout(this.L);
        clearTimeout(this.H);
        clearTimeout(this.I);
        this.stateChanged(C.STATUS_TERMINATED);
        this.ua.destroyTransaction(this);
      }
    }
  }, {
    key: "resend_provisional",
    value: function resend_provisional() {
      if (!this.transport.send(this.last_response)) {
        this.onTransportError();
      }
    } // INVITE Server Transaction RFC 3261 17.2.1.

  }, {
    key: "receiveResponse",
    value: function receiveResponse(status_code, response, onSuccess, onFailure) {
      var _this12 = this;

      if (status_code >= 100 && status_code <= 199) {
        switch (this.state) {
          case C.STATUS_PROCEEDING:
            if (!this.transport.send(response)) {
              this.onTransportError();
            }

            this.last_response = response;
            break;
        }
      }

      if (status_code > 100 && status_code <= 199 && this.state === C.STATUS_PROCEEDING) {
        // Trigger the resendProvisionalTimer only for the first non 100 provisional response.
        if (this.resendProvisionalTimer === null) {
          this.resendProvisionalTimer = setInterval(function () {
            _this12.resend_provisional();
          }, Timers.PROVISIONAL_RESPONSE_INTERVAL);
        }
      } else if (status_code >= 200 && status_code <= 299) {
        switch (this.state) {
          case C.STATUS_PROCEEDING:
            this.stateChanged(C.STATUS_ACCEPTED);
            this.last_response = response;
            this.L = setTimeout(function () {
              _this12.timer_L();
            }, Timers.TIMER_L);

            if (this.resendProvisionalTimer !== null) {
              clearInterval(this.resendProvisionalTimer);
              this.resendProvisionalTimer = null;
            }

          /* falls through */

          case C.STATUS_ACCEPTED:
            // Note that this point will be reached for proceeding this.state also.
            if (!this.transport.send(response)) {
              this.onTransportError();

              if (onFailure) {
                onFailure();
              }
            } else if (onSuccess) {
              onSuccess();
            }

            break;
        }
      } else if (status_code >= 300 && status_code <= 699) {
        switch (this.state) {
          case C.STATUS_PROCEEDING:
            if (this.resendProvisionalTimer !== null) {
              clearInterval(this.resendProvisionalTimer);
              this.resendProvisionalTimer = null;
            }

            if (!this.transport.send(response)) {
              this.onTransportError();

              if (onFailure) {
                onFailure();
              }
            } else {
              this.stateChanged(C.STATUS_COMPLETED);
              this.H = setTimeout(function () {
                _this12.timer_H();
              }, Timers.TIMER_H);

              if (onSuccess) {
                onSuccess();
              }
            }

            break;
        }
      }
    }
  }, {
    key: "C",
    get: function get() {
      return C;
    }
  }]);

  return InviteServerTransaction;
}(EventEmitter);
/**
 * INVITE:
 *  _true_ if retransmission
 *  _false_ new request
 *
 * ACK:
 *  _true_  ACK to non2xx response
 *  _false_ ACK must be passed to TU (accepted state)
 *          ACK to 2xx response
 *
 * CANCEL:
 *  _true_  no matching invite transaction
 *  _false_ matching invite transaction and no final response sent
 *
 * OTHER:
 *  _true_  retransmission
 *  _false_ new request
 */


function checkTransaction(_ref, request) {
  var _transactions = _ref._transactions;
  var tr;

  switch (request.method) {
    case JsSIP_C.INVITE:
      tr = _transactions.ist[request.via_branch];

      if (tr) {
        switch (tr.state) {
          case C.STATUS_PROCEEDING:
            tr.transport.send(tr.last_response);
            break;
          // RFC 6026 7.1 Invite retransmission.
          // Received while in C.STATUS_ACCEPTED state. Absorb it.

          case C.STATUS_ACCEPTED:
            break;
        }

        return true;
      }

      break;

    case JsSIP_C.ACK:
      tr = _transactions.ist[request.via_branch]; // RFC 6026 7.1.

      if (tr) {
        if (tr.state === C.STATUS_ACCEPTED) {
          return false;
        } else if (tr.state === C.STATUS_COMPLETED) {
          tr.state = C.STATUS_CONFIRMED;
          tr.I = setTimeout(function () {
            tr.timer_I();
          }, Timers.TIMER_I);
          return true;
        }
      } // ACK to 2XX Response.
      else {
          return false;
        }

      break;

    case JsSIP_C.CANCEL:
      tr = _transactions.ist[request.via_branch];

      if (tr) {
        request.reply_sl(200);

        if (tr.state === C.STATUS_PROCEEDING) {
          return false;
        } else {
          return true;
        }
      } else {
        request.reply_sl(481);
        return true;
      }

    default:
      // Non-INVITE Server Transaction RFC 3261 17.2.2.
      tr = _transactions.nist[request.via_branch];

      if (tr) {
        switch (tr.state) {
          case C.STATUS_TRYING:
            break;

          case C.STATUS_PROCEEDING:
          case C.STATUS_COMPLETED:
            tr.transport.send(tr.last_response);
            break;
        }

        return true;
      }

      break;
  }
}

module.exports = {
  C: C,
  NonInviteClientTransaction: NonInviteClientTransaction,
  InviteClientTransaction: InviteClientTransaction,
  AckClientTransaction: AckClientTransaction,
  NonInviteServerTransaction: NonInviteServerTransaction,
  InviteServerTransaction: InviteServerTransaction,
  checkTransaction: checkTransaction
};
},{"./Constants":2,"./Logger":9,"./SIPMessage":21,"./Timers":23,"events":31}],25:[function(require,module,exports){
"use strict";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var Logger = require('./Logger');

var Socket = require('./Socket');

var JsSIP_C = require('./Constants');

var logger = new Logger('Transport');
/**
 * Constants
 */

var C = {
  // Transport status.
  STATUS_CONNECTED: 0,
  STATUS_CONNECTING: 1,
  STATUS_DISCONNECTED: 2,
  // Socket status.
  SOCKET_STATUS_READY: 0,
  SOCKET_STATUS_ERROR: 1,
  // Recovery options.
  recovery_options: {
    // minimum interval in seconds between recover attempts.
    min_interval: JsSIP_C.CONNECTION_RECOVERY_MIN_INTERVAL,
    // maximum interval in seconds between recover attempts.
    max_interval: JsSIP_C.CONNECTION_RECOVERY_MAX_INTERVAL
  }
};
/*
 * Manages one or multiple JsSIP.Socket instances.
 * Is reponsible for transport recovery logic among all socket instances.
 *
 * @socket JsSIP::Socket instance
 */

module.exports = /*#__PURE__*/function () {
  function Transport(sockets) {
    var recovery_options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : C.recovery_options;

    _classCallCheck(this, Transport);

    logger.debug('new()');
    this.status = C.STATUS_DISCONNECTED; // Current socket.

    this.socket = null; // Socket collection.

    this.sockets = [];
    this.recovery_options = recovery_options;
    this.recover_attempts = 0;
    this.recovery_timer = null;
    this.close_requested = false; // It seems that TextDecoder is not available in some versions of React-Native.
    // See https://github.com/versatica/JsSIP/issues/695

    try {
      this.textDecoder = new TextDecoder('utf8');
    } catch (error) {
      logger.warn("cannot use TextDecoder: ".concat(error));
    }

    if (typeof sockets === 'undefined') {
      throw new TypeError('Invalid argument.' + ' undefined \'sockets\' argument');
    }

    if (!(sockets instanceof Array)) {
      sockets = [sockets];
    }

    sockets.forEach(function (socket) {
      if (!Socket.isSocket(socket.socket)) {
        throw new TypeError('Invalid argument.' + ' invalid \'JsSIP.Socket\' instance');
      }

      if (socket.weight && !Number(socket.weight)) {
        throw new TypeError('Invalid argument.' + ' \'weight\' attribute is not a number');
      }

      this.sockets.push({
        socket: socket.socket,
        weight: socket.weight || 0,
        status: C.SOCKET_STATUS_READY
      });
    }, this); // Get the socket with higher weight.

    this._getSocket();
  }
  /**
   * Instance Methods
   */


  _createClass(Transport, [{
    key: "connect",
    value: function connect() {
      logger.debug('connect()');

      if (this.isConnected()) {
        logger.debug('Transport is already connected');
        return;
      } else if (this.isConnecting()) {
        logger.debug('Transport is connecting');
        return;
      }

      this.close_requested = false;
      this.status = C.STATUS_CONNECTING;
      this.onconnecting({
        socket: this.socket,
        attempts: this.recover_attempts
      });

      if (!this.close_requested) {
        // Bind socket event callbacks.
        this.socket.onconnect = this._onConnect.bind(this);
        this.socket.ondisconnect = this._onDisconnect.bind(this);
        this.socket.ondata = this._onData.bind(this);
        this.socket.connect();
      }

      return;
    }
  }, {
    key: "disconnect",
    value: function disconnect() {
      logger.debug('close()');
      this.close_requested = true;
      this.recover_attempts = 0;
      this.status = C.STATUS_DISCONNECTED; // Clear recovery_timer.

      if (this.recovery_timer !== null) {
        clearTimeout(this.recovery_timer);
        this.recovery_timer = null;
      } // Unbind socket event callbacks.


      this.socket.onconnect = function () {};

      this.socket.ondisconnect = function () {};

      this.socket.ondata = function () {};

      this.socket.disconnect();
      this.ondisconnect({
        socket: this.socket,
        error: false
      });
    }
  }, {
    key: "send",
    value: function send(data) {
      logger.debug('send()');

      if (!this.isConnected()) {
        logger.warn('unable to send message, transport is not connected');
        return false;
      }

      var message = data.toString();
      logger.debug("sending message:\n\n".concat(message, "\n"));
      return this.socket.send(message);
    }
  }, {
    key: "isConnected",
    value: function isConnected() {
      return this.status === C.STATUS_CONNECTED;
    }
  }, {
    key: "isConnecting",
    value: function isConnecting() {
      return this.status === C.STATUS_CONNECTING;
    }
    /**
     * Private API.
     */

  }, {
    key: "_reconnect",
    value: function _reconnect() {
      var _this = this;

      this.recover_attempts += 1;
      var k = Math.floor(Math.random() * Math.pow(2, this.recover_attempts) + 1);

      if (k < this.recovery_options.min_interval) {
        k = this.recovery_options.min_interval;
      } else if (k > this.recovery_options.max_interval) {
        k = this.recovery_options.max_interval;
      }

      logger.debug("reconnection attempt: ".concat(this.recover_attempts, ". next connection attempt in ").concat(k, " seconds"));
      this.recovery_timer = setTimeout(function () {
        if (!_this.close_requested && !(_this.isConnected() || _this.isConnecting())) {
          // Get the next available socket with higher weight.
          _this._getSocket(); // Connect the socket.


          _this.connect();
        }
      }, k * 1000);
    }
    /**
     * get the next available socket with higher weight
     */

  }, {
    key: "_getSocket",
    value: function _getSocket() {
      var candidates = [];
      this.sockets.forEach(function (socket) {
        if (socket.status === C.SOCKET_STATUS_ERROR) {
          return; // continue the array iteration
        } else if (candidates.length === 0) {
          candidates.push(socket);
        } else if (socket.weight > candidates[0].weight) {
          candidates = [socket];
        } else if (socket.weight === candidates[0].weight) {
          candidates.push(socket);
        }
      });

      if (candidates.length === 0) {
        // All sockets have failed. reset sockets status.
        this.sockets.forEach(function (socket) {
          socket.status = C.SOCKET_STATUS_READY;
        }); // Get next available socket.

        this._getSocket();

        return;
      }

      var idx = Math.floor(Math.random() * candidates.length);
      this.socket = candidates[idx].socket;
    }
    /**
     * Socket Event Handlers
     */

  }, {
    key: "_onConnect",
    value: function _onConnect() {
      this.recover_attempts = 0;
      this.status = C.STATUS_CONNECTED; // Clear recovery_timer.

      if (this.recovery_timer !== null) {
        clearTimeout(this.recovery_timer);
        this.recovery_timer = null;
      }

      this.onconnect({
        socket: this
      });
    }
  }, {
    key: "_onDisconnect",
    value: function _onDisconnect(error, code, reason) {
      this.status = C.STATUS_DISCONNECTED;
      this.ondisconnect({
        socket: this.socket,
        error: error,
        code: code,
        reason: reason
      });

      if (this.close_requested) {
        return;
      } // Update socket status.
      else {
          this.sockets.forEach(function (socket) {
            if (this.socket === socket.socket) {
              socket.status = C.SOCKET_STATUS_ERROR;
            }
          }, this);
        }

      this._reconnect(error);
    }
  }, {
    key: "_onData",
    value: function _onData(data) {
      // CRLF Keep Alive response from server. Ignore it.
      if (data === '\r\n') {
        logger.debug('received message with CRLF Keep Alive response');
        return;
      } // Binary message.
      else if (typeof data !== 'string') {
          try {
            if (this.textDecoder) data = this.textDecoder.decode(data);else data = String.fromCharCode.apply(null, new Uint8Array(data));
          } catch (evt) {
            logger.debug('received binary message failed to be converted into string,' + ' message discarded');
            return;
          }

          logger.debug("received binary message:\n\n".concat(data, "\n"));
        } // Text message.
        else {
            logger.debug("received text message:\n\n".concat(data, "\n"));
          }

      this.ondata({
        transport: this,
        message: data
      });
    }
  }, {
    key: "via_transport",
    get: function get() {
      return this.socket.via_transport;
    }
  }, {
    key: "url",
    get: function get() {
      return this.socket.url;
    }
  }, {
    key: "sip_uri",
    get: function get() {
      return this.socket.sip_uri;
    }
  }]);

  return Transport;
}();
},{"./Constants":2,"./Logger":9,"./Socket":22}],26:[function(require,module,exports){
"use strict";

function _typeof(obj) { "@babel/helpers - typeof"; 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); }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }

function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }

function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

var EventEmitter = require('events').EventEmitter;

var Logger = require('./Logger');

var JsSIP_C = require('./Constants');

var Registrator = require('./Registrator');

var RTCSession = require('./RTCSession');

var Message = require('./Message');

var Options = require('./Options');

var Transactions = require('./Transactions');

var Transport = require('./Transport');

var Utils = require('./Utils');

var Exceptions = require('./Exceptions');

var URI = require('./URI');

var Parser = require('./Parser');

var SIPMessage = require('./SIPMessage');

var sanityCheck = require('./sanityCheck');

var config = require('./Config');

var logger = new Logger('UA');
var C = {
  // UA status codes.
  STATUS_INIT: 0,
  STATUS_READY: 1,
  STATUS_USER_CLOSED: 2,
  STATUS_NOT_READY: 3,
  // UA error codes.
  CONFIGURATION_ERROR: 1,
  NETWORK_ERROR: 2
};
/**
 * The User-Agent class.
 * @class JsSIP.UA
 * @param {Object} configuration Configuration parameters.
 * @throws {JsSIP.Exceptions.ConfigurationError} If a configuration parameter is invalid.
 * @throws {TypeError} If no configuration is given.
 */

module.exports = /*#__PURE__*/function (_EventEmitter) {
  _inherits(UA, _EventEmitter);

  var _super = _createSuper(UA);

  _createClass(UA, null, [{
    key: "C",
    // Expose C object.
    get: function get() {
      return C;
    }
  }]);

  function UA(configuration) {
    var _this;

    _classCallCheck(this, UA);

    logger.debug('new() [configuration:%o]', configuration);
    _this = _super.call(this);
    _this._cache = {
      credentials: {}
    };
    _this._configuration = Object.assign({}, config.settings);
    _this._dynConfiguration = {};
    _this._dialogs = {}; // User actions outside any session/dialog (MESSAGE/OPTIONS).

    _this._applicants = {};
    _this._sessions = {};
    _this._transport = null;
    _this._contact = null;
    _this._status = C.STATUS_INIT;
    _this._error = null;
    _this._transactions = {
      nist: {},
      nict: {},
      ist: {},
      ict: {}
    }; // Custom UA empty object for high level use.

    _this._data = {};
    _this._closeTimer = null; // Check configuration argument.

    if (configuration === undefined) {
      throw new TypeError('Not enough arguments');
    } // Load configuration.


    try {
      _this._loadConfig(configuration);
    } catch (e) {
      _this._status = C.STATUS_NOT_READY;
      _this._error = C.CONFIGURATION_ERROR;
      throw e;
    } // Initialize registrator.


    _this._registrator = new Registrator(_assertThisInitialized(_this));
    return _this;
  }

  _createClass(UA, [{
    key: "start",
    // =================
    //  High Level API
    // =================

    /**
     * Connect to the server if status = STATUS_INIT.
     * Resume UA after being closed.
     */
    value: function start() {
      logger.debug('start()');

      if (this._status === C.STATUS_INIT) {
        this._transport.connect();
      } else if (this._status === C.STATUS_USER_CLOSED) {
        logger.debug('restarting UA'); // Disconnect.

        if (this._closeTimer !== null) {
          clearTimeout(this._closeTimer);
          this._closeTimer = null;

          this._transport.disconnect();
        } // Reconnect.


        this._status = C.STATUS_INIT;

        this._transport.connect();
      } else if (this._status === C.STATUS_READY) {
        logger.debug('UA is in READY status, not restarted');
      } else {
        logger.debug('ERROR: connection is down, Auto-Recovery system is trying to reconnect');
      } // Set dynamic configuration.


      this._dynConfiguration.register = this._configuration.register;
    }
    /**
     * Register.
     */

  }, {
    key: "register",
    value: function register() {
      logger.debug('register()');
      this._dynConfiguration.register = true;

      this._registrator.register();
    }
    /**
     * Unregister.
     */

  }, {
    key: "unregister",
    value: function unregister(options) {
      logger.debug('unregister()');
      this._dynConfiguration.register = false;

      this._registrator.unregister(options);
    }
    /**
     * Get the Registrator instance.
     */

  }, {
    key: "registrator",
    value: function registrator() {
      return this._registrator;
    }
    /**
     * Registration state.
     */

  }, {
    key: "isRegistered",
    value: function isRegistered() {
      return this._registrator.registered;
    }
    /**
     * Connection state.
     */

  }, {
    key: "isConnected",
    value: function isConnected() {
      return this._transport.isConnected();
    }
    /**
     * Make an outgoing call.
     *
     * -param {String} target
     * -param {Object} [options]
     *
     * -throws {TypeError}
     *
     */

  }, {
    key: "call",
    value: function call(target, options) {
      logger.debug('call()');
      var session = new RTCSession(this);
      session.connect(target, options);
      return session;
    }
    /**
     * Send a message.
     *
     * -param {String} target
     * -param {String} body
     * -param {Object} [options]
     *
     * -throws {TypeError}
     *
     */

  }, {
    key: "sendMessage",
    value: function sendMessage(target, body, options) {
      logger.debug('sendMessage()');
      var message = new Message(this);
      message.send(target, body, options);
      return message;
    }
    /**
     * Send a SIP OPTIONS.
     *
     * -param {String} target
     * -param {String} [body]
     * -param {Object} [options]
     *
     * -throws {TypeError}
     *
     */

  }, {
    key: "sendOptions",
    value: function sendOptions(target, body, options) {
      logger.debug('sendOptions()');
      var message = new Options(this);
      message.send(target, body, options);
      return message;
    }
    /**
     * Terminate ongoing sessions.
     */

  }, {
    key: "terminateSessions",
    value: function terminateSessions(options) {
      logger.debug('terminateSessions()');

      for (var idx in this._sessions) {
        if (!this._sessions[idx].isEnded()) {
          this._sessions[idx].terminate(options);
        }
      }
    }
    /**
     * Gracefully close.
     *
     */

  }, {
    key: "stop",
    value: function stop() {
      var _this2 = this;

      logger.debug('stop()'); // Remove dynamic settings.

      this._dynConfiguration = {};

      if (this._status === C.STATUS_USER_CLOSED) {
        logger.debug('UA already closed');
        return;
      } // Close registrator.


      this._registrator.close(); // If there are session wait a bit so CANCEL/BYE can be sent and their responses received.


      var num_sessions = Object.keys(this._sessions).length; // Run  _terminate_ on every Session.

      for (var session in this._sessions) {
        if (Object.prototype.hasOwnProperty.call(this._sessions, session)) {
          logger.debug("closing session ".concat(session));

          try {
            this._sessions[session].terminate();
          } catch (error) {}
        }
      } // Run  _close_ on every applicant.


      for (var applicant in this._applicants) {
        if (Object.prototype.hasOwnProperty.call(this._applicants, applicant)) try {
          this._applicants[applicant].close();
        } catch (error) {}
      }

      this._status = C.STATUS_USER_CLOSED;
      var num_transactions = Object.keys(this._transactions.nict).length + Object.keys(this._transactions.nist).length + Object.keys(this._transactions.ict).length + Object.keys(this._transactions.ist).length;

      if (num_transactions === 0 && num_sessions === 0) {
        this._transport.disconnect();
      } else {
        this._closeTimer = setTimeout(function () {
          _this2._closeTimer = null;

          _this2._transport.disconnect();
        }, 2000);
      }
    }
    /**
     * Normalice a string into a valid SIP request URI
     * -param {String} target
     * -returns {JsSIP.URI|undefined}
     */

  }, {
    key: "normalizeTarget",
    value: function normalizeTarget(target) {
      return Utils.normalizeTarget(target, this._configuration.hostport_params);
    }
    /**
     * Allow retrieving configuration and autogenerated fields in runtime.
     */

  }, {
    key: "get",
    value: function get(parameter) {
      switch (parameter) {
        case 'authorization_user':
          return this._configuration.authorization_user;

        case 'realm':
          return this._configuration.realm;

        case 'ha1':
          return this._configuration.ha1;

        case 'authorization_jwt':
          return this._configuration.authorization_jwt;

        default:
          logger.warn('get() | cannot get "%s" parameter in runtime', parameter);
          return undefined;
      }
    }
    /**
     * Allow configuration changes in runtime.
     * Returns true if the parameter could be set.
     */

  }, {
    key: "set",
    value: function set(parameter, value) {
      switch (parameter) {
        case 'authorization_user':
          {
            this._configuration.authorization_user = String(value);
            break;
          }

        case 'password':
          {
            this._configuration.password = String(value);
            break;
          }

        case 'realm':
          {
            this._configuration.realm = String(value);
            break;
          }

        case 'ha1':
          {
            this._configuration.ha1 = String(value); // Delete the plain SIP password.

            this._configuration.password = null;
            break;
          }

        case 'authorization_jwt':
          {
            this._configuration.authorization_jwt = String(value);
            break;
          }

        case 'display_name':
          {
            this._configuration.display_name = value;
            break;
          }

        default:
          logger.warn('set() | cannot set "%s" parameter in runtime', parameter);
          return false;
      }

      return true;
    } // ==========================
    // Event Handlers.
    // ==========================

    /**
     * new Transaction
     */

  }, {
    key: "newTransaction",
    value: function newTransaction(transaction) {
      this._transactions[transaction.type][transaction.id] = transaction;
      this.emit('newTransaction', {
        transaction: transaction
      });
    }
    /**
     * Transaction destroyed.
     */

  }, {
    key: "destroyTransaction",
    value: function destroyTransaction(transaction) {
      delete this._transactions[transaction.type][transaction.id];
      this.emit('transactionDestroyed', {
        transaction: transaction
      });
    }
    /**
     * new Dialog
     */

  }, {
    key: "newDialog",
    value: function newDialog(dialog) {
      this._dialogs[dialog.id] = dialog;
    }
    /**
     * Dialog destroyed.
     */

  }, {
    key: "destroyDialog",
    value: function destroyDialog(dialog) {
      delete this._dialogs[dialog.id];
    }
    /**
     *  new Message
     */

  }, {
    key: "newMessage",
    value: function newMessage(message, data) {
      this._applicants[message] = message;
      this.emit('newMessage', data);
    }
    /**
     *  new Options
     */

  }, {
    key: "newOptions",
    value: function newOptions(message, data) {
      this._applicants[message] = message;
      this.emit('newOptions', data);
    }
    /**
     *  Message destroyed.
     */

  }, {
    key: "destroyMessage",
    value: function destroyMessage(message) {
      delete this._applicants[message];
    }
    /**
     * new RTCSession
     */

  }, {
    key: "newRTCSession",
    value: function newRTCSession(session, data) {
      this._sessions[session.id] = session;
      this.emit('newRTCSession', data);
    }
    /**
     * RTCSession destroyed.
     */

  }, {
    key: "destroyRTCSession",
    value: function destroyRTCSession(session) {
      delete this._sessions[session.id];
    }
    /**
     * Registered
     */

  }, {
    key: "registered",
    value: function registered(data) {
      this.emit('registered', data);
    }
    /**
     * Unregistered
     */

  }, {
    key: "unregistered",
    value: function unregistered(data) {
      this.emit('unregistered', data);
    }
    /**
     * Registration Failed
     */

  }, {
    key: "registrationFailed",
    value: function registrationFailed(data) {
      this.emit('registrationFailed', data);
    } // =========================
    // ReceiveRequest.
    // =========================

    /**
     * Request reception
     */

  }, {
    key: "receiveRequest",
    value: function receiveRequest(request) {
      var method = request.method; // Check that request URI points to us.

      if (request.ruri.user !== this._configuration.uri.user && request.ruri.user !== this._contact.uri.user) {
        logger.debug('Request-URI does not point to us');

        if (request.method !== JsSIP_C.ACK) {
          request.reply_sl(404);
        }

        return;
      } // Check request URI scheme.


      if (request.ruri.scheme === JsSIP_C.SIPS) {
        request.reply_sl(416);
        return;
      } // Check transaction.


      if (Transactions.checkTransaction(this, request)) {
        return;
      } // Create the server transaction.


      if (method === JsSIP_C.INVITE) {
        /* eslint-disable no-new */
        new Transactions.InviteServerTransaction(this, this._transport, request);
        /* eslint-enable no-new */
      } else if (method !== JsSIP_C.ACK && method !== JsSIP_C.CANCEL) {
        /* eslint-disable no-new */
        new Transactions.NonInviteServerTransaction(this, this._transport, request);
        /* eslint-enable no-new */
      }
      /* RFC3261 12.2.2
       * Requests that do not change in any way the state of a dialog may be
       * received within a dialog (for example, an OPTIONS request).
       * They are processed as if they had been received outside the dialog.
       */


      if (method === JsSIP_C.OPTIONS) {
        if (this.listeners('newOptions').length === 0) {
          request.reply(200);
          return;
        }

        var message = new Options(this);
        message.init_incoming(request);
      } else if (method === JsSIP_C.MESSAGE) {
        if (this.listeners('newMessage').length === 0) {
          request.reply(405);
          return;
        }

        var _message = new Message(this);

        _message.init_incoming(request);
      } else if (method === JsSIP_C.INVITE) {
        // Initial INVITE.
        if (!request.to_tag && this.listeners('newRTCSession').length === 0) {
          request.reply(405);
          return;
        }
      }

      var dialog;
      var session; // Initial Request.

      if (!request.to_tag) {
        switch (method) {
          case JsSIP_C.INVITE:
            if (window.RTCPeerConnection) {
              // TODO
              if (request.hasHeader('replaces')) {
                var replaces = request.replaces;
                dialog = this._findDialog(replaces.call_id, replaces.from_tag, replaces.to_tag);

                if (dialog) {
                  session = dialog.owner;

                  if (!session.isEnded()) {
                    session.receiveRequest(request);
                  } else {
                    request.reply(603);
                  }
                } else {
                  request.reply(481);
                }
              } else {
                session = new RTCSession(this);
                session.init_incoming(request);
              }
            } else {
              logger.warn('INVITE received but WebRTC is not supported');
              request.reply(488);
            }

            break;

          case JsSIP_C.BYE:
            // Out of dialog BYE received.
            request.reply(481);
            break;

          case JsSIP_C.CANCEL:
            session = this._findSession(request);

            if (session) {
              session.receiveRequest(request);
            } else {
              logger.debug('received CANCEL request for a non existent session');
            }

            break;

          case JsSIP_C.ACK:
            /* Absorb it.
             * ACK request without a corresponding Invite Transaction
             * and without To tag.
             */
            break;

          case JsSIP_C.NOTIFY:
            // Receive new sip event.
            this.emit('sipEvent', {
              event: request.event,
              request: request
            });
            request.reply(200);
            break;

          default:
            request.reply(405);
            break;
        }
      } // In-dialog request.
      else {
          dialog = this._findDialog(request.call_id, request.from_tag, request.to_tag);

          if (dialog) {
            dialog.receiveRequest(request);
          } else if (method === JsSIP_C.NOTIFY) {
            session = this._findSession(request);

            if (session) {
              session.receiveRequest(request);
            } else {
              logger.debug('received NOTIFY request for a non existent subscription');
              request.reply(481, 'Subscription does not exist');
            }
          }
          /* RFC3261 12.2.2
           * Request with to tag, but no matching dialog found.
           * Exception: ACK for an Invite request for which a dialog has not
           * been created.
           */
          else if (method !== JsSIP_C.ACK) {
              request.reply(481);
            }
        }
    } // =================
    // Utils.
    // =================

    /**
     * Get the session to which the request belongs to, if any.
     */

  }, {
    key: "_findSession",
    value: function _findSession(_ref) {
      var call_id = _ref.call_id,
          from_tag = _ref.from_tag,
          to_tag = _ref.to_tag;
      var sessionIDa = call_id + from_tag;
      var sessionA = this._sessions[sessionIDa];
      var sessionIDb = call_id + to_tag;
      var sessionB = this._sessions[sessionIDb];

      if (sessionA) {
        return sessionA;
      } else if (sessionB) {
        return sessionB;
      } else {
        return null;
      }
    }
    /**
     * Get the dialog to which the request belongs to, if any.
     */

  }, {
    key: "_findDialog",
    value: function _findDialog(call_id, from_tag, to_tag) {
      var id = call_id + from_tag + to_tag;
      var dialog = this._dialogs[id];

      if (dialog) {
        return dialog;
      } else {
        id = call_id + to_tag + from_tag;
        dialog = this._dialogs[id];

        if (dialog) {
          return dialog;
        } else {
          return null;
        }
      }
    }
  }, {
    key: "_loadConfig",
    value: function _loadConfig(configuration) {
      // Check and load the given configuration.
      try {
        config.load(this._configuration, configuration);
      } catch (e) {
        throw e;
      } // Post Configuration Process.
      // Allow passing 0 number as display_name.


      if (this._configuration.display_name === 0) {
        this._configuration.display_name = '0';
      } // Instance-id for GRUU.


      if (!this._configuration.instance_id) {
        this._configuration.instance_id = Utils.newUUID();
      } // Jssip_id instance parameter. Static random tag of length 5.


      this._configuration.jssip_id = Utils.createRandomToken(5); // String containing this._configuration.uri without scheme and user.

      var hostport_params = this._configuration.uri.clone();

      hostport_params.user = null;
      this._configuration.hostport_params = hostport_params.toString().replace(/^sip:/i, ''); // Transport.

      try {
        this._transport = new Transport(this._configuration.sockets, {
          // Recovery options.
          max_interval: this._configuration.connection_recovery_max_interval,
          min_interval: this._configuration.connection_recovery_min_interval
        }); // Transport event callbacks.

        this._transport.onconnecting = onTransportConnecting.bind(this);
        this._transport.onconnect = onTransportConnect.bind(this);
        this._transport.ondisconnect = onTransportDisconnect.bind(this);
        this._transport.ondata = onTransportData.bind(this);
      } catch (e) {
        logger.warn(e);
        throw new Exceptions.ConfigurationError('sockets', this._configuration.sockets);
      } // Remove sockets instance from configuration object.


      delete this._configuration.sockets; // Check whether authorization_user is explicitly defined.
      // Take 'this._configuration.uri.user' value if not.

      if (!this._configuration.authorization_user) {
        this._configuration.authorization_user = this._configuration.uri.user;
      } // If no 'registrar_server' is set use the 'uri' value without user portion and
      // without URI params/headers.


      if (!this._configuration.registrar_server) {
        var registrar_server = this._configuration.uri.clone();

        registrar_server.user = null;
        registrar_server.clearParams();
        registrar_server.clearHeaders();
        this._configuration.registrar_server = registrar_server;
      } // User no_answer_timeout.


      this._configuration.no_answer_timeout *= 1000; // Via Host.

      if (this._configuration.contact_uri) {
        this._configuration.via_host = this._configuration.contact_uri.host;
      } // Contact URI.
      else {
          this._configuration.contact_uri = new URI('sip', Utils.createRandomToken(8), this._configuration.via_host, null, {
            transport: 'ws'
          });
        }

      this._contact = {
        pub_gruu: null,
        temp_gruu: null,
        uri: this._configuration.contact_uri,
        toString: function toString() {
          var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
          var anonymous = options.anonymous || null;
          var outbound = options.outbound || null;
          var contact = '<';

          if (anonymous) {
            contact += this.temp_gruu || 'sip:anonymous@anonymous.invalid;transport=ws';
          } else {
            contact += this.pub_gruu || this.uri.toString();
          }

          if (outbound && (anonymous ? !this.temp_gruu : !this.pub_gruu)) {
            contact += ';ob';
          }

          contact += '>';
          return contact;
        }
      }; // Seal the configuration.

      var writable_parameters = ['authorization_user', 'password', 'realm', 'ha1', 'authorization_jwt', 'display_name', 'register'];

      for (var parameter in this._configuration) {
        if (Object.prototype.hasOwnProperty.call(this._configuration, parameter)) {
          if (writable_parameters.indexOf(parameter) !== -1) {
            Object.defineProperty(this._configuration, parameter, {
              writable: true,
              configurable: false
            });
          } else {
            Object.defineProperty(this._configuration, parameter, {
              writable: false,
              configurable: false
            });
          }
        }
      }

      logger.debug('configuration parameters after validation:');

      for (var _parameter in this._configuration) {
        // Only show the user user configurable parameters.
        if (Object.prototype.hasOwnProperty.call(config.settings, _parameter)) {
          switch (_parameter) {
            case 'uri':
            case 'registrar_server':
              logger.debug("- ".concat(_parameter, ": ").concat(this._configuration[_parameter]));
              break;

            case 'password':
            case 'ha1':
            case 'authorization_jwt':
              logger.debug("- ".concat(_parameter, ": NOT SHOWN"));
              break;

            default:
              logger.debug("- ".concat(_parameter, ": ").concat(JSON.stringify(this._configuration[_parameter])));
          }
        }
      }

      return;
    }
  }, {
    key: "C",
    get: function get() {
      return C;
    }
  }, {
    key: "status",
    get: function get() {
      return this._status;
    }
  }, {
    key: "contact",
    get: function get() {
      return this._contact;
    }
  }, {
    key: "configuration",
    get: function get() {
      return this._configuration;
    }
  }, {
    key: "transport",
    get: function get() {
      return this._transport;
    }
  }]);

  return UA;
}(EventEmitter);
/**
 * Transport event handlers
 */
// Transport connecting event.


function onTransportConnecting(data) {
  this.emit('connecting', data);
} // Transport connected event.


function onTransportConnect(data) {
  if (this._status === C.STATUS_USER_CLOSED) {
    return;
  }

  this._status = C.STATUS_READY;
  this._error = null;
  this.emit('connected', data);

  if (this._dynConfiguration.register) {
    this._registrator.register();
  }
} // Transport disconnected event.


function onTransportDisconnect(data) {
  // Run _onTransportError_ callback on every client transaction using _transport_.
  var client_transactions = ['nict', 'ict', 'nist', 'ist'];

  for (var _i = 0, _client_transactions = client_transactions; _i < _client_transactions.length; _i++) {
    var type = _client_transactions[_i];

    for (var id in this._transactions[type]) {
      if (Object.prototype.hasOwnProperty.call(this._transactions[type], id)) {
        this._transactions[type][id].onTransportError();
      }
    }
  }

  this.emit('disconnected', data); // Call registrator _onTransportClosed_.

  this._registrator.onTransportClosed();

  if (this._status !== C.STATUS_USER_CLOSED) {
    this._status = C.STATUS_NOT_READY;
    this._error = C.NETWORK_ERROR;
  }
} // Transport data event.


function onTransportData(data) {
  var transport = data.transport;
  var message = data.message;
  message = Parser.parseMessage(message, this);

  if (!message) {
    return;
  }

  if (this._status === C.STATUS_USER_CLOSED && message instanceof SIPMessage.IncomingRequest) {
    return;
  } // Do some sanity check.


  if (!sanityCheck(message, this, transport)) {
    return;
  }

  if (message instanceof SIPMessage.IncomingRequest) {
    message.transport = transport;
    this.receiveRequest(message);
  } else if (message instanceof SIPMessage.IncomingResponse) {
    /* Unike stated in 18.1.2, if a response does not match
    * any transaction, it is discarded here and no passed to the core
    * in order to be discarded there.
    */
    var transaction;

    switch (message.method) {
      case JsSIP_C.INVITE:
        transaction = this._transactions.ict[message.via_branch];

        if (transaction) {
          transaction.receiveResponse(message);
        }

        break;

      case JsSIP_C.ACK:
        // Just in case ;-).
        break;

      default:
        transaction = this._transactions.nict[message.via_branch];

        if (transaction) {
          transaction.receiveResponse(message);
        }

        break;
    }
  }
}
},{"./Config":1,"./Constants":2,"./Exceptions":6,"./Logger":9,"./Message":10,"./Options":12,"./Parser":13,"./RTCSession":14,"./Registrator":19,"./SIPMessage":21,"./Transactions":24,"./Transport":25,"./URI":27,"./Utils":28,"./sanityCheck":30,"events":31}],27:[function(require,module,exports){
"use strict";

function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var JsSIP_C = require('./Constants');

var Utils = require('./Utils');

var Grammar = require('./Grammar');
/**
 * -param {String} [scheme]
 * -param {String} [user]
 * -param {String} host
 * -param {String} [port]
 * -param {Object} [parameters]
 * -param {Object} [headers]
 *
 */


module.exports = /*#__PURE__*/function () {
  _createClass(URI, null, [{
    key: "parse",

    /**
      * Parse the given string and returns a JsSIP.URI instance or undefined if
      * it is an invalid URI.
      */
    value: function parse(uri) {
      uri = Grammar.parse(uri, 'SIP_URI');

      if (uri !== -1) {
        return uri;
      } else {
        return undefined;
      }
    }
  }]);

  function URI(scheme, user, host, port) {
    var parameters = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
    var headers = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};

    _classCallCheck(this, URI);

    // Checks.
    if (!host) {
      throw new TypeError('missing or invalid "host" parameter');
    } // Initialize parameters.


    this._parameters = {};
    this._headers = {};
    this._scheme = scheme || JsSIP_C.SIP;
    this._user = user;
    this._host = host;
    this._port = port;

    for (var param in parameters) {
      if (Object.prototype.hasOwnProperty.call(parameters, param)) {
        this.setParam(param, parameters[param]);
      }
    }

    for (var header in headers) {
      if (Object.prototype.hasOwnProperty.call(headers, header)) {
        this.setHeader(header, headers[header]);
      }
    }
  }

  _createClass(URI, [{
    key: "setParam",
    value: function setParam(key, value) {
      if (key) {
        this._parameters[key.toLowerCase()] = typeof value === 'undefined' || value === null ? null : value.toString();
      }
    }
  }, {
    key: "getParam",
    value: function getParam(key) {
      if (key) {
        return this._parameters[key.toLowerCase()];
      }
    }
  }, {
    key: "hasParam",
    value: function hasParam(key) {
      if (key) {
        return this._parameters.hasOwnProperty(key.toLowerCase()) && true || false;
      }
    }
  }, {
    key: "deleteParam",
    value: function deleteParam(parameter) {
      parameter = parameter.toLowerCase();

      if (this._parameters.hasOwnProperty(parameter)) {
        var value = this._parameters[parameter];
        delete this._parameters[parameter];
        return value;
      }
    }
  }, {
    key: "clearParams",
    value: function clearParams() {
      this._parameters = {};
    }
  }, {
    key: "setHeader",
    value: function setHeader(name, value) {
      this._headers[Utils.headerize(name)] = Array.isArray(value) ? value : [value];
    }
  }, {
    key: "getHeader",
    value: function getHeader(name) {
      if (name) {
        return this._headers[Utils.headerize(name)];
      }
    }
  }, {
    key: "hasHeader",
    value: function hasHeader(name) {
      if (name) {
        return this._headers.hasOwnProperty(Utils.headerize(name)) && true || false;
      }
    }
  }, {
    key: "deleteHeader",
    value: function deleteHeader(header) {
      header = Utils.headerize(header);

      if (this._headers.hasOwnProperty(header)) {
        var value = this._headers[header];
        delete this._headers[header];
        return value;
      }
    }
  }, {
    key: "clearHeaders",
    value: function clearHeaders() {
      this._headers = {};
    }
  }, {
    key: "clone",
    value: function clone() {
      return new URI(this._scheme, this._user, this._host, this._port, JSON.parse(JSON.stringify(this._parameters)), JSON.parse(JSON.stringify(this._headers)));
    }
  }, {
    key: "toString",
    value: function toString() {
      var headers = [];
      var uri = "".concat(this._scheme, ":");

      if (this._user) {
        uri += "".concat(Utils.escapeUser(this._user), "@");
      }

      uri += this._host;

      if (this._port || this._port === 0) {
        uri += ":".concat(this._port);
      }

      for (var parameter in this._parameters) {
        if (Object.prototype.hasOwnProperty.call(this._parameters, parameter)) {
          uri += ";".concat(parameter);

          if (this._parameters[parameter] !== null) {
            uri += "=".concat(this._parameters[parameter]);
          }
        }
      }

      for (var header in this._headers) {
        if (Object.prototype.hasOwnProperty.call(this._headers, header)) {
          var _iterator = _createForOfIteratorHelper(this._headers[header]),
              _step;

          try {
            for (_iterator.s(); !(_step = _iterator.n()).done;) {
              var item = _step.value;
              headers.push("".concat(header, "=").concat(item));
            }
          } catch (err) {
            _iterator.e(err);
          } finally {
            _iterator.f();
          }
        }
      }

      if (headers.length > 0) {
        uri += "?".concat(headers.join('&'));
      }

      return uri;
    }
  }, {
    key: "toAor",
    value: function toAor(show_port) {
      var aor = "".concat(this._scheme, ":");

      if (this._user) {
        aor += "".concat(Utils.escapeUser(this._user), "@");
      }

      aor += this._host;

      if (show_port && (this._port || this._port === 0)) {
        aor += ":".concat(this._port);
      }

      return aor;
    }
  }, {
    key: "scheme",
    get: function get() {
      return this._scheme;
    },
    set: function set(value) {
      this._scheme = value.toLowerCase();
    }
  }, {
    key: "user",
    get: function get() {
      return this._user;
    },
    set: function set(value) {
      this._user = value;
    }
  }, {
    key: "host",
    get: function get() {
      return this._host;
    },
    set: function set(value) {
      this._host = value.toLowerCase();
    }
  }, {
    key: "port",
    get: function get() {
      return this._port;
    },
    set: function set(value) {
      this._port = value === 0 ? value : parseInt(value, 10) || null;
    }
  }]);

  return URI;
}();
},{"./Constants":2,"./Grammar":7,"./Utils":28}],28:[function(require,module,exports){
"use strict";

function _typeof(obj) { "@babel/helpers - typeof"; 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); }

function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

var JsSIP_C = require('./Constants');

var URI = require('./URI');

var Grammar = require('./Grammar');

exports.str_utf8_length = function (string) {
  return unescape(encodeURIComponent(string)).length;
}; // Used by 'hasMethods'.


var isFunction = exports.isFunction = function (fn) {
  if (fn !== undefined) {
    return Object.prototype.toString.call(fn) === '[object Function]' ? true : false;
  } else {
    return false;
  }
};

exports.isString = function (str) {
  if (str !== undefined) {
    return Object.prototype.toString.call(str) === '[object String]' ? true : false;
  } else {
    return false;
  }
};

exports.isDecimal = function (num) {
  return !isNaN(num) && parseFloat(num) === parseInt(num, 10);
};

exports.isEmpty = function (value) {
  return value === null || value === '' || value === undefined || Array.isArray(value) && value.length === 0 || typeof value === 'number' && isNaN(value);
};

exports.hasMethods = function (obj) {
  for (var _len = arguments.length, methodNames = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
    methodNames[_key - 1] = arguments[_key];
  }

  for (var _i = 0, _methodNames = methodNames; _i < _methodNames.length; _i++) {
    var methodName = _methodNames[_i];

    if (isFunction(obj[methodName])) {
      return false;
    }
  }

  return true;
}; // Used by 'newTag'.


var createRandomToken = exports.createRandomToken = function (size) {
  var base = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 32;
  var i,
      r,
      token = '';

  for (i = 0; i < size; i++) {
    r = Math.random() * base | 0;
    token += r.toString(base);
  }

  return token;
};

exports.newTag = function () {
  return createRandomToken(10);
}; // https://stackoverflow.com/users/109538/broofa.


exports.newUUID = function () {
  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);
  });
  return UUID;
};

exports.hostType = function (host) {
  if (!host) {
    return;
  } else {
    host = Grammar.parse(host, 'host');

    if (host !== -1) {
      return host.host_type;
    }
  }
};
/**
* Hex-escape a SIP URI user.
* Don't hex-escape ':' (%3A), '+' (%2B), '?' (%3F"), '/' (%2F).
*
* Used by 'normalizeTarget'.
*/


var escapeUser = exports.escapeUser = function (user) {
  return encodeURIComponent(decodeURIComponent(user)).replace(/%3A/ig, ':').replace(/%2B/ig, '+').replace(/%3F/ig, '?').replace(/%2F/ig, '/');
};
/**
* Normalize SIP URI.
* NOTE: It does not allow a SIP URI without username.
* Accepts 'sip', 'sips' and 'tel' URIs and convert them into 'sip'.
* Detects the domain part (if given) and properly hex-escapes the user portion.
* If the user portion has only 'tel' number symbols the user portion is clean of 'tel' visual separators.
*/


exports.normalizeTarget = function (target, domain) {
  // If no target is given then raise an error.
  if (!target) {
    return; // If a URI instance is given then return it.
  } else if (target instanceof URI) {
    return target; // If a string is given split it by '@':
    // - Last fragment is the desired domain.
    // - Otherwise append the given domain argument.
  } else if (typeof target === 'string') {
    var target_array = target.split('@');
    var target_user;
    var target_domain;

    switch (target_array.length) {
      case 1:
        if (!domain) {
          return;
        }

        target_user = target;
        target_domain = domain;
        break;

      case 2:
        target_user = target_array[0];
        target_domain = target_array[1];
        break;

      default:
        target_user = target_array.slice(0, target_array.length - 1).join('@');
        target_domain = target_array[target_array.length - 1];
    } // Remove the URI scheme (if present).


    target_user = target_user.replace(/^(sips?|tel):/i, ''); // Remove 'tel' visual separators if the user portion just contains 'tel' number symbols.

    if (/^[-.()]*\+?[0-9\-.()]+$/.test(target_user)) {
      target_user = target_user.replace(/[-.()]/g, '');
    } // Build the complete SIP URI.


    target = "".concat(JsSIP_C.SIP, ":").concat(escapeUser(target_user), "@").concat(target_domain); // Finally parse the resulting URI.

    var uri;

    if (uri = URI.parse(target)) {
      return uri;
    } else {
      return;
    }
  } else {
    return;
  }
};

exports.headerize = function (string) {
  var exceptions = {
    'Call-Id': 'Call-ID',
    'Cseq': 'CSeq',
    'Www-Authenticate': 'WWW-Authenticate'
  };
  var name = string.toLowerCase().replace(/_/g, '-').split('-');
  var hname = '';
  var parts = name.length;
  var part;

  for (part = 0; part < parts; part++) {
    if (part !== 0) {
      hname += '-';
    }

    hname += name[part].charAt(0).toUpperCase() + name[part].substring(1);
  }

  if (exceptions[hname]) {
    hname = exceptions[hname];
  }

  return hname;
};

exports.sipErrorCause = function (status_code) {
  for (var cause in JsSIP_C.SIP_ERROR_CAUSES) {
    if (JsSIP_C.SIP_ERROR_CAUSES[cause].indexOf(status_code) !== -1) {
      return JsSIP_C.causes[cause];
    }
  }

  return JsSIP_C.causes.SIP_FAILURE_CODE;
};
/**
* Generate a random Test-Net IP (https://tools.ietf.org/html/rfc5735)
*/


exports.getRandomTestNetIP = function () {
  function getOctet(from, to) {
    return Math.floor(Math.random() * (to - from + 1) + from);
  }

  return "192.0.2.".concat(getOctet(1, 254));
}; // MD5 (Message-Digest Algorithm) https://www.webtoolkit.info.


exports.calculateMD5 = function (string) {
  function rotateLeft(lValue, iShiftBits) {
    return lValue << iShiftBits | lValue >>> 32 - iShiftBits;
  }

  function addUnsigned(lX, lY) {
    var lX8 = lX & 0x80000000;
    var lY8 = lY & 0x80000000;
    var lX4 = lX & 0x40000000;
    var lY4 = lY & 0x40000000;
    var lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);

    if (lX4 & lY4) {
      return lResult ^ 0x80000000 ^ lX8 ^ lY8;
    }

    if (lX4 | lY4) {
      if (lResult & 0x40000000) {
        return lResult ^ 0xC0000000 ^ lX8 ^ lY8;
      } else {
        return lResult ^ 0x40000000 ^ lX8 ^ lY8;
      }
    } else {
      return lResult ^ lX8 ^ lY8;
    }
  }

  function doF(x, y, z) {
    return x & y | ~x & z;
  }

  function doG(x, y, z) {
    return x & z | y & ~z;
  }

  function doH(x, y, z) {
    return x ^ y ^ z;
  }

  function doI(x, y, z) {
    return y ^ (x | ~z);
  }

  function doFF(a, b, c, d, x, s, ac) {
    a = addUnsigned(a, addUnsigned(addUnsigned(doF(b, c, d), x), ac));
    return addUnsigned(rotateLeft(a, s), b);
  }

  function doGG(a, b, c, d, x, s, ac) {
    a = addUnsigned(a, addUnsigned(addUnsigned(doG(b, c, d), x), ac));
    return addUnsigned(rotateLeft(a, s), b);
  }

  function doHH(a, b, c, d, x, s, ac) {
    a = addUnsigned(a, addUnsigned(addUnsigned(doH(b, c, d), x), ac));
    return addUnsigned(rotateLeft(a, s), b);
  }

  function doII(a, b, c, d, x, s, ac) {
    a = addUnsigned(a, addUnsigned(addUnsigned(doI(b, c, d), x), ac));
    return addUnsigned(rotateLeft(a, s), b);
  }

  function convertToWordArray(str) {
    var lWordCount;
    var lMessageLength = str.length;
    var lNumberOfWords_temp1 = lMessageLength + 8;
    var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - lNumberOfWords_temp1 % 64) / 64;
    var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
    var lWordArray = new Array(lNumberOfWords - 1);
    var lBytePosition = 0;
    var lByteCount = 0;

    while (lByteCount < lMessageLength) {
      lWordCount = (lByteCount - lByteCount % 4) / 4;
      lBytePosition = lByteCount % 4 * 8;
      lWordArray[lWordCount] = lWordArray[lWordCount] | str.charCodeAt(lByteCount) << lBytePosition;
      lByteCount++;
    }

    lWordCount = (lByteCount - lByteCount % 4) / 4;
    lBytePosition = lByteCount % 4 * 8;
    lWordArray[lWordCount] = lWordArray[lWordCount] | 0x80 << lBytePosition;
    lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
    lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
    return lWordArray;
  }

  function wordToHex(lValue) {
    var wordToHexValue = '',
        wordToHexValue_temp = '',
        lByte,
        lCount;

    for (lCount = 0; lCount <= 3; lCount++) {
      lByte = lValue >>> lCount * 8 & 255;
      wordToHexValue_temp = "0".concat(lByte.toString(16));
      wordToHexValue = wordToHexValue + wordToHexValue_temp.substr(wordToHexValue_temp.length - 2, 2);
    }

    return wordToHexValue;
  }

  function utf8Encode(str) {
    str = str.replace(/\r\n/g, '\n');
    var utftext = '';

    for (var n = 0; n < str.length; n++) {
      var _c = str.charCodeAt(n);

      if (_c < 128) {
        utftext += String.fromCharCode(_c);
      } else if (_c > 127 && _c < 2048) {
        utftext += String.fromCharCode(_c >> 6 | 192);
        utftext += String.fromCharCode(_c & 63 | 128);
      } else {
        utftext += String.fromCharCode(_c >> 12 | 224);
        utftext += String.fromCharCode(_c >> 6 & 63 | 128);
        utftext += String.fromCharCode(_c & 63 | 128);
      }
    }

    return utftext;
  }

  var x = [];
  var k, AA, BB, CC, DD, a, b, c, d;
  var S11 = 7,
      S12 = 12,
      S13 = 17,
      S14 = 22;
  var S21 = 5,
      S22 = 9,
      S23 = 14,
      S24 = 20;
  var S31 = 4,
      S32 = 11,
      S33 = 16,
      S34 = 23;
  var S41 = 6,
      S42 = 10,
      S43 = 15,
      S44 = 21;
  string = utf8Encode(string);
  x = convertToWordArray(string);
  a = 0x67452301;
  b = 0xEFCDAB89;
  c = 0x98BADCFE;
  d = 0x10325476;

  for (k = 0; k < x.length; k += 16) {
    AA = a;
    BB = b;
    CC = c;
    DD = d;
    a = doFF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
    d = doFF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
    c = doFF(c, d, a, b, x[k + 2], S13, 0x242070DB);
    b = doFF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
    a = doFF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
    d = doFF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
    c = doFF(c, d, a, b, x[k + 6], S13, 0xA8304613);
    b = doFF(b, c, d, a, x[k + 7], S14, 0xFD469501);
    a = doFF(a, b, c, d, x[k + 8], S11, 0x698098D8);
    d = doFF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
    c = doFF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
    b = doFF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
    a = doFF(a, b, c, d, x[k + 12], S11, 0x6B901122);
    d = doFF(d, a, b, c, x[k + 13], S12, 0xFD987193);
    c = doFF(c, d, a, b, x[k + 14], S13, 0xA679438E);
    b = doFF(b, c, d, a, x[k + 15], S14, 0x49B40821);
    a = doGG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
    d = doGG(d, a, b, c, x[k + 6], S22, 0xC040B340);
    c = doGG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
    b = doGG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
    a = doGG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
    d = doGG(d, a, b, c, x[k + 10], S22, 0x2441453);
    c = doGG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
    b = doGG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
    a = doGG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
    d = doGG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
    c = doGG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
    b = doGG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
    a = doGG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
    d = doGG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
    c = doGG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
    b = doGG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
    a = doHH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
    d = doHH(d, a, b, c, x[k + 8], S32, 0x8771F681);
    c = doHH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
    b = doHH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
    a = doHH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
    d = doHH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
    c = doHH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
    b = doHH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
    a = doHH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
    d = doHH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
    c = doHH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
    b = doHH(b, c, d, a, x[k + 6], S34, 0x4881D05);
    a = doHH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
    d = doHH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
    c = doHH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
    b = doHH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
    a = doII(a, b, c, d, x[k + 0], S41, 0xF4292244);
    d = doII(d, a, b, c, x[k + 7], S42, 0x432AFF97);
    c = doII(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
    b = doII(b, c, d, a, x[k + 5], S44, 0xFC93A039);
    a = doII(a, b, c, d, x[k + 12], S41, 0x655B59C3);
    d = doII(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
    c = doII(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
    b = doII(b, c, d, a, x[k + 1], S44, 0x85845DD1);
    a = doII(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
    d = doII(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
    c = doII(c, d, a, b, x[k + 6], S43, 0xA3014314);
    b = doII(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
    a = doII(a, b, c, d, x[k + 4], S41, 0xF7537E82);
    d = doII(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
    c = doII(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
    b = doII(b, c, d, a, x[k + 9], S44, 0xEB86D391);
    a = addUnsigned(a, AA);
    b = addUnsigned(b, BB);
    c = addUnsigned(c, CC);
    d = addUnsigned(d, DD);
  }

  var temp = wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d);
  return temp.toLowerCase();
};

exports.closeMediaStream = function (stream) {
  if (!stream) {
    return;
  } // Latest spec states that MediaStream has no stop() method and instead must
  // call stop() on every MediaStreamTrack.


  try {
    var tracks;

    if (stream.getTracks) {
      tracks = stream.getTracks();

      var _iterator = _createForOfIteratorHelper(tracks),
          _step;

      try {
        for (_iterator.s(); !(_step = _iterator.n()).done;) {
          var track = _step.value;
          track.stop();
        }
      } catch (err) {
        _iterator.e(err);
      } finally {
        _iterator.f();
      }
    } else {
      tracks = stream.getAudioTracks();

      var _iterator2 = _createForOfIteratorHelper(tracks),
          _step2;

      try {
        for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
          var _track = _step2.value;

          _track.stop();
        }
      } catch (err) {
        _iterator2.e(err);
      } finally {
        _iterator2.f();
      }

      tracks = stream.getVideoTracks();

      var _iterator3 = _createForOfIteratorHelper(tracks),
          _step3;

      try {
        for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
          var _track2 = _step3.value;

          _track2.stop();
        }
      } catch (err) {
        _iterator3.e(err);
      } finally {
        _iterator3.f();
      }
    }
  } catch (error) {
    // Deprecated by the spec, but still in use.
    // NOTE: In Temasys IE plugin stream.stop is a callable 'object'.
    if (typeof stream.stop === 'function' || _typeof(stream.stop) === 'object') {
      stream.stop();
    }
  }
};

exports.cloneArray = function (array) {
  return array && array.slice() || [];
};

exports.cloneObject = function (obj) {
  var fallback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  return obj && Object.assign({}, obj) || fallback;
};
},{"./Constants":2,"./Grammar":7,"./URI":27}],29:[function(require,module,exports){
"use strict";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var Logger = require('./Logger');

var Grammar = require('./Grammar');

var logger = new Logger('WebSocketInterface');

module.exports = /*#__PURE__*/function () {
  function WebSocketInterface(url) {
    _classCallCheck(this, WebSocketInterface);

    logger.debug('new() [url:"%s"]', url);
    this._url = url;
    this._sip_uri = null;
    this._via_transport = null;
    this._ws = null;
    var parsed_url = Grammar.parse(url, 'absoluteURI');

    if (parsed_url === -1) {
      logger.warn("invalid WebSocket URI: ".concat(url));
      throw new TypeError("Invalid argument: ".concat(url));
    } else if (parsed_url.scheme !== 'wss' && parsed_url.scheme !== 'ws') {
      logger.warn("invalid WebSocket URI scheme: ".concat(parsed_url.scheme));
      throw new TypeError("Invalid argument: ".concat(url));
    } else {
      this._sip_uri = "sip:".concat(parsed_url.host).concat(parsed_url.port ? ":".concat(parsed_url.port) : '', ";transport=ws");
      this._via_transport = parsed_url.scheme.toUpperCase();
    }
  }

  _createClass(WebSocketInterface, [{
    key: "connect",
    value: function connect() {
      logger.debug('connect()');

      if (this.isConnected()) {
        logger.debug("WebSocket ".concat(this._url, " is already connected"));
        return;
      } else if (this.isConnecting()) {
        logger.debug("WebSocket ".concat(this._url, " is connecting"));
        return;
      }

      if (this._ws) {
        this.disconnect();
      }

      logger.debug("connecting to WebSocket ".concat(this._url));

      try {
        this._ws = new WebSocket(this._url, 'sip');
        this._ws.binaryType = 'arraybuffer';
        this._ws.onopen = this._onOpen.bind(this);
        this._ws.onclose = this._onClose.bind(this);
        this._ws.onmessage = this._onMessage.bind(this);
        this._ws.onerror = this._onError.bind(this);
      } catch (e) {
        this._onError(e);
      }
    }
  }, {
    key: "disconnect",
    value: function disconnect() {
      logger.debug('disconnect()');

      if (this._ws) {
        // Unbind websocket event callbacks.
        this._ws.onopen = function () {};

        this._ws.onclose = function () {};

        this._ws.onmessage = function () {};

        this._ws.onerror = function () {};

        this._ws.close();

        this._ws = null;
      }
    }
  }, {
    key: "send",
    value: function send(message) {
      logger.debug('send()');

      if (this.isConnected()) {
        this._ws.send(message);

        return true;
      } else {
        logger.warn('unable to send message, WebSocket is not open');
        return false;
      }
    }
  }, {
    key: "isConnected",
    value: function isConnected() {
      return this._ws && this._ws.readyState === this._ws.OPEN;
    }
  }, {
    key: "isConnecting",
    value: function isConnecting() {
      return this._ws && this._ws.readyState === this._ws.CONNECTING;
    }
    /**
     * WebSocket Event Handlers
     */

  }, {
    key: "_onOpen",
    value: function _onOpen() {
      logger.debug("WebSocket ".concat(this._url, " connected"));
      this.onconnect();
    }
  }, {
    key: "_onClose",
    value: function _onClose(_ref) {
      var wasClean = _ref.wasClean,
          code = _ref.code,
          reason = _ref.reason;
      logger.debug("WebSocket ".concat(this._url, " closed"));

      if (wasClean === false) {
        logger.debug('WebSocket abrupt disconnection');
      }

      this.ondisconnect(!wasClean, code, reason);
    }
  }, {
    key: "_onMessage",
    value: function _onMessage(_ref2) {
      var data = _ref2.data;
      logger.debug('received WebSocket message');
      this.ondata(data);
    }
  }, {
    key: "_onError",
    value: function _onError(e) {
      logger.warn("WebSocket ".concat(this._url, " error: "), e);
    }
  }, {
    key: "via_transport",
    get: function get() {
      return this._via_transport;
    },
    set: function set(value) {
      this._via_transport = value.toUpperCase();
    }
  }, {
    key: "sip_uri",
    get: function get() {
      return this._sip_uri;
    }
  }, {
    key: "url",
    get: function get() {
      return this._url;
    }
  }]);

  return WebSocketInterface;
}();
},{"./Grammar":7,"./Logger":9}],30:[function(require,module,exports){
"use strict";

function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

var Logger = require('./Logger');

var JsSIP_C = require('./Constants');

var SIPMessage = require('./SIPMessage');

var Utils = require('./Utils');

var logger = new Logger('sanityCheck'); // Checks for requests and responses.

var all = [minimumHeaders]; // Checks for requests.

var requests = [rfc3261_8_2_2_1, rfc3261_16_3_4, rfc3261_18_3_request, rfc3261_8_2_2_2]; // Checks for responses.

var responses = [rfc3261_8_1_3_3, rfc3261_18_3_response]; // local variables.

var message;
var ua;
var transport;

module.exports = function (m, u, t) {
  message = m;
  ua = u;
  transport = t;

  var _iterator = _createForOfIteratorHelper(all),
      _step;

  try {
    for (_iterator.s(); !(_step = _iterator.n()).done;) {
      var _check2 = _step.value;

      if (_check2() === false) {
        return false;
      }
    }
  } catch (err) {
    _iterator.e(err);
  } finally {
    _iterator.f();
  }

  if (message instanceof SIPMessage.IncomingRequest) {
    var _iterator2 = _createForOfIteratorHelper(requests),
        _step2;

    try {
      for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
        var check = _step2.value;

        if (check() === false) {
          return false;
        }
      }
    } catch (err) {
      _iterator2.e(err);
    } finally {
      _iterator2.f();
    }
  } else if (message instanceof SIPMessage.IncomingResponse) {
    var _iterator3 = _createForOfIteratorHelper(responses),
        _step3;

    try {
      for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
        var _check = _step3.value;

        if (_check() === false) {
          return false;
        }
      }
    } catch (err) {
      _iterator3.e(err);
    } finally {
      _iterator3.f();
    }
  } // Everything is OK.


  return true;
};
/*
 * Sanity Check for incoming Messages
 *
 * Requests:
 *  - _rfc3261_8_2_2_1_ Receive a Request with a non supported URI scheme
 *  - _rfc3261_16_3_4_ Receive a Request already sent by us
 *   Does not look at via sent-by but at jssip_id, which is inserted as
 *   a prefix in all initial requests generated by the ua
 *  - _rfc3261_18_3_request_ Body Content-Length
 *  - _rfc3261_8_2_2_2_ Merged Requests
 *
 * Responses:
 *  - _rfc3261_8_1_3_3_ Multiple Via headers
 *  - _rfc3261_18_3_response_ Body Content-Length
 *
 * All:
 *  - Minimum headers in a SIP message
 */
// Sanity Check functions for requests.


function rfc3261_8_2_2_1() {
  if (message.s('to').uri.scheme !== 'sip') {
    reply(416);
    return false;
  }
}

function rfc3261_16_3_4() {
  if (!message.to_tag) {
    if (message.call_id.substr(0, 5) === ua.configuration.jssip_id) {
      reply(482);
      return false;
    }
  }
}

function rfc3261_18_3_request() {
  var len = Utils.str_utf8_length(message.body);
  var contentLength = message.getHeader('content-length');

  if (len < contentLength) {
    reply(400);
    return false;
  }
}

function rfc3261_8_2_2_2() {
  var fromTag = message.from_tag;
  var call_id = message.call_id;
  var cseq = message.cseq;
  var tr; // Accept any in-dialog request.

  if (message.to_tag) {
    return;
  } // INVITE request.


  if (message.method === JsSIP_C.INVITE) {
    // If the branch matches the key of any IST then assume it is a retransmission
    // and ignore the INVITE.
    // TODO: we should reply the last response.
    if (ua._transactions.ist[message.via_branch]) {
      return false;
    } // Otherwise check whether it is a merged request.
    else {
        for (var transaction in ua._transactions.ist) {
          if (Object.prototype.hasOwnProperty.call(ua._transactions.ist, transaction)) {
            tr = ua._transactions.ist[transaction];

            if (tr.request.from_tag === fromTag && tr.request.call_id === call_id && tr.request.cseq === cseq) {
              reply(482);
              return false;
            }
          }
        }
      }
  } // Non INVITE request.
  // If the branch matches the key of any NIST then assume it is a retransmission
  // and ignore the request.
  // TODO: we should reply the last response.
  else if (ua._transactions.nist[message.via_branch]) {
      return false;
    } // Otherwise check whether it is a merged request.
    else {
        for (var _transaction in ua._transactions.nist) {
          if (Object.prototype.hasOwnProperty.call(ua._transactions.nist, _transaction)) {
            tr = ua._transactions.nist[_transaction];

            if (tr.request.from_tag === fromTag && tr.request.call_id === call_id && tr.request.cseq === cseq) {
              reply(482);
              return false;
            }
          }
        }
      }
} // Sanity Check functions for responses.


function rfc3261_8_1_3_3() {
  if (message.getHeaders('via').length > 1) {
    logger.debug('more than one Via header field present in the response, dropping the response');
    return false;
  }
}

function rfc3261_18_3_response() {
  var len = Utils.str_utf8_length(message.body),
      contentLength = message.getHeader('content-length');

  if (len < contentLength) {
    logger.debug('message body length is lower than the value in Content-Length header field, dropping the response');
    return false;
  }
} // Sanity Check functions for requests and responses.


function minimumHeaders() {
  var mandatoryHeaders = ['from', 'to', 'call_id', 'cseq', 'via'];

  for (var _i = 0, _mandatoryHeaders = mandatoryHeaders; _i < _mandatoryHeaders.length; _i++) {
    var header = _mandatoryHeaders[_i];

    if (!message.hasHeader(header)) {
      logger.debug("missing mandatory header field : ".concat(header, ", dropping the response"));
      return false;
    }
  }
} // Reply.


function reply(status_code) {
  var vias = message.getHeaders('via');
  var to;
  var response = "SIP/2.0 ".concat(status_code, " ").concat(JsSIP_C.REASON_PHRASE[status_code], "\r\n");

  var _iterator4 = _createForOfIteratorHelper(vias),
      _step4;

  try {
    for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
      var via = _step4.value;
      response += "Via: ".concat(via, "\r\n");
    }
  } catch (err) {
    _iterator4.e(err);
  } finally {
    _iterator4.f();
  }

  to = message.getHeader('To');

  if (!message.to_tag) {
    to += ";tag=".concat(Utils.newTag());
  }

  response += "To: ".concat(to, "\r\n");
  response += "From: ".concat(message.getHeader('From'), "\r\n");
  response += "Call-ID: ".concat(message.call_id, "\r\n");
  response += "CSeq: ".concat(message.cseq, " ").concat(message.method, "\r\n");
  response += '\r\n';
  transport.send(response);
}
},{"./Constants":2,"./Logger":9,"./SIPMessage":21,"./Utils":28}],31:[function(require,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

var objectCreate = Object.create || objectCreatePolyfill
var objectKeys = Object.keys || objectKeysPolyfill
var bind = Function.prototype.bind || functionBindPolyfill

function EventEmitter() {
  if (!this._events || !Object.prototype.hasOwnProperty.call(this, '_events')) {
    this._events = objectCreate(null);
    this._eventsCount = 0;
  }

  this._maxListeners = this._maxListeners || undefined;
}
module.exports = EventEmitter;

// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;

EventEmitter.prototype._events = undefined;
EventEmitter.prototype._maxListeners = undefined;

// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
var defaultMaxListeners = 10;

var hasDefineProperty;
try {
  var o = {};
  if (Object.defineProperty) Object.defineProperty(o, 'x', { value: 0 });
  hasDefineProperty = o.x === 0;
} catch (err) { hasDefineProperty = false }
if (hasDefineProperty) {
  Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
    enumerable: true,
    get: function() {
      return defaultMaxListeners;
    },
    set: function(arg) {
      // check whether the input is a positive number (whose value is zero or
      // greater and not a NaN).
      if (typeof arg !== 'number' || arg < 0 || arg !== arg)
        throw new TypeError('"defaultMaxListeners" must be a positive number');
      defaultMaxListeners = arg;
    }
  });
} else {
  EventEmitter.defaultMaxListeners = defaultMaxListeners;
}

// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
  if (typeof n !== 'number' || n < 0 || isNaN(n))
    throw new TypeError('"n" argument must be a positive number');
  this._maxListeners = n;
  return this;
};

function $getMaxListeners(that) {
  if (that._maxListeners === undefined)
    return EventEmitter.defaultMaxListeners;
  return that._maxListeners;
}

EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
  return $getMaxListeners(this);
};

// These standalone emit* functions are used to optimize calling of event
// handlers for fast cases because emit() itself often has a variable number of
// arguments and can be deoptimized because of that. These functions always have
// the same number of arguments and thus do not get deoptimized, so the code
// inside them can execute faster.
function emitNone(handler, isFn, self) {
  if (isFn)
    handler.call(self);
  else {
    var len = handler.length;
    var listeners = arrayClone(handler, len);
    for (var i = 0; i < len; ++i)
      listeners[i].call(self);
  }
}
function emitOne(handler, isFn, self, arg1) {
  if (isFn)
    handler.call(self, arg1);
  else {
    var len = handler.length;
    var listeners = arrayClone(handler, len);
    for (var i = 0; i < len; ++i)
      listeners[i].call(self, arg1);
  }
}
function emitTwo(handler, isFn, self, arg1, arg2) {
  if (isFn)
    handler.call(self, arg1, arg2);
  else {
    var len = handler.length;
    var listeners = arrayClone(handler, len);
    for (var i = 0; i < len; ++i)
      listeners[i].call(self, arg1, arg2);
  }
}
function emitThree(handler, isFn, self, arg1, arg2, arg3) {
  if (isFn)
    handler.call(self, arg1, arg2, arg3);
  else {
    var len = handler.length;
    var listeners = arrayClone(handler, len);
    for (var i = 0; i < len; ++i)
      listeners[i].call(self, arg1, arg2, arg3);
  }
}

function emitMany(handler, isFn, self, args) {
  if (isFn)
    handler.apply(self, args);
  else {
    var len = handler.length;
    var listeners = arrayClone(handler, len);
    for (var i = 0; i < len; ++i)
      listeners[i].apply(self, args);
  }
}

EventEmitter.prototype.emit = function emit(type) {
  var er, handler, len, args, i, events;
  var doError = (type === 'error');

  events = this._events;
  if (events)
    doError = (doError && events.error == null);
  else if (!doError)
    return false;

  // If there is no 'error' event listener then throw.
  if (doError) {
    if (arguments.length > 1)
      er = arguments[1];
    if (er instanceof Error) {
      throw er; // Unhandled 'error' event
    } else {
      // At least give some kind of context to the user
      var err = new Error('Unhandled "error" event. (' + er + ')');
      err.context = er;
      throw err;
    }
    return false;
  }

  handler = events[type];

  if (!handler)
    return false;

  var isFn = typeof handler === 'function';
  len = arguments.length;
  switch (len) {
      // fast cases
    case 1:
      emitNone(handler, isFn, this);
      break;
    case 2:
      emitOne(handler, isFn, this, arguments[1]);
      break;
    case 3:
      emitTwo(handler, isFn, this, arguments[1], arguments[2]);
      break;
    case 4:
      emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]);
      break;
      // slower
    default:
      args = new Array(len - 1);
      for (i = 1; i < len; i++)
        args[i - 1] = arguments[i];
      emitMany(handler, isFn, this, args);
  }

  return true;
};

function _addListener(target, type, listener, prepend) {
  var m;
  var events;
  var existing;

  if (typeof listener !== 'function')
    throw new TypeError('"listener" argument must be a function');

  events = target._events;
  if (!events) {
    events = target._events = objectCreate(null);
    target._eventsCount = 0;
  } else {
    // To avoid recursion in the case that type === "newListener"! Before
    // adding it to the listeners, first emit "newListener".
    if (events.newListener) {
      target.emit('newListener', type,
          listener.listener ? listener.listener : listener);

      // Re-assign `events` because a newListener handler could have caused the
      // this._events to be assigned to a new object
      events = target._events;
    }
    existing = events[type];
  }

  if (!existing) {
    // Optimize the case of one listener. Don't need the extra array object.
    existing = events[type] = listener;
    ++target._eventsCount;
  } else {
    if (typeof existing === 'function') {
      // Adding the second element, need to change to array.
      existing = events[type] =
          prepend ? [listener, existing] : [existing, listener];
    } else {
      // If we've already got an array, just append.
      if (prepend) {
        existing.unshift(listener);
      } else {
        existing.push(listener);
      }
    }

    // Check for listener leak
    if (!existing.warned) {
      m = $getMaxListeners(target);
      if (m && m > 0 && existing.length > m) {
        existing.warned = true;
        var w = new Error('Possible EventEmitter memory leak detected. ' +
            existing.length + ' "' + String(type) + '" listeners ' +
            'added. Use emitter.setMaxListeners() to ' +
            'increase limit.');
        w.name = 'MaxListenersExceededWarning';
        w.emitter = target;
        w.type = type;
        w.count = existing.length;
        if (typeof console === 'object' && console.warn) {
          console.warn('%s: %s', w.name, w.message);
        }
      }
    }
  }

  return target;
}

EventEmitter.prototype.addListener = function addListener(type, listener) {
  return _addListener(this, type, listener, false);
};

EventEmitter.prototype.on = EventEmitter.prototype.addListener;

EventEmitter.prototype.prependListener =
    function prependListener(type, listener) {
      return _addListener(this, type, listener, true);
    };

function onceWrapper() {
  if (!this.fired) {
    this.target.removeListener(this.type, this.wrapFn);
    this.fired = true;
    switch (arguments.length) {
      case 0:
        return this.listener.call(this.target);
      case 1:
        return this.listener.call(this.target, arguments[0]);
      case 2:
        return this.listener.call(this.target, arguments[0], arguments[1]);
      case 3:
        return this.listener.call(this.target, arguments[0], arguments[1],
            arguments[2]);
      default:
        var args = new Array(arguments.length);
        for (var i = 0; i < args.length; ++i)
          args[i] = arguments[i];
        this.listener.apply(this.target, args);
    }
  }
}

function _onceWrap(target, type, listener) {
  var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
  var wrapped = bind.call(onceWrapper, state);
  wrapped.listener = listener;
  state.wrapFn = wrapped;
  return wrapped;
}

EventEmitter.prototype.once = function once(type, listener) {
  if (typeof listener !== 'function')
    throw new TypeError('"listener" argument must be a function');
  this.on(type, _onceWrap(this, type, listener));
  return this;
};

EventEmitter.prototype.prependOnceListener =
    function prependOnceListener(type, listener) {
      if (typeof listener !== 'function')
        throw new TypeError('"listener" argument must be a function');
      this.prependListener(type, _onceWrap(this, type, listener));
      return this;
    };

// Emits a 'removeListener' event if and only if the listener was removed.
EventEmitter.prototype.removeListener =
    function removeListener(type, listener) {
      var list, events, position, i, originalListener;

      if (typeof listener !== 'function')
        throw new TypeError('"listener" argument must be a function');

      events = this._events;
      if (!events)
        return this;

      list = events[type];
      if (!list)
        return this;

      if (list === listener || list.listener === listener) {
        if (--this._eventsCount === 0)
          this._events = objectCreate(null);
        else {
          delete events[type];
          if (events.removeListener)
            this.emit('removeListener', type, list.listener || listener);
        }
      } else if (typeof list !== 'function') {
        position = -1;

        for (i = list.length - 1; i >= 0; i--) {
          if (list[i] === listener || list[i].listener === listener) {
            originalListener = list[i].listener;
            position = i;
            break;
          }
        }

        if (position < 0)
          return this;

        if (position === 0)
          list.shift();
        else
          spliceOne(list, position);

        if (list.length === 1)
          events[type] = list[0];

        if (events.removeListener)
          this.emit('removeListener', type, originalListener || listener);
      }

      return this;
    };

EventEmitter.prototype.removeAllListeners =
    function removeAllListeners(type) {
      var listeners, events, i;

      events = this._events;
      if (!events)
        return this;

      // not listening for removeListener, no need to emit
      if (!events.removeListener) {
        if (arguments.length === 0) {
          this._events = objectCreate(null);
          this._eventsCount = 0;
        } else if (events[type]) {
          if (--this._eventsCount === 0)
            this._events = objectCreate(null);
          else
            delete events[type];
        }
        return this;
      }

      // emit removeListener for all listeners on all events
      if (arguments.length === 0) {
        var keys = objectKeys(events);
        var key;
        for (i = 0; i < keys.length; ++i) {
          key = keys[i];
          if (key === 'removeListener') continue;
          this.removeAllListeners(key);
        }
        this.removeAllListeners('removeListener');
        this._events = objectCreate(null);
        this._eventsCount = 0;
        return this;
      }

      listeners = events[type];

      if (typeof listeners === 'function') {
        this.removeListener(type, listeners);
      } else if (listeners) {
        // LIFO order
        for (i = listeners.length - 1; i >= 0; i--) {
          this.removeListener(type, listeners[i]);
        }
      }

      return this;
    };

function _listeners(target, type, unwrap) {
  var events = target._events;

  if (!events)
    return [];

  var evlistener = events[type];
  if (!evlistener)
    return [];

  if (typeof evlistener === 'function')
    return unwrap ? [evlistener.listener || evlistener] : [evlistener];

  return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
}

EventEmitter.prototype.listeners = function listeners(type) {
  return _listeners(this, type, true);
};

EventEmitter.prototype.rawListeners = function rawListeners(type) {
  return _listeners(this, type, false);
};

EventEmitter.listenerCount = function(emitter, type) {
  if (typeof emitter.listenerCount === 'function') {
    return emitter.listenerCount(type);
  } else {
    return listenerCount.call(emitter, type);
  }
};

EventEmitter.prototype.listenerCount = listenerCount;
function listenerCount(type) {
  var events = this._events;

  if (events) {
    var evlistener = events[type];

    if (typeof evlistener === 'function') {
      return 1;
    } else if (evlistener) {
      return evlistener.length;
    }
  }

  return 0;
}

EventEmitter.prototype.eventNames = function eventNames() {
  return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : [];
};

// About 1.5x faster than the two-arg version of Array#splice().
function spliceOne(list, index) {
  for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1)
    list[i] = list[k];
  list.pop();
}

function arrayClone(arr, n) {
  var copy = new Array(n);
  for (var i = 0; i < n; ++i)
    copy[i] = arr[i];
  return copy;
}

function unwrapListeners(arr) {
  var ret = new Array(arr.length);
  for (var i = 0; i < ret.length; ++i) {
    ret[i] = arr[i].listener || arr[i];
  }
  return ret;
}

function objectCreatePolyfill(proto) {
  var F = function() {};
  F.prototype = proto;
  return new F;
}
function objectKeysPolyfill(obj) {
  var keys = [];
  for (var k in obj) if (Object.prototype.hasOwnProperty.call(obj, k)) {
    keys.push(k);
  }
  return k;
}
function functionBindPolyfill(context) {
  var fn = this;
  return function () {
    return fn.apply(context, arguments);
  };
}

},{}],32:[function(require,module,exports){
(function (process){
/* eslint-env browser */

/**
 * This is the web browser implementation of `debug()`.
 */

exports.log = log;
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
exports.storage = localstorage();

/**
 * Colors.
 */

exports.colors = [
	'#0000CC',
	'#0000FF',
	'#0033CC',
	'#0033FF',
	'#0066CC',
	'#0066FF',
	'#0099CC',
	'#0099FF',
	'#00CC00',
	'#00CC33',
	'#00CC66',
	'#00CC99',
	'#00CCCC',
	'#00CCFF',
	'#3300CC',
	'#3300FF',
	'#3333CC',
	'#3333FF',
	'#3366CC',
	'#3366FF',
	'#3399CC',
	'#3399FF',
	'#33CC00',
	'#33CC33',
	'#33CC66',
	'#33CC99',
	'#33CCCC',
	'#33CCFF',
	'#6600CC',
	'#6600FF',
	'#6633CC',
	'#6633FF',
	'#66CC00',
	'#66CC33',
	'#9900CC',
	'#9900FF',
	'#9933CC',
	'#9933FF',
	'#99CC00',
	'#99CC33',
	'#CC0000',
	'#CC0033',
	'#CC0066',
	'#CC0099',
	'#CC00CC',
	'#CC00FF',
	'#CC3300',
	'#CC3333',
	'#CC3366',
	'#CC3399',
	'#CC33CC',
	'#CC33FF',
	'#CC6600',
	'#CC6633',
	'#CC9900',
	'#CC9933',
	'#CCCC00',
	'#CCCC33',
	'#FF0000',
	'#FF0033',
	'#FF0066',
	'#FF0099',
	'#FF00CC',
	'#FF00FF',
	'#FF3300',
	'#FF3333',
	'#FF3366',
	'#FF3399',
	'#FF33CC',
	'#FF33FF',
	'#FF6600',
	'#FF6633',
	'#FF9900',
	'#FF9933',
	'#FFCC00',
	'#FFCC33'
];

/**
 * Currently only WebKit-based Web Inspectors, Firefox >= v31,
 * and the Firebug extension (any Firefox version) are known
 * to support "%c" CSS customizations.
 *
 * TODO: add a `localStorage` variable to explicitly enable/disable colors
 */

// eslint-disable-next-line complexity
function useColors() {
	// NB: In an Electron preload script, document will be defined but not fully
	// initialized. Since we know we're in Chrome, we'll just detect this case
	// explicitly
	if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) {
		return true;
	}

	// Internet Explorer and Edge do not support colors.
	if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
		return false;
	}

	// Is webkit? http://stackoverflow.com/a/16459606/376773
	// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
	return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
		// Is firebug? http://stackoverflow.com/a/398120/376773
		(typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
		// Is firefox >= v31?
		// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
		(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
		// Double check webkit in userAgent just in case we are in a worker
		(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
}

/**
 * Colorize log arguments if enabled.
 *
 * @api public
 */

function formatArgs(args) {
	args[0] = (this.useColors ? '%c' : '') +
		this.namespace +
		(this.useColors ? ' %c' : ' ') +
		args[0] +
		(this.useColors ? '%c ' : ' ') +
		'+' + module.exports.humanize(this.diff);

	if (!this.useColors) {
		return;
	}

	const c = 'color: ' + this.color;
	args.splice(1, 0, c, 'color: inherit');

	// The final "%c" is somewhat tricky, because there could be other
	// arguments passed either before or after the %c, so we need to
	// figure out the correct index to insert the CSS into
	let index = 0;
	let lastC = 0;
	args[0].replace(/%[a-zA-Z%]/g, match => {
		if (match === '%%') {
			return;
		}
		index++;
		if (match === '%c') {
			// We only are interested in the *last* %c
			// (the user may have provided their own)
			lastC = index;
		}
	});

	args.splice(lastC, 0, c);
}

/**
 * Invokes `console.log()` when available.
 * No-op when `console.log` is not a "function".
 *
 * @api public
 */
function log(...args) {
	// This hackery is required for IE8/9, where
	// the `console.log` function doesn't have 'apply'
	return typeof console === 'object' &&
		console.log &&
		console.log(...args);
}

/**
 * Save `namespaces`.
 *
 * @param {String} namespaces
 * @api private
 */
function save(namespaces) {
	try {
		if (namespaces) {
			exports.storage.setItem('debug', namespaces);
		} else {
			exports.storage.removeItem('debug');
		}
	} catch (error) {
		// Swallow
		// XXX (@Qix-) should we be logging these?
	}
}

/**
 * Load `namespaces`.
 *
 * @return {String} returns the previously persisted debug modes
 * @api private
 */
function load() {
	let r;
	try {
		r = exports.storage.getItem('debug');
	} catch (error) {
		// Swallow
		// XXX (@Qix-) should we be logging these?
	}

	// If debug isn't set in LS, and we're in Electron, try to load $DEBUG
	if (!r && typeof process !== 'undefined' && 'env' in process) {
		r = process.env.DEBUG;
	}

	return r;
}

/**
 * Localstorage attempts to return the localstorage.
 *
 * This is necessary because safari throws
 * when a user disables cookies/localstorage
 * and you attempt to access it.
 *
 * @return {LocalStorage}
 * @api private
 */

function localstorage() {
	try {
		// TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context
		// The Browser also has localStorage in the global context.
		return localStorage;
	} catch (error) {
		// Swallow
		// XXX (@Qix-) should we be logging these?
	}
}

module.exports = require('./common')(exports);

const {formatters} = module.exports;

/**
 * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
 */

formatters.j = function (v) {
	try {
		return JSON.stringify(v);
	} catch (error) {
		return '[UnexpectedJSONParseError]: ' + error.message;
	}
};

}).call(this,require('_process'))
},{"./common":33,"_process":35}],33:[function(require,module,exports){

/**
 * This is the common logic for both the Node.js and web browser
 * implementations of `debug()`.
 */

function setup(env) {
	createDebug.debug = createDebug;
	createDebug.default = createDebug;
	createDebug.coerce = coerce;
	createDebug.disable = disable;
	createDebug.enable = enable;
	createDebug.enabled = enabled;
	createDebug.humanize = require('ms');

	Object.keys(env).forEach(key => {
		createDebug[key] = env[key];
	});

	/**
	* Active `debug` instances.
	*/
	createDebug.instances = [];

	/**
	* The currently active debug mode names, and names to skip.
	*/

	createDebug.names = [];
	createDebug.skips = [];

	/**
	* Map of special "%n" handling functions, for the debug "format" argument.
	*
	* Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".
	*/
	createDebug.formatters = {};

	/**
	* Selects a color for a debug namespace
	* @param {String} namespace The namespace string for the for the debug instance to be colored
	* @return {Number|String} An ANSI color code for the given namespace
	* @api private
	*/
	function selectColor(namespace) {
		let hash = 0;

		for (let i = 0; i < namespace.length; i++) {
			hash = ((hash << 5) - hash) + namespace.charCodeAt(i);
			hash |= 0; // Convert to 32bit integer
		}

		return createDebug.colors[Math.abs(hash) % createDebug.colors.length];
	}
	createDebug.selectColor = selectColor;

	/**
	* Create a debugger with the given `namespace`.
	*
	* @param {String} namespace
	* @return {Function}
	* @api public
	*/
	function createDebug(namespace) {
		let prevTime;

		function debug(...args) {
			// Disabled?
			if (!debug.enabled) {
				return;
			}

			const self = debug;

			// Set `diff` timestamp
			const curr = Number(new Date());
			const ms = curr - (prevTime || curr);
			self.diff = ms;
			self.prev = prevTime;
			self.curr = curr;
			prevTime = curr;

			args[0] = createDebug.coerce(args[0]);

			if (typeof args[0] !== 'string') {
				// Anything else let's inspect with %O
				args.unshift('%O');
			}

			// Apply any `formatters` transformations
			let index = 0;
			args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {
				// If we encounter an escaped % then don't increase the array index
				if (match === '%%') {
					return match;
				}
				index++;
				const formatter = createDebug.formatters[format];
				if (typeof formatter === 'function') {
					const val = args[index];
					match = formatter.call(self, val);

					// Now we need to remove `args[index]` since it's inlined in the `format`
					args.splice(index, 1);
					index--;
				}
				return match;
			});

			// Apply env-specific formatting (colors, etc.)
			createDebug.formatArgs.call(self, args);

			const logFn = self.log || createDebug.log;
			logFn.apply(self, args);
		}

		debug.namespace = namespace;
		debug.enabled = createDebug.enabled(namespace);
		debug.useColors = createDebug.useColors();
		debug.color = selectColor(namespace);
		debug.destroy = destroy;
		debug.extend = extend;
		// Debug.formatArgs = formatArgs;
		// debug.rawLog = rawLog;

		// env-specific initialization logic for debug instances
		if (typeof createDebug.init === 'function') {
			createDebug.init(debug);
		}

		createDebug.instances.push(debug);

		return debug;
	}

	function destroy() {
		const index = createDebug.instances.indexOf(this);
		if (index !== -1) {
			createDebug.instances.splice(index, 1);
			return true;
		}
		return false;
	}

	function extend(namespace, delimiter) {
		const newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace);
		newDebug.log = this.log;
		return newDebug;
	}

	/**
	* Enables a debug mode by namespaces. This can include modes
	* separated by a colon and wildcards.
	*
	* @param {String} namespaces
	* @api public
	*/
	function enable(namespaces) {
		createDebug.save(namespaces);

		createDebug.names = [];
		createDebug.skips = [];

		let i;
		const split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);
		const len = split.length;

		for (i = 0; i < len; i++) {
			if (!split[i]) {
				// ignore empty strings
				continue;
			}

			namespaces = split[i].replace(/\*/g, '.*?');

			if (namespaces[0] === '-') {
				createDebug.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
			} else {
				createDebug.names.push(new RegExp('^' + namespaces + '$'));
			}
		}

		for (i = 0; i < createDebug.instances.length; i++) {
			const instance = createDebug.instances[i];
			instance.enabled = createDebug.enabled(instance.namespace);
		}
	}

	/**
	* Disable debug output.
	*
	* @return {String} namespaces
	* @api public
	*/
	function disable() {
		const namespaces = [
			...createDebug.names.map(toNamespace),
			...createDebug.skips.map(toNamespace).map(namespace => '-' + namespace)
		].join(',');
		createDebug.enable('');
		return namespaces;
	}

	/**
	* Returns true if the given mode name is enabled, false otherwise.
	*
	* @param {String} name
	* @return {Boolean}
	* @api public
	*/
	function enabled(name) {
		if (name[name.length - 1] === '*') {
			return true;
		}

		let i;
		let len;

		for (i = 0, len = createDebug.skips.length; i < len; i++) {
			if (createDebug.skips[i].test(name)) {
				return false;
			}
		}

		for (i = 0, len = createDebug.names.length; i < len; i++) {
			if (createDebug.names[i].test(name)) {
				return true;
			}
		}

		return false;
	}

	/**
	* Convert regexp to namespace
	*
	* @param {RegExp} regxep
	* @return {String} namespace
	* @api private
	*/
	function toNamespace(regexp) {
		return regexp.toString()
			.substring(2, regexp.toString().length - 2)
			.replace(/\.\*\?$/, '*');
	}

	/**
	* Coerce `val`.
	*
	* @param {Mixed} val
	* @return {Mixed}
	* @api private
	*/
	function coerce(val) {
		if (val instanceof Error) {
			return val.stack || val.message;
		}
		return val;
	}

	createDebug.enable(createDebug.load());

	return createDebug;
}

module.exports = setup;

},{"ms":34}],34:[function(require,module,exports){
/**
 * Helpers.
 */

var s = 1000;
var m = s * 60;
var h = m * 60;
var d = h * 24;
var w = d * 7;
var y = d * 365.25;

/**
 * Parse or format the given `val`.
 *
 * Options:
 *
 *  - `long` verbose formatting [false]
 *
 * @param {String|Number} val
 * @param {Object} [options]
 * @throws {Error} throw an error if val is not a non-empty string or a number
 * @return {String|Number}
 * @api public
 */

module.exports = function(val, options) {
  options = options || {};
  var type = typeof val;
  if (type === 'string' && val.length > 0) {
    return parse(val);
  } else if (type === 'number' && isFinite(val)) {
    return options.long ? fmtLong(val) : fmtShort(val);
  }
  throw new Error(
    'val is not a non-empty string or a valid number. val=' +
      JSON.stringify(val)
  );
};

/**
 * Parse the given `str` and return milliseconds.
 *
 * @param {String} str
 * @return {Number}
 * @api private
 */

function parse(str) {
  str = String(str);
  if (str.length > 100) {
    return;
  }
  var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(
    str
  );
  if (!match) {
    return;
  }
  var n = parseFloat(match[1]);
  var type = (match[2] || 'ms').toLowerCase();
  switch (type) {
    case 'years':
    case 'year':
    case 'yrs':
    case 'yr':
    case 'y':
      return n * y;
    case 'weeks':
    case 'week':
    case 'w':
      return n * w;
    case 'days':
    case 'day':
    case 'd':
      return n * d;
    case 'hours':
    case 'hour':
    case 'hrs':
    case 'hr':
    case 'h':
      return n * h;
    case 'minutes':
    case 'minute':
    case 'mins':
    case 'min':
    case 'm':
      return n * m;
    case 'seconds':
    case 'second':
    case 'secs':
    case 'sec':
    case 's':
      return n * s;
    case 'milliseconds':
    case 'millisecond':
    case 'msecs':
    case 'msec':
    case 'ms':
      return n;
    default:
      return undefined;
  }
}

/**
 * Short format for `ms`.
 *
 * @param {Number} ms
 * @return {String}
 * @api private
 */

function fmtShort(ms) {
  var msAbs = Math.abs(ms);
  if (msAbs >= d) {
    return Math.round(ms / d) + 'd';
  }
  if (msAbs >= h) {
    return Math.round(ms / h) + 'h';
  }
  if (msAbs >= m) {
    return Math.round(ms / m) + 'm';
  }
  if (msAbs >= s) {
    return Math.round(ms / s) + 's';
  }
  return ms + 'ms';
}

/**
 * Long format for `ms`.
 *
 * @param {Number} ms
 * @return {String}
 * @api private
 */

function fmtLong(ms) {
  var msAbs = Math.abs(ms);
  if (msAbs >= d) {
    return plural(ms, msAbs, d, 'day');
  }
  if (msAbs >= h) {
    return plural(ms, msAbs, h, 'hour');
  }
  if (msAbs >= m) {
    return plural(ms, msAbs, m, 'minute');
  }
  if (msAbs >= s) {
    return plural(ms, msAbs, s, 'second');
  }
  return ms + ' ms';
}

/**
 * Pluralization helper.
 */

function plural(ms, msAbs, n, name) {
  var isPlural = msAbs >= n * 1.5;
  return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : '');
}

},{}],35:[function(require,module,exports){
// shim for using process in browser
var process = module.exports = {};

// cached from whatever global is present so that test runners that stub it
// don't break things.  But we need to wrap it in a try catch in case it is
// wrapped in strict mode code which doesn't define any globals.  It's inside a
// function because try/catches deoptimize in certain engines.

var cachedSetTimeout;
var cachedClearTimeout;

function defaultSetTimout() {
    throw new Error('setTimeout has not been defined');
}
function defaultClearTimeout () {
    throw new Error('clearTimeout has not been defined');
}
(function () {
    try {
        if (typeof setTimeout === 'function') {
            cachedSetTimeout = setTimeout;
        } else {
            cachedSetTimeout = defaultSetTimout;
        }
    } catch (e) {
        cachedSetTimeout = defaultSetTimout;
    }
    try {
        if (typeof clearTimeout === 'function') {
            cachedClearTimeout = clearTimeout;
        } else {
            cachedClearTimeout = defaultClearTimeout;
        }
    } catch (e) {
        cachedClearTimeout = defaultClearTimeout;
    }
} ())
function runTimeout(fun) {
    if (cachedSetTimeout === setTimeout) {
        //normal enviroments in sane situations
        return setTimeout(fun, 0);
    }
    // if setTimeout wasn't available but was latter defined
    if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
        cachedSetTimeout = setTimeout;
        return setTimeout(fun, 0);
    }
    try {
        // when when somebody has screwed with setTimeout but no I.E. maddness
        return cachedSetTimeout(fun, 0);
    } catch(e){
        try {
            // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
            return cachedSetTimeout.call(null, fun, 0);
        } catch(e){
            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
            return cachedSetTimeout.call(this, fun, 0);
        }
    }


}
function runClearTimeout(marker) {
    if (cachedClearTimeout === clearTimeout) {
        //normal enviroments in sane situations
        return clearTimeout(marker);
    }
    // if clearTimeout wasn't available but was latter defined
    if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
        cachedClearTimeout = clearTimeout;
        return clearTimeout(marker);
    }
    try {
        // when when somebody has screwed with setTimeout but no I.E. maddness
        return cachedClearTimeout(marker);
    } catch (e){
        try {
            // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally
            return cachedClearTimeout.call(null, marker);
        } catch (e){
            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
            // Some versions of I.E. have different rules for clearTimeout vs setTimeout
            return cachedClearTimeout.call(this, marker);
        }
    }



}
var queue = [];
var draining = false;
var currentQueue;
var queueIndex = -1;

function cleanUpNextTick() {
    if (!draining || !currentQueue) {
        return;
    }
    draining = false;
    if (currentQueue.length) {
        queue = currentQueue.concat(queue);
    } else {
        queueIndex = -1;
    }
    if (queue.length) {
        drainQueue();
    }
}

function drainQueue() {
    if (draining) {
        return;
    }
    var timeout = runTimeout(cleanUpNextTick);
    draining = true;

    var len = queue.length;
    while(len) {
        currentQueue = queue;
        queue = [];
        while (++queueIndex < len) {
            if (currentQueue) {
                currentQueue[queueIndex].run();
            }
        }
        queueIndex = -1;
        len = queue.length;
    }
    currentQueue = null;
    draining = false;
    runClearTimeout(timeout);
}

process.nextTick = function (fun) {
    var args = new Array(arguments.length - 1);
    if (arguments.length > 1) {
        for (var i = 1; i < arguments.length; i++) {
            args[i - 1] = arguments[i];
        }
    }
    queue.push(new Item(fun, args));
    if (queue.length === 1 && !draining) {
        runTimeout(drainQueue);
    }
};

// v8 likes predictible objects
function Item(fun, array) {
    this.fun = fun;
    this.array = array;
}
Item.prototype.run = function () {
    this.fun.apply(null, this.array);
};
process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];
process.version = ''; // empty string to avoid regexp issues
process.versions = {};

function noop() {}

process.on = noop;
process.addListener = noop;
process.once = noop;
process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;
process.prependListener = noop;
process.prependOnceListener = noop;

process.listeners = function (name) { return [] }

process.binding = function (name) {
    throw new Error('process.binding is not supported');
};

process.cwd = function () { return '/' };
process.chdir = function (dir) {
    throw new Error('process.chdir is not supported');
};
process.umask = function() { return 0; };

},{}],36:[function(require,module,exports){
var grammar = module.exports = {
  v: [{
    name: 'version',
    reg: /^(\d*)$/
  }],
  o: [{
    // o=- 20518 0 IN IP4 203.0.113.1
    // NB: sessionId will be a String in most cases because it is huge
    name: 'origin',
    reg: /^(\S*) (\d*) (\d*) (\S*) IP(\d) (\S*)/,
    names: ['username', 'sessionId', 'sessionVersion', 'netType', 'ipVer', 'address'],
    format: '%s %s %d %s IP%d %s'
  }],
  // default parsing of these only (though some of these feel outdated)
  s: [{ name: 'name' }],
  i: [{ name: 'description' }],
  u: [{ name: 'uri' }],
  e: [{ name: 'email' }],
  p: [{ name: 'phone' }],
  z: [{ name: 'timezones' }], // TODO: this one can actually be parsed properly...
  r: [{ name: 'repeats' }],   // TODO: this one can also be parsed properly
  // k: [{}], // outdated thing ignored
  t: [{
    // t=0 0
    name: 'timing',
    reg: /^(\d*) (\d*)/,
    names: ['start', 'stop'],
    format: '%d %d'
  }],
  c: [{
    // c=IN IP4 10.47.197.26
    name: 'connection',
    reg: /^IN IP(\d) (\S*)/,
    names: ['version', 'ip'],
    format: 'IN IP%d %s'
  }],
  b: [{
    // b=AS:4000
    push: 'bandwidth',
    reg: /^(TIAS|AS|CT|RR|RS):(\d*)/,
    names: ['type', 'limit'],
    format: '%s:%s'
  }],
  m: [{
    // m=video 51744 RTP/AVP 126 97 98 34 31
    // NB: special - pushes to session
    // TODO: rtp/fmtp should be filtered by the payloads found here?
    reg: /^(\w*) (\d*) ([\w/]*)(?: (.*))?/,
    names: ['type', 'port', 'protocol', 'payloads'],
    format: '%s %d %s %s'
  }],
  a: [
    {
      // a=rtpmap:110 opus/48000/2
      push: 'rtp',
      reg: /^rtpmap:(\d*) ([\w\-.]*)(?:\s*\/(\d*)(?:\s*\/(\S*))?)?/,
      names: ['payload', 'codec', 'rate', 'encoding'],
      format: function (o) {
        return (o.encoding)
          ? 'rtpmap:%d %s/%s/%s'
          : o.rate
            ? 'rtpmap:%d %s/%s'
            : 'rtpmap:%d %s';
      }
    },
    {
      // a=fmtp:108 profile-level-id=24;object=23;bitrate=64000
      // a=fmtp:111 minptime=10; useinbandfec=1
      push: 'fmtp',
      reg: /^fmtp:(\d*) ([\S| ]*)/,
      names: ['payload', 'config'],
      format: 'fmtp:%d %s'
    },
    {
      // a=control:streamid=0
      name: 'control',
      reg: /^control:(.*)/,
      format: 'control:%s'
    },
    {
      // a=rtcp:65179 IN IP4 193.84.77.194
      name: 'rtcp',
      reg: /^rtcp:(\d*)(?: (\S*) IP(\d) (\S*))?/,
      names: ['port', 'netType', 'ipVer', 'address'],
      format: function (o) {
        return (o.address != null)
          ? 'rtcp:%d %s IP%d %s'
          : 'rtcp:%d';
      }
    },
    {
      // a=rtcp-fb:98 trr-int 100
      push: 'rtcpFbTrrInt',
      reg: /^rtcp-fb:(\*|\d*) trr-int (\d*)/,
      names: ['payload', 'value'],
      format: 'rtcp-fb:%d trr-int %d'
    },
    {
      // a=rtcp-fb:98 nack rpsi
      push: 'rtcpFb',
      reg: /^rtcp-fb:(\*|\d*) ([\w-_]*)(?: ([\w-_]*))?/,
      names: ['payload', 'type', 'subtype'],
      format: function (o) {
        return (o.subtype != null)
          ? 'rtcp-fb:%s %s %s'
          : 'rtcp-fb:%s %s';
      }
    },
    {
      // a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
      // a=extmap:1/recvonly URI-gps-string
      // a=extmap:3 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:smpte-tc 25@600/24
      push: 'ext',
      reg: /^extmap:(\d+)(?:\/(\w+))?(?: (urn:ietf:params:rtp-hdrext:encrypt))? (\S*)(?: (\S*))?/,
      names: ['value', 'direction', 'encrypt-uri', 'uri', 'config'],
      format: function (o) {
        return (
          'extmap:%d' +
          (o.direction ? '/%s' : '%v') +
          (o['encrypt-uri'] ? ' %s' : '%v') +
          ' %s' +
          (o.config ? ' %s' : '')
        );
      }
    },
    {
      // a=extmap-allow-mixed
      name: 'extmapAllowMixed',
      reg: /^(extmap-allow-mixed)/
    },
    {
      // a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR|2^20|1:32
      push: 'crypto',
      reg: /^crypto:(\d*) ([\w_]*) (\S*)(?: (\S*))?/,
      names: ['id', 'suite', 'config', 'sessionConfig'],
      format: function (o) {
        return (o.sessionConfig != null)
          ? 'crypto:%d %s %s %s'
          : 'crypto:%d %s %s';
      }
    },
    {
      // a=setup:actpass
      name: 'setup',
      reg: /^setup:(\w*)/,
      format: 'setup:%s'
    },
    {
      // a=connection:new
      name: 'connectionType',
      reg: /^connection:(new|existing)/,
      format: 'connection:%s'
    },
    {
      // a=mid:1
      name: 'mid',
      reg: /^mid:([^\s]*)/,
      format: 'mid:%s'
    },
    {
      // a=msid:0c8b064d-d807-43b4-b434-f92a889d8587 98178685-d409-46e0-8e16-7ef0db0db64a
      name: 'msid',
      reg: /^msid:(.*)/,
      format: 'msid:%s'
    },
    {
      // a=ptime:20
      name: 'ptime',
      reg: /^ptime:(\d*(?:\.\d*)*)/,
      format: 'ptime:%d'
    },
    {
      // a=maxptime:60
      name: 'maxptime',
      reg: /^maxptime:(\d*(?:\.\d*)*)/,
      format: 'maxptime:%d'
    },
    {
      // a=sendrecv
      name: 'direction',
      reg: /^(sendrecv|recvonly|sendonly|inactive)/
    },
    {
      // a=ice-lite
      name: 'icelite',
      reg: /^(ice-lite)/
    },
    {
      // a=ice-ufrag:F7gI
      name: 'iceUfrag',
      reg: /^ice-ufrag:(\S*)/,
      format: 'ice-ufrag:%s'
    },
    {
      // a=ice-pwd:x9cml/YzichV2+XlhiMu8g
      name: 'icePwd',
      reg: /^ice-pwd:(\S*)/,
      format: 'ice-pwd:%s'
    },
    {
      // a=fingerprint:SHA-1 00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33
      name: 'fingerprint',
      reg: /^fingerprint:(\S*) (\S*)/,
      names: ['type', 'hash'],
      format: 'fingerprint:%s %s'
    },
    {
      // a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host
      // a=candidate:1162875081 1 udp 2113937151 192.168.34.75 60017 typ host generation 0 network-id 3 network-cost 10
      // a=candidate:3289912957 2 udp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 generation 0 network-id 3 network-cost 10
      // a=candidate:229815620 1 tcp 1518280447 192.168.150.19 60017 typ host tcptype active generation 0 network-id 3 network-cost 10
      // a=candidate:3289912957 2 tcp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 tcptype passive generation 0 network-id 3 network-cost 10
      push:'candidates',
      reg: /^candidate:(\S*) (\d*) (\S*) (\d*) (\S*) (\d*) typ (\S*)(?: raddr (\S*) rport (\d*))?(?: tcptype (\S*))?(?: generation (\d*))?(?: network-id (\d*))?(?: network-cost (\d*))?/,
      names: ['foundation', 'component', 'transport', 'priority', 'ip', 'port', 'type', 'raddr', 'rport', 'tcptype', 'generation', 'network-id', 'network-cost'],
      format: function (o) {
        var str = 'candidate:%s %d %s %d %s %d typ %s';

        str += (o.raddr != null) ? ' raddr %s rport %d' : '%v%v';

        // NB: candidate has three optional chunks, so %void middles one if it's missing
        str += (o.tcptype != null) ? ' tcptype %s' : '%v';

        if (o.generation != null) {
          str += ' generation %d';
        }

        str += (o['network-id'] != null) ? ' network-id %d' : '%v';
        str += (o['network-cost'] != null) ? ' network-cost %d' : '%v';
        return str;
      }
    },
    {
      // a=end-of-candidates (keep after the candidates line for readability)
      name: 'endOfCandidates',
      reg: /^(end-of-candidates)/
    },
    {
      // a=remote-candidates:1 203.0.113.1 54400 2 203.0.113.1 54401 ...
      name: 'remoteCandidates',
      reg: /^remote-candidates:(.*)/,
      format: 'remote-candidates:%s'
    },
    {
      // a=ice-options:google-ice
      name: 'iceOptions',
      reg: /^ice-options:(\S*)/,
      format: 'ice-options:%s'
    },
    {
      // a=ssrc:2566107569 cname:t9YU8M1UxTF8Y1A1
      push: 'ssrcs',
      reg: /^ssrc:(\d*) ([\w_-]*)(?::(.*))?/,
      names: ['id', 'attribute', 'value'],
      format: function (o) {
        var str = 'ssrc:%d';
        if (o.attribute != null) {
          str += ' %s';
          if (o.value != null) {
            str += ':%s';
          }
        }
        return str;
      }
    },
    {
      // a=ssrc-group:FEC 1 2
      // a=ssrc-group:FEC-FR 3004364195 1080772241
      push: 'ssrcGroups',
      // token-char = %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39 / %x41-5A / %x5E-7E
      reg: /^ssrc-group:([\x21\x23\x24\x25\x26\x27\x2A\x2B\x2D\x2E\w]*) (.*)/,
      names: ['semantics', 'ssrcs'],
      format: 'ssrc-group:%s %s'
    },
    {
      // a=msid-semantic: WMS Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlV
      name: 'msidSemantic',
      reg: /^msid-semantic:\s?(\w*) (\S*)/,
      names: ['semantic', 'token'],
      format: 'msid-semantic: %s %s' // space after ':' is not accidental
    },
    {
      // a=group:BUNDLE audio video
      push: 'groups',
      reg: /^group:(\w*) (.*)/,
      names: ['type', 'mids'],
      format: 'group:%s %s'
    },
    {
      // a=rtcp-mux
      name: 'rtcpMux',
      reg: /^(rtcp-mux)/
    },
    {
      // a=rtcp-rsize
      name: 'rtcpRsize',
      reg: /^(rtcp-rsize)/
    },
    {
      // a=sctpmap:5000 webrtc-datachannel 1024
      name: 'sctpmap',
      reg: /^sctpmap:([\w_/]*) (\S*)(?: (\S*))?/,
      names: ['sctpmapNumber', 'app', 'maxMessageSize'],
      format: function (o) {
        return (o.maxMessageSize != null)
          ? 'sctpmap:%s %s %s'
          : 'sctpmap:%s %s';
      }
    },
    {
      // a=x-google-flag:conference
      name: 'xGoogleFlag',
      reg: /^x-google-flag:([^\s]*)/,
      format: 'x-google-flag:%s'
    },
    {
      // a=rid:1 send max-width=1280;max-height=720;max-fps=30;depend=0
      push: 'rids',
      reg: /^rid:([\d\w]+) (\w+)(?: ([\S| ]*))?/,
      names: ['id', 'direction', 'params'],
      format: function (o) {
        return (o.params) ? 'rid:%s %s %s' : 'rid:%s %s';
      }
    },
    {
      // a=imageattr:97 send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320] recv [x=330,y=250]
      // a=imageattr:* send [x=800,y=640] recv *
      // a=imageattr:100 recv [x=320,y=240]
      push: 'imageattrs',
      reg: new RegExp(
        // a=imageattr:97
        '^imageattr:(\\d+|\\*)' +
        // send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320]
        '[\\s\\t]+(send|recv)[\\s\\t]+(\\*|\\[\\S+\\](?:[\\s\\t]+\\[\\S+\\])*)' +
        // recv [x=330,y=250]
        '(?:[\\s\\t]+(recv|send)[\\s\\t]+(\\*|\\[\\S+\\](?:[\\s\\t]+\\[\\S+\\])*))?'
      ),
      names: ['pt', 'dir1', 'attrs1', 'dir2', 'attrs2'],
      format: function (o) {
        return 'imageattr:%s %s %s' + (o.dir2 ? ' %s %s' : '');
      }
    },
    {
      // a=simulcast:send 1,2,3;~4,~5 recv 6;~7,~8
      // a=simulcast:recv 1;4,5 send 6;7
      name: 'simulcast',
      reg: new RegExp(
        // a=simulcast:
        '^simulcast:' +
        // send 1,2,3;~4,~5
        '(send|recv) ([a-zA-Z0-9\\-_~;,]+)' +
        // space + recv 6;~7,~8
        '(?:\\s?(send|recv) ([a-zA-Z0-9\\-_~;,]+))?' +
        // end
        '$'
      ),
      names: ['dir1', 'list1', 'dir2', 'list2'],
      format: function (o) {
        return 'simulcast:%s %s' + (o.dir2 ? ' %s %s' : '');
      }
    },
    {
      // old simulcast draft 03 (implemented by Firefox)
      //   https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-03
      // a=simulcast: recv pt=97;98 send pt=97
      // a=simulcast: send rid=5;6;7 paused=6,7
      name: 'simulcast_03',
      reg: /^simulcast:[\s\t]+([\S+\s\t]+)$/,
      names: ['value'],
      format: 'simulcast: %s'
    },
    {
      // a=framerate:25
      // a=framerate:29.97
      name: 'framerate',
      reg: /^framerate:(\d+(?:$|\.\d+))/,
      format: 'framerate:%s'
    },
    {
      // RFC4570
      // a=source-filter: incl IN IP4 239.5.2.31 10.1.15.5
      name: 'sourceFilter',
      reg: /^source-filter: *(excl|incl) (\S*) (IP4|IP6|\*) (\S*) (.*)/,
      names: ['filterMode', 'netType', 'addressTypes', 'destAddress', 'srcList'],
      format: 'source-filter: %s %s %s %s %s'
    },
    {
      // a=bundle-only
      name: 'bundleOnly',
      reg: /^(bundle-only)/
    },
    {
      // a=label:1
      name: 'label',
      reg: /^label:(.+)/,
      format: 'label:%s'
    },
    {
      // RFC version 26 for SCTP over DTLS
      // https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-26#section-5
      name: 'sctpPort',
      reg: /^sctp-port:(\d+)$/,
      format: 'sctp-port:%s'
    },
    {
      // RFC version 26 for SCTP over DTLS
      // https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-26#section-6
      name: 'maxMessageSize',
      reg: /^max-message-size:(\d+)$/,
      format: 'max-message-size:%s'
    },
    {
      // RFC7273
      // a=ts-refclk:ptp=IEEE1588-2008:39-A7-94-FF-FE-07-CB-D0:37
      push:'tsRefClocks',
      reg: /^ts-refclk:([^\s=]*)(?:=(\S*))?/,
      names: ['clksrc', 'clksrcExt'],
      format: function (o) {
        return 'ts-refclk:%s' + (o.clksrcExt != null ? '=%s' : '');
      }
    },
    {
      // RFC7273
      // a=mediaclk:direct=963214424
      name:'mediaClk',
      reg: /^mediaclk:(?:id=(\S*))? *([^\s=]*)(?:=(\S*))?(?: *rate=(\d+)\/(\d+))?/,
      names: ['id', 'mediaClockName', 'mediaClockValue', 'rateNumerator', 'rateDenominator'],
      format: function (o) {
        var str = 'mediaclk:';
        str += (o.id != null ? 'id=%s %s' : '%v%s');
        str += (o.mediaClockValue != null ? '=%s' : '');
        str += (o.rateNumerator != null ? ' rate=%s' : '');
        str += (o.rateDenominator != null ? '/%s' : '');
        return str;
      }
    },
    {
      // a=keywds:keywords
      name: 'keywords',
      reg: /^keywds:(.+)$/,
      format: 'keywds:%s'
    },
    {
      // a=content:main
      name: 'content',
      reg: /^content:(.+)/,
      format: 'content:%s'
    },
    // BFCP https://tools.ietf.org/html/rfc4583
    {
      // a=floorctrl:c-s
      name: 'bfcpFloorCtrl',
      reg: /^floorctrl:(c-only|s-only|c-s)/,
      format: 'floorctrl:%s'
    },
    {
      // a=confid:1
      name: 'bfcpConfId',
      reg: /^confid:(\d+)/,
      format: 'confid:%s'
    },
    {
      // a=userid:1
      name: 'bfcpUserId',
      reg: /^userid:(\d+)/,
      format: 'userid:%s'
    },
    {
      // a=floorid:1
      name: 'bfcpFloorId',
      reg: /^floorid:(.+) (?:m-stream|mstrm):(.+)/,
      names: ['id', 'mStream'],
      format: 'floorid:%s mstrm:%s'
    },
    {
      // any a= that we don't understand is kept verbatim on media.invalid
      push: 'invalid',
      names: ['value']
    }
  ]
};

// set sensible defaults to avoid polluting the grammar with boring details
Object.keys(grammar).forEach(function (key) {
  var objs = grammar[key];
  objs.forEach(function (obj) {
    if (!obj.reg) {
      obj.reg = /(.*)/;
    }
    if (!obj.format) {
      obj.format = '%s';
    }
  });
});

},{}],37:[function(require,module,exports){
var parser = require('./parser');
var writer = require('./writer');

exports.write = writer;
exports.parse = parser.parse;
exports.parseParams = parser.parseParams;
exports.parseFmtpConfig = parser.parseFmtpConfig; // Alias of parseParams().
exports.parsePayloads = parser.parsePayloads;
exports.parseRemoteCandidates = parser.parseRemoteCandidates;
exports.parseImageAttributes = parser.parseImageAttributes;
exports.parseSimulcastStreamList = parser.parseSimulcastStreamList;

},{"./parser":38,"./writer":39}],38:[function(require,module,exports){
var toIntIfInt = function (v) {
  return String(Number(v)) === v ? Number(v) : v;
};

var attachProperties = function (match, location, names, rawName) {
  if (rawName && !names) {
    location[rawName] = toIntIfInt(match[1]);
  }
  else {
    for (var i = 0; i < names.length; i += 1) {
      if (match[i+1] != null) {
        location[names[i]] = toIntIfInt(match[i+1]);
      }
    }
  }
};

var parseReg = function (obj, location, content) {
  var needsBlank = obj.name && obj.names;
  if (obj.push && !location[obj.push]) {
    location[obj.push] = [];
  }
  else if (needsBlank && !location[obj.name]) {
    location[obj.name] = {};
  }
  var keyLocation = obj.push ?
    {} :  // blank object that will be pushed
    needsBlank ? location[obj.name] : location; // otherwise, named location or root

  attachProperties(content.match(obj.reg), keyLocation, obj.names, obj.name);

  if (obj.push) {
    location[obj.push].push(keyLocation);
  }
};

var grammar = require('./grammar');
var validLine = RegExp.prototype.test.bind(/^([a-z])=(.*)/);

exports.parse = function (sdp) {
  var session = {}
    , media = []
    , location = session; // points at where properties go under (one of the above)

  // parse lines we understand
  sdp.split(/(\r\n|\r|\n)/).filter(validLine).forEach(function (l) {
    var type = l[0];
    var content = l.slice(2);
    if (type === 'm') {
      media.push({rtp: [], fmtp: []});
      location = media[media.length-1]; // point at latest media line
    }

    for (var j = 0; j < (grammar[type] || []).length; j += 1) {
      var obj = grammar[type][j];
      if (obj.reg.test(content)) {
        return parseReg(obj, location, content);
      }
    }
  });

  session.media = media; // link it up
  return session;
};

var paramReducer = function (acc, expr) {
  var s = expr.split(/=(.+)/, 2);
  if (s.length === 2) {
    acc[s[0]] = toIntIfInt(s[1]);
  } else if (s.length === 1 && expr.length > 1) {
    acc[s[0]] = undefined;
  }
  return acc;
};

exports.parseParams = function (str) {
  return str.split(/;\s?/).reduce(paramReducer, {});
};

// For backward compatibility - alias will be removed in 3.0.0
exports.parseFmtpConfig = exports.parseParams;

exports.parsePayloads = function (str) {
  return str.toString().split(' ').map(Number);
};

exports.parseRemoteCandidates = function (str) {
  var candidates = [];
  var parts = str.split(' ').map(toIntIfInt);
  for (var i = 0; i < parts.length; i += 3) {
    candidates.push({
      component: parts[i],
      ip: parts[i + 1],
      port: parts[i + 2]
    });
  }
  return candidates;
};

exports.parseImageAttributes = function (str) {
  return str.split(' ').map(function (item) {
    return item.substring(1, item.length-1).split(',').reduce(paramReducer, {});
  });
};

exports.parseSimulcastStreamList = function (str) {
  return str.split(';').map(function (stream) {
    return stream.split(',').map(function (format) {
      var scid, paused = false;

      if (format[0] !== '~') {
        scid = toIntIfInt(format);
      } else {
        scid = toIntIfInt(format.substring(1, format.length));
        paused = true;
      }

      return {
        scid: scid,
        paused: paused
      };
    });
  });
};

},{"./grammar":36}],39:[function(require,module,exports){
var grammar = require('./grammar');

// customized util.format - discards excess arguments and can void middle ones
var formatRegExp = /%[sdv%]/g;
var format = function (formatStr) {
  var i = 1;
  var args = arguments;
  var len = args.length;
  return formatStr.replace(formatRegExp, function (x) {
    if (i >= len) {
      return x; // missing argument
    }
    var arg = args[i];
    i += 1;
    switch (x) {
    case '%%':
      return '%';
    case '%s':
      return String(arg);
    case '%d':
      return Number(arg);
    case '%v':
      return '';
    }
  });
  // NB: we discard excess arguments - they are typically undefined from makeLine
};

var makeLine = function (type, obj, location) {
  var str = obj.format instanceof Function ?
    (obj.format(obj.push ? location : location[obj.name])) :
    obj.format;

  var args = [type + '=' + str];
  if (obj.names) {
    for (var i = 0; i < obj.names.length; i += 1) {
      var n = obj.names[i];
      if (obj.name) {
        args.push(location[obj.name][n]);
      }
      else { // for mLine and push attributes
        args.push(location[obj.names[i]]);
      }
    }
  }
  else {
    args.push(location[obj.name]);
  }
  return format.apply(null, args);
};

// RFC specified order
// TODO: extend this with all the rest
var defaultOuterOrder = [
  'v', 'o', 's', 'i',
  'u', 'e', 'p', 'c',
  'b', 't', 'r', 'z', 'a'
];
var defaultInnerOrder = ['i', 'c', 'b', 'a'];


module.exports = function (session, opts) {
  opts = opts || {};
  // ensure certain properties exist
  if (session.version == null) {
    session.version = 0; // 'v=0' must be there (only defined version atm)
  }
  if (session.name == null) {
    session.name = ' '; // 's= ' must be there if no meaningful name set
  }
  session.media.forEach(function (mLine) {
    if (mLine.payloads == null) {
      mLine.payloads = '';
    }
  });

  var outerOrder = opts.outerOrder || defaultOuterOrder;
  var innerOrder = opts.innerOrder || defaultInnerOrder;
  var sdp = [];

  // loop through outerOrder for matching properties on session
  outerOrder.forEach(function (type) {
    grammar[type].forEach(function (obj) {
      if (obj.name in session && session[obj.name] != null) {
        sdp.push(makeLine(type, obj, session));
      }
      else if (obj.push in session && session[obj.push] != null) {
        session[obj.push].forEach(function (el) {
          sdp.push(makeLine(type, obj, el));
        });
      }
    });
  });

  // then for each media line, follow the innerOrder
  session.media.forEach(function (mLine) {
    sdp.push(makeLine('m', grammar.m[0], mLine));

    innerOrder.forEach(function (type) {
      grammar[type].forEach(function (obj) {
        if (obj.name in mLine && mLine[obj.name] != null) {
          sdp.push(makeLine(type, obj, mLine));
        }
        else if (obj.push in mLine && mLine[obj.push] != null) {
          mLine[obj.push].forEach(function (el) {
            sdp.push(makeLine(type, obj, el));
          });
        }
      });
    });
  });

  return sdp.join('\r\n') + '\r\n';
};

},{"./grammar":36}],40:[function(require,module,exports){
module.exports={
  "name": "jssip",
  "title": "JsSIP",
  "description": "the Javascript SIP library",
  "version": "3.9.1",
  "homepage": "https://jssip.net",
  "contributors": [
    "JosÃ© Luis MillÃ¡n <jmillan@aliax.net> (https://github.com/jmillan)",
    "IÃ±aki Baz Castillo <ibc@aliax.net> (https://inakibaz.me)"
  ],
  "types": "lib/JsSIP.d.ts",
  "main": "lib-es5/JsSIP.js",
  "keywords": [
    "sip",
    "websocket",
    "webrtc",
    "node",
    "browser",
    "library"
  ],
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/versatica/JsSIP.git"
  },
  "bugs": {
    "url": "https://github.com/versatica/JsSIP/issues"
  },
  "dependencies": {
    "@types/debug": "^4.1.5",
    "@types/node": "^14.14.34",
    "debug": "^4.3.1",
    "events": "^3.3.0",
    "sdp-transform": "^2.14.1"
  },
  "devDependencies": {
    "@babel/core": "^7.13.10",
    "@babel/preset-env": "^7.13.10",
    "ansi-colors": "^3.2.4",
    "browserify": "^16.5.1",
    "eslint": "^5.16.0",
    "fancy-log": "^1.3.3",
    "gulp": "^4.0.2",
    "gulp-babel": "^8.0.0",
    "gulp-eslint": "^5.0.0",
    "gulp-expect-file": "^1.0.2",
    "gulp-header": "^2.0.9",
    "gulp-nodeunit-runner": "^0.2.2",
    "gulp-plumber": "^1.2.1",
    "gulp-rename": "^1.4.0",
    "gulp-uglify-es": "^1.0.4",
    "pegjs": "^0.7.0",
    "vinyl-buffer": "^1.0.1",
    "vinyl-source-stream": "^2.0.0"
  },
  "scripts": {
    "lint": "gulp lint",
    "test": "gulp test",
    "prepublishOnly": "gulp babel"
  }
}

},{}]},{},[8])(8)
});
