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