AjaxObservable.js 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. /** PURE_IMPORTS_START .._.._util_root,.._.._util_tryCatch,.._.._util_errorObject,.._.._Observable,.._.._Subscriber,.._.._operators_map PURE_IMPORTS_END */
  2. var __extends = (this && this.__extends) || function (d, b) {
  3. for (var p in b)
  4. if (b.hasOwnProperty(p))
  5. d[p] = b[p];
  6. function __() { this.constructor = d; }
  7. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  8. };
  9. import { root } from '../../util/root';
  10. import { tryCatch } from '../../util/tryCatch';
  11. import { errorObject } from '../../util/errorObject';
  12. import { Observable } from '../../Observable';
  13. import { Subscriber } from '../../Subscriber';
  14. import { map } from '../../operators/map';
  15. function getCORSRequest() {
  16. if (root.XMLHttpRequest) {
  17. return new root.XMLHttpRequest();
  18. }
  19. else if (!!root.XDomainRequest) {
  20. return new root.XDomainRequest();
  21. }
  22. else {
  23. throw new Error('CORS is not supported by your browser');
  24. }
  25. }
  26. function getXMLHttpRequest() {
  27. if (root.XMLHttpRequest) {
  28. return new root.XMLHttpRequest();
  29. }
  30. else {
  31. var progId = void 0;
  32. try {
  33. var progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];
  34. for (var i = 0; i < 3; i++) {
  35. try {
  36. progId = progIds[i];
  37. if (new root.ActiveXObject(progId)) {
  38. break;
  39. }
  40. }
  41. catch (e) {
  42. }
  43. }
  44. return new root.ActiveXObject(progId);
  45. }
  46. catch (e) {
  47. throw new Error('XMLHttpRequest is not supported by your browser');
  48. }
  49. }
  50. }
  51. export function ajaxGet(url, headers) {
  52. if (headers === void 0) {
  53. headers = null;
  54. }
  55. return new AjaxObservable({ method: 'GET', url: url, headers: headers });
  56. }
  57. ;
  58. export function ajaxPost(url, body, headers) {
  59. return new AjaxObservable({ method: 'POST', url: url, body: body, headers: headers });
  60. }
  61. ;
  62. export function ajaxDelete(url, headers) {
  63. return new AjaxObservable({ method: 'DELETE', url: url, headers: headers });
  64. }
  65. ;
  66. export function ajaxPut(url, body, headers) {
  67. return new AjaxObservable({ method: 'PUT', url: url, body: body, headers: headers });
  68. }
  69. ;
  70. export function ajaxPatch(url, body, headers) {
  71. return new AjaxObservable({ method: 'PATCH', url: url, body: body, headers: headers });
  72. }
  73. ;
  74. var mapResponse = /*@__PURE__*/ map(function (x, index) { return x.response; });
  75. export function ajaxGetJSON(url, headers) {
  76. return mapResponse(new AjaxObservable({
  77. method: 'GET',
  78. url: url,
  79. responseType: 'json',
  80. headers: headers
  81. }));
  82. }
  83. ;
  84. /**
  85. * We need this JSDoc comment for affecting ESDoc.
  86. * @extends {Ignored}
  87. * @hide true
  88. */
  89. export var AjaxObservable = /*@__PURE__*/ (/*@__PURE__*/ function (_super) {
  90. __extends(AjaxObservable, _super);
  91. function AjaxObservable(urlOrRequest) {
  92. _super.call(this);
  93. var request = {
  94. async: true,
  95. createXHR: function () {
  96. return this.crossDomain ? getCORSRequest.call(this) : getXMLHttpRequest();
  97. },
  98. crossDomain: false,
  99. withCredentials: false,
  100. headers: {},
  101. method: 'GET',
  102. responseType: 'json',
  103. timeout: 0
  104. };
  105. if (typeof urlOrRequest === 'string') {
  106. request.url = urlOrRequest;
  107. }
  108. else {
  109. for (var prop in urlOrRequest) {
  110. if (urlOrRequest.hasOwnProperty(prop)) {
  111. request[prop] = urlOrRequest[prop];
  112. }
  113. }
  114. }
  115. this.request = request;
  116. }
  117. /** @deprecated internal use only */ AjaxObservable.prototype._subscribe = function (subscriber) {
  118. return new AjaxSubscriber(subscriber, this.request);
  119. };
  120. /**
  121. * Creates an observable for an Ajax request with either a request object with
  122. * url, headers, etc or a string for a URL.
  123. *
  124. * @example
  125. * source = Rx.Observable.ajax('/products');
  126. * source = Rx.Observable.ajax({ url: 'products', method: 'GET' });
  127. *
  128. * @param {string|Object} request Can be one of the following:
  129. * A string of the URL to make the Ajax call.
  130. * An object with the following properties
  131. * - url: URL of the request
  132. * - body: The body of the request
  133. * - method: Method of the request, such as GET, POST, PUT, PATCH, DELETE
  134. * - async: Whether the request is async
  135. * - headers: Optional headers
  136. * - crossDomain: true if a cross domain request, else false
  137. * - createXHR: a function to override if you need to use an alternate
  138. * XMLHttpRequest implementation.
  139. * - resultSelector: a function to use to alter the output value type of
  140. * the Observable. Gets {@link AjaxResponse} as an argument.
  141. * @return {Observable} An observable sequence containing the XMLHttpRequest.
  142. * @static true
  143. * @name ajax
  144. * @owner Observable
  145. */
  146. AjaxObservable.create = (function () {
  147. var create = function (urlOrRequest) {
  148. return new AjaxObservable(urlOrRequest);
  149. };
  150. create.get = ajaxGet;
  151. create.post = ajaxPost;
  152. create.delete = ajaxDelete;
  153. create.put = ajaxPut;
  154. create.patch = ajaxPatch;
  155. create.getJSON = ajaxGetJSON;
  156. return create;
  157. })();
  158. return AjaxObservable;
  159. }(Observable));
  160. /**
  161. * We need this JSDoc comment for affecting ESDoc.
  162. * @ignore
  163. * @extends {Ignored}
  164. */
  165. export var AjaxSubscriber = /*@__PURE__*/ (/*@__PURE__*/ function (_super) {
  166. __extends(AjaxSubscriber, _super);
  167. function AjaxSubscriber(destination, request) {
  168. _super.call(this, destination);
  169. this.request = request;
  170. this.done = false;
  171. var headers = request.headers = request.headers || {};
  172. // force CORS if requested
  173. if (!request.crossDomain && !headers['X-Requested-With']) {
  174. headers['X-Requested-With'] = 'XMLHttpRequest';
  175. }
  176. // ensure content type is set
  177. if (!('Content-Type' in headers) && !(root.FormData && request.body instanceof root.FormData) && typeof request.body !== 'undefined') {
  178. headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
  179. }
  180. // properly serialize body
  181. request.body = this.serializeBody(request.body, request.headers['Content-Type']);
  182. this.send();
  183. }
  184. AjaxSubscriber.prototype.next = function (e) {
  185. this.done = true;
  186. var _a = this, xhr = _a.xhr, request = _a.request, destination = _a.destination;
  187. var response = new AjaxResponse(e, xhr, request);
  188. destination.next(response);
  189. };
  190. AjaxSubscriber.prototype.send = function () {
  191. var _a = this, request = _a.request, _b = _a.request, user = _b.user, method = _b.method, url = _b.url, async = _b.async, password = _b.password, headers = _b.headers, body = _b.body;
  192. var createXHR = request.createXHR;
  193. var xhr = tryCatch(createXHR).call(request);
  194. if (xhr === errorObject) {
  195. this.error(errorObject.e);
  196. }
  197. else {
  198. this.xhr = xhr;
  199. // set up the events before open XHR
  200. // https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
  201. // You need to add the event listeners before calling open() on the request.
  202. // Otherwise the progress events will not fire.
  203. this.setupEvents(xhr, request);
  204. // open XHR
  205. var result = void 0;
  206. if (user) {
  207. result = tryCatch(xhr.open).call(xhr, method, url, async, user, password);
  208. }
  209. else {
  210. result = tryCatch(xhr.open).call(xhr, method, url, async);
  211. }
  212. if (result === errorObject) {
  213. this.error(errorObject.e);
  214. return null;
  215. }
  216. // timeout, responseType and withCredentials can be set once the XHR is open
  217. if (async) {
  218. xhr.timeout = request.timeout;
  219. xhr.responseType = request.responseType;
  220. }
  221. if ('withCredentials' in xhr) {
  222. xhr.withCredentials = !!request.withCredentials;
  223. }
  224. // set headers
  225. this.setHeaders(xhr, headers);
  226. // finally send the request
  227. result = body ? tryCatch(xhr.send).call(xhr, body) : tryCatch(xhr.send).call(xhr);
  228. if (result === errorObject) {
  229. this.error(errorObject.e);
  230. return null;
  231. }
  232. }
  233. return xhr;
  234. };
  235. AjaxSubscriber.prototype.serializeBody = function (body, contentType) {
  236. if (!body || typeof body === 'string') {
  237. return body;
  238. }
  239. else if (root.FormData && body instanceof root.FormData) {
  240. return body;
  241. }
  242. if (contentType) {
  243. var splitIndex = contentType.indexOf(';');
  244. if (splitIndex !== -1) {
  245. contentType = contentType.substring(0, splitIndex);
  246. }
  247. }
  248. switch (contentType) {
  249. case 'application/x-www-form-urlencoded':
  250. return Object.keys(body).map(function (key) { return (encodeURI(key) + "=" + encodeURI(body[key])); }).join('&');
  251. case 'application/json':
  252. return JSON.stringify(body);
  253. default:
  254. return body;
  255. }
  256. };
  257. AjaxSubscriber.prototype.setHeaders = function (xhr, headers) {
  258. for (var key in headers) {
  259. if (headers.hasOwnProperty(key)) {
  260. xhr.setRequestHeader(key, headers[key]);
  261. }
  262. }
  263. };
  264. AjaxSubscriber.prototype.setupEvents = function (xhr, request) {
  265. var progressSubscriber = request.progressSubscriber;
  266. function xhrTimeout(e) {
  267. var _a = xhrTimeout, subscriber = _a.subscriber, progressSubscriber = _a.progressSubscriber, request = _a.request;
  268. if (progressSubscriber) {
  269. progressSubscriber.error(e);
  270. }
  271. subscriber.error(new AjaxTimeoutError(this, request)); //TODO: Make betterer.
  272. }
  273. ;
  274. xhr.ontimeout = xhrTimeout;
  275. xhrTimeout.request = request;
  276. xhrTimeout.subscriber = this;
  277. xhrTimeout.progressSubscriber = progressSubscriber;
  278. if (xhr.upload && 'withCredentials' in xhr) {
  279. if (progressSubscriber) {
  280. var xhrProgress_1;
  281. xhrProgress_1 = function (e) {
  282. var progressSubscriber = xhrProgress_1.progressSubscriber;
  283. progressSubscriber.next(e);
  284. };
  285. if (root.XDomainRequest) {
  286. xhr.onprogress = xhrProgress_1;
  287. }
  288. else {
  289. xhr.upload.onprogress = xhrProgress_1;
  290. }
  291. xhrProgress_1.progressSubscriber = progressSubscriber;
  292. }
  293. var xhrError_1;
  294. xhrError_1 = function (e) {
  295. var _a = xhrError_1, progressSubscriber = _a.progressSubscriber, subscriber = _a.subscriber, request = _a.request;
  296. if (progressSubscriber) {
  297. progressSubscriber.error(e);
  298. }
  299. subscriber.error(new AjaxError('ajax error', this, request));
  300. };
  301. xhr.onerror = xhrError_1;
  302. xhrError_1.request = request;
  303. xhrError_1.subscriber = this;
  304. xhrError_1.progressSubscriber = progressSubscriber;
  305. }
  306. function xhrReadyStateChange(e) {
  307. var _a = xhrReadyStateChange, subscriber = _a.subscriber, progressSubscriber = _a.progressSubscriber, request = _a.request;
  308. if (this.readyState === 4) {
  309. // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
  310. var status_1 = this.status === 1223 ? 204 : this.status;
  311. var response = (this.responseType === 'text' ? (this.response || this.responseText) : this.response);
  312. // fix status code when it is 0 (0 status is undocumented).
  313. // Occurs when accessing file resources or on Android 4.1 stock browser
  314. // while retrieving files from application cache.
  315. if (status_1 === 0) {
  316. status_1 = response ? 200 : 0;
  317. }
  318. if (200 <= status_1 && status_1 < 300) {
  319. if (progressSubscriber) {
  320. progressSubscriber.complete();
  321. }
  322. subscriber.next(e);
  323. subscriber.complete();
  324. }
  325. else {
  326. if (progressSubscriber) {
  327. progressSubscriber.error(e);
  328. }
  329. subscriber.error(new AjaxError('ajax error ' + status_1, this, request));
  330. }
  331. }
  332. }
  333. ;
  334. xhr.onreadystatechange = xhrReadyStateChange;
  335. xhrReadyStateChange.subscriber = this;
  336. xhrReadyStateChange.progressSubscriber = progressSubscriber;
  337. xhrReadyStateChange.request = request;
  338. };
  339. AjaxSubscriber.prototype.unsubscribe = function () {
  340. var _a = this, done = _a.done, xhr = _a.xhr;
  341. if (!done && xhr && xhr.readyState !== 4 && typeof xhr.abort === 'function') {
  342. xhr.abort();
  343. }
  344. _super.prototype.unsubscribe.call(this);
  345. };
  346. return AjaxSubscriber;
  347. }(Subscriber));
  348. /**
  349. * A normalized AJAX response.
  350. *
  351. * @see {@link ajax}
  352. *
  353. * @class AjaxResponse
  354. */
  355. export var AjaxResponse = /*@__PURE__*/ (/*@__PURE__*/ function () {
  356. function AjaxResponse(originalEvent, xhr, request) {
  357. this.originalEvent = originalEvent;
  358. this.xhr = xhr;
  359. this.request = request;
  360. this.status = xhr.status;
  361. this.responseType = xhr.responseType || request.responseType;
  362. this.response = parseXhrResponse(this.responseType, xhr);
  363. }
  364. return AjaxResponse;
  365. }());
  366. /**
  367. * A normalized AJAX error.
  368. *
  369. * @see {@link ajax}
  370. *
  371. * @class AjaxError
  372. */
  373. export var AjaxError = /*@__PURE__*/ (/*@__PURE__*/ function (_super) {
  374. __extends(AjaxError, _super);
  375. function AjaxError(message, xhr, request) {
  376. _super.call(this, message);
  377. this.message = message;
  378. this.xhr = xhr;
  379. this.request = request;
  380. this.status = xhr.status;
  381. this.responseType = xhr.responseType || request.responseType;
  382. this.response = parseXhrResponse(this.responseType, xhr);
  383. }
  384. return AjaxError;
  385. }(Error));
  386. function parseXhrResponse(responseType, xhr) {
  387. switch (responseType) {
  388. case 'json':
  389. if ('response' in xhr) {
  390. //IE does not support json as responseType, parse it internally
  391. return xhr.responseType ? xhr.response : JSON.parse(xhr.response || xhr.responseText || 'null');
  392. }
  393. else {
  394. // HACK(benlesh): TypeScript shennanigans
  395. // tslint:disable-next-line:no-any latest TS seems to think xhr is "never" here.
  396. return JSON.parse(xhr.responseText || 'null');
  397. }
  398. case 'xml':
  399. return xhr.responseXML;
  400. case 'text':
  401. default:
  402. // HACK(benlesh): TypeScript shennanigans
  403. // tslint:disable-next-line:no-any latest TS seems to think xhr is "never" here.
  404. return ('response' in xhr) ? xhr.response : xhr.responseText;
  405. }
  406. }
  407. /**
  408. * @see {@link ajax}
  409. *
  410. * @class AjaxTimeoutError
  411. */
  412. export var AjaxTimeoutError = /*@__PURE__*/ (/*@__PURE__*/ function (_super) {
  413. __extends(AjaxTimeoutError, _super);
  414. function AjaxTimeoutError(xhr, request) {
  415. _super.call(this, 'ajax timeout', xhr, request);
  416. }
  417. return AjaxTimeoutError;
  418. }(AjaxError));
  419. //# sourceMappingURL=AjaxObservable.js.map