blob.js 3.89 KB
'use strict'
// Based on https://github.com/tmpvar/jsdom/blob/aa85b2abf07766ff7bf5c1f6daafb3726f2f2db5/lib/jsdom/living/blob.js
//
// Used under the terms of the MIT license, shown below.
//
// Copyright (c) 2010 Elijah Insua
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

const Buffer = require('safe-buffer').Buffer

const BUFFER = Symbol('buffer')
const TYPE = Symbol('type')
const CLOSED = Symbol('closed')

class Blob {
  constructor () {
    Object.defineProperty(this, Symbol.toStringTag, {
      value: 'Blob',
      writable: false,
      enumerable: false,
      configurable: true
    })

    this[CLOSED] = false
    this[TYPE] = ''

    const blobParts = arguments[0]
    const options = arguments[1]

    const buffers = []

    if (blobParts) {
      const a = blobParts
      const length = Number(a.length)
      for (let i = 0; i < length; i++) {
        const element = a[i]
        let buffer
        if (Buffer.isBuffer(element)) {
          buffer = element
        } else if (ArrayBuffer.isView(element)) {
          buffer = Buffer.from(new Uint8Array(element.buffer, element.byteOffset, element.byteLength))
        } else if (element instanceof ArrayBuffer) {
          buffer = Buffer.from(new Uint8Array(element))
        } else if (element instanceof Blob) {
          buffer = element[BUFFER]
        } else {
          buffer = Buffer.from(typeof element === 'string' ? element : String(element))
        }
        buffers.push(buffer)
      }
    }

    this[BUFFER] = Buffer.concat(buffers)

    let type = options && options.type !== undefined && String(options.type).toLowerCase()
    if (type && !/[^\u0020-\u007E]/.test(type)) {
      this[TYPE] = type
    }
  }
  get size () {
    return this[CLOSED] ? 0 : this[BUFFER].length
  }
  get type () {
    return this[TYPE]
  }
  get isClosed () {
    return this[CLOSED]
  }
  slice () {
    const size = this.size

    const start = arguments[0]
    const end = arguments[1]
    let relativeStart, relativeEnd
    if (start === undefined) {
      relativeStart = 0
    } else if (start < 0) {
      relativeStart = Math.max(size + start, 0)
    } else {
      relativeStart = Math.min(start, size)
    }
    if (end === undefined) {
      relativeEnd = size
    } else if (end < 0) {
      relativeEnd = Math.max(size + end, 0)
    } else {
      relativeEnd = Math.min(end, size)
    }
    const span = Math.max(relativeEnd - relativeStart, 0)

    const buffer = this[BUFFER]
    const slicedBuffer = buffer.slice(
      relativeStart,
      relativeStart + span
    )
    const blob = new Blob([], { type: arguments[2] })
    blob[BUFFER] = slicedBuffer
    blob[CLOSED] = this[CLOSED]
    return blob
  }
  close () {
    this[CLOSED] = true
  }
}
exports = module.exports = Blob
exports.BUFFER = BUFFER

Object.defineProperty(Blob.prototype, Symbol.toStringTag, {
  value: 'BlobPrototype',
  writable: false,
  enumerable: false,
  configurable: true
})