[ 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 /** 107 * @implements {Iterable} 108 */ 109 class FormDataPolyfill { 110 111 /** 112 * FormData class 113 * 114 * @param {HTMLElement=} form 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 arrayFrom(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 * Append a field 141 * 142 * @param {String} name field name 143 * @param {String|Blob|File} value string / blob / file 144 * @param {String=} filename filename to use with blob 145 * @return {Undefined} 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 * Delete all fields values given name 159 * 160 * @param {String} name Field name 161 * @return {Undefined} 162 */ 163 delete(name) { 164 delete wm(this)[name] 165 } 166 167 168 /** 169 * Iterate over all fields as [name, value] 170 * 171 * @return {Iterator} 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 * Iterate over all fields 183 * 184 * @param {Function} callback Executed for each item with parameters (value, name, thisArg) 185 * @param {Object=} thisArg `this` context for callback function 186 * @return {Undefined} 187 */ 188 forEach(callback, thisArg) { 189 for (let [name, value] of this) 190 callback.call(thisArg, value, name, this) 191 } 192 193 194 /** 195 * Return first field value given name 196 * or null if non existen 197 * 198 * @param {String} name Field name 199 * @return {String|File|null} value Fields value 200 */ 201 get(name) { 202 const map = wm(this) 203 return map[name] ? normalizeValue(map[name][0]) : null 204 } 205 206 207 /** 208 * Return all fields values given name 209 * 210 * @param {String} name Fields name 211 * @return {Array} [{String|File}] 212 */ 213 getAll(name) { 214 return (wm(this)[name] || []).map(normalizeValue) 215 } 216 217 218 /** 219 * Check for field name existence 220 * 221 * @param {String} name Field name 222 * @return {boolean} 223 */ 224 has(name) { 225 return name in wm(this) 226 } 227 228 229 /** 230 * Iterate over all fields name 231 * 232 * @return {Iterator} 233 */ 234 *keys() { 235 for (let [name] of this) 236 yield name 237 } 238 239 240 /** 241 * Overwrite all values given name 242 * 243 * @param {String} name Filed name 244 * @param {String} value Field value 245 * @param {String=} filename Filename (optional) 246 * @return {Undefined} 247 */ 248 set(name, value, filename) { 249 wm(this)[name] = [[value, filename]] 250 } 251 252 253 /** 254 * Iterate over all fields 255 * 256 * @return {Iterator} 257 */ 258 *values() { 259 for (let [name, value] of this) 260 yield value 261 } 262 263 264 /** 265 * Return a native (perhaps degraded) FormData with only a `append` method 266 * Can throw if it's not supported 267 * 268 * @return {FormData} 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 * [_blob description] 282 * 283 * @return {Blob} [description] 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 * The class itself is iterable 314 * alias for formdata.entries() 315 * 316 * @return {Iterator} 317 */ 318 [Symbol.iterator]() { 319 return this.entries() 320 } 321 322 323 /** 324 * Create the default string description. 325 * 326 * @return {String} [object FormData] 327 */ 328 toString() { 329 return '[object FormData]' 330 } 331 } 332 333 334 if (stringTag) { 335 /** 336 * Create the default string description. 337 * It is accessed internally by the Object.prototype.toString(). 338 * 339 * @return {String} FormData 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 // Patch xhr's send method to call _blob transparently 361 if (_send) { 362 XMLHttpRequest.prototype.send = function(data) { 363 // I would check if Content-Type isn't already set 364 // But xhr lacks getRequestHeaders functionallity 365 // https://github.com/jimmywarting/FormData/issues/44 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 // Patch fetch's function to call _blob transparently 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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Mon Jan 7 01:00:05 2019 | Cross-referenced by PHPXref 0.7.1 |