blob: 6bc5d38593b0e08a1f9274edbb138e40b962fb26 [file] [log] [blame]
/* global fetch */
/* global Headers */
import {blob as createBlob, readAsArrayBuffer } from 'pouchdb-binary-utils';
function wrappedFetch() {
var wrappedPromise = {};
var promise = new Promise(function (resolve, reject) {
wrappedPromise.resolve = resolve;
wrappedPromise.reject = reject;
});
var args = new Array(arguments.length);
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i];
}
wrappedPromise.promise = promise;
Promise.resolve().then(function () {
return fetch.apply(null, args);
}).then(function (response) {
wrappedPromise.resolve(response);
}).catch(function (error) {
wrappedPromise.reject(error);
});
return wrappedPromise;
}
function fetchRequest(options, callback) {
var wrappedPromise, timer, response;
var headers = new Headers();
var fetchOptions = {
method: options.method,
credentials: 'include',
headers: headers
};
if (options.json) {
headers.set('Accept', 'application/json');
headers.set('Content-Type', options.headers['Content-Type'] ||
'application/json');
}
if (options.body &&
options.processData &&
typeof options.body !== 'string') {
fetchOptions.body = JSON.stringify(options.body);
} else if ('body' in options) {
fetchOptions.body = options.body;
} else {
fetchOptions.body = null;
}
Object.keys(options.headers).forEach(function (key) {
if (options.headers.hasOwnProperty(key)) {
headers.set(key, options.headers[key]);
}
});
wrappedPromise = wrappedFetch(options.url, fetchOptions);
if (options.timeout > 0) {
timer = setTimeout(function () {
wrappedPromise.reject(new Error('Load timeout for resource: ' +
options.url));
}, options.timeout);
}
wrappedPromise.promise.then(function (fetchResponse) {
response = {
statusCode: fetchResponse.status
};
if (options.timeout > 0) {
clearTimeout(timer);
}
if (response.statusCode >= 200 && response.statusCode < 300) {
return options.binary ? fetchResponse.blob() : fetchResponse.text();
}
return fetchResponse.json();
}).then(function (result) {
if (response.statusCode >= 200 && response.statusCode < 300) {
callback(null, response, result);
} else {
result.status = response.statusCode;
callback(result);
}
}).catch(function (error) {
if (!error) {
// this happens when the listener is canceled
error = new Error('canceled');
}
callback(error);
});
return {abort: wrappedPromise.reject};
}
function xhRequest(options, callback) {
var xhr, timer;
var timedout = false;
var abortReq = function () {
xhr.abort();
cleanUp();
};
var timeoutReq = function () {
timedout = true;
xhr.abort();
cleanUp();
};
var ret = {abort: abortReq};
var cleanUp = function () {
clearTimeout(timer);
ret.abort = function () {};
if (xhr) {
xhr.onprogress = undefined;
if (xhr.upload) {
xhr.upload.onprogress = undefined;
}
xhr.onreadystatechange = undefined;
xhr = undefined;
}
};
if (options.xhr) {
xhr = new options.xhr();
} else {
xhr = new XMLHttpRequest();
}
try {
xhr.open(options.method, options.url);
} catch (exception) {
return callback(new Error(exception.name || 'Url is invalid'));
}
xhr.withCredentials = ('withCredentials' in options) ?
options.withCredentials : true;
if (options.method === 'GET') {
delete options.headers['Content-Type'];
} else if (options.json) {
options.headers.Accept = 'application/json';
options.headers['Content-Type'] = options.headers['Content-Type'] ||
'application/json';
if (options.body &&
options.processData &&
typeof options.body !== "string") {
options.body = JSON.stringify(options.body);
}
}
if (options.binary) {
xhr.responseType = 'arraybuffer';
}
if (!('body' in options)) {
options.body = null;
}
for (var key in options.headers) {
if (options.headers.hasOwnProperty(key)) {
xhr.setRequestHeader(key, options.headers[key]);
}
}
if (options.timeout > 0) {
timer = setTimeout(timeoutReq, options.timeout);
xhr.onprogress = function () {
clearTimeout(timer);
if(xhr.readyState !== 4) {
timer = setTimeout(timeoutReq, options.timeout);
}
};
if (typeof xhr.upload !== 'undefined') { // does not exist in ie9
xhr.upload.onprogress = xhr.onprogress;
}
}
xhr.onreadystatechange = function () {
if (xhr.readyState !== 4) {
return;
}
var response = {
statusCode: xhr.status
};
if (xhr.status >= 200 && xhr.status < 300) {
var data;
if (options.binary) {
data = createBlob([xhr.response || ''], {
type: xhr.getResponseHeader('Content-Type')
});
} else {
data = xhr.responseText;
}
callback(null, response, data);
} else {
var err = {};
if (timedout) {
err = new Error('ETIMEDOUT');
err.code = 'ETIMEDOUT';
} else if (typeof xhr.response === 'string') {
try {
err = JSON.parse(xhr.response);
} catch(e) {}
}
err.status = xhr.status;
callback(err);
}
cleanUp();
};
if (options.body && (options.body instanceof Blob)) {
readAsArrayBuffer(options.body, function (arrayBuffer) {
xhr.send(arrayBuffer);
});
} else {
xhr.send(options.body);
}
return ret;
}
function testXhr() {
try {
new XMLHttpRequest();
return true;
} catch (err) {
return false;
}
}
var hasXhr = testXhr();
function ajax(options, callback) {
if (!process.env.FETCH && (hasXhr || options.xhr)) {
return xhRequest(options, callback);
} else {
return fetchRequest(options, callback);
}
}
export default ajax;