Spaces:
Runtime error
Runtime error
| // This file implements some extremely terrible tricks to monitor project loading progress. | |
| // Please don't use this as a reference for "good JS code" | |
| let total = 0; | |
| let complete = 0; | |
| // 0 - none | |
| // 1 - load json | |
| // 2 - load assets | |
| let state = 0; | |
| let currentProgress = 0; | |
| let progressHandler = (state, progress, complete, total) => {}; | |
| export const setProgressHandler = newHandler => { | |
| progressHandler = newHandler; | |
| progressHandler(state, currentProgress, complete, total); | |
| }; | |
| let progressHandlerTimeout = null; | |
| const fireProgressHandler = () => { | |
| progressHandler(state, currentProgress, complete, total); | |
| progressHandlerTimeout = null; | |
| }; | |
| const queueProgressHandlerUpdate = () => { | |
| if (progressHandlerTimeout === null) { | |
| progressHandlerTimeout = requestAnimationFrame(fireProgressHandler); | |
| } | |
| }; | |
| const setProgress = progress => { | |
| if (progress < 0) { | |
| progress = 0; | |
| } | |
| if (progress > 1) { | |
| progress = 1; | |
| } | |
| currentProgress = progress; | |
| queueProgressHandlerUpdate(); | |
| }; | |
| const setState = newState => { | |
| if (state === newState) { | |
| return; | |
| } | |
| state = newState; | |
| complete = 0; | |
| total = 0; | |
| setProgress(0); | |
| }; | |
| export const fetchWithProgress = url => { | |
| setState(1); | |
| return new Promise((resolve, reject) => { | |
| // fetch() does not support progress, so we use XMLHttpRequest | |
| const xhr = new XMLHttpRequest(); | |
| xhr.responseType = 'blob'; | |
| xhr.onload = () => { | |
| resolve(new Response(xhr.response, { | |
| status: xhr.status, | |
| statusText: xhr.statusText | |
| })); | |
| }; | |
| xhr.onloadend = () => setProgress(1); | |
| xhr.onerror = () => reject(new Error('[tw-progress-monitor] xhr failed with status' + xhr.status)); | |
| xhr.onprogress = e => { | |
| if (e.lengthComputable) { | |
| setProgress(e.loaded / e.total); | |
| } | |
| }; | |
| xhr.open('GET', url); | |
| xhr.send(); | |
| }); | |
| }; | |
| // Scratch uses fetch() to download the project JSON, so we override it to monitor when the project is being downloaded. | |
| const originalFetch = window.fetch; | |
| window.fetch = (url, opts) => { | |
| const isGET = typeof opts === 'object' && opts && opts.method === 'GET'; | |
| const isProjectURL = typeof url === 'string' && /^https:\/\/projects\.scratch\.mit\.edu\/\d+$/.test(url); | |
| if (isGET && isProjectURL) { | |
| return fetchWithProgress(url); | |
| } | |
| return originalFetch(url, opts); | |
| }; | |
| const handleWorkerMessage = e => { | |
| const data = e.data; | |
| if (Array.isArray(data)) { | |
| complete += data.length; | |
| setProgress(complete / total); | |
| } | |
| }; | |
| if (window.Worker) { | |
| let downloadWorker = null; | |
| const originalPostMessage = window.Worker.prototype.postMessage; | |
| window.Worker.prototype.postMessage = function (message) { | |
| if (downloadWorker === null) { | |
| if (message && message.url && message.id && message.options) { | |
| downloadWorker = this; | |
| downloadWorker.addEventListener('message', handleWorkerMessage); | |
| } | |
| } | |
| if (downloadWorker === this) { | |
| setState(2); | |
| total++; | |
| } | |
| originalPostMessage.call(this, message); | |
| }; | |
| } | |