* options.buffer isn't a valid option * truly private internal properties * 100% test coverage
122 lines
2.6 KiB
JavaScript
122 lines
2.6 KiB
JavaScript
// Based on https://github.com/tmpvar/jsdom/blob/aa85b2abf07766ff7bf5c1f6daafb3726f2f2db5/lib/jsdom/living/blob.js
|
|
// (MIT licensed)
|
|
|
|
const {Readable: ReadableStream} = require('stream');
|
|
|
|
const wm = new WeakMap();
|
|
|
|
class Blob {
|
|
constructor(blobParts = [], options = {type: ''}) {
|
|
const buffers = [];
|
|
let size = 0;
|
|
|
|
blobParts.forEach(element => {
|
|
let buffer;
|
|
if (element instanceof Buffer) {
|
|
buffer = element;
|
|
} else if (ArrayBuffer.isView(element)) {
|
|
buffer = Buffer.from(element.buffer, element.byteOffset, element.byteLength);
|
|
} else if (element instanceof ArrayBuffer) {
|
|
buffer = Buffer.from(element);
|
|
} else if (element instanceof Blob) {
|
|
buffer = wm.get(element).buffer;
|
|
} else {
|
|
buffer = Buffer.from(typeof element === 'string' ? element : String(element));
|
|
}
|
|
|
|
size += buffer.length;
|
|
buffers.push(buffer);
|
|
});
|
|
|
|
const buffer = Buffer.concat(buffers, size);
|
|
|
|
const type = options.type === undefined ? '' : String(options.type).toLowerCase();
|
|
|
|
wm.set(this, {
|
|
type: /[^\u0020-\u007E]/.test(type) ? '' : type,
|
|
size,
|
|
buffer
|
|
});
|
|
}
|
|
|
|
get size() {
|
|
return wm.get(this).size;
|
|
}
|
|
|
|
get type() {
|
|
return wm.get(this).type;
|
|
}
|
|
|
|
text() {
|
|
return Promise.resolve(wm.get(this).buffer.toString());
|
|
}
|
|
|
|
arrayBuffer() {
|
|
const buf = wm.get(this).buffer;
|
|
const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
return Promise.resolve(ab);
|
|
}
|
|
|
|
stream() {
|
|
const readable = new ReadableStream();
|
|
readable._read = () => { };
|
|
readable.push(wm.get(this).buffer);
|
|
readable.push(null);
|
|
return readable;
|
|
}
|
|
|
|
toString() {
|
|
return '[object Blob]';
|
|
}
|
|
|
|
slice(...args) {
|
|
const {size} = this;
|
|
|
|
const start = args[0];
|
|
const end = args[1];
|
|
let relativeStart;
|
|
let 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 slicedBuffer = wm.get(this).buffer.slice(
|
|
relativeStart,
|
|
relativeStart + span
|
|
);
|
|
const blob = new Blob([], {type: args[2]});
|
|
const _ = wm.get(blob);
|
|
_.buffer = slicedBuffer;
|
|
return blob;
|
|
}
|
|
}
|
|
|
|
Object.defineProperties(Blob.prototype, {
|
|
size: {enumerable: true},
|
|
type: {enumerable: true},
|
|
slice: {enumerable: true}
|
|
});
|
|
|
|
Object.defineProperty(Blob.prototype, Symbol.toStringTag, {
|
|
value: 'Blob',
|
|
writable: false,
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
|
|
module.exports = Blob;
|