Skip to content

Commit ce12fb3

Browse files
authored
feat: ajax (#44)
* chore: babel, webpack to transpile es6 * feat: initial ajax module * feat: event handler - beforeRequest, success, error, complete * feat: return the Promise object if the browser supports * feat: response and error wrapper * feat: set default options using static property * feat: support aliases for request methods * chore: merge into one file * feat: defaults.headers.[method] * chore: apply code review * chore: apply code review * docs: jsdoc for the ajax module * chore: use babel-plugin-add-module-exports and @babel/plugin-transform-member-expression-literals
1 parent dc3f741 commit ce12fb3

14 files changed

+5418
-673
lines changed

.babelrc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"presets": [["@babel/preset-env", { "loose": true }]],
3+
"plugins": ["add-module-exports"],
4+
"comments": false
5+
}

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ajax/index.js

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ You can also see the older versions of API page on the [releases page](https://g
3131

3232
## 🎨 Features
3333

34+
* ajax
35+
* Send the Ajax request
3436
* array
3537
* Handle arrays
3638
* browser

ajax/.eslintrc.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
'extends': ['tui/es6', 'plugin:prettier/recommended'],
3+
'parserOptions': {
4+
'sourceType': 'module'
5+
}
6+
};

ajax/index.js

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
"use strict";
2+
3+
exports.__esModule = true;
4+
exports["default"] = void 0;
5+
6+
var _forEachArray = _interopRequireDefault(require("../collection/forEachArray"));
7+
8+
var _forEachOwnProperties = _interopRequireDefault(require("../collection/forEachOwnProperties"));
9+
10+
var _extend = _interopRequireDefault(require("../object/extend"));
11+
12+
var _isArray = _interopRequireDefault(require("../type/isArray"));
13+
14+
var _isEmpty = _interopRequireDefault(require("../type/isEmpty"));
15+
16+
var _isFunction = _interopRequireDefault(require("../type/isFunction"));
17+
18+
var _isNull = _interopRequireDefault(require("../type/isNull"));
19+
20+
var _isObject = _interopRequireDefault(require("../type/isObject"));
21+
22+
var _isUndefined = _interopRequireDefault(require("../type/isUndefined"));
23+
24+
function _interopRequireDefault(obj) {
25+
return obj && obj.__esModule ? obj : {
26+
"default": obj
27+
};
28+
}
29+
30+
function encodePairs(key, value) {
31+
return encodeURIComponent(key) + "=" + encodeURIComponent((0, _isNull["default"])(value) || (0, _isUndefined["default"])(value) ? '' : value);
32+
}
33+
34+
function serializeParams(key, value, serializedList) {
35+
if ((0, _isArray["default"])(value)) {
36+
(0, _forEachArray["default"])(value, function (arrVal, index) {
37+
serializeParams(key + "[" + ((0, _isObject["default"])(arrVal) ? index : '') + "]", arrVal, serializedList);
38+
});
39+
} else if ((0, _isObject["default"])(value)) {
40+
(0, _forEachOwnProperties["default"])(value, function (objValue, objKey) {
41+
serializeParams(key + "[" + objKey + "]", objValue, serializedList);
42+
});
43+
} else {
44+
serializedList.push(encodePairs(key, value));
45+
}
46+
}
47+
48+
function serialize(params) {
49+
if (!params || (0, _isEmpty["default"])(params)) {
50+
return '';
51+
}
52+
53+
var serializedList = [];
54+
(0, _forEachOwnProperties["default"])(params, function (value, key) {
55+
serializeParams(key, value, serializedList);
56+
});
57+
return serializedList.join('&');
58+
}
59+
60+
var getDefaultOptions = function getDefaultOptions() {
61+
return {
62+
baseURL: '',
63+
headers: {
64+
common: {},
65+
get: {},
66+
post: {},
67+
put: {},
68+
"delete": {},
69+
patch: {},
70+
options: {},
71+
head: {}
72+
},
73+
serializer: serialize
74+
};
75+
};
76+
77+
var HTTP_PROTOCOL_REGEXP = /^(http|https):\/\//i;
78+
79+
function combineURL(baseURL, url) {
80+
if (HTTP_PROTOCOL_REGEXP.test(url)) {
81+
return url;
82+
}
83+
84+
if (baseURL.slice(-1) === '/' && url.slice(0, 1) === '/') {
85+
url = url.slice(1);
86+
}
87+
88+
return baseURL + url;
89+
}
90+
91+
function getComputedOptions(defaultOptions, customOptions) {
92+
var baseURL = defaultOptions.baseURL,
93+
defaultHeaders = defaultOptions.headers,
94+
defaultSerializer = defaultOptions.serializer,
95+
defaultBeforeRequest = defaultOptions.beforeRequest,
96+
defaultSuccess = defaultOptions.success,
97+
defaultError = defaultOptions.error,
98+
defaultComplete = defaultOptions.complete;
99+
var url = customOptions.url,
100+
contentType = customOptions.contentType,
101+
method = customOptions.method,
102+
params = customOptions.params,
103+
headers = customOptions.headers,
104+
serializer = customOptions.serializer,
105+
beforeRequest = customOptions.beforeRequest,
106+
success = customOptions.success,
107+
error = customOptions.error,
108+
complete = customOptions.complete,
109+
withCredentials = customOptions.withCredentials,
110+
mimeType = customOptions.mimeType;
111+
var options = {
112+
url: combineURL(baseURL, url),
113+
method: method,
114+
params: params,
115+
headers: (0, _extend["default"])(defaultHeaders.common, defaultHeaders[method.toLowerCase()], headers),
116+
serializer: serializer || defaultSerializer || serialize,
117+
beforeRequest: [defaultBeforeRequest, beforeRequest],
118+
success: [defaultSuccess, success],
119+
error: [defaultError, error],
120+
complete: [defaultComplete, complete],
121+
withCredentials: withCredentials,
122+
mimeType: mimeType
123+
};
124+
options.contentType = contentType || options.headers['Content-Type'];
125+
delete options.headers['Content-Type'];
126+
return options;
127+
}
128+
129+
var ENCODED_SPACE_REGEXP = /%20/g;
130+
var QS_DELIM_REGEXP = /\?/;
131+
132+
function validateStatus(status) {
133+
return status >= 200 && status < 300;
134+
}
135+
136+
function hasRequestBody(method) {
137+
return /^(?:POST|PUT|PATCH)$/.test(method.toUpperCase());
138+
}
139+
140+
function executeCallback(callback, param) {
141+
if ((0, _isArray["default"])(callback)) {
142+
(0, _forEachArray["default"])(callback, function (fn) {
143+
return executeCallback(fn, param);
144+
});
145+
} else if ((0, _isFunction["default"])(callback)) {
146+
callback(param);
147+
}
148+
}
149+
150+
function parseHeaders(text) {
151+
var headers = {};
152+
(0, _forEachArray["default"])(text.split('\r\n'), function (header) {
153+
var _header$split = header.split(': '),
154+
key = _header$split[0],
155+
value = _header$split[1];
156+
157+
if (key !== '' && !(0, _isUndefined["default"])(value)) {
158+
headers[key] = value;
159+
}
160+
});
161+
return headers;
162+
}
163+
164+
function parseJSONData(data) {
165+
var result = '';
166+
167+
try {
168+
result = JSON.parse(data);
169+
} catch (_) {
170+
result = data;
171+
}
172+
173+
return result;
174+
}
175+
176+
function handleReadyStateChange(xhr, options) {
177+
var readyState = xhr.readyState,
178+
status = xhr.status,
179+
statusText = xhr.statusText,
180+
responseText = xhr.responseText;
181+
var success = options.success,
182+
resolve = options.resolve,
183+
error = options.error,
184+
reject = options.reject,
185+
complete = options.complete;
186+
187+
if (readyState != XMLHttpRequest.DONE) {
188+
return;
189+
}
190+
191+
if (validateStatus(status)) {
192+
var contentType = xhr.getResponseHeader('Content-Type');
193+
var data = responseText;
194+
195+
if (contentType && contentType.indexOf('application/json') > -1) {
196+
data = parseJSONData(data);
197+
}
198+
199+
executeCallback([success, resolve], {
200+
status: status,
201+
statusText: statusText,
202+
data: data,
203+
headers: parseHeaders(xhr.getAllResponseHeaders())
204+
});
205+
} else {
206+
executeCallback([error, reject], {
207+
status: status,
208+
statusText: statusText
209+
});
210+
}
211+
212+
executeCallback(complete, {
213+
status: status,
214+
statusText: statusText
215+
});
216+
}
217+
218+
function open(xhr, options) {
219+
var url = options.url,
220+
method = options.method,
221+
serializer = options.serializer,
222+
params = options.params;
223+
var requestUrl = url;
224+
225+
if (!hasRequestBody(method) && params) {
226+
var qs = (QS_DELIM_REGEXP.test(url) ? '&' : '?') + serializer(params);
227+
requestUrl = "" + url + qs;
228+
}
229+
230+
xhr.open(method, requestUrl);
231+
}
232+
233+
function applyConfig(xhr, options) {
234+
var method = options.method,
235+
contentType = options.contentType,
236+
mimeType = options.mimeType,
237+
headers = options.headers,
238+
_options$withCredenti = options.withCredentials,
239+
withCredentials = _options$withCredenti === void 0 ? false : _options$withCredenti;
240+
241+
if (withCredentials) {
242+
xhr.withCredentials = withCredentials;
243+
}
244+
245+
if (mimeType) {
246+
xhr.overrideMimeType(mimeType);
247+
}
248+
249+
(0, _forEachOwnProperties["default"])(headers, function (value, header) {
250+
if (!(0, _isObject["default"])(value)) {
251+
xhr.setRequestHeader(header, value);
252+
}
253+
});
254+
255+
if (hasRequestBody(method)) {
256+
xhr.setRequestHeader('Content-Type', contentType + "; charset=UTF-8");
257+
}
258+
259+
xhr.setRequestHeader('x-requested-with', 'XMLHttpRequest');
260+
}
261+
262+
function send(xhr, options) {
263+
var method = options.method,
264+
serializer = options.serializer,
265+
beforeRequest = options.beforeRequest,
266+
_options$params = options.params,
267+
params = _options$params === void 0 ? {} : _options$params,
268+
_options$contentType = options.contentType,
269+
contentType = _options$contentType === void 0 ? 'application/x-www-form-urlencoded' : _options$contentType;
270+
var body = null;
271+
272+
if (hasRequestBody(method)) {
273+
body = contentType.indexOf('application/x-www-form-urlencoded') > -1 ? serializer(params).replace(ENCODED_SPACE_REGEXP, '+') : JSON.stringify(params);
274+
}
275+
276+
xhr.onreadystatechange = function () {
277+
return handleReadyStateChange(xhr, options);
278+
};
279+
280+
executeCallback(beforeRequest, xhr);
281+
xhr.send(body);
282+
}
283+
284+
function ajax(options) {
285+
var xhr = new XMLHttpRequest();
286+
287+
var request = function request(opts) {
288+
return (0, _forEachArray["default"])([open, applyConfig, send], function (fn) {
289+
return fn(xhr, opts);
290+
});
291+
};
292+
293+
options = getComputedOptions(ajax.defaults, options);
294+
295+
if (typeof Promise !== 'undefined') {
296+
return new Promise(function (resolve, reject) {
297+
request((0, _extend["default"])(options, {
298+
resolve: resolve,
299+
reject: reject
300+
}));
301+
});
302+
}
303+
304+
request(options);
305+
return null;
306+
}
307+
308+
ajax.defaults = getDefaultOptions();
309+
310+
ajax._reset = function () {
311+
ajax.defaults = getDefaultOptions();
312+
};
313+
314+
ajax._request = function (url, method, options) {
315+
if (options === void 0) {
316+
options = {};
317+
}
318+
319+
return ajax((0, _extend["default"])(options, {
320+
url: url,
321+
method: method
322+
}));
323+
};
324+
325+
(0, _forEachArray["default"])(['get', 'post', 'put', 'delete', 'patch', 'options', 'head'], function (type) {
326+
ajax[type] = function (url, options) {
327+
return ajax._request(url, type.toUpperCase(), options);
328+
};
329+
});
330+
var _default = ajax;
331+
exports["default"] = _default;
332+
module.exports = exports["default"];

0 commit comments

Comments
 (0)