[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

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

   1  /* global FormData self Blob File */
   2  /* eslint-disable no-inner-declarations */
   3  
   4  if (typeof Blob !== 'undefined' && (typeof FormData === 'undefined' || !FormData.prototype.keys)) {
   5    const global = typeof globalThis === 'object'
   6      ? globalThis
   7      : typeof window === 'object'
   8        ? window
   9        : typeof self === 'object' ? self : this
  10  
  11    // keep a reference to native implementation
  12    const _FormData = global.FormData
  13  
  14    // To be monkey patched
  15    const _send = global.XMLHttpRequest && global.XMLHttpRequest.prototype.send
  16    const _fetch = global.Request && global.fetch
  17    const _sendBeacon = global.navigator && global.navigator.sendBeacon
  18    // Might be a worker thread...
  19    const _match = global.Element && global.Element.prototype
  20  
  21    // Unable to patch Request/Response constructor correctly #109
  22    // only way is to use ES6 class extend
  23    // https://github.com/babel/babel/issues/1966
  24  
  25    const stringTag = global.Symbol && Symbol.toStringTag
  26  
  27    // Add missing stringTags to blob and files
  28    if (stringTag) {
  29      if (!Blob.prototype[stringTag]) {
  30        Blob.prototype[stringTag] = 'Blob'
  31      }
  32  
  33      if ('File' in global && !File.prototype[stringTag]) {
  34        File.prototype[stringTag] = 'File'
  35      }
  36    }
  37  
  38    // Fix so you can construct your own File
  39    try {
  40      new File([], '') // eslint-disable-line
  41    } catch (a) {
  42      global.File = function File (b, d, c) {
  43        const blob = new Blob(b, c)
  44        const t = c && void 0 !== c.lastModified ? new Date(c.lastModified) : new Date()
  45  
  46        Object.defineProperties(blob, {
  47          name: {
  48            value: d
  49          },
  50          lastModifiedDate: {
  51            value: t
  52          },
  53          lastModified: {
  54            value: +t
  55          },
  56          toString: {
  57            value () {
  58              return '[object File]'
  59            }
  60          }
  61        })
  62  
  63        if (stringTag) {
  64          Object.defineProperty(blob, stringTag, {
  65            value: 'File'
  66          })
  67        }
  68  
  69        return blob
  70      }
  71    }
  72  
  73    function normalizeValue ([name, value, filename]) {
  74      if (value instanceof Blob) {
  75        // Should always returns a new File instance
  76        // console.assert(fd.get(x) !== fd.get(x))
  77        value = new File([value], filename, {
  78          type: value.type,
  79          lastModified: value.lastModified
  80        })
  81      }
  82  
  83      return [name, value]
  84    }
  85  
  86    function ensureArgs (args, expected) {
  87      if (args.length < expected) {
  88        throw new TypeError(`$expected} argument required, but only $args.length} present.`)
  89      }
  90    }
  91  
  92    function normalizeArgs (name, value, filename) {
  93      return value instanceof Blob
  94        // normalize name and filename if adding an attachment
  95        ? [String(name), value, filename !== undefined
  96          ? filename + '' // Cast filename to string if 3th arg isn't undefined
  97          : typeof value.name === 'string' // if name prop exist
  98            ? value.name // Use File.name
  99            : 'blob'] // otherwise fallback to Blob
 100  
 101        // If no attachment, just cast the args to strings
 102        : [String(name), String(value)]
 103    }
 104  
 105    // normalize linefeeds for textareas
 106    // https://html.spec.whatwg.org/multipage/form-elements.html#textarea-line-break-normalisation-transformation
 107    function normalizeLinefeeds (value) {
 108      return value.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n')
 109    }
 110  
 111    function each (arr, cb) {
 112      for (let i = 0; i < arr.length; i++) {
 113        cb(arr[i])
 114      }
 115    }
 116  
 117    /**
 118     * @implements {Iterable}
 119     */
 120    class FormDataPolyfill {
 121      /**
 122       * FormData class
 123       *
 124       * @param {HTMLElement=} form
 125       */
 126      constructor (form) {
 127        this._data = []
 128  
 129        const self = this
 130  
 131        form && each(form.elements, elm => {
 132          if (
 133            !elm.name ||
 134            elm.disabled ||
 135            elm.type === 'submit' ||
 136            elm.type === 'button' ||
 137            elm.matches('form fieldset[disabled] *')
 138          ) return
 139  
 140          if (elm.type === 'file') {
 141            const files = elm.files && elm.files.length
 142              ? elm.files
 143              : [new File([], '', { type: 'application/octet-stream' })] // #78
 144  
 145            each(files, file => {
 146              self.append(elm.name, file)
 147            })
 148          } else if (elm.type === 'select-multiple' || elm.type === 'select-one') {
 149            each(elm.options, opt => {
 150              !opt.disabled && opt.selected && self.append(elm.name, opt.value)
 151            })
 152          } else if (elm.type === 'checkbox' || elm.type === 'radio') {
 153            if (elm.checked) self.append(elm.name, elm.value)
 154          } else {
 155            const value = elm.type === 'textarea' ? normalizeLinefeeds(elm.value) : elm.value
 156            self.append(elm.name, value)
 157          }
 158        })
 159      }
 160  
 161      /**
 162       * Append a field
 163       *
 164       * @param   {string}           name      field name
 165       * @param   {string|Blob|File} value     string / blob / file
 166       * @param   {string=}          filename  filename to use with blob
 167       * @return  {undefined}
 168       */
 169      append (name, value, filename) {
 170        ensureArgs(arguments, 2)
 171        this._data.push(normalizeArgs(name, value, filename))
 172      }
 173  
 174      /**
 175       * Delete all fields values given name
 176       *
 177       * @param   {string}  name  Field name
 178       * @return  {undefined}
 179       */
 180      delete (name) {
 181        ensureArgs(arguments, 1)
 182        const result = []
 183        name = String(name)
 184  
 185        each(this._data, entry => {
 186          entry[0] !== name && result.push(entry)
 187        })
 188  
 189        this._data = result
 190      }
 191  
 192      /**
 193       * Iterate over all fields as [name, value]
 194       *
 195       * @return {Iterator}
 196       */
 197      * entries () {
 198        for (var i = 0; i < this._data.length; i++) {
 199          yield normalizeValue(this._data[i])
 200        }
 201      }
 202  
 203      /**
 204       * Iterate over all fields
 205       *
 206       * @param   {Function}  callback  Executed for each item with parameters (value, name, thisArg)
 207       * @param   {Object=}   thisArg   `this` context for callback function
 208       * @return  {undefined}
 209       */
 210      forEach (callback, thisArg) {
 211        ensureArgs(arguments, 1)
 212        for (const [name, value] of this) {
 213          callback.call(thisArg, value, name, this)
 214        }
 215      }
 216  
 217      /**
 218       * Return first field value given name
 219       * or null if non existen
 220       *
 221       * @param   {string}  name      Field name
 222       * @return  {string|File|null}  value Fields value
 223       */
 224      get (name) {
 225        ensureArgs(arguments, 1)
 226        const entries = this._data
 227        name = String(name)
 228        for (let i = 0; i < entries.length; i++) {
 229          if (entries[i][0] === name) {
 230            return normalizeValue(entries[i])[1]
 231          }
 232        }
 233        return null
 234      }
 235  
 236      /**
 237       * Return all fields values given name
 238       *
 239       * @param   {string}  name  Fields name
 240       * @return  {Array}         [{String|File}]
 241       */
 242      getAll (name) {
 243        ensureArgs(arguments, 1)
 244        const result = []
 245        name = String(name)
 246        each(this._data, data => {
 247          data[0] === name && result.push(normalizeValue(data)[1])
 248        })
 249  
 250        return result
 251      }
 252  
 253      /**
 254       * Check for field name existence
 255       *
 256       * @param   {string}   name  Field name
 257       * @return  {boolean}
 258       */
 259      has (name) {
 260        ensureArgs(arguments, 1)
 261        name = String(name)
 262        for (let i = 0; i < this._data.length; i++) {
 263          if (this._data[i][0] === name) {
 264            return true
 265          }
 266        }
 267        return false
 268      }
 269  
 270      /**
 271       * Iterate over all fields name
 272       *
 273       * @return {Iterator}
 274       */
 275      * keys () {
 276        for (const [name] of this) {
 277          yield name
 278        }
 279      }
 280  
 281      /**
 282       * Overwrite all values given name
 283       *
 284       * @param   {string}    name      Filed name
 285       * @param   {string}    value     Field value
 286       * @param   {string=}   filename  Filename (optional)
 287       * @return  {undefined}
 288       */
 289      set (name, value, filename) {
 290        ensureArgs(arguments, 2)
 291        name = String(name)
 292        const result = []
 293        const args = normalizeArgs(name, value, filename)
 294        let replace = true
 295  
 296        // - replace the first occurrence with same name
 297        // - discards the remaning with same name
 298        // - while keeping the same order items where added
 299        each(this._data, data => {
 300          data[0] === name
 301            ? replace && (replace = !result.push(args))
 302            : result.push(data)
 303        })
 304  
 305        replace && result.push(args)
 306  
 307        this._data = result
 308      }
 309  
 310      /**
 311       * Iterate over all fields
 312       *
 313       * @return {Iterator}
 314       */
 315      * values () {
 316        for (const [, value] of this) {
 317          yield value
 318        }
 319      }
 320  
 321      /**
 322       * Return a native (perhaps degraded) FormData with only a `append` method
 323       * Can throw if it's not supported
 324       *
 325       * @return {FormData}
 326       */
 327      ['_asNative'] () {
 328        const fd = new _FormData()
 329  
 330        for (const [name, value] of this) {
 331          fd.append(name, value)
 332        }
 333  
 334        return fd
 335      }
 336  
 337      /**
 338       * [_blob description]
 339       *
 340       * @return {Blob} [description]
 341       */
 342      ['_blob'] () {
 343        const boundary = '----formdata-polyfill-' + Math.random()
 344        const chunks = []
 345  
 346        for (const [name, value] of this) {
 347          chunks.push(`--$boundary}\r\n`)
 348  
 349          if (value instanceof Blob) {
 350            chunks.push(
 351              `Content-Disposition: form-data; name="$name}"; filename="$value.name}"\r\n` +
 352              `Content-Type: $value.type || 'application/octet-stream'}\r\n\r\n`,
 353              value,
 354              '\r\n'
 355            )
 356          } else {
 357            chunks.push(
 358              `Content-Disposition: form-data; name="$name}"\r\n\r\n$value}\r\n`
 359            )
 360          }
 361        }
 362  
 363        chunks.push(`--$boundary}--`)
 364  
 365        return new Blob(chunks, {
 366          type: 'multipart/form-data; boundary=' + boundary
 367        })
 368      }
 369  
 370      /**
 371       * The class itself is iterable
 372       * alias for formdata.entries()
 373       *
 374       * @return  {Iterator}
 375       */
 376      [Symbol.iterator] () {
 377        return this.entries()
 378      }
 379  
 380      /**
 381       * Create the default string description.
 382       *
 383       * @return  {string} [object FormData]
 384       */
 385      toString () {
 386        return '[object FormData]'
 387      }
 388    }
 389  
 390    if (_match && !_match.matches) {
 391      _match.matches =
 392        _match.matchesSelector ||
 393        _match.mozMatchesSelector ||
 394        _match.msMatchesSelector ||
 395        _match.oMatchesSelector ||
 396        _match.webkitMatchesSelector ||
 397        function (s) {
 398          var matches = (this.document || this.ownerDocument).querySelectorAll(s)
 399          var i = matches.length
 400          while (--i >= 0 && matches.item(i) !== this) {}
 401          return i > -1
 402        }
 403    }
 404  
 405    if (stringTag) {
 406      /**
 407       * Create the default string description.
 408       * It is accessed internally by the Object.prototype.toString().
 409       */
 410      FormDataPolyfill.prototype[stringTag] = 'FormData'
 411    }
 412  
 413    // Patch xhr's send method to call _blob transparently
 414    if (_send) {
 415      const setRequestHeader = global.XMLHttpRequest.prototype.setRequestHeader
 416  
 417      global.XMLHttpRequest.prototype.setRequestHeader = function (name, value) {
 418        setRequestHeader.call(this, name, value)
 419        if (name.toLowerCase() === 'content-type') this._hasContentType = true
 420      }
 421  
 422      global.XMLHttpRequest.prototype.send = function (data) {
 423        // need to patch send b/c old IE don't send blob's type (#44)
 424        if (data instanceof FormDataPolyfill) {
 425          const blob = data['_blob']()
 426          if (!this._hasContentType) this.setRequestHeader('Content-Type', blob.type)
 427          _send.call(this, blob)
 428        } else {
 429          _send.call(this, data)
 430        }
 431      }
 432    }
 433  
 434    // Patch fetch's function to call _blob transparently
 435    if (_fetch) {
 436      global.fetch = function (input, init) {
 437        if (init && init.body && init.body instanceof FormDataPolyfill) {
 438          init.body = init.body['_blob']()
 439        }
 440  
 441        return _fetch.call(this, input, init)
 442      }
 443    }
 444  
 445    // Patch navigator.sendBeacon to use native FormData
 446    if (_sendBeacon) {
 447      global.navigator.sendBeacon = function (url, data) {
 448        if (data instanceof FormDataPolyfill) {
 449          data = data['_asNative']()
 450        }
 451        return _sendBeacon.call(this, url, data)
 452      }
 453    }
 454  
 455    global['FormData'] = FormDataPolyfill
 456  }


Generated: Mon Apr 19 01:00:04 2021 Cross-referenced by PHPXref 0.7.1