1 | if (typeof FormData === 'undefined' || !FormData.prototype.keys) {
|
2 | const global = typeof window === 'object'
|
3 | ? window : typeof self === 'object'
|
4 | ? self : this
|
5 |
|
6 |
|
7 | const _FormData = global.FormData
|
8 |
|
9 |
|
10 | const _send = global.XMLHttpRequest && global.XMLHttpRequest.prototype.send
|
11 | const _fetch = global.Request && global.fetch
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | const stringTag = global.Symbol && Symbol.toStringTag
|
19 | const map = new WeakMap
|
20 | const wm = o => map.get(o)
|
21 | const arrayFrom = Array.from || (obj => [].slice.call(obj))
|
22 |
|
23 |
|
24 | if (stringTag) {
|
25 | if (!Blob.prototype[stringTag]) {
|
26 | Blob.prototype[stringTag] = 'Blob'
|
27 | }
|
28 |
|
29 | if ('File' in global && !File.prototype[stringTag]) {
|
30 | File.prototype[stringTag] = 'File'
|
31 | }
|
32 | }
|
33 |
|
34 |
|
35 | try {
|
36 | new File([], '')
|
37 | } catch (a) {
|
38 | global.File = function(b, d, c) {
|
39 | const blob = new Blob(b, c)
|
40 | const t = c && void 0 !== c.lastModified ? new Date(c.lastModified) : new Date
|
41 |
|
42 | Object.defineProperties(blob, {
|
43 | name: {
|
44 | value: d
|
45 | },
|
46 | lastModifiedDate: {
|
47 | value: t
|
48 | },
|
49 | lastModified: {
|
50 | value: +t
|
51 | },
|
52 | toString: {
|
53 | value() {
|
54 | return '[object File]'
|
55 | }
|
56 | }
|
57 | })
|
58 |
|
59 | if (stringTag) {
|
60 | Object.defineProperty(blob, stringTag, {
|
61 | value: 'File'
|
62 | })
|
63 | }
|
64 |
|
65 | return blob
|
66 | }
|
67 | }
|
68 |
|
69 | function normalizeValue([value, filename]) {
|
70 | if (value instanceof Blob)
|
71 |
|
72 |
|
73 | value = new File([value], filename, {
|
74 | type: value.type,
|
75 | lastModified: value.lastModified
|
76 | })
|
77 |
|
78 | return value
|
79 | }
|
80 |
|
81 | function stringify(name) {
|
82 | if (!arguments.length)
|
83 | throw new TypeError('1 argument required, but only 0 present.')
|
84 |
|
85 | return [name + '']
|
86 | }
|
87 |
|
88 | function normalizeArgs(name, value, filename) {
|
89 | if (arguments.length < 2)
|
90 | throw new TypeError(
|
91 | `2 arguments required, but only ${arguments.length} present.`
|
92 | )
|
93 |
|
94 | return value instanceof Blob
|
95 |
|
96 | ? [name + '', value, filename !== undefined
|
97 | ? filename + ''
|
98 | : typeof value.name === 'string'
|
99 | ? value.name
|
100 | : 'blob']
|
101 |
|
102 |
|
103 | : [name + '', value + '']
|
104 | }
|
105 |
|
106 | |
107 |
|
108 |
|
109 | class FormDataPolyfill {
|
110 |
|
111 | |
112 |
|
113 |
|
114 |
|
115 |
|
116 | constructor(form) {
|
117 | map.set(this, Object.create(null))
|
118 |
|
119 | if (!form)
|
120 | return this
|
121 |
|
122 | for (let elm of arrayFrom(form.elements)) {
|
123 | if (!elm.name || elm.disabled) continue
|
124 |
|
125 | if (elm.type === 'file')
|
126 | for (let file of elm.files)
|
127 | this.append(elm.name, file)
|
128 | else if (elm.type === 'select-multiple' || elm.type === 'select-one')
|
129 | for (let opt of arrayFrom(elm.options))
|
130 | !opt.disabled && opt.selected && this.append(elm.name, opt.value)
|
131 | else if (elm.type === 'checkbox' || elm.type === 'radio') {
|
132 | if (elm.checked) this.append(elm.name, elm.value)
|
133 | } else
|
134 | this.append(elm.name, elm.value)
|
135 | }
|
136 | }
|
137 |
|
138 |
|
139 | |
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 | append(name, value, filename) {
|
148 | const map = wm(this)
|
149 |
|
150 | if (!map[name])
|
151 | map[name] = []
|
152 |
|
153 | map[name].push([value, filename])
|
154 | }
|
155 |
|
156 |
|
157 | |
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 | delete(name) {
|
164 | delete wm(this)[name]
|
165 | }
|
166 |
|
167 |
|
168 | |
169 |
|
170 |
|
171 |
|
172 |
|
173 | *entries() {
|
174 | const map = wm(this)
|
175 |
|
176 | for (let name in map)
|
177 | for (let value of map[name])
|
178 | yield [name, normalizeValue(value)]
|
179 | }
|
180 |
|
181 | |
182 |
|
183 |
|
184 |
|
185 |
|
186 |
|
187 |
|
188 | forEach(callback, thisArg) {
|
189 | for (let [name, value] of this)
|
190 | callback.call(thisArg, value, name, this)
|
191 | }
|
192 |
|
193 |
|
194 | |
195 |
|
196 |
|
197 |
|
198 |
|
199 |
|
200 |
|
201 | get(name) {
|
202 | const map = wm(this)
|
203 | return map[name] ? normalizeValue(map[name][0]) : null
|
204 | }
|
205 |
|
206 |
|
207 | |
208 |
|
209 |
|
210 |
|
211 |
|
212 |
|
213 | getAll(name) {
|
214 | return (wm(this)[name] || []).map(normalizeValue)
|
215 | }
|
216 |
|
217 |
|
218 | |
219 |
|
220 |
|
221 |
|
222 |
|
223 |
|
224 | has(name) {
|
225 | return name in wm(this)
|
226 | }
|
227 |
|
228 |
|
229 | |
230 |
|
231 |
|
232 |
|
233 |
|
234 | *keys() {
|
235 | for (let [name] of this)
|
236 | yield name
|
237 | }
|
238 |
|
239 |
|
240 | |
241 |
|
242 |
|
243 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 | set(name, value, filename) {
|
249 | wm(this)[name] = [[value, filename]]
|
250 | }
|
251 |
|
252 |
|
253 | |
254 |
|
255 |
|
256 |
|
257 |
|
258 | *values() {
|
259 | for (let [name, value] of this)
|
260 | yield value
|
261 | }
|
262 |
|
263 |
|
264 | |
265 |
|
266 |
|
267 |
|
268 |
|
269 |
|
270 | ['_asNative']() {
|
271 | const fd = new _FormData
|
272 |
|
273 | for (let [name, value] of this)
|
274 | fd.append(name, value)
|
275 |
|
276 | return fd
|
277 | }
|
278 |
|
279 |
|
280 | |
281 |
|
282 |
|
283 |
|
284 |
|
285 | ['_blob']() {
|
286 | const boundary = '----formdata-polyfill-' + Math.random()
|
287 | const chunks = []
|
288 |
|
289 | for (let [name, value] of this) {
|
290 | chunks.push(`--${boundary}\r\n`)
|
291 |
|
292 | if (value instanceof Blob) {
|
293 | chunks.push(
|
294 | `Content-Disposition: form-data; name="${name}"; filename="${value.name}"\r\n`,
|
295 | `Content-Type: ${value.type || 'application/octet-stream'}\r\n\r\n`,
|
296 | value,
|
297 | '\r\n'
|
298 | )
|
299 | } else {
|
300 | chunks.push(
|
301 | `Content-Disposition: form-data; name="${name}"\r\n\r\n${value}\r\n`
|
302 | )
|
303 | }
|
304 | }
|
305 |
|
306 | chunks.push(`--${boundary}--`)
|
307 |
|
308 | return new Blob(chunks, {type: 'multipart/form-data; boundary=' + boundary})
|
309 | }
|
310 |
|
311 |
|
312 | |
313 |
|
314 |
|
315 |
|
316 |
|
317 |
|
318 | [Symbol.iterator]() {
|
319 | return this.entries()
|
320 | }
|
321 |
|
322 |
|
323 | |
324 |
|
325 |
|
326 |
|
327 |
|
328 | toString() {
|
329 | return '[object FormData]'
|
330 | }
|
331 | }
|
332 |
|
333 |
|
334 | if (stringTag) {
|
335 | |
336 |
|
337 |
|
338 |
|
339 |
|
340 |
|
341 | FormDataPolyfill.prototype[stringTag] = 'FormData'
|
342 | }
|
343 |
|
344 | const decorations = [
|
345 | ['append', normalizeArgs],
|
346 | ['delete', stringify],
|
347 | ['get', stringify],
|
348 | ['getAll', stringify],
|
349 | ['has', stringify],
|
350 | ['set', normalizeArgs]
|
351 | ]
|
352 |
|
353 | decorations.forEach(arr => {
|
354 | const orig = FormDataPolyfill.prototype[arr[0]]
|
355 | FormDataPolyfill.prototype[arr[0]] = function() {
|
356 | return orig.apply(this, arr[1].apply(this, arrayFrom(arguments)))
|
357 | }
|
358 | })
|
359 |
|
360 |
|
361 | if (_send) {
|
362 | XMLHttpRequest.prototype.send = function(data) {
|
363 |
|
364 |
|
365 |
|
366 | if (data instanceof FormDataPolyfill) {
|
367 | const blob = data['_blob']()
|
368 | this.setRequestHeader('Content-Type', blob.type)
|
369 | _send.call(this, blob)
|
370 | } else {
|
371 | _send.call(this, data)
|
372 | }
|
373 | }
|
374 | }
|
375 |
|
376 |
|
377 | if (_fetch) {
|
378 | const _fetch = global.fetch
|
379 |
|
380 | global.fetch = function(input, init) {
|
381 | if (init && init.body && init.body instanceof FormDataPolyfill) {
|
382 | init.body = init.body['_blob']()
|
383 | }
|
384 |
|
385 | return _fetch(input, init)
|
386 | }
|
387 | }
|
388 |
|
389 | global['FormData'] = FormDataPolyfill
|
390 | }
|