-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path71.js
More file actions
326 lines (306 loc) · 7.65 KB
/
Copy path71.js
File metadata and controls
326 lines (306 loc) · 7.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
import Cookies from 'js-cookie'
import {ApiError} from './errors'
/**
* Make it a little easier to use content types.
*/
const contentTypes = {
form: 'application/x-www-form-urlencoded',
multiForm: 'multipart/form-data',
json: 'application/json',
jsonApi: 'application/vnd.api+json'
}
function matchContentType(src, id) {
if (!src)
return false
let ii = src.indexOf(';')
if (ii >= 0)
src = src.substring(0, ii)
return src.toLowerCase() == contentTypes[id]
}
function addTrailingSlash( path ) {
return path + ((path[path.length - 1] == '/') ? '' : '/')
}
/**
* Find and replace terms in a string.
*
* Terms are identified by being surrounded by curly braces, such as
* this: "a string with a {substitution}". Here, "substitution" will
* be replaced by looking for the same named key in the supplied
* mapping.
*
* @param {string} text - The string with replacements.
* @param {object} mapping - The mapping.
*/
export function supplant( text, mapping ) {
return text.replace(
/{([^{}]*)}/g,
function( a, b ) {
let r = mapping[b]
if( r === undefined ) {
throw new ApiError( `Missing string template: ${b}` )
}
return typeof r === 'string' || typeof r === 'number' ? r : a
}
)
}
/**
* Capitalize a string.
*
* @param {string} text - The string to capitalize.
*/
export function capitalize( text ) {
return text[0].toUpperCase() + text.slice( 1 )
}
function isEmpty(value) {
return value === '' || value === undefined || value === null
}
/**
* Global storage for authorization and CSRF.
*
* Often when making requests the same credentials or CSRF token needs
* to be used. This is a place to store these details. Currently
* accepts "csrf" and "bearer" values. Upon initialisation cookies
* are examined for a current value for the CSRF token (looks for
* a cookie called "csrftoken").
*/
let ajaxSettings = {
csrf: Cookies ? Cookies.get('csrftoken') : undefined,
bearer: null
}
function makeHeaders(opts) {
let {
method = 'get',
contentType = contentTypes.json,
extraHeaders,
useBearer = true,
bearer
} = opts || {}
let headers = {
'X-Requested-With': 'XMLHttpRequest',
}
if (contentType !== contentTypes.multiForm) {
headers['Content-Type'] = contentType
}
if (!isEmpty(ajaxSettings.csrf) && !(/^(GET|HEAD|OPTIONS\TRACE)$/i.test(method))) {
headers['X-CSRFToken'] = ajaxSettings.csrf
}
if (!bearer) {
bearer = ajaxSettings.bearer
}
if (useBearer && bearer) {
headers.Authorization = `Bearer ${bearer}`
}
headers = {
...headers,
...extraHeaders
}
return headers
}
/**
* Construct headers for a fetch request.
*
* Uses the HTML5 Headers object to formulate an appropriate set of
* headers based on the supplied options.
*
* @param {string} method - The request method. Defaults to "get".
* @param {string} contentType - The content type of the request. Defaults to "application/json".
* @param {object} extraHeaders - Custom headers to add.
* @param {boolean} useBearer - Flag indicating whether to include bearer authorization.
*/
function fetchHeaders(opts) {
return new Headers(makeHeaders(opts))
}
function makeRequest( opts ) {
const method = (opts.method || 'get').toUpperCase()
const {
url,
body,
contentType,
extraHeaders,
useBearer = true,
bearer
} = opts || {}
let request = {
url,
method,
headers: makeHeaders({
method,
contentType,
extraHeaders,
useBearer,
bearer
}),
credentials: 'same-origin'
}
if( method != 'GET' && method != 'HEAD' && method != 'OPTIONS') {
request.body = body
}
return request
}
/**
* Perform an ajax request.
*
* Uses HTML5 fetch to perform an ajax request according to parameters
* supplied via the options object.
*
* @param {string} url - The URL to make the request to.
* @param {string} method - The request method. Defaults to "get".
* @param {string} body - Data to be sent with the request.
* @param {string} contentType - The content type of the request. Defaults to "application/json".
* @param {object} extraHeaders - Custom headers to add.
* @param {boolean} useBearer - Flag indicating whether to include bearer authorization.
*/
function ajax( opts ) {
const method = (opts.method || 'get').toUpperCase()
const {
url,
body,
contentType,
extraHeaders,
useBearer = true,
bearer
} = opts || {}
let requestInit = {
method,
headers: fetchHeaders({
method,
contentType,
extraHeaders,
useBearer,
bearer
}),
credentials: 'same-origin'
}
if( method != 'GET' && method != 'HEAD' && method != 'OPTIONS') {
requestInit.body = body
}
let request = new Request( url, requestInit )
return fetch( request )
.then( response => {
if( !!response.ok ) {
if( response.status == 204 ) {
return {}
}
if( typeof TINYAPI_NODE !== 'undefined' && TINYAPI_NODE ) {
return response
}
if( !!response.json ) {
return response.json()
}
else {
return response
}
}
if( !!response.json ) {
return response.json()
.catch( e => Object({ status: response.status }) )
.then( e => Promise.reject( e ) )
}
else {
return response
}
})
}
function ajaxWithRequest( opts ) {
const {
url,
...requestInit
} = opts
let request = new Request( url, requestInit )
return fetch( request )
.then( response => {
if( !!response.ok ) {
if( response.status == 204 ) {
return {}
}
if( typeof TINYAPI_NODE !== 'undefined' && TINYAPI_NODE ) {
return response
}
if( !!response.json ) {
return response.json().then(data => ({
data,
response
}))
}
else {
return {response}
}
}
if( !!response.json ) {
const data = response.json()
.catch( e => Object({ status: response.status }) )
.then( e => Promise.reject( e ) )
return {
data,
response
}
}
else {
return {response}
}
})
}
/**
* Post JSON data.
*
* @param {string} url - The URL to make the request to.
* @param {object} payload - Data to be sent with the request.
* @param {string} contentType - The content type of the request. Defaults to "application/json".
* @param {boolean} useBearer - Flag indicating whether to include bearer authorization.
*/
function postJson({ url, payload, contentType, useBearer }) {
return ajax({
url,
method: 'post',
body: JSON.stringify( payload || {} ),
contentType,
useBearer
})
}
/**
* Convert an object into HTML5 FormData.
*/
function makeFormData( payload ) {
let body = new FormData()
for( let k in (payload || {}) ) {
body.append( k, payload[k] )
}
return body
}
/**
* Post form data.
*
* @param {string} url - The URL to make the request to.
* @param {object} payload - Data to be sent with the request.
* @param {boolean} useBearer - Flag indicating whether to include bearer authorization.
*/
function postForm({ url, payload, useBearer }) {
return ajax({
url,
body: makeFormData( payload ),
method: 'post',
useBearer
})
}
export function takeFirst() {
if (arguments) {
for (const v of arguments) {
if (v !== undefined)
return v
}
}
}
export {
addTrailingSlash,
ajax,
postJson,
postForm,
ajaxSettings,
contentTypes,
matchContentType,
makeFormData,
makeRequest,
ajaxWithRequest,
makeHeaders,
fetchHeaders
}