Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 188 additions & 0 deletions static/extensions/XmerOriginals/dropfiles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
// Description: Manage Files Dragged and Dropped on Top of Your Standalone Project from the File Manager.
// License: MPL-2.0

class DropFiles {
constructor(runtime) {
this.runtime = runtime;
this.enabled = false;
this.dragging = false;
this.droppedFile = null;
this._boundDragOver = this._onDragOver.bind(this);
this._boundDrop = this._onDrop.bind(this);
this._boundDragLeave = this._onDragLeave.bind(this);
}

getInfo() {
return {
id: "dropfiles",
name: "Drop Files",
color1: "#05a047",
blocks: [
{
opcode: "whenFileDropped",
blockType: Scratch.BlockType.HAT,
text: "when a file is dropped",
},
{
opcode: "setEnabled",
blockType: Scratch.BlockType.COMMAND,
text: "set file drop to [STATE]",
arguments: {
STATE: {
type: Scratch.ArgumentType.STRING,
menu: "stateMenu",
},
},
},
{
opcode: "clearDroppedFile",
blockType: Scratch.BlockType.COMMAND,
text: "clear dropped file",
},
{
blockType: "label",
text: "Controls",
},
{
opcode: "isEnabled",
blockType: Scratch.BlockType.BOOLEAN,
text: "file drop is enabled?",
},
{
opcode: "isDragging",
blockType: Scratch.BlockType.BOOLEAN,
text: "a file is being dragged?",
},
{
blockType: "label",
text: "File",
},
{
opcode: "getFileContent",
blockType: Scratch.BlockType.REPORTER,
text: "get dropped file content as [FORMAT]",
arguments: {
FORMAT: {
type: Scratch.ArgumentType.STRING,
menu: "formatMenu",
},
},
},
{
opcode: "getFileInfo",
blockType: Scratch.BlockType.REPORTER,
text: "get dropped file [INFO]",
arguments: {
INFO: {
type: Scratch.ArgumentType.STRING,
menu: "infoMenu",
},
},
},
],
menus: {
stateMenu: {
acceptReporters: true,
items: ["true", "false"],
},
formatMenu: {
acceptReporters: true,
items: ["text", "data uri"],
},
infoMenu: {
acceptReporters: true,
items: ["name", "size", "type"],
},
},
};
}

setEnabled({ STATE }) {
const enable = STATE === "true";
if (enable && !this.enabled) {
window.addEventListener("dragover", this._boundDragOver);
window.addEventListener("drop", this._boundDrop);
window.addEventListener("dragleave", this._boundDragLeave);
} else if (!enable && this.enabled) {
window.removeEventListener("dragover", this._boundDragOver);
window.removeEventListener("drop", this._boundDrop);
window.removeEventListener("dragleave", this._boundDragLeave);
}
this.enabled = enable;
}

clearDroppedFile() {
this.droppedFile = null;
}

isEnabled() {
return this.enabled;
}

isDragging() {
return this.dragging;
}

getFileContent({ FORMAT }) {
if (!this.droppedFile) return "";
if (FORMAT === "data uri") {
return this._getDataURI(this.droppedFile);
} else {
return this._getText(this.droppedFile);
}
}

async _getDataURI(file) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.readAsDataURL(file);
});
}

async _getText(file) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.readAsText(file);
});
}

getFileInfo({ INFO }) {
if (!this.droppedFile) return "";
switch (INFO) {
case "name":
return this.droppedFile.name;
case "size":
return this.droppedFile.size.toString();
case "type":
return this.droppedFile.type;
}
return "";
}

whenFileDropped() {
return !!this.droppedFile;
}

_onDragOver(e) {
e.preventDefault();
this.dragging = true;
}

_onDragLeave(e) {
e.preventDefault();
this.dragging = false;
}

_onDrop(e) {
e.preventDefault();
this.dragging = false;
if (e.dataTransfer.files.length > 0) {
this.droppedFile = e.dataTransfer.files[0];
this.runtime.startHats("dropfiles_whenFileDropped");
}
}
}

Scratch.extensions.register(new DropFiles());
133 changes: 133 additions & 0 deletions static/extensions/XmerOriginals/multifileplus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Description: Quickly Process Multiple Selected Files.
// License: MPL-2.0

class MultiFileSelector {
constructor(runtime) {
this.runtime = runtime;
this.files = [];
this.fileContents = [];
}

getInfo() {
return {
id: "multiFileSelector",
name: "Multi File Selector+",
color1: "#fc6400",
blocks: [
{
opcode: "selectFiles",
blockType: Scratch.BlockType.COMMAND,
text: "select files [TYPES] max [MAX]",
arguments: {
TYPES: {
type: Scratch.ArgumentType.STRING,
menu: "fileTypes",
defaultValue: "all",
},
MAX: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: 10,
},
},
},
{
opcode: "clearFiles",
blockType: Scratch.BlockType.COMMAND,
text: "clear file selection",
},
{
blockType: "label",
text: "File Data",
},
{
opcode: "getFileInfo",
blockType: Scratch.BlockType.REPORTER,
text: "file info list",
},
{
opcode: "getFileData",
blockType: Scratch.BlockType.REPORTER,
text: "file data list",
},
],
menus: {
fileTypes: {
acceptReporters: true,
items: ["all", "audio", "text", "image"],
},
},
};
}

async selectFiles(args, util) {
const maxFiles = Math.max(1, Math.floor(args.MAX));
const typeMap = {
all: "*",
audio: "audio/*",
text: "text/*",
image: "image/*",
};
const acceptType = typeMap[args.TYPES] || "*";

const input = document.createElement("input");
input.type = "file";
input.multiple = true;
input.accept = acceptType;

this.files = [];
this.fileContents = [];

return new Promise((resolve) => {
input.onchange = async (event) => {
const selectedFiles = Array.from(event.target.files).slice(0, maxFiles);
this.files = selectedFiles.map((file) => ({
name: file.name,
size: file.size,
modified: file.lastModified,
type: file.type,
}));
this.fileContents = await Promise.all(
selectedFiles.map((file) => this.readFileAsDataURI(file))
);
resolve();
util.target.runtime.requestRedraw();
};

input.oncancel = () => {
this.files = [];
this.fileContents = [];
resolve();
util.target.runtime.requestRedraw();
};

input.click();
});
}

async readFileAsDataURI(file) {
return new Promise((resolve) => {
if (!file) {
return resolve("");
}
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.readAsDataURL(file);
});
}

clearFiles() {
this.files = [];
this.fileContents = [];
}

getFileInfo() {
return this.files.length > 0 ? JSON.stringify(this.files) : "[]";
}

getFileData() {
return this.fileContents.length > 0
? JSON.stringify(this.fileContents)
: "[]";
}
}
Scratch.extensions.register(new MultiFileSelector());
Binary file added static/images/XmerOriginals/dropfiles.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/images/XmerOriginals/multifileplus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.