ajax.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. const MIME_TYPES = {
  2. json: "application/json",
  3. html: "text/html",
  4. };
  5. function ajax(options) {
  6. applyDefaults(options);
  7. serializeData(options);
  8. const xhr = new XMLHttpRequest();
  9. xhr.open(options.type, options.url, options.async);
  10. applyCallbacks(xhr, options);
  11. applyHeaders(xhr, options);
  12. xhr.send(options.data);
  13. if (options.async) {
  14. return { abort: abort.bind(undefined, xhr) };
  15. } else {
  16. return parseResponse(xhr, options);
  17. }
  18. function applyDefaults(options) {
  19. for (var key in ajax.defaults) {
  20. if (options[key] == null) {
  21. options[key] = ajax.defaults[key];
  22. }
  23. }
  24. }
  25. function serializeData(options) {
  26. if (!options.data) {
  27. return;
  28. }
  29. if (options.type === "GET") {
  30. options.url += "?" + serializeParams(options.data);
  31. options.data = null;
  32. } else {
  33. options.data = serializeParams(options.data);
  34. }
  35. }
  36. function serializeParams(params) {
  37. return Object.entries(params)
  38. .map(
  39. ([key, value]) =>
  40. `${encodeURIComponent(key)}=${encodeURIComponent(value)}`,
  41. )
  42. .join("&");
  43. }
  44. function applyCallbacks(xhr, options) {
  45. if (!options.async) {
  46. return;
  47. }
  48. xhr.timer = setTimeout(
  49. onTimeout.bind(undefined, xhr, options),
  50. options.timeout * 1000,
  51. );
  52. if (options.progress) {
  53. xhr.onprogress = options.progress;
  54. }
  55. xhr.onreadystatechange = function () {
  56. if (xhr.readyState === 4) {
  57. clearTimeout(xhr.timer);
  58. onComplete(xhr, options);
  59. }
  60. };
  61. }
  62. function applyHeaders(xhr, options) {
  63. if (!options.headers) {
  64. options.headers = {};
  65. }
  66. if (options.contentType) {
  67. options.headers["Content-Type"] = options.contentType;
  68. }
  69. if (
  70. !options.headers["Content-Type"] &&
  71. options.data &&
  72. options.type !== "GET"
  73. ) {
  74. options.headers["Content-Type"] = "application/x-www-form-urlencoded";
  75. }
  76. if (options.dataType) {
  77. options.headers["Accept"] =
  78. MIME_TYPES[options.dataType] || options.dataType;
  79. }
  80. for (var key in options.headers) {
  81. var value = options.headers[key];
  82. xhr.setRequestHeader(key, value);
  83. }
  84. }
  85. function onComplete(xhr, options) {
  86. if (200 <= xhr.status && xhr.status < 300) {
  87. let response;
  88. if ((response = parseResponse(xhr, options)) != null) {
  89. onSuccess(response, xhr, options);
  90. } else {
  91. onError("invalid", xhr, options);
  92. }
  93. } else {
  94. onError("error", xhr, options);
  95. }
  96. }
  97. function onSuccess(response, xhr, options) {
  98. if (options.success != null) {
  99. options.success.call(options.context, response, xhr, options);
  100. }
  101. }
  102. function onError(type, xhr, options) {
  103. if (options.error != null) {
  104. options.error.call(options.context, type, xhr, options);
  105. }
  106. }
  107. function onTimeout(xhr, options) {
  108. xhr.abort();
  109. onError("timeout", xhr, options);
  110. }
  111. function abort(xhr) {
  112. clearTimeout(xhr.timer);
  113. xhr.onreadystatechange = null;
  114. xhr.abort();
  115. }
  116. function parseResponse(xhr, options) {
  117. if (options.dataType === "json") {
  118. return parseJSON(xhr.responseText);
  119. } else {
  120. return xhr.responseText;
  121. }
  122. }
  123. function parseJSON(json) {
  124. try {
  125. return JSON.parse(json);
  126. } catch (error) {}
  127. }
  128. }
  129. ajax.defaults = {
  130. async: true,
  131. dataType: "json",
  132. timeout: 30,
  133. type: "GET",
  134. // contentType
  135. // context
  136. // data
  137. // error
  138. // headers
  139. // progress
  140. // success
  141. // url
  142. };