[ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
1 if (typeof FormData === 'undefined' || !FormData.prototype.keys) { 2 const global = typeof window === 'object' 3 ? window : typeof self === 'object' 4 ? self : this 5 6 // keep a reference to native implementation 7 const _FormData = global.FormData 8 9 // To be monkey patched 10 const _send = global.XMLHttpRequest && global.XMLHttpRequest.prototype.send 11 const _fetch = global.Request && global.fetch 12 13 // Unable to patch Request constructor correctly 14 // const _Request = global.Request 15 // only way is to use ES6 class extend 16 // https://github.com/babel/babel/issues/1966 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 // Add missing stringTags to blob and files 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 // Fix so you can construct your own File 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 // Should always returns a new File instance 72 // console.assert(fd.get(x) !== fd.get(x)) 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 // normalize name and filename if adding an attachment 96 ? [name + '', value, filename !== undefined 97 ? filename + '' // Cast filename to string if 3th arg isn't undefined 98 : typeof value.name === 'string' // if name prop exist 99 ? value.name // Use File.name 100 : 'blob'] // otherwise fallback to Blob 101 102 // If no attachment, just cast the args to strings 103 : [name + '', value + ''] 104 } 105 106 function each (arr, cb) { 107 for (let i = 0; i < arr.length; i++) { 108 cb(arr[i]) 109 } 110 } 111 112 /** 113 * @implements {Iterable} 114 */ 115 class FormDataPolyfill { 116 117 /** 118 * FormData class 119 * 120 * @param {HTMLElement=} form 121 */ 122 constructor(form) { 123 map.set(this, Object.create(null)) 124 125 if (!form) 126 return this 127 128 const self = this 129 130 each(form.elements, elm => { 131 if (!elm.name || elm.disabled || elm.type === 'submit' || elm.type === 'button') return 132 133 if (elm.type === 'file') { 134 each(elm.files || [], file => { 135 self.append(elm.name, file) 136 }) 137 } else if (elm.type === 'select-multiple' || elm.type === 'select-one') { 138 each(elm.options, opt => { 139 !opt.disabled && opt.selected && self.append(elm.name, opt.value) 140 }) 141 } else if (elm.type === 'checkbox' || elm.type === 'radio') { 142 if (elm.checked) self.append(elm.name, elm.value) 143 } else { 144 self.append(elm.name, elm.value) 145 } 146 }) 147 } 148 149 150 /** 151 * Append a field 152 * 153 * @param {String} name field name 154 * @param {String|Blob|File} value string / blob / file 155 * @param {String=} filename filename to use with blob 156 * @return {Undefined} 157 */ 158 append(name, value, filename) { 159 const map = wm(this) 160 161 if (!map[name]) 162 map[name] = [] 163 164 map[name].push([value, filename]) 165 } 166 167 168 /** 169 * Delete all fields values given name 170 * 171 * @param {String} name Field name 172 * @return {Undefined} 173 */ 174 delete(name) { 175 delete wm(this)[name] 176 } 177 178 179 /** 180 * Iterate over all fields as [name, value] 181 * 182 * @return {Iterator} 183 */ 184 *entries() { 185 const map = wm(this) 186 187 for (let name in map) 188 for (let value of map[name]) 189 yield [name, normalizeValue(value)] 190 } 191 192 /** 193 * Iterate over all fields 194 * 195 * @param {Function} callback Executed for each item with parameters (value, name, thisArg) 196 * @param {Object=} thisArg `this` context for callback function 197 * @return {Undefined} 198 */ 199 forEach(callback, thisArg) { 200 for (let [name, value] of this) 201 callback.call(thisArg, value, name, this) 202 } 203 204 205 /** 206 * Return first field value given name 207 * or null if non existen 208 * 209 * @param {String} name Field name 210 * @return {String|File|null} value Fields value 211 */ 212 get(name) { 213 const map = wm(this) 214 return map[name] ? normalizeValue(map[name][0]) : null 215 } 216 217 218 /** 219 * Return all fields values given name 220 * 221 * @param {String} name Fields name 222 * @return {Array} [{String|File}] 223 */ 224 getAll(name) { 225 return (wm(this)[name] || []).map(normalizeValue) 226 } 227 228 229 /** 230 * Check for field name existence 231 * 232 * @param {String} name Field name 233 * @return {boolean} 234 */ 235 has(name) { 236 return name in wm(this) 237 } 238 239 240 /** 241 * Iterate over all fields name 242 * 243 * @return {Iterator} 244 */ 245 *keys() { 246 for (let [name] of this) 247 yield name 248 } 249 250 251 /** 252 * Overwrite all values given name 253 * 254 * @param {String} name Filed name 255 * @param {String} value Field value 256 * @param {String=} filename Filename (optional) 257 * @return {Undefined} 258 */ 259 set(name, value, filename) { 260 wm(this)[name] = [[value, filename]] 261 } 262 263 264 /** 265 * Iterate over all fields 266 * 267 * @return {Iterator} 268 */ 269 *values() { 270 for (let [name, value] of this) 271 yield value 272 } 273 274 275 /** 276 * Return a native (perhaps degraded) FormData with only a `append` method 277 * Can throw if it's not supported 278 * 279 * @return {FormData} 280 */ 281 ['_asNative']() { 282 const fd = new _FormData 283 284 for (let [name, value] of this) 285 fd.append(name, value) 286 287 return fd 288 } 289 290 291 /** 292 * [_blob description] 293 * 294 * @return {Blob} [description] 295 */ 296 ['_blob']() { 297 const boundary = '----formdata-polyfill-' + Math.random() 298 const chunks = [] 299 300 for (let [name, value] of this) { 301 chunks.push(`--$boundary}\r\n`) 302 303 if (value instanceof Blob) { 304 chunks.push( 305 `Content-Disposition: form-data; name="$name}"; filename="$value.name}"\r\n`, 306 `Content-Type: $value.type || 'application/octet-stream'}\r\n\r\n`, 307 value, 308 '\r\n' 309 ) 310 } else { 311 chunks.push( 312 `Content-Disposition: form-data; name="$name}"\r\n\r\n$value}\r\n` 313 ) 314 } 315 } 316 317 chunks.push(`--$boundary}--`) 318 319 return new Blob(chunks, {type: 'multipart/form-data; boundary=' + boundary}) 320 } 321 322 323 /** 324 * The class itself is iterable 325 * alias for formdata.entries() 326 * 327 * @return {Iterator} 328 */ 329 [Symbol.iterator]() { 330 return this.entries() 331 } 332 333 334 /** 335 * Create the default string description. 336 * 337 * @return {String} [object FormData] 338 */ 339 toString() { 340 return '[object FormData]' 341 } 342 } 343 344 345 if (stringTag) { 346 /** 347 * Create the default string description. 348 * It is accessed internally by the Object.prototype.toString(). 349 * 350 * @return {String} FormData 351 */ 352 FormDataPolyfill.prototype[stringTag] = 'FormData' 353 } 354 355 const decorations = [ 356 ['append', normalizeArgs], 357 ['delete', stringify], 358 ['get', stringify], 359 ['getAll', stringify], 360 ['has', stringify], 361 ['set', normalizeArgs] 362 ] 363 364 decorations.forEach(arr => { 365 const orig = FormDataPolyfill.prototype[arr[0]] 366 FormDataPolyfill.prototype[arr[0]] = function() { 367 return orig.apply(this, arr[1].apply(this, arrayFrom(arguments))) 368 } 369 }) 370 371 // Patch xhr's send method to call _blob transparently 372 if (_send) { 373 XMLHttpRequest.prototype.send = function(data) { 374 // I would check if Content-Type isn't already set 375 // But xhr lacks getRequestHeaders functionallity 376 // https://github.com/jimmywarting/FormData/issues/44 377 if (data instanceof FormDataPolyfill) { 378 const blob = data['_blob']() 379 this.setRequestHeader('Content-Type', blob.type) 380 _send.call(this, blob) 381 } else { 382 _send.call(this, data) 383 } 384 } 385 } 386 387 // Patch fetch's function to call _blob transparently 388 if (_fetch) { 389 const _fetch = global.fetch 390 391 global.fetch = function(input, init) { 392 if (init && init.body && init.body instanceof FormDataPolyfill) { 393 init.body = init.body['_blob']() 394 } 395 396 return _fetch(input, init) 397 } 398 } 399 400 global['FormData'] = FormDataPolyfill 401 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sat Jan 23 01:00:05 2021 | Cross-referenced by PHPXref 0.7.1 |