ajax.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. /**
  2. * DevExtreme (core/utils/ajax.js)
  3. * Version: 19.1.16
  4. * Build date: Tue Oct 18 2022
  5. *
  6. * Copyright (c) 2012 - 2022 Developer Express Inc. ALL RIGHTS RESERVED
  7. * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
  8. */
  9. "use strict";
  10. var Deferred = require("./deferred").Deferred;
  11. var domAdapter = require("../../core/dom_adapter");
  12. var httpRequest = require("../../core/http_request");
  13. var windowUtils = require("../../core/utils/window");
  14. var window = windowUtils.getWindow();
  15. var extendFromObject = require("./extend").extendFromObject;
  16. var isDefined = require("./type").isDefined;
  17. var Promise = require("../polyfills/promise");
  18. var injector = require("./dependency_injector");
  19. var SUCCESS = "success";
  20. var ERROR = "error";
  21. var TIMEOUT = "timeout";
  22. var NO_CONTENT = "nocontent";
  23. var PARSER_ERROR = "parsererror";
  24. var isStatusSuccess = function(status) {
  25. return 200 <= status && status < 300
  26. };
  27. var hasContent = function(status) {
  28. return 204 !== status
  29. };
  30. var paramsConvert = function(params) {
  31. var result = [];
  32. for (var name in params) {
  33. var value = params[name];
  34. if (void 0 === value) {
  35. continue
  36. }
  37. if (null === value) {
  38. value = ""
  39. }
  40. result.push(encodeURIComponent(name) + "=" + encodeURIComponent(value))
  41. }
  42. return result.join("&")
  43. };
  44. var createScript = function(options) {
  45. var script = domAdapter.createElement("script");
  46. for (var name in options) {
  47. script[name] = options[name]
  48. }
  49. return script
  50. };
  51. var removeScript = function(scriptNode) {
  52. scriptNode.parentNode.removeChild(scriptNode)
  53. };
  54. var appendToHead = function(element) {
  55. return domAdapter.getHead().appendChild(element)
  56. };
  57. var evalScript = function(code) {
  58. var script = createScript({
  59. text: code
  60. });
  61. appendToHead(script);
  62. removeScript(script)
  63. };
  64. var evalCrossDomainScript = function(url) {
  65. var script = createScript({
  66. src: url
  67. });
  68. return new Promise(function(resolve, reject) {
  69. var events = {
  70. load: resolve,
  71. error: reject
  72. };
  73. var loadHandler = function(e) {
  74. events[e.type]();
  75. removeScript(script)
  76. };
  77. for (var event in events) {
  78. domAdapter.listen(script, event, loadHandler)
  79. }
  80. appendToHead(script)
  81. })
  82. };
  83. var getAcceptHeader = function(options) {
  84. var dataType = options.dataType || "*";
  85. var scriptAccept = "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript";
  86. var accepts = {
  87. "*": "*/*",
  88. text: "text/plain",
  89. html: "text/html",
  90. xml: "application/xml, text/xml",
  91. json: "application/json, text/javascript",
  92. jsonp: scriptAccept,
  93. script: scriptAccept
  94. };
  95. extendFromObject(accepts, options.accepts, true);
  96. return accepts[dataType] ? accepts[dataType] + ("*" !== dataType ? ", */*; q=0.01" : "") : accepts["*"]
  97. };
  98. var getContentTypeHeader = function(options) {
  99. var defaultContentType;
  100. if (options.data && !options.upload && "GET" !== getMethod(options)) {
  101. defaultContentType = "application/x-www-form-urlencoded;charset=utf-8"
  102. }
  103. return options.contentType || defaultContentType
  104. };
  105. var getDataFromResponse = function(xhr) {
  106. return xhr.responseType && "text" !== xhr.responseType || "string" !== typeof xhr.responseText ? xhr.response : xhr.responseText
  107. };
  108. var postProcess = function(deferred, xhr, dataType) {
  109. var data = getDataFromResponse(xhr);
  110. switch (dataType) {
  111. case "jsonp":
  112. evalScript(data);
  113. break;
  114. case "script":
  115. evalScript(data);
  116. deferred.resolve(data, SUCCESS, xhr);
  117. break;
  118. case "json":
  119. try {
  120. deferred.resolve(JSON.parse(data), SUCCESS, xhr)
  121. } catch (e) {
  122. deferred.reject(xhr, PARSER_ERROR, e)
  123. }
  124. break;
  125. default:
  126. deferred.resolve(data, SUCCESS, xhr)
  127. }
  128. };
  129. var isCrossDomain = function(url) {
  130. if (!windowUtils.hasWindow()) {
  131. return true
  132. }
  133. var crossDomain = false;
  134. var originAnchor = domAdapter.createElement("a");
  135. var urlAnchor = domAdapter.createElement("a");
  136. originAnchor.href = window.location.href;
  137. try {
  138. urlAnchor.href = url;
  139. urlAnchor.href = urlAnchor.href;
  140. crossDomain = originAnchor.protocol + "//" + originAnchor.host !== urlAnchor.protocol + "//" + urlAnchor.host
  141. } catch (e) {
  142. crossDomain = true
  143. }
  144. return crossDomain
  145. };
  146. var setHttpTimeout = function(timeout, xhr) {
  147. return timeout && setTimeout(function() {
  148. xhr.customStatus = TIMEOUT;
  149. xhr.abort()
  150. }, timeout)
  151. };
  152. var getJsonpOptions = function(options) {
  153. if ("jsonp" === options.dataType) {
  154. var random = Math.random().toString().replace(/\D/g, "");
  155. var callbackName = options.jsonpCallback || "dxCallback" + Date.now() + "_" + random;
  156. var callbackParameter = options.jsonp || "callback";
  157. options.data = options.data || {};
  158. options.data[callbackParameter] = callbackName;
  159. return callbackName
  160. }
  161. };
  162. var getRequestOptions = function(options, headers) {
  163. var params = options.data;
  164. var paramsAlreadyString = "string" === typeof params;
  165. var url = options.url || window.location.href;
  166. if (!paramsAlreadyString && !options.cache) {
  167. params = params || {};
  168. params._ = Date.now()
  169. }
  170. if (params && !options.upload) {
  171. if (!paramsAlreadyString) {
  172. params = paramsConvert(params)
  173. }
  174. if ("GET" === getMethod(options)) {
  175. if ("" !== params) {
  176. url += (url.indexOf("?") > -1 ? "&" : "?") + params
  177. }
  178. params = null
  179. } else {
  180. if (headers["Content-Type"] && headers["Content-Type"].indexOf("application/x-www-form-urlencoded") > -1) {
  181. params = params.replace(/%20/g, "+")
  182. }
  183. }
  184. }
  185. return {
  186. url: url,
  187. parameters: params
  188. }
  189. };
  190. var getMethod = function(options) {
  191. return (options.method || "GET").toUpperCase()
  192. };
  193. var getRequestHeaders = function(options) {
  194. var headers = options.headers || {};
  195. headers["Content-Type"] = headers["Content-Type"] || getContentTypeHeader(options);
  196. headers.Accept = headers.Accept || getAcceptHeader(options);
  197. if (!options.crossDomain && !headers["X-Requested-With"]) {
  198. headers["X-Requested-With"] = "XMLHttpRequest"
  199. }
  200. return headers
  201. };
  202. var sendRequest = function(options) {
  203. var xhr = httpRequest.getXhr();
  204. var d = new Deferred;
  205. var result = d.promise();
  206. var async = isDefined(options.async) ? options.async : true;
  207. var dataType = options.dataType;
  208. var timeout = options.timeout || 0;
  209. var timeoutId;
  210. options.crossDomain = isCrossDomain(options.url);
  211. var needScriptEvaluation = "jsonp" === dataType || "script" === dataType;
  212. if (void 0 === options.cache) {
  213. options.cache = !needScriptEvaluation
  214. }
  215. var callbackName = getJsonpOptions(options);
  216. var headers = getRequestHeaders(options);
  217. var requestOptions = getRequestOptions(options, headers);
  218. var url = requestOptions.url;
  219. var parameters = requestOptions.parameters;
  220. if (callbackName) {
  221. window[callbackName] = function(data) {
  222. d.resolve(data, SUCCESS, xhr)
  223. }
  224. }
  225. if (options.crossDomain && needScriptEvaluation) {
  226. var reject = function() {
  227. d.reject(xhr, ERROR)
  228. };
  229. var resolve = function() {
  230. if ("jsonp" === dataType) {
  231. return
  232. }
  233. d.resolve(null, SUCCESS, xhr)
  234. };
  235. evalCrossDomainScript(url).then(resolve, reject);
  236. return result
  237. }
  238. if (options.crossDomain && !("withCredentials" in xhr)) {
  239. d.reject(xhr, ERROR);
  240. return result
  241. }
  242. xhr.open(getMethod(options), url, async, options.username, options.password);
  243. if (async) {
  244. xhr.timeout = timeout;
  245. timeoutId = setHttpTimeout(timeout, xhr, d)
  246. }
  247. xhr.onreadystatechange = function(e) {
  248. if (4 === xhr.readyState) {
  249. clearTimeout(timeoutId);
  250. if (isStatusSuccess(xhr.status)) {
  251. if (hasContent(xhr.status)) {
  252. postProcess(d, xhr, dataType)
  253. } else {
  254. d.resolve(null, NO_CONTENT, xhr)
  255. }
  256. } else {
  257. d.reject(xhr, xhr.customStatus || ERROR)
  258. }
  259. }
  260. };
  261. if (options.upload) {
  262. xhr.upload.onprogress = options.upload.onprogress;
  263. xhr.upload.onloadstart = options.upload.onloadstart;
  264. xhr.upload.onabort = options.upload.onabort
  265. }
  266. if (options.xhrFields) {
  267. for (var field in options.xhrFields) {
  268. xhr[field] = options.xhrFields[field]
  269. }
  270. }
  271. if ("arraybuffer" === options.responseType) {
  272. xhr.responseType = options.responseType
  273. }
  274. for (var name in headers) {
  275. if (Object.prototype.hasOwnProperty.call(headers, name) && isDefined(headers[name])) {
  276. xhr.setRequestHeader(name, headers[name])
  277. }
  278. }
  279. if (options.beforeSend) {
  280. options.beforeSend(xhr)
  281. }
  282. xhr.send(parameters);
  283. result.abort = function() {
  284. xhr.abort()
  285. };
  286. return result
  287. };
  288. module.exports = injector({
  289. sendRequest: sendRequest
  290. });