libs/Blob.js

  1. /**
  2. * @license
  3. * Blob.js
  4. * A Blob, File, FileReader & URL implementation.
  5. * 2018-08-09
  6. *
  7. * By Eli Grey, http://eligrey.com
  8. * By Jimmy Wärting, https://github.com/jimmywarting
  9. * License: MIT
  10. * See https://github.com/eligrey/Blob.js/blob/master/LICENSE.md
  11. */
  12. import { globalObject as global } from "./globalObject.js";
  13. var BlobBuilder =
  14. global.BlobBuilder ||
  15. global.WebKitBlobBuilder ||
  16. global.MSBlobBuilder ||
  17. global.MozBlobBuilder;
  18. global.URL =
  19. global.URL ||
  20. global.webkitURL ||
  21. function(href, a) {
  22. a = document.createElement("a");
  23. a.href = href;
  24. return a;
  25. };
  26. var origBlob = global.Blob;
  27. var createObjectURL = URL.createObjectURL;
  28. var revokeObjectURL = URL.revokeObjectURL;
  29. var strTag = global.Symbol && global.Symbol.toStringTag;
  30. var blobSupported = false;
  31. var blobSupportsArrayBufferView = false;
  32. var arrayBufferSupported = !!global.ArrayBuffer;
  33. var blobBuilderSupported =
  34. BlobBuilder && BlobBuilder.prototype.append && BlobBuilder.prototype.getBlob;
  35. try {
  36. // Check if Blob constructor is supported
  37. blobSupported = new Blob(["ä"]).size === 2;
  38. // Check if Blob constructor supports ArrayBufferViews
  39. // Fails in Safari 6, so we need to map to ArrayBuffers there.
  40. blobSupportsArrayBufferView = new Blob([new Uint8Array([1, 2])]).size === 2;
  41. } catch (e) {}
  42. /**
  43. * Helper function that maps ArrayBufferViews to ArrayBuffers
  44. * Used by BlobBuilder constructor and old browsers that didn't
  45. * support it in the Blob constructor.
  46. */
  47. function mapArrayBufferViews(ary) {
  48. return ary.map(function(chunk) {
  49. if (chunk.buffer instanceof ArrayBuffer) {
  50. var buf = chunk.buffer;
  51. // if this is a subarray, make a copy so we only
  52. // include the subarray region from the underlying buffer
  53. if (chunk.byteLength !== buf.byteLength) {
  54. var copy = new Uint8Array(chunk.byteLength);
  55. copy.set(new Uint8Array(buf, chunk.byteOffset, chunk.byteLength));
  56. buf = copy.buffer;
  57. }
  58. return buf;
  59. }
  60. return chunk;
  61. });
  62. }
  63. function BlobBuilderConstructor(ary, options) {
  64. options = options || {};
  65. var bb = new BlobBuilder();
  66. mapArrayBufferViews(ary).forEach(function(part) {
  67. bb.append(part);
  68. });
  69. return options.type ? bb.getBlob(options.type) : bb.getBlob();
  70. }
  71. function BlobConstructor(ary, options) {
  72. return new origBlob(mapArrayBufferViews(ary), options || {});
  73. }
  74. if (global.Blob) {
  75. BlobBuilderConstructor.prototype = Blob.prototype;
  76. BlobConstructor.prototype = Blob.prototype;
  77. }
  78. function FakeBlobBuilder() {
  79. function toUTF8Array(str) {
  80. var utf8 = [];
  81. for (var i = 0; i < str.length; i++) {
  82. var charcode = str.charCodeAt(i);
  83. if (charcode < 0x80) utf8.push(charcode);
  84. else if (charcode < 0x800) {
  85. utf8.push(0xc0 | (charcode >> 6), 0x80 | (charcode & 0x3f));
  86. } else if (charcode < 0xd800 || charcode >= 0xe000) {
  87. utf8.push(
  88. 0xe0 | (charcode >> 12),
  89. 0x80 | ((charcode >> 6) & 0x3f),
  90. 0x80 | (charcode & 0x3f)
  91. );
  92. }
  93. // surrogate pair
  94. else {
  95. i++;
  96. // UTF-16 encodes 0x10000-0x10FFFF by
  97. // subtracting 0x10000 and splitting the
  98. // 20 bits of 0x0-0xFFFFF into two halves
  99. charcode =
  100. 0x10000 + (((charcode & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff));
  101. utf8.push(
  102. 0xf0 | (charcode >> 18),
  103. 0x80 | ((charcode >> 12) & 0x3f),
  104. 0x80 | ((charcode >> 6) & 0x3f),
  105. 0x80 | (charcode & 0x3f)
  106. );
  107. }
  108. }
  109. return utf8;
  110. }
  111. function fromUtf8Array(array) {
  112. var out, i, len, c;
  113. var char2, char3;
  114. out = "";
  115. len = array.length;
  116. i = 0;
  117. while (i < len) {
  118. c = array[i++];
  119. switch (c >> 4) {
  120. case 0:
  121. case 1:
  122. case 2:
  123. case 3:
  124. case 4:
  125. case 5:
  126. case 6:
  127. case 7:
  128. // 0xxxxxxx
  129. out += String.fromCharCode(c);
  130. break;
  131. case 12:
  132. case 13:
  133. // 110x xxxx 10xx xxxx
  134. char2 = array[i++];
  135. out += String.fromCharCode(((c & 0x1f) << 6) | (char2 & 0x3f));
  136. break;
  137. case 14:
  138. // 1110 xxxx 10xx xxxx 10xx xxxx
  139. char2 = array[i++];
  140. char3 = array[i++];
  141. out += String.fromCharCode(
  142. ((c & 0x0f) << 12) | ((char2 & 0x3f) << 6) | ((char3 & 0x3f) << 0)
  143. );
  144. break;
  145. }
  146. }
  147. return out;
  148. }
  149. function isDataView(obj) {
  150. return obj && DataView.prototype.isPrototypeOf(obj);
  151. }
  152. function bufferClone(buf) {
  153. var view = new Array(buf.byteLength);
  154. var array = new Uint8Array(buf);
  155. var i = view.length;
  156. while (i--) {
  157. view[i] = array[i];
  158. }
  159. return view;
  160. }
  161. function encodeByteArray(input) {
  162. var byteToCharMap =
  163. "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  164. var output = [];
  165. for (var i = 0; i < input.length; i += 3) {
  166. var byte1 = input[i];
  167. var haveByte2 = i + 1 < input.length;
  168. var byte2 = haveByte2 ? input[i + 1] : 0;
  169. var haveByte3 = i + 2 < input.length;
  170. var byte3 = haveByte3 ? input[i + 2] : 0;
  171. var outByte1 = byte1 >> 2;
  172. var outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4);
  173. var outByte3 = ((byte2 & 0x0f) << 2) | (byte3 >> 6);
  174. var outByte4 = byte3 & 0x3f;
  175. if (!haveByte3) {
  176. outByte4 = 64;
  177. if (!haveByte2) {
  178. outByte3 = 64;
  179. }
  180. }
  181. output.push(
  182. byteToCharMap[outByte1],
  183. byteToCharMap[outByte2],
  184. byteToCharMap[outByte3],
  185. byteToCharMap[outByte4]
  186. );
  187. }
  188. return output.join("");
  189. }
  190. var create =
  191. Object.create ||
  192. function(a) {
  193. function c() {}
  194. c.prototype = a;
  195. return new c();
  196. };
  197. if (arrayBufferSupported) {
  198. var viewClasses = [
  199. "[object Int8Array]",
  200. "[object Uint8Array]",
  201. "[object Uint8ClampedArray]",
  202. "[object Int16Array]",
  203. "[object Uint16Array]",
  204. "[object Int32Array]",
  205. "[object Uint32Array]",
  206. "[object Float32Array]",
  207. "[object Float64Array]"
  208. ];
  209. var isArrayBufferView =
  210. ArrayBuffer.isView ||
  211. function(obj) {
  212. return (
  213. obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1
  214. );
  215. };
  216. }
  217. /********************************************************/
  218. /* Blob constructor */
  219. /********************************************************/
  220. function Blob(chunks, opts) {
  221. chunks = chunks || [];
  222. for (var i = 0, len = chunks.length; i < len; i++) {
  223. var chunk = chunks[i];
  224. if (chunk instanceof Blob) {
  225. chunks[i] = chunk._buffer;
  226. } else if (typeof chunk === "string") {
  227. chunks[i] = toUTF8Array(chunk);
  228. } else if (
  229. arrayBufferSupported &&
  230. (ArrayBuffer.prototype.isPrototypeOf(chunk) || isArrayBufferView(chunk))
  231. ) {
  232. chunks[i] = bufferClone(chunk);
  233. } else if (arrayBufferSupported && isDataView(chunk)) {
  234. chunks[i] = bufferClone(chunk.buffer);
  235. } else {
  236. chunks[i] = toUTF8Array(String(chunk));
  237. }
  238. }
  239. this._buffer = [].concat.apply([], chunks);
  240. this.size = this._buffer.length;
  241. this.type = opts ? opts.type || "" : "";
  242. }
  243. Blob.prototype.slice = function(start, end, type) {
  244. var slice = this._buffer.slice(start || 0, end || this._buffer.length);
  245. return new Blob([slice], { type: type });
  246. };
  247. Blob.prototype.toString = function() {
  248. return "[object Blob]";
  249. };
  250. /********************************************************/
  251. /* File constructor */
  252. /********************************************************/
  253. function File(chunks, name, opts) {
  254. opts = opts || {};
  255. var a = Blob.call(this, chunks, opts) || this;
  256. a.name = name;
  257. a.lastModifiedDate = opts.lastModified
  258. ? new Date(opts.lastModified)
  259. : new Date();
  260. a.lastModified = +a.lastModifiedDate;
  261. return a;
  262. }
  263. File.prototype = create(Blob.prototype);
  264. File.prototype.constructor = File;
  265. if (Object.setPrototypeOf) Object.setPrototypeOf(File, Blob);
  266. else {
  267. try {
  268. File.__proto__ = Blob;
  269. } catch (e) {}
  270. }
  271. File.prototype.toString = function() {
  272. return "[object File]";
  273. };
  274. /********************************************************/
  275. /* FileReader constructor */
  276. /********************************************************/
  277. function FileReader() {
  278. if (!(this instanceof FileReader))
  279. throw new TypeError(
  280. "Failed to construct 'FileReader': Please use the 'new' operator, this DOM object constructor cannot be called as a function."
  281. );
  282. var delegate = document.createDocumentFragment();
  283. this.addEventListener = delegate.addEventListener;
  284. this.dispatchEvent = function(evt) {
  285. var local = this["on" + evt.type];
  286. if (typeof local === "function") local(evt);
  287. delegate.dispatchEvent(evt);
  288. };
  289. this.removeEventListener = delegate.removeEventListener;
  290. }
  291. function _read(fr, blob, kind) {
  292. if (!(blob instanceof Blob))
  293. throw new TypeError(
  294. "Failed to execute '" +
  295. kind +
  296. "' on 'FileReader': parameter 1 is not of type 'Blob'."
  297. );
  298. fr.result = "";
  299. setTimeout(function() {
  300. this.readyState = FileReader.LOADING;
  301. fr.dispatchEvent(new Event("load"));
  302. fr.dispatchEvent(new Event("loadend"));
  303. });
  304. }
  305. FileReader.EMPTY = 0;
  306. FileReader.LOADING = 1;
  307. FileReader.DONE = 2;
  308. FileReader.prototype.error = null;
  309. FileReader.prototype.onabort = null;
  310. FileReader.prototype.onerror = null;
  311. FileReader.prototype.onload = null;
  312. FileReader.prototype.onloadend = null;
  313. FileReader.prototype.onloadstart = null;
  314. FileReader.prototype.onprogress = null;
  315. FileReader.prototype.readAsDataURL = function(blob) {
  316. _read(this, blob, "readAsDataURL");
  317. this.result =
  318. "data:" + blob.type + ";base64," + encodeByteArray(blob._buffer);
  319. };
  320. FileReader.prototype.readAsText = function(blob) {
  321. _read(this, blob, "readAsText");
  322. this.result = fromUtf8Array(blob._buffer);
  323. };
  324. FileReader.prototype.readAsArrayBuffer = function(blob) {
  325. _read(this, blob, "readAsText");
  326. this.result = blob._buffer.slice();
  327. };
  328. FileReader.prototype.abort = function() {};
  329. /********************************************************/
  330. /* URL */
  331. /********************************************************/
  332. URL.createObjectURL = function(blob) {
  333. return blob instanceof Blob
  334. ? "data:" + blob.type + ";base64," + encodeByteArray(blob._buffer)
  335. : createObjectURL.call(URL, blob);
  336. };
  337. URL.revokeObjectURL = function(url) {
  338. revokeObjectURL && revokeObjectURL.call(URL, url);
  339. };
  340. /********************************************************/
  341. /* XHR */
  342. /********************************************************/
  343. var _send = global.XMLHttpRequest && global.XMLHttpRequest.prototype.send;
  344. if (_send) {
  345. XMLHttpRequest.prototype.send = function(data) {
  346. if (data instanceof Blob) {
  347. this.setRequestHeader("Content-Type", data.type);
  348. _send.call(this, fromUtf8Array(data._buffer));
  349. } else {
  350. _send.call(this, data);
  351. }
  352. };
  353. }
  354. global.FileReader = FileReader;
  355. global.File = File;
  356. global.Blob = Blob;
  357. }
  358. if (strTag) {
  359. try {
  360. File.prototype[strTag] = "File";
  361. Blob.prototype[strTag] = "Blob";
  362. FileReader.prototype[strTag] = "FileReader";
  363. } catch (e) {}
  364. }
  365. function fixFileAndXHR() {
  366. try {
  367. new File([], "");
  368. } catch (e) {
  369. try {
  370. var klass = new Function(
  371. "class File extends Blob {" +
  372. "constructor(chunks, name, opts) {" +
  373. "opts = opts || {};" +
  374. "super(chunks, opts || {});" +
  375. "this.name = name;" +
  376. "this.lastModifiedDate = opts.lastModified ? new Date(opts.lastModified) : new Date;" +
  377. "this.lastModified = +this.lastModifiedDate;" +
  378. "}};" +
  379. 'return new File([], ""), File'
  380. )();
  381. global.File = klass;
  382. } catch (e) {
  383. var klass = function(b, d, c) {
  384. var blob = new Blob(b, c);
  385. var t =
  386. c && void 0 !== c.lastModified
  387. ? new Date(c.lastModified)
  388. : new Date();
  389. blob.name = d;
  390. blob.lastModifiedDate = t;
  391. blob.lastModified = +t;
  392. blob.toString = function() {
  393. return "[object File]";
  394. };
  395. if (strTag) blob[strTag] = "File";
  396. return blob;
  397. };
  398. global.File = klass;
  399. }
  400. }
  401. }
  402. if (blobSupported) {
  403. fixFileAndXHR();
  404. global.Blob = blobSupportsArrayBufferView ? global.Blob : BlobConstructor;
  405. } else if (blobBuilderSupported) {
  406. fixFileAndXHR();
  407. global.Blob = BlobBuilderConstructor;
  408. } else {
  409. FakeBlobBuilder();
  410. }