freeCodeCamp/api-server/common/utils/ajax-stream.js

315 lines
8.3 KiB
JavaScript
Raw Normal View History

2015-07-23 06:10:57 +00:00
/*
* Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
* Microsoft Open Technologies would like to thank its contributors, a list
* of whom are at http://rx.codeplex.com/wikipage?title=Contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing permissions
* and limitations under the License.
*/
2015-07-23 20:40:26 +00:00
import debugFactory from 'debug';
2015-12-30 01:35:50 +00:00
import { Observable, AnonymousObservable, helpers } from 'rx';
2015-07-23 06:10:57 +00:00
2016-01-27 19:34:44 +00:00
const debug = debugFactory('fcc:ajax$');
2015-07-23 06:10:57 +00:00
const root = typeof window !== 'undefined' ? window : {};
// Gets the proper XMLHttpRequest for support for older IE
function getXMLHttpRequest() {
if (root.XMLHttpRequest) {
return new root.XMLHttpRequest();
} else {
var progId;
try {
var progIds = [
'Msxml2.XMLHTTP',
'Microsoft.XMLHTTP',
'Msxml2.XMLHTTP.4.0'
];
for (var i = 0; i < 3; i++) {
try {
progId = progIds[i];
if (new root.ActiveXObject(progId)) {
break;
}
} catch (e) {
// purposely do nothing
helpers.noop(e);
}
}
return new root.ActiveXObject(progId);
} catch (e) {
throw new Error('XMLHttpRequest is not supported by your browser');
}
}
}
// Get CORS support even for older IE
function getCORSRequest() {
var xhr = new root.XMLHttpRequest();
if ('withCredentials' in xhr) {
return xhr;
} else if (root.XDomainRequest) {
return new XDomainRequest();
} else {
throw new Error('CORS is not supported by your browser');
}
}
function parseXhrResponse(responseType, xhr) {
switch (responseType) {
case 'json':
if ('response' in xhr) {
return xhr.responseType ?
xhr.response :
JSON.parse(xhr.response || xhr.responseText || 'null');
} else {
return JSON.parse(xhr.responseText || 'null');
}
case 'xml':
return xhr.responseXML;
case 'text':
default:
return ('response' in xhr) ? xhr.response : xhr.responseText;
}
}
2015-07-23 06:10:57 +00:00
function normalizeAjaxSuccessEvent(e, xhr, settings) {
return {
response: parseXhrResponse(settings.responseType || xhr.responseType, xhr),
2015-07-23 06:10:57 +00:00
status: xhr.status,
responseType: xhr.responseType,
xhr: xhr,
originalEvent: e
};
}
function normalizeAjaxErrorEvent(e, xhr, type) {
return {
type: type,
status: xhr.status,
xhr: xhr,
originalEvent: e
};
}
/*
* Creates an observable for an Ajax request with either a settings object
* with url, headers, etc or a string for a URL.
*
* @example
* source = Rx.DOM.ajax('/products');
* source = Rx.DOM.ajax( url: 'products', method: 'GET' });
*
2016-05-17 17:25:20 +00:00
* interface Options {
* url: String, // URL of the request
* body?: Object, // The body of the request
* method? = 'GET' : 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',
* async? = true: Boolean, // Whether the request is async
* headers?: Object, // optional headers
* crossDomain?: true // if a cross domain request, else false
* }
2015-07-23 06:10:57 +00:00
*
2016-05-17 17:25:20 +00:00
* ajax$(url?: String, options: Options) => Observable[XMLHttpRequest]
*/
2015-07-23 06:10:57 +00:00
export function ajax$(options) {
var settings = {
method: 'GET',
crossDomain: false,
async: true,
headers: {},
responseType: 'text',
createXHR: function() {
return this.crossDomain ? getCORSRequest() : getXMLHttpRequest();
},
normalizeError: normalizeAjaxErrorEvent,
normalizeSuccess: normalizeAjaxSuccessEvent
};
if (typeof options === 'string') {
settings.url = options;
} else {
for (var prop in options) {
if (hasOwnProperty.call(options, prop)) {
settings[prop] = options[prop];
}
}
}
var normalizeError = settings.normalizeError;
var normalizeSuccess = settings.normalizeSuccess;
if (!settings.crossDomain && !settings.headers['X-Requested-With']) {
settings.headers['X-Requested-With'] = 'XMLHttpRequest';
}
settings.hasContent = typeof settings.body !== 'undefined';
return new AnonymousObservable(function(observer) {
var isDone = false;
var xhr;
var processResponse = function(xhr, e) {
var status = xhr.status === 1223 ? 204 : xhr.status;
if ((status >= 200 && status <= 300) || status === 0 || status === '') {
2015-12-30 01:35:50 +00:00
try {
observer.onNext(normalizeSuccess(e, xhr, settings));
observer.onCompleted();
} catch (err) {
observer.onError(err);
}
2015-07-23 06:10:57 +00:00
} else {
observer.onError(normalizeError(e, xhr, 'error'));
}
isDone = true;
};
try {
xhr = settings.createXHR();
} catch (err) {
observer.onError(err);
}
try {
if (settings.user) {
xhr.open(
settings.method,
settings.url,
settings.async,
settings.user,
settings.password
);
} else {
xhr.open(settings.method, settings.url, settings.async);
}
var headers = settings.headers;
for (var header in headers) {
if (hasOwnProperty.call(headers, header)) {
xhr.setRequestHeader(header, headers[header]);
}
}
if (
!xhr.upload ||
(!('withCredentials' in xhr) && root.XDomainRequest)
) {
xhr.onload = function(e) {
if (settings.progressObserver) {
settings.progressObserver.onNext(e);
settings.progressObserver.onCompleted();
}
processResponse(xhr, e);
};
if (settings.progressObserver) {
xhr.onprogress = function(e) {
settings.progressObserver.onNext(e);
};
}
xhr.onerror = function(e) {
if (settings.progressObserver) {
settings.progressObserver.onError(e);
}
observer.onError(normalizeError(e, xhr, 'error'));
isDone = true;
};
xhr.onabort = function(e) {
if (settings.progressObserver) {
settings.progressObserver.onError(e);
}
observer.onError(normalizeError(e, xhr, 'abort'));
isDone = true;
};
} else {
xhr.onreadystatechange = function(e) {
if (xhr.readyState === 4) {
processResponse(xhr, e);
}
};
}
2015-07-23 20:40:26 +00:00
debug(
'ajax$ sending content',
settings.hasContent && settings.body
);
2015-07-23 06:10:57 +00:00
xhr.send(settings.hasContent && settings.body || null);
2015-12-30 01:35:50 +00:00
} catch (err) {
observer.onError(err);
2015-07-23 06:10:57 +00:00
}
return function() {
if (!isDone && xhr.readyState !== 4) { xhr.abort(); }
};
});
}
2016-05-17 17:25:20 +00:00
// Creates an observable sequence from an Ajax POST Request with the body.
// post$(url: String, body: Object) => Observable[Any]
2015-07-23 06:10:57 +00:00
export function post$(url, body) {
2015-12-30 01:35:50 +00:00
try {
body = JSON.stringify(body);
} catch (e) {
return Observable.throw(e);
}
2015-07-23 20:40:26 +00:00
return ajax$({ url, body, method: 'POST' });
}
2016-05-17 17:25:20 +00:00
// postJSON$(url: String, body: Object) => Observable[Object]
2015-07-23 20:40:26 +00:00
export function postJSON$(url, body) {
2015-12-30 01:35:50 +00:00
try {
body = JSON.stringify(body);
} catch (e) {
return Observable.throw(e);
}
2015-07-23 20:40:26 +00:00
return ajax$({
url,
2015-12-30 01:35:50 +00:00
body,
2015-07-23 20:40:26 +00:00
method: 'POST',
2015-10-27 00:39:38 +00:00
responseType: 'json',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json'
},
normalizeError: (e, xhr) => parseXhrResponse('json', xhr)
2016-01-10 07:05:53 +00:00
})
.map(({ response }) => response);
2015-07-23 06:10:57 +00:00
}
2016-05-17 17:25:20 +00:00
// Creates an observable sequence from an Ajax GET Request with the body.
// get$(url: String) => Obserable[Any]
2015-07-23 06:10:57 +00:00
export function get$(url) {
return ajax$({ url: url });
}
/**
* Creates an observable sequence from JSON from an Ajax request
*
* @param {String} url The URL to GET
* @returns {Observable} The observable sequence which contains the parsed JSON
*/
2016-05-17 17:25:20 +00:00
// getJSON$(url: String) => Observable[Object];
2015-07-23 06:10:57 +00:00
export function getJSON$(url) {
return ajax$({
url: url,
responseType: 'json',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json'
},
normalizeError: (e, xhr) => parseXhrResponse('json', xhr)
2016-01-10 07:05:53 +00:00
}).map(({ response }) => response);
2015-07-23 06:10:57 +00:00
}