diff --git a/src/puter-js/src/lib/polyfills/fileReaderPoly.js b/src/puter-js/src/lib/polyfills/fileReaderPoly.js new file mode 100644 index 000000000..9a3ab26fd --- /dev/null +++ b/src/puter-js/src/lib/polyfills/fileReaderPoly.js @@ -0,0 +1,40 @@ +function toBase64FromBuffer(buffer) { + const bytes = new Uint8Array(buffer); + // use the requested reduce logic + const binary = bytes.reduce((data, byte) => data + String.fromCharCode(byte), ''); + return typeof btoa === 'function' ? btoa(binary) : Buffer.from(binary, 'binary').toString('base64'); +} + +export class FileReaderPoly { + constructor() { + this.result = null; + this.error = null; + this.onloadend = null; + } + readAsDataURL(blob) { + const self = this; + (async function () { + try { + let buffer; + if (blob && typeof blob.arrayBuffer === 'function') { + buffer = await blob.arrayBuffer(); + } else if (blob instanceof ArrayBuffer) { + buffer = blob; + } else if (ArrayBuffer.isView(blob)) { + buffer = blob.buffer; + } else { + buffer = new Uint8Array(0).buffer; + } + + const base64 = toBase64FromBuffer(buffer); + const mime = (blob && blob.type) || 'application/octet-stream'; + self.result = 'data:' + mime + ';base64,' + base64; + if (typeof self.onloadend === 'function') self.onloadend(); + } catch (err) { + self.error = err; + if (typeof self.onloadend === 'function') self.onloadend(); + } + })(); + } +} + diff --git a/src/puter-js/src/lib/utils.js b/src/puter-js/src/lib/utils.js index 7aee94fd0..6c2dbe02e 100644 --- a/src/puter-js/src/lib/utils.js +++ b/src/puter-js/src/lib/utils.js @@ -1,3 +1,5 @@ +import { FileReaderPoly } from "./polyfills/fileReaderPoly.js"; + /** * Parses a given response text into a JSON object. If the parsing fails due to invalid JSON format, * the original response text is returned. @@ -589,7 +591,7 @@ class TeePromise { async function blob_to_url (blob) { const tp = new TeePromise(); - const reader = new FileReader(); + const reader = new (globalThis.FileReader || FileReaderPoly)(); reader.onloadend = () => tp.resolve(reader.result); reader.readAsDataURL(blob); return await tp; @@ -597,7 +599,7 @@ async function blob_to_url (blob) { function blobToDataUri (blob) { return new Promise((resolve, reject) => { - const reader = new FileReader(); + const reader = new (globalThis.FileReader || FileReaderPoly)(); reader.onload = function (event) { resolve(event.target.result); }; @@ -611,7 +613,7 @@ function blobToDataUri (blob) { function arrayBufferToDataUri (arrayBuffer) { return new Promise((resolve, reject) => { const blob = new Blob([arrayBuffer]); - const reader = new FileReader(); + const reader = new (globalThis.FileReader || FileReaderPoly)(); reader.onload = function (event) { resolve(event.target.result); }; @@ -622,10 +624,6 @@ function arrayBufferToDataUri (arrayBuffer) { }); } -export { parseResponse, uuidv4, handle_resp, handle_error, initXhr, setupXhrEventHandlers, driverCall, - TeePromise, - make_driver_method, - blob_to_url, - arrayBufferToDataUri, - blobToDataUri, -}; \ No newline at end of file +export { + arrayBufferToDataUri, blob_to_url, blobToDataUri, driverCall, handle_error, handle_resp, initXhr, make_driver_method, parseResponse, setupXhrEventHandlers, TeePromise, uuidv4 +}; diff --git a/src/puter-js/src/modules/AI.js b/src/puter-js/src/modules/AI.js index b3844aa48..2a18cadb1 100644 --- a/src/puter-js/src/modules/AI.js +++ b/src/puter-js/src/modules/AI.js @@ -391,7 +391,8 @@ class AI { } else { throw { message: 'Unexpected audio response format', code: 'invalid_audio_response' }; } - const audio = new Audio(url); + const audio = new (globalThis.Audio || Object)(); + audio.src = url; audio.toString = () => url; audio.valueOf = () => url; return audio; @@ -1061,7 +1062,7 @@ class AI { } else { throw { message: 'Unexpected image response format', code: 'invalid_image_response' }; } - let img = new Image(); + let img = new (globalThis.Image || Object)(); img.src = url; img.toString = () => img.src; img.valueOf = () => img.src; @@ -1153,7 +1154,7 @@ class AI { return result; } - const video = document.createElement('video'); + const video = (globalThis.document?.createElement('video') || {setAttribute: ()=>{}}); video.src = sourceUrl; video.controls = true; video.preload = 'metadata';