http/ajax.js

  1. /**
  2. * Wrapper class for XHR
  3. * @module http/ajax
  4. * @memberOf http
  5. */
  6. const {HttpRq, HttpContent} = require("./request");
  7. const {HttpRs} = require("./response");
  8. const T = require("../core/types");
  9. const {forEach} = require("../core/collections");
  10. /**
  11. * A wrapper class for {@link XMLHttpRequest} to facilitate sending requests and handling events
  12. * @class
  13. */
  14. class Ajax {
  15. /**
  16. * @constructor
  17. * @param {HttpMethod|HttpRq} m - Request method string or {@link http.HttpRq}
  18. * @param {String} [url]
  19. * @param {Object} [params] - Request parameters object
  20. * @param {Object?} [headers] - Headers object
  21. * @param {HttpContent?} [content] - Optional http content {@link HttpContent}
  22. */
  23. constructor(m, url, params = {}, headers = {}, content = new HttpContent()) {
  24. /** @type {HttpRq}
  25. * @see {http.HttpRq}
  26. */
  27. this.rq = {}
  28. /** @type {http.HttpRs}
  29. * @see {http.HttpRs}
  30. */
  31. this.rs = {}
  32. if (m instanceof HttpRq) {
  33. this.rq = m
  34. } else {
  35. this.rq = new HttpRq(m, url, params, headers, content);
  36. }
  37. // Fields
  38. this.rs = {readyState: 0};
  39. this.xhr = new XMLHttpRequest();
  40. this.preparedCallback = function (rq) {
  41. };
  42. this.progressCallback = function (ev, rq) {
  43. };
  44. this.uploadProgressCallback = function (xhr) {
  45. };
  46. this.successCallback = function (rq, rs) {
  47. };
  48. this.uploadFinishCallback = function (xhr) {
  49. };
  50. this.failCallback = function (rq, rs) {
  51. };
  52. Object.defineProperty(this, 'xhr', {enumerable: false})
  53. }
  54. // Methods
  55. /**
  56. * Set header
  57. * @param {String} n - header name
  58. * @param {String} [v] - header value
  59. * @returns {Ajax}
  60. */
  61. header(n, v) {
  62. this.rq.setHeader(n, v);
  63. return this;
  64. };
  65. /**
  66. * Add/Set headers
  67. * @param {Object} hdrs - headers object
  68. * @returns {Ajax}
  69. */
  70. headers(hdrs = {}) {
  71. forEach(hdrs, (v, k) => {
  72. this.rq.setHeader(k, v);
  73. });
  74. return this;
  75. };
  76. /**
  77. * @param {Function} callbackRqRs
  78. * @returns {Ajax}
  79. */
  80. onSuccess(callbackRqRs) {
  81. this.successCallback = callbackRqRs;
  82. return this;
  83. };
  84. /**
  85. * @param {Function} callbackRqRs
  86. * @returns {Ajax}
  87. */
  88. onUploadSuccess(callbackRqRs) {
  89. this.uploadFinishCallback = callbackRqRs;
  90. return this;
  91. };
  92. /**
  93. * @param {Function} callbackRqRs
  94. * @returns {Ajax}
  95. */
  96. onFail(callbackRqRs) {
  97. this.failCallback = callbackRqRs;
  98. return this;
  99. };
  100. /**
  101. * @param {Function} callbackRqRs
  102. * @returns {Ajax}
  103. */
  104. onProgress(callbackRqRs) {
  105. this.progressCallback = callbackRqRs;
  106. return this;
  107. };
  108. /**
  109. * @param {Function} callbackRqRs
  110. * @returns {Ajax}
  111. */
  112. onUploadProgress(callbackRqRs) {
  113. this.uploadProgressCallback = callbackRqRs;
  114. return this;
  115. };
  116. /**
  117. * Set custom content {@link HttpContent}
  118. * @param {HttpContent} content
  119. * @returns {Ajax}
  120. */
  121. withContent(content={}) {
  122. switch (content.type) {
  123. case 'json': this.rq.jsonContent(content.data); break;
  124. case 'xml': this.rq.xmlContent(content.data); break;
  125. case 'form': this.rq.formContent(content.data); break;
  126. case 'form_multipart': this.rq.formMultiPartContent(content.data); break;
  127. case 'form_urlencoded': this.rq.formUrlEncodedContent(content.data); break;
  128. default: this.rq.setContent(content.type, content.data);
  129. }
  130. return this;
  131. }
  132. /**
  133. * Set xml request data
  134. * @param {XMLDocument} data
  135. * @returns {Ajax}
  136. */
  137. xmlData(data) {
  138. this.rq.xmlContent(data);
  139. return this;
  140. }
  141. /**
  142. * Set form-data request data
  143. * @param {String|Node} form
  144. * @returns {Ajax}
  145. */
  146. formData(form) {
  147. this.rq.formContent(form);
  148. return this;
  149. };
  150. /**
  151. * Set json request data
  152. * @param {String|Object} data
  153. * @returns {Ajax}
  154. */
  155. jsonData(data) {
  156. this.rq.jsonContent(data);
  157. return this;
  158. };
  159. /**
  160. * Set url-encoded request data
  161. * @param {Object} data - simple data object
  162. * @returns {Ajax}
  163. */
  164. urlEncodedData(data) {
  165. this.rq.formUrlEncodedContent(data);
  166. return this;
  167. };
  168. _prepare(reset) {
  169. if (this.isPrepared && !reset) {
  170. // self.onprepare && self.onprepare(self.rq);
  171. return this
  172. }
  173. // prepare url
  174. let url = this.rq.url;
  175. if (this.rq.args && !T.isEmpty(this.rq.args)) {
  176. url.indexOf('?') >= 0 || (url += '?');
  177. url += this.rq.buildUrlEncoded();
  178. }
  179. reset && (this.xhr = new XMLHttpRequest());
  180. this.xhr.open(this.rq.method, url);
  181. // prepare headers
  182. for (let h in this.rq.headers) {
  183. if (this.rq.headers.hasOwnProperty(h))
  184. this.xhr.setRequestHeader(h, this.rq.headers[h]);
  185. }
  186. this.isPrepared = true;
  187. this.preparedCallback && this.preparedCallback(this.rq);
  188. // preparedCallback && preparedCallback();
  189. return this;
  190. };
  191. /**
  192. * Send XHR request
  193. * @param {Function} [finishCallback] - called after all other callbacks
  194. * @returns {Ajax}
  195. */
  196. send(finishCallback) {
  197. this._prepare();
  198. let ajax = this;
  199. let xhr = this.xhr;
  200. this.xhr.onreadystatechange = function (ev) {
  201. // onloadend
  202. if (xhr.readyState === 4) {
  203. let callback;
  204. if (xhr.status >= 200 && xhr.status <= 399) {
  205. callback = ajax.successCallback;
  206. } else {
  207. callback = ajax.failCallback;
  208. }
  209. ajax.rs = new HttpRs(xhr);
  210. finishCallback && finishCallback(ajax.rq, ajax.rs, ajax.xhr);
  211. callback && callback(ajax.rq, ajax.rs, ajax.xhr);
  212. }
  213. };
  214. this.xhr.onprogress = function (ev) {
  215. ajax.progressCallback && ajax.progressCallback(ev, ajax);
  216. };
  217. this.xhr.upload.onprogress = function (ev) {
  218. ajax.uploadProgressCallback && ajax.uploadProgressCallback(ev, ajax);
  219. };
  220. this.xhr.upload.onloadend = function (ev) {
  221. ajax.uploadFinishCallback && ajax.uploadFinishCallback(ev, ajax);
  222. };
  223. try {
  224. this.xhr.send(this.rq.content.data);
  225. } catch (e) {
  226. this.onFail(e);
  227. }
  228. return ajax;
  229. }
  230. /**
  231. * Send request with Promise
  232. * @return {Promise<Ajax>}
  233. */
  234. async sendAsync() {
  235. const ajax = this;
  236. const promise = new Promise((res, rej) => {
  237. ajax.onSuccess(()=>{
  238. return res(ajax)
  239. });
  240. ajax.onFail(()=>rej(ajax));
  241. });
  242. ajax.send();
  243. return promise;
  244. }
  245. }
  246. module.exports = {Ajax: Ajax};