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": {