{"ast":null,"code":"'use strict';\n\nrequire('./shims');\n\nvar URL = require('url-parse'),\n    inherits = require('inherits'),\n    JSON3 = require('json3'),\n    random = require('./utils/random'),\n    escape = require('./utils/escape'),\n    urlUtils = require('./utils/url'),\n    eventUtils = require('./utils/event'),\n    transport = require('./utils/transport'),\n    objectUtils = require('./utils/object'),\n    browser = require('./utils/browser'),\n    log = require('./utils/log'),\n    Event = require('./event/event'),\n    EventTarget = require('./event/eventtarget'),\n    loc = require('./location'),\n    CloseEvent = require('./event/close'),\n    TransportMessageEvent = require('./event/trans-message'),\n    InfoReceiver = require('./info-receiver');\n\nvar debug = function () {};\n\nif (process.env.NODE_ENV !== 'production') {\n  debug = require('debug')('sockjs-client:main');\n}\n\nvar transports; // follow constructor steps defined at http://dev.w3.org/html5/websockets/#the-websocket-interface\n\nfunction SockJS(url, protocols, options) {\n  if (!(this instanceof SockJS)) {\n    return new SockJS(url, protocols, options);\n  }\n\n  if (arguments.length < 1) {\n    throw new TypeError(\"Failed to construct 'SockJS: 1 argument required, but only 0 present\");\n  }\n\n  EventTarget.call(this);\n  this.readyState = SockJS.CONNECTING;\n  this.extensions = '';\n  this.protocol = ''; // non-standard extension\n\n  options = options || {};\n\n  if (options.protocols_whitelist) {\n    log.warn(\"'protocols_whitelist' is DEPRECATED. Use 'transports' instead.\");\n  }\n\n  this._transportsWhitelist = options.transports;\n  this._transportOptions = options.transportOptions || {};\n  var sessionId = options.sessionId || 8;\n\n  if (typeof sessionId === 'function') {\n    this._generateSessionId = sessionId;\n  } else if (typeof sessionId === 'number') {\n    this._generateSessionId = function () {\n      return random.string(sessionId);\n    };\n  } else {\n    throw new TypeError('If sessionId is used in the options, it needs to be a number or a function.');\n  }\n\n  this._server = options.server || random.numberString(1000); // Step 1 of WS spec - parse and validate the url. Issue #8\n\n  var parsedUrl = new URL(url);\n\n  if (!parsedUrl.host || !parsedUrl.protocol) {\n    throw new SyntaxError(\"The URL '\" + url + \"' is invalid\");\n  } else if (parsedUrl.hash) {\n    throw new SyntaxError('The URL must not contain a fragment');\n  } else if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {\n    throw new SyntaxError(\"The URL's scheme must be either 'http:' or 'https:'. '\" + parsedUrl.protocol + \"' is not allowed.\");\n  }\n\n  var secure = parsedUrl.protocol === 'https:'; // Step 2 - don't allow secure origin with an insecure protocol\n\n  if (loc.protocol === 'https:' && !secure) {\n    throw new Error('SecurityError: An insecure SockJS connection may not be initiated from a page loaded over HTTPS');\n  } // Step 3 - check port access - no need here\n  // Step 4 - parse protocols argument\n\n\n  if (!protocols) {\n    protocols = [];\n  } else if (!Array.isArray(protocols)) {\n    protocols = [protocols];\n  } // Step 5 - check protocols argument\n\n\n  var sortedProtocols = protocols.sort();\n  sortedProtocols.forEach(function (proto, i) {\n    if (!proto) {\n      throw new SyntaxError(\"The protocols entry '\" + proto + \"' is invalid.\");\n    }\n\n    if (i < sortedProtocols.length - 1 && proto === sortedProtocols[i + 1]) {\n      throw new SyntaxError(\"The protocols entry '\" + proto + \"' is duplicated.\");\n    }\n  }); // Step 6 - convert origin\n\n  var o = urlUtils.getOrigin(loc.href);\n  this._origin = o ? o.toLowerCase() : null; // remove the trailing slash\n\n  parsedUrl.set('pathname', parsedUrl.pathname.replace(/\\/+$/, '')); // store the sanitized url\n\n  this.url = parsedUrl.href;\n  debug('using url', this.url); // Step 7 - start connection in background\n  // obtain server info\n  // http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-26\n\n  this._urlInfo = {\n    nullOrigin: !browser.hasDomain(),\n    sameOrigin: urlUtils.isOriginEqual(this.url, loc.href),\n    sameScheme: urlUtils.isSchemeEqual(this.url, loc.href)\n  };\n  this._ir = new InfoReceiver(this.url, this._urlInfo);\n\n  this._ir.once('finish', this._receiveInfo.bind(this));\n}\n\ninherits(SockJS, EventTarget);\n\nfunction userSetCode(code) {\n  return code === 1000 || code >= 3000 && code <= 4999;\n}\n\nSockJS.prototype.close = function (code, reason) {\n  // Step 1\n  if (code && !userSetCode(code)) {\n    throw new Error('InvalidAccessError: Invalid code');\n  } // Step 2.4 states the max is 123 bytes, but we are just checking length\n\n\n  if (reason && reason.length > 123) {\n    throw new SyntaxError('reason argument has an invalid length');\n  } // Step 3.1\n\n\n  if (this.readyState === SockJS.CLOSING || this.readyState === SockJS.CLOSED) {\n    return;\n  } // TODO look at docs to determine how to set this\n\n\n  var wasClean = true;\n\n  this._close(code || 1000, reason || 'Normal closure', wasClean);\n};\n\nSockJS.prototype.send = function (data) {\n  // #13 - convert anything non-string to string\n  // TODO this currently turns objects into [object Object]\n  if (typeof data !== 'string') {\n    data = '' + data;\n  }\n\n  if (this.readyState === SockJS.CONNECTING) {\n    throw new Error('InvalidStateError: The connection has not been established yet');\n  }\n\n  if (this.readyState !== SockJS.OPEN) {\n    return;\n  }\n\n  this._transport.send(escape.quote(data));\n};\n\nSockJS.version = require('./version');\nSockJS.CONNECTING = 0;\nSockJS.OPEN = 1;\nSockJS.CLOSING = 2;\nSockJS.CLOSED = 3;\n\nSockJS.prototype._receiveInfo = function (info, rtt) {\n  debug('_receiveInfo', rtt);\n  this._ir = null;\n\n  if (!info) {\n    this._close(1002, 'Cannot connect to server');\n\n    return;\n  } // establish a round-trip timeout (RTO) based on the\n  // round-trip time (RTT)\n\n\n  this._rto = this.countRTO(rtt); // allow server to override url used for the actual transport\n\n  this._transUrl = info.base_url ? info.base_url : this.url;\n  info = objectUtils.extend(info, this._urlInfo);\n  debug('info', info); // determine list of desired and supported transports\n\n  var enabledTransports = transports.filterToEnabled(this._transportsWhitelist, info);\n  this._transports = enabledTransports.main;\n  debug(this._transports.length + ' enabled transports');\n\n  this._connect();\n};\n\nSockJS.prototype._connect = function () {\n  for (var Transport = this._transports.shift(); Transport; Transport = this._transports.shift()) {\n    debug('attempt', Transport.transportName);\n\n    if (Transport.needBody) {\n      if (!global.document.body || typeof global.document.readyState !== 'undefined' && global.document.readyState !== 'complete' && global.document.readyState !== 'interactive') {\n        debug('waiting for body');\n\n        this._transports.unshift(Transport);\n\n        eventUtils.attachEvent('load', this._connect.bind(this));\n        return;\n      }\n    } // calculate timeout based on RTO and round trips. Default to 5s\n\n\n    var timeoutMs = this._rto * Transport.roundTrips || 5000;\n    this._transportTimeoutId = setTimeout(this._transportTimeout.bind(this), timeoutMs);\n    debug('using timeout', timeoutMs);\n    var transportUrl = urlUtils.addPath(this._transUrl, '/' + this._server + '/' + this._generateSessionId());\n    var options = this._transportOptions[Transport.transportName];\n    debug('transport url', transportUrl);\n    var transportObj = new Transport(transportUrl, this._transUrl, options);\n    transportObj.on('message', this._transportMessage.bind(this));\n    transportObj.once('close', this._transportClose.bind(this));\n    transportObj.transportName = Transport.transportName;\n    this._transport = transportObj;\n    return;\n  }\n\n  this._close(2000, 'All transports failed', false);\n};\n\nSockJS.prototype._transportTimeout = function () {\n  debug('_transportTimeout');\n\n  if (this.readyState === SockJS.CONNECTING) {\n    if (this._transport) {\n      this._transport.close();\n    }\n\n    this._transportClose(2007, 'Transport timed out');\n  }\n};\n\nSockJS.prototype._transportMessage = function (msg) {\n  debug('_transportMessage', msg);\n  var self = this,\n      type = msg.slice(0, 1),\n      content = msg.slice(1),\n      payload; // first check for messages that don't need a payload\n\n  switch (type) {\n    case 'o':\n      this._open();\n\n      return;\n\n    case 'h':\n      this.dispatchEvent(new Event('heartbeat'));\n      debug('heartbeat', this.transport);\n      return;\n  }\n\n  if (content) {\n    try {\n      payload = JSON3.parse(content);\n    } catch (e) {\n      debug('bad json', content);\n    }\n  }\n\n  if (typeof payload === 'undefined') {\n    debug('empty payload', content);\n    return;\n  }\n\n  switch (type) {\n    case 'a':\n      if (Array.isArray(payload)) {\n        payload.forEach(function (p) {\n          debug('message', self.transport, p);\n          self.dispatchEvent(new TransportMessageEvent(p));\n        });\n      }\n\n      break;\n\n    case 'm':\n      debug('message', this.transport, payload);\n      this.dispatchEvent(new TransportMessageEvent(payload));\n      break;\n\n    case 'c':\n      if (Array.isArray(payload) && payload.length === 2) {\n        this._close(payload[0], payload[1], true);\n      }\n\n      break;\n  }\n};\n\nSockJS.prototype._transportClose = function (code, reason) {\n  debug('_transportClose', this.transport, code, reason);\n\n  if (this._transport) {\n    this._transport.removeAllListeners();\n\n    this._transport = null;\n    this.transport = null;\n  }\n\n  if (!userSetCode(code) && code !== 2000 && this.readyState === SockJS.CONNECTING) {\n    this._connect();\n\n    return;\n  }\n\n  this._close(code, reason);\n};\n\nSockJS.prototype._open = function () {\n  debug('_open', this._transport.transportName, this.readyState);\n\n  if (this.readyState === SockJS.CONNECTING) {\n    if (this._transportTimeoutId) {\n      clearTimeout(this._transportTimeoutId);\n      this._transportTimeoutId = null;\n    }\n\n    this.readyState = SockJS.OPEN;\n    this.transport = this._transport.transportName;\n    this.dispatchEvent(new Event('open'));\n    debug('connected', this.transport);\n  } else {\n    // The server might have been restarted, and lost track of our\n    // connection.\n    this._close(1006, 'Server lost session');\n  }\n};\n\nSockJS.prototype._close = function (code, reason, wasClean) {\n  debug('_close', this.transport, code, reason, wasClean, this.readyState);\n  var forceFail = false;\n\n  if (this._ir) {\n    forceFail = true;\n\n    this._ir.close();\n\n    this._ir = null;\n  }\n\n  if (this._transport) {\n    this._transport.close();\n\n    this._transport = null;\n    this.transport = null;\n  }\n\n  if (this.readyState === SockJS.CLOSED) {\n    throw new Error('InvalidStateError: SockJS has already been closed');\n  }\n\n  this.readyState = SockJS.CLOSING;\n  setTimeout(function () {\n    this.readyState = SockJS.CLOSED;\n\n    if (forceFail) {\n      this.dispatchEvent(new Event('error'));\n    }\n\n    var e = new CloseEvent('close');\n    e.wasClean = wasClean || false;\n    e.code = code || 1000;\n    e.reason = reason;\n    this.dispatchEvent(e);\n    this.onmessage = this.onclose = this.onerror = null;\n    debug('disconnected');\n  }.bind(this), 0);\n}; // See: http://www.erg.abdn.ac.uk/~gerrit/dccp/notes/ccid2/rto_estimator/\n// and RFC 2988.\n\n\nSockJS.prototype.countRTO = function (rtt) {\n  // In a local environment, when using IE8/9 and the `jsonp-polling`\n  // transport the time needed to establish a connection (the time that pass\n  // from the opening of the transport to the call of `_dispatchOpen`) is\n  // around 200msec (the lower bound used in the article above) and this\n  // causes spurious timeouts. For this reason we calculate a value slightly\n  // larger than that used in the article.\n  if (rtt > 100) {\n    return 4 * rtt; // rto > 400msec\n  }\n\n  return 300 + rtt; // 300msec < rto <= 400msec\n};\n\nmodule.exports = function (availableTransports) {\n  transports = transport(availableTransports);\n\n  require('./iframe-bootstrap')(SockJS, availableTransports);\n\n  return SockJS;\n};","map":null,"metadata":{},"sourceType":"script"}