text: /*
   * Copyright 2013 Jeanfrancois Arcand
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  /**
   * Atmosphere.js
   * https://github.com/Atmosphere/atmosphere-javascript
   * 
   * Requires 
   * - jQuery 2.0.3 http://jquery.com/
   * 
   * API reference
   * https://github.com/Atmosphere/atmosphere/wiki/jQuery.atmosphere.js-API
   * 
   * Highly inspired by 
   * - Portal by Donghwan Kim http://flowersinthesand.github.io/portal/
   */
  (function(factory) {
      if (typeof define === 'function' && define.amd) {
          // AMD
          define(['jquery'], factory);
      } else {
          // Browser globals, Window
          factory(jQuery);
      }
  }(function(jQuery) {
  
      jQuery(window).bind("unload.atmosphere", function () {
          jQuery.atmosphere.unsubscribe();
      });
  
      jQuery(window).bind("offline", function () {
          jQuery.atmosphere.unsubscribe();
      });
  
      // Prevent ESC to kill the connection from Firefox.
      jQuery(window).keypress(function (e) {
          if (e.keyCode === 27) {
              e.preventDefault();
          }
      });
  
      var parseHeaders = function (headerString) {
          var match, rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, headers = {};
          while (match = rheaders.exec(headerString)) {
              headers[match[1]] = match[2];
          }
          return headers;
      };
  
      jQuery.atmosphere = {
          version: "2.1.4-jquery",
          uuid : 0,
          requests: [],
          callbacks: [],
  
          onError: function (response) {
          },
          onClose: function (response) {
          },
          onOpen: function (response) {
          },
          onMessage: function (response) {
          },
          onReconnect: function (request, response) {
          },
          onMessagePublished: function (response) {
          },
          onTransportFailure: function (errorMessage, _request) {
          },
          onLocalMessage: function (response) {
          },
          onClientTimeout: function(request){
          },
          onFailureToReconnect: function (request, response) {
          },
  
          AtmosphereRequest: function (options) {
  
              /**
               * {Object} Request parameters.
               *
               * @private
               */
              var _request = {
                  timeout: 300000,
                  method: 'GET',
                  headers: {},
                  contentType: '',
                  callback: null,
                  url: '',
                  data: '',
                  suspend: true,
                  maxRequest: -1,
                  reconnect: true,
                  maxStreamingLength: 10000000,
                  lastIndex: 0,
                  logLevel: 'info',
                  requestCount: 0,
                  fallbackMethod: 'GET',
                  fallbackTransport: 'streaming',
                  transport: 'long-polling',
                  webSocketImpl: null,
                  webSocketBinaryType: null,
                  dispatchUrl: null,
                  webSocketPathDelimiter: "@@",
                  enableXDR: false,
                  rewriteURL: false,
                  attachHeadersAsQueryString: true,
                  executeCallbackBeforeReconnect: false,
                  readyState: 0,
                  lastTimestamp: 0,
                  withCredentials: false,
                  trackMessageLength: false,
                  messageDelimiter: '|',
                  connectTimeout: -1,
                  reconnectInterval: 0,
                  dropHeaders: true,
                  uuid: 0,
                  shared: false,
                  readResponsesHeaders: false,
                  maxReconnectOnClose: 5,
                  enableProtocol: true,
                  onError: function (response) {
                  },
                  onClose: function (response) {
                  },
                  onOpen: function (response) {
                  },
                  onMessage: function (response) {
                  },
                  onReopen: function (request, response) {
                  },
                  onReconnect: function (request, response) {
                  },
                  onMessagePublished: function (response) {
                  },
                  onTransportFailure: function (reason, request) {
                  },
                  onLocalMessage: function (request) {
                  },
                  onFailureToReconnect: function (request, response) {
                  },
                  onClientTimeout: function(request){
                  }
              };
  
              /**
               * {Object} Request's last response.
               *
               * @private
               */
              var _response = {
                  status: 200,
                  reasonPhrase: "OK",
                  responseBody: '',
                  messages: [],
                  headers: [],
                  state: "messageReceived",
                  transport: "polling",
                  error: null,
                  request: null,
                  partialMessage: "",
                  errorHandled: false,
                  closedByClientTimeout: false
              };
  
              /**
               * {websocket} Opened web socket.
               *
               * @private
               */
              var _websocket = null;
  
              /**
               * {SSE} Opened SSE.
               *
               * @private
               */
              var _sse = null;
  
              /**
               * {XMLHttpRequest, ActiveXObject} Opened ajax request (in case of http-streaming or long-polling)
               *
               * @private
               */
              var _activeRequest = null;
  
              /**
               * {Object} Object use for streaming with IE.
               *
               * @private
               */
              var _ieStream = null;
  
              /**
               * {Object} Object use for jsonp transport.
               *
               * @private
               */
              var _jqxhr = null;
  
              /**
               * {boolean} If request has been subscribed or not.
               *
               * @private
               */
              var _subscribed = true;
  
              /**
               * {number} Number of test reconnection.
               *
               * @private
               */
              var _requestCount = 0;
  
              /**
               * {boolean} If request is currently aborded.
               *
               * @private
               */
              var _abordingConnection = false;
  
              /**
               * A local "channel' of communication.
               *
               * @private
               */
              var _localSocketF = null;
  
              /**
               * The storage used.
               *
               * @private
               */
              var _storageService;
  
              /**
               * Local communication
               *
               * @private
               */
              var _localStorageService = null;
  
              /**
               * A Unique ID
               *
               * @private
               */
              var guid = jQuery.now();
  
              /** Trace time */
              var _traceTimer;
  
              /** Key for connection sharing */
              var _sharingKey;
  
              // Automatic call to subscribe
              _subscribe(options);
  
              /**
               * Initialize atmosphere request object.
               *
               * @private
               */
              function _init() {
                  _subscribed = true;
                  _abordingConnection = false;
                  _requestCount = 0;
  
                  _websocket = null;
                  _sse = null;
                  _activeRequest = null;
                  _ieStream = null;
              }
  
              /**
               * Re-initialize atmosphere object.
               *
               * @private
               */
              function _reinit() {
                  _clearState();
                  _init();
              }
  
              /**
               * Subscribe request using request transport. <br>
               * If request is currently opened, this one will be closed.
               *
               * @param {Object} Request parameters.
               * @private
               */
              function _subscribe(options) {
                  _reinit();
  
                  _request = jQuery.extend(_request, options);
                  // Allow at least 1 request
                  _request.mrequest = _request.reconnect;
                  if (!_request.reconnect) {
                      _request.reconnect = true;
                  }
              }
  
              /**
               * Check if web socket is supported (check for custom implementation provided by request object or browser implementation).
               *
               * @returns {boolean} True if web socket is supported, false otherwise.
               * @private
               */
              function _supportWebsocket() {
                  return _request.webSocketImpl != null || window.WebSocket || window.MozWebSocket;
              }
  
              /**
               * Check if server side events (SSE) is supported (check for custom implementation provided by request object or browser implementation).
               *
               * @returns {boolean} True if web socket is supported, false otherwise.
               * @private
               */
              function _supportSSE() {
                  return window.EventSource;
              }
  
              /**
               * Open request using request transport. <br>
               * If request transport is 'websocket' but websocket can't be opened, request will automatically reconnect using fallback transport.
               *
               * @private
               */
              function _execute() {
                  // Shared across multiple tabs/windows.
                  if (_request.shared) {
                      _localStorageService = _local(_request);
                      if (_localStorageService != null) {
                          if (_request.logLevel === 'debug') {
                              jQuery.atmosphere.debug("Storage service available. All communication will be local");
                          }
  
                          if (_localStorageService.open(_request)) {
                              // Local connection.
                              return;
                          }
                      }
  
                      if (_request.logLevel === 'debug') {
                          jQuery.atmosphere.debug("No Storage service available.");
                      }
                      // Wasn't local or an error occurred
                      _localStorageService = null;
                  }
  
                  // Protocol
                  _request.firstMessage = jQuery.atmosphere.uuid == 0 ? true : false;
                  _request.isOpen = false;
                  _request.ctime = jQuery.now();
  
                  // We carry any UUID set by the user or from a previous connection.
                  if (_request.uuid === 0) {
                      _request.uuid = jQuery.atmosphere.uuid;
                  }
                  _request.closedByClientTimeout = false;
  
                  if (_request.transport !== 'websocket' && _request.transport !== 'sse') {
                      _executeRequest(_request);
  
                  } else if (_request.transport === 'websocket') {
                      if (!_supportWebsocket()) {
                          _reconnectWithFallbackTransport("Websocket is not supported, using request.fallbackTransport (" + _request.fallbackTransport
                              + ")");
                      } else {
                          _executeWebSocket(false);
                      }
                  } else if (_request.transport === 'sse') {
                      if (!_supportSSE()) {
                          _reconnectWithFallbackTransport("Server Side Events(SSE) is not supported, using request.fallbackTransport ("
                              + _request.fallbackTransport + ")");
                      } else {
                          _executeSSE(false);
                      }
                  }
              }
  
              function _local(request) {
                  var trace, connector, orphan, name = "atmosphere-" + request.url, connectors = {
                      storage: function () {
                          if (!jQuery.atmosphere.supportStorage()) {
                              return;
                          }
  
                          var storage = window.localStorage, get = function (key) {
                              return jQuery.parseJSON(storage.getItem(name + "-" + key));
                          }, set = function (key, value) {
                              storage.setItem(name + "-" + key, jQuery.stringifyJSON(value));
                          };
  
                          return {
                              init: function () {
                                  set("children", get("children").concat([guid]));
                                  jQuery(window).on("storage.socket", function (event) {
                                      event = event.originalEvent;
                                      if (event.key === name && event.newValue) {
                                          listener(event.newValue);
                                      }
                                  });
                                  return get("opened");
                              },
                              signal: function (type, data) {
                                  storage.setItem(name, jQuery.stringifyJSON({
                                      target: "p",
                                      type: type,
                                      data: data
                                  }));
                              },
                              close: function () {
                                  var index, children = get("children");
  
                                  jQuery(window).off("storage.socket");
                                  if (children) {
                                      index = jQuery.inArray(request.id, children);
                                      if (index > -1) {
                                          children.splice(index, 1);
                                          set("children", children);
                                      }
                                  }
                              }
                          };
                      },
                      windowref: function () {
                          var win = window.open("", name.replace(/\W/g, ""));
  
                          if (!win || win.closed || !win.callbacks) {
                              return;
                          }
  
                          return {
                              init: function () {
                                  win.callbacks.push(listener);
                                  win.children.push(guid);
                                  return win.opened;
                              },
                              signal: function (type, data) {
                                  if (!win.closed && win.fire) {
                                      win.fire(jQuery.stringifyJSON({
                                          target: "p",
                                          type: type,
                                          data: data
                                      }));
                                  }
                              },
                              close: function () {
                                  function remove(array, e) {
                                      var index = jQuery.inArray(e, array);
                                      if (index > -1) {
                                          array.splice(index, 1);
                                      }
                                  }
  
                                  // Removes traces only if the parent is alive
                                  if (!orphan) {
                                      remove(win.callbacks, listener);
                                      remove(win.children, guid);
                                  }
                              }
  
                          };
                      }
                  };
  
                  // Receives open, close and message command from the parent
                  function listener(string) {
                      var command = jQuery.parseJSON(string), data = command.data;
  
                      if (command.target === "c") {
                          switch (command.type) {
                              case "open":
                                  _open("opening", 'local', _request);
                                  break;
                              case "close":
                                  if (!orphan) {
                                      orphan = true;
                                      if (data.reason === "aborted") {
                                          _close();
                                      } else {
                                          // Gives the heir some time to reconnect
                                          if (data.heir === guid) {
                                              _execute();
                                          } else {
                                              setTimeout(function () {
                                                  _execute();
                                              }, 100);
                                          }
                                      }
                                  }
                                  break;
                              case "message":
                                  _prepareCallback(data, "messageReceived", 200, request.transport);
                                  break;
                              case "localMessage":
                                  _localMessage(data);
                                  break;
                          }
                      }
                  }
  
                  function findTrace() {
                      var matcher = new RegExp("(?:^|; )(" + encodeURIComponent(name) + ")=([^;]*)").exec(document.cookie);
                      if (matcher) {
                          return jQuery.parseJSON(decodeURIComponent(matcher[2]));
                      }
                  }
  
                  // Finds and validates the parent socket's trace from the cookie
                  trace = findTrace();
                  if (!trace || jQuery.now() - trace.ts > 1000) {
                      return;
                  }
  
                  // Chooses a connector
                  connector = connectors.storage() || connectors.windowref();
                  if (!connector) {
                      return;
                  }
  
                  return {
                      open: function () {
                          var parentOpened;
  
                          // Checks the shared one is alive
                          _traceTimer = setInterval(function () {
                              var oldTrace = trace;
                              trace = findTrace();
                              if (!trace || oldTrace.ts === trace.ts) {
                                  // Simulates a close signal
                                  listener(jQuery.stringifyJSON({
                                      target: "c",
                                      type: "close",
                                      data: {
                                          reason: "error",
                                          heir: oldTrace.heir
                                      }
                                  }));
                              }
                          }, 1000);
  
                          parentOpened = connector.init();
                          if (parentOpened) {
                              // Firing the open event without delay robs the user of the opportunity to bind connecting event handlers
                              setTimeout(function () {
                                  _open("opening", 'local', request);
                              }, 50);
                          }
                          return parentOpened;
                      },
                      send: function (event) {
                          connector.signal("send", event);
                      },
                      localSend: function (event) {
                          connector.signal("localSend", jQuery.stringifyJSON({
                              id: guid,
                              event: event
                          }));
                      },
                      close: function () {
                          // Do not signal the parent if this method is executed by the unload event handler
                          if (!_abordingConnection) {
                              clearInterval(_traceTimer);
                              connector.signal("close");
                              connector.close();
                          }
                      }
                  };
              }
  
              function share() {
                  var storageService, name = "atmosphere-" + _request.url, servers = {
                      // Powered by the storage event and the localStorage
                      // http://www.w3.org/TR/webstorage/#event-storage
                      storage: function () {
                          if (!jQuery.atmosphere.supportStorage()) {
                              return;
                          }
  
                          var storage = window.localStorage;
  
                          return {
                              init: function () {
                                  // Handles the storage event
                                  jQuery(window).on("storage.socket", function (event) {
                                      event = event.originalEvent;
                                      // When a deletion, newValue initialized to null
                                      if (event.key === name && event.newValue) {
                                          listener(event.newValue);
                                      }
                                  });
                              },
                              signal: function (type, data) {
                                  storage.setItem(name, jQuery.stringifyJSON({
                                      target: "c",
                                      type: type,
                                      data: data
                                  }));
                              },
                              get: function (key) {
                                  return jQuery.parseJSON(storage.getItem(name + "-" + key));
                              },
                              set: function (key, value) {
                                  storage.setItem(name + "-" + key, jQuery.stringifyJSON(value));
                              },
                              close: function () {
                                  jQuery(window).off("storage.socket");
                                  storage.removeItem(name);
                                  storage.removeItem(name + "-opened");
                                  storage.removeItem(name + "-children");
                              }
  
                          };
                      },
                      // Powered by the window.open method
                      // https://developer.mozilla.org/en/DOM/window.open
                      windowref: function () {
                          // Internet Explorer raises an invalid argument error
                          // when calling the window.open method with the name containing non-word characters
                          var neim = name.replace(/\W/g, ""), win = (jQuery('iframe[name="' + neim + '"]')[0] || jQuery(
                              '<iframe name="' + neim + '" />').hide().appendTo("body")[0]).contentWindow;
  
                          return {
                              init: function () {
                                  // Callbacks from different windows
                                  win.callbacks = [listener];
                                  // In IE 8 and less, only string argument can be safely passed to the function in other window
                                  win.fire = function (string) {
                                      var i;
  
                                      for (i = 0; i < win.callbacks.length; i++) {
                                          win.callbacks[i](string);
                                      }
                                  };
                              },
                              signal: function (type, data) {
                                  if (!win.closed && win.fire) {
                                      win.fire(jQuery.stringifyJSON({
                                          target: "c",
                                          type: type,
                                          data: data
                                      }));
                                  }
                              },
                              get: function (key) {
                                  return !win.closed ? win[key] : null;
                              },
                              set: function (key, value) {
                                  if (!win.closed) {
                                      win[key] = value;
                                  }
                              },
                              close: function () {
                              }
                          };
                      }
                  };
  
                  // Receives send and close command from the children
                  function listener(string) {
                      var command = jQuery.parseJSON(string), data = command.data;
  
                      if (command.target === "p") {
                          switch (command.type) {
                              case "send":
                                  _push(data);
                                  break;
                              case "localSend":
                                  _localMessage(data);
                                  break;
                              case "close":
                                  _close();
                                  break;
                          }
                      }
                  }
  
                  _localSocketF = function propagateMessageEvent(context) {
                      storageService.signal("message", context);
                  };
  
                  function leaveTrace() {
                      document.cookie = _sharingKey + "=" +
                          // Opera's JSON implementation ignores a number whose a last digit of 0 strangely
                          // but has no problem with a number whose a last digit of 9 + 1
                          encodeURIComponent(jQuery.stringifyJSON({
                              ts: jQuery.now() + 1,
                              heir: (storageService.get("children") || [])[0]
                          })) + "; path=/";
                  }
  
                  // Chooses a storageService
                  storageService = servers.storage() || servers.windowref();
                  storageService.init();
  
                  if (_request.logLevel === 'debug') {
                      jQuery.atmosphere.debug("Installed StorageService " + storageService);
                  }
  
                  // List of children sockets
                  storageService.set("children", []);
  
                  if (storageService.get("opened") != null && !storageService.get("opened")) {
                      // Flag indicating the parent socket is opened
                      storageService.set("opened", false);
                  }
                  // Leaves traces
                  _sharingKey = encodeURIComponent(name);
                  leaveTrace();
                  _traceTimer = setInterval(leaveTrace, 1000);
  
                  _storageService = storageService;
              }
  
              /**
               * @private
               */
              function _open(state, transport, request) {
                  if (_request.shared && transport !== 'local') {
                      share();
                  }
  
                  if (_storageService != null) {
                      _storageService.set("opened", true);
                  }
  
                  request.close = function () {
                      _close();
                  };
  
                  if (_requestCount > 0 && state === 're-connecting') {
                      request.isReopen = true;
                      _tryingToReconnect(_response);
                  } else if (_response.error == null) {
                      _response.request = request;
                      var prevState = _response.state;
                      _response.state = state;
                      var prevTransport = _response.transport;
                      _response.transport = transport;
  
                      var _body = _response.responseBody;
                      _invokeCallback();
                      _response.responseBody = _body;
  
                      _response.state = prevState;
                      _response.transport = prevTransport;
                  }
              }
  
              /**
               * Execute request using jsonp transport.
               *
               * @param request {Object} request Request parameters, if undefined _request object will be used.
               * @private
               */
              function _jsonp(request) {
                  // When CORS is enabled, make sure we force the proper transport.
                  request.transport = "jsonp";
  
                  var rq = _request;
                  if ((request != null) && (typeof (request) !== 'undefined')) {
                      rq = request;
                  }
  
                  var url = rq.url;
                  if (rq.dispatchUrl != null) {
                      url += rq.dispatchUrl;
                  }
  
                  var data = rq.data;
                  if (rq.attachHeadersAsQueryString) {
                      url = _attachHeaders(rq);
                      if (data !== '') {
                          url += "&X-Atmosphere-Post-Body=" + encodeURIComponent(data);
                      }
                      data = '';
                  }
  
                  _jqxhr = jQuery.ajax({
                      url: url,
                      type: rq.method,
                      dataType: "jsonp",
                      error: function (jqXHR, textStatus, errorThrown) {
                          _response.error = true;
  
                          if (rq.openId) {
                              clearTimeout(rq.openId);
                          }
  
                          if (rq.reconnect && _requestCount++ < rq.maxReconnectOnClose) {
                              _open('re-connecting', rq.transport, rq);
                              _reconnect(_jqxhr, rq, rq.reconnectInterval);
                              rq.openId = setTimeout(function() {
                                  _triggerOpen(rq);
                              }, rq.reconnectInterval + 1000);
                          } else {
                              _onError(jqXHR.status, errorThrown);
                          }
                      },
                      jsonp: "jsonpTransport",
                      success: function (json) {
                          if (rq.reconnect) {
                              if (rq.maxRequest === -1 || rq.requestCount++ < rq.maxRequest) {
                                  _readHeaders(_jqxhr, rq);
  
                                  if (!rq.executeCallbackBeforeReconnect) {
                                      _reconnect(_jqxhr, rq, 0);
                                  }
  
                                  var msg = json.message;
                                  if (msg != null && typeof msg !== 'string') {
                                      try {
                                          msg = jQuery.stringifyJSON(msg);
                                      } catch (err) {
                                          // The message was partial
                                      }
                                  }
  
                                  var skipCallbackInvocation = _trackMessageSize(msg, rq, _response);
                                  if (!skipCallbackInvocation) {
                                      _prepareCallback(_response.responseBody, "messageReceived", 200, rq.transport);
                                  }
  
                                  if (rq.executeCallbackBeforeReconnect) {
                                      _reconnect(_jqxhr, rq, 0);
                                  }
                              } else {
                                  jQuery.atmosphere.log(_request.logLevel, ["JSONP reconnect maximum try reached " + _request.requestCount]);
                                  _onError(0, "maxRequest reached");
                              }
                          }
                      },
                      data: rq.data,
                      beforeSend: function (jqXHR) {
                          _doRequest(jqXHR, rq, false);
                      }
                  });
              }
  
              /**
               * Execute request using ajax transport.
               *
               * @param request {Object} request Request parameters, if undefined _request object will be used.
               * @private
               */
              function _ajax(request) {
                  var rq = _request;
                  if ((request != null) && (typeof (request) !== 'undefined')) {
                      rq = request;
                  }
  
                  var url = rq.url;
                  if (rq.dispatchUrl != null) {
                      url += rq.dispatchUrl;
                  }
  
                  var data = rq.data;
                  if (rq.attachHeadersAsQueryString) {
                      url = _attachHeaders(rq);
                      if (data !== '') {
                          url += "&X-Atmosphere-Post-Body=" + encodeURIComponent(data);
                      }
                      data = '';
                  }
  
                  var async = typeof (rq.async) !== 'undefined' ? rq.async : true;
                  _jqxhr = jQuery.ajax({
                      url: url,
                      type: rq.method,
                      error: function (jqXHR, textStatus, errorThrown) {
                          _response.error = true;
                          if (jqXHR.status < 300) {
                              _reconnect(_jqxhr, rq);
                          } else {
                              _onError(jqXHR.status, errorThrown);
                          }
                      },
                      success: function (data, textStatus, jqXHR) {
  
                          if (rq.reconnect) {
                              if (rq.maxRequest === -1 || rq.requestCount++ < rq.maxRequest) {
                                  if (!rq.executeCallbackBeforeReconnect) {
                                      _reconnect(_jqxhr, rq, 0);
                                  }
                                  var skipCallbackInvocation = _trackMessageSize(data, rq, _response);
                                  if (!skipCallbackInvocation) {
                                      _prepareCallback(_response.responseBody, "messageReceived", 200, rq.transport);
                                  }
  
                                  if (rq.executeCallbackBeforeReconnect) {
                                      _reconnect(_jqxhr, rq, 0);
                                  }
                              } else {
                                  jQuery.atmosphere.log(_request.logLevel, ["AJAX reconnect maximum try reached " + _request.requestCount]);
                                  _onError(0, "maxRequest reached");
                              }
                          }
                      },
                      beforeSend: function (jqXHR) {
                          _doRequest(jqXHR, rq, false);
                      },
                      crossDomain: rq.enableXDR,
                      async: async
                  });
              }
  
              /**
               * Build websocket object.
               *
               * @param location {string} Web socket url.
               * @returns {websocket} Web socket object.
               * @private
               */
              function _getWebSocket(location) {
                  if (_request.webSocketImpl != null) {
                      return _request.webSocketImpl;
                  } else {
                      if (window.WebSocket) {
                          return new WebSocket(location);
                      } else {
                          return new MozWebSocket(location);
                      }
                  }
              }
  
              /**
               * Build web socket url from request url.
               *
               * @return {string} Web socket url (start with "ws" or "wss" for secure web socket).
               * @private
               */
              function _buildWebSocketUrl() {
                  var url = _attachHeaders(_request);
  
                  return decodeURI(jQuery('<a href="' + url + '"/>')[0].href.replace(/^http/, "ws"));
              }
  
              /**
               * Build SSE url from request url.
               *
               * @return a url with Atmosphere's headers
               * @private
               */
              function _buildSSEUrl() {
                  var url = _attachHeaders(_request);
                  return url;
              }
  
              /**
               * Open SSE. <br>
               * Automatically use fallback transport if SSE can't be opened.
               *
               * @private
               */
              function _executeSSE(sseOpened) {
  
                  _response.transport = "sse";
  
                  var location = _buildSSEUrl(_request.url);
  
                  if (_request.logLevel === 'debug') {
                      jQuery.atmosphere.debug("Invoking executeSSE");
                      jQuery.atmosphere.debug("Using URL: " + location);
                  }
  
                  if (_request.enableProtocol && sseOpened) {
                      var time = jQuery.now() - _request.ctime;
                      _request.lastTimestamp = Number(_request.stime) + Number(time);
                  }
  
                  if (sseOpened && !_request.reconnect) {
                      if (_sse != null) {
                          _clearState();
                      }
                      return;
                  }
  
                  try {
                      _sse = new EventSource(location, {
                          withCredentials: _request.withCredentials
                      });
                  } catch (e) {
                      _onError(0, e);
                      _reconnectWithFallbackTransport("SSE failed. Downgrading to fallback transport and resending");
                      return;
                  }
  
                  if (_request.connectTimeout > 0) {
                      _request.id = setTimeout(function () {
                          if (!sseOpened) {
                              _clearState();
                          }
                      }, _request.connectTimeout);
                  }
  
                  _sse.onopen = function (event) {
                      _timeout(_request);
                      if (_request.logLevel === 'debug') {
                          jQuery.atmosphere.debug("SSE successfully opened");
                      }
  
                      if (!_request.enableProtocol) {
                          if (!sseOpened) {
                              _open('opening', "sse", _request);
                          } else {
                              _open('re-opening', "sse", _request);
                          }
                      } else if (_request.isReopen) {
                          _request.isReopen = false;
                          _open('re-opening', _request.transport, _request);
                      }
                      sseOpened = true;
  
                      if (_request.method === 'POST') {
                          _response.state = "messageReceived";
                          _sse.send(_request.data);
                      }
                  };
  
                  _sse.onmessage = function (message) {
                      _timeout(_request);
                      if (!_request.enableXDR && message.origin !== window.location.protocol + "//" + window.location.host) {
                          jQuery.atmosphere.log(_request.logLevel, ["Origin was not " + window.location.protocol + "//" + window.location.host]);
                          return;
                      }
  
                      _response.state = 'messageReceived';
                      _response.status = 200;
  
                      message = message.data;
                      var skipCallbackInvocation = _trackMessageSize(message, _request, _response);
                      if (!skipCallbackInvocation) {
                          _invokeCallback();
                          _response.responseBody = '';
                          _response.messages = [];
                      }
                  };
  
                  _sse.onerror = function (message) {
                      clearTimeout(_request.id);
  
                      if (_response.closedByClientTimeout) return;
  
                      _invokeClose(sseOpened);
                      _clearState();
  
                      if (_abordingConnection) {
                          jQuery.atmosphere.log(_request.logLevel, ["SSE closed normally"]);
                      } else if (!sseOpened) {
                          _reconnectWithFallbackTransport("SSE failed. Downgrading to fallback transport and resending");
                      } else if (_request.reconnect && (_response.transport === 'sse')) {
                          if (_requestCount++ < _request.maxReconnectOnClose) {
                              _open('re-connecting', _request.transport, _request);
                              if (_request.reconnectInterval > 0) {
                                  _request.reconnectId = setTimeout(function () {
                                      _executeSSE(true);
                                  }, _request.reconnectInterval);
                              } else {
                                  _executeSSE(true);
                              }
                              _response.responseBody = "";
                              _response.messages = [];
                          } else {
                              jQuery.atmosphere.log(_request.logLevel, ["SSE reconnect maximum try reached " + _requestCount]);
                              _onError(0, "maxReconnectOnClose reached");
                          }
                      }
                  };
              }
  
              /**
               * Open web socket. <br>
               * Automatically use fallback transport if web socket can't be opened.
               *
               * @private
               */
              function _executeWebSocket(webSocketOpened) {
  
                  _response.transport = "websocket";
  
                  if (_request.enableProtocol && webSocketOpened) {
                      var time = jQuery.now() - _request.ctime;
                      _request.lastTimestamp = Number(_request.stime) + Number(time);
                  }
  
                  var location = _buildWebSocketUrl(_request.url);
                  if (_request.logLevel === 'debug') {
                      jQuery.atmosphere.debug("Invoking executeWebSocket");
                      jQuery.atmosphere.debug("Using URL: " + location);
                  }
  
                  if (webSocketOpened && !_request.reconnect) {
                      if (_websocket != null) {
                          _clearState();
                      }
                      return;
                  }
  
                  _websocket = _getWebSocket(location);
                  if (_request.webSocketBinaryType != null) {
                      _websocket.binaryType = _request.webSocketBinaryType;
                  }
  
                  if (_request.connectTimeout > 0) {
                      _request.id = setTimeout(function () {
                          if (!webSocketOpened) {
                              var _message = {
                                  code: 1002,
                                  reason: "",
                                  wasClean: false
                              };
                              _websocket.onclose(_message);
                              // Close it anyway
                              try {
                                  _clearState();
                              } catch (e) {
                              }
                              return;
                          }
  
                      }, _request.connectTimeout);
                  }
  
                  _websocket.onopen = function (message) {
                      _timeout(_request);
                      if (_request.logLevel === 'debug') {
                          jQuery.atmosphere.debug("Websocket successfully opened");
                      }
  
                      var reopening = webSocketOpened;
  
                      webSocketOpened = true;
                      if(_websocket != null) {
                          _websocket.webSocketOpened = webSocketOpened;
                      }
  
                      if (!_request.enableProtocol) {
                          if (reopening) {
                              _open('re-opening', "websocket", _request);
                          } else {
                              _open('opening', "websocket", _request);
                          }
                      }
  
                      if (_websocket != null) {
                          if (_request.method === 'POST') {
                              _response.state = "messageReceived";
                              _websocket.send(_request.data);
                          }
                      }
                  };
  
                  _websocket.onmessage = function (message) {
                      _timeout(_request);
  
                      _response.state = 'messageReceived';
                      _response.status = 200;
  
                      message = message.data;
                      var isString = typeof (message) === 'string';
                      if (isString) {
                          var skipCallbackInvocation = _trackMessageSize(message, _request, _response);
                          if (!skipCallbackInvocation) {
                              _invokeCallback();
                              _response.responseBody = '';
                              _response.messages = [];
                          }
                      } else {
                          if (!_handleProtocol(_request, message))
                              return;
  
                          _response.responseBody = message;
                          _invokeCallback();
                          _response.responseBody = null;
                      }
                  };
  
                  _websocket.onerror = function (message) {
                      clearTimeout(_request.id);
                  };
  
                  _websocket.onclose = function (message) {
                      if (_response.state === 'closed')
                          return;
                      clearTimeout(_request.id);
  
                      var reason = message.reason;
                      if (reason === "") {
                          switch (message.code) {
                              case 1000:
                                  reason = "Normal closure; the connection successfully completed whatever purpose for which " + "it was created.";
                                  break;
                              case 1001:
                                  reason = "The endpoint is going away, either because of a server failure or because the "
                                      + "browser is navigating away from the page that opened the connection.";
                                  break;
                              case 1002:
                                  reason = "The endpoint is terminating the connection due to a protocol error.";
                                  break;
                              case 1003:
                                  reason = "The connection is being terminated because the endpoint received data of a type it "
                                      + "cannot accept (for example, a text-only endpoint received binary data).";
                                  break;
                              case 1004:
                                  reason = "The endpoint is terminating the connection because a data frame was received that " + "is too large.";
                                  break;
                              case 1005:
                                  reason = "Unknown: no status code was provided even though one was expected.";
                                  break;
                              case 1006:
                                  reason = "Connection was closed abnormally (that is, with no close frame being sent).";
                                  break;
                          }
                      }
  
                      if (_request.logLevel === 'warn') {
                          jQuery.atmosphere.warn("Websocket closed, reason: " + reason);
                          jQuery.atmosphere.warn("Websocket closed, wasClean: " + message.wasClean);
                      }
  
                      if (_response.closedByClientTimeout) {
                          return;
                      }
  
                      _invokeClose(webSocketOpened);
  
                      _response.state = 'closed';
  
                      if (_abordingConnection) {
                          jQuery.atmosphere.log(_request.logLevel, ["Websocket closed normally"]);
                      } else if (!webSocketOpened) {
                          _reconnectWithFallbackTransport("Websocket failed. Downgrading to Comet and resending");
  
                      } else if (_request.reconnect && _response.transport === 'websocket') {
                          _clearState();
                          if (_requestCount++ < _request.maxReconnectOnClose) {
                              _open('re-connecting', _request.transport, _request);
                              if (_request.reconnectInterval > 0) {
                                  _request.reconnectId = setTimeout(function () {
                                      _response.responseBody = "";
                                      _response.messages = [];
                                      _executeWebSocket(true);
                                  }, _request.reconnectInterval);
                              } else {
                                  _response.responseBody = "";
                                  _response.messages = [];
                                  _executeWebSocket(true);
                              }
                          } else {
                              jQuery.atmosphere.log(_request.logLevel, ["Websocket reconnect maximum try reached " + _request.requestCount]);
                              if (_request.logLevel === 'warn') {
                                  jQuery.atmosphere.warn("Websocket error, reason: " + message.reason);
                              }
                              _onError(0, "maxReconnectOnClose reached");
                          }
                      }
                  };
  
                  if (_websocket.url === undefined) {
                      // Android 4.1 does not really support websockets and fails silently
                      _websocket.onclose({
                          reason: "Android 4.1 does not support websockets.",
                          wasClean: false
                      });
                  }
              }
  
              function _handleProtocol(request, message) {
                  // The first messages is always the uuid.
                  var b = true;
  
                  if (request.transport === 'polling') return b;
  
                  if (jQuery.trim(message).length !== 0 && request.enableProtocol && request.firstMessage) {
                      request.firstMessage = false;
                      var messages = message.split(request.messageDelimiter);
                      var pos = messages.length === 2 ? 0 : 1;
                      request.uuid = jQuery.trim(messages[pos]);
                      request.stime = jQuery.trim(messages[pos + 1]);
                      b = false;
                      if (request.transport !== 'long-polling') {
                          _triggerOpen(request);
                      }
  
                      jQuery.atmosphere.uuid = request.uuid;
                  } else if (request.enableProtocol && request.firstMessage) {
                      // In case we are getting some junk from IE
                      b = false;
                  } else {
                      _triggerOpen(request);
                  }
                  return b;
              }
  
              function _timeout(_request) {
                  clearTimeout(_request.id);
                  if (_request.timeout > 0 && _request.transport !== 'polling') {
                      _request.id = setTimeout(function () {
                          _onClientTimeout(_request);
                          _disconnect();
                          _clearState();
                      }, _request.timeout);
                  }
              }
  
              function _onClientTimeout(_request) {
                  _response.closedByClientTimeout = true;
                  _response.state = 'closedByClient';
                  _response.responseBody = "";
                  _response.status = 408;
                  _response.messages = [];
                  _invokeCallback();
              }
  
              function _onError(code, reason) {
                  _clearState();
                  clearTimeout(_request.id);
                  _response.state = 'error';
                  _response.reasonPhrase = reason;
                  _response.responseBody = "";
                  _response.status = code;
                  _response.messages = [];
                  _invokeCallback();
              }
  
              /**
               * Track received message and make sure callbacks/functions are only invoked when the complete message has been received.
               *
               * @param message
               * @param request
               * @param response
               */
              function _trackMessageSize(message, request, response) {
                  if (!_handleProtocol(request, message))
                      return true;
                  if (message.length === 0)
                      return true;
  
                  if (request.trackMessageLength) {
                      // prepend partialMessage if any
                      message = response.partialMessage + message;
  
                      var messages = [];
                      var messageStart = message.indexOf(request.messageDelimiter);
                      while (messageStart !== -1) {
                          var str = jQuery.trim(message.substring(0, messageStart));
                          var messageLength = parseInt(str, 10);
                          if (isNaN(messageLength))
                              throw 'message length "' + str + '" is not a number';
                          messageStart += request.messageDelimiter.length;
                          if (messageStart + messageLength > message.length) {
                              // message not complete, so there is no trailing messageDelimiter
                              messageStart = -1;
                          } else {
                              // message complete, so add it
                              messages.push(message.substring(messageStart, messageStart + messageLength));
                              // remove consumed characters
                              message = message.substring(messageStart + messageLength, message.length);
                              messageStart = message.indexOf(request.messageDelimiter);
                          }
                      }
  
                      /* keep any remaining data */
                      response.partialMessage = message;
  
                      if (messages.length !== 0) {
                          response.responseBody = messages.join(request.messageDelimiter);
                          response.messages = messages;
                          return false;
                      } else {
                          response.responseBody = "";
                          response.messages = [];
                          return true;
                      }
                  } else {
                      response.responseBody = message;
                  }
                  return false;
              }
  
              /**
               * Reconnect request with fallback transport. <br>
               * Used in case websocket can't be opened.
               *
               * @private
               */
              function _reconnectWithFallbackTransport(errorMessage) {
                  jQuery.atmosphere.log(_request.logLevel, [errorMessage]);
  
                  if (typeof (_request.onTransportFailure) !== 'undefined') {
                      _request.onTransportFailure(errorMessage, _request);
                  } else if (typeof (jQuery.atmosphere.onTransportFailure) !== 'undefined') {
                      jQuery.atmosphere.onTransportFailure(errorMessage, _request);
                  }
  
                  _request.transport = _request.fallbackTransport;
                  var reconnectInterval = _request.connectTimeout === -1 ? 0 : _request.connectTimeout;
                  if (_request.reconnect && _request.transport !== 'none' || _request.transport == null) {
                      _request.method = _request.fallbackMethod;
                      _response.transport = _request.fallbackTransport;
                      _request.fallbackTransport = 'none';
                      if (reconnectInterval > 0) {
                          _request.reconnectId = setTimeout(function () {
                              _execute();
                          }, reconnectInterval);
                      } else {
                          _execute();
                      }
                  } else {
                      _onError(500, "Unable to reconnect with fallback transport");
                  }
              }
  
              /**
               * Get url from request and attach headers to it.
               *
               * @param request {Object} request Request parameters, if undefined _request object will be used.
               *
               * @returns {Object} Request object, if undefined, _request object will be used.
               * @private
               */
              function _attachHeaders(request, url) {
                  var rq = _request;
                  if ((request != null) && (typeof (request) !== 'undefined')) {
                      rq = request;
                  }
  
                  if (url == null) {
                      url = rq.url;
                  }
  
                  // If not enabled
                  if (!rq.attachHeadersAsQueryString)
                      return url;
  
                  // If already added
                  if (url.indexOf("X-Atmosphere-Framework") !== -1) {
                      return url;
                  }
  
                  url += (url.indexOf('?') !== -1) ? '&' : '?';
                  url += "X-Atmosphere-tracking-id=" + rq.uuid;
                  url += "&X-Atmosphere-Framework=" + jQuery.atmosphere.version;
                  url += "&X-Atmosphere-Transport=" + rq.transport;
  
                  if (rq.trackMessageLength) {
                      url += "&X-Atmosphere-TrackMessageSize=" + "true";
                  }
  
                  if (rq.lastTimestamp != null) {
                      url += "&X-Cache-Date=" + rq.lastTimestamp;
                  } else {
                      url += "&X-Cache-Date=" + 0;
                  }
  
                  if (rq.contentType !== '') {
                      url += "&Content-Type=" + (rq.transport === 'websocket' ? rq.contentType : encodeURIComponent(rq.contentType));
                  }
  
                  if (rq.enableProtocol) {
                      url += "&X-atmo-protocol=true";
                  }
  
                  jQuery.each(rq.headers, function (name, value) {
                      var h = jQuery.isFunction(value) ? value.call(this, rq, request, _response) : value;
                      if (h != null) {
                          url += "&" + encodeURIComponent(name) + "=" + encodeURIComponent(h);
                      }
                  });
  
                  return url;
              }
  
              function _triggerOpen(rq) {
                  if (!rq.isOpen) {
                      rq.isOpen = true;
                      _open('opening', rq.transport, rq);
                  } else if (rq.isReopen) {
                      rq.isReopen = false;
                      _open('re-opening', rq.transport, rq);
                  }
              }
  
              /**
               * Execute ajax request. <br>
               *
               * @param request {Object} request Request parameters, if undefined _request object will be used.
               * @private
               */
              function _executeRequest(request) {
                  var rq = _request;
                  if ((request != null) || (typeof (request) !== 'undefined')) {
                      rq = request;
                  }
  
                  rq.lastIndex = 0;
                  rq.readyState = 0;
  
                  // CORS fake using JSONP
                  if ((rq.transport === 'jsonp') || ((rq.enableXDR) && (jQuery.atmosphere.checkCORSSupport()))) {
                      _jsonp(rq);
                      return;
                  }
  
                  if (rq.transport === 'ajax') {
                      _ajax(request);
                      return;
                  }
  
                  if (jQuery.browser.msie && +jQuery.browser.version.split(".")[0] < 10) {
                      if ((rq.transport === 'streaming')) {
                          if (rq.enableXDR && window.XDomainRequest) {
                              _ieXDR(rq);
                          } else {
                              _ieStreaming(rq);
                          }
                          return;
                      }
  
                      if ((rq.enableXDR) && (window.XDomainRequest)) {
                          _ieXDR(rq);
                          return;
                      }
                  }
  
                  var reconnectF = function () {
                      rq.lastIndex = 0;
                      if (rq.reconnect && _requestCount++ < rq.maxReconnectOnClose) {
                          _open('re-connecting', request.transport, request);
                          _reconnect(ajaxRequest, rq, request.reconnectInterval);
                      } else {
                          _onError(0, "maxReconnectOnClose reached");
                      }
                  };
  
                  if (rq.reconnect && (rq.maxRequest === -1 || rq.requestCount++ < rq.maxRequest)) {
                      var ajaxRequest = jQuery.ajaxSettings.xhr();
                      ajaxRequest.hasData = false;
  
                      _doRequest(ajaxRequest, rq, true);
  
                      if (rq.suspend) {
                          _activeRequest = ajaxRequest;
                      }
  
                      if (rq.transport !== 'polling') {
                          _response.transport = rq.transport;
  
                          ajaxRequest.onabort = function () {
                              _invokeClose(true);
                          };
  
                          ajaxRequest.onerror = function () {
                              _response.error = true;
                              try {
                                  _response.status = XMLHttpRequest.status;
                              } catch (e) {
                                  _response.status = 500;
                              }
  
                              if (!_response.status) {
                                  _response.status = 500;
                              }
                              if (!_response.errorHandled) {
                                  _clearState();
                                  reconnectF();
                              }
                          };
                      }
  
                      ajaxRequest.onreadystatechange = function () {
                          if (_abordingConnection) {
                              return;
                          }
  
                          _response.error = null;
                          var skipCallbackInvocation = false;
                          var update = false;
  
                          if (rq.transport === 'streaming' && rq.readyState > 2 && ajaxRequest.readyState === 4) {
                              if (rq.reconnectingOnLength) {
                                  return;
                              }
                              _clearState();
                              reconnectF();
                              return;
                          }
  
                          rq.readyState = ajaxRequest.readyState;
  
                          if (rq.transport === 'streaming' && ajaxRequest.readyState >= 3) {
                              update = true;
                          } else if (rq.transport === 'long-polling' && ajaxRequest.readyState === 4) {
                              update = true;
                          }
                          _timeout(_request);
  
                          if (rq.transport !== 'polling') {
                              // MSIE 9 and lower status can be higher than 1000, Chrome can be 0
                              var status = 200;
                              if (ajaxRequest.readyState === 4) {
                                  status = ajaxRequest.status > 1000 ? 0 : ajaxRequest.status;
                              }
  
                              if (status >= 300 || status === 0) {
                                  // Prevent onerror callback to be called
                                  _response.errorHandled = true;
                                  _clearState();
                                  reconnectF();
                                  return;
                              }
                              
                              // Firefox incorrectly send statechange 0->2 when a reconnect attempt fails. The above checks ensure that onopen is not called for these
                              if ((!rq.enableProtocol || !request.firstMessage) && ajaxRequest.readyState === 2) {
                                  _triggerOpen(rq);
                              }
                          } else if (ajaxRequest.readyState === 4) {
                              update = true;
                          }
  
                          if (update) {
                              var responseText = ajaxRequest.responseText;
  
                              if (jQuery.trim(responseText).length === 0 && rq.transport === 'long-polling') {
                                  // For browser that aren't support onabort
                                  if (!ajaxRequest.hasData) {
                                      _reconnect(ajaxRequest, rq, 0);
                                  } else {
                                      ajaxRequest.hasData = false;
                                  }
                                  return;
                              }
                              ajaxRequest.hasData = true;
  
                              _readHeaders(ajaxRequest, _request);
  
                              if (rq.transport === 'streaming') {
                                  if (!jQuery.browser.opera) {
                                      var message = responseText.substring(rq.lastIndex, responseText.length);
                                      skipCallbackInvocation = _trackMessageSize(message, rq, _response);
  
                                      rq.lastIndex = responseText.length;
                                      if (skipCallbackInvocation) {
                                          return;
                                      }
                                  } else {
                                      jQuery.atmosphere.iterate(function () {
                                          if (_response.status !== 500 && ajaxRequest.responseText.length > rq.lastIndex) {
                                              try {
                                                  _response.status = ajaxRequest.status;
                                                  _response.headers = parseHeaders(ajaxRequest.getAllResponseHeaders());
  
                                                  _readHeaders(ajaxRequest, _request);
  
                                              } catch (e) {
                                                  _response.status = 404;
                                              }
                                              _timeout(_request);
  
                                              _response.state = "messageReceived";
                                              var message = ajaxRequest.responseText.substring(rq.lastIndex);
                                              rq.lastIndex = ajaxRequest.responseText.length;
  
                                              skipCallbackInvocation = _trackMessageSize(message, rq, _response);
                                              if (!skipCallbackInvocation) {
                                                  _invokeCallback();
                                              }
  
                                              _verifyStreamingLength(ajaxRequest, rq);
                                          } else if (_response.status > 400) {
                                              // Prevent replaying the last message.
                                              rq.lastIndex = ajaxRequest.responseText.length;
                                              return false;
                                          }
                                      }, 0);
                                  }
                              } else {
                                  skipCallbackInvocation = _trackMessageSize(responseText, rq, _response);
                              }
  
                              try {
                                  _response.status = ajaxRequest.status;
                                  _response.headers = parseHeaders(ajaxRequest.getAllResponseHeaders());
  
                                  _readHeaders(ajaxRequest, rq);
                              } catch (e) {
                                  _response.status = 404;
                              }
  
                              if (rq.suspend) {
                                  _response.state = _response.status === 0 ? "closed" : "messageReceived";
                              } else {
                                  _response.state = "messagePublished";
                              }
  
                              var isAllowedToReconnect = request.transport !== 'streaming' && request.transport !== 'polling';;
                              if (isAllowedToReconnect && !rq.executeCallbackBeforeReconnect) {
                                  _reconnect(ajaxRequest, rq, 0);
                              }
  
                              if (_response.responseBody.length !== 0 && !skipCallbackInvocation)
                                  _invokeCallback();
  
                              if (isAllowedToReconnect && rq.executeCallbackBeforeReconnect) {
                                  _reconnect(ajaxRequest, rq, 0);
                              }
  
                              _verifyStreamingLength(ajaxRequest, rq);
                          }
                      };
  
                      ajaxRequest.send(rq.data);
                      _subscribed = true;
                  } else {
                      if (rq.logLevel === 'debug') {
                          jQuery.atmosphere.log(rq.logLevel, ["Max re-connection reached."]);
                      }
                      _onError(0, "maxRequest reached");
                  }
              }
  
              /**
               * Do ajax request.
               *
               * @param ajaxRequest Ajax request.
               * @param request Request parameters.
               * @param create If ajax request has to be open.
               */
              function _doRequest(ajaxRequest, request, create) {
                  // Prevent Android to cache request
                  var url = request.url;
                  if (request.dispatchUrl != null && request.method === 'POST') {
                      url += request.dispatchUrl;
                  }
                  url = _attachHeaders(request, url);
                  url = jQuery.atmosphere.prepareURL(url);
  
                  if (create) {
                      ajaxRequest.open(request.method, url, true);
                      if (request.connectTimeout > 0) {
                          request.id = setTimeout(function () {
                              if (request.requestCount === 0) {
                                  _clearState();
                                  _prepareCallback("Connect timeout", "closed", 200, request.transport);
                              }
                          }, request.connectTimeout);
                      }
                  }
  
                  if (_request.withCredentials) {
                      if ("withCredentials" in ajaxRequest) {
                          ajaxRequest.withCredentials = true;
                      }
                  }
  
                  if (!_request.dropHeaders) {
                      ajaxRequest.setRequestHeader("X-Atmosphere-Framework", jQuery.atmosphere.version);
                      ajaxRequest.setRequestHeader("X-Atmosphere-Transport", request.transport);
                      if (request.lastTimestamp != null) {
                          ajaxRequest.setRequestHeader("X-Cache-Date", request.lastTimestamp);
                      } else {
                          ajaxRequest.setRequestHeader("X-Cache-Date", 0);
                      }
  
                      if (request.trackMessageLength) {
                          ajaxRequest.setRequestHeader("X-Atmosphere-TrackMessageSize", "true");
                      }
                      ajaxRequest.setRequestHeader("X-Atmosphere-tracking-id", request.uuid);
  
                      jQuery.each(request.headers, function (name, value) {
                          var h = jQuery.isFunction(value) ? value.call(this, ajaxRequest, request, create, _response) : value;
                          if (h != null) {
                              ajaxRequest.setRequestHeader(name, h);
                          }
                      });
                  }
  
                  if (request.contentType !== '') {
                      ajaxRequest.setRequestHeader("Content-Type", request.contentType);
                  }
              }
  
              function _reconnect(ajaxRequest, request, reconnectInterval) {
                  if (request.reconnect || (request.suspend && _subscribed)) {
                      var status = 0;
                      if (ajaxRequest.readyState > 1) {
                          status = ajaxRequest.status > 1000 ? 0 : ajaxRequest.status;
                      }
                      _response.status = status === 0 ? 204 : status;
                      _response.reason = status === 0 ? "Server resumed the connection or down." : "OK";
  
                      // Reconnect immediately
                      clearTimeout(request.id);
                      if (request.reconnectId) {
                          clearTimeout(request.reconnectId);
                          delete request.reconnectId;
                      }
  
                      if (reconnectInterval > 0) {
                          setTimeout(function () {
                              _request.reconnectId = _executeRequest(request);
                          }, reconnectInterval);
                      } else {
                          _executeRequest(request);
                      }
                  }
              }
  
              function _tryingToReconnect(response) {
                  response.state = 're-connecting';
                  _invokeFunction(response);
              }
  
              // From jquery-stream, which is APL2 licensed as well.
              function _ieXDR(request) {
                  if (request.transport !== "polling") {
                      _ieStream = _configureXDR(request);
                      _ieStream.open();
                  } else {
                      _configureXDR(request).open();
                  }
              }
  
              function _configureXDR(request) {
                  var rq = _request;
                  if ((request != null) && (typeof (request) !== 'undefined')) {
                      rq = request;
                  }
  
                  var transport = rq.transport;
                  var lastIndex = 0;
                  var xdr = new window.XDomainRequest();
  
                  var reconnect = function () {
                      if (rq.transport === "long-polling" && (rq.reconnect && (rq.maxRequest === -1 || rq.requestCount++ < rq.maxRequest))) {
                          xdr.status = 200;
                          _ieXDR(rq);
                      }
                  };
  
                  var rewriteURL = rq.rewriteURL || function (url) {
                      // Maintaining session by rewriting URL
                      // http://stackoverflow.com/questions/6453779/maintaining-session-by-rewriting-url
                      var match = /(?:^|;\s*)(JSESSIONID|PHPSESSID)=([^;]*)/.exec(document.cookie);
  
                      switch (match && match[1]) {
                          case "JSESSIONID":
                              return url.replace(/;jsessionid=[^\?]*|(\?)|$/, ";jsessionid=" + match[2] + "$1");
                          case "PHPSESSID":
                              return url.replace(/\?PHPSESSID=[^&]*&?|\?|$/, "?PHPSESSID=" + match[2] + "&").replace(/&$/, "");
                      }
                      return url;
                  };
  
                  // Handles open and message event
                  xdr.onprogress = function () {
                      handle(xdr);
                  };
                  // Handles error event
                  xdr.onerror = function () {
                      // If the server doesn't send anything back to XDR will fail with polling
                      if (rq.transport !== 'polling') {
                          _clearState();
                          if (_requestCount++ < rq.maxReconnectOnClose) {
                              if (rq.reconnectInterval > 0) {
                                  rq.reconnectId = setTimeout(function () {
                                      _open('re-connecting', request.transport, request);
                                      _ieXDR(rq);
                                  }, rq.reconnectInterval);
                              } else {
                                  _open('re-connecting', request.transport, request);
                                  _ieXDR(rq);
                              }
                          } else {
                              _onError(0, "maxReconnectOnClose reached");
                          }
                      }
                  };
  
                  // Handles close event
                  xdr.onload = function () {
                  };
  
                  var handle = function (xdr) {
                      clearTimeout(rq.id);
                      var message = xdr.responseText;
  
                      message = message.substring(lastIndex);
                      lastIndex += message.length;
  
                      if (transport !== 'polling') {
                          _timeout(rq);
  
                          var skipCallbackInvocation = _trackMessageSize(message, rq, _response);
  
                          if (transport === 'long-polling' && jQuery.trim(message).length === 0)
                              return;
  
                          if (rq.executeCallbackBeforeReconnect) {
                              reconnect();
                          }
  
                          if (!skipCallbackInvocation) {
                              _prepareCallback(_response.responseBody, "messageReceived", 200, transport);
                          }
  
                          if (!rq.executeCallbackBeforeReconnect) {
                              reconnect();
                          }
                      }
                  };
  
                  return {
                      open: function () {
                          var url = rq.url;
                          if (rq.dispatchUrl != null) {
                              url += rq.dispatchUrl;
                          }
                          url = _attachHeaders(rq, url);
                          xdr.open(rq.method, rewriteURL(url));
                          if (rq.method === 'GET') {
                              xdr.send();
                          } else {
                              xdr.send(rq.data);
                          }
  
                          if (rq.connectTimeout > 0) {
                              rq.id = setTimeout(function () {
                                  if (rq.requestCount === 0) {
                                      _clearState();
                                      _prepareCallback("Connect timeout", "closed", 200, rq.transport);
                                  }
                              }, rq.connectTimeout);
                          }
                      },
                      close: function () {
                          xdr.abort();
                      }
                  };
              }
  
              function _ieStreaming(request) {
                  _ieStream = _configureIE(request);
                  _ieStream.open();
              }
  
              function _configureIE(request) {
                  var rq = _request;
                  if ((request != null) && (typeof (request) !== 'undefined')) {
                      rq = request;
                  }
  
                  var stop;
                  var doc = new window.ActiveXObject("htmlfile");
  
                  doc.open();
                  doc.close();
  
                  var url = rq.url;
                  if (rq.dispatchUrl != null) {
                      url += rq.dispatchUrl;
                  }
  
                  if (rq.transport !== 'polling') {
                      _response.transport = rq.transport;
                  }
  
                  return {
                      open: function () {
                          var iframe = doc.createElement("iframe");
  
                          url = _attachHeaders(rq);
                          if (rq.data !== '') {
                              url += "&X-Atmosphere-Post-Body=" + encodeURIComponent(rq.data);
                          }
  
                          // Finally attach a timestamp to prevent Android and IE caching.
                          url = jQuery.atmosphere.prepareURL(url);
  
                          iframe.src = url;
                          doc.body.appendChild(iframe);
  
                          // For the server to respond in a consistent format regardless of user agent, we polls response text
                          var cdoc = iframe.contentDocument || iframe.contentWindow.document;
  
                          stop = jQuery.atmosphere.iterate(function () {
                              try {
                                  if (!cdoc.firstChild) {
                                      return;
                                  }
  
                                  // Detects connection failure
                                  if (cdoc.readyState === "complete") {
                                      try {
                                          jQuery.noop(cdoc.fileSize);
                                      } catch (e) {
                                          _prepareCallback("Connection Failure", "error", 500, rq.transport);
                                          return false;
                                      }
                                  }
  
                                  var res = cdoc.body ? cdoc.body.lastChild : cdoc;
                                  var readResponse = function () {
                                      // Clones the element not to disturb the original one
                                      var clone = res.cloneNode(true);
  
                                      // If the last character is a carriage return or a line feed, IE ignores it in the innerText property
                                      // therefore, we add another non-newline character to preserve it
                                      clone.appendChild(cdoc.createTextNode("."));
  
                                      var text = clone.innerText;
  
                                      text = text.substring(0, text.length - 1);
                                      return text;
  
                                  };
  
                                  // To support text/html content type
                                  if (!jQuery.nodeName(res, "pre")) {
                                      // Injects a plaintext element which renders text without interpreting the HTML and cannot be stopped
                                      // it is deprecated in HTML5, but still works
                                      var head = cdoc.head || cdoc.getElementsByTagName("head")[0] || cdoc.documentElement || cdoc;
                                      var script = cdoc.createElement("script");
  
                                      script.text = "document.write('<plaintext>')";
  
                                      head.insertBefore(script, head.firstChild);
                                      head.removeChild(script);
  
                                      // The plaintext element will be the response container
                                      res = cdoc.body.lastChild;
                                  }
  
                                  if (rq.closed) {
                                      rq.isReopen = true;
                                  }
  
                                  // Handles message and close event
                                  stop = jQuery.atmosphere.iterate(function () {
                                      var text = readResponse();
                                      if (text.length > rq.lastIndex) {
                                          _timeout(_request);
  
                                          _response.status = 200;
                                          _response.error = null;
  
                                          // Empties response every time that it is handled
                                          res.innerText = "";
                                          var skipCallbackInvocation = _trackMessageSize(text, rq, _response);
                                          if (skipCallbackInvocation) {
                                              return "";
                                          }
  
                                          _prepareCallback(_response.responseBody, "messageReceived", 200, rq.transport);
                                      }
  
                                      rq.lastIndex = 0;
  
                                      if (cdoc.readyState === "complete") {
                                          _invokeClose(true);
                                          _open('re-connecting', rq.transport, rq);
                                          if (rq.reconnectInterval > 0) {
                                              rq.reconnectId = setTimeout(function () {
                                                  _ieStreaming(rq);
                                              }, rq.reconnectInterval);
                                          } else {
                                              _ieStreaming(rq);
                                          }
                                          return false;
                                      }
                                  }, null);
  
                                  return false;
                              } catch (err) {
                                  _response.error = true;
                                  _open('re-connecting', rq.transport, rq);
                                  if (_requestCount++ < rq.maxReconnectOnClose) {
                                      if (rq.reconnectInterval > 0) {
                                          rq.reconnectId = setTimeout(function () {
                                              _ieStreaming(rq);
                                          }, rq.reconnectInterval);
                                      } else {
                                          _ieStreaming(rq);
                                      }
                                  } else {
                                      _onError(0, "maxReconnectOnClose reached");
                                  }
                                  doc.execCommand("Stop");
                                  doc.close();
                                  return false;
                              }
                          });
                      },
  
                      close: function () {
                          if (stop) {
                              stop();
                          }
  
                          doc.execCommand("Stop");
                          _invokeClose(true);
                      }
                  };
              }
  
              /**
               * Send message. <br>
               * Will be automatically dispatch to other connected.
               *
               * @param {Object, string} Message to send.
               * @private
               */
              function _push(message) {
  
                  if (_localStorageService != null) {
                      _pushLocal(message);
                  } else if (_activeRequest != null || _sse != null) {
                      _pushAjaxMessage(message);
                  } else if (_ieStream != null) {
                      _pushIE(message);
                  } else if (_jqxhr != null) {
                      _pushJsonp(message);
                  } else if (_websocket != null) {
                      _pushWebSocket(message);
                  } else {
                      _onError(0, "No suspended connection available");
                      jQuery.atmosphere.error("No suspended connection available. Make sure atmosphere.subscribe has been called and request.onOpen invoked before invoking this method");
                  }
              }
  
              function _pushOnClose(message) {
                  var rq = _getPushRequest(message);
                  rq.transport = "ajax";
                  rq.method = "GET";
                  rq.async = false;
                  rq.reconnect = false;
                  _executeRequest(rq);
              }
  
              function _pushLocal(message) {
                  _localStorageService.send(message);
              }
  
              function _intraPush(message) {
                  // IE 9 will crash if not.
                  if (message.length === 0)
                      return;
  
                  try {
                      if (_localStorageService) {
                          _localStorageService.localSend(message);
                      } else if (_storageService) {
                          _storageService.signal("localMessage", jQuery.stringifyJSON({
                              id: guid,
                              event: message
                          }));
                      }
                  } catch (err) {
                      jQuery.atmosphere.error(err);
                  }
              }
  
              /**
               * Send a message using currently opened ajax request (using http-streaming or long-polling). <br>
               *
               * @param {string, Object} Message to send. This is an object, string message is saved in data member.
               * @private
               */
              function _pushAjaxMessage(message) {
                  var rq = _getPushRequest(message);
                  _executeRequest(rq);
              }
  
              /**
               * Send a message using currently opened ie streaming (using http-streaming or long-polling). <br>
               *
               * @param {string, Object} Message to send. This is an object, string message is saved in data member.
               * @private
               */
              function _pushIE(message) {
                  if (_request.enableXDR && jQuery.atmosphere.checkCORSSupport()) {
                      var rq = _getPushRequest(message);
                      // Do not reconnect since we are pushing.
                      rq.reconnect = false;
                      _jsonp(rq);
                  } else {
                      _pushAjaxMessage(message);
                  }
              }
  
              /**
               * Send a message using jsonp transport. <br>
               *
               * @param {string, Object} Message to send. This is an object, string message is saved in data member.
               * @private
               */
              function _pushJsonp(message) {
                  _pushAjaxMessage(message);
              }
  
              function _getStringMessage(message) {
                  var msg = message;
                  if (typeof (msg) === 'object') {
                      msg = message.data;
                  }
                  return msg;
              }
  
              /**
               * Build request use to push message using method 'POST' <br>. Transport is defined as 'polling' and 'suspend' is set to false.
               *
               * @return {Object} Request object use to push message.
               * @private
               */
              function _getPushRequest(message) {
                  var msg = _getStringMessage(message);
  
                  var rq = {
                      connected: false,
                      timeout: 60000,
                      method: 'POST',
                      url: _request.url,
                      contentType: _request.contentType,
                      headers: _request.headers,
                      reconnect: true,
                      callback: null,
                      data: msg,
                      suspend: false,
                      maxRequest: -1,
                      logLevel: 'info',
                      requestCount: 0,
                      withCredentials: _request.withCredentials,
                      transport: 'polling',
                      isOpen: true,
                      attachHeadersAsQueryString: true,
                      enableXDR: _request.enableXDR,
                      uuid: _request.uuid,
                      dispatchUrl: _request.dispatchUrl,
                      enableProtocol: false,
                      messageDelimiter: '|',
                      maxReconnectOnClose: _request.maxReconnectOnClose
                  };
  
                  if (typeof (message) === 'object') {
                      rq = jQuery.extend(rq, message);
                  }
  
                  return rq;
              }
  
              /**
               * Send a message using currently opened websocket. <br>
               *
               */
              function _pushWebSocket(message) {
                  var msg = jQuery.atmosphere.isBinary(message) ? message : _getStringMessage(message);
                  var data;
                  try {
                      if (_request.dispatchUrl != null) {
                          data = _request.webSocketPathDelimiter + _request.dispatchUrl + _request.webSocketPathDelimiter + msg;
                      } else {
                          data = msg;
                      }
  
                      if (!_websocket.webSocketOpened) {
                          jQuery.atmosphere.error("WebSocket not connected.");
                          return;
                      }
  
                      _websocket.send(data);
  
                  } catch (e) {
                      _websocket.onclose = function (message) {
                      };
                      _clearState();
  
                      _reconnectWithFallbackTransport("Websocket failed. Downgrading to Comet and resending " + data);
                      _pushAjaxMessage(message);
                  }
              }
  
              function _localMessage(message) {
                  var m = jQuery.parseJSON(message);
                  if (m.id !== guid) {
                      if (typeof (_request.onLocalMessage) !== 'undefined') {
                          _request.onLocalMessage(m.event);
                      } else if (typeof (jQuery.atmosphere.onLocalMessage) !== 'undefined') {
                          jQuery.atmosphere.onLocalMessage(m.event);
                      }
                  }
              }
  
              function _prepareCallback(messageBody, state, errorCode, transport) {
  
                  _response.responseBody = messageBody;
                  _response.transport = transport;
                  _response.status = errorCode;
                  _response.state = state;
  
                  _invokeCallback();
              }
  
              function _readHeaders(xdr, request) {
                  if (!request.readResponsesHeaders) {
                      if (!request.enableProtocol) {
                          request.lastTimestamp = jQuery.now();
                          request.uuid = jQuery.atmosphere.guid();
                      }
                  }
                  else {
                      try {
                          var tempDate = xdr.getResponseHeader('X-Cache-Date');
                          if (tempDate && tempDate != null && tempDate.length > 0) {
                              request.lastTimestamp = tempDate.split(" ").pop();
                          }
  
                          var tempUUID = xdr.getResponseHeader('X-Atmosphere-tracking-id');
                          if (tempUUID && tempUUID != null) {
                              request.uuid = tempUUID.split(" ").pop();
                          }
                      } catch (e) {
                      }
                  }
              }
  
              function _invokeFunction(response) {
                  _f(response, _request);
                  // Global
                  _f(response, jQuery.atmosphere);
              }
  
              function _f(response, f) {
                  switch (response.state) {
                      case "messageReceived":
                          _requestCount = 0;
                          if (typeof (f.onMessage) !== 'undefined')
                              f.onMessage(response);
                          break;
                      case "error":
                          if (typeof (f.onError) !== 'undefined')
                              f.onError(response);
                          break;
                      case "opening":
                          delete _request.closed;
                          if (typeof (f.onOpen) !== 'undefined')
                              f.onOpen(response);
                          break;
                      case "messagePublished":
                          if (typeof (f.onMessagePublished) !== 'undefined')
                              f.onMessagePublished(response);
                          break;
                      case "re-connecting":
                          if (typeof (f.onReconnect) !== 'undefined')
                              f.onReconnect(_request, response);
                          break;
                      case "closedByClient":
                          if (typeof (f.onClientTimeout) !== 'undefined')
                              f.onClientTimeout(_request);
                          break;
                      case "re-opening":
                          delete _request.closed;
                          if (typeof (f.onReopen) !== 'undefined')
                              f.onReopen(_request, response);
                          break;
                      case "fail-to-reconnect":
                          if (typeof (f.onFailureToReconnect) !== 'undefined')
                              f.onFailureToReconnect(_request, response);
                          break;
                      case "unsubscribe":
                      case "closed":
                          var closed = typeof (_request.closed) !== 'undefined' ? _request.closed : false;
                          if (typeof (f.onClose) !== 'undefined' && !closed)
                              f.onClose(response);
                          _request.closed = true;
                          break;
                  }
              }
  
              function _invokeClose(wasOpen) {
                  if (_response.state !== 'closed') {
                      _response.state = 'closed';
                      _response.responseBody = "";
                      _response.messages = [];
                      _response.status = !wasOpen ? 501 : 200;
                      _invokeCallback();
                  }
              }
  
              /**
               * Invoke request callbacks.
               *
               * @private
               */
              function _invokeCallback() {
                  var call = function (index, func) {
                      func(_response);
                  };
  
                  if (_localStorageService == null && _localSocketF != null) {
                      _localSocketF(_response.responseBody);
                  }
  
                  _request.reconnect = _request.mrequest;
  
                  var isString = typeof (_response.responseBody) === 'string';
                  var messages = (isString && _request.trackMessageLength) ? (_response.messages.length > 0 ? _response.messages : ['']) : new Array(
                      _response.responseBody);
                  for (var i = 0; i < messages.length; i++) {
  
                      if (messages.length > 1 && messages[i].length === 0) {
                          continue;
                      }
                      _response.responseBody = (isString) ? jQuery.trim(messages[i]) : messages[i];
  
                      if (_localStorageService == null && _localSocketF != null) {
                          _localSocketF(_response.responseBody);
                      }
  
                      if (_response.responseBody.length === 0 && _response.state === "messageReceived") {
                          continue;
                      }
  
                      _invokeFunction(_response);
  
                      // Invoke global callbacks
                      if (jQuery.atmosphere.callbacks.length > 0) {
                          if (_request.logLevel === 'debug') {
                              jQuery.atmosphere.debug("Invoking " + jQuery.atmosphere.callbacks.length + " global callbacks: " + _response.state);
                          }
                          try {
                              jQuery.each(jQuery.atmosphere.callbacks, call);
                          } catch (e) {
                              jQuery.atmosphere.log(_request.logLevel, ["Callback exception" + e]);
                          }
                      }
  
                      // Invoke request callback
                      if (typeof (_request.callback) === 'function') {
                          if (_request.logLevel === 'debug') {
                              jQuery.atmosphere.debug("Invoking request callbacks");
                          }
                          try {
                              _request.callback(_response);
                          } catch (e) {
                              jQuery.atmosphere.log(_request.logLevel, ["Callback exception" + e]);
                          }
                      }
                  }
  
              }
  
              /**
               *
               * @private
               */
              function _verifyStreamingLength(ajaxRequest, rq) {
                  // Wait to be sure we have the full message before closing.
                  if (_response.partialMessage === "" && (rq.transport === 'streaming') && (ajaxRequest.responseText.length > rq.maxStreamingLength)) {
                      _response.messages = [];
                      rq.reconnectingOnLength = true;
                      _invokeClose(true);
                      _disconnect();
                      _clearState();
                      _reconnect(ajaxRequest, rq, 0);
                  }
              }
  
              /**
               * Disconnect
               *
               * @private
               */
              function _disconnect() {
                  if (_request.enableProtocol && !_request.firstMessage) {
                      var query = "X-Atmosphere-Transport=close&X-Atmosphere-tracking-id=" + _request.uuid;
  
                      jQuery.each(_request.headers, function (name, value) {
                          var h = jQuery.isFunction(value) ? value.call(this, _request, _request, _response) : value;
                          if (h != null) {
                              query += "&" + encodeURIComponent(name) + "=" + encodeURIComponent(h);
                          }
                      });
  
                      var url = _request.url.replace(/([?&])_=[^&]*/, query);
                      url = url + (url === _request.url ? (/\?/.test(_request.url) ? "&" : "?") + query : "");
  
                      if (_request.connectTimeout > 0) {
                          jQuery.ajax({
                              url: url,
                              async: false,
                              timeout: _request.connectTimeout,
                              cache: false
                          });
                      } else {
                          jQuery.ajax({
                              url: url,
                              async: false,
                              cache: false
                          });
                      }
                  }
              }
  
              /**
               * Close request.
               *
               * @private
               */
              function _close() {
                  if (_request.reconnectId) {
                      clearTimeout(_request.reconnectId);
                      delete _request.reconnectId;
                  }
                  _request.reconnect = false;
                  _abordingConnection = true;
                  _response.request = _request;
                  _response.state = 'unsubscribe';
                  _response.responseBody = "";
                  _response.status = 408;
                  _invokeCallback();
                  _disconnect();
  
                  _clearState();
              }
  
              function _clearState() {
                  if (_request.id) {
                      clearTimeout(_request.id);
                  }
  
                  if (_ieStream != null) {
                      _ieStream.close();
                      _ieStream = null;
                  }
                  if (_jqxhr != null) {
                      _jqxhr.abort();
                      _jqxhr = null;
                  }
                  if (_activeRequest != null) {
                      _activeRequest.abort();
                      _activeRequest = null;
                  }
                  if (_websocket != null) {
                      if (_websocket.webSocketOpened) {
                          _websocket.close();
                      }
                      _websocket = null;
                  }
                  if (_sse != null) {
                      _sse.close();
                      _sse = null;
                  }
  
                  _clearStorage();
              }
  
              function _clearStorage() {
                  // Stop sharing a connection
                  if (_storageService != null) {
                      // Clears trace timer
                      clearInterval(_traceTimer);
                      // Removes the trace
                      document.cookie = _sharingKey + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
                      // The heir is the parent unless unloading
                      _storageService.signal("close", {
                          reason: "",
                          heir: !_abordingConnection ? guid : (_storageService.get("children") || [])[0]
                      });
                      _storageService.close();
                  }
                  if (_localStorageService != null) {
                      _localStorageService.close();
                  }
              }
  
              this.subscribe = function (options) {
                  _subscribe(options);
                  _execute();
              };
  
              this.execute = function () {
                  _execute();
              };
  
              this.invokeCallback = function () {
                  _invokeCallback();
              };
  
              this.close = function () {
                  _close();
              };
  
              this.disconnect = function () {
                  _disconnect();
              };
  
              this.getUrl = function () {
                  return _request.url;
              };
  
              this.push = function (message, dispatchUrl) {
                  if (dispatchUrl != null) {
                      var originalDispatchUrl = _request.dispatchUrl;
                      _request.dispatchUrl = dispatchUrl;
                      _push(message);
                      _request.dispatchUrl = originalDispatchUrl;
                  } else {
                      _push(message);
                  }
              };
  
              this.getUUID = function () {
                  return _request.uuid;
              };
  
              this.pushLocal = function (message) {
                  _intraPush(message);
              };
  
              this.enableProtocol = function (message) {
                  return _request.enableProtocol;
              };
  
              this.request = _request;
              this.response = _response;
          },
  
          subscribe: function (url, callback, request) {
              if (typeof (callback) === 'function') {
                  jQuery.atmosphere.addCallback(callback);
              }
  
              // https://github.com/Atmosphere/atmosphere-javascript/issues/58
              jQuery.atmosphere.uuid = 0;
  
              if (typeof (url) !== "string") {
                  request = url;
              } else {
                  request.url = url;
              }
  
              var rq = new jQuery.atmosphere.AtmosphereRequest(request);
              rq.execute();
  
              jQuery.atmosphere.requests[jQuery.atmosphere.requests.length] = rq;
              return rq;
          },
  
          addCallback: function (func) {
              if (jQuery.inArray(func, jQuery.atmosphere.callbacks) === -1) {
                  jQuery.atmosphere.callbacks.push(func);
              }
          },
  
          removeCallback: function (func) {
              var index = jQuery.inArray(func, jQuery.atmosphere.callbacks);
              if (index !== -1) {
                  jQuery.atmosphere.callbacks.splice(index, 1);
              }
          },
  
          unsubscribe: function () {
              if (jQuery.atmosphere.requests.length > 0) {
                  var requestsClone = [].concat(jQuery.atmosphere.requests);
                  for (var i = 0; i < requestsClone.length; i++) {
                      var rq = requestsClone[i];
                      rq.close();
                      clearTimeout(rq.response.request.id);
                  }
              }
              jQuery.atmosphere.requests = [];
              jQuery.atmosphere.callbacks = [];
          },
  
          unsubscribeUrl: function (url) {
              var idx = -1;
              if (jQuery.atmosphere.requests.length > 0) {
                  for (var i = 0; i < jQuery.atmosphere.requests.length; i++) {
                      var rq = jQuery.atmosphere.requests[i];
  
                      // Suppose you can subscribe once to an url
                      if (rq.getUrl() === url) {
                          rq.close();
                          clearTimeout(rq.response.request.id);
                          idx = i;
                          break;
                      }
                  }
              }
              if (idx >= 0) {
                  jQuery.atmosphere.requests.splice(idx, 1);
              }
          },
  
          publish: function (request) {
              if (typeof (request.callback) === 'function') {
                  jQuery.atmosphere.addCallback(request.callback);
              }
              request.transport = "polling";
  
              var rq = new jQuery.atmosphere.AtmosphereRequest(request);
              jQuery.atmosphere.requests[jQuery.atmosphere.requests.length] = rq;
              return rq;
          },
  
          checkCORSSupport: function () {
              if (jQuery.browser.msie && !window.XDomainRequest) {
                  return true;
              } else if (jQuery.browser.opera && +jQuery.browser.version.split(".")[0] < 12.0) {
                  return true;
              }
  
              // KreaTV 4.1 -> 4.4
              else if (jQuery.trim(navigator.userAgent).slice(0, 16) === "KreaTVWebKit/531") {
                  return true;
              }
              // KreaTV 3.8
              else if (jQuery.trim(navigator.userAgent).slice(-7).toLowerCase() === "kreatel") {
                  return true;
              }
  
              // Force Android to use CORS as some version like 2.2.3 fail otherwise
              var ua = navigator.userAgent.toLowerCase();
              var isAndroid = ua.indexOf("android") > -1;
              if (isAndroid) {
                  return true;
              }
              return false;
          },
  
          S4: function () {
              return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
          },
  
          guid: function () {
              return (jQuery.atmosphere.S4() + jQuery.atmosphere.S4() + "-" + jQuery.atmosphere.S4() + "-" + jQuery.atmosphere.S4() + "-"
                  + jQuery.atmosphere.S4() + "-" + jQuery.atmosphere.S4() + jQuery.atmosphere.S4() + jQuery.atmosphere.S4());
          },
  
          // From jQuery-Stream
          prepareURL: function (url) {
              // Attaches a time stamp to prevent caching
              var ts = jQuery.now();
              var ret = url.replace(/([?&])_=[^&]*/, "$1_=" + ts);
  
              return ret + (ret === url ? (/\?/.test(url) ? "&" : "?") + "_=" + ts : "");
          },
  
          // From jQuery-Stream
          param: function (data) {
              return jQuery.param(data, jQuery.ajaxSettings.traditional);
          },
  
          supportStorage: function () {
              var storage = window.localStorage;
              if (storage) {
                  try {
                      storage.setItem("t", "t");
                      storage.removeItem("t");
                      // The storage event of Internet Explorer and Firefox 3 works strangely
                      return window.StorageEvent && !jQuery.browser.msie && !(jQuery.browser.mozilla && jQuery.browser.version.split(".")[0] === "1");
                  } catch (e) {
                  }
              }
  
              return false;
          },
  
          iterate: function (fn, interval) {
              var timeoutId;
  
              // Though the interval is 0 for real-time application, there is a delay between setTimeout calls
              // For detail, see https://developer.mozilla.org/en/window.setTimeout#Minimum_delay_and_timeout_nesting
              interval = interval || 0;
  
              (function loop() {
                  timeoutId = setTimeout(function () {
                      if (fn() === false) {
                          return;
                      }
  
                      loop();
                  }, interval);
              })();
  
              return function () {
                  clearTimeout(timeoutId);
              };
          },
  
          log: function (level, args) {
              if (window.console) {
                  var logger = window.console[level];
                  if (typeof logger === 'function') {
                      logger.apply(window.console, args);
                  }
              }
          },
  
          warn: function () {
              jQuery.atmosphere.log('warn', arguments);
          },
  
          info: function () {
              jQuery.atmosphere.log('info', arguments);
          },
  
          debug: function () {
              jQuery.atmosphere.log('debug', arguments);
          },
  
          error: function () {
              jQuery.atmosphere.log('error', arguments);
          },
  
          // TODO extract to utils or something
          isBinary: function (data) {
              // True if data is an instance of Blob, ArrayBuffer or ArrayBufferView 
              return /^\[object\s(?:Blob|ArrayBuffer|.+Array)\]$/.test(Object.prototype.toString.call(data));
          }
      };
  
  
      // http://stackoverflow.com/questions/9645803/whats-the-replacement-for-browser
      // Limit scope pollution from any deprecated API
      (function () {
  	
          var matched, browser;
  	
          // Use of jQuery.browser is frowned upon.
          // More details: http://api.jquery.com/jQuery.browser
          // jQuery.uaMatch maintained for back-compat
          jQuery.uaMatch = function (ua) {
              ua = ua.toLowerCase();
  	
              var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || 
                      /(webkit)[ \/]([\w.]+)/.exec(ua) || 
                      /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || 
                      /(msie) ([\w.]+)/.exec(ua) || 
                      /(trident)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
                      ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || 
                      [];
  	
              return {
                  browser: match[1] || "",
                  version: match[2] || "0"
              };
          };
  	
          matched = jQuery.uaMatch(navigator.userAgent);
          browser = {};
  	
          if (matched.browser) {
              browser[matched.browser] = true;
              browser.version = matched.version;
          }
  	
          // Chrome is Webkit, but Webkit is also Safari.
          if (browser.chrome) {
              browser.webkit = true;
          } else if (browser.webkit) {
              browser.safari = true;
          }
          
          // Trident is the layout engine of the Internet Explorer
          // IE 11 has no "MSIE: 11.0" token
          if (browser.trident) {
              browser.msie = true;
          }
  	
          jQuery.browser = browser;
  	
          jQuery.sub = function () {
              function jQuerySub(selector, context) {
                  return new jQuerySub.fn.init(selector, context);
              }
  	
              jQuery.extend(true, jQuerySub, this);
              jQuerySub.superclass = this;
              jQuerySub.fn = jQuerySub.prototype = this();
              jQuerySub.fn.constructor = jQuerySub;
              jQuerySub.sub = this.sub;
              jQuerySub.fn.init = function init(selector, context) {
                  if (context && context instanceof jQuery && !(context instanceof jQuerySub)) {
                      context = jQuerySub(context);
                  }
  	
                  return jQuery.fn.init.call(this, selector, context, rootjQuerySub);
              };
              jQuerySub.fn.init.prototype = jQuerySub.fn;
              var rootjQuerySub = jQuerySub(document);
              return jQuerySub;
          };
  	
      })();
  	
      /*
       * jQuery stringifyJSON
       * http://github.com/flowersinthesand/jquery-stringifyJSON
       *
       * Copyright 2011, Donghwan Kim
       * Licensed under the Apache License, Version 2.0
       * http://www.apache.org/licenses/LICENSE-2.0
       */
      // This plugin is heavily based on Douglas Crockford's reference implementation
      (function (jQuery) {
  	
          var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, meta = {
              '\b': '\\b',
              '\t': '\\t',
              '\n': '\\n',
              '\f': '\\f',
              '\r': '\\r',
              '"': '\\"',
              '\\': '\\\\'
          };
  	
          function quote(string) {
              return '"' + string.replace(escapable, function (a) {
                  var c = meta[a];
                  return typeof c === "string" ? c : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
              }) + '"';
          }
  	
          function f(n) {
              return n < 10 ? "0" + n : n;
          }
  	
          function str(key, holder) {
              var i, v, len, partial, value = holder[key], type = typeof value;
  	
              if (value && typeof value === "object" && typeof value.toJSON === "function") {
                  value = value.toJSON(key);
                  type = typeof value;
              }
  	
              switch (type) {
                  case "string":
                      return quote(value);
                  case "number":
                      return isFinite(value) ? String(value) : "null";
                  case "boolean":
                      return String(value);
                  case "object":
                      if (!value) {
                          return "null";
                      }
  	
                      switch (Object.prototype.toString.call(value)) {
                          case "[object Date]":
                              return isFinite(value.valueOf()) ? '"' + value.getUTCFullYear() + "-" + f(value.getUTCMonth() + 1) + "-" + f(value.getUTCDate())
                                  + "T" + f(value.getUTCHours()) + ":" + f(value.getUTCMinutes()) + ":" + f(value.getUTCSeconds()) + "Z" + '"' : "null";
                          case "[object Array]":
                              len = value.length;
                              partial = [];
                              for (i = 0; i < len; i++) {
                                  partial.push(str(i, value) || "null");
                              }
  	
                              return "[" + partial.join(",") + "]";
                          default:
                              partial = [];
                              for (i in value) {
                                  if (Object.prototype.hasOwnProperty.call(value, i)) {
                                      v = str(i, value);
                                      if (v) {
                                          partial.push(quote(i) + ":" + v);
                                      }
                                  }
                              }
  	
                              return "{" + partial.join(",") + "}";
                      }
              }
          }
  	
          jQuery.stringifyJSON = function (value) {
              if (window.JSON && window.JSON.stringify) {
                  return window.JSON.stringify(value);
              }
  	
              return str("", {
                  "": value
              });
          };
  	
      }(jQuery));
  }));
  /* jshint noarg:true, noempty:true, eqeqeq:true, evil:true, laxbreak:true, undef:true, browser:true, jquery:true, indent:false, maxerr:50, eqnull:true */
  