diff --git a/extension-dependencies.json b/extension-dependencies.json index 74796240cd..12ed768058 100644 --- a/extension-dependencies.json +++ b/extension-dependencies.json @@ -51,6 +51,10 @@ "https://raw.githubusercontent.com/dectalk/tw-dectalk/b387df5a1133dd08a8f3af94d7899dec3f507fd8/dtc.js": { "sha256": "de29c298d3e260d348e9f8eed74e644258e8222fdf741a55665400d560876e8b", "contentType": "application/octet-stream" + }, + "https://cdn.jsdelivr.net/npm/mime-db@1.52.0/db.json": { + "sha256": "85c8e1ba609079947c8df83c092900ab0226e1d7b60e5e7105fb7dd701833263", + "contentType": "application/json; charset=utf-8" } } } \ No newline at end of file diff --git a/extensions/CST1229/zip.js b/extensions/CST1229/zip.js index 5fa3d59f1b..3fe8337cc6 100644 --- a/extensions/CST1229/zip.js +++ b/extensions/CST1229/zip.js @@ -4,12 +4,56 @@ // By: CST1229 // License: MIT AND MPL-2.0 -(function (Scratch) { +(async function (Scratch) { "use strict"; // @ts-expect-error - not typed yet const JSZip = Scratch.vm.exports.JSZip; + /*! + (The MIT License) + + Copyright (c) 2014 Jonathan Ong + Copyright (c) 2015-2022 Douglas Christopher Wilson + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + 'Software'), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + const MimeTypes = await (async function () { + try { + const dbResponse = await Scratch.external.fetch( + "https://cdn.jsdelivr.net/npm/mime-db@1.52.0/db.json" + ); + if (!dbResponse.ok) return []; + + // We need to convert this table to a lookup array with + // mime types with applicable file type extensions. + const mimeTable = await dbResponse.json(); + return Object.entries(mimeTable) + .filter((m) => m[1].extensions !== undefined) + .map((m) => [m[0], m[1].extensions]); + } catch { + console.warn("Couldnt not retrieve data from Mime DB!"); + return []; + } + })(); + const extIcon = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMCAzMCI+PHJlY3Qgd2lkdGg9IjIzIiBoZWlnaHQ9IjIwIiB4PSI2IiB5PSIzIiBmaWxsPSIjZDhkODZjIiBzdHJva2U9IiM3ZDdkMjMiIHN0cm9rZS13aWR0aD0iMiIgcGFpbnQtb3JkZXI9InN0cm9rZSBtYXJrZXJzIGZpbGwiIHJ4PSI0IiByeT0iNCIgc3R5bGU9ImZvbnQtdmFyaWF0aW9uLXNldHRpbmdzOm5vcm1hbCIvPjxyZWN0IHdpZHRoPSIyOCIgaGVpZ2h0PSIyMCIgeD0iMSIgeT0iOCIgZmlsbD0iI2Q4ZDg2YyIgc3Ryb2tlPSIjN2Q3ZDIzIiBzdHJva2Utd2lkdGg9IjIiIHBhaW50LW9yZGVyPSJzdHJva2UgbWFya2VycyBmaWxsIiByeD0iNCIgcnk9IjQiIHN0eWxlPSJmb250LXZhcmlhdGlvbi1zZXR0aW5nczpub3JtYWwiLz48cGF0aCBmaWxsPSIjN2Q3ZDIzIiBkPSJNNSAxMlY3LjU1bDQtLjAyNlYxMlptMyA0di00aDR2NHptLTMgNHYtNGg0djR6bTMgNHYtNGg0djR6bS0zIDQuMTgxVjI0aDR2NC4xNzV6IiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6bm9ybWFsIi8+PHBhdGggZmlsbD0iIzdkN2QyMyIgc3Ryb2tlPSIjN2Q3ZDIzIiBzdHJva2Utd2lkdGg9Ii4xIiBkPSJNMTAgNy4xMjNWNWgydjIuMTM2Wk0xMSA1VjIuNTYybDItLjE2MlY1WiIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOm5vcm1hbCIvPjxwYXRoIGZpbGw9IiNmZmYiIHN0cm9rZT0iIzdkN2QyMyIgc3Ryb2tlLXdpZHRoPSIyIiBkPSJNMTUuNDg3IDI0Ljh2LTEuNzY1bDUuNjczLTguNTJoLTUuNDkzVjEyLjRoOC40NTN2MS44OTdsLTUuNzExIDguMzg3aDUuNzg3VjI0Ljh6IiBhcmlhLWxhYmVsPSJaIiBmb250LWZhbWlseT0iQ29uc29sYXMiIGZvbnQtc2l6ZT0iMTkuNDMiIGZvbnQtd2VpZ2h0PSI3MDAiIHBhaW50LW9yZGVyPSJzdHJva2UgbWFya2VycyBmaWxsIiBzdHlsZT0iLWlua3NjYXBlLWZvbnQtc3BlY2lmaWNhdGlvbjomcXVvdDtDb25zb2xhcywgQm9sZCZxdW90OyIgdHJhbnNmb3JtPSJzY2FsZSgxLjAzMyAuOTY4KSIvPjwvc3ZnPg=="; @@ -719,6 +763,18 @@ if (this.zipPaths[zip] === "") this.zipPaths[zip] = "/"; } + _tryGetMIMEType(fileName) { + // Try getting the MIME based on the file name using the mime-types library. + // JSZip doesnt store this anywhere, so we have to detect this ourselves. + const FALLBACK_MIME = "application/octet-stream"; + + const fileType = fileName.split(".").pop().toLowerCase(); + if (!fileType) return FALLBACK_MIME; // shouldnt happen + + const foundEntry = MimeTypes.find((m) => m[1].includes(fileType)); + return foundEntry ? foundEntry[0] : FALLBACK_MIME; + } + /// Blocks createEmptyAs({ NAME }) { @@ -910,8 +966,11 @@ case "base64": case "data: URL": { let data = await obj.async("base64"); - if (TYPE === "data: URL") - data = "data:application/octet-stream;base64," + data; + if (TYPE === "data: URL") { + const mime = this._tryGetMIMEType(obj.name); + data = `data:${mime};base64,${data}`; + } + return data; } case "hex": {