[Pkg-javascript-commits] [sockjs-client] 101/350: utils re-org

tonnerre at ancient-solutions.com tonnerre at ancient-solutions.com
Fri Aug 5 01:03:47 UTC 2016


This is an automated email from the git hooks/post-receive script.

tonnerre-guest pushed a commit to branch upstream
in repository sockjs-client.

commit ea6d035e3a42842fd3432ebb916b3a7ca69be483
Author: Bryce Kahle <bkahle at gmail.com>
Date:   Thu Oct 9 20:16:56 2014 -0400

    utils re-org
---
 lib/facade.js                      |   8 +-
 lib/iframe-bootstrap.js            |  16 +-
 lib/info-receiver.js               |   5 +-
 lib/main.js                        |  41 ++-
 lib/shims.js                       |  72 +++++
 lib/transport/jsonp-polling.js     |   7 +-
 lib/transport/lib/frames.js        |   9 +
 lib/transport/lib/iframe-utils.js  |  13 +
 lib/transport/lib/iframe.js        |   3 +-
 lib/transport/receiver/htmlfile.js |   3 +-
 lib/transport/sender/xhr-cors.js   |   9 +
 lib/transport/xdr-streaming.js     |   3 +-
 lib/transport/xhr-polling.js       |   5 +-
 lib/transport/xhr-streaming.js     |   5 +-
 lib/utils.js                       | 548 -------------------------------------
 lib/utils/escape.js                |  49 ++++
 lib/utils/event.js                 |  73 +++++
 lib/utils/iframe.js                | 127 +++++++++
 lib/utils/log.js                   |   7 +
 lib/utils/origin.js                |  28 ++
 lib/utils/random.js                |  43 +++
 tests/html/lib/testutils.js        |  57 ++--
 22 files changed, 533 insertions(+), 598 deletions(-)

diff --git a/lib/facade.js b/lib/facade.js
index 75e4904..c7494d5 100644
--- a/lib/facade.js
+++ b/lib/facade.js
@@ -1,6 +1,8 @@
 'use strict';
 
