github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/public/libs/sockjs-client-1.1.0/dist/sockjs-0.3.4.js (about)

     1  /* SockJS client, version 0.3.4, http://sockjs.org, MIT License
     2  
     3  Copyright (c) 2011-2012 VMware, Inc.
     4  
     5  Permission is hereby granted, free of charge, to any person obtaining a copy
     6  of this software and associated documentation files (the "Software"), to deal
     7  in the Software without restriction, including without limitation the rights
     8  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     9  copies of the Software, and to permit persons to whom the Software is
    10  furnished to do so, subject to the following conditions:
    11  
    12  The above copyright notice and this permission notice shall be included in
    13  all copies or substantial portions of the Software.
    14  
    15  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    17  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    18  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    19  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    20  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    21  THE SOFTWARE.
    22  */
    23  
    24  // JSON2 by Douglas Crockford (minified).
    25  var JSON;JSON||(JSON={}),function(){function str(a,b){var c,d,e,f,g=gap,h,i=b[a];i&&typeof i=="object"&&typeof i.toJSON=="function"&&(i=i.toJSON(a)),typeof rep=="function"&&(i=rep.call(b,a,i));switch(typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";gap+=indent,h=[];if(Object.prototype.toString.apply(i)==="[object Array]"){f=i.length;for(c=0;c<f;c+=1)h[c]=str(c,i)||"null";e=h.length===0?"[]":gap?"[\n"+gap+h.join(",\n"+gap)+"\n"+g+"]":"["+h.join(",")+"]",gap=g;return e}if(rep&&typeof rep=="object"){f=rep.length;for(c=0;c<f;c+=1)typeof rep[c]=="string"&&(d=rep[c],e=str(d,i),e&&h.push(quote(d)+(gap?": ":":")+e))}else for(d in i)Object.prototype.hasOwnProperty.call(i,d)&&(e=str(d,i),e&&h.push(quote(d)+(gap?": ":":")+e));e=h.length===0?"{}":gap?"{\n"+gap+h.join(",\n"+gap)+"\n"+g+"}":"{"+h.join(",")+"}",gap=g;return e}}function quote(a){escapable.lastIndex=0;return escapable.test(a)?'"'+a.replace(escapable,function(a){var b=meta[a];return typeof b=="string"?b:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function f(a){return a<10?"0"+a:a}"use strict",typeof Date.prototype.toJSON!="function"&&(Date.prototype.toJSON=function(a){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(a){return this.valueOf()});var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;typeof JSON.stringify!="function"&&(JSON.stringify=function(a,b,c){var d;gap="",indent="";if(typeof c=="number")for(d=0;d<c;d+=1)indent+=" ";else typeof c=="string"&&(indent=c);rep=b;if(!b||typeof b=="function"||typeof b=="object"&&typeof b.length=="number")return str("",{"":a});throw new Error("JSON.stringify")}),typeof JSON.parse!="function"&&(JSON.parse=function(text,reviver){function walk(a,b){var c,d,e=a[b];if(e&&typeof e=="object")for(c in e)Object.prototype.hasOwnProperty.call(e,c)&&(d=walk(e,c),d!==undefined?e[c]=d:delete e[c]);return reviver.call(a,b,e)}var j;text=String(text),cx.lastIndex=0,cx.test(text)&&(text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver=="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")})}()
    26  
    27  
    28  //     [*] Including lib/index.js
    29  // Public object
    30  SockJS = (function(){
    31                var _document = document;
    32                var _window = window;
    33                var utils = {};
    34  
    35  
    36  //         [*] Including lib/reventtarget.js
    37  /*
    38   * ***** BEGIN LICENSE BLOCK *****
    39   * Copyright (c) 2011-2012 VMware, Inc.
    40   *
    41   * For the license see COPYING.
    42   * ***** END LICENSE BLOCK *****
    43   */
    44  
    45  /* Simplified implementation of DOM2 EventTarget.
    46   *   http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget
    47   */
    48  var REventTarget = function() {};
    49  REventTarget.prototype.addEventListener = function (eventType, listener) {
    50      if(!this._listeners) {
    51           this._listeners = {};
    52      }
    53      if(!(eventType in this._listeners)) {
    54          this._listeners[eventType] = [];
    55      }
    56      var arr = this._listeners[eventType];
    57      if(utils.arrIndexOf(arr, listener) === -1) {
    58          arr.push(listener);
    59      }
    60      return;
    61  };
    62  
    63  REventTarget.prototype.removeEventListener = function (eventType, listener) {
    64      if(!(this._listeners && (eventType in this._listeners))) {
    65          return;
    66      }
    67      var arr = this._listeners[eventType];
    68      var idx = utils.arrIndexOf(arr, listener);
    69      if (idx !== -1) {
    70          if(arr.length > 1) {
    71              this._listeners[eventType] = arr.slice(0, idx).concat( arr.slice(idx+1) );
    72          } else {
    73              delete this._listeners[eventType];
    74          }
    75          return;
    76      }
    77      return;
    78  };
    79  
    80  REventTarget.prototype.dispatchEvent = function (event) {
    81      var t = event.type;
    82      var args = Array.prototype.slice.call(arguments, 0);
    83      if (this['on'+t]) {
    84          this['on'+t].apply(this, args);
    85      }
    86      if (this._listeners && t in this._listeners) {
    87          for(var i=0; i < this._listeners[t].length; i++) {
    88              this._listeners[t][i].apply(this, args);
    89          }
    90      }
    91  };
    92  //         [*] End of lib/reventtarget.js
    93  
    94  
    95  //         [*] Including lib/simpleevent.js
    96  /*
    97   * ***** BEGIN LICENSE BLOCK *****
    98   * Copyright (c) 2011-2012 VMware, Inc.
    99   *
   100   * For the license see COPYING.
   101   * ***** END LICENSE BLOCK *****
   102   */
   103  
   104  var SimpleEvent = function(type, obj) {
   105      this.type = type;
   106      if (typeof obj !== 'undefined') {
   107          for(var k in obj) {
   108              if (!obj.hasOwnProperty(k)) continue;
   109              this[k] = obj[k];
   110          }
   111      }
   112  };
   113  
   114  SimpleEvent.prototype.toString = function() {
   115      var r = [];
   116      for(var k in this) {
   117          if (!this.hasOwnProperty(k)) continue;
   118          var v = this[k];
   119          if (typeof v === 'function') v = '[function]';
   120          r.push(k + '=' + v);
   121      }
   122      return 'SimpleEvent(' + r.join(', ') + ')';
   123  };
   124  //         [*] End of lib/simpleevent.js
   125  
   126  
   127  //         [*] Including lib/eventemitter.js
   128  /*
   129   * ***** BEGIN LICENSE BLOCK *****
   130   * Copyright (c) 2011-2012 VMware, Inc.
   131   *
   132   * For the license see COPYING.
   133   * ***** END LICENSE BLOCK *****
   134   */
   135  
   136  var EventEmitter = function(events) {
   137      var that = this;
   138      that._events = events || [];
   139      that._listeners = {};
   140  };
   141  EventEmitter.prototype.emit = function(type) {
   142      var that = this;
   143      that._verifyType(type);
   144      if (that._nuked) return;
   145  
   146      var args = Array.prototype.slice.call(arguments, 1);
   147      if (that['on'+type]) {
   148          that['on'+type].apply(that, args);
   149      }
   150      if (type in that._listeners) {
   151          for(var i = 0; i < that._listeners[type].length; i++) {
   152              that._listeners[type][i].apply(that, args);
   153          }
   154      }
   155  };
   156  
   157  EventEmitter.prototype.on = function(type, callback) {
   158      var that = this;
   159      that._verifyType(type);
   160      if (that._nuked) return;
   161  
   162      if (!(type in that._listeners)) {
   163          that._listeners[type] = [];
   164      }
   165      that._listeners[type].push(callback);
   166  };
   167  
   168  EventEmitter.prototype._verifyType = function(type) {
   169      var that = this;
   170      if (utils.arrIndexOf(that._events, type) === -1) {
   171          utils.log('Event ' + JSON.stringify(type) +
   172                    ' not listed ' + JSON.stringify(that._events) +
   173                    ' in ' + that);
   174      }
   175  };
   176  
   177  EventEmitter.prototype.nuke = function() {
   178      var that = this;
   179      that._nuked = true;
   180      for(var i=0; i<that._events.length; i++) {
   181          delete that[that._events[i]];
   182      }
   183      that._listeners = {};
   184  };
   185  //         [*] End of lib/eventemitter.js
   186  
   187  
   188  //         [*] Including lib/utils.js
   189  /*
   190   * ***** BEGIN LICENSE BLOCK *****
   191   * Copyright (c) 2011-2012 VMware, Inc.
   192   *
   193   * For the license see COPYING.
   194   * ***** END LICENSE BLOCK *****
   195   */
   196  
   197  var random_string_chars = 'abcdefghijklmnopqrstuvwxyz0123456789_';
   198  utils.random_string = function(length, max) {
   199      max = max || random_string_chars.length;
   200      var i, ret = [];
   201      for(i=0; i < length; i++) {
   202          ret.push( random_string_chars.substr(Math.floor(Math.random() * max),1) );
   203      }
   204      return ret.join('');
   205  };
   206  utils.random_number = function(max) {
   207      return Math.floor(Math.random() * max);
   208  };
   209  utils.random_number_string = function(max) {
   210      var t = (''+(max - 1)).length;
   211      var p = Array(t+1).join('0');
   212      return (p + utils.random_number(max)).slice(-t);
   213  };
   214  
   215  // Assuming that url looks like: http://asdasd:111/asd
   216  utils.getOrigin = function(url) {
   217      url += '/';
   218      var parts = url.split('/').slice(0, 3);
   219      return parts.join('/');
   220  };
   221  
   222  utils.isSameOriginUrl = function(url_a, url_b) {
   223      // location.origin would do, but it's not always available.
   224      if (!url_b) url_b = _window.location.href;
   225  
   226      return (url_a.split('/').slice(0,3).join('/')
   227                  ===
   228              url_b.split('/').slice(0,3).join('/'));
   229  };
   230  
   231  utils.getParentDomain = function(url) {
   232      // ipv4 ip address
   233      if (/^[0-9.]*$/.test(url)) return url;
   234      // ipv6 ip address
   235      if (/^\[/.test(url)) return url;
   236      // no dots
   237      if (!(/[.]/.test(url))) return url;
   238  
   239      var parts = url.split('.').slice(1);
   240      return parts.join('.');
   241  };
   242  
   243  utils.objectExtend = function(dst, src) {
   244      for(var k in src) {
   245          if (src.hasOwnProperty(k)) {
   246              dst[k] = src[k];
   247          }
   248      }
   249      return dst;
   250  };
   251  
   252  var WPrefix = '_jp';
   253  
   254  utils.polluteGlobalNamespace = function() {
   255      if (!(WPrefix in _window)) {
   256          _window[WPrefix] = {};
   257      }
   258  };
   259  
   260  utils.closeFrame = function (code, reason) {
   261      return 'c'+JSON.stringify([code, reason]);
   262  };
   263  
   264  utils.userSetCode = function (code) {
   265      return code === 1000 || (code >= 3000 && code <= 4999);
   266  };
   267  
   268  // See: http://www.erg.abdn.ac.uk/~gerrit/dccp/notes/ccid2/rto_estimator/
   269  // and RFC 2988.
   270  utils.countRTO = function (rtt) {
   271      var rto;
   272      if (rtt > 100) {
   273          rto = 3 * rtt; // rto > 300msec
   274      } else {
   275          rto = rtt + 200; // 200msec < rto <= 300msec
   276      }
   277      return rto;
   278  }
   279  
   280  utils.log = function() {
   281      if (_window.console && console.log && console.log.apply) {
   282          console.log.apply(console, arguments);
   283      }
   284  };
   285  
   286  utils.bind = function(fun, that) {
   287      if (fun.bind) {
   288          return fun.bind(that);
   289      } else {
   290          return function() {
   291              return fun.apply(that, arguments);
   292          };
   293      }
   294  };
   295  
   296  utils.flatUrl = function(url) {
   297      return url.indexOf('?') === -1 && url.indexOf('#') === -1;
   298  };
   299  
   300  utils.amendUrl = function(url) {
   301      var dl = _document.location;
   302      if (!url) {
   303          throw new Error('Wrong url for SockJS');
   304      }
   305      if (!utils.flatUrl(url)) {
   306          throw new Error('Only basic urls are supported in SockJS');
   307      }
   308  
   309      //  '//abc' --> 'http://abc'
   310      if (url.indexOf('//') === 0) {
   311          url = dl.protocol + url;
   312      }
   313      // '/abc' --> 'http://localhost:80/abc'
   314      if (url.indexOf('/') === 0) {
   315          url = dl.protocol + '//' + dl.host + url;
   316      }
   317      // strip trailing slashes
   318      url = url.replace(/[/]+$/,'');
   319      return url;
   320  };
   321  
   322  // IE doesn't support [].indexOf.
   323  utils.arrIndexOf = function(arr, obj){
   324      for(var i=0; i < arr.length; i++){
   325          if(arr[i] === obj){
   326              return i;
   327          }
   328      }
   329      return -1;
   330  };
   331  
   332  utils.arrSkip = function(arr, obj) {
   333      var idx = utils.arrIndexOf(arr, obj);
   334      if (idx === -1) {
   335          return arr.slice();
   336      } else {
   337          var dst = arr.slice(0, idx);
   338          return dst.concat(arr.slice(idx+1));
   339      }
   340  };
   341  
   342  // Via: https://gist.github.com/1133122/2121c601c5549155483f50be3da5305e83b8c5df
   343  utils.isArray = Array.isArray || function(value) {
   344      return {}.toString.call(value).indexOf('Array') >= 0
   345  };
   346  
   347  utils.delay = function(t, fun) {
   348      if(typeof t === 'function') {
   349          fun = t;
   350          t = 0;
   351      }
   352      return setTimeout(fun, t);
   353  };
   354  
   355  
   356  // Chars worth escaping, as defined by Douglas Crockford:
   357  //   https://github.com/douglascrockford/JSON-js/blob/47a9882cddeb1e8529e07af9736218075372b8ac/json2.js#L196
   358  var json_escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
   359      json_lookup = {
   360  "\u0000":"\\u0000","\u0001":"\\u0001","\u0002":"\\u0002","\u0003":"\\u0003",
   361  "\u0004":"\\u0004","\u0005":"\\u0005","\u0006":"\\u0006","\u0007":"\\u0007",
   362  "\b":"\\b","\t":"\\t","\n":"\\n","\u000b":"\\u000b","\f":"\\f","\r":"\\r",
   363  "\u000e":"\\u000e","\u000f":"\\u000f","\u0010":"\\u0010","\u0011":"\\u0011",
   364  "\u0012":"\\u0012","\u0013":"\\u0013","\u0014":"\\u0014","\u0015":"\\u0015",
   365  "\u0016":"\\u0016","\u0017":"\\u0017","\u0018":"\\u0018","\u0019":"\\u0019",
   366  "\u001a":"\\u001a","\u001b":"\\u001b","\u001c":"\\u001c","\u001d":"\\u001d",
   367  "\u001e":"\\u001e","\u001f":"\\u001f","\"":"\\\"","\\":"\\\\",
   368  "\u007f":"\\u007f","\u0080":"\\u0080","\u0081":"\\u0081","\u0082":"\\u0082",
   369  "\u0083":"\\u0083","\u0084":"\\u0084","\u0085":"\\u0085","\u0086":"\\u0086",
   370  "\u0087":"\\u0087","\u0088":"\\u0088","\u0089":"\\u0089","\u008a":"\\u008a",
   371  "\u008b":"\\u008b","\u008c":"\\u008c","\u008d":"\\u008d","\u008e":"\\u008e",
   372  "\u008f":"\\u008f","\u0090":"\\u0090","\u0091":"\\u0091","\u0092":"\\u0092",
   373  "\u0093":"\\u0093","\u0094":"\\u0094","\u0095":"\\u0095","\u0096":"\\u0096",
   374  "\u0097":"\\u0097","\u0098":"\\u0098","\u0099":"\\u0099","\u009a":"\\u009a",
   375  "\u009b":"\\u009b","\u009c":"\\u009c","\u009d":"\\u009d","\u009e":"\\u009e",
   376  "\u009f":"\\u009f","\u00ad":"\\u00ad","\u0600":"\\u0600","\u0601":"\\u0601",
   377  "\u0602":"\\u0602","\u0603":"\\u0603","\u0604":"\\u0604","\u070f":"\\u070f",
   378  "\u17b4":"\\u17b4","\u17b5":"\\u17b5","\u200c":"\\u200c","\u200d":"\\u200d",
   379  "\u200e":"\\u200e","\u200f":"\\u200f","\u2028":"\\u2028","\u2029":"\\u2029",
   380  "\u202a":"\\u202a","\u202b":"\\u202b","\u202c":"\\u202c","\u202d":"\\u202d",
   381  "\u202e":"\\u202e","\u202f":"\\u202f","\u2060":"\\u2060","\u2061":"\\u2061",
   382  "\u2062":"\\u2062","\u2063":"\\u2063","\u2064":"\\u2064","\u2065":"\\u2065",
   383  "\u2066":"\\u2066","\u2067":"\\u2067","\u2068":"\\u2068","\u2069":"\\u2069",
   384  "\u206a":"\\u206a","\u206b":"\\u206b","\u206c":"\\u206c","\u206d":"\\u206d",
   385  "\u206e":"\\u206e","\u206f":"\\u206f","\ufeff":"\\ufeff","\ufff0":"\\ufff0",
   386  "\ufff1":"\\ufff1","\ufff2":"\\ufff2","\ufff3":"\\ufff3","\ufff4":"\\ufff4",
   387  "\ufff5":"\\ufff5","\ufff6":"\\ufff6","\ufff7":"\\ufff7","\ufff8":"\\ufff8",
   388  "\ufff9":"\\ufff9","\ufffa":"\\ufffa","\ufffb":"\\ufffb","\ufffc":"\\ufffc",
   389  "\ufffd":"\\ufffd","\ufffe":"\\ufffe","\uffff":"\\uffff"};
   390  
   391  // Some extra characters that Chrome gets wrong, and substitutes with
   392  // something else on the wire.
   393  var extra_escapable = /[\x00-\x1f\ud800-\udfff\ufffe\uffff\u0300-\u0333\u033d-\u0346\u034a-\u034c\u0350-\u0352\u0357-\u0358\u035c-\u0362\u0374\u037e\u0387\u0591-\u05af\u05c4\u0610-\u0617\u0653-\u0654\u0657-\u065b\u065d-\u065e\u06df-\u06e2\u06eb-\u06ec\u0730\u0732-\u0733\u0735-\u0736\u073a\u073d\u073f-\u0741\u0743\u0745\u0747\u07eb-\u07f1\u0951\u0958-\u095f\u09dc-\u09dd\u09df\u0a33\u0a36\u0a59-\u0a5b\u0a5e\u0b5c-\u0b5d\u0e38-\u0e39\u0f43\u0f4d\u0f52\u0f57\u0f5c\u0f69\u0f72-\u0f76\u0f78\u0f80-\u0f83\u0f93\u0f9d\u0fa2\u0fa7\u0fac\u0fb9\u1939-\u193a\u1a17\u1b6b\u1cda-\u1cdb\u1dc0-\u1dcf\u1dfc\u1dfe\u1f71\u1f73\u1f75\u1f77\u1f79\u1f7b\u1f7d\u1fbb\u1fbe\u1fc9\u1fcb\u1fd3\u1fdb\u1fe3\u1feb\u1fee-\u1fef\u1ff9\u1ffb\u1ffd\u2000-\u2001\u20d0-\u20d1\u20d4-\u20d7\u20e7-\u20e9\u2126\u212a-\u212b\u2329-\u232a\u2adc\u302b-\u302c\uaab2-\uaab3\uf900-\ufa0d\ufa10\ufa12\ufa15-\ufa1e\ufa20\ufa22\ufa25-\ufa26\ufa2a-\ufa2d\ufa30-\ufa6d\ufa70-\ufad9\ufb1d\ufb1f\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4e\ufff0-\uffff]/g,
   394      extra_lookup;
   395  
   396  // JSON Quote string. Use native implementation when possible.
   397  var JSONQuote = (JSON && JSON.stringify) || function(string) {
   398      json_escapable.lastIndex = 0;
   399      if (json_escapable.test(string)) {
   400          string = string.replace(json_escapable, function(a) {
   401              return json_lookup[a];
   402          });
   403      }
   404      return '"' + string + '"';
   405  };
   406  
   407  // This may be quite slow, so let's delay until user actually uses bad
   408  // characters.
   409  var unroll_lookup = function(escapable) {
   410      var i;
   411      var unrolled = {}
   412      var c = []
   413      for(i=0; i<65536; i++) {
   414          c.push( String.fromCharCode(i) );
   415      }
   416      escapable.lastIndex = 0;
   417      c.join('').replace(escapable, function (a) {
   418          unrolled[ a ] = '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
   419          return '';
   420      });
   421      escapable.lastIndex = 0;
   422      return unrolled;
   423  };
   424  
   425  // Quote string, also taking care of unicode characters that browsers
   426  // often break. Especially, take care of unicode surrogates:
   427  //    http://en.wikipedia.org/wiki/Mapping_of_Unicode_characters#Surrogates
   428  utils.quote = function(string) {
   429      var quoted = JSONQuote(string);
   430  
   431      // In most cases this should be very fast and good enough.
   432      extra_escapable.lastIndex = 0;
   433      if(!extra_escapable.test(quoted)) {
   434          return quoted;
   435      }
   436  
   437      if(!extra_lookup) extra_lookup = unroll_lookup(extra_escapable);
   438  
   439      return quoted.replace(extra_escapable, function(a) {
   440          return extra_lookup[a];
   441      });
   442  }
   443  
   444  var _all_protocols = ['websocket',
   445                        'xdr-streaming',
   446                        'xhr-streaming',
   447                        'iframe-eventsource',
   448                        'iframe-htmlfile',
   449                        'xdr-polling',
   450                        'xhr-polling',
   451                        'iframe-xhr-polling',
   452                        'jsonp-polling'];
   453  
   454  utils.probeProtocols = function() {
   455      var probed = {};
   456      for(var i=0; i<_all_protocols.length; i++) {
   457          var protocol = _all_protocols[i];
   458          // User can have a typo in protocol name.
   459          probed[protocol] = SockJS[protocol] &&
   460                             SockJS[protocol].enabled();
   461      }
   462      return probed;
   463  };
   464  
   465  utils.detectProtocols = function(probed, protocols_whitelist, info) {
   466      var pe = {},
   467          protocols = [];
   468      if (!protocols_whitelist) protocols_whitelist = _all_protocols;
   469      for(var i=0; i<protocols_whitelist.length; i++) {
   470          var protocol = protocols_whitelist[i];
   471          pe[protocol] = probed[protocol];
   472      }
   473      var maybe_push = function(protos) {
   474          var proto = protos.shift();
   475          if (pe[proto]) {
   476              protocols.push(proto);
   477          } else {
   478              if (protos.length > 0) {
   479                  maybe_push(protos);
   480              }
   481          }
   482      }
   483  
   484      // 1. Websocket
   485      if (info.websocket !== false) {
   486          maybe_push(['websocket']);
   487      }
   488  
   489      // 2. Streaming
   490      if (pe['xhr-streaming'] && !info.null_origin) {
   491          protocols.push('xhr-streaming');
   492      } else {
   493          if (pe['xdr-streaming'] && !info.cookie_needed && !info.null_origin) {
   494              protocols.push('xdr-streaming');
   495          } else {
   496              maybe_push(['iframe-eventsource',
   497                          'iframe-htmlfile']);
   498          }
   499      }
   500  
   501      // 3. Polling
   502      if (pe['xhr-polling'] && !info.null_origin) {
   503          protocols.push('xhr-polling');
   504      } else {
   505          if (pe['xdr-polling'] && !info.cookie_needed && !info.null_origin) {
   506              protocols.push('xdr-polling');
   507          } else {
   508              maybe_push(['iframe-xhr-polling',
   509                          'jsonp-polling']);
   510          }
   511      }
   512      return protocols;
   513  }
   514  //         [*] End of lib/utils.js
   515  
   516  
   517  //         [*] Including lib/dom.js
   518  /*
   519   * ***** BEGIN LICENSE BLOCK *****
   520   * Copyright (c) 2011-2012 VMware, Inc.
   521   *
   522   * For the license see COPYING.
   523   * ***** END LICENSE BLOCK *****
   524   */
   525  
   526  // May be used by htmlfile jsonp and transports.
   527  var MPrefix = '_sockjs_global';
   528  utils.createHook = function() {
   529      var window_id = 'a' + utils.random_string(8);
   530      if (!(MPrefix in _window)) {
   531          var map = {};
   532          _window[MPrefix] = function(window_id) {
   533              if (!(window_id in map)) {
   534                  map[window_id] = {
   535                      id: window_id,
   536                      del: function() {delete map[window_id];}
   537                  };
   538              }
   539              return map[window_id];
   540          }
   541      }
   542      return _window[MPrefix](window_id);
   543  };
   544  
   545  
   546  
   547  utils.attachMessage = function(listener) {
   548      utils.attachEvent('message', listener);
   549  };
   550  utils.attachEvent = function(event, listener) {
   551      if (typeof _window.addEventListener !== 'undefined') {
   552          _window.addEventListener(event, listener, false);
   553      } else {
   554          // IE quirks.
   555          // According to: http://stevesouders.com/misc/test-postmessage.php
   556          // the message gets delivered only to 'document', not 'window'.
   557          _document.attachEvent("on" + event, listener);
   558          // I get 'window' for ie8.
   559          _window.attachEvent("on" + event, listener);
   560      }
   561  };
   562  
   563  utils.detachMessage = function(listener) {
   564      utils.detachEvent('message', listener);
   565  };
   566  utils.detachEvent = function(event, listener) {
   567      if (typeof _window.addEventListener !== 'undefined') {
   568          _window.removeEventListener(event, listener, false);
   569      } else {
   570          _document.detachEvent("on" + event, listener);
   571          _window.detachEvent("on" + event, listener);
   572      }
   573  };
   574  
   575  
   576  var on_unload = {};
   577  // Things registered after beforeunload are to be called immediately.
   578  var after_unload = false;
   579  
   580  var trigger_unload_callbacks = function() {
   581      for(var ref in on_unload) {
   582          on_unload[ref]();
   583          delete on_unload[ref];
   584      };
   585  };
   586  
   587  var unload_triggered = function() {
   588      if(after_unload) return;
   589      after_unload = true;
   590      trigger_unload_callbacks();
   591  };
   592  
   593  // 'unload' alone is not reliable in opera within an iframe, but we
   594  // can't use `beforeunload` as IE fires it on javascript: links.
   595  utils.attachEvent('unload', unload_triggered);
   596  
   597  utils.unload_add = function(listener) {
   598      var ref = utils.random_string(8);
   599      on_unload[ref] = listener;
   600      if (after_unload) {
   601          utils.delay(trigger_unload_callbacks);
   602      }
   603      return ref;
   604  };
   605  utils.unload_del = function(ref) {
   606      if (ref in on_unload)
   607          delete on_unload[ref];
   608  };
   609  
   610  
   611  utils.createIframe = function (iframe_url, error_callback) {
   612      var iframe = _document.createElement('iframe');
   613      var tref, unload_ref;
   614      var unattach = function() {
   615          clearTimeout(tref);
   616          // Explorer had problems with that.
   617          try {iframe.onload = null;} catch (x) {}
   618          iframe.onerror = null;
   619      };
   620      var cleanup = function() {
   621          if (iframe) {
   622              unattach();
   623              // This timeout makes chrome fire onbeforeunload event
   624              // within iframe. Without the timeout it goes straight to
   625              // onunload.
   626              setTimeout(function() {
   627                  if(iframe) {
   628                      iframe.parentNode.removeChild(iframe);
   629                  }
   630                  iframe = null;
   631              }, 0);
   632              utils.unload_del(unload_ref);
   633          }
   634      };
   635      var onerror = function(r) {
   636          if (iframe) {
   637              cleanup();
   638              error_callback(r);
   639          }
   640      };
   641      var post = function(msg, origin) {
   642          try {
   643              // When the iframe is not loaded, IE raises an exception
   644              // on 'contentWindow'.
   645              if (iframe && iframe.contentWindow) {
   646                  iframe.contentWindow.postMessage(msg, origin);
   647              }
   648          } catch (x) {};
   649      };
   650  
   651      iframe.src = iframe_url;
   652      iframe.style.display = 'none';
   653      iframe.style.position = 'absolute';
   654      iframe.onerror = function(){onerror('onerror');};
   655      iframe.onload = function() {
   656          // `onload` is triggered before scripts on the iframe are
   657          // executed. Give it few seconds to actually load stuff.
   658          clearTimeout(tref);
   659          tref = setTimeout(function(){onerror('onload timeout');}, 2000);
   660      };
   661      _document.body.appendChild(iframe);
   662      tref = setTimeout(function(){onerror('timeout');}, 15000);
   663      unload_ref = utils.unload_add(cleanup);
   664      return {
   665          post: post,
   666          cleanup: cleanup,
   667          loaded: unattach
   668      };
   669  };
   670  
   671  utils.createHtmlfile = function (iframe_url, error_callback) {
   672      var doc = new ActiveXObject('htmlfile');
   673      var tref, unload_ref;
   674      var iframe;
   675      var unattach = function() {
   676          clearTimeout(tref);
   677      };
   678      var cleanup = function() {
   679          if (doc) {
   680              unattach();
   681              utils.unload_del(unload_ref);
   682              iframe.parentNode.removeChild(iframe);
   683              iframe = doc = null;
   684              CollectGarbage();
   685          }
   686      };
   687      var onerror = function(r)  {
   688          if (doc) {
   689              cleanup();
   690              error_callback(r);
   691          }
   692      };
   693      var post = function(msg, origin) {
   694          try {
   695              // When the iframe is not loaded, IE raises an exception
   696              // on 'contentWindow'.
   697              if (iframe && iframe.contentWindow) {
   698                  iframe.contentWindow.postMessage(msg, origin);
   699              }
   700          } catch (x) {};
   701      };
   702  
   703      doc.open();
   704      doc.write('<html><s' + 'cript>' +
   705                'document.domain="' + document.domain + '";' +
   706                '</s' + 'cript></html>');
   707      doc.close();
   708      doc.parentWindow[WPrefix] = _window[WPrefix];
   709      var c = doc.createElement('div');
   710      doc.body.appendChild(c);
   711      iframe = doc.createElement('iframe');
   712      c.appendChild(iframe);
   713      iframe.src = iframe_url;
   714      tref = setTimeout(function(){onerror('timeout');}, 15000);
   715      unload_ref = utils.unload_add(cleanup);
   716      return {
   717          post: post,
   718          cleanup: cleanup,
   719          loaded: unattach
   720      };
   721  };
   722  //         [*] End of lib/dom.js
   723  
   724  
   725  //         [*] Including lib/dom2.js
   726  /*
   727   * ***** BEGIN LICENSE BLOCK *****
   728   * Copyright (c) 2011-2012 VMware, Inc.
   729   *
   730   * For the license see COPYING.
   731   * ***** END LICENSE BLOCK *****
   732   */
   733  
   734  var AbstractXHRObject = function(){};
   735  AbstractXHRObject.prototype = new EventEmitter(['chunk', 'finish']);
   736  
   737  AbstractXHRObject.prototype._start = function(method, url, payload, opts) {
   738      var that = this;
   739  
   740      try {
   741          that.xhr = new XMLHttpRequest();
   742      } catch(x) {};
   743  
   744      if (!that.xhr) {
   745          try {
   746              that.xhr = new _window.ActiveXObject('Microsoft.XMLHTTP');
   747          } catch(x) {};
   748      }
   749      if (_window.ActiveXObject || _window.XDomainRequest) {
   750          // IE8 caches even POSTs
   751          url += ((url.indexOf('?') === -1) ? '?' : '&') + 't='+(+new Date);
   752      }
   753  
   754      // Explorer tends to keep connection open, even after the
   755      // tab gets closed: http://bugs.jquery.com/ticket/5280
   756      that.unload_ref = utils.unload_add(function(){that._cleanup(true);});
   757      try {
   758          that.xhr.open(method, url, true);
   759      } catch(e) {
   760          // IE raises an exception on wrong port.
   761          that.emit('finish', 0, '');
   762          that._cleanup();
   763          return;
   764      };
   765  
   766      if (!opts || !opts.no_credentials) {
   767          // Mozilla docs says https://developer.mozilla.org/en/XMLHttpRequest :
   768          // "This never affects same-site requests."
   769          that.xhr.withCredentials = 'true';
   770      }
   771      if (opts && opts.headers) {
   772          for(var key in opts.headers) {
   773              that.xhr.setRequestHeader(key, opts.headers[key]);
   774          }
   775      }
   776  
   777      that.xhr.onreadystatechange = function() {
   778          if (that.xhr) {
   779              var x = that.xhr;
   780              switch (x.readyState) {
   781              case 3:
   782                  // IE doesn't like peeking into responseText or status
   783                  // on Microsoft.XMLHTTP and readystate=3
   784                  try {
   785                      var status = x.status;
   786                      var text = x.responseText;
   787                  } catch (x) {};
   788                  // IE returns 1223 for 204: http://bugs.jquery.com/ticket/1450
   789                  if (status === 1223) status = 204;
   790  
   791                  // IE does return readystate == 3 for 404 answers.
   792                  if (text && text.length > 0) {
   793                      that.emit('chunk', status, text);
   794                  }
   795                  break;
   796              case 4:
   797                  var status = x.status;
   798                  // IE returns 1223 for 204: http://bugs.jquery.com/ticket/1450
   799                  if (status === 1223) status = 204;
   800  
   801                  that.emit('finish', status, x.responseText);
   802                  that._cleanup(false);
   803                  break;
   804              }
   805          }
   806      };
   807      that.xhr.send(payload);
   808  };
   809  
   810  AbstractXHRObject.prototype._cleanup = function(abort) {
   811      var that = this;
   812      if (!that.xhr) return;
   813      utils.unload_del(that.unload_ref);
   814  
   815      // IE needs this field to be a function
   816      that.xhr.onreadystatechange = function(){};
   817  
   818      if (abort) {
   819          try {
   820              that.xhr.abort();
   821          } catch(x) {};
   822      }
   823      that.unload_ref = that.xhr = null;
   824  };
   825  
   826  AbstractXHRObject.prototype.close = function() {
   827      var that = this;
   828      that.nuke();
   829      that._cleanup(true);
   830  };
   831  
   832  var XHRCorsObject = utils.XHRCorsObject = function() {
   833      var that = this, args = arguments;
   834      utils.delay(function(){that._start.apply(that, args);});
   835  };
   836  XHRCorsObject.prototype = new AbstractXHRObject();
   837  
   838  var XHRLocalObject = utils.XHRLocalObject = function(method, url, payload) {
   839      var that = this;
   840      utils.delay(function(){
   841          that._start(method, url, payload, {
   842              no_credentials: true
   843          });
   844      });
   845  };
   846  XHRLocalObject.prototype = new AbstractXHRObject();
   847  
   848  
   849  
   850  // References:
   851  //   http://ajaxian.com/archives/100-line-ajax-wrapper
   852  //   http://msdn.microsoft.com/en-us/library/cc288060(v=VS.85).aspx
   853  var XDRObject = utils.XDRObject = function(method, url, payload) {
   854      var that = this;
   855      utils.delay(function(){that._start(method, url, payload);});
   856  };
   857  XDRObject.prototype = new EventEmitter(['chunk', 'finish']);
   858  XDRObject.prototype._start = function(method, url, payload) {
   859      var that = this;
   860      var xdr = new XDomainRequest();
   861      // IE caches even POSTs
   862      url += ((url.indexOf('?') === -1) ? '?' : '&') + 't='+(+new Date);
   863  
   864      var onerror = xdr.ontimeout = xdr.onerror = function() {
   865          that.emit('finish', 0, '');
   866          that._cleanup(false);
   867      };
   868      xdr.onprogress = function() {
   869          that.emit('chunk', 200, xdr.responseText);
   870      };
   871      xdr.onload = function() {
   872          that.emit('finish', 200, xdr.responseText);
   873          that._cleanup(false);
   874      };
   875      that.xdr = xdr;
   876      that.unload_ref = utils.unload_add(function(){that._cleanup(true);});
   877      try {
   878          // Fails with AccessDenied if port number is bogus
   879          that.xdr.open(method, url);
   880          that.xdr.send(payload);
   881      } catch(x) {
   882          onerror();
   883      }
   884  };
   885  
   886  XDRObject.prototype._cleanup = function(abort) {
   887      var that = this;
   888      if (!that.xdr) return;
   889      utils.unload_del(that.unload_ref);
   890  
   891      that.xdr.ontimeout = that.xdr.onerror = that.xdr.onprogress =
   892          that.xdr.onload = null;
   893      if (abort) {
   894          try {
   895              that.xdr.abort();
   896          } catch(x) {};
   897      }
   898      that.unload_ref = that.xdr = null;
   899  };
   900  
   901  XDRObject.prototype.close = function() {
   902      var that = this;
   903      that.nuke();
   904      that._cleanup(true);
   905  };
   906  
   907  // 1. Is natively via XHR
   908  // 2. Is natively via XDR
   909  // 3. Nope, but postMessage is there so it should work via the Iframe.
   910  // 4. Nope, sorry.
   911  utils.isXHRCorsCapable = function() {
   912      if (_window.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest()) {
   913          return 1;
   914      }
   915      // XDomainRequest doesn't work if page is served from file://
   916      if (_window.XDomainRequest && _document.domain) {
   917          return 2;
   918      }
   919      if (IframeTransport.enabled()) {
   920          return 3;
   921      }
   922      return 4;
   923  };
   924  //         [*] End of lib/dom2.js
   925  
   926  
   927  //         [*] Including lib/sockjs.js
   928  /*
   929   * ***** BEGIN LICENSE BLOCK *****
   930   * Copyright (c) 2011-2012 VMware, Inc.
   931   *
   932   * For the license see COPYING.
   933   * ***** END LICENSE BLOCK *****
   934   */
   935  
   936  var SockJS = function(url, dep_protocols_whitelist, options) {
   937      if (this === _window) {
   938          // makes `new` optional
   939          return new SockJS(url, dep_protocols_whitelist, options);
   940      }
   941      
   942      var that = this, protocols_whitelist;
   943      that._options = {devel: false, debug: false, protocols_whitelist: [],
   944                       info: undefined, rtt: undefined};
   945      if (options) {
   946          utils.objectExtend(that._options, options);
   947      }
   948      that._base_url = utils.amendUrl(url);
   949      that._server = that._options.server || utils.random_number_string(1000);
   950      if (that._options.protocols_whitelist &&
   951          that._options.protocols_whitelist.length) {
   952          protocols_whitelist = that._options.protocols_whitelist;
   953      } else {
   954          // Deprecated API
   955          if (typeof dep_protocols_whitelist === 'string' &&
   956              dep_protocols_whitelist.length > 0) {
   957              protocols_whitelist = [dep_protocols_whitelist];
   958          } else if (utils.isArray(dep_protocols_whitelist)) {
   959              protocols_whitelist = dep_protocols_whitelist
   960          } else {
   961              protocols_whitelist = null;
   962          }
   963          if (protocols_whitelist) {
   964              that._debug('Deprecated API: Use "protocols_whitelist" option ' +
   965                          'instead of supplying protocol list as a second ' +
   966                          'parameter to SockJS constructor.');
   967          }
   968      }
   969      that._protocols = [];
   970      that.protocol = null;
   971      that.readyState = SockJS.CONNECTING;
   972      that._ir = createInfoReceiver(that._base_url);
   973      that._ir.onfinish = function(info, rtt) {
   974          that._ir = null;
   975          if (info) {
   976              if (that._options.info) {
   977                  // Override if user supplies the option
   978                  info = utils.objectExtend(info, that._options.info);
   979              }
   980              if (that._options.rtt) {
   981                  rtt = that._options.rtt;
   982              }
   983              that._applyInfo(info, rtt, protocols_whitelist);
   984              that._didClose();
   985          } else {
   986              that._didClose(1002, 'Can\'t connect to server', true);
   987          }
   988      };
   989  };
   990  // Inheritance
   991  SockJS.prototype = new REventTarget();
   992  
   993  SockJS.version = "0.3.4";
   994  
   995  SockJS.CONNECTING = 0;
   996  SockJS.OPEN = 1;
   997  SockJS.CLOSING = 2;
   998  SockJS.CLOSED = 3;
   999  
  1000  SockJS.prototype._debug = function() {
  1001      if (this._options.debug)
  1002          utils.log.apply(utils, arguments);
  1003  };
  1004  
  1005  SockJS.prototype._dispatchOpen = function() {
  1006      var that = this;
  1007      if (that.readyState === SockJS.CONNECTING) {
  1008          if (that._transport_tref) {
  1009              clearTimeout(that._transport_tref);
  1010              that._transport_tref = null;
  1011          }
  1012          that.readyState = SockJS.OPEN;
  1013          that.dispatchEvent(new SimpleEvent("open"));
  1014      } else {
  1015          // The server might have been restarted, and lost track of our
  1016          // connection.
  1017          that._didClose(1006, "Server lost session");
  1018      }
  1019  };
  1020  
  1021  SockJS.prototype._dispatchMessage = function(data) {
  1022      var that = this;
  1023      if (that.readyState !== SockJS.OPEN)
  1024              return;
  1025      that.dispatchEvent(new SimpleEvent("message", {data: data}));
  1026  };
  1027  
  1028  SockJS.prototype._dispatchHeartbeat = function(data) {
  1029      var that = this;
  1030      if (that.readyState !== SockJS.OPEN)
  1031          return;
  1032      that.dispatchEvent(new SimpleEvent('heartbeat', {}));
  1033  };
  1034  
  1035  SockJS.prototype._didClose = function(code, reason, force) {
  1036      var that = this;
  1037      if (that.readyState !== SockJS.CONNECTING &&
  1038          that.readyState !== SockJS.OPEN &&
  1039          that.readyState !== SockJS.CLOSING)
  1040              throw new Error('INVALID_STATE_ERR');
  1041      if (that._ir) {
  1042          that._ir.nuke();
  1043          that._ir = null;
  1044      }
  1045  
  1046      if (that._transport) {
  1047          that._transport.doCleanup();
  1048          that._transport = null;
  1049      }
  1050  
  1051      var close_event = new SimpleEvent("close", {
  1052          code: code,
  1053          reason: reason,
  1054          wasClean: utils.userSetCode(code)});
  1055  
  1056      if (!utils.userSetCode(code) &&
  1057          that.readyState === SockJS.CONNECTING && !force) {
  1058          if (that._try_next_protocol(close_event)) {
  1059              return;
  1060          }
  1061          close_event = new SimpleEvent("close", {code: 2000,
  1062                                                  reason: "All transports failed",
  1063                                                  wasClean: false,
  1064                                                  last_event: close_event});
  1065      }
  1066      that.readyState = SockJS.CLOSED;
  1067  
  1068      utils.delay(function() {
  1069                     that.dispatchEvent(close_event);
  1070                  });
  1071  };
  1072  
  1073  SockJS.prototype._didMessage = function(data) {
  1074      var that = this;
  1075      var type = data.slice(0, 1);
  1076      switch(type) {
  1077      case 'o':
  1078          that._dispatchOpen();
  1079          break;
  1080      case 'a':
  1081          var payload = JSON.parse(data.slice(1) || '[]');
  1082          for(var i=0; i < payload.length; i++){
  1083              that._dispatchMessage(payload[i]);
  1084          }
  1085          break;
  1086      case 'm':
  1087          var payload = JSON.parse(data.slice(1) || 'null');
  1088          that._dispatchMessage(payload);
  1089          break;
  1090      case 'c':
  1091          var payload = JSON.parse(data.slice(1) || '[]');
  1092          that._didClose(payload[0], payload[1]);
  1093          break;
  1094      case 'h':
  1095          that._dispatchHeartbeat();
  1096          break;
  1097      }
  1098  };
  1099  
  1100  SockJS.prototype._try_next_protocol = function(close_event) {
  1101      var that = this;
  1102      if (that.protocol) {
  1103          that._debug('Closed transport:', that.protocol, ''+close_event);
  1104          that.protocol = null;
  1105      }
  1106      if (that._transport_tref) {
  1107          clearTimeout(that._transport_tref);
  1108          that._transport_tref = null;
  1109      }
  1110  
  1111      while(1) {
  1112          var protocol = that.protocol = that._protocols.shift();
  1113          if (!protocol) {
  1114              return false;
  1115          }
  1116          // Some protocols require access to `body`, what if were in
  1117          // the `head`?
  1118          if (SockJS[protocol] &&
  1119              SockJS[protocol].need_body === true &&
  1120              (!_document.body ||
  1121               (typeof _document.readyState !== 'undefined'
  1122                && _document.readyState !== 'complete'))) {
  1123              that._protocols.unshift(protocol);
  1124              that.protocol = 'waiting-for-load';
  1125              utils.attachEvent('load', function(){
  1126                  that._try_next_protocol();
  1127              });
  1128              return true;
  1129          }
  1130  
  1131          if (!SockJS[protocol] ||
  1132                !SockJS[protocol].enabled(that._options)) {
  1133              that._debug('Skipping transport:', protocol);
  1134          } else {
  1135              var roundTrips = SockJS[protocol].roundTrips || 1;
  1136              var to = ((that._options.rto || 0) * roundTrips) || 5000;
  1137              that._transport_tref = utils.delay(to, function() {
  1138                  if (that.readyState === SockJS.CONNECTING) {
  1139                      // I can't understand how it is possible to run
  1140                      // this timer, when the state is CLOSED, but
  1141                      // apparently in IE everythin is possible.
  1142                      that._didClose(2007, "Transport timeouted");
  1143                  }
  1144              });
  1145  
  1146              var connid = utils.random_string(8);
  1147              var trans_url = that._base_url + '/' + that._server + '/' + connid;
  1148              that._debug('Opening transport:', protocol, ' url:'+trans_url,
  1149                          ' RTO:'+that._options.rto);
  1150              that._transport = new SockJS[protocol](that, trans_url,
  1151                                                     that._base_url);
  1152              return true;
  1153          }
  1154      }
  1155  };
  1156  
  1157  SockJS.prototype.close = function(code, reason) {
  1158      var that = this;
  1159      if (code && !utils.userSetCode(code))
  1160          throw new Error("INVALID_ACCESS_ERR");
  1161      if(that.readyState !== SockJS.CONNECTING &&
  1162         that.readyState !== SockJS.OPEN) {
  1163          return false;
  1164      }
  1165      that.readyState = SockJS.CLOSING;
  1166      that._didClose(code || 1000, reason || "Normal closure");
  1167      return true;
  1168  };
  1169  
  1170  SockJS.prototype.send = function(data) {
  1171      var that = this;
  1172      if (that.readyState === SockJS.CONNECTING)
  1173          throw new Error('INVALID_STATE_ERR');
  1174      if (that.readyState === SockJS.OPEN) {
  1175          that._transport.doSend(utils.quote('' + data));
  1176      }
  1177      return true;
  1178  };
  1179  
  1180  SockJS.prototype._applyInfo = function(info, rtt, protocols_whitelist) {
  1181      var that = this;
  1182      that._options.info = info;
  1183      that._options.rtt = rtt;
  1184      that._options.rto = utils.countRTO(rtt);
  1185      that._options.info.null_origin = !_document.domain;
  1186      var probed = utils.probeProtocols();
  1187      that._protocols = utils.detectProtocols(probed, protocols_whitelist, info);
  1188  };
  1189  //         [*] End of lib/sockjs.js
  1190  
  1191  
  1192  //         [*] Including lib/trans-websocket.js
  1193  /*
  1194   * ***** BEGIN LICENSE BLOCK *****
  1195   * Copyright (c) 2011-2012 VMware, Inc.
  1196   *
  1197   * For the license see COPYING.
  1198   * ***** END LICENSE BLOCK *****
  1199   */
  1200  
  1201  var WebSocketTransport = SockJS.websocket = function(ri, trans_url) {
  1202      var that = this;
  1203      var url = trans_url + '/websocket';
  1204      if (url.slice(0, 5) === 'https') {
  1205          url = 'wss' + url.slice(5);
  1206      } else {
  1207          url = 'ws' + url.slice(4);
  1208      }
  1209      that.ri = ri;
  1210      that.url = url;
  1211      var Constructor = _window.WebSocket || _window.MozWebSocket;
  1212  
  1213      that.ws = new Constructor(that.url);
  1214      that.ws.onmessage = function(e) {
  1215          that.ri._didMessage(e.data);
  1216      };
  1217      // Firefox has an interesting bug. If a websocket connection is
  1218      // created after onunload, it stays alive even when user
  1219      // navigates away from the page. In such situation let's lie -
  1220      // let's not open the ws connection at all. See:
  1221      // https://github.com/sockjs/sockjs-client/issues/28
  1222      // https://bugzilla.mozilla.org/show_bug.cgi?id=696085
  1223      that.unload_ref = utils.unload_add(function(){that.ws.close()});
  1224      that.ws.onclose = function() {
  1225          that.ri._didMessage(utils.closeFrame(1006, "WebSocket connection broken"));
  1226      };
  1227  };
  1228  
  1229  WebSocketTransport.prototype.doSend = function(data) {
  1230      this.ws.send('[' + data + ']');
  1231  };
  1232  
  1233  WebSocketTransport.prototype.doCleanup = function() {
  1234      var that = this;
  1235      var ws = that.ws;
  1236      if (ws) {
  1237          ws.onmessage = ws.onclose = null;
  1238          ws.close();
  1239          utils.unload_del(that.unload_ref);
  1240          that.unload_ref = that.ri = that.ws = null;
  1241      }
  1242  };
  1243  
  1244  WebSocketTransport.enabled = function() {
  1245      return !!(_window.WebSocket || _window.MozWebSocket);
  1246  };
  1247  
  1248  // In theory, ws should require 1 round trip. But in chrome, this is
  1249  // not very stable over SSL. Most likely a ws connection requires a
  1250  // separate SSL connection, in which case 2 round trips are an
  1251  // absolute minumum.
  1252  WebSocketTransport.roundTrips = 2;
  1253  //         [*] End of lib/trans-websocket.js
  1254  
  1255  
  1256  //         [*] Including lib/trans-sender.js
  1257  /*
  1258   * ***** BEGIN LICENSE BLOCK *****
  1259   * Copyright (c) 2011-2012 VMware, Inc.
  1260   *
  1261   * For the license see COPYING.
  1262   * ***** END LICENSE BLOCK *****
  1263   */
  1264  
  1265  var BufferedSender = function() {};
  1266  BufferedSender.prototype.send_constructor = function(sender) {
  1267      var that = this;
  1268      that.send_buffer = [];
  1269      that.sender = sender;
  1270  };
  1271  BufferedSender.prototype.doSend = function(message) {
  1272      var that = this;
  1273      that.send_buffer.push(message);
  1274      if (!that.send_stop) {
  1275          that.send_schedule();
  1276      }
  1277  };
  1278  
  1279  // For polling transports in a situation when in the message callback,
  1280  // new message is being send. If the sending connection was started
  1281  // before receiving one, it is possible to saturate the network and
  1282  // timeout due to the lack of receiving socket. To avoid that we delay
  1283  // sending messages by some small time, in order to let receiving
  1284  // connection be started beforehand. This is only a halfmeasure and
  1285  // does not fix the big problem, but it does make the tests go more
  1286  // stable on slow networks.
  1287  BufferedSender.prototype.send_schedule_wait = function() {
  1288      var that = this;
  1289      var tref;
  1290      that.send_stop = function() {
  1291          that.send_stop = null;
  1292          clearTimeout(tref);
  1293      };
  1294      tref = utils.delay(25, function() {
  1295          that.send_stop = null;
  1296          that.send_schedule();
  1297      });
  1298  };
  1299  
  1300  BufferedSender.prototype.send_schedule = function() {
  1301      var that = this;
  1302      if (that.send_buffer.length > 0) {
  1303          var payload = '[' + that.send_buffer.join(',') + ']';
  1304          that.send_stop = that.sender(that.trans_url, payload, function(success, abort_reason) {
  1305              that.send_stop = null;
  1306              if (success === false) {
  1307                  that.ri._didClose(1006, 'Sending error ' + abort_reason);
  1308              } else {
  1309                  that.send_schedule_wait();
  1310              }
  1311          });
  1312          that.send_buffer = [];
  1313      }
  1314  };
  1315  
  1316  BufferedSender.prototype.send_destructor = function() {
  1317      var that = this;
  1318      if (that._send_stop) {
  1319          that._send_stop();
  1320      }
  1321      that._send_stop = null;
  1322  };
  1323  
  1324  var jsonPGenericSender = function(url, payload, callback) {
  1325      var that = this;
  1326  
  1327      if (!('_send_form' in that)) {
  1328          var form = that._send_form = _document.createElement('form');
  1329          var area = that._send_area = _document.createElement('textarea');
  1330          area.name = 'd';
  1331          form.style.display = 'none';
  1332          form.style.position = 'absolute';
  1333          form.method = 'POST';
  1334          form.enctype = 'application/x-www-form-urlencoded';
  1335          form.acceptCharset = "UTF-8";
  1336          form.appendChild(area);
  1337          _document.body.appendChild(form);
  1338      }
  1339      var form = that._send_form;
  1340      var area = that._send_area;
  1341      var id = 'a' + utils.random_string(8);
  1342      form.target = id;
  1343      form.action = url + '/jsonp_send?i=' + id;
  1344  
  1345      var iframe;
  1346      try {
  1347          // ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
  1348          iframe = _document.createElement('<iframe name="'+ id +'">');
  1349      } catch(x) {
  1350          iframe = _document.createElement('iframe');
  1351          iframe.name = id;
  1352      }
  1353      iframe.id = id;
  1354      form.appendChild(iframe);
  1355      iframe.style.display = 'none';
  1356  
  1357      try {
  1358          area.value = payload;
  1359      } catch(e) {
  1360          utils.log('Your browser is seriously broken. Go home! ' + e.message);
  1361      }
  1362      form.submit();
  1363  
  1364      var completed = function(e) {
  1365          if (!iframe.onerror) return;
  1366          iframe.onreadystatechange = iframe.onerror = iframe.onload = null;
  1367          // Opera mini doesn't like if we GC iframe
  1368          // immediately, thus this timeout.
  1369          utils.delay(500, function() {
  1370                         iframe.parentNode.removeChild(iframe);
  1371                         iframe = null;
  1372                     });
  1373          area.value = '';
  1374          // It is not possible to detect if the iframe succeeded or
  1375          // failed to submit our form.
  1376          callback(true);
  1377      };
  1378      iframe.onerror = iframe.onload = completed;
  1379      iframe.onreadystatechange = function(e) {
  1380          if (iframe.readyState == 'complete') completed();
  1381      };
  1382      return completed;
  1383  };
  1384  
  1385  var createAjaxSender = function(AjaxObject) {
  1386      return function(url, payload, callback) {
  1387          var xo = new AjaxObject('POST', url + '/xhr_send', payload);
  1388          xo.onfinish = function(status, text) {
  1389              callback(status === 200 || status === 204,
  1390                       'http status ' + status);
  1391          };
  1392          return function(abort_reason) {
  1393              callback(false, abort_reason);
  1394          };
  1395      };
  1396  };
  1397  //         [*] End of lib/trans-sender.js
  1398  
  1399  
  1400  //         [*] Including lib/trans-jsonp-receiver.js
  1401  /*
  1402   * ***** BEGIN LICENSE BLOCK *****
  1403   * Copyright (c) 2011-2012 VMware, Inc.
  1404   *
  1405   * For the license see COPYING.
  1406   * ***** END LICENSE BLOCK *****
  1407   */
  1408  
  1409  // Parts derived from Socket.io:
  1410  //    https://github.com/LearnBoost/socket.io/blob/0.6.17/lib/socket.io/transports/jsonp-polling.js
  1411  // and jQuery-JSONP:
  1412  //    https://code.google.com/p/jquery-jsonp/source/browse/trunk/core/jquery.jsonp.js
  1413  var jsonPGenericReceiver = function(url, callback) {
  1414      var tref;
  1415      var script = _document.createElement('script');
  1416      var script2;  // Opera synchronous load trick.
  1417      var close_script = function(frame) {
  1418          if (script2) {
  1419              script2.parentNode.removeChild(script2);
  1420              script2 = null;
  1421          }
  1422          if (script) {
  1423              clearTimeout(tref);
  1424              // Unfortunately, you can't really abort script loading of
  1425              // the script.
  1426              script.parentNode.removeChild(script);
  1427              script.onreadystatechange = script.onerror =
  1428                  script.onload = script.onclick = null;
  1429              script = null;
  1430              callback(frame);
  1431              callback = null;
  1432          }
  1433      };
  1434  
  1435      // IE9 fires 'error' event after orsc or before, in random order.
  1436      var loaded_okay = false;
  1437      var error_timer = null;
  1438  
  1439      script.id = 'a' + utils.random_string(8);
  1440      script.src = url;
  1441      script.type = 'text/javascript';
  1442      script.charset = 'UTF-8';
  1443      script.onerror = function(e) {
  1444          if (!error_timer) {
  1445              // Delay firing close_script.
  1446              error_timer = setTimeout(function() {
  1447                  if (!loaded_okay) {
  1448                      close_script(utils.closeFrame(
  1449                          1006,
  1450                          "JSONP script loaded abnormally (onerror)"));
  1451                  }
  1452              }, 1000);
  1453          }
  1454      };
  1455      script.onload = function(e) {
  1456          close_script(utils.closeFrame(1006, "JSONP script loaded abnormally (onload)"));
  1457      };
  1458  
  1459      script.onreadystatechange = function(e) {
  1460          if (/loaded|closed/.test(script.readyState)) {
  1461              if (script && script.htmlFor && script.onclick) {
  1462                  loaded_okay = true;
  1463                  try {
  1464                      // In IE, actually execute the script.
  1465                      script.onclick();
  1466                  } catch (x) {}
  1467              }
  1468              if (script) {
  1469                  close_script(utils.closeFrame(1006, "JSONP script loaded abnormally (onreadystatechange)"));
  1470              }
  1471          }
  1472      };
  1473      // IE: event/htmlFor/onclick trick.
  1474      // One can't rely on proper order for onreadystatechange. In order to
  1475      // make sure, set a 'htmlFor' and 'event' properties, so that
  1476      // script code will be installed as 'onclick' handler for the
  1477      // script object. Later, onreadystatechange, manually execute this
  1478      // code. FF and Chrome doesn't work with 'event' and 'htmlFor'
  1479      // set. For reference see:
  1480      //   http://jaubourg.net/2010/07/loading-script-as-onclick-handler-of.html
  1481      // Also, read on that about script ordering:
  1482      //   http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
  1483      if (typeof script.async === 'undefined' && _document.attachEvent) {
  1484          // According to mozilla docs, in recent browsers script.async defaults
  1485          // to 'true', so we may use it to detect a good browser:
  1486          // https://developer.mozilla.org/en/HTML/Element/script
  1487          if (!/opera/i.test(navigator.userAgent)) {
  1488              // Naively assume we're in IE
  1489              try {
  1490                  script.htmlFor = script.id;
  1491                  script.event = "onclick";
  1492              } catch (x) {}
  1493              script.async = true;
  1494          } else {
  1495              // Opera, second sync script hack
  1496              script2 = _document.createElement('script');
  1497              script2.text = "try{var a = document.getElementById('"+script.id+"'); if(a)a.onerror();}catch(x){};";
  1498              script.async = script2.async = false;
  1499          }
  1500      }
  1501      if (typeof script.async !== 'undefined') {
  1502          script.async = true;
  1503      }
  1504  
  1505      // Fallback mostly for Konqueror - stupid timer, 35 seconds shall be plenty.
  1506      tref = setTimeout(function() {
  1507                            close_script(utils.closeFrame(1006, "JSONP script loaded abnormally (timeout)"));
  1508                        }, 35000);
  1509  
  1510      var head = _document.getElementsByTagName('head')[0];
  1511      head.insertBefore(script, head.firstChild);
  1512      if (script2) {
  1513          head.insertBefore(script2, head.firstChild);
  1514      }
  1515      return close_script;
  1516  };
  1517  //         [*] End of lib/trans-jsonp-receiver.js
  1518  
  1519  
  1520  //         [*] Including lib/trans-jsonp-polling.js
  1521  /*
  1522   * ***** BEGIN LICENSE BLOCK *****
  1523   * Copyright (c) 2011-2012 VMware, Inc.
  1524   *
  1525   * For the license see COPYING.
  1526   * ***** END LICENSE BLOCK *****
  1527   */
  1528  
  1529  // The simplest and most robust transport, using the well-know cross
  1530  // domain hack - JSONP. This transport is quite inefficient - one
  1531  // mssage could use up to one http request. But at least it works almost
  1532  // everywhere.
  1533  // Known limitations:
  1534  //   o you will get a spinning cursor
  1535  //   o for Konqueror a dumb timer is needed to detect errors
  1536  
  1537  
  1538  var JsonPTransport = SockJS['jsonp-polling'] = function(ri, trans_url) {
  1539      utils.polluteGlobalNamespace();
  1540      var that = this;
  1541      that.ri = ri;
  1542      that.trans_url = trans_url;
  1543      that.send_constructor(jsonPGenericSender);
  1544      that._schedule_recv();
  1545  };
  1546  
  1547  // Inheritnace
  1548  JsonPTransport.prototype = new BufferedSender();
  1549  
  1550  JsonPTransport.prototype._schedule_recv = function() {
  1551      var that = this;
  1552      var callback = function(data) {
  1553          that._recv_stop = null;
  1554          if (data) {
  1555              // no data - heartbeat;
  1556              if (!that._is_closing) {
  1557                  that.ri._didMessage(data);
  1558              }
  1559          }
  1560          // The message can be a close message, and change is_closing state.
  1561          if (!that._is_closing) {
  1562              that._schedule_recv();
  1563          }
  1564      };
  1565      that._recv_stop = jsonPReceiverWrapper(that.trans_url + '/jsonp',
  1566                                             jsonPGenericReceiver, callback);
  1567  };
  1568  
  1569  JsonPTransport.enabled = function() {
  1570      return true;
  1571  };
  1572  
  1573  JsonPTransport.need_body = true;
  1574  
  1575  
  1576  JsonPTransport.prototype.doCleanup = function() {
  1577      var that = this;
  1578      that._is_closing = true;
  1579      if (that._recv_stop) {
  1580          that._recv_stop();
  1581      }
  1582      that.ri = that._recv_stop = null;
  1583      that.send_destructor();
  1584  };
  1585  
  1586  
  1587  // Abstract away code that handles global namespace pollution.
  1588  var jsonPReceiverWrapper = function(url, constructReceiver, user_callback) {
  1589      var id = 'a' + utils.random_string(6);
  1590      var url_id = url + '?c=' + escape(WPrefix + '.' + id);
  1591  
  1592      // Unfortunately it is not possible to abort loading of the
  1593      // script. We need to keep track of frake close frames.
  1594      var aborting = 0;
  1595  
  1596      // Callback will be called exactly once.
  1597      var callback = function(frame) {
  1598          switch(aborting) {
  1599          case 0:
  1600              // Normal behaviour - delete hook _and_ emit message.
  1601              delete _window[WPrefix][id];
  1602              user_callback(frame);
  1603              break;
  1604          case 1:
  1605              // Fake close frame - emit but don't delete hook.
  1606              user_callback(frame);
  1607              aborting = 2;
  1608              break;
  1609          case 2:
  1610              // Got frame after connection was closed, delete hook, don't emit.
  1611              delete _window[WPrefix][id];
  1612              break;
  1613          }
  1614      };
  1615  
  1616      var close_script = constructReceiver(url_id, callback);
  1617      _window[WPrefix][id] = close_script;
  1618      var stop = function() {
  1619          if (_window[WPrefix][id]) {
  1620              aborting = 1;
  1621              _window[WPrefix][id](utils.closeFrame(1000, "JSONP user aborted read"));
  1622          }
  1623      };
  1624      return stop;
  1625  };
  1626  //         [*] End of lib/trans-jsonp-polling.js
  1627  
  1628  
  1629  //         [*] Including lib/trans-xhr.js
  1630  /*
  1631   * ***** BEGIN LICENSE BLOCK *****
  1632   * Copyright (c) 2011-2012 VMware, Inc.
  1633   *
  1634   * For the license see COPYING.
  1635   * ***** END LICENSE BLOCK *****
  1636   */
  1637  
  1638  var AjaxBasedTransport = function() {};
  1639  AjaxBasedTransport.prototype = new BufferedSender();
  1640  
  1641  AjaxBasedTransport.prototype.run = function(ri, trans_url,
  1642                                              url_suffix, Receiver, AjaxObject) {
  1643      var that = this;
  1644      that.ri = ri;
  1645      that.trans_url = trans_url;
  1646      that.send_constructor(createAjaxSender(AjaxObject));
  1647      that.poll = new Polling(ri, Receiver,
  1648                              trans_url + url_suffix, AjaxObject);
  1649  };
  1650  
  1651  AjaxBasedTransport.prototype.doCleanup = function() {
  1652      var that = this;
  1653      if (that.poll) {
  1654          that.poll.abort();
  1655          that.poll = null;
  1656      }
  1657  };
  1658  
  1659  // xhr-streaming
  1660  var XhrStreamingTransport = SockJS['xhr-streaming'] = function(ri, trans_url) {
  1661      this.run(ri, trans_url, '/xhr_streaming', XhrReceiver, utils.XHRCorsObject);
  1662  };
  1663  
  1664  XhrStreamingTransport.prototype = new AjaxBasedTransport();
  1665  
  1666  XhrStreamingTransport.enabled = function() {
  1667      // Support for CORS Ajax aka Ajax2? Opera 12 claims CORS but
  1668      // doesn't do streaming.
  1669      return (_window.XMLHttpRequest &&
  1670              'withCredentials' in new XMLHttpRequest() &&
  1671              (!/opera/i.test(navigator.userAgent)));
  1672  };
  1673  XhrStreamingTransport.roundTrips = 2; // preflight, ajax
  1674  
  1675  // Safari gets confused when a streaming ajax request is started
  1676  // before onload. This causes the load indicator to spin indefinetely.
  1677  XhrStreamingTransport.need_body = true;
  1678  
  1679  
  1680  // According to:
  1681  //   http://stackoverflow.com/questions/1641507/detect-browser-support-for-cross-domain-xmlhttprequests
  1682  //   http://hacks.mozilla.org/2009/07/cross-site-xmlhttprequest-with-cors/
  1683  
  1684  
  1685  // xdr-streaming
  1686  var XdrStreamingTransport = SockJS['xdr-streaming'] = function(ri, trans_url) {
  1687      this.run(ri, trans_url, '/xhr_streaming', XhrReceiver, utils.XDRObject);
  1688  };
  1689  
  1690  XdrStreamingTransport.prototype = new AjaxBasedTransport();
  1691  
  1692  XdrStreamingTransport.enabled = function() {
  1693      return !!_window.XDomainRequest;
  1694  };
  1695  XdrStreamingTransport.roundTrips = 2; // preflight, ajax
  1696  
  1697  
  1698  
  1699  // xhr-polling
  1700  var XhrPollingTransport = SockJS['xhr-polling'] = function(ri, trans_url) {
  1701      this.run(ri, trans_url, '/xhr', XhrReceiver, utils.XHRCorsObject);
  1702  };
  1703  
  1704  XhrPollingTransport.prototype = new AjaxBasedTransport();
  1705  
  1706  XhrPollingTransport.enabled = XhrStreamingTransport.enabled;
  1707  XhrPollingTransport.roundTrips = 2; // preflight, ajax
  1708  
  1709  
  1710  // xdr-polling
  1711  var XdrPollingTransport = SockJS['xdr-polling'] = function(ri, trans_url) {
  1712      this.run(ri, trans_url, '/xhr', XhrReceiver, utils.XDRObject);
  1713  };
  1714  
  1715  XdrPollingTransport.prototype = new AjaxBasedTransport();
  1716  
  1717  XdrPollingTransport.enabled = XdrStreamingTransport.enabled;
  1718  XdrPollingTransport.roundTrips = 2; // preflight, ajax
  1719  //         [*] End of lib/trans-xhr.js
  1720  
  1721  
  1722  //         [*] Including lib/trans-iframe.js
  1723  /*
  1724   * ***** BEGIN LICENSE BLOCK *****
  1725   * Copyright (c) 2011-2012 VMware, Inc.
  1726   *
  1727   * For the license see COPYING.
  1728   * ***** END LICENSE BLOCK *****
  1729   */
  1730  
  1731  // Few cool transports do work only for same-origin. In order to make
  1732  // them working cross-domain we shall use iframe, served form the
  1733  // remote domain. New browsers, have capabilities to communicate with
  1734  // cross domain iframe, using postMessage(). In IE it was implemented
  1735  // from IE 8+, but of course, IE got some details wrong:
  1736  //    http://msdn.microsoft.com/en-us/library/cc197015(v=VS.85).aspx
  1737  //    http://stevesouders.com/misc/test-postmessage.php
  1738  
  1739  var IframeTransport = function() {};
  1740  
  1741  IframeTransport.prototype.i_constructor = function(ri, trans_url, base_url) {
  1742      var that = this;
  1743      that.ri = ri;
  1744      that.origin = utils.getOrigin(base_url);
  1745      that.base_url = base_url;
  1746      that.trans_url = trans_url;
  1747  
  1748      var iframe_url = base_url + '/iframe.html';
  1749      if (that.ri._options.devel) {
  1750          iframe_url += '?t=' + (+new Date);
  1751      }
  1752      that.window_id = utils.random_string(8);
  1753      iframe_url += '#' + that.window_id;
  1754  
  1755      that.iframeObj = utils.createIframe(iframe_url, function(r) {
  1756                                              that.ri._didClose(1006, "Unable to load an iframe (" + r + ")");
  1757                                          });
  1758  
  1759      that.onmessage_cb = utils.bind(that.onmessage, that);
  1760      utils.attachMessage(that.onmessage_cb);
  1761  };
  1762  
  1763  IframeTransport.prototype.doCleanup = function() {
  1764      var that = this;
  1765      if (that.iframeObj) {
  1766          utils.detachMessage(that.onmessage_cb);
  1767          try {
  1768              // When the iframe is not loaded, IE raises an exception
  1769              // on 'contentWindow'.
  1770              if (that.iframeObj.iframe.contentWindow) {
  1771                  that.postMessage('c');
  1772              }
  1773          } catch (x) {}
  1774          that.iframeObj.cleanup();
  1775          that.iframeObj = null;
  1776          that.onmessage_cb = that.iframeObj = null;
  1777      }
  1778  };
  1779  
  1780  IframeTransport.prototype.onmessage = function(e) {
  1781      var that = this;
  1782      if (e.origin !== that.origin) return;
  1783      var window_id = e.data.slice(0, 8);
  1784      var type = e.data.slice(8, 9);
  1785      var data = e.data.slice(9);
  1786  
  1787      if (window_id !== that.window_id) return;
  1788  
  1789      switch(type) {
  1790      case 's':
  1791          that.iframeObj.loaded();
  1792          that.postMessage('s', JSON.stringify([SockJS.version, that.protocol, that.trans_url, that.base_url]));
  1793          break;
  1794      case 't':
  1795          that.ri._didMessage(data);
  1796          break;
  1797      }
  1798  };
  1799  
  1800  IframeTransport.prototype.postMessage = function(type, data) {
  1801      var that = this;
  1802      that.iframeObj.post(that.window_id + type + (data || ''), that.origin);
  1803  };
  1804  
  1805  IframeTransport.prototype.doSend = function (message) {
  1806      this.postMessage('m', message);
  1807  };
  1808  
  1809  IframeTransport.enabled = function() {
  1810      // postMessage misbehaves in konqueror 4.6.5 - the messages are delivered with
  1811      // huge delay, or not at all.
  1812      var konqueror = navigator && navigator.userAgent && navigator.userAgent.indexOf('Konqueror') !== -1;
  1813      return ((typeof _window.postMessage === 'function' ||
  1814              typeof _window.postMessage === 'object') && (!konqueror));
  1815  };
  1816  //         [*] End of lib/trans-iframe.js
  1817  
  1818  
  1819  //         [*] Including lib/trans-iframe-within.js
  1820  /*
  1821   * ***** BEGIN LICENSE BLOCK *****
  1822   * Copyright (c) 2011-2012 VMware, Inc.
  1823   *
  1824   * For the license see COPYING.
  1825   * ***** END LICENSE BLOCK *****
  1826   */
  1827  
  1828  var curr_window_id;
  1829  
  1830  var postMessage = function (type, data) {
  1831      if(parent !== _window) {
  1832          parent.postMessage(curr_window_id + type + (data || ''), '*');
  1833      } else {
  1834          utils.log("Can't postMessage, no parent window.", type, data);
  1835      }
  1836  };
  1837  
  1838  var FacadeJS = function() {};
  1839  FacadeJS.prototype._didClose = function (code, reason) {
  1840      postMessage('t', utils.closeFrame(code, reason));
  1841  };
  1842  FacadeJS.prototype._didMessage = function (frame) {
  1843      postMessage('t', frame);
  1844  };
  1845  FacadeJS.prototype._doSend = function (data) {
  1846      this._transport.doSend(data);
  1847  };
  1848  FacadeJS.prototype._doCleanup = function () {
  1849      this._transport.doCleanup();
  1850  };
  1851  
  1852  utils.parent_origin = undefined;
  1853  
  1854  SockJS.bootstrap_iframe = function() {
  1855      var facade;
  1856      curr_window_id = _document.location.hash.slice(1);
  1857      var onMessage = function(e) {
  1858          if(e.source !== parent) return;
  1859          if(typeof utils.parent_origin === 'undefined')
  1860              utils.parent_origin = e.origin;
  1861          if (e.origin !== utils.parent_origin) return;
  1862  
  1863          var window_id = e.data.slice(0, 8);
  1864          var type = e.data.slice(8, 9);
  1865          var data = e.data.slice(9);
  1866          if (window_id !== curr_window_id) return;
  1867          switch(type) {
  1868          case 's':
  1869              var p = JSON.parse(data);
  1870              var version = p[0];
  1871              var protocol = p[1];
  1872              var trans_url = p[2];
  1873              var base_url = p[3];
  1874              if (version !== SockJS.version) {
  1875                  utils.log("Incompatibile SockJS! Main site uses:" +
  1876                            " \"" + version + "\", the iframe:" +
  1877                            " \"" + SockJS.version + "\".");
  1878              }
  1879              if (!utils.flatUrl(trans_url) || !utils.flatUrl(base_url)) {
  1880                  utils.log("Only basic urls are supported in SockJS");
  1881                  return;
  1882              }
  1883  
  1884              if (!utils.isSameOriginUrl(trans_url) ||
  1885                  !utils.isSameOriginUrl(base_url)) {
  1886                  utils.log("Can't connect to different domain from within an " +
  1887                            "iframe. (" + JSON.stringify([_window.location.href, trans_url, base_url]) +
  1888                            ")");
  1889                  return;
  1890              }
  1891              facade = new FacadeJS();
  1892              facade._transport = new FacadeJS[protocol](facade, trans_url, base_url);
  1893              break;
  1894          case 'm':
  1895              facade._doSend(data);
  1896              break;
  1897          case 'c':
  1898              if (facade)
  1899                  facade._doCleanup();
  1900              facade = null;
  1901              break;
  1902          }
  1903      };
  1904  
  1905      // alert('test ticker');
  1906      // facade = new FacadeJS();
  1907      // facade._transport = new FacadeJS['w-iframe-xhr-polling'](facade, 'http://host.com:9999/ticker/12/basd');
  1908  
  1909      utils.attachMessage(onMessage);
  1910  
  1911      // Start
  1912      postMessage('s');
  1913  };
  1914  //         [*] End of lib/trans-iframe-within.js
  1915  
  1916  
  1917  //         [*] Including lib/info.js
  1918  /*
  1919   * ***** BEGIN LICENSE BLOCK *****
  1920   * Copyright (c) 2011-2012 VMware, Inc.
  1921   *
  1922   * For the license see COPYING.
  1923   * ***** END LICENSE BLOCK *****
  1924   */
  1925  
  1926  var InfoReceiver = function(base_url, AjaxObject) {
  1927      var that = this;
  1928      utils.delay(function(){that.doXhr(base_url, AjaxObject);});
  1929  };
  1930  
  1931  InfoReceiver.prototype = new EventEmitter(['finish']);
  1932  
  1933  InfoReceiver.prototype.doXhr = function(base_url, AjaxObject) {
  1934      var that = this;
  1935      var t0 = (new Date()).getTime();
  1936      var xo = new AjaxObject('GET', base_url + '/info');
  1937  
  1938      var tref = utils.delay(8000,
  1939                             function(){xo.ontimeout();});
  1940  
  1941      xo.onfinish = function(status, text) {
  1942          clearTimeout(tref);
  1943          tref = null;
  1944          if (status === 200) {
  1945              var rtt = (new Date()).getTime() - t0;
  1946              var info = JSON.parse(text);
  1947              if (typeof info !== 'object') info = {};
  1948              that.emit('finish', info, rtt);
  1949          } else {
  1950              that.emit('finish');
  1951          }
  1952      };
  1953      xo.ontimeout = function() {
  1954          xo.close();
  1955          that.emit('finish');
  1956      };
  1957  };
  1958  
  1959  var InfoReceiverIframe = function(base_url) {
  1960      var that = this;
  1961      var go = function() {
  1962          var ifr = new IframeTransport();
  1963          ifr.protocol = 'w-iframe-info-receiver';
  1964          var fun = function(r) {
  1965              if (typeof r === 'string' && r.substr(0,1) === 'm') {
  1966                  var d = JSON.parse(r.substr(1));
  1967                  var info = d[0], rtt = d[1];
  1968                  that.emit('finish', info, rtt);
  1969              } else {
  1970                  that.emit('finish');
  1971              }
  1972              ifr.doCleanup();
  1973              ifr = null;
  1974          };
  1975          var mock_ri = {
  1976              _options: {},
  1977              _didClose: fun,
  1978              _didMessage: fun
  1979          };
  1980          ifr.i_constructor(mock_ri, base_url, base_url);
  1981      }
  1982      if(!_document.body) {
  1983          utils.attachEvent('load', go);
  1984      } else {
  1985          go();
  1986      }
  1987  };
  1988  InfoReceiverIframe.prototype = new EventEmitter(['finish']);
  1989  
  1990  
  1991  var InfoReceiverFake = function() {
  1992      // It may not be possible to do cross domain AJAX to get the info
  1993      // data, for example for IE7. But we want to run JSONP, so let's
  1994      // fake the response, with rtt=2s (rto=6s).
  1995      var that = this;
  1996      utils.delay(function() {
  1997          that.emit('finish', {}, 2000);
  1998      });
  1999  };
  2000  InfoReceiverFake.prototype = new EventEmitter(['finish']);
  2001  
  2002  var createInfoReceiver = function(base_url) {
  2003      if (utils.isSameOriginUrl(base_url)) {
  2004          // If, for some reason, we have SockJS locally - there's no
  2005          // need to start up the complex machinery. Just use ajax.
  2006          return new InfoReceiver(base_url, utils.XHRLocalObject);
  2007      }
  2008      switch (utils.isXHRCorsCapable()) {
  2009      case 1:
  2010          // XHRLocalObject -> no_credentials=true
  2011          return new InfoReceiver(base_url, utils.XHRLocalObject);
  2012      case 2:
  2013          return new InfoReceiver(base_url, utils.XDRObject);
  2014      case 3:
  2015          // Opera
  2016          return new InfoReceiverIframe(base_url);
  2017      default:
  2018          // IE 7
  2019          return new InfoReceiverFake();
  2020      };
  2021  };
  2022  
  2023  
  2024  var WInfoReceiverIframe = FacadeJS['w-iframe-info-receiver'] = function(ri, _trans_url, base_url) {
  2025      var ir = new InfoReceiver(base_url, utils.XHRLocalObject);
  2026      ir.onfinish = function(info, rtt) {
  2027          ri._didMessage('m'+JSON.stringify([info, rtt]));
  2028          ri._didClose();
  2029      }
  2030  };
  2031  WInfoReceiverIframe.prototype.doCleanup = function() {};
  2032  //         [*] End of lib/info.js
  2033  
  2034  
  2035  //         [*] Including lib/trans-iframe-eventsource.js
  2036  /*
  2037   * ***** BEGIN LICENSE BLOCK *****
  2038   * Copyright (c) 2011-2012 VMware, Inc.
  2039   *
  2040   * For the license see COPYING.
  2041   * ***** END LICENSE BLOCK *****
  2042   */
  2043  
  2044  var EventSourceIframeTransport = SockJS['iframe-eventsource'] = function () {
  2045      var that = this;
  2046      that.protocol = 'w-iframe-eventsource';
  2047      that.i_constructor.apply(that, arguments);
  2048  };
  2049  
  2050  EventSourceIframeTransport.prototype = new IframeTransport();
  2051  
  2052  EventSourceIframeTransport.enabled = function () {
  2053      return ('EventSource' in _window) && IframeTransport.enabled();
  2054  };
  2055  
  2056  EventSourceIframeTransport.need_body = true;
  2057  EventSourceIframeTransport.roundTrips = 3; // html, javascript, eventsource
  2058  
  2059  
  2060  // w-iframe-eventsource
  2061  var EventSourceTransport = FacadeJS['w-iframe-eventsource'] = function(ri, trans_url) {
  2062      this.run(ri, trans_url, '/eventsource', EventSourceReceiver, utils.XHRLocalObject);
  2063  }
  2064  EventSourceTransport.prototype = new AjaxBasedTransport();
  2065  //         [*] End of lib/trans-iframe-eventsource.js
  2066  
  2067  
  2068  //         [*] Including lib/trans-iframe-xhr-polling.js
  2069  /*
  2070   * ***** BEGIN LICENSE BLOCK *****
  2071   * Copyright (c) 2011-2012 VMware, Inc.
  2072   *
  2073   * For the license see COPYING.
  2074   * ***** END LICENSE BLOCK *****
  2075   */
  2076  
  2077  var XhrPollingIframeTransport = SockJS['iframe-xhr-polling'] = function () {
  2078      var that = this;
  2079      that.protocol = 'w-iframe-xhr-polling';
  2080      that.i_constructor.apply(that, arguments);
  2081  };
  2082  
  2083  XhrPollingIframeTransport.prototype = new IframeTransport();
  2084  
  2085  XhrPollingIframeTransport.enabled = function () {
  2086      return _window.XMLHttpRequest && IframeTransport.enabled();
  2087  };
  2088  
  2089  XhrPollingIframeTransport.need_body = true;
  2090  XhrPollingIframeTransport.roundTrips = 3; // html, javascript, xhr
  2091  
  2092  
  2093  // w-iframe-xhr-polling
  2094  var XhrPollingITransport = FacadeJS['w-iframe-xhr-polling'] = function(ri, trans_url) {
  2095      this.run(ri, trans_url, '/xhr', XhrReceiver, utils.XHRLocalObject);
  2096  };
  2097  
  2098  XhrPollingITransport.prototype = new AjaxBasedTransport();
  2099  //         [*] End of lib/trans-iframe-xhr-polling.js
  2100  
  2101  
  2102  //         [*] Including lib/trans-iframe-htmlfile.js
  2103  /*
  2104   * ***** BEGIN LICENSE BLOCK *****
  2105   * Copyright (c) 2011-2012 VMware, Inc.
  2106   *
  2107   * For the license see COPYING.
  2108   * ***** END LICENSE BLOCK *****
  2109   */
  2110  
  2111  // This transport generally works in any browser, but will cause a
  2112  // spinning cursor to appear in any browser other than IE.
  2113  // We may test this transport in all browsers - why not, but in
  2114  // production it should be only run in IE.
  2115  
  2116  var HtmlFileIframeTransport = SockJS['iframe-htmlfile'] = function () {
  2117      var that = this;
  2118      that.protocol = 'w-iframe-htmlfile';
  2119      that.i_constructor.apply(that, arguments);
  2120  };
  2121  
  2122  // Inheritance.
  2123  HtmlFileIframeTransport.prototype = new IframeTransport();
  2124  
  2125  HtmlFileIframeTransport.enabled = function() {
  2126      return IframeTransport.enabled();
  2127  };
  2128  
  2129  HtmlFileIframeTransport.need_body = true;
  2130  HtmlFileIframeTransport.roundTrips = 3; // html, javascript, htmlfile
  2131  
  2132  
  2133  // w-iframe-htmlfile
  2134  var HtmlFileTransport = FacadeJS['w-iframe-htmlfile'] = function(ri, trans_url) {
  2135      this.run(ri, trans_url, '/htmlfile', HtmlfileReceiver, utils.XHRLocalObject);
  2136  };
  2137  HtmlFileTransport.prototype = new AjaxBasedTransport();
  2138  //         [*] End of lib/trans-iframe-htmlfile.js
  2139  
  2140  
  2141  //         [*] Including lib/trans-polling.js
  2142  /*
  2143   * ***** BEGIN LICENSE BLOCK *****
  2144   * Copyright (c) 2011-2012 VMware, Inc.
  2145   *
  2146   * For the license see COPYING.
  2147   * ***** END LICENSE BLOCK *****
  2148   */
  2149  
  2150  var Polling = function(ri, Receiver, recv_url, AjaxObject) {
  2151      var that = this;
  2152      that.ri = ri;
  2153      that.Receiver = Receiver;
  2154      that.recv_url = recv_url;
  2155      that.AjaxObject = AjaxObject;
  2156      that._scheduleRecv();
  2157  };
  2158  
  2159  Polling.prototype._scheduleRecv = function() {
  2160      var that = this;
  2161      var poll = that.poll = new that.Receiver(that.recv_url, that.AjaxObject);
  2162      var msg_counter = 0;
  2163      poll.onmessage = function(e) {
  2164          msg_counter += 1;
  2165          that.ri._didMessage(e.data);
  2166      };
  2167      poll.onclose = function(e) {
  2168          that.poll = poll = poll.onmessage = poll.onclose = null;
  2169          if (!that.poll_is_closing) {
  2170              if (e.reason === 'permanent') {
  2171                  that.ri._didClose(1006, 'Polling error (' + e.reason + ')');
  2172              } else {
  2173                  that._scheduleRecv();
  2174              }
  2175          }
  2176      };
  2177  };
  2178  
  2179  Polling.prototype.abort = function() {
  2180      var that = this;
  2181      that.poll_is_closing = true;
  2182      if (that.poll) {
  2183          that.poll.abort();
  2184      }
  2185  };
  2186  //         [*] End of lib/trans-polling.js
  2187  
  2188  
  2189  //         [*] Including lib/trans-receiver-eventsource.js
  2190  /*
  2191   * ***** BEGIN LICENSE BLOCK *****
  2192   * Copyright (c) 2011-2012 VMware, Inc.
  2193   *
  2194   * For the license see COPYING.
  2195   * ***** END LICENSE BLOCK *****
  2196   */
  2197  
  2198  var EventSourceReceiver = function(url) {
  2199      var that = this;
  2200      var es = new EventSource(url);
  2201      es.onmessage = function(e) {
  2202          that.dispatchEvent(new SimpleEvent('message',
  2203                                             {'data': unescape(e.data)}));
  2204      };
  2205      that.es_close = es.onerror = function(e, abort_reason) {
  2206          // ES on reconnection has readyState = 0 or 1.
  2207          // on network error it's CLOSED = 2
  2208          var reason = abort_reason ? 'user' :
  2209              (es.readyState !== 2 ? 'network' : 'permanent');
  2210          that.es_close = es.onmessage = es.onerror = null;
  2211          // EventSource reconnects automatically.
  2212          es.close();
  2213          es = null;
  2214          // Safari and chrome < 15 crash if we close window before
  2215          // waiting for ES cleanup. See:
  2216          //   https://code.google.com/p/chromium/issues/detail?id=89155
  2217          utils.delay(200, function() {
  2218                          that.dispatchEvent(new SimpleEvent('close', {reason: reason}));
  2219                      });
  2220      };
  2221  };
  2222  
  2223  EventSourceReceiver.prototype = new REventTarget();
  2224  
  2225  EventSourceReceiver.prototype.abort = function() {
  2226      var that = this;
  2227      if (that.es_close) {
  2228          that.es_close({}, true);
  2229      }
  2230  };
  2231  //         [*] End of lib/trans-receiver-eventsource.js
  2232  
  2233  
  2234  //         [*] Including lib/trans-receiver-htmlfile.js
  2235  /*
  2236   * ***** BEGIN LICENSE BLOCK *****
  2237   * Copyright (c) 2011-2012 VMware, Inc.
  2238   *
  2239   * For the license see COPYING.
  2240   * ***** END LICENSE BLOCK *****
  2241   */
  2242  
  2243  var _is_ie_htmlfile_capable;
  2244  var isIeHtmlfileCapable = function() {
  2245      if (_is_ie_htmlfile_capable === undefined) {
  2246          if ('ActiveXObject' in _window) {
  2247              try {
  2248                  _is_ie_htmlfile_capable = !!new ActiveXObject('htmlfile');
  2249              } catch (x) {}
  2250          } else {
  2251              _is_ie_htmlfile_capable = false;
  2252          }
  2253      }
  2254      return _is_ie_htmlfile_capable;
  2255  };
  2256  
  2257  
  2258  var HtmlfileReceiver = function(url) {
  2259      var that = this;
  2260      utils.polluteGlobalNamespace();
  2261  
  2262      that.id = 'a' + utils.random_string(6, 26);
  2263      url += ((url.indexOf('?') === -1) ? '?' : '&') +
  2264          'c=' + escape(WPrefix + '.' + that.id);
  2265  
  2266      var constructor = isIeHtmlfileCapable() ?
  2267          utils.createHtmlfile : utils.createIframe;
  2268  
  2269      var iframeObj;
  2270      _window[WPrefix][that.id] = {
  2271          start: function () {
  2272              iframeObj.loaded();
  2273          },
  2274          message: function (data) {
  2275              that.dispatchEvent(new SimpleEvent('message', {'data': data}));
  2276          },
  2277          stop: function () {
  2278              that.iframe_close({}, 'network');
  2279          }
  2280      };
  2281      that.iframe_close = function(e, abort_reason) {
  2282          iframeObj.cleanup();
  2283          that.iframe_close = iframeObj = null;
  2284          delete _window[WPrefix][that.id];
  2285          that.dispatchEvent(new SimpleEvent('close', {reason: abort_reason}));
  2286      };
  2287      iframeObj = constructor(url, function(e) {
  2288                                  that.iframe_close({}, 'permanent');
  2289                              });
  2290  };
  2291  
  2292  HtmlfileReceiver.prototype = new REventTarget();
  2293  
  2294  HtmlfileReceiver.prototype.abort = function() {
  2295      var that = this;
  2296      if (that.iframe_close) {
  2297          that.iframe_close({}, 'user');
  2298      }
  2299  };
  2300  //         [*] End of lib/trans-receiver-htmlfile.js
  2301  
  2302  
  2303  //         [*] Including lib/trans-receiver-xhr.js
  2304  /*
  2305   * ***** BEGIN LICENSE BLOCK *****
  2306   * Copyright (c) 2011-2012 VMware, Inc.
  2307   *
  2308   * For the license see COPYING.
  2309   * ***** END LICENSE BLOCK *****
  2310   */
  2311  
  2312  var XhrReceiver = function(url, AjaxObject) {
  2313      var that = this;
  2314      var buf_pos = 0;
  2315  
  2316      that.xo = new AjaxObject('POST', url, null);
  2317      that.xo.onchunk = function(status, text) {
  2318          if (status !== 200) return;
  2319          while (1) {
  2320              var buf = text.slice(buf_pos);
  2321              var p = buf.indexOf('\n');
  2322              if (p === -1) break;
  2323              buf_pos += p+1;
  2324              var msg = buf.slice(0, p);
  2325              that.dispatchEvent(new SimpleEvent('message', {data: msg}));
  2326          }
  2327      };
  2328      that.xo.onfinish = function(status, text) {
  2329          that.xo.onchunk(status, text);
  2330          that.xo = null;
  2331          var reason = status === 200 ? 'network' : 'permanent';
  2332          that.dispatchEvent(new SimpleEvent('close', {reason: reason}));
  2333      }
  2334  };
  2335  
  2336  XhrReceiver.prototype = new REventTarget();
  2337  
  2338  XhrReceiver.prototype.abort = function() {
  2339      var that = this;
  2340      if (that.xo) {
  2341          that.xo.close();
  2342          that.dispatchEvent(new SimpleEvent('close', {reason: 'user'}));
  2343          that.xo = null;
  2344      }
  2345  };
  2346  //         [*] End of lib/trans-receiver-xhr.js
  2347  
  2348  
  2349  //         [*] Including lib/test-hooks.js
  2350  /*
  2351   * ***** BEGIN LICENSE BLOCK *****
  2352   * Copyright (c) 2011-2012 VMware, Inc.
  2353   *
  2354   * For the license see COPYING.
  2355   * ***** END LICENSE BLOCK *****
  2356   */
  2357  
  2358  // For testing
  2359  SockJS.getUtils = function(){
  2360      return utils;
  2361  };
  2362  
  2363  SockJS.getIframeTransport = function(){
  2364      return IframeTransport;
  2365  };
  2366  //         [*] End of lib/test-hooks.js
  2367  
  2368                    return SockJS;
  2369            })();
  2370  if ('_sockjs_onload' in window) setTimeout(_sockjs_onload, 1);
  2371  
  2372  // AMD compliance
  2373  if (typeof define === 'function' && define.amd) {
  2374      define('sockjs', [], function(){return SockJS;});
  2375  }
  2376  //     [*] End of lib/index.js
  2377  
  2378  // [*] End of lib/all.js
  2379