--- /dev/null
+"use strict";
+var window = require("global/window")
+var isFunction = require("is-function")
+var parseHeaders = require("parse-headers")
+var xtend = require("xtend")
+
+module.exports = createXHR
+createXHR.XMLHttpRequest = window.XMLHttpRequest || noop
+createXHR.XDomainRequest = "withCredentials" in (new createXHR.XMLHttpRequest()) ? createXHR.XMLHttpRequest : window.XDomainRequest
+
+forEachArray(["get", "put", "post", "patch", "head", "delete"], function(method) {
+ createXHR[method === "delete" ? "del" : method] = function(uri, options, callback) {
+ options = initParams(uri, options, callback)
+ options.method = method.toUpperCase()
+ return _createXHR(options)
+ }
+})
+
+function forEachArray(array, iterator) {
+ for (var i = 0; i < array.length; i++) {
+ iterator(array[i])
+ }
+}
+
+function isEmpty(obj){
+ for(var i in obj){
+ if(obj.hasOwnProperty(i)) return false
+ }
+ return true
+}
+
+function initParams(uri, options, callback) {
+ var params = uri
+
+ if (isFunction(options)) {
+ callback = options
+ if (typeof uri === "string") {
+ params = {uri:uri}
+ }
+ } else {
+ params = xtend(options, {uri: uri})
+ }
+
+ params.callback = callback
+ return params
+}
+
+function createXHR(uri, options, callback) {
+ options = initParams(uri, options, callback)
+ return _createXHR(options)
+}
+
+function _createXHR(options) {
+ if(typeof options.callback === "undefined"){
+ throw new Error("callback argument missing")
+ }
+
+ var called = false
+ var callback = function cbOnce(err, response, body){
+ if(!called){
+ called = true
+ options.callback(err, response, body)
+ }
+ }
+
+ function readystatechange() {
+ if (xhr.readyState === 4) {
+ loadFunc()
+ }
+ }
+
+ function getBody() {
+ // Chrome with requestType=blob throws errors arround when even testing access to responseText
+ var body = undefined
+
+ if (xhr.response) {
+ body = xhr.response
+ } else {
+ body = xhr.responseText || getXml(xhr)
+ }
+
+ if (isJson) {
+ try {
+ body = JSON.parse(body)
+ } catch (e) {}
+ }
+
+ return body
+ }
+
+ var failureResponse = {
+ body: undefined,
+ headers: {},
+ statusCode: 0,
+ method: method,
+ url: uri,
+ rawRequest: xhr
+ }
+
+ function errorFunc(evt) {
+ clearTimeout(timeoutTimer)
+ if(!(evt instanceof Error)){
+ evt = new Error("" + (evt || "Unknown XMLHttpRequest Error") )
+ }
+ evt.statusCode = 0
+ return callback(evt, failureResponse)
+ }
+
+ // will load the data & process the response in a special response object
+ function loadFunc() {
+ if (aborted) return
+ var status
+ clearTimeout(timeoutTimer)
+ if(options.useXDR && xhr.status===undefined) {
+ //IE8 CORS GET successful response doesn't have a status field, but body is fine
+ status = 200
+ } else {
+ status = (xhr.status === 1223 ? 204 : xhr.status)
+ }
+ var response = failureResponse
+ var err = null
+
+ if (status !== 0){
+ response = {
+ body: getBody(),
+ statusCode: status,
+ method: method,
+ headers: {},
+ url: uri,
+ rawRequest: xhr
+ }
+ if(xhr.getAllResponseHeaders){ //remember xhr can in fact be XDR for CORS in IE
+ response.headers = parseHeaders(xhr.getAllResponseHeaders())
+ }
+ } else {
+ err = new Error("Internal XMLHttpRequest Error")
+ }
+ return callback(err, response, response.body)
+ }
+
+ var xhr = options.xhr || null
+
+ if (!xhr) {
+ if (options.cors || options.useXDR) {
+ xhr = new createXHR.XDomainRequest()
+ }else{
+ xhr = new createXHR.XMLHttpRequest()
+ }
+ }
+
+ var key
+ var aborted
+ var uri = xhr.url = options.uri || options.url
+ var method = xhr.method = options.method || "GET"
+ var body = options.body || options.data || null
+ var headers = xhr.headers = options.headers || {}
+ var sync = !!options.sync
+ var isJson = false
+ var timeoutTimer
+
+ if ("json" in options) {
+ isJson = true
+ headers["accept"] || headers["Accept"] || (headers["Accept"] = "application/json") //Don't override existing accept header declared by user
+ if (method !== "GET" && method !== "HEAD") {
+ headers["content-type"] || headers["Content-Type"] || (headers["Content-Type"] = "application/json") //Don't override existing accept header declared by user
+ body = JSON.stringify(options.json)
+ }
+ }
+
+ xhr.onreadystatechange = readystatechange
+ xhr.onload = loadFunc
+ xhr.onerror = errorFunc
+ // IE9 must have onprogress be set to a unique function.
+ xhr.onprogress = function () {
+ // IE must die
+ }
+ xhr.ontimeout = errorFunc
+ xhr.open(method, uri, !sync, options.username, options.password)
+ //has to be after open
+ if(!sync) {
+ xhr.withCredentials = !!options.withCredentials
+ }
+ // Cannot set timeout with sync request
+ // not setting timeout on the xhr object, because of old webkits etc. not handling that correctly
+ // both npm's request and jquery 1.x use this kind of timeout, so this is being consistent
+ if (!sync && options.timeout > 0 ) {
+ timeoutTimer = setTimeout(function(){
+ aborted=true//IE9 may still call readystatechange
+ xhr.abort("timeout")
+ var e = new Error("XMLHttpRequest timeout")
+ e.code = "ETIMEDOUT"
+ errorFunc(e)
+ }, options.timeout )
+ }
+
+ if (xhr.setRequestHeader) {
+ for(key in headers){
+ if(headers.hasOwnProperty(key)){
+ xhr.setRequestHeader(key, headers[key])
+ }
+ }
+ } else if (options.headers && !isEmpty(options.headers)) {
+ throw new Error("Headers cannot be set on an XDomainRequest object")
+ }
+
+ if ("responseType" in options) {
+ xhr.responseType = options.responseType
+ }
+
+ if ("beforeSend" in options &&
+ typeof options.beforeSend === "function"
+ ) {
+ options.beforeSend(xhr)
+ }
+
+ xhr.send(body)
+
+ return xhr
+
+
+}
+
+function getXml(xhr) {
+ if (xhr.responseType === "document") {
+ return xhr.responseXML
+ }
+ var firefoxBugTakenEffect = xhr.status === 204 && xhr.responseXML && xhr.responseXML.documentElement.nodeName === "parsererror"
+ if (xhr.responseType === "" && !firefoxBugTakenEffect) {
+ return xhr.responseXML
+ }
+
+ return null
+}
+
+function noop() {}