wp/wp-includes/js/dist/vendor/wp-polyfill-formdata.js
changeset 9 177826044cd9
child 18 be944660c56a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wp/wp-includes/js/dist/vendor/wp-polyfill-formdata.js	Mon Oct 14 18:28:13 2019 +0200
@@ -0,0 +1,401 @@
+if (typeof FormData === 'undefined' || !FormData.prototype.keys) {
+  const global = typeof window === 'object'
+    ? window : typeof self === 'object'
+    ? self : this
+
+  // keep a reference to native implementation
+  const _FormData = global.FormData
+
+  // To be monkey patched
+  const _send = global.XMLHttpRequest && global.XMLHttpRequest.prototype.send
+  const _fetch = global.Request && global.fetch
+
+  // Unable to patch Request constructor correctly
+  // const _Request = global.Request
+  // only way is to use ES6 class extend
+  // https://github.com/babel/babel/issues/1966
+
+  const stringTag = global.Symbol && Symbol.toStringTag
+  const map = new WeakMap
+  const wm = o => map.get(o)
+  const arrayFrom = Array.from || (obj => [].slice.call(obj))
+
+  // Add missing stringTags to blob and files
+  if (stringTag) {
+    if (!Blob.prototype[stringTag]) {
+      Blob.prototype[stringTag] = 'Blob'
+    }
+
+    if ('File' in global && !File.prototype[stringTag]) {
+      File.prototype[stringTag] = 'File'
+    }
+  }
+
+  // Fix so you can construct your own File
+  try {
+    new File([], '')
+  } catch (a) {
+    global.File = function(b, d, c) {
+      const blob = new Blob(b, c)
+      const t = c && void 0 !== c.lastModified ? new Date(c.lastModified) : new Date
+
+      Object.defineProperties(blob, {
+        name: {
+          value: d
+        },
+        lastModifiedDate: {
+          value: t
+        },
+        lastModified: {
+          value: +t
+        },
+        toString: {
+          value() {
+            return '[object File]'
+          }
+        }
+      })
+
+      if (stringTag) {
+        Object.defineProperty(blob, stringTag, {
+          value: 'File'
+        })
+      }
+
+      return blob
+    }
+  }
+
+  function normalizeValue([value, filename]) {
+    if (value instanceof Blob)
+      // Should always returns a new File instance
+      // console.assert(fd.get(x) !== fd.get(x))
+      value = new File([value], filename, {
+        type: value.type,
+        lastModified: value.lastModified
+      })
+
+    return value
+  }
+
+  function stringify(name) {
+    if (!arguments.length)
+      throw new TypeError('1 argument required, but only 0 present.')
+
+    return [name + '']
+  }
+
+  function normalizeArgs(name, value, filename) {
+    if (arguments.length < 2)
+      throw new TypeError(
+        `2 arguments required, but only ${arguments.length} present.`
+      )
+
+    return value instanceof Blob
+      // normalize name and filename if adding an attachment
+      ? [name + '', value, filename !== undefined
+        ? filename + '' // Cast filename to string if 3th arg isn't undefined
+        : typeof value.name === 'string' // if name prop exist
+          ? value.name // Use File.name
+          : 'blob'] // otherwise fallback to Blob
+
+      // If no attachment, just cast the args to strings
+      : [name + '', value + '']
+  }
+
+  function each (arr, cb) {
+    for (let i = 0; i < arr.length; i++) {
+      cb(arr[i])
+    }
+  }
+
+  /**
+   * @implements {Iterable}
+   */
+  class FormDataPolyfill {
+
+    /**
+     * FormData class
+     *
+     * @param {HTMLElement=} form
+     */
+    constructor(form) {
+      map.set(this, Object.create(null))
+
+      if (!form)
+        return this
+
+      const self = this
+
+      each(form.elements, elm => {
+        if (!elm.name || elm.disabled || elm.type === 'submit' || elm.type === 'button') return
+
+        if (elm.type === 'file') {
+          each(elm.files || [], file => {
+            self.append(elm.name, file)
+          })
+        } else if (elm.type === 'select-multiple' || elm.type === 'select-one') {
+          each(elm.options, opt => {
+            !opt.disabled && opt.selected && self.append(elm.name, opt.value)
+          })
+        } else if (elm.type === 'checkbox' || elm.type === 'radio') {
+          if (elm.checked) self.append(elm.name, elm.value)
+        } else {
+          self.append(elm.name, elm.value)
+        }
+      })
+    }
+
+
+    /**
+     * Append a field
+     *
+     * @param   {String}           name      field name
+     * @param   {String|Blob|File} value     string / blob / file
+     * @param   {String=}          filename  filename to use with blob
+     * @return  {Undefined}
+     */
+    append(name, value, filename) {
+      const map = wm(this)
+
+      if (!map[name])
+        map[name] = []
+
+      map[name].push([value, filename])
+    }
+
+
+    /**
+     * Delete all fields values given name
+     *
+     * @param   {String}  name  Field name
+     * @return  {Undefined}
+     */
+    delete(name) {
+      delete wm(this)[name]
+    }
+
+
+    /**
+     * Iterate over all fields as [name, value]
+     *
+     * @return {Iterator}
+     */
+    *entries() {
+      const map = wm(this)
+
+      for (let name in map)
+        for (let value of map[name])
+          yield [name, normalizeValue(value)]
+    }
+
+    /**
+     * Iterate over all fields
+     *
+     * @param   {Function}  callback  Executed for each item with parameters (value, name, thisArg)
+     * @param   {Object=}   thisArg   `this` context for callback function
+     * @return  {Undefined}
+     */
+    forEach(callback, thisArg) {
+      for (let [name, value] of this)
+        callback.call(thisArg, value, name, this)
+    }
+
+
+    /**
+     * Return first field value given name
+     * or null if non existen
+     *
+     * @param   {String}  name      Field name
+     * @return  {String|File|null}  value Fields value
+     */
+    get(name) {
+      const map = wm(this)
+      return map[name] ? normalizeValue(map[name][0]) : null
+    }
+
+
+    /**
+     * Return all fields values given name
+     *
+     * @param   {String}  name  Fields name
+     * @return  {Array}         [{String|File}]
+     */
+    getAll(name) {
+      return (wm(this)[name] || []).map(normalizeValue)
+    }
+
+
+    /**
+     * Check for field name existence
+     *
+     * @param   {String}   name  Field name
+     * @return  {boolean}
+     */
+    has(name) {
+      return name in wm(this)
+    }
+
+
+    /**
+     * Iterate over all fields name
+     *
+     * @return {Iterator}
+     */
+    *keys() {
+      for (let [name] of this)
+        yield name
+    }
+
+
+    /**
+     * Overwrite all values given name
+     *
+     * @param   {String}    name      Filed name
+     * @param   {String}    value     Field value
+     * @param   {String=}   filename  Filename (optional)
+     * @return  {Undefined}
+     */
+    set(name, value, filename) {
+      wm(this)[name] = [[value, filename]]
+    }
+
+
+    /**
+     * Iterate over all fields
+     *
+     * @return {Iterator}
+     */
+    *values() {
+      for (let [name, value] of this)
+        yield value
+    }
+
+
+    /**
+     * Return a native (perhaps degraded) FormData with only a `append` method
+     * Can throw if it's not supported
+     *
+     * @return {FormData}
+     */
+    ['_asNative']() {
+      const fd = new _FormData
+
+      for (let [name, value] of this)
+        fd.append(name, value)
+
+      return fd
+    }
+
+
+    /**
+     * [_blob description]
+     *
+     * @return {Blob} [description]
+     */
+    ['_blob']() {
+      const boundary = '----formdata-polyfill-' + Math.random()
+      const chunks = []
+
+      for (let [name, value] of this) {
+        chunks.push(`--${boundary}\r\n`)
+
+        if (value instanceof Blob) {
+          chunks.push(
+            `Content-Disposition: form-data; name="${name}"; filename="${value.name}"\r\n`,
+            `Content-Type: ${value.type || 'application/octet-stream'}\r\n\r\n`,
+            value,
+            '\r\n'
+          )
+        } else {
+          chunks.push(
+            `Content-Disposition: form-data; name="${name}"\r\n\r\n${value}\r\n`
+          )
+        }
+      }
+
+      chunks.push(`--${boundary}--`)
+
+      return new Blob(chunks, {type: 'multipart/form-data; boundary=' + boundary})
+    }
+
+
+    /**
+     * The class itself is iterable
+     * alias for formdata.entries()
+     *
+     * @return  {Iterator}
+     */
+    [Symbol.iterator]() {
+      return this.entries()
+    }
+
+
+    /**
+     * Create the default string description.
+     *
+     * @return  {String} [object FormData]
+     */
+    toString() {
+      return '[object FormData]'
+    }
+  }
+
+
+  if (stringTag) {
+    /**
+     * Create the default string description.
+     * It is accessed internally by the Object.prototype.toString().
+     *
+     * @return {String} FormData
+     */
+    FormDataPolyfill.prototype[stringTag] = 'FormData'
+  }
+
+  const decorations = [
+    ['append', normalizeArgs],
+    ['delete', stringify],
+    ['get',    stringify],
+    ['getAll', stringify],
+    ['has',    stringify],
+    ['set',    normalizeArgs]
+  ]
+
+  decorations.forEach(arr => {
+    const orig = FormDataPolyfill.prototype[arr[0]]
+    FormDataPolyfill.prototype[arr[0]] = function() {
+      return orig.apply(this, arr[1].apply(this, arrayFrom(arguments)))
+    }
+  })
+
+  // Patch xhr's send method to call _blob transparently
+  if (_send) {
+    XMLHttpRequest.prototype.send = function(data) {
+      // I would check if Content-Type isn't already set
+      // But xhr lacks getRequestHeaders functionallity
+      // https://github.com/jimmywarting/FormData/issues/44
+      if (data instanceof FormDataPolyfill) {
+        const blob = data['_blob']()
+        this.setRequestHeader('Content-Type', blob.type)
+        _send.call(this, blob)
+      } else {
+        _send.call(this, data)
+      }
+    }
+  }
+
+  // Patch fetch's function to call _blob transparently
+  if (_fetch) {
+    const _fetch = global.fetch
+
+    global.fetch = function(input, init) {
+      if (init && init.body && init.body instanceof FormDataPolyfill) {
+        init.body = init.body['_blob']()
+      }
+
+      return _fetch(input, init)
+    }
+  }
+
+  global['FormData'] = FormDataPolyfill
+}