(function (XHR) {
  'use strict';

  var XHR = XMLHttpRequest;

  var phonyXHR = function FakeXMLHttpRequest () {
    var self = this;

    self.xhr = new XHR();

    self.getAllResponseHeaders = self.xhr.getAllResponseHeaders.bind(self.xhr);
    self.setRequestHeader = self.xhr.setRequestHeader.bind(self.xhr)

    ['onload', 'onerror', 'onprogress', 'onabort'].forEach(function (event) {
      Object.defineProperty(self, event, {
        set: function (func) {
          self.xhr[event] = func;
          self[event + 'Func'] = func; /* Using a different prop to avoid infinite loop */
        },
        get: function () {
          return self.xhr[event];
        }
      });
    });
  };


  window.XMLHttpRequest = phonyXHR;

  phonyXHR.prototype.apiPrefetchOptions = XHR.prototype.apiPrefetchOptions || {
    variable: 'apiPrefetchData'
  };

  phonyXHR.prototype.open = function (method, url, async) {
    var self = this;
    self.apiPrefetchExecuting = method.toUpperCase() === 'GET' &&
      async !== false &&
      typeof window[this.apiPrefetchOptions.variable] !== 'undefined';

    /* If we're not prefetching, open the xhr request like normal */
    if (!self.apiPrefetchExecuting) {
      return self.originalSelf.open.apply(self.xhr, arguments);
    }
  };

  phonyXHR.prototype.send = function () {
    var self = this;

    if (!self.apiPrefetchExecuting) {
      self.xhr.onload = self.onload;
      return self.xhr.send.apply(self.xhr, arguments);
    }

    self.apiPrefetchExecuting = false;
    self.apiPrefetchExecuted = true;

    /* Don't call onload right away, it might be set after send is called */
    setTimeout(function () {
      assignResponse(self);
      if (typeof self.onload == 'function') self.onload();
    }, 0);
  };

  function assignResponse(xhr) {
    var apiPrefetchData = window[xhr.apiPrefetchOptions.variable];

    xhr.status = 200;
    xhr.statusText = '200 OK';

    if (typeof apiPrefetchData === 'string') {
      xhr.responseText = apiPrefetchData;
      if (xhr.responseType === 'json') {
        xhr.response = JSON.parse(apiPrefetchData);
      } else {
        xhr.response = xhr.responseText;
      }
    } else {
      xhr.responseText = JSON.stringify(apiPrefetchData);
      if (xhr.responseType === 'json') {
        xhr.response = apiPrefetchData;
      } else {
        xhr.response = xhr.responseText;
      }
    }
  }

})(XMLHttpRequest);

@@ -236,16 +236,88 @@
 f.xhr);%0A
+    self.getResponseHeader = self.xhr.getResponseHeader.bind(self.xhr);%0A
     self
@@ -380,59 +380,214 @@
 xhr)
+;
 %0A%0A    
-%5B'onload', 'onerror', 'onprogress', 'onabort'
+/* Add getter+setter passthroughs */%0A    %5B%0A      'onload', 'onerror', 'onprogress', 'onabort', 'ontimeout',%0A      'onreadystatechange', 'upload', 'withCredentials',%0A      'timeout', 'readyState'%0A    
 %5D.fo
@@ -602,21 +602,20 @@
 nction (
-event
+prop
 ) %7B%0A    
@@ -644,21 +644,20 @@
 y(self, 
-event
+prop
 , %7B%0A    
@@ -675,20 +675,19 @@
 nction (
-func
+val
 ) %7B%0A    
@@ -705,113 +705,20 @@
 xhr%5B
-event%5D = func;%0A          self%5Bevent + 'Func'%5D = func; /* Using a different prop to avoid infinite loop */
+prop%5D = val;
 %0A   
@@ -782,13 +782,12 @@
 xhr%5B
-event
+prop
 %5D;%0A 
@@ -1227,141 +1227,23 @@
     
-/* If we're not prefetching, open the xhr request like normal */%0A    if (!self.apiPrefetchExecuting) %7B%0A      return self.originalSelf
+return self.xhr
 .ope
@@ -1272,22 +1272,16 @@
 ments);%0A
-    %7D%0A
   %7D;%0A%0A  
@@ -1384,45 +1384,8 @@
 ) %7B%0A
-      self.xhr.onload = self.onload;%0A
     

Add more passthrough properties