penguinmod-editor-2 / src /components /loader /tw-progress-monitor.js
soiz1's picture
Upload 2891 files
6bcb42f verified
// 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);
};
}