You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
359 lines
12 KiB
359 lines
12 KiB
import { root } from '../../util/root'; |
|
import { Observable } from '../../Observable'; |
|
import { Subscriber } from '../../Subscriber'; |
|
import { map } from '../../operators/map'; |
|
function getCORSRequest() { |
|
if (root.XMLHttpRequest) { |
|
return new root.XMLHttpRequest(); |
|
} |
|
else if (!!root.XDomainRequest) { |
|
return new root.XDomainRequest(); |
|
} |
|
else { |
|
throw new Error('CORS is not supported by your browser'); |
|
} |
|
} |
|
function getXMLHttpRequest() { |
|
if (root.XMLHttpRequest) { |
|
return new root.XMLHttpRequest(); |
|
} |
|
else { |
|
let progId; |
|
try { |
|
const progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0']; |
|
for (let i = 0; i < 3; i++) { |
|
try { |
|
progId = progIds[i]; |
|
if (new root.ActiveXObject(progId)) { |
|
break; |
|
} |
|
} |
|
catch (e) { |
|
} |
|
} |
|
return new root.ActiveXObject(progId); |
|
} |
|
catch (e) { |
|
throw new Error('XMLHttpRequest is not supported by your browser'); |
|
} |
|
} |
|
} |
|
export function ajaxGet(url, headers = null) { |
|
return new AjaxObservable({ method: 'GET', url, headers }); |
|
} |
|
export function ajaxPost(url, body, headers) { |
|
return new AjaxObservable({ method: 'POST', url, body, headers }); |
|
} |
|
export function ajaxDelete(url, headers) { |
|
return new AjaxObservable({ method: 'DELETE', url, headers }); |
|
} |
|
export function ajaxPut(url, body, headers) { |
|
return new AjaxObservable({ method: 'PUT', url, body, headers }); |
|
} |
|
export function ajaxPatch(url, body, headers) { |
|
return new AjaxObservable({ method: 'PATCH', url, body, headers }); |
|
} |
|
const mapResponse = map((x, index) => x.response); |
|
export function ajaxGetJSON(url, headers) { |
|
return mapResponse(new AjaxObservable({ |
|
method: 'GET', |
|
url, |
|
responseType: 'json', |
|
headers |
|
})); |
|
} |
|
export class AjaxObservable extends Observable { |
|
constructor(urlOrRequest) { |
|
super(); |
|
const request = { |
|
async: true, |
|
createXHR: function () { |
|
return this.crossDomain ? getCORSRequest() : getXMLHttpRequest(); |
|
}, |
|
crossDomain: true, |
|
withCredentials: false, |
|
headers: {}, |
|
method: 'GET', |
|
responseType: 'json', |
|
timeout: 0 |
|
}; |
|
if (typeof urlOrRequest === 'string') { |
|
request.url = urlOrRequest; |
|
} |
|
else { |
|
for (const prop in urlOrRequest) { |
|
if (urlOrRequest.hasOwnProperty(prop)) { |
|
request[prop] = urlOrRequest[prop]; |
|
} |
|
} |
|
} |
|
this.request = request; |
|
} |
|
_subscribe(subscriber) { |
|
return new AjaxSubscriber(subscriber, this.request); |
|
} |
|
} |
|
AjaxObservable.create = (() => { |
|
const create = (urlOrRequest) => { |
|
return new AjaxObservable(urlOrRequest); |
|
}; |
|
create.get = ajaxGet; |
|
create.post = ajaxPost; |
|
create.delete = ajaxDelete; |
|
create.put = ajaxPut; |
|
create.patch = ajaxPatch; |
|
create.getJSON = ajaxGetJSON; |
|
return create; |
|
})(); |
|
export class AjaxSubscriber extends Subscriber { |
|
constructor(destination, request) { |
|
super(destination); |
|
this.request = request; |
|
this.done = false; |
|
const headers = request.headers = request.headers || {}; |
|
if (!request.crossDomain && !this.getHeader(headers, 'X-Requested-With')) { |
|
headers['X-Requested-With'] = 'XMLHttpRequest'; |
|
} |
|
let contentTypeHeader = this.getHeader(headers, 'Content-Type'); |
|
if (!contentTypeHeader && !(root.FormData && request.body instanceof root.FormData) && typeof request.body !== 'undefined') { |
|
headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'; |
|
} |
|
request.body = this.serializeBody(request.body, this.getHeader(request.headers, 'Content-Type')); |
|
this.send(); |
|
} |
|
next(e) { |
|
this.done = true; |
|
const { xhr, request, destination } = this; |
|
let result; |
|
try { |
|
result = new AjaxResponse(e, xhr, request); |
|
} |
|
catch (err) { |
|
return destination.error(err); |
|
} |
|
destination.next(result); |
|
} |
|
send() { |
|
const { request, request: { user, method, url, async, password, headers, body } } = this; |
|
try { |
|
const xhr = this.xhr = request.createXHR(); |
|
this.setupEvents(xhr, request); |
|
if (user) { |
|
xhr.open(method, url, async, user, password); |
|
} |
|
else { |
|
xhr.open(method, url, async); |
|
} |
|
if (async) { |
|
xhr.timeout = request.timeout; |
|
xhr.responseType = request.responseType; |
|
} |
|
if ('withCredentials' in xhr) { |
|
xhr.withCredentials = !!request.withCredentials; |
|
} |
|
this.setHeaders(xhr, headers); |
|
if (body) { |
|
xhr.send(body); |
|
} |
|
else { |
|
xhr.send(); |
|
} |
|
} |
|
catch (err) { |
|
this.error(err); |
|
} |
|
} |
|
serializeBody(body, contentType) { |
|
if (!body || typeof body === 'string') { |
|
return body; |
|
} |
|
else if (root.FormData && body instanceof root.FormData) { |
|
return body; |
|
} |
|
if (contentType) { |
|
const splitIndex = contentType.indexOf(';'); |
|
if (splitIndex !== -1) { |
|
contentType = contentType.substring(0, splitIndex); |
|
} |
|
} |
|
switch (contentType) { |
|
case 'application/x-www-form-urlencoded': |
|
return Object.keys(body).map(key => `${encodeURIComponent(key)}=${encodeURIComponent(body[key])}`).join('&'); |
|
case 'application/json': |
|
return JSON.stringify(body); |
|
default: |
|
return body; |
|
} |
|
} |
|
setHeaders(xhr, headers) { |
|
for (let key in headers) { |
|
if (headers.hasOwnProperty(key)) { |
|
xhr.setRequestHeader(key, headers[key]); |
|
} |
|
} |
|
} |
|
getHeader(headers, headerName) { |
|
for (let key in headers) { |
|
if (key.toLowerCase() === headerName.toLowerCase()) { |
|
return headers[key]; |
|
} |
|
} |
|
return undefined; |
|
} |
|
setupEvents(xhr, request) { |
|
const progressSubscriber = request.progressSubscriber; |
|
function xhrTimeout(e) { |
|
const { subscriber, progressSubscriber, request } = xhrTimeout; |
|
if (progressSubscriber) { |
|
progressSubscriber.error(e); |
|
} |
|
let error; |
|
try { |
|
error = new AjaxTimeoutError(this, request); |
|
} |
|
catch (err) { |
|
error = err; |
|
} |
|
subscriber.error(error); |
|
} |
|
xhr.ontimeout = xhrTimeout; |
|
xhrTimeout.request = request; |
|
xhrTimeout.subscriber = this; |
|
xhrTimeout.progressSubscriber = progressSubscriber; |
|
if (xhr.upload && 'withCredentials' in xhr) { |
|
if (progressSubscriber) { |
|
let xhrProgress; |
|
xhrProgress = function (e) { |
|
const { progressSubscriber } = xhrProgress; |
|
progressSubscriber.next(e); |
|
}; |
|
if (root.XDomainRequest) { |
|
xhr.onprogress = xhrProgress; |
|
} |
|
else { |
|
xhr.upload.onprogress = xhrProgress; |
|
} |
|
xhrProgress.progressSubscriber = progressSubscriber; |
|
} |
|
let xhrError; |
|
xhrError = function (e) { |
|
const { progressSubscriber, subscriber, request } = xhrError; |
|
if (progressSubscriber) { |
|
progressSubscriber.error(e); |
|
} |
|
let error; |
|
try { |
|
error = new AjaxError('ajax error', this, request); |
|
} |
|
catch (err) { |
|
error = err; |
|
} |
|
subscriber.error(error); |
|
}; |
|
xhr.onerror = xhrError; |
|
xhrError.request = request; |
|
xhrError.subscriber = this; |
|
xhrError.progressSubscriber = progressSubscriber; |
|
} |
|
function xhrReadyStateChange(e) { |
|
return; |
|
} |
|
xhr.onreadystatechange = xhrReadyStateChange; |
|
xhrReadyStateChange.subscriber = this; |
|
xhrReadyStateChange.progressSubscriber = progressSubscriber; |
|
xhrReadyStateChange.request = request; |
|
function xhrLoad(e) { |
|
const { subscriber, progressSubscriber, request } = xhrLoad; |
|
if (this.readyState === 4) { |
|
let status = this.status === 1223 ? 204 : this.status; |
|
let response = (this.responseType === 'text' ? (this.response || this.responseText) : this.response); |
|
if (status === 0) { |
|
status = response ? 200 : 0; |
|
} |
|
if (status < 400) { |
|
if (progressSubscriber) { |
|
progressSubscriber.complete(); |
|
} |
|
subscriber.next(e); |
|
subscriber.complete(); |
|
} |
|
else { |
|
if (progressSubscriber) { |
|
progressSubscriber.error(e); |
|
} |
|
let error; |
|
try { |
|
error = new AjaxError('ajax error ' + status, this, request); |
|
} |
|
catch (err) { |
|
error = err; |
|
} |
|
subscriber.error(error); |
|
} |
|
} |
|
} |
|
xhr.onload = xhrLoad; |
|
xhrLoad.subscriber = this; |
|
xhrLoad.progressSubscriber = progressSubscriber; |
|
xhrLoad.request = request; |
|
} |
|
unsubscribe() { |
|
const { done, xhr } = this; |
|
if (!done && xhr && xhr.readyState !== 4 && typeof xhr.abort === 'function') { |
|
xhr.abort(); |
|
} |
|
super.unsubscribe(); |
|
} |
|
} |
|
export class AjaxResponse { |
|
constructor(originalEvent, xhr, request) { |
|
this.originalEvent = originalEvent; |
|
this.xhr = xhr; |
|
this.request = request; |
|
this.status = xhr.status; |
|
this.responseType = xhr.responseType || request.responseType; |
|
this.response = parseXhrResponse(this.responseType, xhr); |
|
} |
|
} |
|
const AjaxErrorImpl = (() => { |
|
function AjaxErrorImpl(message, xhr, request) { |
|
Error.call(this); |
|
this.message = message; |
|
this.name = 'AjaxError'; |
|
this.xhr = xhr; |
|
this.request = request; |
|
this.status = xhr.status; |
|
this.responseType = xhr.responseType || request.responseType; |
|
this.response = parseXhrResponse(this.responseType, xhr); |
|
return this; |
|
} |
|
AjaxErrorImpl.prototype = Object.create(Error.prototype); |
|
return AjaxErrorImpl; |
|
})(); |
|
export const AjaxError = AjaxErrorImpl; |
|
function parseJson(xhr) { |
|
if ('response' in xhr) { |
|
return xhr.responseType ? xhr.response : JSON.parse(xhr.response || xhr.responseText || 'null'); |
|
} |
|
else { |
|
return JSON.parse(xhr.responseText || 'null'); |
|
} |
|
} |
|
function parseXhrResponse(responseType, xhr) { |
|
switch (responseType) { |
|
case 'json': |
|
return parseJson(xhr); |
|
case 'xml': |
|
return xhr.responseXML; |
|
case 'text': |
|
default: |
|
return ('response' in xhr) ? xhr.response : xhr.responseText; |
|
} |
|
} |
|
function AjaxTimeoutErrorImpl(xhr, request) { |
|
AjaxError.call(this, 'ajax timeout', xhr, request); |
|
this.name = 'AjaxTimeoutError'; |
|
return this; |
|
} |
|
export const AjaxTimeoutError = AjaxTimeoutErrorImpl; |
|
//# sourceMappingURL=AjaxObservable.js.map
|