-var utils = require('./utils');
+var frameDefs = require('./lib/frames')
+  , iframeUtils = require('./transport/lib/iframe-utils')
+  ;
 
 function FacadeJS(transport) {
   this._transport = transport;
@@ -9,10 +11,10 @@ function FacadeJS(transport) {
 }
 
 FacadeJS.prototype._transportClose = function (code, reason) {
-  utils.postMessage('t', utils.closeFrame(code, reason));
+  iframeUtils.postMessage('t', frameDefs.close(code, reason));
 };
 FacadeJS.prototype._transportMessage = function (frame) {
-  utils.postMessage('t', frame);
+  iframeUtils.postMessage('t', frame);
 };
 FacadeJS.prototype._send = function (data) {
   this._transport.send(data);
diff --git a/lib/iframe-bootstrap.js b/lib/iframe-bootstrap.js
index 8046579..2c67654 100644
--- a/lib/iframe-bootstrap.js
+++ b/lib/iframe-bootstrap.js
@@ -4,6 +4,8 @@ var utils = require('./utils')
   , JSON3 = require('json3')
   , FacadeJS = require('./facade')
   , InfoIframeFacade = require('./transport/facade/info-receiver-iframe')
+  , iframeUtils = require('./transports/lib/iframe-utils')
+  , loc = require('./polyfills/location')
   ;
 
 module.exports = function (SockJS, facadeTransports) {
@@ -19,7 +21,7 @@ module.exports = function (SockJS, facadeTransports) {
   SockJS.bootstrap_iframe = function() {
     /* eslint-enable camelcase */
     var facade;
-    utils.currentWindowId = global.location.hash.slice(1);
+    iframeUtils.currentWindowId = global.location.hash.slice(1);
     var onMessage = function(e) {
       if (e.source !== parent) {
         return;
@@ -34,7 +36,7 @@ module.exports = function (SockJS, facadeTransports) {
       var windowId = e.data.slice(0, 8);
       var type = e.data.slice(8, 9);
       var data = e.data.slice(9);
-      if (windowId !== utils.currentWindowId) {
+      if (windowId !== iframeUtils.currentWindowId) {
         return;
       }
       switch(type) {
@@ -50,13 +52,9 @@ module.exports = function (SockJS, facadeTransports) {
                     ' "' + version + '", the iframe:' +
                     ' "' + SockJS.version + '".');
         }
-        if (!utils.flatUrl(transUrl) || !utils.flatUrl(baseUrl)) {
-          utils.log('Only basic urls are supported in SockJS');
-          return;
-        }
 
-        if (!utils.isSameOriginUrl(transUrl) ||
-            !utils.isSameOriginUrl(baseUrl)) {
+        if (!utils.isSameOriginUrl(transUrl, loc.href) ||
+            !utils.isSameOriginUrl(baseUrl, loc.href)) {
           utils.log('Can\'t connect to different domain from within an ' +
                     'iframe. (' + JSON3.stringify([global.location.href, transUrl, baseUrl]) +
                     ')');
@@ -79,6 +77,6 @@ module.exports = function (SockJS, facadeTransports) {
     utils.attachMessage(onMessage);
 
     // Start
-    utils.postMessage('s');
+    iframeUtils.postMessage('s');
   };
 };
diff --git a/lib/info-receiver.js b/lib/info-receiver.js
index 11ab0ae..9c542f1 100644
--- a/lib/info-receiver.js
+++ b/lib/info-receiver.js
@@ -20,10 +20,11 @@ function InfoReceiver(baseUrl) {
   EventEmitter.call(this);
 
   var AjaxObject = XHRFake;
+  // determine method of CORS support (if needed)
   if (utils.isSameOriginUrl(baseUrl, loc.href)) {
-    AjaxObject = XHRCors;
-  } else if (utils.isXHRCorsCapable() === 1) {
     AjaxObject = XHRLocal;
+  } else if (XHRCors.capable()) {
+    AjaxObject = XHRCors;
   } else if (XDRPolling.enabled(baseUrl)) {
     AjaxObject = XDR;
   } else if (IframeTransport.enabled()) {
diff --git a/lib/main.js b/lib/main.js
index 55fcb64..f7f1411 100644
--- a/lib/main.js
+++ b/lib/main.js
@@ -4,7 +4,7 @@ require('./shims');
 
 var u = require('url')
   , util = require('util')
-  , utils = require('./utils')
+  , random = require('./utils/random')
   , SecurityError = require('./error/securityerror')
   , InvalidAccessError = require('./error/invalidaccesserror')
   , InvalidStateError = require('./error/invalidstateerror')
@@ -72,13 +72,23 @@ function SockJS(url, protocols, transportsWhitelist) {
   // Step 6 - convert origin
   this._origin = loc.origin ? loc.origin.toLowerCase() : null;
 
-  // Step 7 - start connection in background
-  this.url = parsedUrl.href;
-  // if we don't have any path component, remove the trailing slash to make code easier
-  if (parsedUrl.path === '/') {
-    this.url = this.url.slice(0, -1);
+  // TODO do we want to allow relative urls? Spec says no
+
+  // strip port numbers for 80 and 443 cases
+  // Issue #74
+  if (!secure && parsedUrl.port === '80') {
+    parsedUrl.host = parsedUrl.port = null;
+  } else if (secure && parsedUrl.port === '443') {
+    parsedUrl.host = parsedUrl.port = null;
   }
 
+  // remove the trailing slash
+  parsedUrl.pathname = parsedUrl.pathname.replace(/[/]+$/, '');
+
+  // store the sanitized url
+  this.url = url.format(parsedUrl);
+
+  // Step 7 - start connection in background
   // obtain server info
   // http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-26
   this._ir = new InfoReceiver(this.url);
@@ -131,7 +141,7 @@ SockJS.prototype._receiveInfo = function(info, rtt) {
 
   // establish a round-trip timeout (RTO) based on the
   // round-trip time (RTT)
-  this._rto = utils.countRTO(rtt);
+  this._rto = this.countRTO(rtt);
   // allow server to override url used for the actual transport
   this._transUrl = info.base_url ? info.base_url : this.url;
   info.nullOrigin = !document.domain;
@@ -160,7 +170,7 @@ SockJS.prototype._connect = function() {
     var timeoutMs = (this._rto * Transport.roundTrips) || 5000;
     this._transportTimeoutId = setTimeout(this._transportTimeout.bind(this), timeoutMs);
 
-    var transportUrl = this._transUrl + '/' + this._server + '/' + utils.randomString(8);
+    var transportUrl = this._transUrl + '/' + this._server + '/' + random.string(8);
     var transport = new Transport(transportUrl, this._transUrl);
     transport.onmessage = this._transportMessage.bind(this);
     transport.onclose = this._transportClose.bind(this);
@@ -264,6 +274,21 @@ SockJS.prototype._close = function(code, reason, wasClean) {
   }.bind(this));
 };
 
+// See: http://www.erg.abdn.ac.uk/~gerrit/dccp/notes/ccid2/rto_estimator/
+// and RFC 2988.
+SockJS.prototype.countRTO = function (rtt) {
+  // In a local environment, when using IE8/9 and the `jsonp-polling`
+  // transport the time needed to establish a connection (the time that pass
+  // from the opening of the transport to the call of `_dispatchOpen`) is
+  // around 200msec (the lower bound used in the article above) and this
+  // causes spurious timeouts. For this reason we calculate a value slightly
+  // larger than that used in the article.
+  if (rtt > 100) {
+    return 4 * rtt; // rto > 400msec
+  }
+  return 300 + rtt; // 300msec < rto <= 400msec
+};
+
 module.exports = function (availableTransports) {
   transports = require('./transports')(availableTransports);
   return SockJS;
diff --git a/lib/shims.js b/lib/shims.js
index cd4f72f..b8e980d 100644
--- a/lib/shims.js
+++ b/lib/shims.js
@@ -31,3 +31,75 @@ if (!Function.prototype.bind) {
     return Bound;
   };
 }
+
+if (!Array.isArray) {
+  Array.isArray = function(arg) {
+    return Object.prototype.toString.call(arg) === '[object Array]';
+  };
+}
+
+// Production steps of ECMA-262, Edition 5, 15.4.4.14
+// Reference: http://es5.github.io/#x15.4.4.14
+if (!Array.prototype.indexOf) {
+  Array.prototype.indexOf = function(searchElement, fromIndex) {
+
+    var k;
+
+    // 1. Let O be the result of calling ToObject passing
+    //    the this value as the argument.
+    if (this == null) {
+      throw new TypeError('"this" is null or not defined');
+    }
+
+    var O = Object(this);
+
+    // 2. Let lenValue be the result of calling the Get
+    //    internal method of O with the argument "length".
+    // 3. Let len be ToUint32(lenValue).
+    var len = O.length >>> 0;
+
+    // 4. If len is 0, return -1.
+    if (len === 0) {
+      return -1;
+    }
+
+    // 5. If argument fromIndex was passed let n be
+    //    ToInteger(fromIndex); else let n be 0.
+    var n = +fromIndex || 0;
+
+    if (Math.abs(n) === Infinity) {
+      n = 0;
+    }
+
+    // 6. If n >= len, return -1.
+    if (n >= len) {
+      return -1;
+    }
+
+    // 7. If n >= 0, then Let k be n.
+    // 8. Else, n<0, Let k be len - abs(n).
+    //    If k is less than 0, then let k be 0.
+    k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
+
+    // 9. Repeat, while k < len
+    while (k < len) {
+      // a. Let Pk be ToString(k).
+      //   This is implicit for LHS operands of the in operator
+      // b. Let kPresent be the result of calling the
+      //    HasProperty internal method of O with argument Pk.
+      //   This step can be combined with c
+      // c. If kPresent is true, then
+      //    i.  Let elementK be the result of calling the Get
+      //        internal method of O with the argument ToString(k).
+      //   ii.  Let same be the result of applying the
+      //        Strict Equality Comparison Algorithm to
+      //        searchElement and elementK.
+      //  iii.  If same is true, return k.
+      if (k in O && O[k] === searchElement) {
+        return k;
+      }
+      k++;
+    }
+    return -1;
+  };
+}
diff --git a/lib/transport/jsonp-polling.js b/lib/transport/jsonp-polling.js
index 492e872..5320986 100644
--- a/lib/transport/jsonp-polling.js
+++ b/lib/transport/jsonp-polling.js
@@ -10,13 +10,14 @@
 
 var util = require('util')
   , utils = require('../utils')
+  , random = require('../utils/random')
   , BufferedSender = require('./lib/buffered-sender')
   , TransportMessageEvent = require('./lib/trans-message-event')
   ;
 
 // Abstract away code that handles global namespace pollution.
 var jsonPReceiverWrapper = function(url, constructReceiver, userCallback) {
-  var id = 'a' + utils.randomString(6);
+  var id = 'a' + random.string(6);
   var urlId = url + '?c=' + encodeURIComponent(utils.WPrefix + '.' + id);
 
   // Unfortunately it is not possible to abort loading of the
@@ -72,7 +73,7 @@ function jsonPGenericSender(url, payload, callback) {
     form.appendChild(area);
     document.body.appendChild(form);
   }
-  var id = 'a' + utils.randomString(8);
+  var id = 'a' + random.string(8);
   form.target = id;
   form.action = url + '/jsonp_send?i=' + id;
 
@@ -150,7 +151,7 @@ function jsonPGenericReceiver(url, callback) {
   var loadedOkay = false;
   var errorTimer = null;
 
-  script.id = 'a' + utils.randomString(8);
+  script.id = 'a' + random.string(8);
   script.src = url;
   script.type = 'text/javascript';
   script.charset = 'UTF-8';
diff --git a/lib/transport/lib/frames.js b/lib/transport/lib/frames.js
new file mode 100644
index 0000000..c0bbd90
--- /dev/null
+++ b/lib/transport/lib/frames.js
@@ -0,0 +1,9 @@
+'use strict';
+
+var JSON3 = require('JSON3');
+
+module.exports = {
+  close: function (code, reason) {
+    return 'c' + JSON3.stringify([code, reason]);
+  }
+};
diff --git a/lib/transport/lib/iframe-utils.js b/lib/transport/lib/iframe-utils.js
new file mode 100644
index 0000000..b724cb3
--- /dev/null
+++ b/lib/transport/lib/iframe-utils.js
@@ -0,0 +1,13 @@
+'use strict';
+
+// currentWindowId comes from SockJS.bootstrap_iframe()
+module.exports = {
+  currentWindowId: null
+, postMessage: function (type, data) {
+    if (global.parent !== global) {
+      global.parent.postMessage(this.currentWindowId + type + (data || ''), '*');
+    } else {
+      console.log('Cannot postMessage, no parent window.', type, data);
+    }
+  }
+};
diff --git a/lib/transport/lib/iframe.js b/lib/transport/lib/iframe.js
index be0ba00..c278f10 100644
--- a/lib/transport/lib/iframe.js
+++ b/lib/transport/lib/iframe.js
@@ -14,6 +14,7 @@ var util = require('util')
   , TransportMessageEvent = require('./trans-message-event')
   , JSON3 = require('json3')
   , utils = require('../../utils')
+  , random = require('../../utils/random')
   ;
 
 function IframeTransport(transport, transUrl, baseUrl) {
@@ -30,7 +31,7 @@ function IframeTransport(transport, transUrl, baseUrl) {
   // if (this.ri._devel) {
   //   iframeUrl += '?t=' + Date.now();
   // }
-  this.windowId = utils.randomString(8);
+  this.windowId = random.string(8);
   iframeUrl += '#' + this.windowId;
 
   this.iframeObj = utils.createIframe(iframeUrl, function(r) {
diff --git a/lib/transport/receiver/htmlfile.js b/lib/transport/receiver/htmlfile.js
index 1727549..e23dc54 100644
--- a/lib/transport/receiver/htmlfile.js
+++ b/lib/transport/receiver/htmlfile.js
@@ -4,6 +4,7 @@ var util = require('util')
   , utils = require('../../utils')
   , SimpleEvent = require('../../simpleevent')
   , EventTarget = require('../../polyfills/eventtarget')
+  , random = require('../../utils/random')
   ;
 
 var _isIeHtmlfileCapable;
@@ -26,7 +27,7 @@ function HtmlfileReceiver(url) {
   var self = this;
   utils.polluteGlobalNamespace();
 
-  this.id = 'a' + utils.randomString(6);
+  this.id = 'a' + random.string(6);
   url += ((url.indexOf('?') === -1) ? '?' : '&') +
       'c=' + decodeURIComponent(utils.WPrefix + '.' + this.id);
 
diff --git a/lib/transport/sender/xhr-cors.js b/lib/transport/sender/xhr-cors.js
index 04d0052..e98d658 100644
--- a/lib/transport/sender/xhr-cors.js
+++ b/lib/transport/sender/xhr-cors.js
@@ -15,4 +15,13 @@ function XHRCorsObject(method, url, payload, opts) {
 
 util.inherits(XHRCorsObject, AbstractXHRObject);
 
+XHRCorsObject.capable = function () {
+  try {
+    if (global.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest()) {
+      return true;
+    }
+  } catch (ignored) {}
+  return false;
+};
+
 module.exports = XHRCorsObject;
diff --git a/lib/transport/xdr-streaming.js b/lib/transport/xdr-streaming.js
index 260a70c..2c934de 100644
--- a/lib/transport/xdr-streaming.js
+++ b/lib/transport/xdr-streaming.js
@@ -5,6 +5,7 @@ var util = require('util')
   , XhrReceiver = require('./receiver/xhr')
   , XDRObject = require('./sender/xdr')
   , utils = require('../utils')
+  , loc = require('../polyfills/location')
   ;
 
 // According to:
@@ -23,7 +24,7 @@ XdrStreamingTransport.enabled = function(url, info) {
   }
   // IE 8/9 if the request target uses the same scheme - #79
   return !!(global.XDomainRequest && global.document &&
-    global.document.domain && utils.isSameOriginScheme(url));
+    global.document.domain && utils.isSameOriginScheme(url, loc.href));
 };
 
 XdrStreamingTransport.transportName = 'xdr-streaming';
diff --git a/lib/transport/xhr-polling.js b/lib/transport/xhr-polling.js
index d4822c5..355b12e 100644
--- a/lib/transport/xhr-polling.js
+++ b/lib/transport/xhr-polling.js
@@ -5,6 +5,7 @@ var util = require('util')
   , XhrReceiver = require('./receiver/xhr')
   , XHRCorsObject = require('./sender/xhr-cors')
   , utils = require('../utils')
+  , loc = require('../polyfills/location')
   ;
 
 function XhrPollingTransport(ri, transUrl) {
@@ -17,10 +18,10 @@ XhrPollingTransport.enabled = function(url, info) {
   if (info.nullOrigin) {
     return false;
   }
-  if (global.XMLHttpRequest && utils.isSameOriginUrl(url)) {
+  if (global.XMLHttpRequest && utils.isSameOriginUrl(url, loc.href)) {
     return true;
   }
-  return utils.isXHRCorsCapable() === 1;
+  return XHRCorsObject.capable();
 };
 
 XhrPollingTransport.transportName = 'xhr-polling';
diff --git a/lib/transport/xhr-streaming.js b/lib/transport/xhr-streaming.js
index c54809e..630b979 100644
--- a/lib/transport/xhr-streaming.js
+++ b/lib/transport/xhr-streaming.js
@@ -5,6 +5,7 @@ var util = require('util')
   , XhrReceiver = require('./receiver/xhr')
   , XHRCorsObject = require('./sender/xhr-cors')
   , utils = require('../utils')
+  , loc = require('../polyfills/location')
   ;
 
 function XhrStreamingTransport(ri, transUrl) {
@@ -21,11 +22,11 @@ XhrStreamingTransport.enabled = function(url, info) {
   if (/opera/i.test(global.navigator.userAgent)) {
     return false;
   }
-  if (global.XMLHttpRequest && utils.isSameOriginUrl(url)) {
+  if (global.XMLHttpRequest && utils.isSameOriginUrl(url, loc.href)) {
     return true;
   }
 
-  return utils.isXHRCorsCapable() === 1;
+  return XHRCorsObject.capable();
 };
 
 XhrStreamingTransport.transportName = 'xhr-streaming';
diff --git a/lib/utils.js b/lib/utils.js
deleted file mode 100644
index 8646a08..0000000
--- a/lib/utils.js
+++ /dev/null
@@ -1,548 +0,0 @@
-'use strict';
-
-var JSON3 = require('json3');
-var IframeTransport = require('./transport/lib/iframe');
-var utils = {};
-
-var getRandomBytes;
-if (global && global.crypto && global.crypto.getRandomValues) {
-  getRandomBytes = function (length) {
-    var bytes = new Uint8Array(length);
-    global.crypto.getRandomValues(bytes);
-    return bytes;
-  };
-} else {
-  getRandomBytes = function (length) {
-    var bytes = new Uint8Array(length);
-    for (var i = 0; i < length; i++) {
-      bytes[i] = Math.floor(Math.random() * 256);
-    }
-    return bytes;
-  };
-}
-
-// This string has length 32, a power of 2, so the modulus doesn't introduce a
-// bias.
-var _randomStringChars = 'abcdefghijklmnopqrstuvwxyz012345';
-utils.randomString = function(length) {
-  var max = _randomStringChars.length;
-  var bytes = getRandomBytes(length);
-  var ret = [];
-  for (var i = 0; i < length; i++) {
-    ret.push( _randomStringChars[bytes[i] % max] );
-  }
-  return ret.join('');
-};
-utils.randomNumber = function(max) {
-  return Math.floor(Math.random() * max);
-};
-utils.randomNumberString = function(max) {
-  var t = ('' + (max - 1)).length;
-  var p = new Array(t + 1).join('0');
-  return (p + utils.randomNumber(max)).slice(-t);
-};
-
-utils.getOrigin = function(url) {
-  if (url.match(/^file:\/\//)) {
-    // no origin when using file protocol
-    return null;
-  }
-
-  var parts = url.split('/');
-  var protocol = parts[0];
-  var host = parts[2];
-  var atSignIndex = host.lastIndexOf('@');
-  var hostname;
-  var port;
-
-  if (~atSignIndex) {
-    host = host.slice(atSignIndex + 1);
-  }
-
-  parts = host.split(':');
-  hostname = parts[0];
-  port = parts[1];
-
-  if (!port) {
-    port = (protocol === 'https:') ? 443 : 80;
-  }
-
-  return protocol + '//' + hostname + ':' + port;
-};
-
-utils.isSameOriginUrl = function(urlA, urlB) {
-  // location.origin would do, but it's not always available.
-  if (!urlB) {
-    urlB = global.location.href;
-  }
-  return utils.getOrigin(urlA) === utils.getOrigin(urlB);
-};
-
-utils.isSameOriginScheme = function(urlA, urlB) {
-  if (!urlB) {
-    urlB = global.location.href;
-  }
-
-  return (urlA.split(':')[0] === urlB.split(':')[0]);
-};
-
-utils.getParentDomain = function(url) {
-  // ipv4 ip address
-  if (/^[0-9.]*$/.test(url)) {
-    return url;
-  }
-  // ipv6 ip address
-  if (/^\[/.test(url)) {
-    return url;
-  }
-  // no dots
-  if (!(/[.]/.test(url))) {
-    return url;
-  }
-
-  var parts = url.split('.').slice(1);
-  return parts.join('.');
-};
-
-utils.objectExtend = function(dst, src) {
-  for (var k in src) {
-    if (src.hasOwnProperty(k)) {
-      dst[k] = src[k];
-    }
-  }
-  return dst;
-};
-
-var WPrefix = utils.WPrefix = '_jp';
-
-utils.polluteGlobalNamespace = function() {
-  if (!(WPrefix in global)) {
-    global[WPrefix] = {};
-  }
-};
-
-utils.closeFrame = function (code, reason) {
-  return 'c' + JSON3.stringify([code, reason]);
-};
-
-utils.userSetCode = function (code) {
-  return code === 1000 || (code >= 3000 && code <= 4999);
-};
-
-// See: http://www.erg.abdn.ac.uk/~gerrit/dccp/notes/ccid2/rto_estimator/
-// and RFC 2988.
-utils.countRTO = function (rtt) {
-  // In a local environment, when using IE8/9 and the `jsonp-polling`
-  // transport the time needed to establish a connection (the time that pass
-  // from the opening of the transport to the call of `_dispatchOpen`) is
-  // around 200msec (the lower bound used in the article above) and this
-  // causes spurious timeouts. For this reason we calculate a value slightly
-  // larger than that used in the article.
-  if (rtt > 100) {
-    return 4 * rtt; // rto > 400msec
-  }
-  return 300 + rtt; // 300msec < rto <= 400msec
-};
-
-utils.log = function() {
-  if (window.console && console.log && console.log.apply) {
-    console.log.apply(console, arguments);
-  }
-};
-
-utils.bind = function(fun, that) {
-  if (fun.bind) {
-    return fun.bind(that);
-  } else {
-    return function() {
-      return fun.apply(that, arguments);
-    };
-  }
-};
-
-utils.flatUrl = function(url) {
-  return url.indexOf('?') === -1 && url.indexOf('#') === -1;
-};
-
-utils.amendUrl = function(url) {
-  var dl = document.location;
-  if (!url) {
-    throw new Error('Wrong url for SockJS');
-  }
-  if (!utils.flatUrl(url)) {
-    throw new Error('Only basic urls are supported in SockJS');
-  }
-
-  //  '//abc' --> 'http://abc'
-  if (url.indexOf('//') === 0) {
-    url = dl.protocol + url;
-  }
-  // '/abc' --> 'http://localhost:1234/abc'
-  if (url.indexOf('/') === 0) {
-    url = dl.protocol + '//' + dl.host + url;
-  }
-  // strip trailing slashes
-  url = url.replace(/[/]+$/,'');
-
-  // We have a full url here, with proto and host. For some browsers
-  // http://localhost:80/ is not in the same origin as http://localhost/
-  // Remove explicit :80 or :443 in such cases. See #74
-  var parts = url.split('/');
-  if ((parts[0] === 'http:' && /:80$/.test(parts[2])) ||
-      (parts[0] === 'https:' && /:443$/.test(parts[2]))) {
-    parts[2] = parts[2].replace(/:(80|443)$/, '');
-  }
-  url = parts.join('/');
-  return url;
-};
-
-// IE doesn't support [].indexOf.
-utils.arrIndexOf = function(arr, obj){
-  for(var i = 0; i < arr.length; i++){
-    if(arr[i] === obj){
-      return i;
-    }
-  }
-  return -1;
-};
-
-utils.arrSkip = function(arr, obj) {
-  var idx = utils.arrIndexOf(arr, obj);
-  if (idx === -1) {
-    return arr.slice();
-  } else {
-    var dst = arr.slice(0, idx);
-    return dst.concat(arr.slice(idx + 1));
-  }
-};
-
-utils.isArray = Array.isArray || function(arg) {
-  return Object.prototype.toString.call(arg) === '[object Array]';
-};
-
-// Chars worth escaping, as defined by Douglas Crockford:
-//   https://github.com/douglascrockford/JSON-js/blob/47a9882cddeb1e8529e07af9736218075372b8ac/json2.js#L196
-var jsonEscapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
-    jsonLookup = {
-'\u0000':'\\u0000','\u0001':'\\u0001','\u0002':'\\u0002','\u0003':'\\u0003',
-'\u0004':'\\u0004','\u0005':'\\u0005','\u0006':'\\u0006','\u0007':'\\u0007',
-'\b':'\\b','\t':'\\t','\n':'\\n','\u000b':'\\u000b','\f':'\\f','\r':'\\r',
-'\u000e':'\\u000e','\u000f':'\\u000f','\u0010':'\\u0010','\u0011':'\\u0011',
-'\u0012':'\\u0012','\u0013':'\\u0013','\u0014':'\\u0014','\u0015':'\\u0015',
-'\u0016':'\\u0016','\u0017':'\\u0017','\u0018':'\\u0018','\u0019':'\\u0019',
-'\u001a':'\\u001a','\u001b':'\\u001b','\u001c':'\\u001c','\u001d':'\\u001d',
-'\u001e':'\\u001e','\u001f':'\\u001f','\'':'\\\'','\\':'\\\\',
-'\u007f':'\\u007f','\u0080':'\\u0080','\u0081':'\\u0081','\u0082':'\\u0082',
-'\u0083':'\\u0083','\u0084':'\\u0084','\u0085':'\\u0085','\u0086':'\\u0086',
-'\u0087':'\\u0087','\u0088':'\\u0088','\u0089':'\\u0089','\u008a':'\\u008a',
-'\u008b':'\\u008b','\u008c':'\\u008c','\u008d':'\\u008d','\u008e':'\\u008e',
-'\u008f':'\\u008f','\u0090':'\\u0090','\u0091':'\\u0091','\u0092':'\\u0092',
-'\u0093':'\\u0093','\u0094':'\\u0094','\u0095':'\\u0095','\u0096':'\\u0096',
-'\u0097':'\\u0097','\u0098':'\\u0098','\u0099':'\\u0099','\u009a':'\\u009a',
-'\u009b':'\\u009b','\u009c':'\\u009c','\u009d':'\\u009d','\u009e':'\\u009e',
-'\u009f':'\\u009f','\u00ad':'\\u00ad','\u0600':'\\u0600','\u0601':'\\u0601',
-'\u0602':'\\u0602','\u0603':'\\u0603','\u0604':'\\u0604','\u070f':'\\u070f',
-'\u17b4':'\\u17b4','\u17b5':'\\u17b5','\u200c':'\\u200c','\u200d':'\\u200d',
-'\u200e':'\\u200e','\u200f':'\\u200f','\u2028':'\\u2028','\u2029':'\\u2029',
-'\u202a':'\\u202a','\u202b':'\\u202b','\u202c':'\\u202c','\u202d':'\\u202d',
-'\u202e':'\\u202e','\u202f':'\\u202f','\u2060':'\\u2060','\u2061':'\\u2061',
-'\u2062':'\\u2062','\u2063':'\\u2063','\u2064':'\\u2064','\u2065':'\\u2065',
-'\u2066':'\\u2066','\u2067':'\\u2067','\u2068':'\\u2068','\u2069':'\\u2069',
-'\u206a':'\\u206a','\u206b':'\\u206b','\u206c':'\\u206c','\u206d':'\\u206d',
-'\u206e':'\\u206e','\u206f':'\\u206f','\ufeff':'\\ufeff','\ufff0':'\\ufff0',
-'\ufff1':'\\ufff1','\ufff2':'\\ufff2','\ufff3':'\\ufff3','\ufff4':'\\ufff4',
-'\ufff5':'\\ufff5','\ufff6':'\\ufff6','\ufff7':'\\ufff7','\ufff8':'\\ufff8',
-'\ufff9':'\\ufff9','\ufffa':'\\ufffa','\ufffb':'\\ufffb','\ufffc':'\\ufffc',
-'\ufffd':'\\ufffd','\ufffe':'\\ufffe','\uffff':'\\uffff'};
-
-// Some extra characters that Chrome gets wrong, and substitutes with
-// something else on the wire.
-var extraEscapable = /[\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\u0f [...]
-    extraLookup;
-
-// JSON Quote string. Use native implementation when possible.
-var quoteJSON = (JSON3 && JSON3.stringify) || function(string) {
-  jsonEscapable.lastIndex = 0;
-  if (jsonEscapable.test(string)) {
-    string = string.replace(jsonEscapable, function(a) {
-      return jsonLookup[a];
-    });
-  }
-  return '"' + string + '"';
-};
-
-// This may be quite slow, so let's delay until user actually uses bad
-// characters.
-var unrollLookup = function(escapable) {
-  var i;
-  var unrolled = {};
-  var c = [];
-  for(i = 0; i < 65536; i++) {
-    c.push( String.fromCharCode(i) );
-  }
-  escapable.lastIndex = 0;
-  c.join('').replace(escapable, function (a) {
-    unrolled[ a ] = '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
-    return '';
-  });
-  escapable.lastIndex = 0;
-  return unrolled;
-};
-
-// Quote string, also taking care of unicode characters that browsers
-// often break. Especially, take care of unicode surrogates:
-//    http://en.wikipedia.org/wiki/Mapping_of_Unicode_characters#Surrogates
-utils.quote = function(string) {
-  var quoted = quoteJSON(string);
-
-  // In most cases this should be very fast and good enough.
-  extraEscapable.lastIndex = 0;
-  if(!extraEscapable.test(quoted)) {
-    return quoted;
-  }
-
-  if (!extraLookup) {
-    extraLookup = unrollLookup(extraEscapable);
-  }
-
-  return quoted.replace(extraEscapable, function(a) {
-    return extraLookup[a];
-  });
-};
-
-// 1. Is natively via XHR
-// 2. Is natively via XDR
-// 3. Nope, but postMessage is there so it should work via the Iframe.
-// 4. Nope, sorry.
-utils.isXHRCorsCapable = function() {
-  try {
-    if (global.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest()) {
-      return 1;
-    }
-  } catch (ignored) {}
-  // XDomainRequest doesn't work if page is served from file://
-  if (global.XDomainRequest && global.document && global.document.domain) {
-    return 2;
-  }
-  if (IframeTransport.enabled()) {
-    return 3;
-  }
-  return 4;
-};
-
-// May be used by htmlfile jsonp and transports.
-var MPrefix = '_sockjs_global';
-utils.createHook = function() {
-  var windowId = 'a' + utils.randomString(8);
-  if (!(MPrefix in window)) {
-    var map = {};
-    window[MPrefix] = function(windowId) {
-      if (!(windowId in map)) {
-        map[windowId] = {
-          id: windowId,
-          del: function() {delete map[windowId];}
-        };
-      }
-      return map[windowId];
-    };
-  }
-  return window[MPrefix](windowId);
-};
-
-utils.attachMessage = function(listener) {
-  utils.attachEvent('message', listener);
-};
-utils.attachEvent = function(event, listener) {
-  if (typeof global.addEventListener !== 'undefined') {
-    global.addEventListener(event, listener, false);
-  } else {
-    // IE quirks.
-    // According to: http://stevesouders.com/misc/test-postmessage.php
-    // the message gets delivered only to 'document', not 'window'.
-    global.document.attachEvent('on' + event, listener);
-    // I get 'window' for ie8.
-    global.attachEvent('on' + event, listener);
-  }
-};
-
-utils.detachMessage = function(listener) {
-  utils.detachEvent('message', listener);
-};
-utils.detachEvent = function(event, listener) {
-  if (typeof global.addEventListener !== 'undefined') {
-    global.removeEventListener(event, listener, false);
-  } else {
-    document.detachEvent('on' + event, listener);
-    global.detachEvent('on' + event, listener);
-  }
-};
-
-
-var onUnload = {};
-// Things registered after beforeunload are to be called immediately.
-var afterUnload = false;
-
-var triggerUnloadCallbacks = function() {
-  for(var ref in onUnload) {
-    onUnload[ref]();
-    delete onUnload[ref];
-  }
-};
-
-var unloadTriggered = function() {
-  if (afterUnload) {
-    return;
-  }
-  afterUnload = true;
-  triggerUnloadCallbacks();
-};
-
-// 'unload' alone is not reliable in opera within an iframe, but we
-// can't use `beforeunload` as IE fires it on javascript: links.
-
-// TODO see if we need to uncomment this
-//utils.attachEvent('unload', unloadTriggered);
-
-utils.unloadAdd = function(listener) {
-  var ref = utils.randomString(8);
-  onUnload[ref] = listener;
-  if (afterUnload) {
-    process.nextTick(triggerUnloadCallbacks);
-  }
-  return ref;
-};
-utils.unloadDel = function(ref) {
-  if (ref in onUnload) {
-    delete onUnload[ref];
-  }
-};
-
-
-utils.createIframe = function (iframeUrl, errorCallback) {
-  var iframe = document.createElement('iframe');
-  var tref, unloadRef;
-  var unattach = function() {
-    clearTimeout(tref);
-    // Explorer had problems with that.
-    try {iframe.onload = null;} catch (x) {}
-    iframe.onerror = null;
-  };
-  var cleanup = function() {
-    if (iframe) {
-      unattach();
-      // This timeout makes chrome fire onbeforeunload event
-      // within iframe. Without the timeout it goes straight to
-      // onunload.
-      setTimeout(function() {
-        if(iframe) {
-            iframe.parentNode.removeChild(iframe);
-        }
-        iframe = null;
-      }, 0);
-      utils.unloadDel(unloadRef);
-    }
-  };
-  var onerror = function(r) {
-    if (iframe) {
-      cleanup();
-      errorCallback(r);
-    }
-  };
-  var post = function(msg, origin) {
-    try {
-      // When the iframe is not loaded, IE raises an exception
-      // on 'contentWindow'.
-      if (iframe && iframe.contentWindow) {
-        setTimeout(function() {
-          iframe.contentWindow.postMessage(msg, origin);
-        }, 0);
-      }
-    } catch (x) {}
-  };
-
-  iframe.src = iframeUrl;
-  iframe.style.display = 'none';
-  iframe.style.position = 'absolute';
-  iframe.onerror = function(){onerror('onerror');};
-  iframe.onload = function() {
-    // `onload` is triggered before scripts on the iframe are
-    // executed. Give it few seconds to actually load stuff.
-    clearTimeout(tref);
-    tref = setTimeout(function(){onerror('onload timeout');}, 2000);
-  };
-  document.body.appendChild(iframe);
-  tref = setTimeout(function(){onerror('timeout');}, 15000);
-  unloadRef = utils.unloadAdd(cleanup);
-  return {
-    post: post,
-    cleanup: cleanup,
-    loaded: unattach
-  };
-};
-
-/* jshint undef: false, newcap: false */
-/* eslint no-undef: [0], new-cap: [0] */
-utils.createHtmlfile = function (iframeUrl, errorCallback) {
-    var doc = new ActiveXObject('htmlfile');
-    var tref, unloadRef;
-    var iframe;
-    var unattach = function() {
-        clearTimeout(tref);
-    };
-    var cleanup = function() {
-      if (doc) {
-        unattach();
-        utils.unloadDel(unloadRef);
-        iframe.parentNode.removeChild(iframe);
-        iframe = doc = null;
-        CollectGarbage();
-      }
-    };
-    var onerror = function(r)  {
-      if (doc) {
-        cleanup();
-        errorCallback(r);
-      }
-    };
-    var post = function(msg, origin) {
-      try {
-        // When the iframe is not loaded, IE raises an exception
-        // on 'contentWindow'.
-        if (iframe && iframe.contentWindow) {
-          setTimeout(function() {
-            iframe.contentWindow.postMessage(msg, origin);
-          }, 0);
-        }
-      } catch (x) {}
-    };
-
-    doc.open();
-    doc.write('<html><s' + 'cript>' +
-              'document.domain="' + document.domain + '";' +
-              '</s' + 'cript></html>');
-    doc.close();
-    doc.parentWindow[utils.WPrefix] = window[utils.WPrefix];
-    var c = doc.createElement('div');
-    doc.body.appendChild(c);
-    iframe = doc.createElement('iframe');
-    c.appendChild(iframe);
-    iframe.src = iframeUrl;
-    tref = setTimeout(function(){onerror('timeout');}, 15000);
-    unloadRef = utils.unloadAdd(cleanup);
-    return {
-      post: post,
-      cleanup: cleanup,
-      loaded: unattach
-    };
-};
-
-// curr_window_id comes from SockJS.bootstrap_iframe()
-utils.postMessage = function (type, data) {
-  if (parent !== window) {
-    parent.postMessage(utils.currentWindowId + type + (data || ''), '*');
-  } else {
-    utils.log("Can't postMessage, no parent window.", type, data);
-  }
-};
-
-module.exports = utils;
diff --git a/lib/utils/escape.js b/lib/utils/escape.js
new file mode 100644
index 0000000..1ecc8b6
--- /dev/null
+++ b/lib/utils/escape.js
@@ -0,0 +1,49 @@
+'use strict';
+
+var JSON3 = require('JSON3');
+
+// Some extra characters that Chrome gets wrong, and substitutes with
+// something else on the wire.
+var extraEscapable = /[\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\u0f [...]
+    extraLookup;
+
+// This may be quite slow, so let's delay until user actually uses bad
+// characters.
+var unrollLookup = function(escapable) {
+  var i;
+  var unrolled = {};
+  var c = [];
+  for(i = 0; i < 65536; i++) {
+    c.push( String.fromCharCode(i) );
+  }
+  escapable.lastIndex = 0;
+  c.join('').replace(escapable, function (a) {
+    unrolled[ a ] = '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+    return '';
+  });
+  escapable.lastIndex = 0;
+  return unrolled;
+};
+
+// Quote string, also taking care of unicode characters that browsers
+// often break. Especially, take care of unicode surrogates:
+//    http://en.wikipedia.org/wiki/Mapping_of_Unicode_characters#Surrogates
+module.exports = {
+  quote: function(string) {
+    var quoted = JSON3.stringify(string);
+
+    // In most cases this should be very fast and good enough.
+    extraEscapable.lastIndex = 0;
+    if(!extraEscapable.test(quoted)) {
+      return quoted;
+    }
+
+    if (!extraLookup) {
+      extraLookup = unrollLookup(extraEscapable);
+    }
+
+    return quoted.replace(extraEscapable, function(a) {
+      return extraLookup[a];
+    });
+  }
+};
diff --git a/lib/utils/event.js b/lib/utils/event.js
new file mode 100644
index 0000000..cf2cec9
--- /dev/null
+++ b/lib/utils/event.js
@@ -0,0 +1,73 @@
+'use strict';
+
+module.exports = {
+  attachMessage: function(listener) {
+    this.attachEvent('message', listener);
+  }
+
+, attachEvent: function(event, listener) {
+    if (typeof global.addEventListener !== 'undefined') {
+      global.addEventListener(event, listener, false);
+    } else {
+      // IE quirks.
+      // According to: http://stevesouders.com/misc/test-postmessage.php
+      // the message gets delivered only to 'document', not 'window'.
+      global.document.attachEvent('on' + event, listener);
+      // I get 'window' for ie8.
+      global.attachEvent('on' + event, listener);
+    }
+  }
+
+, detachMessage: function(listener) {
+    this.detachEvent('message', listener);
+  }
+
+, detachEvent: function(event, listener) {
+    if (typeof global.addEventListener !== 'undefined') {
+      global.removeEventListener(event, listener, false);
+    } else {
+      global.document.detachEvent('on' + event, listener);
+      global.detachEvent('on' + event, listener);
+    }
+  }
+};
+
+
+var onUnload = {};
+// Things registered after beforeunload are to be called immediately.
+var afterUnload = false;
+
+var triggerUnloadCallbacks = function() {
+  for(var ref in onUnload) {
+    onUnload[ref]();
+    delete onUnload[ref];
+  }
+};
+
+var unloadTriggered = function() {
+  if (afterUnload) {
+    return;
+  }
+  afterUnload = true;
+  triggerUnloadCallbacks();
+};
+
+// 'unload' alone is not reliable in opera within an iframe, but we
+// can't use `beforeunload` as IE fires it on javascript: links.
+
+// TODO see if we need to uncomment this
+//utils.attachEvent('unload', unloadTriggered);
+
+utils.unloadAdd = function(listener) {
+  var ref = utils.randomString(8);
+  onUnload[ref] = listener;
+  if (afterUnload) {
+    process.nextTick(triggerUnloadCallbacks);
+  }
+  return ref;
+};
+utils.unloadDel = function(ref) {
+  if (ref in onUnload) {
+    delete onUnload[ref];
+  }
+};
diff --git a/lib/utils/iframe.js b/lib/utils/iframe.js
new file mode 100644
index 0000000..d5fad06
--- /dev/null
+++ b/lib/utils/iframe.js
@@ -0,0 +1,127 @@
+'use strict';
+
+var WPrefix = utils.WPrefix = '_jp';
+
+utils.polluteGlobalNamespace = function() {
+  if (!(WPrefix in global)) {
+    global[WPrefix] = {};
+  }
+};
+
+utils.createIframe = function (iframeUrl, errorCallback) {
+  var iframe = document.createElement('iframe');
+  var tref, unloadRef;
+  var unattach = function() {
+    clearTimeout(tref);
+    // Explorer had problems with that.
+    try {iframe.onload = null;} catch (x) {}
+    iframe.onerror = null;
+  };
+  var cleanup = function() {
+    if (iframe) {
+      unattach();
+      // This timeout makes chrome fire onbeforeunload event
+      // within iframe. Without the timeout it goes straight to
+      // onunload.
+      setTimeout(function() {
+        if(iframe) {
+            iframe.parentNode.removeChild(iframe);
+        }
+        iframe = null;
+      }, 0);
+      utils.unloadDel(unloadRef);
+    }
+  };
+  var onerror = function(r) {
+    if (iframe) {
+      cleanup();
+      errorCallback(r);
+    }
+  };
+  var post = function(msg, origin) {
+    try {
+      // When the iframe is not loaded, IE raises an exception
+      // on 'contentWindow'.
+      if (iframe && iframe.contentWindow) {
+        setTimeout(function() {
+          iframe.contentWindow.postMessage(msg, origin);
+        }, 0);
+      }
+    } catch (x) {}
+  };
+
+  iframe.src = iframeUrl;
+  iframe.style.display = 'none';
+  iframe.style.position = 'absolute';
+  iframe.onerror = function(){onerror('onerror');};
+  iframe.onload = function() {
+    // `onload` is triggered before scripts on the iframe are
+    // executed. Give it few seconds to actually load stuff.
+    clearTimeout(tref);
+    tref = setTimeout(function(){onerror('onload timeout');}, 2000);
+  };
+  document.body.appendChild(iframe);
+  tref = setTimeout(function(){onerror('timeout');}, 15000);
+  unloadRef = utils.unloadAdd(cleanup);
+  return {
+    post: post,
+    cleanup: cleanup,
+    loaded: unattach
+  };
+};
+
+/* jshint undef: false, newcap: false */
+/* eslint no-undef: [0], new-cap: [0] */
+utils.createHtmlfile = function (iframeUrl, errorCallback) {
+    var doc = new ActiveXObject('htmlfile');
+    var tref, unloadRef;
+    var iframe;
+    var unattach = function() {
+        clearTimeout(tref);
+    };
+    var cleanup = function() {
+      if (doc) {
+        unattach();
+        utils.unloadDel(unloadRef);
+        iframe.parentNode.removeChild(iframe);
+        iframe = doc = null;
+        CollectGarbage();
+      }
+    };
+    var onerror = function(r)  {
+      if (doc) {
+        cleanup();
+        errorCallback(r);
+      }
+    };
+    var post = function(msg, origin) {
+      try {
+        // When the iframe is not loaded, IE raises an exception
+        // on 'contentWindow'.
+        if (iframe && iframe.contentWindow) {
+          setTimeout(function() {
+            iframe.contentWindow.postMessage(msg, origin);
+          }, 0);
+        }
+      } catch (x) {}
+    };
+
+    doc.open();
+    doc.write('<html><s' + 'cript>' +
+              'document.domain="' + document.domain + '";' +
+              '</s' + 'cript></html>');
+    doc.close();
+    doc.parentWindow[utils.WPrefix] = window[utils.WPrefix];
+    var c = doc.createElement('div');
+    doc.body.appendChild(c);
+    iframe = doc.createElement('iframe');
+    c.appendChild(iframe);
+    iframe.src = iframeUrl;
+    tref = setTimeout(function(){onerror('timeout');}, 15000);
+    unloadRef = utils.unloadAdd(cleanup);
+    return {
+      post: post,
+      cleanup: cleanup,
+      loaded: unattach
+    };
+};
diff --git a/lib/utils/log.js b/lib/utils/log.js
new file mode 100644
index 0000000..c4fc960
--- /dev/null
+++ b/lib/utils/log.js
@@ -0,0 +1,7 @@
+'use strict';
+
+utils.log = function() {
+  if (global.console && console.log && console.log.apply) {
+    console.log.apply(console, arguments);
+  }
+};
diff --git a/lib/utils/origin.js b/lib/utils/origin.js
new file mode 100644
index 0000000..6aa216a
--- /dev/null
+++ b/lib/utils/origin.js
@@ -0,0 +1,28 @@
+'use strict';
+
+var u = require('url');
+
+module.exports = {
+  getOrigin: function (url) {
+    var p = u.parse(url);
+    if (p.protocol === 'file:') {
+      return null;
+    }
+
+    var port = p.port;
+    if (!port) {
+      port = (p.protocol === 'https:') ? '443' : '80';
+    }
+
+    return p.protocol + '//' + p.hostname + ':' + port;
+  }
+
+, isSameOriginUrl: function(urlA, urlB) {
+    // location.origin would do, but it's not always available.
+    return this.getOrigin(urlA) === this.getOrigin(urlB);
+  }
+
+, isSameOriginScheme: function(urlA, urlB) {
+    return (urlA.split(':')[0] === urlB.split(':')[0]);
+  }
+};
diff --git a/lib/utils/random.js b/lib/utils/random.js
new file mode 100644
index 0000000..0e66e09
--- /dev/null
+++ b/lib/utils/random.js
@@ -0,0 +1,43 @@
+'use strict';
+
+var getRandomBytes;
+if (global && global.crypto && global.crypto.getRandomValues) {
+  getRandomBytes = function (length) {
+    var bytes = new Uint8Array(length);
+    global.crypto.getRandomValues(bytes);
+    return bytes;
+  };
+} else {
+  getRandomBytes = function (length) {
+    var bytes = new Uint8Array(length);
+    for (var i = 0; i < length; i++) {
+      bytes[i] = Math.floor(Math.random() * 256);
+    }
+    return bytes;
+  };
+}
+
+// This string has length 32, a power of 2, so the modulus doesn't introduce a
+// bias.
+var _randomStringChars = 'abcdefghijklmnopqrstuvwxyz012345';
+module.exports = {
+  string: function(length) {
+    var max = _randomStringChars.length;
+    var bytes = getRandomBytes(length);
+    var ret = [];
+    for (var i = 0; i < length; i++) {
+      ret.push( _randomStringChars[bytes[i] % max] );
+    }
+    return ret.join('');
+  }
+
+, number: function(max) {
+    return Math.floor(Math.random() * max);
+  }
+
+, numberString: function(max) {
+    var t = ('' + (max - 1)).length;
+    var p = new Array(t + 1).join('0');
+    return (p + this.number(max)).slice(-t);
+  }
+};
diff --git a/tests/html/lib/testutils.js b/tests/html/lib/testutils.js
index aebc051..a13bb2a 100644
--- a/tests/html/lib/testutils.js
+++ b/tests/html/lib/testutils.js
@@ -1,27 +1,48 @@
 'use strict';
 /* global jQuery, client_opts, SockJS */
 
-var u = require('../../../lib/utils');
+var random = require('../../../lib/utils/random');
+
+// May be used by htmlfile jsonp and transports.
+var MPrefix = '_sockjs_global';
+var createHook = function() {
+  var windowId = 'a' + random.string(8);
+  if (!(MPrefix in window)) {
+    var map = {};
+    window[MPrefix] = function(windowId) {
+      if (!(windowId in map)) {
+        map[windowId] = {
+          id: windowId,
+          del: function() {delete map[windowId];}
+        };
+      }
+      return map[windowId];
+    };
+  }
+  return window[MPrefix](windowId);
+};
 
 module.exports = {
 
-newIframe: function(path) {
-  var err, hook;
-  if (!path) path = '/iframe.html';
-  hook = u.createHook();
-  err = function() {
-    return u.log('iframe error. bad.');
-  };
-  hook.iobj = u.createIframe(path + '?a=' + Math.random() + '#' + hook.id, err);
-  return hook;
-},
+  newIframe: function(path) {
+    var err, hook;
+    if (!path) {
+      path = '/iframe.html';
+    }
+    hook = createHook();
+    err = function() {
+      return u.log('iframe error. bad.');
+    };
+    hook.iobj = u.createIframe(path + '?a=' + Math.random() + '#' + hook.id, err);
+    return hook;
+  },
 
-newSockJS: function(path, protocol) {
-  var options, url;
-  url = /^http/.test(path) ? path : client_opts.url + path;
-  options = jQuery.extend({}, client_opts.sockjs_opts);
-  if (protocol) options.protocols_whitelist = [protocol];
-  return new SockJS(url, null, options);
-}
+  newSockJS: function(path, protocol) {
+    var options, url;
+    url = /^http/.test(path) ? path : client_opts.url + path;
+    options = jQuery.extend({}, client_opts.sockjs_opts);
+    if (protocol) options.protocols_whitelist = [protocol];
+    return new SockJS(url, null, options);
+  }
 
 };
\ No newline at end of file

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/sockjs-client.git



More information about the Pkg-javascript-commits mailing list