[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

title

Body

[close]

/wp-includes/js/dist/vendor/ -> wp-polyfill-formdata.js (source)

   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  }


Generated: Sat Nov 23 20:47:33 2019 Cross-referenced by PHPXref 0.7