Most recently modified extensions: - <% for (const extensionID of mostRecentExtensions) { %> - <%= extensionID %>.js + <% for (const extensionSlug of mostRecentExtensions) { %> + <%= extensionSlug %>.js <% } %>
@@ -273,6 +314,11 @@ e.target.focus(); } } + + if (e.target.className.includes('sample-list-button')) { + var extension = e.target.closest('.extension'); + extension.dataset.samplesOpen = extension.dataset.samplesOpen !== 'true'; + } }); @@ -297,17 +343,36 @@ return result; }; %> - <% for (const [extensionID, metadata] of Object.entries(extensionMetadata)) { %> + <% for (const [extensionSlug, metadata] of Object.entries(extensionMetadata)) { %>diff --git a/development/parse-extension-metadata.js b/development/parse-extension-metadata.js index a640b2ef33..632f01216c 100644 --- a/development/parse-extension-metadata.js +++ b/development/parse-extension-metadata.js @@ -1,12 +1,12 @@ class Person { - constructor (name, link) { + constructor(name, link) { /** @type {string} */ this.name = name; /** @type {string|null} */ this.link = link; } - toHTML () { + toHTML() { // Don't need to bother escaping here. There's no vulnerability. if (this.link) { return `${this.name}`; @@ -16,10 +16,10 @@ class Person { } class Extension { - constructor () { - this.id = ''; - this.name = ''; - this.description = ''; + constructor() { + this.id = ""; + this.name = ""; + this.description = ""; /** @type {Person[]} */ this.by = []; /** @type {Person[]} */ @@ -45,13 +45,13 @@ const splitFirst = (string, split) => { * @returns {Person} */ const parsePerson = (person) => { - const parts = splitFirst(person, '<'); + const parts = splitFirst(person, "<"); if (parts.length === 1) { return new Person(person, null); } const name = parts[0].trim(); - const link = parts[1].replace('>', ''); + const link = parts[1].replace(">", ""); return new Person(name, link); }; @@ -62,14 +62,14 @@ const parsePerson = (person) => { const parseMetadata = (extensionCode) => { const metadata = new Extension(); - for (const line of extensionCode.split('\n')) { - if (!line.startsWith('//')) { + for (const line of extensionCode.split("\n")) { + if (!line.startsWith("//")) { // End of header. break; } const withoutComment = line.substring(2).trim(); - const parts = splitFirst(withoutComment, ':'); + const parts = splitFirst(withoutComment, ":"); if (parts.length === 1) { // Invalid. continue; @@ -79,16 +79,19 @@ const parseMetadata = (extensionCode) => { const value = parts[1].trim(); switch (key) { - case 'name': + case "id": + metadata.id = value; + break; + case "name": metadata.name = value; break; - case 'description': + case "description": metadata.description = value; break; - case 'by': + case "by": metadata.by.push(parsePerson(value)); break; - case 'original': + case "original": metadata.original.push(parsePerson(value)); break; default: diff --git a/development/render-docs.js b/development/render-docs.js index b882596354..3bdcfab88e 100644 --- a/development/render-docs.js +++ b/development/render-docs.js @@ -1,24 +1,28 @@ -const path = require('path'); -const MarkdownIt = require('markdown-it'); -const renderTemplate = require('./render-template'); +const path = require("path"); +const MarkdownIt = require("markdown-it"); +const renderTemplate = require("./render-template"); const md = new MarkdownIt({ html: true, linkify: true, - breaks: true + breaks: true, }); md.renderer.rules.fence = function (tokens, idx, options, env, self) { const token = tokens[idx]; - if (token.info === 'scratch') { + if (token.info === "scratch") { env.usesScratchBlocks = true; - return `
and ; we'd rather it
// just use
- return `${md.utils.escapeHtml(token.content)}
`;
+ return `${md.utils.escapeHtml(token.content)}
`;
};
/**
@@ -31,12 +35,19 @@ const renderDocs = (markdownSource, slug) => {
const tokens = md.parse(markdownSource, env);
// Extract the header
- let headerHTML = '## file did not contain header ##';
+ let headerHTML = "## file did not contain header ##";
let headerText = headerHTML;
- const headerStart = tokens.findIndex((token) => token.type === 'heading_open' && token.tag === 'h1');
- const headerEnd = tokens.findIndex((token) => token.type === 'heading_close' && token.tag === 'h1');
+ const headerStart = tokens.findIndex(
+ (token) => token.type === "heading_open" && token.tag === "h1"
+ );
+ const headerEnd = tokens.findIndex(
+ (token) => token.type === "heading_close" && token.tag === "h1"
+ );
if (headerStart !== -1 && headerEnd !== -1) {
- const headerTokens = tokens.splice(headerStart, headerEnd - headerStart + 1);
+ const headerTokens = tokens.splice(
+ headerStart,
+ headerEnd - headerStart + 1
+ );
// Discard the header tokens themselves, but render the HTML title with any formatting
headerTokens.shift();
@@ -44,18 +55,20 @@ const renderDocs = (markdownSource, slug) => {
headerHTML = md.renderer.render(headerTokens, md.options, env);
// We also need a no-formatting version for the title
- const justTextTokens = headerTokens.filter(token => token.type === 'inline');
+ const justTextTokens = headerTokens.filter(
+ (token) => token.type === "inline"
+ );
headerText = md.renderer.render(justTextTokens, md.options, env);
}
const bodyHTML = md.renderer.render(tokens, md.options, env);
- return renderTemplate(path.join(__dirname, 'docs-template.ejs'), {
+ return renderTemplate(path.join(__dirname, "docs-template.ejs"), {
slug,
headerHTML,
headerText,
bodyHTML,
- usesScratchBlocks: !!env.usesScratchBlocks
+ usesScratchBlocks: !!env.usesScratchBlocks,
});
};
diff --git a/development/render-template.js b/development/render-template.js
index fe8afd689d..41f036b4d1 100644
--- a/development/render-template.js
+++ b/development/render-template.js
@@ -1,10 +1,10 @@
-const fs = require('fs');
-const ejs = require('ejs');
+const fs = require("fs");
+const ejs = require("ejs");
// TODO: Investigate the value of removing dependency on `ejs` and possibly writing our own DSL.
const renderTemplate = (path, data) => {
- const inputEJS = fs.readFileSync(path, 'utf-8');
+ const inputEJS = fs.readFileSync(path, "utf-8");
const outputHTML = ejs.render(inputEJS, data);
return outputHTML;
};
diff --git a/development/server.js b/development/server.js
index e0bdfee855..517ccdc68a 100644
--- a/development/server.js
+++ b/development/server.js
@@ -1,50 +1,53 @@
-const express = require('express');
-const Builder = require('./builder');
+const express = require("express");
+const Builder = require("./builder");
let mostRecentBuild = null;
-const builder = new Builder('development');
+const builder = new Builder("development");
builder.startWatcher((newBuild) => {
mostRecentBuild = newBuild;
});
const app = express();
-app.set('strict routing', true);
-app.set('x-powered-by', false);
+app.set("strict routing", true);
+app.set("x-powered-by", false);
app.use((req, res, next) => {
// If we don't tell the browser not to cache files, it does by default, and people will get confused when
// script changes aren't being applied if they don't do a reload without cache.
- res.setHeader('Cache-Control', 'no-store');
- res.setHeader('Pragma', 'no-cache');
+ res.setHeader("Cache-Control", "no-store");
+ res.setHeader("Pragma", "no-cache");
// Prevent browser from trying to guess file types.
- res.setHeader('X-Content-Type-Options', 'nosniff');
+ res.setHeader("X-Content-Type-Options", "nosniff");
// We don't want this site to be embedded in frames.
- res.setHeader('X-Frame-Options', 'DENY');
+ res.setHeader("X-Frame-Options", "DENY");
// No need to leak Referer headers.
- res.setHeader('Referrer-Policy', 'no-referrer');
+ res.setHeader("Referrer-Policy", "no-referrer");
// We want all resources used by the website to be local.
// This CSP does *not* apply to the extensions, just the website.
- res.setHeader('Content-Security-Policy', "default-src 'self' 'unsafe-inline' data: blob:");
+ res.setHeader(
+ "Content-Security-Policy",
+ "default-src 'self' 'unsafe-inline' data: blob:"
+ );
// Allows loading cross-origin example images and matches GitHub pages.
- res.setHeader('Access-Control-Allow-Origin', '*');
+ res.setHeader("Access-Control-Allow-Origin", "*");
next();
});
-app.get('/*', (req, res, next) => {
+app.get("/*", (req, res, next) => {
if (!mostRecentBuild) {
- res.contentType('text/plain');
+ res.contentType("text/plain");
res.status(500);
- res.send('Build Failed; See Console');
+ res.send("Build Failed; See Console");
return;
}
- const fileInBuild = mostRecentBuild.getFile(req.path);
+ const fileInBuild = mostRecentBuild.getFile(decodeURIComponent(req.path));
if (!fileInBuild) {
return next();
}
@@ -54,9 +57,9 @@ app.get('/*', (req, res, next) => {
});
app.use((req, res) => {
- res.contentType('text/plain');
+ res.contentType("text/plain");
res.status(404);
- res.send('404 Not Found');
+ res.send("404 Not Found");
});
// The port the server runs on matters. The editor only treats port 8000 as unsandboxed.
diff --git a/development/validate.js b/development/validate.js
index e9386baebe..64bf9496ab 100644
--- a/development/validate.js
+++ b/development/validate.js
@@ -1,16 +1,22 @@
-const Builder = require('./builder');
-const Colors = require('./colors');
+const Builder = require("./builder");
+const Colors = require("./colors");
-const builder = new Builder('production');
+const builder = new Builder("production");
const errors = builder.validate();
if (errors.length === 0) {
- console.log(`${Colors.GREEN}${Colors.BOLD}Validation checks passed.${Colors.RESET}`);
+ console.log(
+ `${Colors.GREEN}${Colors.BOLD}Validation checks passed.${Colors.RESET}`
+ );
process.exit(0);
} else {
- console.error(`${Colors.RED}${Colors.BOLD}${errors.length} ${errors.length === 1 ? 'file' : 'files'} failed validation.${Colors.RESET}`);
- console.error('');
- for (const {fileName, error} of errors) {
+ console.error(
+ `${Colors.RED}${Colors.BOLD}${errors.length} ${
+ errors.length === 1 ? "file" : "files"
+ } failed validation.${Colors.RESET}`
+ );
+ console.error("");
+ for (const { fileName, error } of errors) {
console.error(`${Colors.BOLD}${fileName}${Colors.RESET}: ${error}`);
}
console.error(``);
diff --git a/docs/CubesterYT/WindowControls.md b/docs/CubesterYT/WindowControls.md
new file mode 100644
index 0000000000..b0efe57279
--- /dev/null
+++ b/docs/CubesterYT/WindowControls.md
@@ -0,0 +1,463 @@
+# Window Controls
+
+This extension provides a set of blocks that gives you greater control over the Program Window.
+
+Note: Most of these blocks only work in Electron, Pop Ups/Web Apps containing HTML packaged projects, and normal Web Apps.
+
+Examples include, but are not limited to: TurboWarp Desktop App, TurboWarp Web App, Pop Up/Web App windows that contain the HTML packaged project, and plain Electron.
+
+Blocks that still work outside of these will be specified.
+
+
+
+Move Window Block (#)
+
+```scratch
+move window to x: (0) y: (0) :: #359ed4
+```
+
+Moves the Program Window to the defined "x" and "y" coordinate on the screen.
+
+
+
+Move Window to Preset Block (#)
+
+Moves the Program Window to a preset.
+
+The menu area has ten options, ("center", "right", "left", "top", "bottom", "top right", "top left", "bottom right", "bottom left", "random position")
+
+#### Center
+
+```scratch
+move window to the (center v) :: #359ed4
+```
+
+When choosing "center", it will move the Program Window to the center of the screen.
+
+#### Right
+
+```scratch
+move window to the (right v) :: #359ed4
+```
+
+When choosing "right", it will move the Program Window to the right of the screen.
+
+#### Left
+
+```scratch
+move window to the (left v) :: #359ed4
+```
+
+When choosing "left", it will move the Program Window to the left of the screen.
+
+#### Top
+
+```scratch
+move window to the (top v) :: #359ed4
+```
+
+When choosing "top", it will move the Program Window to the top of the screen.
+
+#### Bottom
+
+```scratch
+move window to the (bottom v) :: #359ed4
+```
+
+When choosing "bottom", it will move the Program Window to the bottom of the screen.
+
+#### Top Right
+
+```scratch
+move window to the (top right v) :: #359ed4
+```
+
+When choosing "top right", it will move the Program Window to the top right of the screen.
+
+#### Top Left
+
+```scratch
+move window to the (top left v) :: #359ed4
+```
+
+When choosing "top left", it will move the Program Window to the top left of the screen.
+
+#### Bottom Right
+
+```scratch
+move window to the (bottom right v) :: #359ed4
+```
+
+When choosing "bottom right", it will move the Program Window to the bottom right of the screen.
+
+#### Bottom Left
+
+```scratch
+move window to the (bottom left v) :: #359ed4
+```
+
+When choosing "bottom left", it will move the Program Window to the bottom left of the screen.
+
+#### Random Position
+
+```scratch
+move window to the (random position v) :: #359ed4
+```
+
+When choosing "random position", it will move the Program Window to a random position on the screen.
+
+
+
+Change "x" Block (#)
+
+```scratch
+change window x by (50) :: #359ed4
+```
+
+Dynamically changes the "x" position of the Program Window on the screen.
+
+
+
+Set "x" Block (#)
+
+```scratch
+set window x to (100) :: #359ed4
+```
+
+Statically changes the "x" position of the Program Window on the screen.
+
+
+
+Change "y" Block (#)
+
+```scratch
+change window y by (50) :: #359ed4
+```
+
+Dynamically changes the "y" position of the Program Window on the screen.
+
+
+
+Set "y" Block (#)
+
+```scratch
+set window y to (100) :: #359ed4
+```
+
+Statically changes the "y" position of the Program Window on the screen.
+
+
+
+Window "x" Reporter (#)
+
+```scratch
+(window x :: #359ed4)
+```
+
+This reporter returns the "x" position of the Program Window.
+
+This is supported outside of Electron, Pop Ups, and Web Apps.
+
+
+
+Window "y" Reporter (#)
+
+```scratch
+(window y :: #359ed4)
+```
+
+This reporter returns the "y" position of the Program Window.
+
+This is supported outside of Electron, Pop Ups, and Web Apps.
+
+
+
+Resize Window Block (#)
+
+```scratch
+resize window to width: (1000) height: (1000) :: #359ed4
+```
+
+Resizes the Program Window to the defined width and height values.
+
+
+
+Resize Window Preset Block (#)
+
+Resizes the Program Window to a preset.
+
+The menu area has eight options, ("480x360", "640x480", "1280x720", "1920x1080", "2560x1440", "2048x1080", "3840x2160", "7680x4320")
+
+#### 480x360
+
+```scratch
+resize window to (480x360 v) :: #359ed4
+```
+
+When choosing "480x360", it will resize the Program Window to 480x360 (360p). The aspect ratio for this size is 4:3.
+
+#### 640x480
+
+```scratch
+resize window to (640x480 v) :: #359ed4
+```
+
+When choosing "640x480", it will resize the Program Window to 640x480 (480p). The aspect ratio for this size is 4:3.
+
+#### 1280x720
+
+```scratch
+resize window to (1280x720 v) :: #359ed4
+```
+
+When choosing "1280x720", it will resize the Program Window to 1280x720 (720p). The aspect ratio for this size is 16:9.
+
+#### 1920x1080
+
+```scratch
+resize window to (1920x1080 v) :: #359ed4
+```
+
+When choosing "1920x1080", it will resize the Program Window to 1920x1080 (1080p). The aspect ratio for this size is 16:9.
+
+#### 2560x1440
+
+```scratch
+resize window to (2560x1440 v) :: #359ed4
+```
+
+When choosing "2560x1440", it will resize the Program Window to 2560x1440 (1440p). The aspect ratio for this size is 16:9.
+
+#### 2048x1080
+
+```scratch
+resize window to (2048x1080 v) :: #359ed4
+```
+
+When choosing "2048x1080", it will resize the Program Window to 2048x1080 (2K/1080p[Higher Pixel Rate]). The aspect ratio for this size is 1:1.77.
+
+#### 3840x2160
+
+```scratch
+resize window to (3840x2160 v) :: #359ed4
+```
+
+When choosing "3840x2160", it will resize the Program Window to 3840x2160 (4K). The aspect ratio for this size is 1:1.9.
+
+#### 7680x4320
+
+```scratch
+resize window to (7680x4320 v) :: #359ed4
+```
+
+When choosing "7680x4320", it will resize the Program Window to 7680x4320 (8K). The aspect ratio for this size is 16:9.
+
+
+
+Change Width Block (#)
+
+```scratch
+change window width by (50) :: #359ed4
+```
+
+Dynamically changes the width of the Program Window.
+
+
+
+Set Width Block (#)
+
+```scratch
+set window width to (1000) :: #359ed4
+```
+
+Statically changes the width of the Program Window.
+
+
+
+Change Height Block (#)
+
+```scratch
+change window height by (50) :: #359ed4
+```
+
+Dynamically changes the height of the Program Window.
+
+
+
+Set Height Block (#)
+
+```scratch
+set window height to (1000) :: #359ed4
+```
+
+Statically changes the height of the Program Window.
+
+
+
+Match Stage Size Block (#)
+
+```scratch
+match stage size :: #359ed4
+```
+
+Resizes the Program Window to match the aspect ratio of the stage. Works best when the stage is dynamically changed.
+
+Example: When using runtime options to change the stage size, using this block can help you adapt to the new stage size.
+
+Try this example script in a packaged project:
+
+```scratch
+when green flag clicked
+wait (1) seconds
+set stage size width: (360) height: (480) :: #8c9abf
+match stage size :: #359ed4
+move window to the (center v) :: #359ed4
+wait (1) seconds
+set stage size width: (480) height: (360) :: #8c9abf
+match stage size :: #359ed4
+move window to the (center v) :: #359ed4
+```
+
+
+
+Window Width Reporter (#)
+
+```scratch
+(window width :: #359ed4)
+```
+
+This reporter returns the width of the Program Window.
+
+This is supported outside of Electron, Pop Ups, and Web Apps.
+
+
+
+Window Height Reporter (#)
+
+```scratch
+(window height :: #359ed4)
+```
+
+This reporter returns the height of the Program Window.
+
+This is supported outside of Electron, Pop Ups, and Web Apps.
+
+
+
+Is Window Touching Screen Edge Boolean (#)
+
+```scratch
+
+```
+
+This boolean returns true or false for whether or not the Program Window is touching the screen's edge.
+
+This is supported outside of Electron, Pop Ups, and Web Apps.
+
+
+
+Screen Width Reporter (#)
+
+```scratch
+(screen width :: #359ed4)
+```
+
+This reporter returns the width of the Screen.
+
+This is supported outside of Electron, Pop Ups, and Web Apps.
+
+
+
+Screen Height Reporter (#)
+
+```scratch
+(screen height :: #359ed4)
+```
+
+This reporter returns the height of the Screen.
+
+This is supported outside of Electron, Pop Ups, and Web Apps.
+
+
+
+Is Window Focused Boolean (#)
+
+```scratch
+
+```
+
+This boolean returns true or false for whether or not the Program Window is in focus.
+
+This is supported outside of Electron, Pop Ups, and Web Apps.
+
+
+
+Set Window Title Block (#)
+
+```scratch
+set window title to ["Hello World!] :: #359ed4
+```
+
+Changes the title of the Program Window.
+
+This is supported outside of Electron, Pop Ups, and Web Apps.
+
+
+
+Window Title Reporter (#)
+
+```scratch
+(window title :: #359ed4)
+```
+
+This reporter returns the title of the Program Window.
+
+This is supported outside of Electron, Pop Ups, and Web Apps.
+
+
+
+Enter Fullscreen Block (#)
+
+```scratch
+enter fullscreen :: #359ed4
+```
+
+Makes the Program Window enter Fullscreen.
+
+This is supported outside of Electron, Pop Ups, and Web Apps.
+
+
+
+Exit Fullscreen Block (#)
+
+```scratch
+exit fullscreen :: #359ed4
+```
+
+Makes the Program Window exit Fullscreen.
+
+This is supported outside of Electron, Pop Ups, and Web Apps.
+
+
+
+Is Window Fullscreen Boolean (#)
+
+```scratch
+
+```
+
+This boolean returns true or false for whether or not the Program Window is in fullscreen.
+
+This is supported outside of Electron, Pop Ups, and Web Apps.
+
+
+
+Close Window Block (#)
+
+```scratch
+close window :: cap :: #359ed4
+```
+
+Closes the Program Window.
+
+This is supported outside of Electron, Pop Ups, and Web Apps.
\ No newline at end of file
diff --git a/docs/DNin/wake-lock.md b/docs/DNin/wake-lock.md
new file mode 100644
index 0000000000..000f7f8ea2
--- /dev/null
+++ b/docs/DNin/wake-lock.md
@@ -0,0 +1,26 @@
+# Wake Lock
+
+The Wake Lock feature allows you to keep your computer's screen on while a project is running. This can be helpful when playing media or performing an important but time-consuming task.
+
+## Activating and Releasing Wake Lock
+
+```scratch
+set wake lock to [on v] :: #0FBD8C
+```
+This block will activate wake lock.
+
+If you ever need to check that wake lock has properly been activated, use the `is wake lock active?` boolean reporter.
+
+To release wake lock, simply change "on" to "off".
+
+You can also insert boolean reporters into the menu input.
+
+Wake lock will also be released automatically when the project stops or is restarted to ensure it isn't accidentally left on forever.
+
+## Browser support
+
+Not all browsers support wake lock (notably, Firefox does not). In these browsers requesting wake lock will not do anything.
+
+## Note
+
+The wake lock block takes a moment to finish running as it activates wake lock, so if you put it in a script with other blocks, it will yield briefly, so try keeping it separate from your other scripts. The `is wake lock active?` boolean reporter, however, does not have a delay.
diff --git a/docs/Lily/Skins.md b/docs/Lily/Skins.md
new file mode 100644
index 0000000000..2bd8771766
--- /dev/null
+++ b/docs/Lily/Skins.md
@@ -0,0 +1,109 @@
+# Skins
+
+This extension allows you to load and display images onto sprites, as Skins.
+
+In this extension, a "Skin" is an image that can replace what a sprite looks like.
+
+Unlike costumes, Skins are not loaded when the project is opened. Instead, Skins are loaded with blocks as the project is running.
+
+## Loading Skins
+
+Skins can be created in 3 different ways. Each way requires you to give the skin a name, which will be used by other blocks to reference the skin later.
+
+Loading a skin with the same name as another skin will overwrite the data of that skin. Any sprite that was using this skin will now show the new skin.
+
+---
+
+```scratch
+create SVG skin [] as [my skin] :: #6b56ff
+```
+The first way is by creating a new skin with SVG markup data. The advantage to this is that it loads much quicker than the other loading blocks. The obvious disadvantage is that, unlike the other 2 blocks, it can only work with SVGs.
+
+---
+
+```scratch
+load skin from (costume 1 v) as [my skin] :: #6b56ff
+```
+The second way is by loading a skin from a costume.
+
+It's important to note that this block will require the Advanced Option "Remove raw asset data after loading to save RAM" to be disabled in the packager in order for this block to work correctly in a packaged environment. **You do not need to do this within the editor.**
+
+If you intend to package your project, we don't encourage using this block for that reason. **None of the other blocks in this extension require this option to be disabled.**
+
+---
+
+```scratch
+load skin from URL [https://...] as [my skin] :: #6b56ff
+```
+The final way is loading a skin through a URL. This block allows you to load any bitmap image as well as SVGs.
+
+```scratch
+load skin from URL (snapshot stage :: #9966ff) as [my skin] :: #6b56ff
+```
+While this block can work with a website URL, it's primarily designed to work with data URIs. Try using this with the "snapshot stage" block from the "Looks Plus" extension.
+
+For the final 2 blocks, the block will pause the script for a moment in order to load the skin. Treat them like "wait" blocks in your scripts, don't expect them to finish instantaneously.
+
+## Using Skins
+
+```scratch
+set skin of (myself v) to [my skin] :: #6b56ff
+```
+Skins can be applied to a sprite with this block, so long as you loaded the skin beforehand. Skins can be applied to multiple sprites/clones.
+
+Using the "myself" option will apply the skin to the sprite the block is running in: if the block is running in a clone, it will apply the skin to the clone. **Do not confuse "myself" with the sprite's name.**
+
+Skins will automatically be removed from every sprite when the project has stopped.
+
+---
+
+```scratch
+restore skin of (myself v) :: #6b56ff
+```
+You can remove the skin of a sprite with the "restore skin" block. This will remove the skin from that specific sprite.
+
+---
+
+```scratch
+restore targets with skin [my skin] :: #6b56ff
+```
+You can remove a skin from every sprite that has it applied with the "restore targets with skin" block. "Target" refers to "sprite" in this context.
+
+## Deleting Skins
+
+Skins that have been loaded will still exist after the project has stopped. In order to truly delete a skin, you have 2 methods.
+
+---
+
+```scratch
+delete skin [my skin] :: #6b56ff
+```
+Delete a specified skin, and reset any sprite that had it applied.
+
+---
+
+```scratch
+delete all skins :: #6b56ff
+```
+Delete every skin that has been loaded and reset all sprites that had any skin applied.
+
+## Other Blocks
+
+```scratch
+
+```
+Check whether a skin is actually loaded. This becomes true **after** the block has finished loading the skin.
+
+---
+
+```scratch
+((width v) of [my skin] :: #6b56ff)
+```
+Get the width/height of a skin. The values are rounded.
+
+---
+
+```scratch
+(current skin of (myself v) :: #6b56ff)
+```
+The name of the skin that is applied to the specified sprite.
\ No newline at end of file
diff --git a/docs/TheShovel/ShovelUtils.md b/docs/TheShovel/ShovelUtils.md
new file mode 100644
index 0000000000..bf852c9523
--- /dev/null
+++ b/docs/TheShovel/ShovelUtils.md
@@ -0,0 +1,105 @@
+# ShovelUtils
+
+Shovel Utils is an extension focused mostly on injecting and modifying sprites and assets inside the project, as well as several other functions.
+
+**Disclaimer: Modifying and importing assets can be dangerous, and has the potential to corrupt your project. Be careful!**
+
+## Importing Assets
+
+Shovel Utils offers an easy way to import several types of assets, including sprites, costumes, sounds, extensions, and even full projects.
+
+---
+
+**This goes for all blocks that fetch from a link: If you're experiences errors and are not able to import an asset from a link, check your console! You may be running into a CORS error. To resolve this, use a proxy like [corsproxy.io](https://corsproxy.io).**
+
+```scratch
+Import sprite from [Link or data uri here]
+```
+
+Imports a sprite into the project using a DataURI or link. Be mindful of sprite names; having two or more sprites with the same name can cause issues.
+
+```scratch
+Import image from [https://extensions.turbowarp.org/dango.png] name [Dango]
+```
+
+Imports a costume from a PNG, Bitmap, or JPEG. **Does not work with SVGS**. The costume imports into the current sprite/backdrop the user has selected.
+
+```scratch
+Import sound from [https://extensions.turbowarp.org/meow.mp3] name [Meow]
+```
+
+Imports a sound from any Scratch-compatible sound file. The sound imports into the current sprite/backdrop the user has selected.
+
+```scratch
+Import project from [https://theshovel.github.io/Bullet-Hell/Bullet%20Hell]
+```
+
+Imports a full project from a link. This project will completely replace the contents of the current one. If the project is unsandboxed, it will ask permission before swapping contents.
+
+```scratch
+Load extension from [https://extensions.turbowarp.org/utilities.js]
+```
+
+Imports any extension from a link. Extensions from the [Extension Gallery](https://extensions.turbowarp.org) can run unsandboxed, and don't require permission to import.
+
+## Other Ways to Modify The Project
+
+Aside from importing assets, Shovel Utils provides multiple miscellaneous features to modify and straight up delete parts of your projects.
+
+```scratch
+Set editing target to [Sprite1]
+```
+
+Sets the selected sprite in the editor. You can also set your input to "Stage" to set the selected target to the backdrop. This does work packaged, however will not have a visual effect.
+
+```scratch
+(get all sprites ::)
+```
+
+Gets the names of all the sprites (and the stage) as a JSON array. This can then be parsed using the JSON Extension.
+
+```scratch
+Restart project
+```
+
+Emulates a green flag click on a project, even if the green flag isn't present.
+
+```scratch
+Delete costume [costume1] in [Sprite1]
+```
+
+Deletes a costume from the specified sprite. If the costume doesn't exist, the block simply doesn't do anything.
+
+```scratch
+Delete sprite [Sprite1]
+```
+
+Deletes a the specified sprite. If the user has the "Sprite Deletion Confirmation" addon enabled and the project is unpackaged, it will ask permission before deleting sprites.
+
+## Miscellaneous Features
+
+Aside from project modification, there's several utility blocks present in Shovel Utils.
+
+```scratch
+(fps::)
+```
+
+Get the accurate FPS, or frames per second, of the current project. This is *not* the same as the "framerate limit" block from Runtime Options, as the block in Shovel Utils accounts for lag.
+
+```scratch
+(Get list [MyList])
+```
+
+Get the values of a list, exported as a JSON array. If the specified list has not been created yet, or is empty, the block will return empty.
+
+```scratch
+Set list [MyList] to [⟦1,2⟧]
+```
+
+Sets the values of lists. Accepts JSON arrays as inputs. If the specified list has not been created yet, the block simply doesn't do anything.
+
+```scratch
+(Get brightness of [ #ffffff] ::)
+```
+
+Gets the brightness of a hex value. Reports a whole number between 0 and 255. To transfer this to a value between 0 and 100 (what TurboWarp uses), divide the output of the block by 2.55 and round.
diff --git a/extensions/-SIPC-/consoles.js b/extensions/-SIPC-/consoles.js
index 4591507ace..3454a518e1 100644
--- a/extensions/-SIPC-/consoles.js
+++ b/extensions/-SIPC-/consoles.js
@@ -1,189 +1,190 @@
// Name: Consoles
+// ID: sipcconsole
// Description: Blocks that interact the JavaScript console built in to your browser's developer tools.
// By: -SIPC-
(function (Scratch) {
- 'use strict';
- const icon = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSI4MS41NTIwNyIgaGVpZ2h0PSI4MC42MDMwOCIgdmlld0JveD0iMCwwLDgxLjU1MjA3LDgwLjYwMzA4Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTk5LjIyMzk3LC0xNDAuNjk4NDYpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTI4MC43NzYwMywxODFjMCwyMi4yNTc5MiAtMTguMjU2MDUsNDAuMzAxNTQgLTQwLjc3NjAzLDQwLjMwMTU0Yy0yMi41MTk5OCwwIC00MC43NzYwMywtMTguMDQzNjEgLTQwLjc3NjAzLC00MC4zMDE1NGMwLC0yMi4yNTc5MiAxOC4yNTYwNSwtNDAuMzAxNTQgNDAuNzc2MDMsLTQwLjMwMTU0YzIyLjUxOTk4LDAgNDAuNzc2MDMsMTguMDQzNjEgNDAuNzc2MDMsNDAuMzAxNTR6IiBmaWxsPSIjODA4MDgwIiBzdHJva2Utd2lkdGg9IjAiLz48cGF0aCBkPSJNMjY2LjE2NTgzLDE2Mi4xOTM1NnYyOS4yMDMwMWMwLDIuMjU4MzMgLTEuODMwNTksNC4wODg0MSAtNC4wODg0MSw0LjA4ODQxaC00NC4xNTQ4NWMtMi4yNTgzMywwIC00LjA4ODQxLC0xLjgzMDA3IC00LjA4ODQxLC00LjA4ODQxdi0yOS4yMDMwMWMwLC0yLjI1ODMzIDEuODMwMDcsLTQuMDg4NDEgNC4wODg0MSwtNC4wODg0MWg0NC4xNTQ4NWMyLjI1NzgzLDAgNC4wODg0MSwxLjgzMDA3IDQuMDg4NDEsNC4wODg0MXpNMjYyLjM1NTk1LDE2NC42NDUwOGMwLC0xLjM0MjAyIC0xLjA4ODAzLC0yLjQzMDA1IC0yLjQzMDA1LC0yLjQzMDA1aC0zOS44NTEyOGMtMS4zNDIwMiwwIC0yLjQzMDA1LDEuMDg4MDMgLTIuNDMwMDUsMi40MzAwNXYyNC4yOTk0N2MwLDEuMzQyMDIgMS4wODgwMywyLjQzMDA1IDIuNDMwMDUsMi40MzAwNWg3Ljc3NDYzdi0xMC4yMDU2OWMwLC0xLjM0MjU0IDEuMDg4MDMsLTIuNDMwMDUgMi40MzAwNSwtMi40MzAwNWMxLjM0MjU0LDAgMi40MzAwNSwxLjA4ODAzIDIuNDMwMDUsMi40MzAwNXYxMC4yMDU2OWg0Ljg2MDF2LTE4Ljk1Mzg4YzAsLTEuMzQyMDIgMS4wODgwMywtMi40MzAwNSAyLjQzMDA1LC0yLjQzMDA1YzEuMzQyNTQsMCAyLjQzMDA1LDEuMDg4MDMgMi40MzAwNSwyLjQzMDA1djE4Ljk1Mzg4aDQuODYwMXYtMTQuMDkzNzhjMCwtMS4zNDIwMiAxLjA4ODAzLC0yLjQzMDA1IDIuNDMwMDUsLTIuNDMwMDVjMS4zNDI1NCwwIDIuNDMwMDUsMS4wODgwMyAyLjQzMDA1LDIuNDMwMDV2MTQuMDkzNzhoNy43NzYxNmMxLjM0MjAyLDAgMi40MzAwNSwtMS4wODgwMyAyLjQzMDA1LC0yLjQzMDA1ek0yNTMuMDgyOTIsMjAxLjU1ODgzYzAsMS4yOTA0MSAtMS4wNDYxMiwyLjMzNjAyIC0yLjMzNjAyLDIuMzM2MDJoLTIxLjQ5MzhjLTEuMjg5ODksMCAtMi4zMzYwMiwtMS4wNDU2MSAtMi4zMzYwMiwtMi4zMzYwMmMwLC0xLjI4OTg5IDEuMDQ1NjEsLTIuMzM2MDIgMi4zMzYwMiwtMi4zMzYwMmgyMS40OTM4YzEuMjg5ODksMCAyLjMzNjAyLDEuMDQ2MTMgMi4zMzYwMiwyLjMzNjAyek0yNTAuNzQ2OSwxOTkuMjIyODEiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMSIvPjwvZz48L2c+PC9zdmc+PCEtLXJvdGF0aW9uQ2VudGVyOjQwLjc3NjAzMzMzMzMzMzQ6MzkuMzAxNTM5OTk5OTk5OTYtLT4=';
- const icon2 = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjczNzk0NjIzOTU3IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjQ0NDAiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCI+PHBhdGggZD0iTTk0NC4zOCA3MC4xOWgtODY0Yy00NC4xOSAwLTgwIDM1LjgxLTgwIDgwdjU3MS40M2MwIDQ0LjE5IDM1LjgxIDgwIDgwIDgwaDg2NGM0NC4xOCAwIDgwLTM1LjgxIDgwLTgwVjE1MC4xOWMwLTQ0LjE5LTM1LjgyLTgwLTgwLTgweiBtNS40NSA2MDMuNDVjMCAyNi4yNi0yMS4yOSA0Ny41NS00Ny41NSA0Ny41NUg3NTAuMTJWNDQ1LjQxYzAtMjYuMjYtMjEuMjgtNDcuNTUtNDcuNTUtNDcuNTUtMjYuMjYgMC00Ny41NSAyMS4yOS00Ny41NSA0Ny41NXYyNzUuNzhoLTk1LjFWMzUwLjMxYzAtMjYuMjYtMjEuMjgtNDcuNTUtNDcuNTUtNDcuNTUtMjYuMjYgMC00Ny41NSAyMS4yOS00Ny41NSA0Ny41NXYzNzAuODhoLTk1LjF2LTE5OS43YzAtMjYuMjYtMjEuMjgtNDcuNTUtNDcuNTUtNDcuNTUtMjYuMjYgMC00Ny41NSAyMS4yOC00Ny41NSA0Ny41NXYxOTkuN0gxMjIuNDljLTI2LjI2IDAtNDcuNTUtMjEuMjktNDcuNTUtNDcuNTVWMTk4LjE2YzAtMjYuMjYgMjEuMjktNDcuNTUgNDcuNTUtNDcuNTVoNzc5Ljc5YzI2LjI2IDAgNDcuNTUgMjEuMjkgNDcuNTUgNDcuNTV2NDc1LjQ4ek03MjIuNjcgODc0Ljc2SDMwMi4wOWMtMjUuMjUgMC00NS43MSAyMC40Ny00NS43MSA0NS43MSAwIDI1LjI1IDIwLjQ3IDQ1LjcxIDQ1LjcxIDQ1LjcxaDQyMC41OGMyNS4yNCAwIDQ1LjcxLTIwLjQ2IDQ1LjcxLTQ1LjcxIDAtMjUuMjQtMjAuNDctNDUuNzEtNDUuNzEtNDUuNzF6IG0wIDAiIGZpbGw9IiNmZmZmZmYiIHAtaWQ9IjQ0NDEiPjwvcGF0aD48L3N2Zz4=';
- class Consoles {
- constructor () {}
- getInfo() {
- return {
- id: 'sipcconsole',
- name: 'Consoles',
- color1: '#808080',
- color2: '#8c8c8c',
- color3: '#999999',
- menuIconURI: icon,
- blockIconURI: icon2,
- blocks: [
- {
- opcode: 'Emptying',
- blockType: Scratch.BlockType.COMMAND,
- text: 'Clear Console',
- arguments: {}
- },
- {
- opcode: 'Information',
- blockType: Scratch.BlockType.COMMAND,
- text: 'Information [string]',
- arguments: {
- string: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Information'
- }
- }
- },
- {
- opcode: 'Journal',
- blockType: Scratch.BlockType.COMMAND,
- text: 'Journal [string]',
- arguments: {
- string: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Journal'
- }
- }
- },
- {
- opcode: 'Warning',
- blockType: Scratch.BlockType.COMMAND,
- text: 'Warning [string]',
- arguments: {
- string: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Warning'
- }
- }
- },
- {
- opcode: 'Error',
- blockType: Scratch.BlockType.COMMAND,
- text: 'Error [string]',
- arguments: {
- string: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Error'
- }
- }
- },
- {
- opcode: 'debug',
- blockType: Scratch.BlockType.COMMAND,
- text: 'Debug [string]',
- arguments: {
- string: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Debug'
- }
- }
- },
-
-
- '---',
- {
- opcode: 'group',
- blockType: Scratch.BlockType.COMMAND,
- text: 'Create a group named [string]',
- arguments: {
- string: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'group'
- }
- }
- },
- {
- opcode: 'groupCollapsed',
- blockType: Scratch.BlockType.COMMAND,
- text: 'Create a collapsed group named [string]',
- arguments: {
- string: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'group'
- }
- }
- },
- {
- opcode: 'groupEnd',
- blockType: Scratch.BlockType.COMMAND,
- text: 'Exit the current group',
- arguments: {}
- },
- '---',
- {
- opcode: 'Timeron',
- blockType: Scratch.BlockType.COMMAND,
- text: 'Start a timer named [string]',
- arguments: {
- string: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Time'
- }
- }
- },
- {
- opcode: 'Timerlog',
- blockType: Scratch.BlockType.COMMAND,
- text: 'Print the time run by the timer named [string]',
- arguments: {
- string: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Time'
- }
- }
- },
- {
- opcode: 'Timeroff',
- blockType: Scratch.BlockType.COMMAND,
- text: 'End the timer named [string] and print the time elapsed from start to end',
- arguments: {
- string: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Time'
- }
- }
- },
- ]
- };
- }
- Emptying () {
- console.clear();
- }
- Information ({string}) {
- console.info(string);
- }
- Journal ({string}) {
- console.log(string);
- }
- Warning ({string}) {
- console.warn(string);
- }
- Error ({string}) {
- console.error(string);
- }
- debug ({string}) {
- console.debug(string);
- }
- group({string}) {
- console.group(string);
- }
- groupCollapsed({string}) {
- console.groupCollapsed(string);
- }
- groupEnd() {
- console.groupEnd();
- }
- Timeron ({string}) {
- console.time(string);
- }
- Timerlog ({string}) {
- console.timeLog(string);
- }
- Timeroff ({string}) {
- console.timeEnd(string);
- }
+ "use strict";
+ const icon =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSI4MS41NTIwNyIgaGVpZ2h0PSI4MC42MDMwOCIgdmlld0JveD0iMCwwLDgxLjU1MjA3LDgwLjYwMzA4Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTk5LjIyMzk3LC0xNDAuNjk4NDYpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTI4MC43NzYwMywxODFjMCwyMi4yNTc5MiAtMTguMjU2MDUsNDAuMzAxNTQgLTQwLjc3NjAzLDQwLjMwMTU0Yy0yMi41MTk5OCwwIC00MC43NzYwMywtMTguMDQzNjEgLTQwLjc3NjAzLC00MC4zMDE1NGMwLC0yMi4yNTc5MiAxOC4yNTYwNSwtNDAuMzAxNTQgNDAuNzc2MDMsLTQwLjMwMTU0YzIyLjUxOTk4LDAgNDAuNzc2MDMsMTguMDQzNjEgNDAuNzc2MDMsNDAuMzAxNTR6IiBmaWxsPSIjODA4MDgwIiBzdHJva2Utd2lkdGg9IjAiLz48cGF0aCBkPSJNMjY2LjE2NTgzLDE2Mi4xOTM1NnYyOS4yMDMwMWMwLDIuMjU4MzMgLTEuODMwNTksNC4wODg0MSAtNC4wODg0MSw0LjA4ODQxaC00NC4xNTQ4NWMtMi4yNTgzMywwIC00LjA4ODQxLC0xLjgzMDA3IC00LjA4ODQxLC00LjA4ODQxdi0yOS4yMDMwMWMwLC0yLjI1ODMzIDEuODMwMDcsLTQuMDg4NDEgNC4wODg0MSwtNC4wODg0MWg0NC4xNTQ4NWMyLjI1NzgzLDAgNC4wODg0MSwxLjgzMDA3IDQuMDg4NDEsNC4wODg0MXpNMjYyLjM1NTk1LDE2NC42NDUwOGMwLC0xLjM0MjAyIC0xLjA4ODAzLC0yLjQzMDA1IC0yLjQzMDA1LC0yLjQzMDA1aC0zOS44NTEyOGMtMS4zNDIwMiwwIC0yLjQzMDA1LDEuMDg4MDMgLTIuNDMwMDUsMi40MzAwNXYyNC4yOTk0N2MwLDEuMzQyMDIgMS4wODgwMywyLjQzMDA1IDIuNDMwMDUsMi40MzAwNWg3Ljc3NDYzdi0xMC4yMDU2OWMwLC0xLjM0MjU0IDEuMDg4MDMsLTIuNDMwMDUgMi40MzAwNSwtMi40MzAwNWMxLjM0MjU0LDAgMi40MzAwNSwxLjA4ODAzIDIuNDMwMDUsMi40MzAwNXYxMC4yMDU2OWg0Ljg2MDF2LTE4Ljk1Mzg4YzAsLTEuMzQyMDIgMS4wODgwMywtMi40MzAwNSAyLjQzMDA1LC0yLjQzMDA1YzEuMzQyNTQsMCAyLjQzMDA1LDEuMDg4MDMgMi40MzAwNSwyLjQzMDA1djE4Ljk1Mzg4aDQuODYwMXYtMTQuMDkzNzhjMCwtMS4zNDIwMiAxLjA4ODAzLC0yLjQzMDA1IDIuNDMwMDUsLTIuNDMwMDVjMS4zNDI1NCwwIDIuNDMwMDUsMS4wODgwMyAyLjQzMDA1LDIuNDMwMDV2MTQuMDkzNzhoNy43NzYxNmMxLjM0MjAyLDAgMi40MzAwNSwtMS4wODgwMyAyLjQzMDA1LC0yLjQzMDA1ek0yNTMuMDgyOTIsMjAxLjU1ODgzYzAsMS4yOTA0MSAtMS4wNDYxMiwyLjMzNjAyIC0yLjMzNjAyLDIuMzM2MDJoLTIxLjQ5MzhjLTEuMjg5ODksMCAtMi4zMzYwMiwtMS4wNDU2MSAtMi4zMzYwMiwtMi4zMzYwMmMwLC0xLjI4OTg5IDEuMDQ1NjEsLTIuMzM2MDIgMi4zMzYwMiwtMi4zMzYwMmgyMS40OTM4YzEuMjg5ODksMCAyLjMzNjAyLDEuMDQ2MTMgMi4zMzYwMiwyLjMzNjAyek0yNTAuNzQ2OSwxOTkuMjIyODEiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMSIvPjwvZz48L2c+PC9zdmc+PCEtLXJvdGF0aW9uQ2VudGVyOjQwLjc3NjAzMzMzMzMzMzQ6MzkuMzAxNTM5OTk5OTk5OTYtLT4=";
+ const icon2 =
+ "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjczNzk0NjIzOTU3IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjQ0NDAiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCI+PHBhdGggZD0iTTk0NC4zOCA3MC4xOWgtODY0Yy00NC4xOSAwLTgwIDM1LjgxLTgwIDgwdjU3MS40M2MwIDQ0LjE5IDM1LjgxIDgwIDgwIDgwaDg2NGM0NC4xOCAwIDgwLTM1LjgxIDgwLTgwVjE1MC4xOWMwLTQ0LjE5LTM1LjgyLTgwLTgwLTgweiBtNS40NSA2MDMuNDVjMCAyNi4yNi0yMS4yOSA0Ny41NS00Ny41NSA0Ny41NUg3NTAuMTJWNDQ1LjQxYzAtMjYuMjYtMjEuMjgtNDcuNTUtNDcuNTUtNDcuNTUtMjYuMjYgMC00Ny41NSAyMS4yOS00Ny41NSA0Ny41NXYyNzUuNzhoLTk1LjFWMzUwLjMxYzAtMjYuMjYtMjEuMjgtNDcuNTUtNDcuNTUtNDcuNTUtMjYuMjYgMC00Ny41NSAyMS4yOS00Ny41NSA0Ny41NXYzNzAuODhoLTk1LjF2LTE5OS43YzAtMjYuMjYtMjEuMjgtNDcuNTUtNDcuNTUtNDcuNTUtMjYuMjYgMC00Ny41NSAyMS4yOC00Ny41NSA0Ny41NXYxOTkuN0gxMjIuNDljLTI2LjI2IDAtNDcuNTUtMjEuMjktNDcuNTUtNDcuNTVWMTk4LjE2YzAtMjYuMjYgMjEuMjktNDcuNTUgNDcuNTUtNDcuNTVoNzc5Ljc5YzI2LjI2IDAgNDcuNTUgMjEuMjkgNDcuNTUgNDcuNTV2NDc1LjQ4ek03MjIuNjcgODc0Ljc2SDMwMi4wOWMtMjUuMjUgMC00NS43MSAyMC40Ny00NS43MSA0NS43MSAwIDI1LjI1IDIwLjQ3IDQ1LjcxIDQ1LjcxIDQ1LjcxaDQyMC41OGMyNS4yNCAwIDQ1LjcxLTIwLjQ2IDQ1LjcxLTQ1LjcxIDAtMjUuMjQtMjAuNDctNDUuNzEtNDUuNzEtNDUuNzF6IG0wIDAiIGZpbGw9IiNmZmZmZmYiIHAtaWQ9IjQ0NDEiPjwvcGF0aD48L3N2Zz4=";
+ class Consoles {
+ constructor() {}
+ getInfo() {
+ return {
+ id: "sipcconsole",
+ name: "Consoles",
+ color1: "#808080",
+ color2: "#8c8c8c",
+ color3: "#999999",
+ menuIconURI: icon,
+ blockIconURI: icon2,
+ blocks: [
+ {
+ opcode: "Emptying",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "Clear Console",
+ arguments: {},
+ },
+ {
+ opcode: "Information",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "Information [string]",
+ arguments: {
+ string: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Information",
+ },
+ },
+ },
+ {
+ opcode: "Journal",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "Journal [string]",
+ arguments: {
+ string: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Journal",
+ },
+ },
+ },
+ {
+ opcode: "Warning",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "Warning [string]",
+ arguments: {
+ string: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Warning",
+ },
+ },
+ },
+ {
+ opcode: "Error",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "Error [string]",
+ arguments: {
+ string: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Error",
+ },
+ },
+ },
+ {
+ opcode: "debug",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "Debug [string]",
+ arguments: {
+ string: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Debug",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "group",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "Create a group named [string]",
+ arguments: {
+ string: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "group",
+ },
+ },
+ },
+ {
+ opcode: "groupCollapsed",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "Create a collapsed group named [string]",
+ arguments: {
+ string: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "group",
+ },
+ },
+ },
+ {
+ opcode: "groupEnd",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "Exit the current group",
+ arguments: {},
+ },
+ "---",
+ {
+ opcode: "Timeron",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "Start a timer named [string]",
+ arguments: {
+ string: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Time",
+ },
+ },
+ },
+ {
+ opcode: "Timerlog",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "Print the time run by the timer named [string]",
+ arguments: {
+ string: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Time",
+ },
+ },
+ },
+ {
+ opcode: "Timeroff",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "End the timer named [string] and print the time elapsed from start to end",
+ arguments: {
+ string: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Time",
+ },
+ },
+ },
+ ],
+ };
+ }
+ Emptying() {
+ console.clear();
+ }
+ Information({ string }) {
+ console.info(string);
+ }
+ Journal({ string }) {
+ console.log(string);
+ }
+ Warning({ string }) {
+ console.warn(string);
+ }
+ Error({ string }) {
+ console.error(string);
+ }
+ debug({ string }) {
+ console.debug(string);
+ }
+ group({ string }) {
+ console.group(string);
+ }
+ groupCollapsed({ string }) {
+ console.groupCollapsed(string);
+ }
+ groupEnd() {
+ console.groupEnd();
+ }
+ Timeron({ string }) {
+ console.time(string);
+ }
+ Timerlog({ string }) {
+ console.timeLog(string);
+ }
+ Timeroff({ string }) {
+ console.timeEnd(string);
}
- Scratch.extensions.register(new Consoles());
+ }
+ Scratch.extensions.register(new Consoles());
})(Scratch);
diff --git a/extensions/-SIPC-/recording.js b/extensions/-SIPC-/recording.js
index 40b2fd8770..2265654c94 100644
--- a/extensions/-SIPC-/recording.js
+++ b/extensions/-SIPC-/recording.js
@@ -1,111 +1,114 @@
-(function (Scratch) {
- 'use strict';
- if (!Scratch.extensions.unsandboxed) {
- throw new Error('Recording must be run unsandboxed');
- }
- /** @type {MediaRecorder|null} */
- let mediaRecorder = null;
- let recordedChunks = [];
- const icon = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjg1MTAzNzAzNDU1IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjMzMjIiIGRhdGEtc3BtLWFuY2hvci1pZD0iYTMxM3guNzc4MTA2OS4wLmkxIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiPjxwYXRoIGQ9Ik04MzMgNDQ1LjVjMTcuNjczIDAgMzIgMTQuMzI3IDMyIDMyIDAgMTgzLjcyMi0xNDAuNTUzIDMzNC42MTYtMzE5Ljk5NyAzNTEuMDIxTDU0NSA4OTVoMTk0YzE3LjY3MyAwIDMyIDE0LjMyNyAzMiAzMiAwIDE3LjY3My0xNC4zMjcgMzItMzIgMzJIMjg3Yy0xNy42NzMgMC0zMi0xNC4zMjctMzItMzIgMC0xNy42NzMgMTQuMzI3LTMyIDMyLTMyaDE5NGwtMC4wMDMtNjYuMzg5QzMwMS4wNzUgODEyLjY3NyAxNjAgNjYxLjU2MyAxNjAgNDc3LjVjMC0xNy42NzMgMTQuMzI3LTMyIDMyLTMyIDE3LjQ5NiAwIDMxLjcxMyAxNC4wNDIgMzEuOTk2IDMxLjQ3bDAuMDA0IDAuNTNDMjI0IDYzNi44MzQgMzUzLjE2NiA3NjYgNTEyLjUgNzY2YzE1Ny43NCAwIDI4NS45MTQtMTI2LjU5NSAyODguNDYxLTI4My43M2wwLjAzOS00Ljc3YzAtMTcuNjczIDE0LjMyNy0zMiAzMi0zMnpNNTEzIDY1YzEyMy4wMjEgMCAyMjIuOTgzIDk4LjczMSAyMjQuOTcgMjIxLjI4TDczOCAyOTB2MTg2YzAgMTI0LjI2NC0xMDAuNzM2IDIyNS0yMjUgMjI1LTEyMy4wMjEgMC0yMjIuOTgzLTk4LjczMS0yMjQuOTctMjIxLjI4TDI4OCA0NzZWMjkwYzAtMTI0LjI2NCAxMDAuNzM2LTIyNSAyMjUtMjI1eiBtMCA2NGMtODguMDI5IDAtMTU5LjU1NyA3MC42NDgtMTYwLjk3OCAxNTguMzM4TDM1MiAyOTB2MTg2YzAgODguOTE4IDcyLjA4MiAxNjEgMTYxIDE2MSA4OC4wMjkgMCAxNTkuNTU3LTcwLjY0OCAxNjAuOTc4LTE1OC4zMzhMNjc0IDQ3NlYyOTBjMC04OC45MTgtNzIuMDgyLTE2MS0xNjEtMTYxeiBtMTI0LjU0MyAyNTguNTkxYzE3LjM4OCA4OS40NTMtNDEuMDMyIDE3Ni4wNjQtMTMwLjQ4NSAxOTMuNDUyLTE3LjM0OCAzLjM3Mi0zNC4xNDYtNy45NTgtMzcuNTE4LTI1LjMwNi0zLjMzOC0xNy4xNzUgNy43MzMtMzMuODEgMjQuNzg4LTM3LjQxM2wwLjUxOC0wLjEwNWM1NC4yMDktMTAuNTM3IDg5LjgtNjIuNjA0IDgwLjE3OC0xMTYuNzc0bC0wLjMwNS0xLjY0MmMtMy4zNzItMTcuMzQ4IDcuOTU4LTM0LjE0NiAyNS4zMDYtMzcuNTE4IDE3LjM0OS0zLjM3MiAzNC4xNDYgNy45NTggMzcuNTE4IDI1LjMwNnoiIGZpbGw9IiNmZmZmZmYiIHAtaWQ9IjMzMjMiPjwvcGF0aD48L3N2Zz4=';
- class Recording {
- getInfo() {
- return {
- id: 'sipcrecording',
- name: 'Recording',
- color1: '#696969',
- blocks: [
- {
- opcode: 'startRecording',
- blockType: Scratch.BlockType.COMMAND,
- text: 'Start recording',
- blockIconURI: icon,
- arguments: {}
- },
- {
- opcode: 'stopRecording',
- blockType: Scratch.BlockType.COMMAND,
- text: 'Stop recording',
- blockIconURI: icon,
- arguments: {}
- },
- {
- opcode: 'stopRecordingAndDownload',
- blockType: Scratch.BlockType.COMMAND,
- text: 'Stop recording and download with [name] as filename',
- blockIconURI: icon,
- arguments: {
- name: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'recording.wav'
- }
- }
- },
- {
- opcode: 'isRecording',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'Recording?',
- blockIconURI: icon,
- arguments: {}
- },
- ]
- };
- }
- async startRecording() {
- recordedChunks = [];
- if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
- console.error('The recording function is not supported by the browser');
- return;
- }
- try {
- if (!await Scratch.canRecordAudio()) {
- throw new Error('VM denied permission');
- }
- const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
- mediaRecorder = new MediaRecorder(stream);
- mediaRecorder.addEventListener('dataavailable', function (e) {
- recordedChunks.push(e.data);
- });
- mediaRecorder.start();
- console.log('Start recording');
- } catch (e) {
- console.error('Could not start recording', e);
- }
- }
- stopRecording() {
- if (!mediaRecorder) {
- console.error('Recording not started');
- return;
- }
- console.log('Stop recording');
- mediaRecorder.stop();
- mediaRecorder = null;
- recordedChunks = [];
- }
- stopRecordingAndDownload({name}) {
- if (!mediaRecorder) {
- console.error('Recording not started');
- return;
- }
- console.log('Stop recording');
- mediaRecorder.addEventListener('stop', function () {
- const blob = new Blob(recordedChunks, { type: 'audio/wav' });
- const url = URL.createObjectURL(blob);
- const downloadLink = document.createElement('a');
- downloadLink.href = url;
- downloadLink.download = name;
- document.body.appendChild(downloadLink);
- downloadLink.click();
- document.body.removeChild(downloadLink);
- URL.revokeObjectURL(url);
- recordedChunks = [];
- });
- mediaRecorder.stop();
- mediaRecorder = null;
- }
- isRecording() {
- return !!mediaRecorder;
- }
- }
- Scratch.extensions.register(new Recording());
-})(Scratch);
-//BY -SIPC- 502415953
+(function (Scratch) {
+ "use strict";
+ if (!Scratch.extensions.unsandboxed) {
+ throw new Error("Recording must be run unsandboxed");
+ }
+ /** @type {MediaRecorder|null} */
+ let mediaRecorder = null;
+ let recordedChunks = [];
+ const icon =
+ "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNjg1MTAzNzAzNDU1IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjMzMjIiIGRhdGEtc3BtLWFuY2hvci1pZD0iYTMxM3guNzc4MTA2OS4wLmkxIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiPjxwYXRoIGQ9Ik04MzMgNDQ1LjVjMTcuNjczIDAgMzIgMTQuMzI3IDMyIDMyIDAgMTgzLjcyMi0xNDAuNTUzIDMzNC42MTYtMzE5Ljk5NyAzNTEuMDIxTDU0NSA4OTVoMTk0YzE3LjY3MyAwIDMyIDE0LjMyNyAzMiAzMiAwIDE3LjY3My0xNC4zMjcgMzItMzIgMzJIMjg3Yy0xNy42NzMgMC0zMi0xNC4zMjctMzItMzIgMC0xNy42NzMgMTQuMzI3LTMyIDMyLTMyaDE5NGwtMC4wMDMtNjYuMzg5QzMwMS4wNzUgODEyLjY3NyAxNjAgNjYxLjU2MyAxNjAgNDc3LjVjMC0xNy42NzMgMTQuMzI3LTMyIDMyLTMyIDE3LjQ5NiAwIDMxLjcxMyAxNC4wNDIgMzEuOTk2IDMxLjQ3bDAuMDA0IDAuNTNDMjI0IDYzNi44MzQgMzUzLjE2NiA3NjYgNTEyLjUgNzY2YzE1Ny43NCAwIDI4NS45MTQtMTI2LjU5NSAyODguNDYxLTI4My43M2wwLjAzOS00Ljc3YzAtMTcuNjczIDE0LjMyNy0zMiAzMi0zMnpNNTEzIDY1YzEyMy4wMjEgMCAyMjIuOTgzIDk4LjczMSAyMjQuOTcgMjIxLjI4TDczOCAyOTB2MTg2YzAgMTI0LjI2NC0xMDAuNzM2IDIyNS0yMjUgMjI1LTEyMy4wMjEgMC0yMjIuOTgzLTk4LjczMS0yMjQuOTctMjIxLjI4TDI4OCA0NzZWMjkwYzAtMTI0LjI2NCAxMDAuNzM2LTIyNSAyMjUtMjI1eiBtMCA2NGMtODguMDI5IDAtMTU5LjU1NyA3MC42NDgtMTYwLjk3OCAxNTguMzM4TDM1MiAyOTB2MTg2YzAgODguOTE4IDcyLjA4MiAxNjEgMTYxIDE2MSA4OC4wMjkgMCAxNTkuNTU3LTcwLjY0OCAxNjAuOTc4LTE1OC4zMzhMNjc0IDQ3NlYyOTBjMC04OC45MTgtNzIuMDgyLTE2MS0xNjEtMTYxeiBtMTI0LjU0MyAyNTguNTkxYzE3LjM4OCA4OS40NTMtNDEuMDMyIDE3Ni4wNjQtMTMwLjQ4NSAxOTMuNDUyLTE3LjM0OCAzLjM3Mi0zNC4xNDYtNy45NTgtMzcuNTE4LTI1LjMwNi0zLjMzOC0xNy4xNzUgNy43MzMtMzMuODEgMjQuNzg4LTM3LjQxM2wwLjUxOC0wLjEwNWM1NC4yMDktMTAuNTM3IDg5LjgtNjIuNjA0IDgwLjE3OC0xMTYuNzc0bC0wLjMwNS0xLjY0MmMtMy4zNzItMTcuMzQ4IDcuOTU4LTM0LjE0NiAyNS4zMDYtMzcuNTE4IDE3LjM0OS0zLjM3MiAzNC4xNDYgNy45NTggMzcuNTE4IDI1LjMwNnoiIGZpbGw9IiNmZmZmZmYiIHAtaWQ9IjMzMjMiPjwvcGF0aD48L3N2Zz4=";
+ class Recording {
+ getInfo() {
+ return {
+ id: "sipcrecording",
+ name: "Recording",
+ color1: "#696969",
+ blocks: [
+ {
+ opcode: "startRecording",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "Start recording",
+ blockIconURI: icon,
+ arguments: {},
+ },
+ {
+ opcode: "stopRecording",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "Stop recording",
+ blockIconURI: icon,
+ arguments: {},
+ },
+ {
+ opcode: "stopRecordingAndDownload",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "Stop recording and download with [name] as filename",
+ blockIconURI: icon,
+ arguments: {
+ name: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "recording.wav",
+ },
+ },
+ },
+ {
+ opcode: "isRecording",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "Recording?",
+ blockIconURI: icon,
+ arguments: {},
+ },
+ ],
+ };
+ }
+ async startRecording() {
+ recordedChunks = [];
+ if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
+ console.error("The recording function is not supported by the browser");
+ return;
+ }
+ try {
+ if (!(await Scratch.canRecordAudio())) {
+ throw new Error("VM denied permission");
+ }
+ const stream = await navigator.mediaDevices.getUserMedia({
+ audio: true,
+ });
+ mediaRecorder = new MediaRecorder(stream);
+ mediaRecorder.addEventListener("dataavailable", function (e) {
+ recordedChunks.push(e.data);
+ });
+ mediaRecorder.start();
+ console.log("Start recording");
+ } catch (e) {
+ console.error("Could not start recording", e);
+ }
+ }
+ stopRecording() {
+ if (!mediaRecorder) {
+ console.error("Recording not started");
+ return;
+ }
+ console.log("Stop recording");
+ mediaRecorder.stop();
+ mediaRecorder = null;
+ recordedChunks = [];
+ }
+ stopRecordingAndDownload({ name }) {
+ if (!mediaRecorder) {
+ console.error("Recording not started");
+ return;
+ }
+ console.log("Stop recording");
+ mediaRecorder.addEventListener("stop", function () {
+ const blob = new Blob(recordedChunks, { type: "audio/wav" });
+ const url = URL.createObjectURL(blob);
+ const downloadLink = document.createElement("a");
+ downloadLink.href = url;
+ downloadLink.download = name;
+ document.body.appendChild(downloadLink);
+ downloadLink.click();
+ document.body.removeChild(downloadLink);
+ URL.revokeObjectURL(url);
+ recordedChunks = [];
+ });
+ mediaRecorder.stop();
+ mediaRecorder = null;
+ }
+ isRecording() {
+ return !!mediaRecorder;
+ }
+ }
+ Scratch.extensions.register(new Recording());
+})(Scratch);
+//BY -SIPC- 502415953
diff --git a/extensions/-SIPC-/time.js b/extensions/-SIPC-/time.js
index 9343f52677..6a8413c821 100644
--- a/extensions/-SIPC-/time.js
+++ b/extensions/-SIPC-/time.js
@@ -1,122 +1,143 @@
// Name: Time
+// ID: sipctime
// Description: Blocks for interacting with unix timestamps and other date strings.
// By: -SIPC-
(function (Scratch) {
- 'use strict';
- const icon = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSI4MS44ODU0IiBoZWlnaHQ9IjgwLjYwMzA4IiB2aWV3Qm94PSIwLDAsODEuODg1NCw4MC42MDMwOCI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTE5OS4wNTczLC0xMzkuNjk4NDYpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTI4MC45NDI3LDE4MGMwLDIyLjI1NzkyIC0xOC4zMzA2Nyw0MC4zMDE1NCAtNDAuOTQyNyw0MC4zMDE1NGMtMjIuNjEyMDMsMCAtNDAuOTQyNywtMTguMDQzNjEgLTQwLjk0MjcsLTQwLjMwMTU0YzAsLTIyLjI1NzkyIDE4LjMzMDY3LC00MC4zMDE1NCA0MC45NDI3LC00MC4zMDE1NGMyMi42MTIwMywwIDQwLjk0MjcsMTguMDQzNjEgNDAuOTQyNyw0MC4zMDE1NHoiIGZpbGw9IiNmZjgwMDAiIHN0cm9rZS13aWR0aD0iMCIvPjxwYXRoIGQ9Ik0yNjYuNTM0MzcsMTgwYzAsMTQuNjQxMjkgLTExLjg5MzA4LDI2LjUzNDM3IC0yNi41MzQzNywyNi41MzQzN2MtMTQuNjQxMjksMCAtMjYuNTM0MzcsLTExLjg5MzA4IC0yNi41MzQzNywtMjYuNTM0MzdjMCwtMTQuNjQxMjkgMTEuODkzMDgsLTI2LjUzNDM3IDI2LjUzNDM3LC0yNi41MzQzN2MxNC42NDEyOSwwIDI2LjUzNDM3LDExLjg5MzA4IDI2LjUzNDM3LDI2LjUzNDM3ek0yNTMuMjE5OCwxODUuOTcwMjNsLTExLjMyNDQ5LC02LjUzODgzdi0xNC41OTM5aC0zLjc5MDYydjE3LjA1NzgxaDAuNTIxMjFsMTIuNjk4NTksNy4zNDQzM3oiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMSIvPjwvZz48L2c+PC9zdmc+PCEtLXJvdGF0aW9uQ2VudGVyOjQwLjk0MjY5NjA1MzgwMTE0OjQwLjMwMTUzNTI2NTQ4NjcwNi0tPg==';
- const icon2 = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxNzUiIGhlaWdodD0iMTc1IiB2aWV3Qm94PSIwLDAsMTc1LDE3NSI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTE1Mi41LC05Mi41KSI+PGcgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aXNQYWludGluZ0xheWVyJnF1b3Q7OnRydWV9IiBmaWxsPSIjZmZmZmZmIiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTMyNy41LDE4MGMwLDQ4LjI4MTI1IC0zOS4yMTg3NSw4Ny41IC04Ny41LDg3LjVjLTQ4LjI4MTI1LDAgLTg3LjUsLTM5LjIxODc1IC04Ny41LC04Ny41YzAsLTQ4LjI4MTI1IDM5LjIxODc1LC04Ny41IDg3LjUsLTg3LjVjNDguMjgxMjUsMCA4Ny41LDM5LjIxODc1IDg3LjUsODcuNXpNMjgzLjU5Mzc1LDE5OS42ODc1bC0zNy4zNDM3NSwtMjEuNTYyNXYtNDguMTI1aC0xMi41djU2LjI1aDEuNzE4NzVsNDEuODc1LDI0LjIxODc1eiIvPjwvZz48L2c+PC9zdmc+PCEtLXJvdGF0aW9uQ2VudGVyOjg3LjU6ODcuNS0tPg==';
- class Time {
- getInfo() {
- return {
- id: 'sipctime',
- name: 'Time',
- color1: '#ff8000',
- color2: '#804000',
- color3: '#804000',
- menuIconURI: icon,
- blockIconURI: icon2,
- blocks: [
- {
- opcode: 'Timestamp',
- blockType: Scratch.BlockType.REPORTER,
- text: 'current timestamp',
- arguments: {}
- },
- {
- opcode: 'timezone',
- blockType: Scratch.BlockType.REPORTER,
- text: 'current time zone',
- arguments: {}
- },
- {
- opcode: 'Timedata',
- blockType: Scratch.BlockType.REPORTER,
- text: 'get [Timedata] from [timestamp]',
- arguments: {
- timestamp: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1145141980000'
- },
- Timedata: {
- type: Scratch.ArgumentType.STRING,
- menu: "Time",
- defaultValue: 'year'
- }
- }
- },
- {
- opcode: 'TimestampToTime',
- blockType: Scratch.BlockType.REPORTER,
- text: 'convert [timestamp] to datetime',
- arguments: {
- timestamp: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1145141980000'
- }
- }
- },
- {
- opcode: 'TimeToTimestamp',
- blockType: Scratch.BlockType.REPORTER,
- text: 'convert [time] to timestamp',
- arguments: {
- time: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '2006-04-16 06:59:40'
- }
- }
- }
- ],
- menus: {
- Time: {
- acceptReporters: true,
- items: ['year', 'month', 'day', 'hour', 'minute', 'second']
- },
- }
- };
- }
- Timestamp() {
- return Date.now();
- }
- timezone() {
- return 'UTC+' + new Date().getTimezoneOffset() / -60;
- }
- Timedata(args) {
- args.timestamp = args.timestamp ? args.timestamp : null;
- let date1 = new Date(Scratch.Cast.toNumber(args.timestamp));
- switch (args.Timedata) {
- case 'year':
- return date1.getFullYear();
- case 'month':
- return date1.getMonth() + 1 < 10 ? '0' + (date1.getMonth() + 1) : date1.getMonth() + 1;
- case 'day':
- return date1.getDate() < 10 ? '0' + date1.getDate() : date1.getDate();
- case 'hour':
- return date1.getHours() < 10 ? '0' + date1.getHours() : date1.getHours();
- case 'minute':
- return date1.getMinutes() < 10 ? '0' + date1.getMinutes() : date1.getMinutes();
- case 'second':
- return date1.getSeconds() < 10 ? '0' + date1.getSeconds() : date1.getSeconds();
- }
- return 0;
- }
- TimestampToTime({ timestamp }) {
- timestamp = timestamp ? timestamp : null;
- let date2 = new Date(timestamp);
- let Y = date2.getFullYear() + '-';
- let M = (date2.getMonth() + 1 < 10 ? '0' + (date2.getMonth() + 1) : date2.getMonth() + 1) + '-';
- let D = (date2.getDate() < 10 ? '0' + date2.getDate() : date2.getDate()) + ' ';
- let h = (date2.getHours() < 10 ? '0' + date2.getHours() : date2.getHours()) + ':';
- let m = (date2.getMinutes() < 10 ? '0' + date2.getMinutes() : date2.getMinutes()) + ':';
- let s = date2.getSeconds() < 10 ? '0' + date2.getSeconds() : date2.getSeconds();
- return Y + M + D + h + m + s;
- }
- TimeToTimestamp({ time }) {
- let data3 = time;
- let timestamp = Date.parse(data3);
- return timestamp;
- }
+ "use strict";
+ const icon =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSI4MS44ODU0IiBoZWlnaHQ9IjgwLjYwMzA4IiB2aWV3Qm94PSIwLDAsODEuODg1NCw4MC42MDMwOCI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTE5OS4wNTczLC0xMzkuNjk4NDYpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTI4MC45NDI3LDE4MGMwLDIyLjI1NzkyIC0xOC4zMzA2Nyw0MC4zMDE1NCAtNDAuOTQyNyw0MC4zMDE1NGMtMjIuNjEyMDMsMCAtNDAuOTQyNywtMTguMDQzNjEgLTQwLjk0MjcsLTQwLjMwMTU0YzAsLTIyLjI1NzkyIDE4LjMzMDY3LC00MC4zMDE1NCA0MC45NDI3LC00MC4zMDE1NGMyMi42MTIwMywwIDQwLjk0MjcsMTguMDQzNjEgNDAuOTQyNyw0MC4zMDE1NHoiIGZpbGw9IiNmZjgwMDAiIHN0cm9rZS13aWR0aD0iMCIvPjxwYXRoIGQ9Ik0yNjYuNTM0MzcsMTgwYzAsMTQuNjQxMjkgLTExLjg5MzA4LDI2LjUzNDM3IC0yNi41MzQzNywyNi41MzQzN2MtMTQuNjQxMjksMCAtMjYuNTM0MzcsLTExLjg5MzA4IC0yNi41MzQzNywtMjYuNTM0MzdjMCwtMTQuNjQxMjkgMTEuODkzMDgsLTI2LjUzNDM3IDI2LjUzNDM3LC0yNi41MzQzN2MxNC42NDEyOSwwIDI2LjUzNDM3LDExLjg5MzA4IDI2LjUzNDM3LDI2LjUzNDM3ek0yNTMuMjE5OCwxODUuOTcwMjNsLTExLjMyNDQ5LC02LjUzODgzdi0xNC41OTM5aC0zLjc5MDYydjE3LjA1NzgxaDAuNTIxMjFsMTIuNjk4NTksNy4zNDQzM3oiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMSIvPjwvZz48L2c+PC9zdmc+PCEtLXJvdGF0aW9uQ2VudGVyOjQwLjk0MjY5NjA1MzgwMTE0OjQwLjMwMTUzNTI2NTQ4NjcwNi0tPg==";
+ const icon2 =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxNzUiIGhlaWdodD0iMTc1IiB2aWV3Qm94PSIwLDAsMTc1LDE3NSI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTE1Mi41LC05Mi41KSI+PGcgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aXNQYWludGluZ0xheWVyJnF1b3Q7OnRydWV9IiBmaWxsPSIjZmZmZmZmIiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTMyNy41LDE4MGMwLDQ4LjI4MTI1IC0zOS4yMTg3NSw4Ny41IC04Ny41LDg3LjVjLTQ4LjI4MTI1LDAgLTg3LjUsLTM5LjIxODc1IC04Ny41LC04Ny41YzAsLTQ4LjI4MTI1IDM5LjIxODc1LC04Ny41IDg3LjUsLTg3LjVjNDguMjgxMjUsMCA4Ny41LDM5LjIxODc1IDg3LjUsODcuNXpNMjgzLjU5Mzc1LDE5OS42ODc1bC0zNy4zNDM3NSwtMjEuNTYyNXYtNDguMTI1aC0xMi41djU2LjI1aDEuNzE4NzVsNDEuODc1LDI0LjIxODc1eiIvPjwvZz48L2c+PC9zdmc+PCEtLXJvdGF0aW9uQ2VudGVyOjg3LjU6ODcuNS0tPg==";
+ class Time {
+ getInfo() {
+ return {
+ id: "sipctime",
+ name: "Time",
+ color1: "#ff8000",
+ color2: "#804000",
+ color3: "#804000",
+ menuIconURI: icon,
+ blockIconURI: icon2,
+ blocks: [
+ {
+ opcode: "Timestamp",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "current timestamp",
+ arguments: {},
+ },
+ {
+ opcode: "timezone",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "current time zone",
+ arguments: {},
+ },
+ {
+ opcode: "Timedata",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "get [Timedata] from [timestamp]",
+ arguments: {
+ timestamp: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1145141980000",
+ },
+ Timedata: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "Time",
+ defaultValue: "year",
+ },
+ },
+ },
+ {
+ opcode: "TimestampToTime",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "convert [timestamp] to datetime",
+ arguments: {
+ timestamp: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1145141980000",
+ },
+ },
+ },
+ {
+ opcode: "TimeToTimestamp",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "convert [time] to timestamp",
+ arguments: {
+ time: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "2006-04-16 06:59:40",
+ },
+ },
+ },
+ ],
+ menus: {
+ Time: {
+ acceptReporters: true,
+ items: ["year", "month", "day", "hour", "minute", "second"],
+ },
+ },
+ };
}
- Scratch.extensions.register(new Time());
+ Timestamp() {
+ return Date.now();
+ }
+ timezone() {
+ return "UTC+" + new Date().getTimezoneOffset() / -60;
+ }
+ Timedata(args) {
+ args.timestamp = args.timestamp ? args.timestamp : null;
+ let date1 = new Date(Scratch.Cast.toNumber(args.timestamp));
+ switch (args.Timedata) {
+ case "year":
+ return date1.getFullYear();
+ case "month":
+ return date1.getMonth() + 1 < 10
+ ? "0" + (date1.getMonth() + 1)
+ : date1.getMonth() + 1;
+ case "day":
+ return date1.getDate() < 10 ? "0" + date1.getDate() : date1.getDate();
+ case "hour":
+ return date1.getHours() < 10
+ ? "0" + date1.getHours()
+ : date1.getHours();
+ case "minute":
+ return date1.getMinutes() < 10
+ ? "0" + date1.getMinutes()
+ : date1.getMinutes();
+ case "second":
+ return date1.getSeconds() < 10
+ ? "0" + date1.getSeconds()
+ : date1.getSeconds();
+ }
+ return 0;
+ }
+ TimestampToTime({ timestamp }) {
+ timestamp = timestamp ? timestamp : null;
+ let date2 = new Date(timestamp);
+ let Y = date2.getFullYear() + "-";
+ let M =
+ (date2.getMonth() + 1 < 10
+ ? "0" + (date2.getMonth() + 1)
+ : date2.getMonth() + 1) + "-";
+ let D =
+ (date2.getDate() < 10 ? "0" + date2.getDate() : date2.getDate()) + " ";
+ let h =
+ (date2.getHours() < 10 ? "0" + date2.getHours() : date2.getHours()) +
+ ":";
+ let m =
+ (date2.getMinutes() < 10
+ ? "0" + date2.getMinutes()
+ : date2.getMinutes()) + ":";
+ let s =
+ date2.getSeconds() < 10 ? "0" + date2.getSeconds() : date2.getSeconds();
+ return Y + M + D + h + m + s;
+ }
+ TimeToTimestamp({ time }) {
+ let data3 = time;
+ let timestamp = Date.parse(data3);
+ return timestamp;
+ }
+ }
+ Scratch.extensions.register(new Time());
})(Scratch);
diff --git a/extensions/0832/rxFS.js b/extensions/0832/rxFS.js
index 85d204539d..b903ed8828 100644
--- a/extensions/0832/rxFS.js
+++ b/extensions/0832/rxFS.js
@@ -2,243 +2,255 @@
* Made by 0832
* This file was originally under the rxLI Version 2.1 license:
* https://0832k12.github.io/rxLi/2.1/
- *
+ *
* However they have since claimed it to be "directly compatible with MIT license",
* which is the license we use this file under.
*/
(function (Scratch) {
- 'use strict';
-
- var rxFSfi = new Array();
- var rxFSsy = new Array();
- var Search, i, str, str2;
-
- const file = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIzMC4zMTIxMiIgaGVpZ2h0PSIyNC4yNDk2NyIgdmlld0JveD0iMCwwLDMwLjMxMjEyLDI0LjI0OTY3Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMzA0Ljg0Mzk0LC0xNjcuODc1MTYpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGw9IiNmZmI5MDAiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIHN0eWxlPSJtaXgtYmxlbmQtbW9kZTogbm9ybWFsIj48cGF0aCBkPSJNMzE5Ljk5OTk5LDE3MC45MDYzN2gxMi4xMjQ4M2MxLjY3NDEyLDAgMy4wMzEyNCwxLjM1NzEyIDMuMDMxMjQsMy4wMzEydjE1LjE1NjA3YzAsMS42NzQwOCAtMS4zNTcxMiwzLjAzMTIgLTMuMDMxMjQsMy4wMzEyaC0yNC4yNDk2NmMtMS42NzQxMiwwIC0zLjAzMTIyLC0xLjM1NzEyIC0zLjAzMTIyLC0zLjAzMTJ2LTE4LjE4NzI3YzAsLTEuNjgyMzIgMS4zNDg5LC0zLjAzMTIgMy4wMzEyMiwtMy4wMzEyaDkuMDkzNjN6Ii8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6MTUuMTU2MDYwMDAwMDAwMDI1OjEyLjEyNDgzNTAwMDAwMDAxOS0tPg==';
- const wenj = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIyMS4zMzc2IiBoZWlnaHQ9IjI3LjEzNTI4IiB2aWV3Qm94PSIwLDAsMjEuMzM3NiwyNy4xMzUyOCI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTMwOS4zMzEyLC0xNjYuNDMyMzYpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIHN0eWxlPSJtaXgtYmxlbmQtbW9kZTogbm9ybWFsIj48cGF0aCBkPSJNMzMwLjU0OTY3LDE5MC41MzY0YzAsMS42NzQxMiAtMS4zNTcxMiwzLjAzMTI0IC0zLjAzMTIsMy4wMzEyNGgtMTUuMTU2MDdjLTEuNjc0MDgsMCAtMy4wMzEyLC0xLjM1NzEyIC0zLjAzMTIsLTMuMDMxMjR2LTIxLjA3MjgyYzAsLTEuNjc0MTIgMS4zNTcxMiwtMy4wMzEyMiAzLjAzMTIsLTMuMDMxMjJoMTQuMDQ5MzNsNC4xMzc5NCw0LjEwMTc3eiIgZmlsbD0iI2FmYWZhZiIgc3Ryb2tlPSJub25lIiBzdHJva2UtbGluZWNhcD0iYnV0dCIvPjxwYXRoIGQ9Ik0zMzAuNjY4OCwxNzAuNzAxNDdsLTIuMTE5OTIsMC4wNTEzN2MtMS4xMzM3NCwwIC0yLjA1MjgyLC0wLjkxOTA4IC0yLjA1MjgyLC0yLjA1Mjg1di0yLjEwMjgyeiIgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aW5kZXgmcXVvdDs6bnVsbH0iIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiLz48cGF0aCBkPSJNMzEyLjgzMjY0LDE3My41MTk1OGgxMi4wMDE0MSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmZmZmIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48cGF0aCBkPSJNMzEyLjc5MjMyLDE3Ni44NzI5MWgxMi4xNzc5IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjxwYXRoIGQ9Ik0zMTIuODA1NzYsMTgwLjE3MTZoNi44ODMxNiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmZmZmIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48L2c+PC9nPjwvc3ZnPjwhLS1yb3RhdGlvbkNlbnRlcjoxMC42Njg4MDAwMDAwMDAwMzM6MTMuNTY3NjM3NTAwMDAwMDE4LS0+';
-
-
- class rxFS {
- getInfo() {
- return {
- id: '0832rxfs',
- name: 'rxFS',
- color1: '#2bdab7',
- blocks: [
- {
- blockIconURI: wenj,
- opcode: 'start',
- blockType: Scratch.BlockType.COMMAND,
- text: 'New [STR] ',
- arguments: {
- STR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '📃'
- }
- }
- },
- {
- blockIconURI: wenj,
- opcode: 'file',
- blockType: Scratch.BlockType.COMMAND,
- text: 'Set [STR] to [STR2] ',
- arguments: {
- STR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '📃'
- },
- STR2: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'rxFS is good!'
- }
- }
- },
- {
- blockIconURI: wenj,
- opcode: 'sync',
- blockType: Scratch.BlockType.COMMAND,
- text: 'Change the location of [STR] to [STR2] ',
- arguments: {
- STR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '📃'
- },
- STR2: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '📃'
- }
- }
- },
- {
- blockIconURI: wenj,
- opcode: 'del',
- blockType: Scratch.BlockType.COMMAND,
- text: 'Delete [STR] ',
- arguments: {
- STR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '📃'
- }
- }
- },
- {
- blockIconURI: wenj,
- opcode: 'webin',
- blockType: Scratch.BlockType.REPORTER,
- text: 'Load [STR] from the network',
- arguments: {
- STR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'https://0832k12.github.io/rxFS/hello.txt'
- }
- }
- },
- {
- blockIconURI: wenj,
- opcode: 'open',
- blockType: Scratch.BlockType.REPORTER,
- text: 'Open [STR]',
- arguments: {
- STR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '📃'
- }
- }
- },
- {
- blockIconURI: file,
- opcode: 'clean',
- blockType: Scratch.BlockType.COMMAND,
- text: 'Clear file system',
- arguments: {}
- },
- {
- blockIconURI: file,
- opcode: 'in',
- blockType: Scratch.BlockType.COMMAND,
- text: 'Import file system from [STR]',
- arguments: {
- STR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '📁'
- }
- }
- },
- {
- blockIconURI: file,
- opcode: 'out',
- blockType: Scratch.BlockType.REPORTER,
- text: 'Export file system',
- arguments: {}
- },
- {
- blockIconURI: file,
- opcode: 'list',
- blockType: Scratch.BlockType.REPORTER,
- text: 'List the contents under the same folder [STR]',
- arguments: {
- STR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '📁'
- }
- }
- },
- {
- blockIconURI: file,
- opcode: 'search',
- blockType: Scratch.BlockType.REPORTER,
- text: 'Search [STR]',
- arguments: {
- STR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '📃'
- }
- }
- }
- ]
- };
- }
+ "use strict";
+ var rxFSfi = new Array();
+ var rxFSsy = new Array();
+ var Search, i, str, str2;
- clean() {
- rxFSfi = [];
- rxFSsy = [];
- }
+ const file =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIzMC4zMTIxMiIgaGVpZ2h0PSIyNC4yNDk2NyIgdmlld0JveD0iMCwwLDMwLjMxMjEyLDI0LjI0OTY3Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMzA0Ljg0Mzk0LC0xNjcuODc1MTYpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGw9IiNmZmI5MDAiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIHN0eWxlPSJtaXgtYmxlbmQtbW9kZTogbm9ybWFsIj48cGF0aCBkPSJNMzE5Ljk5OTk5LDE3MC45MDYzN2gxMi4xMjQ4M2MxLjY3NDEyLDAgMy4wMzEyNCwxLjM1NzEyIDMuMDMxMjQsMy4wMzEydjE1LjE1NjA3YzAsMS42NzQwOCAtMS4zNTcxMiwzLjAzMTIgLTMuMDMxMjQsMy4wMzEyaC0yNC4yNDk2NmMtMS42NzQxMiwwIC0zLjAzMTIyLC0xLjM1NzEyIC0zLjAzMTIyLC0zLjAzMTJ2LTE4LjE4NzI3YzAsLTEuNjgyMzIgMS4zNDg5LC0zLjAzMTIgMy4wMzEyMiwtMy4wMzEyaDkuMDkzNjN6Ii8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6MTUuMTU2MDYwMDAwMDAwMDI1OjEyLjEyNDgzNTAwMDAwMDAxOS0tPg==";
+ const wenj =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIyMS4zMzc2IiBoZWlnaHQ9IjI3LjEzNTI4IiB2aWV3Qm94PSIwLDAsMjEuMzM3NiwyNy4xMzUyOCI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTMwOS4zMzEyLC0xNjYuNDMyMzYpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIHN0eWxlPSJtaXgtYmxlbmQtbW9kZTogbm9ybWFsIj48cGF0aCBkPSJNMzMwLjU0OTY3LDE5MC41MzY0YzAsMS42NzQxMiAtMS4zNTcxMiwzLjAzMTI0IC0zLjAzMTIsMy4wMzEyNGgtMTUuMTU2MDdjLTEuNjc0MDgsMCAtMy4wMzEyLC0xLjM1NzEyIC0zLjAzMTIsLTMuMDMxMjR2LTIxLjA3MjgyYzAsLTEuNjc0MTIgMS4zNTcxMiwtMy4wMzEyMiAzLjAzMTIsLTMuMDMxMjJoMTQuMDQ5MzNsNC4xMzc5NCw0LjEwMTc3eiIgZmlsbD0iI2FmYWZhZiIgc3Ryb2tlPSJub25lIiBzdHJva2UtbGluZWNhcD0iYnV0dCIvPjxwYXRoIGQ9Ik0zMzAuNjY4OCwxNzAuNzAxNDdsLTIuMTE5OTIsMC4wNTEzN2MtMS4xMzM3NCwwIC0yLjA1MjgyLC0wLjkxOTA4IC0yLjA1MjgyLC0yLjA1Mjg1di0yLjEwMjgyeiIgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aW5kZXgmcXVvdDs6bnVsbH0iIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiLz48cGF0aCBkPSJNMzEyLjgzMjY0LDE3My41MTk1OGgxMi4wMDE0MSIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmZmZmIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48cGF0aCBkPSJNMzEyLjc5MjMyLDE3Ni44NzI5MWgxMi4xNzc5IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjxwYXRoIGQ9Ik0zMTIuODA1NzYsMTgwLjE3MTZoNi44ODMxNiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmZmZmIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48L2c+PC9nPjwvc3ZnPjwhLS1yb3RhdGlvbkNlbnRlcjoxMC42Njg4MDAwMDAwMDAwMzM6MTMuNTY3NjM3NTAwMDAwMDE4LS0+";
- sync({ STR, STR2 }) {
- str = btoa(unescape(encodeURIComponent(STR)));
- str2 = btoa(unescape(encodeURIComponent(STR2)));
- if (rxFSsy.indexOf(str) + 1 == 0) {
- rxFSsy[((rxFSsy.indexOf(str) + 1) - 1)] = str2;
- }
- }
+ class rxFS {
+ getInfo() {
+ return {
+ id: "0832rxfs",
+ name: "rxFS",
+ color1: "#2bdab7",
+ blocks: [
+ {
+ blockIconURI: wenj,
+ opcode: "start",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "New [STR] ",
+ arguments: {
+ STR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "📃",
+ },
+ },
+ },
+ {
+ blockIconURI: wenj,
+ opcode: "file",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "Set [STR] to [STR2] ",
+ arguments: {
+ STR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "📃",
+ },
+ STR2: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "rxFS is good!",
+ },
+ },
+ },
+ {
+ blockIconURI: wenj,
+ opcode: "sync",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "Change the location of [STR] to [STR2] ",
+ arguments: {
+ STR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "📃",
+ },
+ STR2: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "📃",
+ },
+ },
+ },
+ {
+ blockIconURI: wenj,
+ opcode: "del",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "Delete [STR] ",
+ arguments: {
+ STR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "📃",
+ },
+ },
+ },
+ {
+ blockIconURI: wenj,
+ opcode: "webin",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "Load [STR] from the network",
+ arguments: {
+ STR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "https://0832k12.github.io/rxFS/hello.txt",
+ },
+ },
+ },
+ {
+ blockIconURI: wenj,
+ opcode: "open",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "Open [STR]",
+ arguments: {
+ STR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "📃",
+ },
+ },
+ },
+ {
+ blockIconURI: file,
+ opcode: "clean",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "Clear file system",
+ arguments: {},
+ },
+ {
+ blockIconURI: file,
+ opcode: "in",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "Import file system from [STR]",
+ arguments: {
+ STR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "📁",
+ },
+ },
+ },
+ {
+ blockIconURI: file,
+ opcode: "out",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "Export file system",
+ arguments: {},
+ },
+ {
+ blockIconURI: file,
+ opcode: "list",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "List the contents under the same folder [STR]",
+ arguments: {
+ STR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "📁",
+ },
+ },
+ },
+ {
+ blockIconURI: file,
+ opcode: "search",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "Search [STR]",
+ arguments: {
+ STR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "📃",
+ },
+ },
+ },
+ ],
+ };
+ }
- start({ STR }) {
- str = btoa(unescape(encodeURIComponent(STR)));
- if (!(str.charAt((str.length - 1)) == '/') && rxFSsy.indexOf(str) + 1 == 0) {
- rxFSfi.splice(((rxFSfi.length + 1) - 1), 0, null);
- rxFSsy.splice(((rxFSsy.length + 1) - 1), 0, str);
- }
- }
+ clean() {
+ rxFSfi = [];
+ rxFSsy = [];
+ }
- open({ STR }) {
- return decodeURIComponent(escape(atob(rxFSfi[((rxFSsy.indexOf(btoa(unescape(encodeURIComponent(STR)))) + 1) - 1)])));
- }
+ sync({ STR, STR2 }) {
+ str = btoa(unescape(encodeURIComponent(STR)));
+ str2 = btoa(unescape(encodeURIComponent(STR2)));
+ if (rxFSsy.indexOf(str) + 1 == 0) {
+ rxFSsy[rxFSsy.indexOf(str) + 1 - 1] = str2;
+ }
+ }
- del({ STR }) {
- str = btoa(unescape(encodeURIComponent(STR)));
- rxFSfi[((rxFSsy.indexOf(str) + 1) - 1)] = undefined;
- rxFSsy[((rxFSsy.indexOf(str) + 1) - 1)] = undefined;
- }
+ start({ STR }) {
+ str = btoa(unescape(encodeURIComponent(STR)));
+ if (
+ !(str.charAt(str.length - 1) == "/") &&
+ rxFSsy.indexOf(str) + 1 == 0
+ ) {
+ rxFSfi.splice(rxFSfi.length + 1 - 1, 0, null);
+ rxFSsy.splice(rxFSsy.length + 1 - 1, 0, str);
+ }
+ }
- file({ STR, STR2 }) {
- rxFSfi[((rxFSsy.indexOf(btoa(unescape(encodeURIComponent(STR)))) + 1) - 1)] = btoa(unescape(encodeURIComponent(STR2)));
- }
+ open({ STR }) {
+ return decodeURIComponent(
+ escape(
+ atob(
+ rxFSfi[
+ rxFSsy.indexOf(btoa(unescape(encodeURIComponent(STR)))) + 1 - 1
+ ]
+ )
+ )
+ );
+ }
- search({ STR }) {
- Search = '';
- i = 0;
- str = btoa(unescape(encodeURIComponent(STR)));
- for (var i in rxFSsy) {
- if (!(rxFSsy[(i)].indexOf(str) == undefined)) {
- Search = [Search, 'LA==', rxFSsy[(i)]].join('');
- }
- }
- return decodeURIComponent(escape(atob(Search)));
- }
+ del({ STR }) {
+ str = btoa(unescape(encodeURIComponent(STR)));
+ rxFSfi[rxFSsy.indexOf(str) + 1 - 1] = undefined;
+ rxFSsy[rxFSsy.indexOf(str) + 1 - 1] = undefined;
+ }
- list({ STR }) {
- Search = '';
- i = 0;
- str = btoa(unescape(encodeURIComponent(STR)));
- for (var i in rxFSsy) {
- if (rxFSsy[(i)].slice(0, str.length) == str) {
- Search = [Search, 'LA==', rxFSsy[(i)]].join('');
- }
- }
- return decodeURIComponent(escape(atob(Search)));
- }
+ file({ STR, STR2 }) {
+ rxFSfi[rxFSsy.indexOf(btoa(unescape(encodeURIComponent(STR)))) + 1 - 1] =
+ btoa(unescape(encodeURIComponent(STR2)));
+ }
- webin({ STR }) {
- return Scratch.fetch(STR)
- .then((response) => {
- return response.text();
- })
- .catch((error) => {
- console.error(error);
- return 'undefined';
- });
+ search({ STR }) {
+ Search = "";
+ i = 0;
+ str = btoa(unescape(encodeURIComponent(STR)));
+ for (var i in rxFSsy) {
+ if (!(rxFSsy[i].indexOf(str) == undefined)) {
+ Search = [Search, "LA==", rxFSsy[i]].join("");
}
+ }
+ return decodeURIComponent(escape(atob(Search)));
+ }
- in({ STR }) {
- rxFSfi = STR.slice(0, STR.indexOf('|')).split(',');
- rxFSsy = STR.slice(((STR.indexOf('|') + 1)), STR.length).split(',');
+ list({ STR }) {
+ Search = "";
+ i = 0;
+ str = btoa(unescape(encodeURIComponent(STR)));
+ for (var i in rxFSsy) {
+ if (rxFSsy[i].slice(0, str.length) == str) {
+ Search = [Search, "LA==", rxFSsy[i]].join("");
}
+ }
+ return decodeURIComponent(escape(atob(Search)));
+ }
- out() {
- return [rxFSfi.join(','), '|', rxFSsy.join(',')].join('');
- }
+ webin({ STR }) {
+ return Scratch.fetch(STR)
+ .then((response) => {
+ return response.text();
+ })
+ .catch((error) => {
+ console.error(error);
+ return "undefined";
+ });
+ }
+
+ in({ STR }) {
+ rxFSfi = STR.slice(0, STR.indexOf("|")).split(",");
+ rxFSsy = STR.slice(STR.indexOf("|") + 1, STR.length).split(",");
+ }
+
+ out() {
+ return [rxFSfi.join(","), "|", rxFSsy.join(",")].join("");
}
+ }
- Scratch.extensions.register(new rxFS());
+ Scratch.extensions.register(new rxFS());
})(Scratch);
diff --git a/extensions/0832/rxFS2.js b/extensions/0832/rxFS2.js
index 5f1a514945..871ef0b1ca 100644
--- a/extensions/0832/rxFS2.js
+++ b/extensions/0832/rxFS2.js
@@ -1,4 +1,5 @@
// Name: rxFS
+// ID: 0832rxfs2
// Description: Blocks for interacting with a virtual in-memory filesystem.
// By: 0832
@@ -6,290 +7,321 @@
* Made by 0832
* This file was originally under the rxLI Version 2.1 license:
* https://0832k12.github.io/rxLi/2.1/
- *
+ *
* However they have since claimed it to be "directly compatible with MIT license",
* which is the license we use this file under.
*/
(function (Scratch) {
- 'use strict';
+ "use strict";
- Scratch.translate.setup({
- zh: {
- start: '新建 [STR] ',
- folder: '设置 [STR] 为 [STR2] ',
- folder_default: '大主教大祭司主宰世界!',
- sync: '将 [STR] 的位置更改为 [STR2] ',
- del: '删除 [STR] ',
- webin: '从网络加载 [STR]',
- open: '打开 [STR]',
- clean: '清空文件系统',
- in: '从 [STR] 导入文件系统',
- out: '导出文件系统',
- list: '列出 [STR] 下的所有文件',
- search: '搜索 [STR]'
- },
- ru: {
- start: 'Создать [STR]',
- folder: 'Установить [STR] в [STR2]',
- folder_default: 'Архиепископ Верховный жрец Правитель мира!',
- sync: 'Изменить расположение [STR] на [STR2]',
- del: 'Удалить [STR]',
- webin: 'Загрузить [STR] из Интернета',
- open: 'Открыть [STR]',
- clean: 'Очистить файловую систему',
- in: 'Импортировать файловую систему из [STR]',
- out: 'Экспортировать файловую систему',
- list: 'Список всех файлов в [STR]',
- search: 'Поиск [STR]'
- },
- jp: {
- start: '新規作成 [STR]',
- folder: '[STR] を [STR2] に設定する',
- folder_default: '大主教大祭司世界の支配者!',
- sync: '[STR] の位置を [STR2] に変更する',
- del: '[STR] を削除する',
- webin: '[STR] をウェブから読み込む',
- open: '[STR] を開く',
- clean: 'ファイルシステムをクリアする',
- in: '[STR] からファイルシステムをインポートする',
- out: 'ファイルシステムをエクスポートする',
- list: '[STR] にあるすべてのファイルをリストする',
- search: '[STR] を検索する'
- }
- });
-
- var rxFSfi = new Array();
- var rxFSsy = new Array();
- var Search, i, str, str2;
+ Scratch.translate.setup({
+ zh: {
+ start: "新建 [STR] ",
+ folder: "设置 [STR] 为 [STR2] ",
+ folder_default: "大主教大祭司主宰世界!",
+ sync: "将 [STR] 的位置更改为 [STR2] ",
+ del: "删除 [STR] ",
+ webin: "从网络加载 [STR]",
+ open: "打开 [STR]",
+ clean: "清空文件系统",
+ in: "从 [STR] 导入文件系统",
+ out: "导出文件系统",
+ list: "列出 [STR] 下的所有文件",
+ search: "搜索 [STR]",
+ },
+ ru: {
+ start: "Создать [STR]",
+ folder: "Установить [STR] в [STR2]",
+ folder_default: "Архиепископ Верховный жрец Правитель мира!",
+ sync: "Изменить расположение [STR] на [STR2]",
+ del: "Удалить [STR]",
+ webin: "Загрузить [STR] из Интернета",
+ open: "Открыть [STR]",
+ clean: "Очистить файловую систему",
+ in: "Импортировать файловую систему из [STR]",
+ out: "Экспортировать файловую систему",
+ list: "Список всех файлов в [STR]",
+ search: "Поиск [STR]",
+ },
+ jp: {
+ start: "新規作成 [STR]",
+ folder: "[STR] を [STR2] に設定する",
+ folder_default: "大主教大祭司世界の支配者!",
+ sync: "[STR] の位置を [STR2] に変更する",
+ del: "[STR] を削除する",
+ webin: "[STR] をウェブから読み込む",
+ open: "[STR] を開く",
+ clean: "ファイルシステムをクリアする",
+ in: "[STR] からファイルシステムをインポートする",
+ out: "ファイルシステムをエクスポートする",
+ list: "[STR] にあるすべてのファイルをリストする",
+ search: "[STR] を検索する",
+ },
+ });
- const folder = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIyOC40NjI1IiBoZWlnaHQ9IjI3LjciIHZpZXdCb3g9IjAsMCwyOC40NjI1LDI3LjciPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMjYuMDE5NTMsLTE2NC4xMTg3NSkiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbD0iIzk5NjZmZiIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWRhc2hhcnJheT0iIiBzdHJva2UtZGFzaG9mZnNldD0iMCIgZm9udC1mYW1pbHk9IlNhbnMgU2VyaWYiIGZvbnQtd2VpZ2h0PSJub3JtYWwiIGZvbnQtc2l6ZT0iNDAiIHRleHQtYW5jaG9yPSJzdGFydCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjx0ZXh0IHRyYW5zZm9ybT0idHJhbnNsYXRlKDIyNi4yNjk1MywxODUuNzY4NzUpIHNjYWxlKDAuNSwwLjUpIiBmb250LXNpemU9IjQwIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmaWxsPSIjOTk2NmZmIiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBmb250LWZhbWlseT0iU2FucyBTZXJpZiIgZm9udC13ZWlnaHQ9Im5vcm1hbCIgdGV4dC1hbmNob3I9InN0YXJ0IiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHRzcGFuIHg9IjAiIGR5PSIwIj7wn5OBPC90c3Bhbj48L3RleHQ+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6MTMuOTgwNDY4NzU6MTUuODgxMjQ5MjM3MDYwNTMtLT4=';
- const file = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIyOC40NjI1IiBoZWlnaHQ9IjI3LjciIHZpZXdCb3g9IjAsMCwyOC40NjI1LDI3LjciPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMjYuMDE5NTMsLTE2NC4xMTg3NSkiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbD0iIzk5NjZmZiIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWRhc2hhcnJheT0iIiBzdHJva2UtZGFzaG9mZnNldD0iMCIgZm9udC1mYW1pbHk9IlNhbnMgU2VyaWYiIGZvbnQtd2VpZ2h0PSJub3JtYWwiIGZvbnQtc2l6ZT0iNDAiIHRleHQtYW5jaG9yPSJzdGFydCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjx0ZXh0IHRyYW5zZm9ybT0idHJhbnNsYXRlKDIyNi4yNjk1MywxODUuNzY4NzUpIHNjYWxlKDAuNSwwLjUpIiBmb250LXNpemU9IjQwIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmaWxsPSIjOTk2NmZmIiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBmb250LWZhbWlseT0iU2FucyBTZXJpZiIgZm9udC13ZWlnaHQ9Im5vcm1hbCIgdGV4dC1hbmNob3I9InN0YXJ0IiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHRzcGFuIHg9IjAiIGR5PSIwIj7wn5ODPC90c3Bhbj48L3RleHQ+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6MTMuOTgwNDY4NzU6MTUuODgxMjQ5NjE4NTMwMjYyLS0+';
+ var rxFSfi = new Array();
+ var rxFSsy = new Array();
+ var Search, i, str, str2;
- class rxFS {
- getInfo() {
- return {
- id: '0832rxfs2',
- name: 'rxFS',
- color1: '#192d50',
- color2: '#192d50',
- color3: '#192d50',
- blocks: [
- {
- blockIconURI: file,
- opcode: 'start',
- blockType: Scratch.BlockType.COMMAND,
- text: Scratch.translate({ id: 'start', default: 'Create [STR]' }),
- arguments: {
- STR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '/rxFS/example'
- }
- }
- },
- {
- blockIconURI: file,
- opcode: 'folder',
- blockType: Scratch.BlockType.COMMAND,
- text: Scratch.translate({ id: 'folder', default: 'Set [STR] to [STR2]' }),
- arguments: {
- STR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '/rxFS/example'
- },
- STR2: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: Scratch.translate({ id: 'folder_default', default: 'rxFS is good!' }),
- }
- }
- },
- {
- blockIconURI: file,
- opcode: 'sync',
- blockType: Scratch.BlockType.COMMAND,
- text: Scratch.translate({ id: 'sync', default: 'Change the location of [STR] to [STR2]' }),
- arguments: {
- STR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '/rxFS/example'
- },
- STR2: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '/rxFS/example'
- }
- }
- },
- {
- blockIconURI: file,
- opcode: 'del',
- blockType: Scratch.BlockType.COMMAND,
- text: Scratch.translate({ id: 'del', default: 'Delete [STR]' }),
- arguments: {
- STR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '/rxFS/example'
- }
- }
- },
- {
- blockIconURI: file,
- opcode: 'webin',
- blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate({ id: 'webin', default: 'Load [STR] from the web' }),
- arguments: {
- STR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'https://0832k12.github.io/rxFS/hello.txt'
- }
- }
- },
- {
- blockIconURI: file,
- opcode: 'open',
- blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate({ id: 'open', default: 'Open [STR]' }),
- arguments: {
- STR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '/rxFS/example'
- }
- }
- },
- '---',
- {
- blockIconURI: folder,
- opcode: 'clean',
- blockType: Scratch.BlockType.COMMAND,
- text: Scratch.translate({ id: 'clean', default: 'Clear the file system' }),
- arguments: {}
- },
- {
- blockIconURI: folder,
- opcode: 'in',
- blockType: Scratch.BlockType.COMMAND,
- text: Scratch.translate({ id: 'in', default: 'Import file system from [STR]' }),
- arguments: {
- STR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '/rxFS/'
- }
- }
- },
- {
- blockIconURI: folder,
- opcode: 'out',
- blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate({ id: 'out', default: 'Export file system' }),
- arguments: {}
- },
- {
- blockIconURI: folder,
- opcode: 'list',
- blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate({ id: 'list', default: 'List all files under [STR]' }),
- arguments: {
- STR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '/rxFS/'
- }
- }
- },
- {
- blockIconURI: folder,
- opcode: 'search',
- blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate({ id: 'search', default: 'Search [STR]' }),
- arguments: {
- STR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '/rxFS/example'
- }
- }
- }
- ]
- };
- }
+ const folder =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIyOC40NjI1IiBoZWlnaHQ9IjI3LjciIHZpZXdCb3g9IjAsMCwyOC40NjI1LDI3LjciPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMjYuMDE5NTMsLTE2NC4xMTg3NSkiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbD0iIzk5NjZmZiIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWRhc2hhcnJheT0iIiBzdHJva2UtZGFzaG9mZnNldD0iMCIgZm9udC1mYW1pbHk9IlNhbnMgU2VyaWYiIGZvbnQtd2VpZ2h0PSJub3JtYWwiIGZvbnQtc2l6ZT0iNDAiIHRleHQtYW5jaG9yPSJzdGFydCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjx0ZXh0IHRyYW5zZm9ybT0idHJhbnNsYXRlKDIyNi4yNjk1MywxODUuNzY4NzUpIHNjYWxlKDAuNSwwLjUpIiBmb250LXNpemU9IjQwIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmaWxsPSIjOTk2NmZmIiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBmb250LWZhbWlseT0iU2FucyBTZXJpZiIgZm9udC13ZWlnaHQ9Im5vcm1hbCIgdGV4dC1hbmNob3I9InN0YXJ0IiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHRzcGFuIHg9IjAiIGR5PSIwIj7wn5OBPC90c3Bhbj48L3RleHQ+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6MTMuOTgwNDY4NzU6MTUuODgxMjQ5MjM3MDYwNTMtLT4=";
+ const file =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIyOC40NjI1IiBoZWlnaHQ9IjI3LjciIHZpZXdCb3g9IjAsMCwyOC40NjI1LDI3LjciPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMjYuMDE5NTMsLTE2NC4xMTg3NSkiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbD0iIzk5NjZmZiIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWRhc2hhcnJheT0iIiBzdHJva2UtZGFzaG9mZnNldD0iMCIgZm9udC1mYW1pbHk9IlNhbnMgU2VyaWYiIGZvbnQtd2VpZ2h0PSJub3JtYWwiIGZvbnQtc2l6ZT0iNDAiIHRleHQtYW5jaG9yPSJzdGFydCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjx0ZXh0IHRyYW5zZm9ybT0idHJhbnNsYXRlKDIyNi4yNjk1MywxODUuNzY4NzUpIHNjYWxlKDAuNSwwLjUpIiBmb250LXNpemU9IjQwIiB4bWw6c3BhY2U9InByZXNlcnZlIiBmaWxsPSIjOTk2NmZmIiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBmb250LWZhbWlseT0iU2FucyBTZXJpZiIgZm9udC13ZWlnaHQ9Im5vcm1hbCIgdGV4dC1hbmNob3I9InN0YXJ0IiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHRzcGFuIHg9IjAiIGR5PSIwIj7wn5ODPC90c3Bhbj48L3RleHQ+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6MTMuOTgwNDY4NzU6MTUuODgxMjQ5NjE4NTMwMjYyLS0+";
+ class rxFS {
+ getInfo() {
+ return {
+ id: "0832rxfs2",
+ name: "rxFS",
+ color1: "#192d50",
+ color2: "#192d50",
+ color3: "#192d50",
+ blocks: [
+ {
+ blockIconURI: file,
+ opcode: "start",
+ blockType: Scratch.BlockType.COMMAND,
+ text: Scratch.translate({ id: "start", default: "Create [STR]" }),
+ arguments: {
+ STR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "/rxFS/example",
+ },
+ },
+ },
+ {
+ blockIconURI: file,
+ opcode: "folder",
+ blockType: Scratch.BlockType.COMMAND,
+ text: Scratch.translate({
+ id: "folder",
+ default: "Set [STR] to [STR2]",
+ }),
+ arguments: {
+ STR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "/rxFS/example",
+ },
+ STR2: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: Scratch.translate({
+ id: "folder_default",
+ default: "rxFS is good!",
+ }),
+ },
+ },
+ },
+ {
+ blockIconURI: file,
+ opcode: "sync",
+ blockType: Scratch.BlockType.COMMAND,
+ text: Scratch.translate({
+ id: "sync",
+ default: "Change the location of [STR] to [STR2]",
+ }),
+ arguments: {
+ STR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "/rxFS/example",
+ },
+ STR2: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "/rxFS/example",
+ },
+ },
+ },
+ {
+ blockIconURI: file,
+ opcode: "del",
+ blockType: Scratch.BlockType.COMMAND,
+ text: Scratch.translate({ id: "del", default: "Delete [STR]" }),
+ arguments: {
+ STR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "/rxFS/example",
+ },
+ },
+ },
+ {
+ blockIconURI: file,
+ opcode: "webin",
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate({
+ id: "webin",
+ default: "Load [STR] from the web",
+ }),
+ arguments: {
+ STR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "https://0832k12.github.io/rxFS/hello.txt",
+ },
+ },
+ },
+ {
+ blockIconURI: file,
+ opcode: "open",
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate({ id: "open", default: "Open [STR]" }),
+ arguments: {
+ STR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "/rxFS/example",
+ },
+ },
+ },
+ "---",
+ {
+ blockIconURI: folder,
+ opcode: "clean",
+ blockType: Scratch.BlockType.COMMAND,
+ text: Scratch.translate({
+ id: "clean",
+ default: "Clear the file system",
+ }),
+ arguments: {},
+ },
+ {
+ blockIconURI: folder,
+ opcode: "in",
+ blockType: Scratch.BlockType.COMMAND,
+ text: Scratch.translate({
+ id: "in",
+ default: "Import file system from [STR]",
+ }),
+ arguments: {
+ STR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "/rxFS/",
+ },
+ },
+ },
+ {
+ blockIconURI: folder,
+ opcode: "out",
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate({
+ id: "out",
+ default: "Export file system",
+ }),
+ arguments: {},
+ },
+ {
+ blockIconURI: folder,
+ opcode: "list",
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate({
+ id: "list",
+ default: "List all files under [STR]",
+ }),
+ arguments: {
+ STR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "/rxFS/",
+ },
+ },
+ },
+ {
+ blockIconURI: folder,
+ opcode: "search",
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate({ id: "search", default: "Search [STR]" }),
+ arguments: {
+ STR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "/rxFS/example",
+ },
+ },
+ },
+ ],
+ };
+ }
- clean() {
- rxFSfi = [];
- rxFSsy = [];
- }
+ clean() {
+ rxFSfi = [];
+ rxFSsy = [];
+ }
- sync({ STR, STR2 }) {
- str = encodeURIComponent(STR);
- str2 = encodeURIComponent(STR2);
- if (rxFSsy.indexOf(str) + 1 == 0) {
- rxFSsy[((rxFSsy.indexOf(str) + 1) - 1)] = str2;
- }
- }
+ sync({ STR, STR2 }) {
+ str = encodeURIComponent(STR);
+ str2 = encodeURIComponent(STR2);
+ if (rxFSsy.indexOf(str) + 1 == 0) {
+ rxFSsy[rxFSsy.indexOf(str) + 1 - 1] = str2;
+ }
+ }
- start({ STR }) {
- str = encodeURIComponent(STR);
- if (!(str.charAt((str.length - 1)) == '/') && rxFSsy.indexOf(str) + 1 == 0) {
- rxFSfi.splice(((rxFSfi.length + 1) - 1), 0, null);
- rxFSsy.splice(((rxFSsy.length + 1) - 1), 0, str);
- }
- }
+ start({ STR }) {
+ str = encodeURIComponent(STR);
+ if (
+ !(str.charAt(str.length - 1) == "/") &&
+ rxFSsy.indexOf(str) + 1 == 0
+ ) {
+ rxFSfi.splice(rxFSfi.length + 1 - 1, 0, null);
+ rxFSsy.splice(rxFSsy.length + 1 - 1, 0, str);
+ }
+ }
- open({ STR }) {
- return decodeURIComponent(rxFSfi[((rxFSsy.indexOf(encodeURIComponent(STR)) + 1) - 1)]);
- }
+ open({ STR }) {
+ return decodeURIComponent(
+ rxFSfi[rxFSsy.indexOf(encodeURIComponent(STR)) + 1 - 1]
+ );
+ }
- del({ STR }) {
- str = encodeURIComponent(STR);
- rxFSfi[((rxFSsy.indexOf(str) + 1) - 1)] = undefined;
- rxFSsy[((rxFSsy.indexOf(str) + 1) - 1)] = undefined;
- }
+ del({ STR }) {
+ str = encodeURIComponent(STR);
+ rxFSfi[rxFSsy.indexOf(str) + 1 - 1] = undefined;
+ rxFSsy[rxFSsy.indexOf(str) + 1 - 1] = undefined;
+ }
- folder({ STR, STR2 }) {
- rxFSfi[((rxFSsy.indexOf(encodeURIComponent(STR)) + 1) - 1)] = encodeURIComponent(STR2);
- }
+ folder({ STR, STR2 }) {
+ rxFSfi[rxFSsy.indexOf(encodeURIComponent(STR)) + 1 - 1] =
+ encodeURIComponent(STR2);
+ }
- search({ STR }) {
- Search = '';
- i = 0;
- str = encodeURIComponent(STR);
- for (var i in rxFSsy) {
- if (!(rxFSsy[(i)].indexOf(str) == undefined)) {
- Search = [Search, ',"', rxFSsy[(i)], '"'].join('');
- }
- }
- return decodeURIComponent(Search);
+ search({ STR }) {
+ Search = "";
+ i = 0;
+ str = encodeURIComponent(STR);
+ for (var i in rxFSsy) {
+ if (!(rxFSsy[i].indexOf(str) == undefined)) {
+ Search = [Search, ',"', rxFSsy[i], '"'].join("");
}
+ }
+ return decodeURIComponent(Search);
+ }
- list({ STR }) {
- Search = '';
- i = 0;
- str = encodeURIComponent(STR);
- for (var i in rxFSsy) {
- if (rxFSsy[(i)].slice(0, str.length) == str) {
- Search = [Search, ',"', rxFSsy[(i)], '"'].join('');
- }
- }
- return decodeURIComponent(Search);
+ list({ STR }) {
+ Search = "";
+ i = 0;
+ str = encodeURIComponent(STR);
+ for (var i in rxFSsy) {
+ if (rxFSsy[i].slice(0, str.length) == str) {
+ Search = [Search, ',"', rxFSsy[i], '"'].join("");
}
+ }
+ return decodeURIComponent(Search);
+ }
- webin({ STR }) {
- return Scratch.fetch(STR)
- .then((response) => {
- return response.text();
- })
- .catch((error) => {
- console.error(error);
- return 'undefined';
- });
- }
+ webin({ STR }) {
+ return Scratch.fetch(STR)
+ .then((response) => {
+ return response.text();
+ })
+ .catch((error) => {
+ console.error(error);
+ return "undefined";
+ });
+ }
- in({ STR }) {
- rxFSfi = STR.slice(0, STR.indexOf('|')).split(',');
- rxFSsy = STR.slice(((STR.indexOf('|') + 1)), STR.length).split(',');
- }
+ in({ STR }) {
+ rxFSfi = STR.slice(0, STR.indexOf("|")).split(",");
+ rxFSsy = STR.slice(STR.indexOf("|") + 1, STR.length).split(",");
+ }
- out() {
- return [rxFSfi.join(','), '|', rxFSsy.join(',')].join('');
- }
+ out() {
+ return [rxFSfi.join(","), "|", rxFSsy.join(",")].join("");
}
+ }
- Scratch.extensions.register(new rxFS());
+ Scratch.extensions.register(new rxFS());
})(Scratch);
diff --git a/extensions/Alestore/nfcwarp.js b/extensions/Alestore/nfcwarp.js
index 1976fe7349..6e1face62c 100644
--- a/extensions/Alestore/nfcwarp.js
+++ b/extensions/Alestore/nfcwarp.js
@@ -1,72 +1,76 @@
// Name: NFCWarp
+// ID: alestorenfc
// Description: Allows reading data from NFC (NDEF) devices. Only works in Chrome on Android.
// By: Alestore Games
-(function(Scratch) {
- 'use strict';
+(function (Scratch) {
+ "use strict";
/* globals NDEFReader */
- const extIcon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAACXBIWXMAAAsTAAALEwEAmpwYAAACN0lEQVR4nK2WzW7TQBDHDRIVHCr6CYV+PUG5hENvlpKdfyxX6q0SanmVqo+AkKqqPAG8CAVE1RJaKYn6cSiiIO70RBnYydi4ttcJjiPtIbsz/59nPZ4Zz9MfEz1nog8MXDHAhYvoGxPtMDAvvrXaHQZeJ2ysxnsGNiL9HgR4lRK6YOB8AOAPNmZJNJaX76lf2m73XyTZwwM2ZoGBswFgLfa8W6q16bBb9zTEDEgjnWei074wgMTemKeOh9nzHO9EQOIcBHNMdNInqheJ67vOOf/pOZxjkAjU67MMdAtAe4mkusyzKQTx6uooAxN6LY+ZqOMAXSYSq/v/oDBclMRoNCblf7P5iInaOaBrm+IKOiwLskKf2Pen9GoeMHCU8THmvoIOyoMGgdXrD6sBRd+MC2bMwvAg+y0RHcewIJjW1J+OsywMF6uIaF9Egc8Ka3MYzqjwfqWgRAStGGazsFKQvZ6b19XS/eO4Hg4F6hXWaO/IJoDs+/6UZGHSZ8iIZlJZ17EVQs5WVsYZ+FgNqCfGTpjvj8WwkqBDcVpbG2Hgdw6sbRNBbBqNSREvBSLqJIrkd5cNR5EBE7YAl4noawKU1xhZV9e2kNRY4ARlGx/RL/b9u+r4sgDE0hSDYK4QpI3vnUOkpineLARBS1Q0EeWD3tqDDYfzpjhubd2OS0/xOtPvLg/0LAp3N+fwi50BtBo8kdGqP+w8M3IR7aRnu3Xb+2+8M6I3cee0Qwqw7ZoJUutKtKJI/gr8AfOqgU5hKhA4AAAAAElFTkSuQmCC';
- const blocksIcon = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMCIgd2lkdGg9IjE2MDAuMDAwMDAwcHQiIGhlaWdodD0iMTYwMC4wMDAwMDBwdCIgdmlld0JveD0iMCAwIDE2MDAuMDAwMDAwIDE2MDAuMDAwMDAwIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCBtZWV0Ij4KPG1ldGFkYXRhIGZpbGw9IiNmZmZmZmYiPgpDcmVhdGVkIGJ5IHBvdHJhY2UgMS4xNSwgd3JpdHRlbiBieSBQZXRlciBTZWxpbmdlciAyMDAxLTIwMTcKPC9tZXRhZGF0YT4KPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsMTYwMC4wMDAwMDApIHNjYWxlKDAuMTAwMDAwLC0wLjEwMDAwMCkiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0ibm9uZSI+CjxwYXRoIGQ9Ik0yNjk3IDE1OTg5IGMtNTY1IC01MCAtMTEwOCAtMjY0IC0xNTY3IC02MTkgLTExNCAtODggLTM5MSAtMzYyIC00ODQgLTQ4MCAtMTY0IC0yMDYgLTM0MCAtNTA5IC00MzQgLTc0NSAtODQgLTIxMSAtMTQzIC00MzAgLTE4NCAtNjgwIGwtMjMgLTE0MCAtMyAtNTEwNSBjLTIgLTM3MDkgMCAtNTE0NSA4IC01MjUwIDcwIC05MDUgNTQ5IC0xNzI0IDEzMDQgLTIyMjkgMzU3IC0yMzkgNzc4IC00MDIgMTIwMyAtNDY2IDIwOSAtMzIgNDA0IC0zNiAxNDM4IC0zMyBsMTAzMCAzIC01MCAyOSBjLTQ3MyAyNzIgLTkwMCA5ODAgLTExMzUgMTg4MCAtMTM2IDUyMSAtMjAwIDk1MiAtMjYyIDE3NjYgLTkgMTE2IC0xMyAxNTYzIC0xNSA1NzE5IGwtMyA1NTY0IDU5IC01NCBjMzMgLTMwIDE0NTAgLTE0MzMgMzE1MCAtMzExOSBsMzA5MSAtMzA2NSAwIC05NjAgMCAtOTYwIC0xNjcgMTY2IGMtMjc2IDI3MiAtMzY2MiAzNjM3IC00Mjg4IDQyNjEgbC01ODAgNTc4IC0zIC0zMDI4IGMtMyAtMzA1NiAwIC0zMzg3IDM0IC0zOTU3IDc2IC0xMjg5IDI1NyAtMjI1MSA1NTggLTI5NjggNTggLTEzOCAxOTUgLTQwNCAyNzEgLTUyNyAxNTYgLTI1MSA0MzQgLTU2MCA2NDggLTcxOSAzNTcgLTI2NiA3NzkgLTQ0OSAxMzIwIC01NzEgbDE3NyAtNDEgMjYzMyA0IGMyODk0IDMgMjY2NyAtMiAyOTgzIDYyIDk2MSAxOTQgMTc4NyA4OTMgMjE0OCAxODE2IDg2IDIyMSAxNDAgNDMwIDE4MyA3MDkgMTYgMTA4IDE4IDQxMyAyMSA1MTg1IDIgMzQ1NCAwIDUxMTcgLTggNTIxNyAtMTkgMjc4IC02MSA0OTAgLTE0NCA3NDMgLTM3NCAxMTI2IC0xMzc3IDE5MTQgLTI1NjEgMjAxNSAtNzIgNiAtNTU1IDEwIC0xMjAwIDEwIGwtMTA4MCAwIDU4IC0zMyBjMTAwIC01NiAxNzkgLTExOSAyOTcgLTIzNyA0NjUgLTQ2MyA4MDggLTEyNzMgOTc5IC0yMzEwIDU0IC0zMjkgODkgLTY1NCAxMjMgLTExNDUgOCAtMTI1IDEyIC0xNjI3IDE1IC01NzA0IGw0IC01NTMzIC0zOSAzMyBjLTIyIDE5IC0xMDc0IDEwNjAgLTIzMzggMjMxNCAtMTI2NCAxMjU0IC0yNjY1IDI2NDIgLTMxMTEgMzA4NSBsLTgxMyA4MDUgMCA5NjQgMCA5NjMgMTg1MyAtMTg0MyBjMTAxOCAtMTAxNCAyMTUyIC0yMTQzIDI1MjAgLTI1MDggbDY2NyAtNjY1IDAgMzAyNSBjMCAxNzQ5IC00IDMxNTMgLTEwIDMzMjkgLTU3IDE3OTQgLTI3NCAyOTkyIC02OTUgMzgzOCAtMTUzIDMwOCAtMzExIDUzNiAtNTI1IDc1NiAtMjIxIDIyNyAtNDQxIDM4NiAtNzM1IDUzMSAtMjY2IDEzMSAtNTM2IDIyMyAtODgzIDMwMSBsLTE1NCAzNCAtMjU5NiAtMSBjLTE0MjkgLTEgLTI2MzYgLTUgLTI2ODUgLTEweiIgZmlsbD0iI2ZmZmZmZiIvPgo8L2c+Cjwvc3ZnPg==';
+ const extIcon =
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAACXBIWXMAAAsTAAALEwEAmpwYAAACN0lEQVR4nK2WzW7TQBDHDRIVHCr6CYV+PUG5hENvlpKdfyxX6q0SanmVqo+AkKqqPAG8CAVE1RJaKYn6cSiiIO70RBnYydi4ttcJjiPtIbsz/59nPZ4Zz9MfEz1nog8MXDHAhYvoGxPtMDAvvrXaHQZeJ2ysxnsGNiL9HgR4lRK6YOB8AOAPNmZJNJaX76lf2m73XyTZwwM2ZoGBswFgLfa8W6q16bBb9zTEDEgjnWei074wgMTemKeOh9nzHO9EQOIcBHNMdNInqheJ67vOOf/pOZxjkAjU67MMdAtAe4mkusyzKQTx6uooAxN6LY+ZqOMAXSYSq/v/oDBclMRoNCblf7P5iInaOaBrm+IKOiwLskKf2Pen9GoeMHCU8THmvoIOyoMGgdXrD6sBRd+MC2bMwvAg+y0RHcewIJjW1J+OsywMF6uIaF9Egc8Ka3MYzqjwfqWgRAStGGazsFKQvZ6b19XS/eO4Hg4F6hXWaO/IJoDs+/6UZGHSZ8iIZlJZ17EVQs5WVsYZ+FgNqCfGTpjvj8WwkqBDcVpbG2Hgdw6sbRNBbBqNSREvBSLqJIrkd5cNR5EBE7YAl4noawKU1xhZV9e2kNRY4ARlGx/RL/b9u+r4sgDE0hSDYK4QpI3vnUOkpineLARBS1Q0EeWD3tqDDYfzpjhubd2OS0/xOtPvLg/0LAp3N+fwi50BtBo8kdGqP+w8M3IR7aRnu3Xb+2+8M6I3cee0Qwqw7ZoJUutKtKJI/gr8AfOqgU5hKhA4AAAAAElFTkSuQmCC";
+ const blocksIcon =
+ "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMCIgd2lkdGg9IjE2MDAuMDAwMDAwcHQiIGhlaWdodD0iMTYwMC4wMDAwMDBwdCIgdmlld0JveD0iMCAwIDE2MDAuMDAwMDAwIDE2MDAuMDAwMDAwIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCBtZWV0Ij4KPG1ldGFkYXRhIGZpbGw9IiNmZmZmZmYiPgpDcmVhdGVkIGJ5IHBvdHJhY2UgMS4xNSwgd3JpdHRlbiBieSBQZXRlciBTZWxpbmdlciAyMDAxLTIwMTcKPC9tZXRhZGF0YT4KPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsMTYwMC4wMDAwMDApIHNjYWxlKDAuMTAwMDAwLC0wLjEwMDAwMCkiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0ibm9uZSI+CjxwYXRoIGQ9Ik0yNjk3IDE1OTg5IGMtNTY1IC01MCAtMTEwOCAtMjY0IC0xNTY3IC02MTkgLTExNCAtODggLTM5MSAtMzYyIC00ODQgLTQ4MCAtMTY0IC0yMDYgLTM0MCAtNTA5IC00MzQgLTc0NSAtODQgLTIxMSAtMTQzIC00MzAgLTE4NCAtNjgwIGwtMjMgLTE0MCAtMyAtNTEwNSBjLTIgLTM3MDkgMCAtNTE0NSA4IC01MjUwIDcwIC05MDUgNTQ5IC0xNzI0IDEzMDQgLTIyMjkgMzU3IC0yMzkgNzc4IC00MDIgMTIwMyAtNDY2IDIwOSAtMzIgNDA0IC0zNiAxNDM4IC0zMyBsMTAzMCAzIC01MCAyOSBjLTQ3MyAyNzIgLTkwMCA5ODAgLTExMzUgMTg4MCAtMTM2IDUyMSAtMjAwIDk1MiAtMjYyIDE3NjYgLTkgMTE2IC0xMyAxNTYzIC0xNSA1NzE5IGwtMyA1NTY0IDU5IC01NCBjMzMgLTMwIDE0NTAgLTE0MzMgMzE1MCAtMzExOSBsMzA5MSAtMzA2NSAwIC05NjAgMCAtOTYwIC0xNjcgMTY2IGMtMjc2IDI3MiAtMzY2MiAzNjM3IC00Mjg4IDQyNjEgbC01ODAgNTc4IC0zIC0zMDI4IGMtMyAtMzA1NiAwIC0zMzg3IDM0IC0zOTU3IDc2IC0xMjg5IDI1NyAtMjI1MSA1NTggLTI5NjggNTggLTEzOCAxOTUgLTQwNCAyNzEgLTUyNyAxNTYgLTI1MSA0MzQgLTU2MCA2NDggLTcxOSAzNTcgLTI2NiA3NzkgLTQ0OSAxMzIwIC01NzEgbDE3NyAtNDEgMjYzMyA0IGMyODk0IDMgMjY2NyAtMiAyOTgzIDYyIDk2MSAxOTQgMTc4NyA4OTMgMjE0OCAxODE2IDg2IDIyMSAxNDAgNDMwIDE4MyA3MDkgMTYgMTA4IDE4IDQxMyAyMSA1MTg1IDIgMzQ1NCAwIDUxMTcgLTggNTIxNyAtMTkgMjc4IC02MSA0OTAgLTE0NCA3NDMgLTM3NCAxMTI2IC0xMzc3IDE5MTQgLTI1NjEgMjAxNSAtNzIgNiAtNTU1IDEwIC0xMjAwIDEwIGwtMTA4MCAwIDU4IC0zMyBjMTAwIC01NiAxNzkgLTExOSAyOTcgLTIzNyA0NjUgLTQ2MyA4MDggLTEyNzMgOTc5IC0yMzEwIDU0IC0zMjkgODkgLTY1NCAxMjMgLTExNDUgOCAtMTI1IDEyIC0xNjI3IDE1IC01NzA0IGw0IC01NTMzIC0zOSAzMyBjLTIyIDE5IC0xMDc0IDEwNjAgLTIzMzggMjMxNCAtMTI2NCAxMjU0IC0yNjY1IDI2NDIgLTMxMTEgMzA4NSBsLTgxMyA4MDUgMCA5NjQgMCA5NjMgMTg1MyAtMTg0MyBjMTAxOCAtMTAxNCAyMTUyIC0yMTQzIDI1MjAgLTI1MDggbDY2NyAtNjY1IDAgMzAyNSBjMCAxNzQ5IC00IDMxNTMgLTEwIDMzMjkgLTU3IDE3OTQgLTI3NCAyOTkyIC02OTUgMzgzOCAtMTUzIDMwOCAtMzExIDUzNiAtNTI1IDc1NiAtMjIxIDIyNyAtNDQxIDM4NiAtNzM1IDUzMSAtMjY2IDEzMSAtNTM2IDIyMyAtODgzIDMwMSBsLTE1NCAzNCAtMjU5NiAtMSBjLTE0MjkgLTEgLTI2MzYgLTUgLTI2ODUgLTEweiIgZmlsbD0iI2ZmZmZmZiIvPgo8L2c+Cjwvc3ZnPg==";
class NFCWarp {
getInfo() {
return {
- id: 'alestorenfc',
- name: 'NFCWarp',
- color1: '#FF4646',
- color2: '#FF0000',
- color3: '#990033',
+ id: "alestorenfc",
+ name: "NFCWarp",
+ color1: "#FF4646",
+ color2: "#FF0000",
+ color3: "#990033",
menuIconURI: extIcon,
blockIconURI: blocksIcon,
blocks: [
{
blockType: Scratch.BlockType.LABEL,
- text: 'Only works in Chrome on Android'
+ text: "Only works in Chrome on Android",
},
{
- opcode: 'supported',
+ opcode: "supported",
blockType: Scratch.BlockType.BOOLEAN,
- text: 'NFC supported?'
+ text: "NFC supported?",
},
{
- opcode: 'nfcRead',
+ opcode: "nfcRead",
blockType: Scratch.BlockType.REPORTER,
- text: 'read NFC tag',
- disableMonitor: true
- }
- ]
+ text: "read NFC tag",
+ disableMonitor: true,
+ },
+ ],
};
}
- supported () {
- return typeof NDEFReader !== 'undefined';
+ supported() {
+ return typeof NDEFReader !== "undefined";
}
nfcRead() {
if (!this.supported()) {
- return 'NFC not supported';
+ return "NFC not supported";
}
return new Promise((resolve, reject) => {
const ndef = new NDEFReader();
- ndef.scan()
+ ndef
+ .scan()
.then(() => {
- ndef.onreadingerror = event => {
- console.log('Reading error', event);
- resolve('Tag not supported');
+ ndef.onreadingerror = (event) => {
+ console.log("Reading error", event);
+ resolve("Tag not supported");
};
- ndef.onreading = evt => {
+ ndef.onreading = (evt) => {
const decoder = new TextDecoder();
const record = evt.message.records[0];
- console.log('Record type: ' + record.recordType);
- console.log('Record encoding: ' + record.encoding);
- console.log('Record data: ' + decoder.decode(record.data));
+ console.log("Record type: " + record.recordType);
+ console.log("Record encoding: " + record.encoding);
+ console.log("Record data: " + decoder.decode(record.data));
resolve(decoder.decode(record.data));
};
})
- .catch(error => {
- console.log('Scan error', error);
+ .catch((error) => {
+ console.log("Scan error", error);
resolve(`Error: ${error}`);
});
});
diff --git a/extensions/CST1229/images.js b/extensions/CST1229/images.js
index a7a296b461..7524b64dea 100644
--- a/extensions/CST1229/images.js
+++ b/extensions/CST1229/images.js
@@ -202,7 +202,7 @@
case "image/bmp":
case "image/jpeg":
{
- if (!await Scratch.canFetch(IMAGEURL)) return;
+ if (!(await Scratch.canFetch(IMAGEURL))) return;
// eslint-disable-next-line no-restricted-syntax
const image = new Image();
image.crossOrigin = "anonymous";
@@ -377,6 +377,7 @@
}
}
- if (!Scratch.extensions.unsandboxed) throw new Error("This extension cannot run in sandboxed mode.");
+ if (!Scratch.extensions.unsandboxed)
+ throw new Error("This extension cannot run in sandboxed mode.");
Scratch.extensions.register(new ImagesExt(Scratch.vm));
})(globalThis.Scratch);
diff --git a/extensions/CST1229/zip.js b/extensions/CST1229/zip.js
index d573367947..a362fd17d7 100644
--- a/extensions/CST1229/zip.js
+++ b/extensions/CST1229/zip.js
@@ -1,43 +1,16 @@
// Name: Zip
+// ID: cst1229zip
// Description: Create and edit .zip format files, including .sb3 files.
// By: CST1229
(function (Scratch) {
"use strict";
- // Tricking JSZip into thinking it's running as a CommonJS module
- // is probably better than letting it overwrite globals
- const exports = {};
- const module = { exports: null };
-
- // jszip source code:
- // https://github.com/Stuk/jszip
- // using it under the MIT license
-
- // minified code from: https://cdn.jsdelivr.net/npm/jszip@3/dist/jszip.min.js
- // in a function for code folding
- function jsZip() {
- /*!
-
- JSZip v3.10.1 - A JavaScript class for generating and reading zip files
-
-
- (c) 2009-2016 Stuart Knightley
- Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/main/LICENSE.markdown.
-
- JSZip uses the library pako released under the MIT license :
- https://github.com/nodeca/pako/blob/main/LICENSE
- */
- /* eslint-disable */
- // @ts-ignore
- !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).JSZip=e()}}(function(){return function s(a,o,h){function u(r,e){if(!o[r]){if(!a[r]){var t="function"==typeof require&&require;if(!e&&t)return t(r,!0);if(l)return l(r,!0);var n=new Error("Cannot find module '"+r+"'");throw n.code="MODULE_NOT_FOUND",n}var i=o[r]={exports:{}};a[r][0].call(i.exports,function(e){var t=a[r][1][e];return u(t||e)},i,i.exports,s,a,o,h)}return o[r].exports}for(var l="function"==typeof require&&require,e=0;e>2,s=(3&t)<<4|r>>4,a=1>6:64,o=2>4,r=(15&i)<<4|(s=p.indexOf(e.charAt(o++)))>>2,n=(3&s)<<6|(a=p.indexOf(e.charAt(o++))),l[h++]=t,64!==s&&(l[h++]=r),64!==a&&(l[h++]=n);return l}},{"./support":30,"./utils":32}],2:[function(e,t,r){"use strict";var n=e("./external"),i=e("./stream/DataWorker"),s=e("./stream/Crc32Probe"),a=e("./stream/DataLengthProbe");function o(e,t,r,n,i){this.compressedSize=e,this.uncompressedSize=t,this.crc32=r,this.compression=n,this.compressedContent=i}o.prototype={getContentWorker:function(){var e=new i(n.Promise.resolve(this.compressedContent)).pipe(this.compression.uncompressWorker()).pipe(new a("data_length")),t=this;return e.on("end",function(){if(this.streamInfo.data_length!==t.uncompressedSize)throw new Error("Bug : uncompressed data size mismatch")}),e},getCompressedWorker:function(){return new i(n.Promise.resolve(this.compressedContent)).withStreamInfo("compressedSize",this.compressedSize).withStreamInfo("uncompressedSize",this.uncompressedSize).withStreamInfo("crc32",this.crc32).withStreamInfo("compression",this.compression)}},o.createWorkerFrom=function(e,t,r){return e.pipe(new s).pipe(new a("uncompressedSize")).pipe(t.compressWorker(r)).pipe(new a("compressedSize")).withStreamInfo("compression",t)},t.exports=o},{"./external":6,"./stream/Crc32Probe":25,"./stream/DataLengthProbe":26,"./stream/DataWorker":27}],3:[function(e,t,r){"use strict";var n=e("./stream/GenericWorker");r.STORE={magic:"\0\0",compressWorker:function(){return new n("STORE compression")},uncompressWorker:function(){return new n("STORE decompression")}},r.DEFLATE=e("./flate")},{"./flate":7,"./stream/GenericWorker":28}],4:[function(e,t,r){"use strict";var n=e("./utils");var o=function(){for(var e,t=[],r=0;r<256;r++){e=r;for(var n=0;n<8;n++)e=1&e?3988292384^e>>>1:e>>>1;t[r]=e}return t}();t.exports=function(e,t){return void 0!==e&&e.length?"string"!==n.getTypeOf(e)?function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t[a])];return-1^e}(0|t,e,e.length,0):function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t.charCodeAt(a))];return-1^e}(0|t,e,e.length,0):0}},{"./utils":32}],5:[function(e,t,r){"use strict";r.base64=!1,r.binary=!1,r.dir=!1,r.createFolders=!0,r.date=null,r.compression=null,r.compressionOptions=null,r.comment=null,r.unixPermissions=null,r.dosPermissions=null},{}],6:[function(e,t,r){"use strict";var n=null;n="undefined"!=typeof Promise?Promise:e("lie"),t.exports={Promise:n}},{lie:37}],7:[function(e,t,r){"use strict";var n="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array,i=e("pako"),s=e("./utils"),a=e("./stream/GenericWorker"),o=n?"uint8array":"array";function h(e,t){a.call(this,"FlateWorker/"+e),this._pako=null,this._pakoAction=e,this._pakoOptions=t,this.meta={}}r.magic="\b\0",s.inherits(h,a),h.prototype.processChunk=function(e){this.meta=e.meta,null===this._pako&&this._createPako(),this._pako.push(s.transformTo(o,e.data),!1)},h.prototype.flush=function(){a.prototype.flush.call(this),null===this._pako&&this._createPako(),this._pako.push([],!0)},h.prototype.cleanUp=function(){a.prototype.cleanUp.call(this),this._pako=null},h.prototype._createPako=function(){this._pako=new i[this._pakoAction]({raw:!0,level:this._pakoOptions.level||-1});var t=this;this._pako.onData=function(e){t.push({data:e,meta:t.meta})}},r.compressWorker=function(e){return new h("Deflate",e)},r.uncompressWorker=function(){return new h("Inflate",{})}},{"./stream/GenericWorker":28,"./utils":32,pako:38}],8:[function(e,t,r){"use strict";function A(e,t){var r,n="";for(r=0;r>>=8;return n}function n(e,t,r,n,i,s){var a,o,h=e.file,u=e.compression,l=s!==O.utf8encode,f=I.transformTo("string",s(h.name)),c=I.transformTo("string",O.utf8encode(h.name)),d=h.comment,p=I.transformTo("string",s(d)),m=I.transformTo("string",O.utf8encode(d)),_=c.length!==h.name.length,g=m.length!==d.length,b="",v="",y="",w=h.dir,k=h.date,x={crc32:0,compressedSize:0,uncompressedSize:0};t&&!r||(x.crc32=e.crc32,x.compressedSize=e.compressedSize,x.uncompressedSize=e.uncompressedSize);var S=0;t&&(S|=8),l||!_&&!g||(S|=2048);var z=0,C=0;w&&(z|=16),"UNIX"===i?(C=798,z|=function(e,t){var r=e;return e||(r=t?16893:33204),(65535&r)<<16}(h.unixPermissions,w)):(C=20,z|=function(e){return 63&(e||0)}(h.dosPermissions)),a=k.getUTCHours(),a<<=6,a|=k.getUTCMinutes(),a<<=5,a|=k.getUTCSeconds()/2,o=k.getUTCFullYear()-1980,o<<=4,o|=k.getUTCMonth()+1,o<<=5,o|=k.getUTCDate(),_&&(v=A(1,1)+A(B(f),4)+c,b+="up"+A(v.length,2)+v),g&&(y=A(1,1)+A(B(p),4)+m,b+="uc"+A(y.length,2)+y);var E="";return E+="\n\0",E+=A(S,2),E+=u.magic,E+=A(a,2),E+=A(o,2),E+=A(x.crc32,4),E+=A(x.compressedSize,4),E+=A(x.uncompressedSize,4),E+=A(f.length,2),E+=A(b.length,2),{fileRecord:R.LOCAL_FILE_HEADER+E+f+b,dirRecord:R.CENTRAL_FILE_HEADER+A(C,2)+E+A(p.length,2)+"\0\0\0\0"+A(z,4)+A(n,4)+f+b+p}}var I=e("../utils"),i=e("../stream/GenericWorker"),O=e("../utf8"),B=e("../crc32"),R=e("../signature");function s(e,t,r,n){i.call(this,"ZipFileWorker"),this.bytesWritten=0,this.zipComment=t,this.zipPlatform=r,this.encodeFileName=n,this.streamFiles=e,this.accumulate=!1,this.contentBuffer=[],this.dirRecords=[],this.currentSourceOffset=0,this.entriesCount=0,this.currentFile=null,this._sources=[]}I.inherits(s,i),s.prototype.push=function(e){var t=e.meta.percent||0,r=this.entriesCount,n=this._sources.length;this.accumulate?this.contentBuffer.push(e):(this.bytesWritten+=e.data.length,i.prototype.push.call(this,{data:e.data,meta:{currentFile:this.currentFile,percent:r?(t+100*(r-n-1))/r:100}}))},s.prototype.openedSource=function(e){this.currentSourceOffset=this.bytesWritten,this.currentFile=e.file.name;var t=this.streamFiles&&!e.file.dir;if(t){var r=n(e,t,!1,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);this.push({data:r.fileRecord,meta:{percent:0}})}else this.accumulate=!0},s.prototype.closedSource=function(e){this.accumulate=!1;var t=this.streamFiles&&!e.file.dir,r=n(e,t,!0,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);if(this.dirRecords.push(r.dirRecord),t)this.push({data:function(e){return R.DATA_DESCRIPTOR+A(e.crc32,4)+A(e.compressedSize,4)+A(e.uncompressedSize,4)}(e),meta:{percent:100}});else for(this.push({data:r.fileRecord,meta:{percent:0}});this.contentBuffer.length;)this.push(this.contentBuffer.shift());this.currentFile=null},s.prototype.flush=function(){for(var e=this.bytesWritten,t=0;t=this.index;t--)r=(r<<8)+this.byteAt(t);return this.index+=e,r},readString:function(e){return n.transformTo("string",this.readData(e))},readData:function(){},lastIndexOfSignature:function(){},readAndCheckSignature:function(){},readDate:function(){var e=this.readInt(4);return new Date(Date.UTC(1980+(e>>25&127),(e>>21&15)-1,e>>16&31,e>>11&31,e>>5&63,(31&e)<<1))}},t.exports=i},{"../utils":32}],19:[function(e,t,r){"use strict";var n=e("./Uint8ArrayReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./Uint8ArrayReader":21}],20:[function(e,t,r){"use strict";var n=e("./DataReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.byteAt=function(e){return this.data.charCodeAt(this.zero+e)},i.prototype.lastIndexOfSignature=function(e){return this.data.lastIndexOf(e)-this.zero},i.prototype.readAndCheckSignature=function(e){return e===this.readData(4)},i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./DataReader":18}],21:[function(e,t,r){"use strict";var n=e("./ArrayReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.readData=function(e){if(this.checkOffset(e),0===e)return new Uint8Array(0);var t=this.data.subarray(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./ArrayReader":17}],22:[function(e,t,r){"use strict";var n=e("../utils"),i=e("../support"),s=e("./ArrayReader"),a=e("./StringReader"),o=e("./NodeBufferReader"),h=e("./Uint8ArrayReader");t.exports=function(e){var t=n.getTypeOf(e);return n.checkSupport(t),"string"!==t||i.uint8array?"nodebuffer"===t?new o(e):i.uint8array?new h(n.transformTo("uint8array",e)):new s(n.transformTo("array",e)):new a(e)}},{"../support":30,"../utils":32,"./ArrayReader":17,"./NodeBufferReader":19,"./StringReader":20,"./Uint8ArrayReader":21}],23:[function(e,t,r){"use strict";r.LOCAL_FILE_HEADER="PK",r.CENTRAL_FILE_HEADER="PK",r.CENTRAL_DIRECTORY_END="PK",r.ZIP64_CENTRAL_DIRECTORY_LOCATOR="PK",r.ZIP64_CENTRAL_DIRECTORY_END="PK",r.DATA_DESCRIPTOR="PK\b"},{}],24:[function(e,t,r){"use strict";var n=e("./GenericWorker"),i=e("../utils");function s(e){n.call(this,"ConvertWorker to "+e),this.destType=e}i.inherits(s,n),s.prototype.processChunk=function(e){this.push({data:i.transformTo(this.destType,e.data),meta:e.meta})},t.exports=s},{"../utils":32,"./GenericWorker":28}],25:[function(e,t,r){"use strict";var n=e("./GenericWorker"),i=e("../crc32");function s(){n.call(this,"Crc32Probe"),this.withStreamInfo("crc32",0)}e("../utils").inherits(s,n),s.prototype.processChunk=function(e){this.streamInfo.crc32=i(e.data,this.streamInfo.crc32||0),this.push(e)},t.exports=s},{"../crc32":4,"../utils":32,"./GenericWorker":28}],26:[function(e,t,r){"use strict";var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataLengthProbe for "+e),this.propName=e,this.withStreamInfo(e,0)}n.inherits(s,i),s.prototype.processChunk=function(e){if(e){var t=this.streamInfo[this.propName]||0;this.streamInfo[this.propName]=t+e.data.length}i.prototype.processChunk.call(this,e)},t.exports=s},{"../utils":32,"./GenericWorker":28}],27:[function(e,t,r){"use strict";var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataWorker");var t=this;this.dataIsReady=!1,this.index=0,this.max=0,this.data=null,this.type="",this._tickScheduled=!1,e.then(function(e){t.dataIsReady=!0,t.data=e,t.max=e&&e.length||0,t.type=n.getTypeOf(e),t.isPaused||t._tickAndRepeat()},function(e){t.error(e)})}n.inherits(s,i),s.prototype.cleanUp=function(){i.prototype.cleanUp.call(this),this.data=null},s.prototype.resume=function(){return!!i.prototype.resume.call(this)&&(!this._tickScheduled&&this.dataIsReady&&(this._tickScheduled=!0,n.delay(this._tickAndRepeat,[],this)),!0)},s.prototype._tickAndRepeat=function(){this._tickScheduled=!1,this.isPaused||this.isFinished||(this._tick(),this.isFinished||(n.delay(this._tickAndRepeat,[],this),this._tickScheduled=!0))},s.prototype._tick=function(){if(this.isPaused||this.isFinished)return!1;var e=null,t=Math.min(this.max,this.index+16384);if(this.index>=this.max)return this.end();switch(this.type){case"string":e=this.data.substring(this.index,t);break;case"uint8array":e=this.data.subarray(this.index,t);break;case"array":case"nodebuffer":e=this.data.slice(this.index,t)}return this.index=t,this.push({data:e,meta:{percent:this.max?this.index/this.max*100:0}})},t.exports=s},{"../utils":32,"./GenericWorker":28}],28:[function(e,t,r){"use strict";function n(e){this.name=e||"default",this.streamInfo={},this.generatedError=null,this.extraStreamInfo={},this.isPaused=!0,this.isFinished=!1,this.isLocked=!1,this._listeners={data:[],end:[],error:[]},this.previous=null}n.prototype={push:function(e){this.emit("data",e)},end:function(){if(this.isFinished)return!1;this.flush();try{this.emit("end"),this.cleanUp(),this.isFinished=!0}catch(e){this.emit("error",e)}return!0},error:function(e){return!this.isFinished&&(this.isPaused?this.generatedError=e:(this.isFinished=!0,this.emit("error",e),this.previous&&this.previous.error(e),this.cleanUp()),!0)},on:function(e,t){return this._listeners[e].push(t),this},cleanUp:function(){this.streamInfo=this.generatedError=this.extraStreamInfo=null,this._listeners=[]},emit:function(e,t){if(this._listeners[e])for(var r=0;r "+e:e}},t.exports=n},{}],29:[function(e,t,r){"use strict";var h=e("../utils"),i=e("./ConvertWorker"),s=e("./GenericWorker"),u=e("../base64"),n=e("../support"),a=e("../external"),o=null;if(n.nodestream)try{o=e("../nodejs/NodejsStreamOutputAdapter")}catch(e){}function l(e,o){return new a.Promise(function(t,r){var n=[],i=e._internalType,s=e._outputType,a=e._mimeType;e.on("data",function(e,t){n.push(e),o&&o(t)}).on("error",function(e){n=[],r(e)}).on("end",function(){try{var e=function(e,t,r){switch(e){case"blob":return h.newBlob(h.transformTo("arraybuffer",t),r);case"base64":return u.encode(t);default:return h.transformTo(e,t)}}(s,function(e,t){var r,n=0,i=null,s=0;for(r=0;r>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t}(e)},s.utf8decode=function(e){return h.nodebuffer?o.transformTo("nodebuffer",e).toString("utf-8"):function(e){var t,r,n,i,s=e.length,a=new Array(2*s);for(t=r=0;t>10&1023,a[r++]=56320|1023&n)}return a.length!==r&&(a.subarray?a=a.subarray(0,r):a.length=r),o.applyFromCharCode(a)}(e=o.transformTo(h.uint8array?"uint8array":"array",e))},o.inherits(a,n),a.prototype.processChunk=function(e){var t=o.transformTo(h.uint8array?"uint8array":"array",e.data);if(this.leftOver&&this.leftOver.length){if(h.uint8array){var r=t;(t=new Uint8Array(r.length+this.leftOver.length)).set(this.leftOver,0),t.set(r,this.leftOver.length)}else t=this.leftOver.concat(t);this.leftOver=null}var n=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t}(t),i=t;n!==t.length&&(h.uint8array?(i=t.subarray(0,n),this.leftOver=t.subarray(n,t.length)):(i=t.slice(0,n),this.leftOver=t.slice(n,t.length))),this.push({data:s.utf8decode(i),meta:e.meta})},a.prototype.flush=function(){this.leftOver&&this.leftOver.length&&(this.push({data:s.utf8decode(this.leftOver),meta:{}}),this.leftOver=null)},s.Utf8DecodeWorker=a,o.inherits(l,n),l.prototype.processChunk=function(e){this.push({data:s.utf8encode(e.data),meta:e.meta})},s.Utf8EncodeWorker=l},{"./nodejsUtils":14,"./stream/GenericWorker":28,"./support":30,"./utils":32}],32:[function(e,t,a){"use strict";var o=e("./support"),h=e("./base64"),r=e("./nodejsUtils"),u=e("./external");function n(e){return e}function l(e,t){for(var r=0;r>8;this.dir=!!(16&this.externalFileAttributes),0==e&&(this.dosPermissions=63&this.externalFileAttributes),3==e&&(this.unixPermissions=this.externalFileAttributes>>16&65535),this.dir||"/"!==this.fileNameStr.slice(-1)||(this.dir=!0)},parseZIP64ExtraField:function(){if(this.extraFields[1]){var e=n(this.extraFields[1].value);this.uncompressedSize===s.MAX_VALUE_32BITS&&(this.uncompressedSize=e.readInt(8)),this.compressedSize===s.MAX_VALUE_32BITS&&(this.compressedSize=e.readInt(8)),this.localHeaderOffset===s.MAX_VALUE_32BITS&&(this.localHeaderOffset=e.readInt(8)),this.diskNumberStart===s.MAX_VALUE_32BITS&&(this.diskNumberStart=e.readInt(4))}},readExtraFields:function(e){var t,r,n,i=e.index+this.extraFieldsLength;for(this.extraFields||(this.extraFields={});e.index+4>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t},r.buf2binstring=function(e){return l(e,e.length)},r.binstring2buf=function(e){for(var t=new h.Buf8(e.length),r=0,n=t.length;r>10&1023,o[n++]=56320|1023&i)}return l(o,n)},r.utf8border=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t}},{"./common":41}],43:[function(e,t,r){"use strict";t.exports=function(e,t,r,n){for(var i=65535&e|0,s=e>>>16&65535|0,a=0;0!==r;){for(r-=a=2e3>>1:e>>>1;t[r]=e}return t}();t.exports=function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t[a])];return-1^e}},{}],46:[function(e,t,r){"use strict";var h,c=e("../utils/common"),u=e("./trees"),d=e("./adler32"),p=e("./crc32"),n=e("./messages"),l=0,f=4,m=0,_=-2,g=-1,b=4,i=2,v=8,y=9,s=286,a=30,o=19,w=2*s+1,k=15,x=3,S=258,z=S+x+1,C=42,E=113,A=1,I=2,O=3,B=4;function R(e,t){return e.msg=n[t],t}function T(e){return(e<<1)-(4e.avail_out&&(r=e.avail_out),0!==r&&(c.arraySet(e.output,t.pending_buf,t.pending_out,r,e.next_out),e.next_out+=r,t.pending_out+=r,e.total_out+=r,e.avail_out-=r,t.pending-=r,0===t.pending&&(t.pending_out=0))}function N(e,t){u._tr_flush_block(e,0<=e.block_start?e.block_start:-1,e.strstart-e.block_start,t),e.block_start=e.strstart,F(e.strm)}function U(e,t){e.pending_buf[e.pending++]=t}function P(e,t){e.pending_buf[e.pending++]=t>>>8&255,e.pending_buf[e.pending++]=255&t}function L(e,t){var r,n,i=e.max_chain_length,s=e.strstart,a=e.prev_length,o=e.nice_match,h=e.strstart>e.w_size-z?e.strstart-(e.w_size-z):0,u=e.window,l=e.w_mask,f=e.prev,c=e.strstart+S,d=u[s+a-1],p=u[s+a];e.prev_length>=e.good_match&&(i>>=2),o>e.lookahead&&(o=e.lookahead);do{if(u[(r=t)+a]===p&&u[r+a-1]===d&&u[r]===u[s]&&u[++r]===u[s+1]){s+=2,r++;do{}while(u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&sh&&0!=--i);return a<=e.lookahead?a:e.lookahead}function j(e){var t,r,n,i,s,a,o,h,u,l,f=e.w_size;do{if(i=e.window_size-e.lookahead-e.strstart,e.strstart>=f+(f-z)){for(c.arraySet(e.window,e.window,f,f,0),e.match_start-=f,e.strstart-=f,e.block_start-=f,t=r=e.hash_size;n=e.head[--t],e.head[t]=f<=n?n-f:0,--r;);for(t=r=f;n=e.prev[--t],e.prev[t]=f<=n?n-f:0,--r;);i+=f}if(0===e.strm.avail_in)break;if(a=e.strm,o=e.window,h=e.strstart+e.lookahead,u=i,l=void 0,l=a.avail_in,u=x)for(s=e.strstart-e.insert,e.ins_h=e.window[s],e.ins_h=(e.ins_h<=x&&(e.ins_h=(e.ins_h<=x)if(n=u._tr_tally(e,e.strstart-e.match_start,e.match_length-x),e.lookahead-=e.match_length,e.match_length<=e.max_lazy_match&&e.lookahead>=x){for(e.match_length--;e.strstart++,e.ins_h=(e.ins_h<=x&&(e.ins_h=(e.ins_h<=x&&e.match_length<=e.prev_length){for(i=e.strstart+e.lookahead-x,n=u._tr_tally(e,e.strstart-1-e.prev_match,e.prev_length-x),e.lookahead-=e.prev_length-1,e.prev_length-=2;++e.strstart<=i&&(e.ins_h=(e.ins_h<e.pending_buf_size-5&&(r=e.pending_buf_size-5);;){if(e.lookahead<=1){if(j(e),0===e.lookahead&&t===l)return A;if(0===e.lookahead)break}e.strstart+=e.lookahead,e.lookahead=0;var n=e.block_start+r;if((0===e.strstart||e.strstart>=n)&&(e.lookahead=e.strstart-n,e.strstart=n,N(e,!1),0===e.strm.avail_out))return A;if(e.strstart-e.block_start>=e.w_size-z&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):(e.strstart>e.block_start&&(N(e,!1),e.strm.avail_out),A)}),new M(4,4,8,4,Z),new M(4,5,16,8,Z),new M(4,6,32,32,Z),new M(4,4,16,16,W),new M(8,16,32,32,W),new M(8,16,128,128,W),new M(8,32,128,256,W),new M(32,128,258,1024,W),new M(32,258,258,4096,W)],r.deflateInit=function(e,t){return Y(e,t,v,15,8,0)},r.deflateInit2=Y,r.deflateReset=K,r.deflateResetKeep=G,r.deflateSetHeader=function(e,t){return e&&e.state?2!==e.state.wrap?_:(e.state.gzhead=t,m):_},r.deflate=function(e,t){var r,n,i,s;if(!e||!e.state||5>8&255),U(n,n.gzhead.time>>16&255),U(n,n.gzhead.time>>24&255),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,255&n.gzhead.os),n.gzhead.extra&&n.gzhead.extra.length&&(U(n,255&n.gzhead.extra.length),U(n,n.gzhead.extra.length>>8&255)),n.gzhead.hcrc&&(e.adler=p(e.adler,n.pending_buf,n.pending,0)),n.gzindex=0,n.status=69):(U(n,0),U(n,0),U(n,0),U(n,0),U(n,0),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,3),n.status=E);else{var a=v+(n.w_bits-8<<4)<<8;a|=(2<=n.strategy||n.level<2?0:n.level<6?1:6===n.level?2:3)<<6,0!==n.strstart&&(a|=32),a+=31-a%31,n.status=E,P(n,a),0!==n.strstart&&(P(n,e.adler>>>16),P(n,65535&e.adler)),e.adler=1}if(69===n.status)if(n.gzhead.extra){for(i=n.pending;n.gzindex<(65535&n.gzhead.extra.length)&&(n.pending!==n.pending_buf_size||(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending!==n.pending_buf_size));)U(n,255&n.gzhead.extra[n.gzindex]),n.gzindex++;n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),n.gzindex===n.gzhead.extra.length&&(n.gzindex=0,n.status=73)}else n.status=73;if(73===n.status)if(n.gzhead.name){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindexi&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.gzindex=0,n.status=91)}else n.status=91;if(91===n.status)if(n.gzhead.comment){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindexi&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.status=103)}else n.status=103;if(103===n.status&&(n.gzhead.hcrc?(n.pending+2>n.pending_buf_size&&F(e),n.pending+2<=n.pending_buf_size&&(U(n,255&e.adler),U(n,e.adler>>8&255),e.adler=0,n.status=E)):n.status=E),0!==n.pending){if(F(e),0===e.avail_out)return n.last_flush=-1,m}else if(0===e.avail_in&&T(t)<=T(r)&&t!==f)return R(e,-5);if(666===n.status&&0!==e.avail_in)return R(e,-5);if(0!==e.avail_in||0!==n.lookahead||t!==l&&666!==n.status){var o=2===n.strategy?function(e,t){for(var r;;){if(0===e.lookahead&&(j(e),0===e.lookahead)){if(t===l)return A;break}if(e.match_length=0,r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++,r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):3===n.strategy?function(e,t){for(var r,n,i,s,a=e.window;;){if(e.lookahead<=S){if(j(e),e.lookahead<=S&&t===l)return A;if(0===e.lookahead)break}if(e.match_length=0,e.lookahead>=x&&0e.lookahead&&(e.match_length=e.lookahead)}if(e.match_length>=x?(r=u._tr_tally(e,1,e.match_length-x),e.lookahead-=e.match_length,e.strstart+=e.match_length,e.match_length=0):(r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++),r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):h[n.level].func(n,t);if(o!==O&&o!==B||(n.status=666),o===A||o===O)return 0===e.avail_out&&(n.last_flush=-1),m;if(o===I&&(1===t?u._tr_align(n):5!==t&&(u._tr_stored_block(n,0,0,!1),3===t&&(D(n.head),0===n.lookahead&&(n.strstart=0,n.block_start=0,n.insert=0))),F(e),0===e.avail_out))return n.last_flush=-1,m}return t!==f?m:n.wrap<=0?1:(2===n.wrap?(U(n,255&e.adler),U(n,e.adler>>8&255),U(n,e.adler>>16&255),U(n,e.adler>>24&255),U(n,255&e.total_in),U(n,e.total_in>>8&255),U(n,e.total_in>>16&255),U(n,e.total_in>>24&255)):(P(n,e.adler>>>16),P(n,65535&e.adler)),F(e),0=r.w_size&&(0===s&&(D(r.head),r.strstart=0,r.block_start=0,r.insert=0),u=new c.Buf8(r.w_size),c.arraySet(u,t,l-r.w_size,r.w_size,0),t=u,l=r.w_size),a=e.avail_in,o=e.next_in,h=e.input,e.avail_in=l,e.next_in=0,e.input=t,j(r);r.lookahead>=x;){for(n=r.strstart,i=r.lookahead-(x-1);r.ins_h=(r.ins_h<>>=y=v>>>24,p-=y,0===(y=v>>>16&255))C[s++]=65535&v;else{if(!(16&y)){if(0==(64&y)){v=m[(65535&v)+(d&(1<>>=y,p-=y),p<15&&(d+=z[n++]<>>=y=v>>>24,p-=y,!(16&(y=v>>>16&255))){if(0==(64&y)){v=_[(65535&v)+(d&(1<>>=y,p-=y,(y=s-a)>3,d&=(1<<(p-=w<<3))-1,e.next_in=n,e.next_out=s,e.avail_in=n>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<24)}function s(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new I.Buf16(320),this.work=new I.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function a(e){var t;return e&&e.state?(t=e.state,e.total_in=e.total_out=t.total=0,e.msg="",t.wrap&&(e.adler=1&t.wrap),t.mode=P,t.last=0,t.havedict=0,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new I.Buf32(n),t.distcode=t.distdyn=new I.Buf32(i),t.sane=1,t.back=-1,N):U}function o(e){var t;return e&&e.state?((t=e.state).wsize=0,t.whave=0,t.wnext=0,a(e)):U}function h(e,t){var r,n;return e&&e.state?(n=e.state,t<0?(r=0,t=-t):(r=1+(t>>4),t<48&&(t&=15)),t&&(t<8||15=s.wsize?(I.arraySet(s.window,t,r-s.wsize,s.wsize,0),s.wnext=0,s.whave=s.wsize):(n<(i=s.wsize-s.wnext)&&(i=n),I.arraySet(s.window,t,r-n,i,s.wnext),(n-=i)?(I.arraySet(s.window,t,r-n,n,0),s.wnext=n,s.whave=s.wsize):(s.wnext+=i,s.wnext===s.wsize&&(s.wnext=0),s.whave>>8&255,r.check=B(r.check,E,2,0),l=u=0,r.mode=2;break}if(r.flags=0,r.head&&(r.head.done=!1),!(1&r.wrap)||(((255&u)<<8)+(u>>8))%31){e.msg="incorrect header check",r.mode=30;break}if(8!=(15&u)){e.msg="unknown compression method",r.mode=30;break}if(l-=4,k=8+(15&(u>>>=4)),0===r.wbits)r.wbits=k;else if(k>r.wbits){e.msg="invalid window size",r.mode=30;break}r.dmax=1<>8&1),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=3;case 3:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<>>8&255,E[2]=u>>>16&255,E[3]=u>>>24&255,r.check=B(r.check,E,4,0)),l=u=0,r.mode=4;case 4:for(;l<16;){if(0===o)break e;o--,u+=n[s++]<>8),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=5;case 5:if(1024&r.flags){for(;l<16;){if(0===o)break e;o--,u+=n[s++]<>>8&255,r.check=B(r.check,E,2,0)),l=u=0}else r.head&&(r.head.extra=null);r.mode=6;case 6:if(1024&r.flags&&(o<(d=r.length)&&(d=o),d&&(r.head&&(k=r.head.extra_len-r.length,r.head.extra||(r.head.extra=new Array(r.head.extra_len)),I.arraySet(r.head.extra,n,s,d,k)),512&r.flags&&(r.check=B(r.check,n,d,s)),o-=d,s+=d,r.length-=d),r.length))break e;r.length=0,r.mode=7;case 7:if(2048&r.flags){if(0===o)break e;for(d=0;k=n[s+d++],r.head&&k&&r.length<65536&&(r.head.name+=String.fromCharCode(k)),k&&d>9&1,r.head.done=!0),e.adler=r.check=0,r.mode=12;break;case 10:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<>>=7&l,l-=7&l,r.mode=27;break}for(;l<3;){if(0===o)break e;o--,u+=n[s++]<>>=1)){case 0:r.mode=14;break;case 1:if(j(r),r.mode=20,6!==t)break;u>>>=2,l-=2;break e;case 2:r.mode=17;break;case 3:e.msg="invalid block type",r.mode=30}u>>>=2,l-=2;break;case 14:for(u>>>=7&l,l-=7&l;l<32;){if(0===o)break e;o--,u+=n[s++]<>>16^65535)){e.msg="invalid stored block lengths",r.mode=30;break}if(r.length=65535&u,l=u=0,r.mode=15,6===t)break e;case 15:r.mode=16;case 16:if(d=r.length){if(o>>=5,l-=5,r.ndist=1+(31&u),u>>>=5,l-=5,r.ncode=4+(15&u),u>>>=4,l-=4,286>>=3,l-=3}for(;r.have<19;)r.lens[A[r.have++]]=0;if(r.lencode=r.lendyn,r.lenbits=7,S={bits:r.lenbits},x=T(0,r.lens,0,19,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg="invalid code lengths set",r.mode=30;break}r.have=0,r.mode=19;case 19:for(;r.have>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=_,l-=_,r.lens[r.have++]=b;else{if(16===b){for(z=_+2;l>>=_,l-=_,0===r.have){e.msg="invalid bit length repeat",r.mode=30;break}k=r.lens[r.have-1],d=3+(3&u),u>>>=2,l-=2}else if(17===b){for(z=_+3;l>>=_)),u>>>=3,l-=3}else{for(z=_+7;l>>=_)),u>>>=7,l-=7}if(r.have+d>r.nlen+r.ndist){e.msg="invalid bit length repeat",r.mode=30;break}for(;d--;)r.lens[r.have++]=k}}if(30===r.mode)break;if(0===r.lens[256]){e.msg="invalid code -- missing end-of-block",r.mode=30;break}if(r.lenbits=9,S={bits:r.lenbits},x=T(D,r.lens,0,r.nlen,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg="invalid literal/lengths set",r.mode=30;break}if(r.distbits=6,r.distcode=r.distdyn,S={bits:r.distbits},x=T(F,r.lens,r.nlen,r.ndist,r.distcode,0,r.work,S),r.distbits=S.bits,x){e.msg="invalid distances set",r.mode=30;break}if(r.mode=20,6===t)break e;case 20:r.mode=21;case 21:if(6<=o&&258<=h){e.next_out=a,e.avail_out=h,e.next_in=s,e.avail_in=o,r.hold=u,r.bits=l,R(e,c),a=e.next_out,i=e.output,h=e.avail_out,s=e.next_in,n=e.input,o=e.avail_in,u=r.hold,l=r.bits,12===r.mode&&(r.back=-1);break}for(r.back=0;g=(C=r.lencode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,r.length=b,0===g){r.mode=26;break}if(32&g){r.back=-1,r.mode=12;break}if(64&g){e.msg="invalid literal/length code",r.mode=30;break}r.extra=15&g,r.mode=22;case 22:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra}r.was=r.length,r.mode=23;case 23:for(;g=(C=r.distcode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,64&g){e.msg="invalid distance code",r.mode=30;break}r.offset=b,r.extra=15&g,r.mode=24;case 24:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra}if(r.offset>r.dmax){e.msg="invalid distance too far back",r.mode=30;break}r.mode=25;case 25:if(0===h)break e;if(d=c-h,r.offset>d){if((d=r.offset-d)>r.whave&&r.sane){e.msg="invalid distance too far back",r.mode=30;break}p=d>r.wnext?(d-=r.wnext,r.wsize-d):r.wnext-d,d>r.length&&(d=r.length),m=r.window}else m=i,p=a-r.offset,d=r.length;for(hd?(m=R[T+a[v]],A[I+a[v]]):(m=96,0),h=1<>S)+(u-=h)]=p<<24|m<<16|_|0,0!==u;);for(h=1<>=1;if(0!==h?(E&=h-1,E+=h):E=0,v++,0==--O[b]){if(b===w)break;b=t[r+a[v]]}if(k>>7)]}function U(e,t){e.pending_buf[e.pending++]=255&t,e.pending_buf[e.pending++]=t>>>8&255}function P(e,t,r){e.bi_valid>d-r?(e.bi_buf|=t<>d-e.bi_valid,e.bi_valid+=r-d):(e.bi_buf|=t<>>=1,r<<=1,0<--t;);return r>>>1}function Z(e,t,r){var n,i,s=new Array(g+1),a=0;for(n=1;n<=g;n++)s[n]=a=a+r[n-1]<<1;for(i=0;i<=t;i++){var o=e[2*i+1];0!==o&&(e[2*i]=j(s[o]++,o))}}function W(e){var t;for(t=0;t>1;1<=r;r--)G(e,s,r);for(i=h;r=e.heap[1],e.heap[1]=e.heap[e.heap_len--],G(e,s,1),n=e.heap[1],e.heap[--e.heap_max]=r,e.heap[--e.heap_max]=n,s[2*i]=s[2*r]+s[2*n],e.depth[i]=(e.depth[r]>=e.depth[n]?e.depth[r]:e.depth[n])+1,s[2*r+1]=s[2*n+1]=i,e.heap[1]=i++,G(e,s,1),2<=e.heap_len;);e.heap[--e.heap_max]=e.heap[1],function(e,t){var r,n,i,s,a,o,h=t.dyn_tree,u=t.max_code,l=t.stat_desc.static_tree,f=t.stat_desc.has_stree,c=t.stat_desc.extra_bits,d=t.stat_desc.extra_base,p=t.stat_desc.max_length,m=0;for(s=0;s<=g;s++)e.bl_count[s]=0;for(h[2*e.heap[e.heap_max]+1]=0,r=e.heap_max+1;r<_;r++)p<(s=h[2*h[2*(n=e.heap[r])+1]+1]+1)&&(s=p,m++),h[2*n+1]=s,u>=7;n>>=1)if(1&r&&0!==e.dyn_ltree[2*t])return o;if(0!==e.dyn_ltree[18]||0!==e.dyn_ltree[20]||0!==e.dyn_ltree[26])return h;for(t=32;t>>3,(s=e.static_len+3+7>>>3)<=i&&(i=s)):i=s=r+5,r+4<=i&&-1!==t?J(e,t,r,n):4===e.strategy||s===i?(P(e,2+(n?1:0),3),K(e,z,C)):(P(e,4+(n?1:0),3),function(e,t,r,n){var i;for(P(e,t-257,5),P(e,r-1,5),P(e,n-4,4),i=0;i>>8&255,e.pending_buf[e.d_buf+2*e.last_lit+1]=255&t,e.pending_buf[e.l_buf+e.last_lit]=255&r,e.last_lit++,0===t?e.dyn_ltree[2*r]++:(e.matches++,t--,e.dyn_ltree[2*(A[r]+u+1)]++,e.dyn_dtree[2*N(t)]++),e.last_lit===e.lit_bufsize-1},r._tr_align=function(e){P(e,2,3),L(e,m,z),function(e){16===e.bi_valid?(U(e,e.bi_buf),e.bi_buf=0,e.bi_valid=0):8<=e.bi_valid&&(e.pending_buf[e.pending++]=255&e.bi_buf,e.bi_buf>>=8,e.bi_valid-=8)}(e)}},{"../utils/common":41}],53:[function(e,t,r){"use strict";t.exports=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}},{}],54:[function(e,t,r){(function(e){!function(r,n){"use strict";if(!r.setImmediate){var i,s,t,a,o=1,h={},u=!1,l=r.document,e=Object.getPrototypeOf&&Object.getPrototypeOf(r);e=e&&e.setTimeout?e:r,i="[object process]"==={}.toString.call(r.process)?function(e){process.nextTick(function(){c(e)})}:function(){if(r.postMessage&&!r.importScripts){var e=!0,t=r.onmessage;return r.onmessage=function(){e=!1},r.postMessage("","*"),r.onmessage=t,e}}()?(a="setImmediate$"+Math.random()+"$",r.addEventListener?r.addEventListener("message",d,!1):r.attachEvent("onmessage",d),function(e){r.postMessage(a+e,"*")}):r.MessageChannel?((t=new MessageChannel).port1.onmessage=function(e){c(e.data)},function(e){t.port2.postMessage(e)}):l&&"onreadystatechange"in l.createElement("script")?(s=l.documentElement,function(e){var t=l.createElement("script");t.onreadystatechange=function(){c(e),t.onreadystatechange=null,s.removeChild(t),t=null},s.appendChild(t)}):function(e){setTimeout(c,0,e)},e.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),r=0;r parseInt(o, 16)));
- } break;
- case "binary": {
- if (!/^(?:[01]{8})*$/i.test(DATA)) return;
- const dataArr = this.splitIntoParts(DATA, 8);
- DATA = Uint8Array.from(dataArr.map(o => parseInt(o, 2)));
- } break;
+ case "URL":
+ {
+ if (TYPE === "base64")
+ DATA = "data:application/zip;base64," + DATA;
+ const resp = await Scratch.fetch(DATA);
+ DATA = await resp.blob();
+ }
+ break;
+ case "hex":
+ {
+ if (!/^(?:[0-9A-F]{2})*$/i.test(DATA)) return;
+ const dataArr = this.splitIntoParts(DATA, 2);
+ DATA = Uint8Array.from(dataArr.map((o) => parseInt(o, 16)));
+ }
+ break;
+ case "binary":
+ {
+ if (!/^(?:[01]{8})*$/i.test(DATA)) return;
+ const dataArr = this.splitIntoParts(DATA, 8);
+ DATA = Uint8Array.from(dataArr.map((o) => parseInt(o, 2)));
+ }
+ break;
}
this.zip = await JSZip.loadAsync(DATA, { createFolders: true });
@@ -479,20 +460,23 @@
COMPRESSION = Math.max(Math.min(Math.round(COMPRESSION), 9), 0);
const compType = COMPRESSION === 0 ? "STORE" : "DEFLATE";
- const options = { compression: compType, compressionOptions: { level: COMPRESSION } };
+ const options = {
+ compression: compType,
+ compressionOptions: { level: COMPRESSION },
+ };
switch (TYPE) {
case "text":
case "string":
return await this.zip.generateAsync({
type: "binarystring",
- ...options
+ ...options,
});
case "base64":
case "data: URL": {
let data = await this.zip.generateAsync({
type: "base64",
- ...options
+ ...options,
});
if (TYPE === "data: URL")
data = "data:application/zip;base64," + data;
@@ -501,21 +485,29 @@
case "hex": {
const data = await this.zip.generateAsync({
type: "array",
- ...options
+ ...options,
});
- return data.map(data => data.toString(16).padStart(2, "0")).join("");
+ return data
+ .map((data) => data.toString(16).padStart(2, "0"))
+ .join("");
}
case "binary": {
const data = await this.zip.generateAsync({
type: "array",
- ...options
+ ...options,
});
- return data.map(data => data.toString(2).padStart(8, "0")).join("");
+ return data
+ .map((data) => data.toString(2).padStart(8, "0"))
+ .join("");
}
- default: return "";
+ default:
+ return "";
}
} catch (e) {
- console.error(`Zip extension: Error creating zip with type ${TYPE} compression ${COMPRESSION}:`, e);
+ console.error(
+ `Zip extension: Error creating zip with type ${TYPE} compression ${COMPRESSION}:`,
+ e
+ );
}
}
close() {
@@ -528,7 +520,9 @@
exists({ OBJECT }) {
try {
- return !!this.getObj(this.normalize(this.zipPath, Scratch.Cast.toString(OBJECT)));
+ return !!this.getObj(
+ this.normalize(this.zipPath, Scratch.Cast.toString(OBJECT))
+ );
} catch (e) {
return false;
}
@@ -556,16 +550,24 @@
}
case "hex": {
const data = await obj.async("array");
- return data.map(data => data.toString(16).padStart(2, "0")).join("");
+ return data
+ .map((data) => data.toString(16).padStart(2, "0"))
+ .join("");
}
case "binary": {
const data = await obj.async("array");
- return data.map(data => data.toString(2).padStart(8, "0")).join("");
+ return data
+ .map((data) => data.toString(2).padStart(8, "0"))
+ .join("");
}
- default: return "";
+ default:
+ return "";
}
} catch (e) {
- console.error(`Zip extension: Error getting file ${FILE} with type ${TYPE}:`, e);
+ console.error(
+ `Zip extension: Error getting file ${FILE} with type ${TYPE}:`,
+ e
+ );
return "";
}
}
@@ -591,7 +593,8 @@
});
break;
case "base64":
- case "data: URL": { // compatibility
+ case "data: URL": {
+ // compatibility
if (TYPE === "data: URL")
CONTENT = CONTENT.substring(CONTENT.indexOf(","));
this.zip.file(path, CONTENT, {
@@ -600,33 +603,43 @@
});
break;
}
- case "URL": {
- const resp = await Scratch.fetch(CONTENT);
- this.zip.file(path, await resp.blob(), {
- base64: true,
- createFolders: true,
- });
- } break;
- case "hex": {
- if (!/^(?:[0-9A-F]{2})*$/i.test(CONTENT)) return "";
- const dataArr = this.splitIntoParts(CONTENT, 2);
- const data = Uint8Array.from(dataArr.map(o => parseInt(o, 16)));
- this.zip.file(path, data, {
- createFolders: true,
- });
- } break;
- case "binary": {
- if (!/^(?:[01]{8})*$/i.test(CONTENT)) return "";
- const dataArr = this.splitIntoParts(CONTENT, 8);
- const data = Uint8Array.from(dataArr.map(o => parseInt(o, 2)));
- this.zip.file(path, data, {
- createFolders: true,
- });
- } break;
- default: return "";
+ case "URL":
+ {
+ const resp = await Scratch.fetch(CONTENT);
+ this.zip.file(path, await resp.blob(), {
+ base64: true,
+ createFolders: true,
+ });
+ }
+ break;
+ case "hex":
+ {
+ if (!/^(?:[0-9A-F]{2})*$/i.test(CONTENT)) return "";
+ const dataArr = this.splitIntoParts(CONTENT, 2);
+ const data = Uint8Array.from(dataArr.map((o) => parseInt(o, 16)));
+ this.zip.file(path, data, {
+ createFolders: true,
+ });
+ }
+ break;
+ case "binary":
+ {
+ if (!/^(?:[01]{8})*$/i.test(CONTENT)) return "";
+ const dataArr = this.splitIntoParts(CONTENT, 8);
+ const data = Uint8Array.from(dataArr.map((o) => parseInt(o, 2)));
+ this.zip.file(path, data, {
+ createFolders: true,
+ });
+ }
+ break;
+ default:
+ return "";
}
} catch (e) {
- console.error(`Zip extension: Error writing to file ${FILE} type ${TYPE}:`, e);
+ console.error(
+ `Zip extension: Error writing to file ${FILE} type ${TYPE}:`,
+ e
+ );
}
}
renameFile({ FROM, TO }) {
@@ -652,7 +665,10 @@
let toPath = this.normalize(this.zipPath, TO);
const replacedTo = TO.replaceAll(/\\/g, "/");
const slashes = replacedTo.split("/").length - 1;
- if (slashes <= +fromObj.dir && (slashes === 0 || replacedTo.endsWith("/"))) {
+ if (
+ slashes <= +fromObj.dir &&
+ (slashes === 0 || replacedTo.endsWith("/"))
+ ) {
// this is a name-only change
toPath = this.normalize(fromPath, "../" + replacedTo);
if (fromObj.dir) {
@@ -678,7 +694,7 @@
// Move current directory
if (this.zipPath.substring(1).startsWith(fromPath)) {
this.zipPath =
- "/" + toPath + (this.zipPath.substring(1).substring(fromPath.length));
+ "/" + toPath + this.zipPath.substring(1).substring(fromPath.length);
}
for (const path in this.zip.files) {
@@ -700,10 +716,10 @@
if (!this.getObj(path)) return;
if (path === "/") return;
- const shouldGoBack = this.getObj(path).dir && this.zipPath.startsWith(path);
+ const shouldGoBack =
+ this.getObj(path).dir && this.zipPath.startsWith(path);
if (path.startsWith("/")) path = path.substring(1);
-
this.zip.remove(path);
if (shouldGoBack) {
@@ -740,18 +756,23 @@
const obj = this.getObj(normalized);
if (!obj) return "";
switch (META) {
- case "modified days since 2000": {
- const msPerDay = 24 * 60 * 60 * 1000;
- const start = +(new Date(2000, 0, 1));
- obj.date = new Date(start + (Scratch.Cast.toNumber(VALUE) * msPerDay));
- } break;
+ case "modified days since 2000":
+ {
+ const msPerDay = 24 * 60 * 60 * 1000;
+ const start = +new Date(2000, 0, 1);
+ obj.date = new Date(
+ start + Scratch.Cast.toNumber(VALUE) * msPerDay
+ );
+ }
+ break;
case "unix modified timestamp":
obj.date = new Date(Scratch.Cast.toNumber(VALUE));
break;
case "comment":
obj.comment = VALUE;
break;
- default: return;
+ default:
+ return;
}
} catch (e) {
console.error(`Zip extension: Error getting ${META} of ${FILE}:`, e);
@@ -779,28 +800,29 @@
case "folder": {
/** @type {Array} */
const splitPath = obj.name.split("/");
- const folders = (splitPath.slice(
- 0, splitPath.length - 1 - +obj.dir
- ).join("/"));
+ const folders = splitPath
+ .slice(0, splitPath.length - 1 - +obj.dir)
+ .join("/");
return "/" + folders + (folders === "" ? "" : "/");
}
case "modification date":
return obj.date.toLocaleString(navigator.language);
case "long modification date":
- return new Date().toLocaleString(
- navigator.language,
- { dateStyle: "full", timeStyle: "medium" }
- );
+ return new Date().toLocaleString(navigator.language, {
+ dateStyle: "full",
+ timeStyle: "medium",
+ });
case "modified days since 2000": {
const msPerDay = 24 * 60 * 60 * 1000;
- const start = +(new Date(2000, 0, 1));
+ const start = +new Date(2000, 0, 1);
return (+obj.date - start) / msPerDay;
}
case "unix modified timestamp":
return +obj.date;
case "comment":
return obj.comment || "";
- default: return "";
+ default:
+ return "";
}
} catch (e) {
console.error(`Zip extension: Error getting ${META} of ${FILE}:`, e);
@@ -844,15 +866,20 @@
const dir = normalized.substring(1);
const length = dir.length;
- return JSON.stringify(Object.values(this.zip.files).filter((obj) => {
- // Above the current directory
- if (!obj.name.startsWith(dir)) return false;
- // Below the current directory
- if (obj.name.substring(length).split("/").length > (obj.dir + 1)) return false;
- // Is the current directory
- if (obj.name === dir) return false;
- return true;
- }).map(obj => obj.name.substring(length)));
+ return JSON.stringify(
+ Object.values(this.zip.files)
+ .filter((obj) => {
+ // Above the current directory
+ if (!obj.name.startsWith(dir)) return false;
+ // Below the current directory
+ if (obj.name.substring(length).split("/").length > obj.dir + 1)
+ return false;
+ // Is the current directory
+ if (obj.name === dir) return false;
+ return true;
+ })
+ .map((obj) => obj.name.substring(length))
+ );
} catch (e) {
console.error(`Zip extension: Could not get directory ${DIR}:`, e);
return "";
diff --git a/extensions/CubesterYT/TurboHook.js b/extensions/CubesterYT/TurboHook.js
index e7995a2f81..a87d1192a4 100644
--- a/extensions/CubesterYT/TurboHook.js
+++ b/extensions/CubesterYT/TurboHook.js
@@ -1,11 +1,13 @@
// Name: TurboHook
+// ID: cubesterTurboHook
// Description: Allows you to use webhooks.
// By: CubesterYT
(function (Scratch) {
"use strict";
- const icon = "data:image/png;base64,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cy5hZG9iZS5jb20veGFwLzEuMC8iCiAgIHhtcE1NOkRvY3VtZW50SUQ9ImdpbXA6ZG9jaWQ6Z2ltcDpjMzJjNzQ0My00ZjNhLTQ4N2QtODRmZS02YjNiZGYwZTA1YWQiCiAgIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OTU1MTE5ODgtZmUwOS00NTlkLWIxYmItMzM2ZjVkMTI4MGJkIgogICB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6MDAyMTI1ZDctYWQyMS00NTBmLTljYTktZjAzNmY1ZjM2ZDA1IgogICBkYzpGb3JtYXQ9ImltYWdlL3BuZyIKICAgR0lNUDpBUEk9IjIuMCIKICAgR0lNUDpQbGF0Zm9ybT0iTGludXgiCiAgIEdJTVA6VGltZVN0YW1wPSIxNjg2OTc0Njc2NjY2MDAxIgogICBHSU1QOlZlcnNpb249IjIuMTAuMzAiCiAgIHRpZmY6T3JpZW50YXRpb249IjEiCiAgIHhtcDpDcmVhdG9yVG9vbD0iR0lNUCAyLjEwIj4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YjJiMWVlNjctMDQzOC00M2NjLWFlOTctNGI2ZTNhMjAyN2YwIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJHaW1wIDIuMTAgKExpbnV4KSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyMy0wNi0xNlQyMzowNDozNi0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAg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";
+ const icon =
+ "data:image/png;base64,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cy5hZG9iZS5jb20veGFwLzEuMC8iCiAgIHhtcE1NOkRvY3VtZW50SUQ9ImdpbXA6ZG9jaWQ6Z2ltcDpjMzJjNzQ0My00ZjNhLTQ4N2QtODRmZS02YjNiZGYwZTA1YWQiCiAgIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OTU1MTE5ODgtZmUwOS00NTlkLWIxYmItMzM2ZjVkMTI4MGJkIgogICB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6MDAyMTI1ZDctYWQyMS00NTBmLTljYTktZjAzNmY1ZjM2ZDA1IgogICBkYzpGb3JtYXQ9ImltYWdlL3BuZyIKICAgR0lNUDpBUEk9IjIuMCIKICAgR0lNUDpQbGF0Zm9ybT0iTGludXgiCiAgIEdJTVA6VGltZVN0YW1wPSIxNjg2OTc0Njc2NjY2MDAxIgogICBHSU1QOlZlcnNpb249IjIuMTAuMzAiCiAgIHRpZmY6T3JpZW50YXRpb249IjEiCiAgIHhtcDpDcmVhdG9yVG9vbD0iR0lNUCAyLjEwIj4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YjJiMWVlNjctMDQzOC00M2NjLWFlOTctNGI2ZTNhMjAyN2YwIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJHaW1wIDIuMTAgKExpbnV4KSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyMy0wNi0xNlQyMzowNDozNi0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAg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";
const parseOrEmptyObject = (str) => {
try {
@@ -34,9 +36,9 @@
blockType: Scratch.BlockType.COMMAND,
arguments: {
hookURL: {
- type: Scratch.ArgumentType.STRING
- }
- }
+ type: Scratch.ArgumentType.STRING,
+ },
+ },
},
{
opcode: "params",
@@ -45,29 +47,29 @@
arguments: {
MENU: {
type: Scratch.ArgumentType.STRING,
- menu: "PARAMS"
+ menu: "PARAMS",
},
DATA: {
- type: Scratch.ArgumentType.STRING
- }
- }
+ type: Scratch.ArgumentType.STRING,
+ },
+ },
},
{
opcode: "connector",
blockType: Scratch.BlockType.REPORTER,
- text: "[STRING1] , [STRING2]"
- }
+ text: "[STRING1] , [STRING2]",
+ },
],
menus: {
PARAMS: {
acceptReporters: true,
- items: ["content", "name", "icon"]
- }
- }
+ items: ["content", "name", "icon"],
+ },
+ },
};
}
- webhook ({hookDATA, hookURL}) {
+ webhook({ hookDATA, hookURL }) {
const data = parseOrEmptyObject(hookDATA);
if (!data.content) {
// Typically this can't be empty, so put something there if they forgot to
@@ -76,12 +78,12 @@
Scratch.fetch(hookURL, {
method: "POST",
headers: {
- "Content-Type": "application/json"
+ "Content-Type": "application/json",
},
- body: JSON.stringify(data)
+ body: JSON.stringify(data),
});
}
- params ({MENU, DATA}) {
+ params({ MENU, DATA }) {
DATA = Scratch.Cast.toString(DATA);
if (MENU == "content") {
return JSON.stringify({ content: DATA });
@@ -92,13 +94,13 @@
}
return "{}";
}
- connector ({STRING1, STRING2}) {
+ connector({ STRING1, STRING2 }) {
return JSON.stringify({
...parseOrEmptyObject(STRING1),
- ...parseOrEmptyObject(STRING2)
+ ...parseOrEmptyObject(STRING2),
});
}
}
Scratch.extensions.register(new TurboHook());
-})(Scratch);
\ No newline at end of file
+})(Scratch);
diff --git a/extensions/CubesterYT/WindowControls.js b/extensions/CubesterYT/WindowControls.js
new file mode 100644
index 0000000000..67fe6832fd
--- /dev/null
+++ b/extensions/CubesterYT/WindowControls.js
@@ -0,0 +1,510 @@
+// Name: Window Controls
+// ID: cubesterWindowControls
+// Description: Move, resize, rename the window, enter fullscreen, get screen size, and more.
+// By: CubesterYT
+
+// Version V.1.0.0
+
+(function (Scratch) {
+ "use strict";
+
+ const icon =
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAA0GVYSWZJSSoACAAAAAoAAAEEAAEAAABAAAAAAQEEAAEAAABAAAAAAgEDAAMAAACGAAAAEgEDAAEAAAABAAAAGgEFAAEAAACMAAAAGwEFAAEAAACUAAAAKAEDAAEAAAACAAAAMQECAA0AAACcAAAAMgECABQAAACqAAAAaYcEAAEAAAC+AAAAAAAAAAgACAAIAEgAAAABAAAASAAAAAEAAABHSU1QIDIuMTAuMzQAADIwMjM6MDg6MTUgMjI6MjU6MTcAAQABoAMAAQAAAAEAAAAAAAAA+Kkp0wAAAYRpQ0NQSUNDIHByb2ZpbGUAAHicfZE9SMNAHMVfW6VFKgp2KOKQoTpZKCriqFUoQoVQK7TqYHLpFzRpSFJcHAXXgoMfi1UHF2ddHVwFQfADxNXFSdFFSvxfUmgR48FxP97de9y9A/zNKlPNngSgapaRSSWFXH5VCL4ihEEEEEVCYqY+J4ppeI6ve/j4ehfnWd7n/hz9SsFkgE8gnmW6YRFvEE9vWjrnfeIIK0sK8TnxuEEXJH7kuuzyG+eSw36eGTGymXniCLFQ6mK5i1nZUImniGOKqlG+P+eywnmLs1qts/Y9+QvDBW1lmes0R5DCIpYgQoCMOiqowkKcVo0UExnaT3r4hx2/SC6ZXBUwciygBhWS4wf/g9/dmsXJCTcpnAR6X2z7YxQI7gKthm1/H9t26wQIPANXWsdfawIzn6Q3OlrsCBjYBi6uO5q8B1zuANEnXTIkRwrQ9BeLwPsZfVMeGLoF+tbc3tr7OH0AstRV+gY4OATGSpS97vHuUHdv/55p9/cDaOdyoyaJtEEAAA14aVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA0LjQuMC1FeGl2MiI+CiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICB4bWxuczpHSU1QPSJodHRwOi8vd3d3LmdpbXAub3JnL3htcC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgeG1wTU06RG9jdW1lbnRJRD0iZ2ltcDpkb2NpZDpnaW1wOmVkNmVmMzUwLWVjZDAtNGIwZC1iZjVlLTUxOTVkZjI4YzRhYiIKICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpmN2Y5ODViOC0yMjg3LTQxNmQtOTFjMC0zNTY3ZmQ1ZjhmMjAiCiAgIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDplMzk3YTJiMC0zNDJjLTQ3MWMtYmQzNi0wNjExMTI2MDQwZmEiCiAgIGRjOkZvcm1hdD0iaW1hZ2UvcG5nIgogICBHSU1QOkFQST0iMi4wIgogICBHSU1QOlBsYXRmb3JtPSJMaW51eCIKICAgR0lNUDpUaW1lU3RhbXA9IjE2OTIxNTYzMTc3MjY0NjciCiAgIEdJTVA6VmVyc2lvbj0iMi4xMC4zNCIKICAgdGlmZjpPcmllbnRhdGlvbj0iMSIKICAgeG1wOkNyZWF0b3JUb29sPSJHSU1QIDIuMTAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjM6MDg6MTVUMjI6MjU6MTctMDU6MDAiCiAgIHhtcDpNb2RpZnlEYXRlPSIyMDIzOjA4OjE1VDIyOjI1OjE3LTA1OjAwIj4KICAgPHhtcE1NOkhpc3Rvcnk+CiAgICA8cmRmOlNlcT4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0OmNoYW5nZWQ9Ii8iCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MzlmYmE1ZjAtZmE4OC00M2ZjLTgyMjQtMGIwYjlhMGRkZDkyIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJHaW1wIDIuMTAgKExpbnV4KSIKICAgICAgc3RFdnQ6d2hlbj0iMjAyMy0wOC0xNVQyMjoyNToxNy0wNTowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgCjw/eHBhY2tldCBlbmQ9InciPz45jkSCAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5wgQAxkRVAqqdwAAAchQTFRFAAAAGn+yG4CyHICzHX2yHYC1GoC0Gn+yGn+zGoCzG3+zG3+0G4CzHICzHIC0HIGzG3+zG4CzG3+zG3+zG3+zG3+zG3+zG3+zG3+zG3+zG3+zG3+zG3+zG3+zG3+zG3+zG3+zG3+zG3+zG3+zG3+zG3+zG3+zG3+zG3+zG3+zG3+zHIC0HIC0G3+zHIC0G3+zG3+zHoK2IIS5IYW5I4e8JIi9JYm+Jou/JovAJ4zBKpDFLJLHLpXKMJjNMZnOMZnPMpnPM5vQM5vRNJzSNJ3TNZ7UNp/UN5/UOJ/UOZ/VO6DVPqHVQ6HVRqLWR6PWSKPWSaPWX6rYbK/abq/ab7DadrPbd7Pce7Tcgbfdgbjdgrjdhrrfh7rfibvfirzfi7zfjLzfjLzgjb3gjr3gjr7gj77gkL7gkb/glMDhnMTjncTjoMfkosfko8jkpMjlpcnlsc/ns9HotNHoutXqvtbrv9frwNfswNjsxNrtzN/vzd/vzeDvzeDwzuDwzuHw0eLw0uPx1uXy2Oby2Ofy2efz2+jz3ur04u324+725O727vT57/X68Pb68fb68vb78vf78/f68/f79vn8+fv9+/z+/P3+/f7+////xM3NqwAAADF0Uk5TAAEBAQEBAgMDAwMDAwMDAwgIEBEcJjlUXV5panOEhY2Qra6vt8bO2Nri4+zx9PT8/cI6cjwAAAABYktHRJfmbhuvAAAC10lEQVRYw7WX51/TUBSGr4KIiIoILaMT6LDL7pG2oSPHDW4FBwpO3CiCigP3QgUB77/rbdOkCaTkpsH3Qz7c5Dy/JPeec96DUA11GM12p8cfDPo9TrvZ2IG0aIvB6grFmQxb4AC4Apth4iGX1bCNMrzd5I4wLKwRy0TcpnaK8H0WXzILisomfRY1xM4ebyoPNZVPeXubNorvdEQHYUMNRh2dteO7AmlQVTrQXSN8ty2cAwrlwrZmpfi2vlgRqFSM9bcpxA8kOKAUlxhYR2juS4AGJfpb1wBsMQ6G7r94S6FX904AF7PJ47vDRZj4hSn1cxyK4S7Z/gdyML6CqbUyDrmA5Dy0OtIwtIDx6vyTx6qaml/FeGEI0o7qZvZEAe6S+DG6P3iFECYBor1i/njJ+Z3B+B3tHnzAeJqcaq+QWZYUlAEzABc+vxk+/vzLJbjz7emhmgD+WUhZKvnvy4uLnzB+dAvjr0e+Y3xODZD38a9gSlYXn+G/l8+u4JcHP+I/p9QAkDSV4re6s9XFw9fOA5y5eQxO3j4NqoCsu5EADBHZIo3EZyMGArAywuLsUUrNCgDGSgAuVgAsL1a0xF/lEm8uLS4LANZF6n9IfC0tqnxuqAMZ43oAcSMyMyJg7uIo0QjRqIJGqpoTAYwZ2TNQ/y5Axo6crB4A60Segh5AwYP8nB4A50dB0AOAA3oBQf2foPsn6t5G3QdJcpTrAJCjLEmmOgAkmSTpXAeApLOkoGgHlAqKpKRpB5RLmu6i2igt69oAfFmXNRZtAL6xyFqbJoDQ2qTNVRNAaK58e5/G+H297b1sMCbpDcbVtQYDNYsWZ4rS4vwoWZwmucma0GKyxuQmi7d516lt3u8bxOZ1rzeaww+pjObrBwpGE+3QanW3b7bZ1mb3+/YqDQwt9ANHi76Rp+v/DV0I7VId+3r2qM2t+gbPTRh9S2pQHr4bNE3w1fF//4bj/z8IjCsoNvb7bgAAAABJRU5ErkJggg==";
+
+ function getRandomInt(min, max) {
+ min = Math.ceil(min);
+ max = Math.floor(max);
+ return Math.floor(Math.random() * (max - min + 1)) + min;
+ }
+
+ class WindowControls {
+ getInfo() {
+ return {
+ id: "cubesterWindowControls",
+ name: "Window Controls",
+ color1: "#359ed4",
+ color2: "#298ec2",
+ color3: "#2081b3",
+ menuIconURI: icon,
+ docsURI: "https://extensions.turbowarp.org/CubesterYT/WindowControls",
+
+ blocks: [
+ {
+ blockType: "label",
+ text: "May not work in normal browser tabs",
+ },
+ {
+ blockType: "label",
+ text: "Refer to Documentation for details",
+ },
+ {
+ opcode: "moveTo",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "move window to x: [X] y: [Y]",
+ arguments: {
+ X: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ Y: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ },
+ },
+ {
+ opcode: "moveToPresets",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "move window to the [PRESETS]",
+ arguments: {
+ PRESETS: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "MOVE",
+ },
+ },
+ },
+ {
+ opcode: "changeX",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "change window x by [X]",
+ arguments: {
+ X: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "50",
+ },
+ },
+ },
+ {
+ opcode: "setX",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set window x to [X]",
+ arguments: {
+ X: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "100",
+ },
+ },
+ },
+ {
+ opcode: "changeY",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "change window y by [Y]",
+ arguments: {
+ Y: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "50",
+ },
+ },
+ },
+ {
+ opcode: "setY",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set window y to [Y]",
+ arguments: {
+ Y: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "100",
+ },
+ },
+ },
+ {
+ opcode: "windowX",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "window x",
+ },
+ {
+ opcode: "windowY",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "window y",
+ },
+
+ "---",
+
+ {
+ opcode: "resizeTo",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "resize window to width: [W] height: [H]",
+ arguments: {
+ W: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "480",
+ },
+ H: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "360",
+ },
+ },
+ },
+ {
+ opcode: "resizeToPresets",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "resize window to [PRESETS]",
+ arguments: {
+ PRESETS: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "RESIZE",
+ },
+ },
+ },
+ {
+ opcode: "changeW",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "change window width by [W]",
+ arguments: {
+ W: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "50",
+ },
+ },
+ },
+ {
+ opcode: "setW",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set window width to [W]",
+ arguments: {
+ W: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1000",
+ },
+ },
+ },
+ {
+ opcode: "changeH",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "change window height by [H]",
+ arguments: {
+ H: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "50",
+ },
+ },
+ },
+ {
+ opcode: "setH",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set window height to [H]",
+ arguments: {
+ H: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1000",
+ },
+ },
+ },
+ {
+ opcode: "matchStageSize",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "match stage size",
+ },
+ {
+ opcode: "windowW",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "window width",
+ },
+ {
+ opcode: "windowH",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "window height",
+ },
+
+ "---",
+
+ {
+ opcode: "isTouchingEdge",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "is window touching screen edge?",
+ },
+ {
+ opcode: "screenW",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "screen width",
+ },
+ {
+ opcode: "screenH",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "screen height",
+ },
+
+ "---",
+
+ {
+ opcode: "isFocused",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "is window focused?",
+ },
+
+ "---",
+
+ {
+ opcode: "changeTitleTo",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set window title to [TITLE]",
+ arguments: {
+ TITLE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Hello World!",
+ },
+ },
+ },
+ {
+ opcode: "windowTitle",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "window title",
+ },
+
+ "---",
+
+ {
+ opcode: "enterFullscreen",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "enter fullscreen",
+ },
+ {
+ opcode: "exitFullscreen",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "exit fullscreen",
+ },
+ {
+ opcode: "isFullscreen",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "is window fullscreen?",
+ },
+
+ "---",
+
+ {
+ opcode: "closeWindow",
+ blockType: Scratch.BlockType.COMMAND,
+ isTerminal: true,
+ text: "close window",
+ },
+ ],
+ menus: {
+ MOVE: {
+ acceptReporters: true,
+ items: [
+ "center",
+ "right",
+ "left",
+ "top",
+ "bottom",
+ "top right",
+ "top left",
+ "bottom right",
+ "bottom left",
+ "random position",
+ ],
+ },
+ RESIZE: {
+ acceptReporters: true,
+ items: [
+ "480x360",
+ "640x480",
+ "1280x720",
+ "1920x1080",
+ "2560x1440",
+ "2048x1080",
+ "3840x2160",
+ "7680x4320",
+ ],
+ },
+ },
+ };
+ }
+
+ moveTo(args) {
+ window.moveTo(args.X, args.Y);
+ Scratch.vm.runtime.requestRedraw();
+ }
+ moveToPresets(args) {
+ if (args.PRESETS == "center") {
+ const left = (screen.width - window.outerWidth) / 2;
+ const top = (screen.height - window.outerHeight) / 2;
+ window.moveTo(left, top);
+ } else if (args.PRESETS == "right") {
+ const right = screen.width - window.outerWidth;
+ const top = (screen.height - window.outerHeight) / 2;
+ window.moveTo(right, top);
+ } else if (args.PRESETS == "left") {
+ const top = (screen.height - window.outerHeight) / 2;
+ window.moveTo(0, top);
+ } else if (args.PRESETS == "top") {
+ const left = (screen.width - window.outerWidth) / 2;
+ window.moveTo(left, 0);
+ } else if (args.PRESETS == "bottom") {
+ const left = (screen.width - window.outerWidth) / 2;
+ const bottom = screen.height - window.outerHeight;
+ window.moveTo(left, bottom);
+ } else if (args.PRESETS == "top right") {
+ const right = screen.width - window.outerWidth;
+ window.moveTo(right, 0);
+ } else if (args.PRESETS == "top left") {
+ window.moveTo(0, 0);
+ } else if (args.PRESETS == "bottom right") {
+ const right = screen.width - window.outerWidth;
+ const bottom = screen.height - window.outerHeight;
+ window.moveTo(right, bottom);
+ } else if (args.PRESETS == "bottom left") {
+ const bottom = screen.height - window.outerHeight;
+ window.moveTo(0, bottom);
+ } else if (args.PRESETS == "random position") {
+ const randomX = getRandomInt(0, screen.width);
+ const randomY = getRandomInt(0, screen.height);
+ window.moveTo(randomX, randomY);
+ }
+ Scratch.vm.runtime.requestRedraw();
+ }
+ changeX(args) {
+ window.moveBy(args.X, 0);
+ Scratch.vm.runtime.requestRedraw();
+ }
+ setX(args) {
+ const currentY = window.screenY;
+ window.moveTo(args.X, currentY);
+ Scratch.vm.runtime.requestRedraw();
+ }
+ changeY(args) {
+ window.moveBy(0, args.Y);
+ Scratch.vm.runtime.requestRedraw();
+ }
+ setY(args) {
+ const currentX = window.screenX;
+ window.moveTo(currentX, args.Y);
+ Scratch.vm.runtime.requestRedraw();
+ }
+ windowX() {
+ return window.screenLeft;
+ }
+ windowY() {
+ return window.screenTop;
+ }
+ resizeTo(args) {
+ window.resizeTo(args.W, args.H);
+ Scratch.vm.runtime.requestRedraw();
+ }
+ resizeToPresets(args) {
+ if (args.PRESETS == "480x360") {
+ window.resizeTo(
+ 480 + (window.outerWidth - window.innerWidth),
+ 360 + (window.outerHeight - window.innerHeight)
+ );
+ } else if (args.PRESETS == "640x480") {
+ window.resizeTo(
+ 640 + (window.outerWidth - window.innerWidth),
+ 480 + (window.outerHeight - window.innerHeight)
+ );
+ } else if (args.PRESETS == "1280x720") {
+ window.resizeTo(
+ 1280 + (window.outerWidth - window.innerWidth),
+ 720 + (window.outerHeight - window.innerHeight)
+ );
+ } else if (args.PRESETS == "1920x1080") {
+ window.resizeTo(
+ 1920 + (window.outerWidth - window.innerWidth),
+ 1080 + (window.outerHeight - window.innerHeight)
+ );
+ } else if (args.PRESETS == "2560x1440") {
+ window.resizeTo(
+ 2560 + (window.outerWidth - window.innerWidth),
+ 1440 + (window.outerHeight - window.innerHeight)
+ );
+ } else if (args.PRESETS == "2048x1080") {
+ window.resizeTo(
+ 2048 + (window.outerWidth - window.innerWidth),
+ 1080 + (window.outerHeight - window.innerHeight)
+ );
+ } else if (args.PRESETS == "3840x2160") {
+ window.resizeTo(
+ 3840 + (window.outerWidth - window.innerWidth),
+ 2160 + (window.outerHeight - window.innerHeight)
+ );
+ } else if (args.PRESETS == "7680x4320") {
+ window.resizeTo(
+ 7680 + (window.outerWidth - window.innerWidth),
+ 4320 + (window.outerHeight - window.innerHeight)
+ );
+ }
+ Scratch.vm.runtime.requestRedraw();
+ }
+ changeW(args) {
+ window.resizeBy(args.W, 0);
+ Scratch.vm.runtime.requestRedraw();
+ }
+ setW(args) {
+ const currentH = window.outerHeight;
+ window.resizeTo(args.W, currentH);
+ Scratch.vm.runtime.requestRedraw();
+ }
+ changeH(args) {
+ window.resizeBy(0, args.H);
+ Scratch.vm.runtime.requestRedraw();
+ }
+ setH(args) {
+ const currentW = window.outerWidth;
+ window.resizeTo(currentW, args.H);
+ Scratch.vm.runtime.requestRedraw();
+ }
+ matchStageSize() {
+ window.resizeTo(
+ Scratch.vm.runtime.stageWidth + (window.outerWidth - window.innerWidth),
+ Scratch.vm.runtime.stageHeight +
+ (window.outerHeight - window.innerHeight)
+ );
+ Scratch.vm.runtime.requestRedraw();
+ }
+ windowW() {
+ return window.outerWidth;
+ }
+ windowH() {
+ return window.outerHeight;
+ }
+ isTouchingEdge() {
+ const edgeX = screen.width - window.outerWidth;
+ const edgeY = screen.height - window.outerHeight;
+ return (
+ window.screenLeft <= 0 ||
+ window.screenTop <= 0 ||
+ window.screenLeft >= edgeX ||
+ window.screenTop >= edgeY
+ );
+ }
+ screenW() {
+ return screen.width;
+ }
+ screenH() {
+ return screen.height;
+ }
+ isFocused() {
+ return document.hasFocus();
+ }
+ changeTitleTo(args) {
+ document.title = args.TITLE;
+ }
+ windowTitle() {
+ return document.title;
+ }
+ enterFullscreen() {
+ if (document.fullscreenElement == null) {
+ document.documentElement.requestFullscreen();
+ }
+ }
+ exitFullscreen() {
+ if (document.fullscreenElement !== null) {
+ document.exitFullscreen();
+ }
+ }
+ isFullscreen() {
+ return document.fullscreenElement !== null;
+ }
+ closeWindow() {
+ const editorConfirmation = [
+ "Are you sure you want to close this window?",
+ "",
+ "(This message will not appear when the project is packaged)",
+ ].join("\n");
+ if (typeof ScratchBlocks === "undefined" || confirm(editorConfirmation)) {
+ window.close();
+ }
+ }
+ }
+ Scratch.extensions.register(new WindowControls());
+})(Scratch);
diff --git a/extensions/DNin/wake-lock.js b/extensions/DNin/wake-lock.js
new file mode 100644
index 0000000000..41b98c8aa2
--- /dev/null
+++ b/extensions/DNin/wake-lock.js
@@ -0,0 +1,118 @@
+// Name: Wake Lock
+// ID: dninwakelock
+// Description: Prevent the computer from falling asleep.
+// By: D-ScratchNinja
+
+(function (Scratch) {
+ "use strict";
+
+ if (!Scratch.extensions.unsandboxed) {
+ throw new Error("Wake Lock extension must run unsandboxed");
+ }
+
+ /** @type {WakeLockSentinel} */
+ let wakeLock = null;
+ let latestEnabled = false;
+ let promise = Promise.resolve();
+
+ class WakeLock {
+ constructor(runtime) {
+ this.runtime = runtime;
+ this.runtime.on("PROJECT_STOP_ALL", this.stopAll.bind(this));
+ }
+
+ getInfo() {
+ return {
+ id: "dninwakelock",
+ name: "Wake Lock",
+ docsURI: "https://extensions.turbowarp.org/DNin/wake-lock",
+ blocks: [
+ {
+ opcode: "setWakeLock",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "turn wake lock [enabled]",
+ arguments: {
+ enabled: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "state",
+ defaultValue: "true",
+ },
+ },
+ },
+ {
+ opcode: "isLocked",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "is wake lock active?",
+ },
+ ],
+ menus: {
+ state: {
+ acceptReporters: true,
+ items: [
+ {
+ text: "on",
+ value: "true",
+ },
+ {
+ text: "off",
+ value: "false",
+ },
+ ],
+ },
+ },
+ };
+ }
+
+ stopAll() {
+ this.setWakeLock({
+ enabled: false,
+ });
+ }
+
+ setWakeLock(args) {
+ if (!navigator.wakeLock) {
+ // Not supported in this browser.
+ return;
+ }
+
+ const previousEnabled = latestEnabled;
+ latestEnabled = Scratch.Cast.toBoolean(args.enabled);
+ if (latestEnabled && !previousEnabled) {
+ promise = promise
+ .then(() => navigator.wakeLock.request("screen"))
+ .then((sentinel) => {
+ wakeLock = sentinel;
+ })
+ .catch((error) => {
+ console.error(error);
+ // Allow to retry
+ latestEnabled = false;
+ });
+ return promise;
+ } else if (!latestEnabled && previousEnabled) {
+ promise = promise
+ .then(() => {
+ if (wakeLock) {
+ return wakeLock.release();
+ } else {
+ // Attempt to enable in the first place didn't work
+ }
+ })
+ .then(() => {
+ wakeLock = null;
+ })
+ .catch((error) => {
+ console.error(error);
+ wakeLock = null;
+ });
+ return promise;
+ }
+ }
+
+ isLocked() {
+ return !!wakeLock;
+ }
+ }
+
+ Scratch.extensions.register(new WakeLock(Scratch.vm.runtime));
+})(Scratch);
diff --git a/extensions/DT/cameracontrols.js b/extensions/DT/cameracontrols.js
index d8bbc94788..fb7944affc 100644
--- a/extensions/DT/cameracontrols.js
+++ b/extensions/DT/cameracontrols.js
@@ -1,17 +1,21 @@
-// Name: Camera Controls
+// Name: Camera Controls (Very Buggy)
+// ID: DTcameracontrols
// Description: Move the visible part of the stage.
// By: DT
-(Scratch => {
- 'use strict';
+((Scratch) => {
+ "use strict";
if (!Scratch.extensions.unsandboxed) {
- throw new Error('Camera extension must be run unsandboxed');
+ throw new Error("Camera extension must be run unsandboxed");
}
- const icon = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIyMjUuMzU0OCIgaGVpZ2h0PSIyMjUuMzU0OCIgdmlld0JveD0iMCwwLDIyNS4zNTQ4LDIyNS4zNTQ4Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTg3LjMyMjkzLC0zNy4zMjI1OSkiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2U9Im5vbmUiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIHN0eWxlPSJtaXgtYmxlbmQtbW9kZTogbm9ybWFsIj48cGF0aCBkPSJNMTg3LjMyMjk0LDE1MGMwLC02Mi4yMzAwMSA1MC40NDczOSwtMTEyLjY3NzQgMTEyLjY3NzQsLTExMi42Nzc0YzYyLjIzMDAxLDAgMTEyLjY3NzQsNTAuNDQ3MzkgMTEyLjY3NzQsMTEyLjY3NzRjMCw2Mi4yMzAwMSAtNTAuNDQ3MzksMTEyLjY3NzQgLTExMi42Nzc0LDExMi42Nzc0Yy02Mi4yMzAwMSwwIC0xMTIuNjc3NCwtNTAuNDQ3MzkgLTExMi42Nzc0LC0xMTIuNjc3NHoiIGZpbGw9IiNmZjRkYTciIHN0cm9rZS13aWR0aD0iMCIvPjxnPjxwYXRoIGQ9Ik0zMTcuMTAyOSw4MC44MTA4N2MyMS44OTI0LDAgMzkuNjYyMDcsMTcuNzM3MjMgMzkuNjYyMDcsMzkuNjM0NGMwLDEyLjMwNTE3IC01LjYxMTQ4LDIzLjI5NjIyIC0xNC40MDA4OCwzMC41NjgyNGg4Ljc3MDMydjY4LjE3NTYzaC0xMTQuMTMzMjV2LTU1Ljc5ODg5Yy0xNC4zMzQwOCwtMy41MjgxNyAtMjQuOTYxNTMsLTE2LjQ1NzQ3IC0yNC45NjE1MywtMzEuODgwNDRjMCwtMTguMTM5IDE0LjY5NjczLC0zMi44MzQ3OCAzMi44MzQ3OCwtMzIuODM0NzhjMTIuMDM3OTUsMCAyMi41NTY2MSw2LjQ3ODAxIDI4LjI3MjExLDE2LjEzMzk1bDQuODYxMzcsLTAuOTI0NzVjMy4xMjkyNiwtMTguNzY2OTYgMTkuNDM5NzYsLTMzLjA3MzM2IDM5LjA5OTAyLC0zMy4wNzMzNnpNMjc2LjIxODUxLDE0MS4yOTE3MWMtMS4xMDAzNSwzLjUzMzg5IC0yLjc2OTQ3LDYuODEyMDMgLTQuOTIwNTQsOS43MjE3OWgyMC41NDc3NGMtMy42ODc1NCwtMy4wNDgxNCAtNi44MDI0OCwtNi43NjUyNyAtOS4xODU0NSwtMTAuOTQ0Mjl6IiBmaWxsPSIjZmZmZmZmIiBzdHJva2Utd2lkdGg9IjEiLz48cGF0aCBkPSJNMzM2LjU3NTI5LDExOS41MjgxNWMwLDExLjA2ODM1IC04Ljk3MjY0LDIwLjA0MDk5IC0yMC4wNDA5OSwyMC4wNDA5OWMtMTEuMDY4MzUsMCAtMjAuMDQwOTksLTguOTcyNjQgLTIwLjA0MDk5LC0yMC4wNDA5OWMwLC0xMS4wNjgzNSA4Ljk3MjY0LC0yMC4wNDA5OSAyMC4wNDA5OSwtMjAuMDQwOTljMTEuMDY4MzUsMCAyMC4wNDA5OSw4Ljk3MjY0IDIwLjA0MDk5LDIwLjA0MDk5eiIgZmlsbD0iI2ZmNGRhNyIgc3Ryb2tlLXdpZHRoPSIwLjUiLz48cGF0aCBkPSJNMjYxLjE4MywxMzAuMDI1ODFjMCw4Ljk2MDA0IC03LjI2MzYyLDE2LjIyMzY2IC0xNi4yMjM2NiwxNi4yMjM2NmMtOC45NjAwNCwwIC0xNi4yMjM2NiwtNy4yNjM2MiAtMTYuMjIzNjYsLTE2LjIyMzY2YzAsLTguOTYwMDQgNy4yNjM2MiwtMTYuMjIzNjYgMTYuMjIzNjYsLTE2LjIyMzY2YzguOTYwMDQsMCAxNi4yMjM2Niw3LjI2MzYyIDE2LjIyMzY2LDE2LjIyMzY2eiIgZmlsbD0iI2ZmNGRhNyIgc3Ryb2tlLXdpZHRoPSIwLjUiLz48cGF0aCBkPSJNMzg3Ljk2MDM5LDE0NS44NTcyNHY2MC41MTA0M2wtMjEuNjAzMjYsLTEzLjQ3MjE3aC0xNi44OTgzNHYtMzMuNTY2MDdoMTYuODk4MzRsMjEuNTk5MTcsLTEzLjQ3MTEyeiIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlLXdpZHRoPSIxIi8+PC9nPjwvZz48L2c+PC9zdmc+PCEtLXJvdGF0aW9uQ2VudGVyOjExMi42NzcwNjU6MTEyLjY3NzQwNS0tPg==';
- const CW = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIUAAAB+CAYAAAAKj9LmAAAAAXNSR0IArs4c6QAABqdJREFUeF7tneuV1DAMhZdSoBWoB2qBeqAVKAWOOQRCNvHV27Kt/bsex7r6ciV7MjNvXupvWgW+v//8sy3+3bdPbyyDMJ3McmHauQ7BqPNYC0u9LnccissijumhQCJxRb8bbyG0dh3cODVrng4KrjjaZGSARBqzFIz0UEgF8YDhaU6p+JQ1auOXrC0tFFoxKIJ7jJEk4WkdVhpw15QKCisRPJLNnZObiOv8llpw15ICCksBKMl7+/Xj72E/PnyhDFeN4SbkuBjS5IjhGN+LhbuGoVCgwKnZuApEfR0aZwkNJzFIl6d4rcAYAgUKGiXLCwJ0XQt3QXAgbXqxTwkFCriXlJEgPK1L4yR3cCB9KBo8rQnBeI4xzClQwHfCU0Sg3N0RY6SAHMlC+lC1sHCLEChQwNekUQWISDb3GhI4GhhII6om6aFAga4Ewx08EkC0jpkaCg4Q1LuAe9dmGa+BQ6KNtq9wKR9UICQBZ0k0dx0SMKT6pIKCCkMTVBowNxnZxlPh0OijLSFmTkEFQhNstgRr1oPg0OiUAooCQobHU/I0QKBjb8p5hdopKEBYBCmTPf+rrmBYaaXpK1RQFBB20LUkWgGBjuORW4ihKCDsgPCYSdNXuEFhSb2HaDvMKS0hIiiQSxQQOZALg6KAyJFwyiqkJYTlFAUEJRV5xgyHokpGHhjQWUX7f28HQnaKcol8SaesSNJXkKAoICjy5xwzBIoqGzlh0JQQ6BQ9lyggcgMhPdnsQjFL2UDvOFJStzLg3BIihmK0iBYg9GAZHR8FZOoYMyiylg1vGK5CrwAH97zi0SmyQRENw0pwmECRCYjRMKwCB6eE3DpFFiiyAXEGZLayooJiBSDQQyTn5KId1irNKKeEvHKKDFBwHYIDQS/JEkBmcQwXKCKCHwXDHShcQCL0oW5Bn8ZRS8h/TjHSJThAWDkDRWQOHNnBMIUiIlgKFJEwXIFBcERoRIG4N2YqKLID0YReGYrr8xV/y8eo0lFAaO9/3uspbgGh8LTFAoKXUIvR00MxsodYpWRcQSJDMaJ0IJcYDQSCwtNBLRyBuy099xW/y0c0FLMD0TTbFgqvwDmna553zdPcK+w0NFvTrlPsCMXqQDRYUF8RDkVml9gBiB4UR1/R/ao+D6eYFQoPLUaURhUUHiLMCsTMjSV3F9J2fY9OUVD8k9NDi1EucVy311ekgCLzmcSKQKBms6AYcE4z2iVQXxEGxYz9xKouUVAQbsunrejKUPTAGO4UmfuJFXcdqNFs/y8o/qgU/f4PwcDchqBSXlAQoDiyM3s5QW9EwhNNawHQebvbbUGcGB1xE6eZfljo4VV2KFo2dwfj6O+qfFzu7V3BODf8BcWN4e8GxnUHWFA8dAG7gHF3JFBQEFrD1QBBZ0MFBQGK3YYUFLtlnBBvQUEQabchw6E4TtF2Ez5zvKHPaM5wgJU5WVFrC32au6CISqvuOgWFTr8lX50CiuorcrEV/lnSKiG5ALhbTfj3UxQUBcUrBdBTP/klW3+F4U7RJC23yA0WhKItP+oprGo4c8BC+h5Nayh6blFgjAejoBifg3QrIEHhUULKLdKx8HdB5C9sjy4hVUbGQUOGotxiXJKirzwciioj0SnH10vxy0Dok0vomUIcZo3gKJACCuQW1V9wUqofm+rXBssx9Am1mCEVFOUYFinVz5HyF4yRY1Q50Se+N0NKKCiOUWD4gdGFol125Jd5lGP4JV7sFAgKrwOt84IpYJRr2MIDnQKB4XH8fQ2RCsbxujrX0EFCgmJGMMo95GCYQBFRRo4Qua5R7sGHgwwFcosZwLiTp0rNa1VYUCAwIvoLSRPKuVcKkpcXUygi3aLg4KDOG8uGArnFKDDadaX9Rk+yHZ1DBEVmMLQNafUdgvJxFg19F1R0j9G747UuspNjiJ3iSMBMYNxBw4FlFzDcoRjZY/DaK1pPsgMYaigo/cVMYKCGtaBg3GqojBQYDDEHDzVxCmp/cYzL1IA+6b/zp+NNoaCWkllcY9dPx5tDsRIYBYVDHaP0Gdld4w6M1ZtNF6fgHHBl7zMKCge34JSTjK5RUDhBwQUjCxzVUzgCISknGcpKQREEhcQ1RjlHQREIBfew67q0iMOvOrwaAISmpJxf6wHIzkA0bd23pFTeqGcaaD4tJOit9NXPKFJBYeUcT9AgWBAMbd4dgEgLhbbnQG4i+f8uQKSHIgscOwExDRTepaXnHLsBMSUUkYDsCMT0UNzd4Ra7mF1hOPRMsyWVNH/c1/SA2R2Es5a/ACFtcn/xxMQMAAAAAElFTkSuQmCC';
- const CCW = 'data:image/png;base64,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';
+ const icon =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIyMjUuMzU0OCIgaGVpZ2h0PSIyMjUuMzU0OCIgdmlld0JveD0iMCwwLDIyNS4zNTQ4LDIyNS4zNTQ4Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTg3LjMyMjkzLC0zNy4zMjI1OSkiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2U9Im5vbmUiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIHN0eWxlPSJtaXgtYmxlbmQtbW9kZTogbm9ybWFsIj48cGF0aCBkPSJNMTg3LjMyMjk0LDE1MGMwLC02Mi4yMzAwMSA1MC40NDczOSwtMTEyLjY3NzQgMTEyLjY3NzQsLTExMi42Nzc0YzYyLjIzMDAxLDAgMTEyLjY3NzQsNTAuNDQ3MzkgMTEyLjY3NzQsMTEyLjY3NzRjMCw2Mi4yMzAwMSAtNTAuNDQ3MzksMTEyLjY3NzQgLTExMi42Nzc0LDExMi42Nzc0Yy02Mi4yMzAwMSwwIC0xMTIuNjc3NCwtNTAuNDQ3MzkgLTExMi42Nzc0LC0xMTIuNjc3NHoiIGZpbGw9IiNmZjRkYTciIHN0cm9rZS13aWR0aD0iMCIvPjxnPjxwYXRoIGQ9Ik0zMTcuMTAyOSw4MC44MTA4N2MyMS44OTI0LDAgMzkuNjYyMDcsMTcuNzM3MjMgMzkuNjYyMDcsMzkuNjM0NGMwLDEyLjMwNTE3IC01LjYxMTQ4LDIzLjI5NjIyIC0xNC40MDA4OCwzMC41NjgyNGg4Ljc3MDMydjY4LjE3NTYzaC0xMTQuMTMzMjV2LTU1Ljc5ODg5Yy0xNC4zMzQwOCwtMy41MjgxNyAtMjQuOTYxNTMsLTE2LjQ1NzQ3IC0yNC45NjE1MywtMzEuODgwNDRjMCwtMTguMTM5IDE0LjY5NjczLC0zMi44MzQ3OCAzMi44MzQ3OCwtMzIuODM0NzhjMTIuMDM3OTUsMCAyMi41NTY2MSw2LjQ3ODAxIDI4LjI3MjExLDE2LjEzMzk1bDQuODYxMzcsLTAuOTI0NzVjMy4xMjkyNiwtMTguNzY2OTYgMTkuNDM5NzYsLTMzLjA3MzM2IDM5LjA5OTAyLC0zMy4wNzMzNnpNMjc2LjIxODUxLDE0MS4yOTE3MWMtMS4xMDAzNSwzLjUzMzg5IC0yLjc2OTQ3LDYuODEyMDMgLTQuOTIwNTQsOS43MjE3OWgyMC41NDc3NGMtMy42ODc1NCwtMy4wNDgxNCAtNi44MDI0OCwtNi43NjUyNyAtOS4xODU0NSwtMTAuOTQ0Mjl6IiBmaWxsPSIjZmZmZmZmIiBzdHJva2Utd2lkdGg9IjEiLz48cGF0aCBkPSJNMzM2LjU3NTI5LDExOS41MjgxNWMwLDExLjA2ODM1IC04Ljk3MjY0LDIwLjA0MDk5IC0yMC4wNDA5OSwyMC4wNDA5OWMtMTEuMDY4MzUsMCAtMjAuMDQwOTksLTguOTcyNjQgLTIwLjA0MDk5LC0yMC4wNDA5OWMwLC0xMS4wNjgzNSA4Ljk3MjY0LC0yMC4wNDA5OSAyMC4wNDA5OSwtMjAuMDQwOTljMTEuMDY4MzUsMCAyMC4wNDA5OSw4Ljk3MjY0IDIwLjA0MDk5LDIwLjA0MDk5eiIgZmlsbD0iI2ZmNGRhNyIgc3Ryb2tlLXdpZHRoPSIwLjUiLz48cGF0aCBkPSJNMjYxLjE4MywxMzAuMDI1ODFjMCw4Ljk2MDA0IC03LjI2MzYyLDE2LjIyMzY2IC0xNi4yMjM2NiwxNi4yMjM2NmMtOC45NjAwNCwwIC0xNi4yMjM2NiwtNy4yNjM2MiAtMTYuMjIzNjYsLTE2LjIyMzY2YzAsLTguOTYwMDQgNy4yNjM2MiwtMTYuMjIzNjYgMTYuMjIzNjYsLTE2LjIyMzY2YzguOTYwMDQsMCAxNi4yMjM2Niw3LjI2MzYyIDE2LjIyMzY2LDE2LjIyMzY2eiIgZmlsbD0iI2ZmNGRhNyIgc3Ryb2tlLXdpZHRoPSIwLjUiLz48cGF0aCBkPSJNMzg3Ljk2MDM5LDE0NS44NTcyNHY2MC41MTA0M2wtMjEuNjAzMjYsLTEzLjQ3MjE3aC0xNi44OTgzNHYtMzMuNTY2MDdoMTYuODk4MzRsMjEuNTk5MTcsLTEzLjQ3MTEyeiIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlLXdpZHRoPSIxIi8+PC9nPjwvZz48L2c+PC9zdmc+PCEtLXJvdGF0aW9uQ2VudGVyOjExMi42NzcwNjU6MTEyLjY3NzQwNS0tPg==";
+ const CW =
+ "data:image/png;base64,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";
+ const CCW =
+ "data:image/png;base64,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";
const vm = Scratch.vm;
@@ -19,269 +23,284 @@
let cameraY = 0;
let cameraZoom = 100;
let cameraDirection = 90;
- let cameraBG = '#ffffff';
+ let cameraBG = "#ffffff";
vm.runtime.runtimeOptions.fencing = false;
vm.renderer.offscreenTouching = true;
vm.setInterpolation(false);
- function updateCamera(x = cameraX, y = cameraY, scale = cameraZoom / 100, rot = -cameraDirection + 90) {
- rot = rot / 180 * Math.PI;
+ function updateCamera(
+ x = cameraX,
+ y = cameraY,
+ scale = cameraZoom / 100,
+ rot = -cameraDirection + 90
+ ) {
+ rot = (rot / 180) * Math.PI;
let s = Math.sin(rot) * scale;
let c = Math.cos(rot) * scale;
let w = vm.runtime.stageWidth / 2;
let h = vm.runtime.stageHeight / 2;
vm.renderer._projection = [
- c / w, -s / h, 0, 0,
- s / w, c / h, 0, 0,
- 0, 0, -1, 0,
- (c * -x + s * -y) / w, (c * -y - s * -x) / h, 0, 1
+ c / w,
+ -s / h,
+ 0,
+ 0,
+ s / w,
+ c / h,
+ 0,
+ 0,
+ 0,
+ 0,
+ -1,
+ 0,
+ (c * -x + s * -y) / w,
+ (c * -y - s * -x) / h,
+ 0,
+ 1,
];
vm.renderer.dirty = true;
}
// tell resize to update camera as well
- vm.runtime.on('STAGE_SIZE_CHANGED', _ => updateCamera());
+ vm.runtime.on("STAGE_SIZE_CHANGED", (_) => updateCamera());
// fix mouse positions
let oldSX = vm.runtime.ioDevices.mouse.getScratchX;
let oldSY = vm.runtime.ioDevices.mouse.getScratchY;
- vm.runtime.ioDevices.mouse.getScratchX = function(...a) {
- return (oldSX.apply(this, a) + cameraX) / cameraZoom * 100;
+ vm.runtime.ioDevices.mouse.getScratchX = function (...a) {
+ return ((oldSX.apply(this, a) + cameraX) / cameraZoom) * 100;
};
- vm.runtime.ioDevices.mouse.getScratchY = function(...a) {
- return (oldSY.apply(this, a) + cameraY) / cameraZoom * 100;
+ vm.runtime.ioDevices.mouse.getScratchY = function (...a) {
+ return ((oldSY.apply(this, a) + cameraY) / cameraZoom) * 100;
};
class Camera {
-
getInfo() {
return {
+ id: "DTcameracontrols",
+ name: "Camera (Very Buggy)",
- id: 'DTcameracontrols',
- name: 'Camera',
-
- color1: '#ff4da7',
- color2: '#de4391',
- color3: '#c83c82',
+ color1: "#ff4da7",
+ color2: "#de4391",
+ color3: "#c83c82",
menuIconURI: icon,
blocks: [
{
- opcode: 'moveSteps',
+ opcode: "moveSteps",
blockType: Scratch.BlockType.COMMAND,
- text: 'move camera [val] steps',
+ text: "move camera [val] steps",
arguments: {
val: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: 10
+ defaultValue: 10,
},
- }
+ },
},
{
- opcode: 'rotateCW',
+ opcode: "rotateCW",
blockType: Scratch.BlockType.COMMAND,
- text: 'turn camera [image] [val] degrees',
+ text: "turn camera [image] [val] degrees",
arguments: {
image: {
type: Scratch.ArgumentType.IMAGE,
- dataURI: CW
+ dataURI: CW,
},
val: {
type: Scratch.ArgumentType.ANGLE,
- defaultValue: 15
- }
- }
+ defaultValue: 15,
+ },
+ },
},
{
- opcode: 'rotateCCW',
+ opcode: "rotateCCW",
blockType: Scratch.BlockType.COMMAND,
- text: 'turn camera [image] [val] degrees',
+ text: "turn camera [image] [val] degrees",
arguments: {
image: {
type: Scratch.ArgumentType.IMAGE,
- dataURI: CCW
+ dataURI: CCW,
},
val: {
type: Scratch.ArgumentType.ANGLE,
- defaultValue: 15
- }
- }
+ defaultValue: 15,
+ },
+ },
},
- '---',
+ "---",
{
- opcode: 'goTo',
+ opcode: "goTo",
blockType: Scratch.BlockType.COMMAND,
- text: 'move camera to [sprite]',
+ text: "move camera to [sprite]",
arguments: {
sprite: {
type: Scratch.ArgumentType.STRING,
menu: "sprites",
},
- }
+ },
},
{
- opcode: 'setBoth',
+ opcode: "setBoth",
blockType: Scratch.BlockType.COMMAND,
- text: 'set camera to x: [x] y: [y]',
+ text: "set camera to x: [x] y: [y]",
arguments: {
x: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: 0
+ defaultValue: 0,
},
y: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: 0
+ defaultValue: 0,
},
- }
+ },
},
- '---',
+ "---",
{
- opcode: 'setDirection',
+ opcode: "setDirection",
blockType: Scratch.BlockType.COMMAND,
- text: 'set camera direction to [val]',
+ text: "set camera direction to [val]",
arguments: {
val: {
type: Scratch.ArgumentType.ANGLE,
- defaultValue: 90
- }
- }
+ defaultValue: 90,
+ },
+ },
},
{
- opcode: 'pointTowards',
+ opcode: "pointTowards",
blockType: Scratch.BlockType.COMMAND,
- text: 'point camera towards [sprite]',
+ text: "point camera towards [sprite]",
arguments: {
sprite: {
type: Scratch.ArgumentType.STRING,
menu: "sprites",
},
- }
+ },
},
- '---',
+ "---",
{
- opcode: 'changeX',
+ opcode: "changeX",
blockType: Scratch.BlockType.COMMAND,
- text: 'change camera x by [val]',
+ text: "change camera x by [val]",
arguments: {
val: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: 10
- }
- }
+ defaultValue: 10,
+ },
+ },
},
{
- opcode: 'setX',
+ opcode: "setX",
blockType: Scratch.BlockType.COMMAND,
- text: 'set camera x to [val]',
+ text: "set camera x to [val]",
arguments: {
val: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: 0
- }
- }
+ defaultValue: 0,
+ },
+ },
},
{
- opcode: 'changeY',
+ opcode: "changeY",
blockType: Scratch.BlockType.COMMAND,
- text: 'change camera y by [val]',
+ text: "change camera y by [val]",
arguments: {
val: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: 10
- }
- }
+ defaultValue: 10,
+ },
+ },
},
{
- opcode: 'setY',
+ opcode: "setY",
blockType: Scratch.BlockType.COMMAND,
- text: 'set camera y to [val]',
+ text: "set camera y to [val]",
arguments: {
val: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: 0
- }
- }
+ defaultValue: 0,
+ },
+ },
},
- '---',
+ "---",
{
- opcode: 'getX',
+ opcode: "getX",
blockType: Scratch.BlockType.REPORTER,
- text: 'camera x',
+ text: "camera x",
},
{
- opcode: 'getY',
+ opcode: "getY",
blockType: Scratch.BlockType.REPORTER,
- text: 'camera y',
+ text: "camera y",
},
{
- opcode: 'getDirection',
+ opcode: "getDirection",
blockType: Scratch.BlockType.REPORTER,
- text: 'camera direction',
+ text: "camera direction",
},
- '---',
+ "---",
{
- opcode: 'changeZoom',
+ opcode: "changeZoom",
blockType: Scratch.BlockType.COMMAND,
- text: 'change camera zoom by [val]',
+ text: "change camera zoom by [val]",
arguments: {
val: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: 10
- }
- }
+ defaultValue: 10,
+ },
+ },
},
{
- opcode: 'setZoom',
+ opcode: "setZoom",
blockType: Scratch.BlockType.COMMAND,
- text: 'set camera zoom to [val] %',
+ text: "set camera zoom to [val] %",
arguments: {
val: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: 100
- }
- }
+ defaultValue: 100,
+ },
+ },
},
{
- opcode: 'getZoom',
+ opcode: "getZoom",
blockType: Scratch.BlockType.REPORTER,
- text: 'camera zoom',
+ text: "camera zoom",
},
- '---',
+ "---",
{
- opcode: 'setCol',
+ opcode: "setCol",
blockType: Scratch.BlockType.COMMAND,
- text: 'set background color to [val]',
+ text: "set background color to [val]",
arguments: {
val: {
- type: Scratch.ArgumentType.COLOR
- }
- }
+ type: Scratch.ArgumentType.COLOR,
+ },
+ },
},
{
- opcode: 'getCol',
+ opcode: "getCol",
blockType: Scratch.BlockType.REPORTER,
- text: 'background color',
+ text: "background color",
},
],
menus: {
sprites: {
- items: 'getSprites',
+ items: "getSprites",
acceptReporters: true,
- }
+ },
},
};
}
- getSprites(){
+ getSprites() {
let sprites = [];
- Scratch.vm.runtime.targets.forEach(e=>{
+ Scratch.vm.runtime.targets.forEach((e) => {
if (e.isOriginal && !e.isStage) sprites.push(e.sprite.name);
});
if (sprites.length === 0) {
- sprites.push('no sprites exist');
+ sprites.push("no sprites exist");
}
return sprites;
}
@@ -351,14 +370,18 @@
}
setCol(args, util) {
const rgb = Scratch.Cast.toRgbColorList(args.val);
- Scratch.vm.renderer.setBackgroundColor(rgb[0] / 255, rgb[1] / 255, rgb[2] / 255);
+ Scratch.vm.renderer.setBackgroundColor(
+ rgb[0] / 255,
+ rgb[1] / 255,
+ rgb[2] / 255
+ );
cameraBG = args.val;
}
getCol() {
return cameraBG;
}
moveSteps(args) {
- let dir = (-cameraDirection + 90) * Math.PI / 180;
+ let dir = ((-cameraDirection + 90) * Math.PI) / 180;
cameraX += args.val * Math.cos(dir);
cameraY += args.val * Math.sin(dir);
updateCamera();
@@ -386,7 +409,7 @@
vm.runtime.requestRedraw();
}
radToDeg(rad) {
- return rad * 180 / Math.PI;
+ return (rad * 180) / Math.PI;
}
}
diff --git a/extensions/JeremyGamer13/tween.js b/extensions/JeremyGamer13/tween.js
index 0850f41f98..e19e1b5d94 100644
--- a/extensions/JeremyGamer13/tween.js
+++ b/extensions/JeremyGamer13/tween.js
@@ -1,308 +1,313 @@
-// Name: Tween
-// Description: Easing methods for smooth animations.
-// By: JeremyGamer13
-
-(function (Scratch) {
- 'use strict';
-
- const EasingMethods = [
- "linear",
- "sine",
- "quad",
- "cubic",
- "quart",
- "quint",
- "expo",
- "circ",
- "back",
- "elastic",
- "bounce"
- ];
-
- const BlockType = Scratch.BlockType;
- const ArgumentType = Scratch.ArgumentType;
- const Cast = Scratch.Cast;
-
- class Tween {
- getInfo() {
- return {
- id: 'jeremygamerTweening',
- name: 'Tweening',
- blocks: [
- {
- opcode: 'tweenValue',
- text: '[MODE] ease [DIRECTION] [START] to [END] by [AMOUNT]%',
- disableMonitor: true,
- blockType: BlockType.REPORTER,
- arguments: {
- MODE: { type: ArgumentType.STRING, menu: 'modes' },
- DIRECTION: { type: ArgumentType.STRING, menu: 'direction' },
- START: { type: ArgumentType.NUMBER, defaultValue: 0 },
- END: { type: ArgumentType.NUMBER, defaultValue: 100 },
- AMOUNT: { type: ArgumentType.NUMBER, defaultValue: 50 },
- }
- }
- ],
- menus: {
- modes: {
- acceptReporters: true,
- items: EasingMethods.map(item => ({ text: item, value: item }))
- },
- direction: {
- acceptReporters: true,
- items: [
- "in",
- "out",
- "in out"
- ].map(item => ({ text: item, value: item }))
- }
- }
- };
- }
-
- // utilities
- multiplierToNormalNumber(mul, start, end) {
- const multiplier = end - start;
- const result = (mul * multiplier) + start;
- return result;
- }
-
- // blocks
- tweenValue(args) {
- const easeMethod = Cast.toString(args.MODE);
- const easeDirection = Cast.toString(args.DIRECTION);
-
- const start = Cast.toNumber(args.START);
- const end = Cast.toNumber(args.END);
-
- // easing method does not exist, return starting number
- if (!EasingMethods.includes(easeMethod)) return start;
- // easing method is not implemented, return starting number
- if (!this[easeMethod]) return start;
-
- const progress = Cast.toNumber(args.AMOUNT) / 100;
-
- const tweened = this[easeMethod](progress, easeDirection);
- return this.multiplierToNormalNumber(tweened, start, end);
- }
-
- // easing functions (placed below blocks for organization)
- linear(x) {
- // lol
- return x;
- }
-
- sine(x, dir) {
- switch (dir) {
- case "in": {
- return 1 - Math.cos((x * Math.PI) / 2);
- }
- case "out": {
- return Math.sin((x * Math.PI) / 2);
- }
- case "in out": {
- return -(Math.cos(Math.PI * x) - 1) / 2;
- }
- default:
- return 0;
- }
- }
-
- quad(x, dir) {
- switch (dir) {
- case "in": {
- return x * x;
- }
- case "out": {
- return 1 - (1 - x) * (1 - x);
- }
- case "in out": {
- return x < 0.5 ? 2 * x * x : 1 - Math.pow(-2 * x + 2, 2) / 2;
- }
- default:
- return 0;
- }
- }
-
- cubic(x, dir) {
- switch (dir) {
- case "in": {
- return x * x * x;
- }
- case "out": {
- return 1 - Math.pow(1 - x, 3);
- }
- case "in out": {
- return x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2;
- }
- default:
- return 0;
- }
- }
-
- quart(x, dir) {
- switch (dir) {
- case "in": {
- return x * x * x * x;
- }
- case "out": {
- return 1 - Math.pow(1 - x, 4);
- }
- case "in out": {
- return x < 0.5 ? 8 * x * x * x * x : 1 - Math.pow(-2 * x + 2, 4) / 2;
- }
- default:
- return 0;
- }
- }
-
- quint(x, dir) {
- switch (dir) {
- case "in": {
- return x * x * x * x * x;
- }
- case "out": {
- return 1 - Math.pow(1 - x, 5);
- }
- case "in out": {
- return x < 0.5 ? 16 * x * x * x * x * x : 1 - Math.pow(-2 * x + 2, 5) / 2;
- }
- default:
- return 0;
- }
- }
-
- expo(x, dir) {
- switch (dir) {
- case "in": {
- return x === 0 ? 0 : Math.pow(2, 10 * x - 10);
- }
- case "out": {
- return x === 1 ? 1 : 1 - Math.pow(2, -10 * x);
- }
- case "in out": {
- return x === 0
- ? 0
- : x === 1
- ? 1
- : x < 0.5 ? Math.pow(2, 20 * x - 10) / 2
- : (2 - Math.pow(2, -20 * x + 10)) / 2;
- }
- default:
- return 0;
- }
- }
-
- circ(x, dir) {
- switch (dir) {
- case "in": {
- return 1 - Math.sqrt(1 - Math.pow(x, 2));
- }
- case "out": {
- return Math.sqrt(1 - Math.pow(x - 1, 2));
- }
- case "in out": {
- return x < 0.5
- ? (1 - Math.sqrt(1 - Math.pow(2 * x, 2))) / 2
- : (Math.sqrt(1 - Math.pow(-2 * x + 2, 2)) + 1) / 2;
- }
- default:
- return 0;
- }
- }
-
- back(x, dir) {
- switch (dir) {
- case "in": {
- const c1 = 1.70158;
- const c3 = c1 + 1;
-
- return c3 * x * x * x - c1 * x * x;
- }
- case "out": {
- const c1 = 1.70158;
- const c3 = c1 + 1;
-
- return 1 + c3 * Math.pow(x - 1, 3) + c1 * Math.pow(x - 1, 2);
- }
- case "in out": {
- const c1 = 1.70158;
- const c2 = c1 * 1.525;
-
- return x < 0.5
- ? (Math.pow(2 * x, 2) * ((c2 + 1) * 2 * x - c2)) / 2
- : (Math.pow(2 * x - 2, 2) * ((c2 + 1) * (x * 2 - 2) + c2) + 2) / 2;
- }
- default:
- return 0;
- }
- }
-
- elastic(x, dir) {
- switch (dir) {
- case "in": {
- const c4 = (2 * Math.PI) / 3;
-
- return x === 0
- ? 0
- : x === 1
- ? 1
- : -Math.pow(2, 10 * x - 10) * Math.sin((x * 10 - 10.75) * c4);
- }
- case "out": {
- const c4 = (2 * Math.PI) / 3;
-
- return x === 0
- ? 0
- : x === 1
- ? 1
- : Math.pow(2, -10 * x) * Math.sin((x * 10 - 0.75) * c4) + 1;
- }
- case "in out": {
- const c5 = (2 * Math.PI) / 4.5;
-
- return x === 0
- ? 0
- : x === 1
- ? 1
- : x < 0.5
- ? -(Math.pow(2, 20 * x - 10) * Math.sin((20 * x - 11.125) * c5)) / 2
- : (Math.pow(2, -20 * x + 10) * Math.sin((20 * x - 11.125) * c5)) / 2 + 1;
- }
- default:
- return 0;
- }
- }
-
- bounce(x, dir) {
- switch (dir) {
- case "in": {
- return 1 - this.bounce(1 - x, "out");
- }
- case "out": {
- const n1 = 7.5625;
- const d1 = 2.75;
-
- if (x < 1 / d1) {
- return n1 * x * x;
- } else if (x < 2 / d1) {
- return n1 * (x -= 1.5 / d1) * x + 0.75;
- } else if (x < 2.5 / d1) {
- return n1 * (x -= 2.25 / d1) * x + 0.9375;
- } else {
- return n1 * (x -= 2.625 / d1) * x + 0.984375;
- }
- }
- case "in out": {
- return x < 0.5
- ? (1 - this.bounce(1 - 2 * x, "out")) / 2
- : (1 + this.bounce(2 * x - 1, "out")) / 2;
- }
- default:
- return 0;
- }
- }
- }
-
- Scratch.extensions.register(new Tween());
-})(Scratch);
+// Name: Tween
+// ID: jeremygamerTweening
+// Description: Easing methods for smooth animations.
+// By: JeremyGamer13
+
+(function (Scratch) {
+ "use strict";
+
+ const EasingMethods = [
+ "linear",
+ "sine",
+ "quad",
+ "cubic",
+ "quart",
+ "quint",
+ "expo",
+ "circ",
+ "back",
+ "elastic",
+ "bounce",
+ ];
+
+ const BlockType = Scratch.BlockType;
+ const ArgumentType = Scratch.ArgumentType;
+ const Cast = Scratch.Cast;
+
+ class Tween {
+ getInfo() {
+ return {
+ id: "jeremygamerTweening",
+ name: "Tweening",
+ blocks: [
+ {
+ opcode: "tweenValue",
+ text: "[MODE] ease [DIRECTION] [START] to [END] by [AMOUNT]%",
+ disableMonitor: true,
+ blockType: BlockType.REPORTER,
+ arguments: {
+ MODE: { type: ArgumentType.STRING, menu: "modes" },
+ DIRECTION: { type: ArgumentType.STRING, menu: "direction" },
+ START: { type: ArgumentType.NUMBER, defaultValue: 0 },
+ END: { type: ArgumentType.NUMBER, defaultValue: 100 },
+ AMOUNT: { type: ArgumentType.NUMBER, defaultValue: 50 },
+ },
+ },
+ ],
+ menus: {
+ modes: {
+ acceptReporters: true,
+ items: EasingMethods.map((item) => ({ text: item, value: item })),
+ },
+ direction: {
+ acceptReporters: true,
+ items: ["in", "out", "in out"].map((item) => ({
+ text: item,
+ value: item,
+ })),
+ },
+ },
+ };
+ }
+
+ // utilities
+ multiplierToNormalNumber(mul, start, end) {
+ const multiplier = end - start;
+ const result = mul * multiplier + start;
+ return result;
+ }
+
+ // blocks
+ tweenValue(args) {
+ const easeMethod = Cast.toString(args.MODE);
+ const easeDirection = Cast.toString(args.DIRECTION);
+
+ const start = Cast.toNumber(args.START);
+ const end = Cast.toNumber(args.END);
+
+ // easing method does not exist, return starting number
+ if (!EasingMethods.includes(easeMethod)) return start;
+ // easing method is not implemented, return starting number
+ if (!this[easeMethod]) return start;
+
+ const progress = Cast.toNumber(args.AMOUNT) / 100;
+
+ const tweened = this[easeMethod](progress, easeDirection);
+ return this.multiplierToNormalNumber(tweened, start, end);
+ }
+
+ // easing functions (placed below blocks for organization)
+ linear(x) {
+ // lol
+ return x;
+ }
+
+ sine(x, dir) {
+ switch (dir) {
+ case "in": {
+ return 1 - Math.cos((x * Math.PI) / 2);
+ }
+ case "out": {
+ return Math.sin((x * Math.PI) / 2);
+ }
+ case "in out": {
+ return -(Math.cos(Math.PI * x) - 1) / 2;
+ }
+ default:
+ return 0;
+ }
+ }
+
+ quad(x, dir) {
+ switch (dir) {
+ case "in": {
+ return x * x;
+ }
+ case "out": {
+ return 1 - (1 - x) * (1 - x);
+ }
+ case "in out": {
+ return x < 0.5 ? 2 * x * x : 1 - Math.pow(-2 * x + 2, 2) / 2;
+ }
+ default:
+ return 0;
+ }
+ }
+
+ cubic(x, dir) {
+ switch (dir) {
+ case "in": {
+ return x * x * x;
+ }
+ case "out": {
+ return 1 - Math.pow(1 - x, 3);
+ }
+ case "in out": {
+ return x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2;
+ }
+ default:
+ return 0;
+ }
+ }
+
+ quart(x, dir) {
+ switch (dir) {
+ case "in": {
+ return x * x * x * x;
+ }
+ case "out": {
+ return 1 - Math.pow(1 - x, 4);
+ }
+ case "in out": {
+ return x < 0.5 ? 8 * x * x * x * x : 1 - Math.pow(-2 * x + 2, 4) / 2;
+ }
+ default:
+ return 0;
+ }
+ }
+
+ quint(x, dir) {
+ switch (dir) {
+ case "in": {
+ return x * x * x * x * x;
+ }
+ case "out": {
+ return 1 - Math.pow(1 - x, 5);
+ }
+ case "in out": {
+ return x < 0.5
+ ? 16 * x * x * x * x * x
+ : 1 - Math.pow(-2 * x + 2, 5) / 2;
+ }
+ default:
+ return 0;
+ }
+ }
+
+ expo(x, dir) {
+ switch (dir) {
+ case "in": {
+ return x === 0 ? 0 : Math.pow(2, 10 * x - 10);
+ }
+ case "out": {
+ return x === 1 ? 1 : 1 - Math.pow(2, -10 * x);
+ }
+ case "in out": {
+ return x === 0
+ ? 0
+ : x === 1
+ ? 1
+ : x < 0.5
+ ? Math.pow(2, 20 * x - 10) / 2
+ : (2 - Math.pow(2, -20 * x + 10)) / 2;
+ }
+ default:
+ return 0;
+ }
+ }
+
+ circ(x, dir) {
+ switch (dir) {
+ case "in": {
+ return 1 - Math.sqrt(1 - Math.pow(x, 2));
+ }
+ case "out": {
+ return Math.sqrt(1 - Math.pow(x - 1, 2));
+ }
+ case "in out": {
+ return x < 0.5
+ ? (1 - Math.sqrt(1 - Math.pow(2 * x, 2))) / 2
+ : (Math.sqrt(1 - Math.pow(-2 * x + 2, 2)) + 1) / 2;
+ }
+ default:
+ return 0;
+ }
+ }
+
+ back(x, dir) {
+ switch (dir) {
+ case "in": {
+ const c1 = 1.70158;
+ const c3 = c1 + 1;
+
+ return c3 * x * x * x - c1 * x * x;
+ }
+ case "out": {
+ const c1 = 1.70158;
+ const c3 = c1 + 1;
+
+ return 1 + c3 * Math.pow(x - 1, 3) + c1 * Math.pow(x - 1, 2);
+ }
+ case "in out": {
+ const c1 = 1.70158;
+ const c2 = c1 * 1.525;
+
+ return x < 0.5
+ ? (Math.pow(2 * x, 2) * ((c2 + 1) * 2 * x - c2)) / 2
+ : (Math.pow(2 * x - 2, 2) * ((c2 + 1) * (x * 2 - 2) + c2) + 2) / 2;
+ }
+ default:
+ return 0;
+ }
+ }
+
+ elastic(x, dir) {
+ switch (dir) {
+ case "in": {
+ const c4 = (2 * Math.PI) / 3;
+
+ return x === 0
+ ? 0
+ : x === 1
+ ? 1
+ : -Math.pow(2, 10 * x - 10) * Math.sin((x * 10 - 10.75) * c4);
+ }
+ case "out": {
+ const c4 = (2 * Math.PI) / 3;
+
+ return x === 0
+ ? 0
+ : x === 1
+ ? 1
+ : Math.pow(2, -10 * x) * Math.sin((x * 10 - 0.75) * c4) + 1;
+ }
+ case "in out": {
+ const c5 = (2 * Math.PI) / 4.5;
+
+ return x === 0
+ ? 0
+ : x === 1
+ ? 1
+ : x < 0.5
+ ? -(Math.pow(2, 20 * x - 10) * Math.sin((20 * x - 11.125) * c5)) / 2
+ : (Math.pow(2, -20 * x + 10) * Math.sin((20 * x - 11.125) * c5)) /
+ 2 +
+ 1;
+ }
+ default:
+ return 0;
+ }
+ }
+
+ bounce(x, dir) {
+ switch (dir) {
+ case "in": {
+ return 1 - this.bounce(1 - x, "out");
+ }
+ case "out": {
+ const n1 = 7.5625;
+ const d1 = 2.75;
+
+ if (x < 1 / d1) {
+ return n1 * x * x;
+ } else if (x < 2 / d1) {
+ return n1 * (x -= 1.5 / d1) * x + 0.75;
+ } else if (x < 2.5 / d1) {
+ return n1 * (x -= 2.25 / d1) * x + 0.9375;
+ } else {
+ return n1 * (x -= 2.625 / d1) * x + 0.984375;
+ }
+ }
+ case "in out": {
+ return x < 0.5
+ ? (1 - this.bounce(1 - 2 * x, "out")) / 2
+ : (1 + this.bounce(2 * x - 1, "out")) / 2;
+ }
+ default:
+ return 0;
+ }
+ }
+ }
+
+ Scratch.extensions.register(new Tween());
+})(Scratch);
diff --git a/extensions/Lily/AllMenus.js b/extensions/Lily/AllMenus.js
index 31755ccf58..827b03aed3 100644
--- a/extensions/Lily/AllMenus.js
+++ b/extensions/Lily/AllMenus.js
@@ -1,56 +1,56 @@
// Name: All Menus
+// ID: lmsAllMenus
// Description: Special category with every menu from every Scratch category and extensions.
// By: LilyMakesThings
(function (Scratch) {
- 'use strict';
+ "use strict";
var blockXML;
- const blacklist = ['looks_costumenumbername', 'extension_wedo_tilt_menu'];
+ const blacklist = ["looks_costumenumbername", "extension_wedo_tilt_menu"];
- Scratch.vm.addListener('BLOCKSINFO_UPDATE', refreshMenus);
+ Scratch.vm.addListener("BLOCKSINFO_UPDATE", refreshMenus);
function refreshMenus() {
if (!window.ScratchBlocks) return;
- Scratch.vm.removeListener('BLOCKSINFO_UPDATE', refreshMenus);
+ Scratch.vm.removeListener("BLOCKSINFO_UPDATE", refreshMenus);
let allBlocks = Object.keys(ScratchBlocks.Blocks);
allBlocks = allBlocks.filter(
- item => item.includes('menu') &&
- !blacklist.includes(item)
+ (item) => item.includes("menu") && !blacklist.includes(item)
);
const menuBlocks = allBlocks.map(
- item => ' '
+ (item) => ' '
);
- blockXML = menuBlocks.join('');
+ blockXML = menuBlocks.join("");
Scratch.vm.runtime.extensionManager.refreshBlocks();
}
class AllMenus {
- constructor () {
- Scratch.vm.runtime.on('EXTENSION_ADDED', () => {
+ constructor() {
+ Scratch.vm.runtime.on("EXTENSION_ADDED", () => {
refreshMenus();
});
}
getInfo() {
return {
- id: 'lmsAllMenus',
- name: 'All Menus',
+ id: "lmsAllMenus",
+ name: "All Menus",
blocks: [
{
blockType: Scratch.BlockType.XML,
- xml: blockXML
- }
- ]
+ xml: blockXML,
+ },
+ ],
};
}
}
refreshMenus();
-Scratch.extensions.register(new AllMenus());
+ Scratch.extensions.register(new AllMenus());
})(Scratch);
diff --git a/extensions/Lily/Cast.js b/extensions/Lily/Cast.js
index fda432c8b7..3baf507a99 100644
--- a/extensions/Lily/Cast.js
+++ b/extensions/Lily/Cast.js
@@ -1,77 +1,86 @@
-// Name: Cast
-// Description: Convert values between types.
-// By: LilyMakesThings
-
-(function (Scratch) {
- 'use strict';
-
- const Cast = Scratch.Cast;
-
- class CastUtil {
- getInfo() {
- return {
- id: 'lmsCast',
- name: 'Cast',
- blocks: [
- {
- opcode: 'toType',
- blockType: Scratch.BlockType.REPORTER,
- text: 'cast [INPUT] to [TYPE]',
- allowDropAnywhere: true,
- disableMonitor: true,
- arguments: {
- INPUT: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'apple'
- },
- TYPE: {
- type: Scratch.ArgumentType.STRING,
- menu: 'type'
- }
- }
- },
- {
- opcode: 'typeOf',
- blockType: Scratch.BlockType.REPORTER,
- text: 'type of [INPUT]',
- disableMonitor: true,
- arguments: {
- INPUT: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'apple'
- }
- }
- }
- ],
- menus: {
- type: {
- acceptReporters: true,
- items: ['number', 'string', 'boolean', 'default']
- }
- }
- };
- }
-
- toType(args) {
- const input = args.INPUT;
- switch (args.TYPE) {
- case ('number'): return Cast.toNumber(input);
- case ('string'): return Cast.toString(input);
- case ('boolean'): return Cast.toBoolean(input);
- default: return input;
- }
- }
-
- typeOf(args) {
- const input = args.INPUT;
- switch (typeof input) {
- case ('number'): return 'number';
- case ('string'): return 'string';
- case ('boolean'): return 'boolean';
- default: return '';
- }
- }
- }
-
- Scratch.extensions.register(new CastUtil());
-})(Scratch);
+// Name: Cast
+// ID: lmsCast
+// Description: Convert values between types.
+// By: LilyMakesThings
+
+(function (Scratch) {
+ "use strict";
+
+ const Cast = Scratch.Cast;
+
+ class CastUtil {
+ getInfo() {
+ return {
+ id: "lmsCast",
+ name: "Cast",
+ blocks: [
+ {
+ opcode: "toType",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "cast [INPUT] to [TYPE]",
+ allowDropAnywhere: true,
+ disableMonitor: true,
+ arguments: {
+ INPUT: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "apple",
+ },
+ TYPE: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "type",
+ },
+ },
+ },
+ {
+ opcode: "typeOf",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "type of [INPUT]",
+ disableMonitor: true,
+ arguments: {
+ INPUT: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "apple",
+ },
+ },
+ },
+ ],
+ menus: {
+ type: {
+ acceptReporters: true,
+ items: ["number", "string", "boolean", "default"],
+ },
+ },
+ };
+ }
+
+ toType(args) {
+ const input = args.INPUT;
+ switch (args.TYPE) {
+ case "number":
+ return Cast.toNumber(input);
+ case "string":
+ return Cast.toString(input);
+ case "boolean":
+ return Cast.toBoolean(input);
+ default:
+ return input;
+ }
+ }
+
+ typeOf(args) {
+ const input = args.INPUT;
+ switch (typeof input) {
+ case "number":
+ return "number";
+ case "string":
+ return "string";
+ case "boolean":
+ return "boolean";
+ default:
+ return "";
+ }
+ }
+ }
+
+ Scratch.extensions.register(new CastUtil());
+})(Scratch);
diff --git a/extensions/Lily/ClonesPlus.js b/extensions/Lily/ClonesPlus.js
index 655fa8e9cc..009e997692 100644
--- a/extensions/Lily/ClonesPlus.js
+++ b/extensions/Lily/ClonesPlus.js
@@ -1,615 +1,656 @@
-// Name: Clones Plus
-// Description: Expansion of Scratch's clone features.
-// By: LilyMakesThings
-
-(function (Scratch) {
- 'use strict';
-
- const menuIconURI = "data:image/png;base64,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";
-
- /**
- * @param {VM.Target|null} target
- * @param {string|unknown} thing
- * @returns {string|number|boolean}
- */
- const getThingOfTarget = (target, thing) => {
- if (!target) {
- return '';
- }
- if (thing === 'x position') {
- return target.x;
- }
- if (thing === 'y position') {
- return target.y;
- }
- if (thing === 'direction') {
- return target.direction;
- }
- if (thing === 'costume num') {
- return (target.currentCostume + 1);
- }
- if (thing === 'costume name') {
- return target.getCostumes()[target.currentCostume].name;
- }
- if (thing === 'size') {
- return target.size;
- }
- if (thing === 'volume') {
- return target.volume;
- }
- // this should never happen
- return '';
- };
-
- class ClonesPlus {
- getInfo() {
- return {
- id: 'lmsclonesplus',
- name: 'Clones+',
- color1: '#FFAB19',
- color2: '#EC9C13',
- color3: '#CF8B17',
- menuIconURI: menuIconURI,
- blocks: [
- {
- opcode: 'whenCloneStartsWithVar',
- blockType: Scratch.BlockType.HAT,
- text: 'when I start as a clone with [INPUTA] set to [INPUTB]',
- filter: [Scratch.TargetType.SPRITE],
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.STRING,
- menu: 'variablesMenu'
- },
- INPUTB: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '1'
- }
- }
- },
- {
- opcode: 'createCloneWithVar',
- blockType: Scratch.BlockType.COMMAND,
- text: 'create clone with [INPUTA] set to [INPUTB]',
- filter: [Scratch.TargetType.SPRITE],
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.STRING,
- menu: 'variablesMenu'
- },
- INPUTB: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '1'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'touchingCloneWithVar',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'touching clone with [INPUTA] set to [INPUTB]?',
- filter: [Scratch.TargetType.SPRITE],
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.STRING,
- menu: 'variablesMenu'
- },
- INPUTB: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '1'
- }
- }
- },
- {
- opcode: 'touchingMainSprite',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'touching main sprite?',
- filter: [Scratch.TargetType.SPRITE]
- },
-
- '---',
-
- {
- opcode: 'setVariableOfClone',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set variable [INPUTA] to [INPUTB] for clones with [INPUTC] set to [INPUTD]',
- filter: [Scratch.TargetType.SPRITE],
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.STRING,
- menu: 'variablesMenu'
- },
- INPUTB: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '0'
- },
- INPUTC: {
- type: Scratch.ArgumentType.STRING,
- menu: 'variablesMenu'
- },
- INPUTD: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '1'
- }
- }
- },
- {
- opcode: 'getVariableOfClone',
- blockType: Scratch.BlockType.REPORTER,
- text: 'variable [INPUTA] of clone with [INPUTB] set to [INPUTC]',
- filter: [Scratch.TargetType.SPRITE],
- disableMonitor: true,
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.STRING,
- menu: 'variablesMenu'
- },
- INPUTB: {
- type: Scratch.ArgumentType.STRING,
- menu: 'variablesMenu'
- },
- INPUTC: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '1'
- }
- }
- },
- {
- opcode: 'setVariableOfMainSprite',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set variable [INPUTA] to [INPUTB] for main sprite',
- filter: [Scratch.TargetType.SPRITE],
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.STRING,
- menu: 'variablesMenu'
- },
- INPUTB: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '1'
- }
- }
- },
- {
- opcode: 'getVariableOfMainSprite',
- blockType: Scratch.BlockType.REPORTER,
- text: 'variable [INPUT] of main sprite',
- filter: [Scratch.TargetType.SPRITE],
- disableMonitor: true,
- arguments: {
- INPUT: {
- type: Scratch.ArgumentType.STRING,
- menu: 'variablesMenu'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'cloneExists',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'clone with [INPUTA] set to [INPUTB] exists?',
- filter: [Scratch.TargetType.SPRITE],
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.STRING,
- menu: 'variablesMenu'
- },
- INPUTB: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '1'
- }
- }
- },
- {
- opcode: 'getThingOfClone',
- blockType: Scratch.BlockType.REPORTER,
- text: '[INPUTA] of clone with [INPUTB] set to [INPUTC]',
- filter: [Scratch.TargetType.SPRITE],
- disableMonitor: true,
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'x position',
- menu: 'thingOfMenu'
- },
- INPUTB: {
- type: Scratch.ArgumentType.STRING,
- menu: 'variablesMenu'
- },
- INPUTC: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '1'
- }
- }
- },
- {
- opcode: 'getThingOfMainSprite',
- blockType: Scratch.BlockType.REPORTER,
- text: '[INPUT] of main sprite',
- filter: [Scratch.TargetType.SPRITE],
- disableMonitor: true,
- arguments: {
- INPUT: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'x position',
- menu: 'thingOfMenu'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'stopScriptsInSprite',
- blockType: Scratch.BlockType.COMMAND,
- text: 'stop scripts in [INPUT]',
- arguments: {
- INPUT: {
- type: Scratch.ArgumentType.STRING,
- menu: 'spriteMenu'
- }
- }
- },
- {
- opcode: 'stopScriptsInClone',
- blockType: Scratch.BlockType.COMMAND,
- text: 'stop scripts in clones with [INPUTA] set to [INPUTB]',
- filter: [Scratch.TargetType.SPRITE],
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.STRING,
- menu: 'variablesMenu'
- },
- INPUTB: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '1'
- }
- }
- },
- {
- opcode: 'stopScriptsInMainSprite',
- blockType: Scratch.BlockType.COMMAND,
- text: 'stop scripts in main sprite',
- filter: [Scratch.TargetType.SPRITE]
- },
-
- '---',
-
- {
- opcode: 'deleteClonesInSprite',
- blockType: Scratch.BlockType.COMMAND,
- text: 'delete clones in [INPUT]',
- arguments: {
- INPUT: {
- type: Scratch.ArgumentType.STRING,
- menu: 'spriteMenu'
- }
- }
- },
- {
- opcode: 'deleteCloneWithVar',
- blockType: Scratch.BlockType.COMMAND,
- text: 'delete clones with [INPUTA] set to [INPUTB]',
- filter: [Scratch.TargetType.SPRITE],
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.STRING,
- menu: 'variablesMenu'
- },
- INPUTB: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '1'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'isClone',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'is clone?',
- filter: [Scratch.TargetType.SPRITE]
- },
-
- '---',
-
- {
- opcode: 'cloneCount',
- blockType: Scratch.BlockType.REPORTER,
- text: 'clone count'
- },
- {
- opcode: 'spriteCloneCount',
- blockType: Scratch.BlockType.REPORTER,
- text: 'clone count of [INPUT]',
- disableMonitor: true,
- arguments: {
- INPUT: {
- type: Scratch.ArgumentType.STRING,
- menu: 'spriteMenu'
- }
- }
- }
- ],
- menus: {
- spriteMenu: {
- acceptReporters: true,
- items: 'getSprites'
- },
- // menus use acceptReporters: false for Scratch parity
- variablesMenu: {
- acceptReporters: false,
- items: 'getVariables'
- },
- thingOfMenu: {
- acceptReporters: false,
- items: [
- {
- text: 'x position',
- value: 'x position'
- },
- {
- text: 'y position',
- value: 'y position'
- },
- {
- text: 'direction',
- value: 'direction'
- },
- {
- text: 'costume #',
- value: 'costume num'
- },
- {
- text: 'costume name',
- value: 'costume name'
- },
- {
- text: 'size',
- value: 'size'
- },
- {
- text: 'volume',
- value: 'volume'
- },
- ]
- }
- }
- };
- }
-
- whenCloneStartsWithVar(args, util) {
- // TODO: this is really not ideal. this should be an event-based hat ideally, but we don't have a good
- // way to do that right now...
- if (util.target.isOriginal) {
- return false;
- }
- const variable = util.target.lookupVariableById(args.INPUTA);
- const expectedValue = args.INPUTB;
- if (variable) {
- return Scratch.Cast.compare(variable.value, expectedValue) === 0;
- }
- return false;
- }
-
- createCloneWithVar(args, util) {
- // @ts-expect-error - not typed yet
- Scratch.vm.runtime.ext_scratch3_control._createClone(util.target.sprite.name, util.target);
- const clones = util.target.sprite.clones;
- const cloneNum = clones.length - 1;
- const cloneVariable = clones[cloneNum].lookupVariableById(args.INPUTA);
- if (cloneVariable) {
- cloneVariable.value = args.INPUTB;
- }
- }
-
- touchingCloneWithVar(args, util) {
- const drawableCandidates = util.target.sprite.clones
- .filter(clone => {
- const variable = clone.lookupVariableById(args.INPUTA);
- return variable && Scratch.Cast.compare(variable.value, args.INPUTB) === 0;
- })
- .map(clone => clone.drawableID);
- if (drawableCandidates.length === 0) {
- return false;
- }
- return Scratch.vm.renderer.isTouchingDrawables(util.target.drawableID, drawableCandidates);
- }
-
- touchingMainSprite(args, util) {
- if (util.target.isOriginal) {
- return false;
- }
- const main = util.target.sprite.clones[0];
- const drawableCandidates = [main.drawableID];
- return Scratch.vm.renderer.isTouchingDrawables(util.target.drawableID, drawableCandidates);
- }
-
- setVariableOfClone(args, util) {
- const newVariableValue = args.INPUTB;
- const expectedVarValue = args.INPUTD;
- const clones = util.target.sprite.clones;
- for (let index = 1; index < clones.length; index++) {
- const checkVar = clones[index].lookupVariableById(args.INPUTC);
- if (checkVar && Scratch.Cast.compare(checkVar.value, expectedVarValue) === 0) {
- const editVar = clones[index].lookupVariableById(args.INPUTA);
- if (editVar) {
- editVar.value = newVariableValue;
- }
- }
- }
- }
-
- getVariableOfClone(args, util) {
- const clone = this.getCloneFromVariable(args.INPUTB, args.INPUTC, util.target.sprite.clones);
- if (!clone) {
- return '';
- }
- // guaranteed to exist by getCloneFromVariable
- const cloneVar = clone.lookupVariableById(args.INPUTA);
- return cloneVar.value;
- }
-
- setVariableOfMainSprite(args, util) {
- const main = util.target.sprite.clones[0];
- const variableObj = main.lookupVariableById(args.INPUTA);
- if (variableObj) {
- variableObj.value = args.INPUTB;
- }
- }
-
- getVariableOfMainSprite(args, util) {
- const main = util.target.sprite.clones[0];
- const variableObj = main.lookupVariableById(args.INPUT);
- if (variableObj) {
- return variableObj.value;
- }
- return '';
- }
-
- cloneExists(args, util) {
- const clone = this.getCloneFromVariable(args.INPUTA, args.INPUTB, util.target.sprite.clones);
- return !!clone;
- }
-
- getThingOfClone(args, util) {
- const clone = this.getCloneFromVariable(args.INPUTB, args.INPUTC, util.target.sprite.clones);
- return getThingOfTarget(clone, args.INPUTA);
- }
-
- getThingOfMainSprite(args, util) {
- const main = util.target.sprite.clones[0];
- return getThingOfTarget(main, args.INPUT);
- }
-
- stopScriptsInSprite(args) {
- const targetObj = Scratch.vm.runtime.getSpriteTargetByName(args.INPUT);
- if (targetObj) {
- Scratch.vm.runtime.stopForTarget(targetObj);
- }
- }
-
- stopScriptsInMainSprite(args, util) {
- Scratch.vm.runtime.stopForTarget(util.target.sprite.clones[0]);
- }
-
- stopScriptsInClone(args, util) {
- const clones = util.target.sprite.clones;
- let expectedValue = args.INPUTB;
- for (let index = 1; index < clones.length; index++) {
- const cloneVariable = clones[index].lookupVariableById(args.INPUTA);
- if (cloneVariable && Scratch.Cast.compare(cloneVariable.value, expectedValue) === 0) {
- Scratch.vm.runtime.stopForTarget(clones[index]);
- }
- }
- }
-
- deleteClonesInSprite(args, util) {
- const target = Scratch.vm.runtime.getSpriteTargetByName(args.INPUT);
- if (!target) {
- return;
- }
- const clones = target.sprite.clones;
- for (let index = clones.length - 1; index > 0; index--) {
- Scratch.vm.runtime.disposeTarget(clones[index]);
- }
- }
-
- deleteCloneWithVar(args, util) {
- const clones = util.target.sprite.clones;
- const expectedValue = args.INPUTB;
- for (let index = clones.length - 1; index > 0; index--) {
- const cloneVar = clones[index].lookupVariableById(args.INPUTA);
- if (cloneVar && Scratch.Cast.compare(cloneVar.value, expectedValue) === 0) {
- Scratch.vm.runtime.disposeTarget(clones[index]);
- }
- }
- }
-
- isClone(args, util) {
- return !util.target.isOriginal;
- }
-
- cloneCount(args, util) {
- return Scratch.vm.runtime._cloneCounter;
- }
-
- spriteCloneCount(args, util) {
- const target = Scratch.vm.runtime.getSpriteTargetByName(args.INPUT);
- if (target) {
- return (target.sprite.clones.length - 1);
- }
- return 0;
- }
-
- /**
- * @param {string} variableId
- * @param {unknown} expectedValue
- * @param {VM.Target[]} clones
- * @returns {VM.Target|null}
- */
- getCloneFromVariable(variableId, expectedValue, clones) {
- for (let index = 1; index < clones.length; index++) {
- const cloneVar = clones[index].lookupVariableById(variableId);
- if (cloneVar && Scratch.Cast.compare(cloneVar.value, expectedValue) === 0) {
- return clones[index];
- }
- }
- return null;
- }
-
- getSprites() {
- let spriteNames = [];
- const targets = Scratch.vm.runtime.targets;
- const myself = Scratch.vm.runtime.getEditingTarget().sprite.name;
- for (let index = 1; index < targets.length; index++) {
- const curTarget = targets[index].sprite;
- let display = curTarget.name;
- if (myself === curTarget.name) {
- display = 'myself';
- }
- if (targets[index].isOriginal) {
- const jsonOBJ = {
- text: display,
- value: curTarget.name
- };
- spriteNames.push(jsonOBJ);
- }
- }
- if (spriteNames.length > 0) {
- return spriteNames;
- } else {
- return [{ text: "", value: 0 }]; //this should never happen but it's a failsafe
- }
- }
-
- getSpriteObj(name) { //This is unused but I'm leaving it in for potential future blocks
- const spriteObj = Scratch.vm.runtime.getSpriteTargetByName(name);
- return JSON.stringify(spriteObj);
- }
-
- getVariables() {
- // @ts-expect-error - Blockly not typed yet
- // eslint-disable-next-line no-undef
- const variables = typeof Blockly === 'undefined' ? [] : Blockly.getMainWorkspace()
- .getVariableMap()
- .getVariablesOfType('')
- .filter(model => model.isLocal)
- .map(model => ({
- text: model.name,
- value: model.getId()
- }));
- if (variables.length > 0) {
- return variables;
- } else {
- return [{ text: "", value: "" }];
- }
- }
- }
- Scratch.extensions.register(new ClonesPlus());
-})(Scratch);
+// Name: Clones Plus
+// ID: lmsclonesplus
+// Description: Expansion of Scratch's clone features.
+// By: LilyMakesThings
+
+(function (Scratch) {
+ "use strict";
+
+ const menuIconURI =
+ "data:image/png;base64,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";
+
+ /**
+ * @param {VM.Target|null} target
+ * @param {string|unknown} thing
+ * @returns {string|number|boolean}
+ */
+ const getThingOfTarget = (target, thing) => {
+ if (!target) {
+ return "";
+ }
+ if (thing === "x position") {
+ return target.x;
+ }
+ if (thing === "y position") {
+ return target.y;
+ }
+ if (thing === "direction") {
+ return target.direction;
+ }
+ if (thing === "costume num") {
+ return target.currentCostume + 1;
+ }
+ if (thing === "costume name") {
+ return target.getCostumes()[target.currentCostume].name;
+ }
+ if (thing === "size") {
+ return target.size;
+ }
+ if (thing === "volume") {
+ return target.volume;
+ }
+ // this should never happen
+ return "";
+ };
+
+ class ClonesPlus {
+ getInfo() {
+ return {
+ id: "lmsclonesplus",
+ name: "Clones+",
+ color1: "#FFAB19",
+ color2: "#EC9C13",
+ color3: "#CF8B17",
+ menuIconURI: menuIconURI,
+ blocks: [
+ {
+ opcode: "whenCloneStartsWithVar",
+ blockType: Scratch.BlockType.HAT,
+ text: "when I start as a clone with [INPUTA] set to [INPUTB]",
+ filter: [Scratch.TargetType.SPRITE],
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "variablesMenu",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "createCloneWithVar",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "create clone with [INPUTA] set to [INPUTB]",
+ filter: [Scratch.TargetType.SPRITE],
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "variablesMenu",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "1",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "touchingCloneWithVar",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "touching clone with [INPUTA] set to [INPUTB]?",
+ filter: [Scratch.TargetType.SPRITE],
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "variablesMenu",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "touchingMainSprite",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "touching main sprite?",
+ filter: [Scratch.TargetType.SPRITE],
+ },
+
+ "---",
+
+ {
+ opcode: "setVariableOfClone",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set variable [INPUTA] to [INPUTB] for clones with [INPUTC] set to [INPUTD]",
+ filter: [Scratch.TargetType.SPRITE],
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "variablesMenu",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "0",
+ },
+ INPUTC: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "variablesMenu",
+ },
+ INPUTD: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "getVariableOfClone",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "variable [INPUTA] of clone with [INPUTB] set to [INPUTC]",
+ filter: [Scratch.TargetType.SPRITE],
+ disableMonitor: true,
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "variablesMenu",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "variablesMenu",
+ },
+ INPUTC: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "setVariableOfMainSprite",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set variable [INPUTA] to [INPUTB] for main sprite",
+ filter: [Scratch.TargetType.SPRITE],
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "variablesMenu",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "getVariableOfMainSprite",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "variable [INPUT] of main sprite",
+ filter: [Scratch.TargetType.SPRITE],
+ disableMonitor: true,
+ arguments: {
+ INPUT: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "variablesMenu",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "cloneExists",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "clone with [INPUTA] set to [INPUTB] exists?",
+ filter: [Scratch.TargetType.SPRITE],
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "variablesMenu",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "getThingOfClone",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[INPUTA] of clone with [INPUTB] set to [INPUTC]",
+ filter: [Scratch.TargetType.SPRITE],
+ disableMonitor: true,
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "x position",
+ menu: "thingOfMenu",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "variablesMenu",
+ },
+ INPUTC: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "getThingOfMainSprite",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[INPUT] of main sprite",
+ filter: [Scratch.TargetType.SPRITE],
+ disableMonitor: true,
+ arguments: {
+ INPUT: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "x position",
+ menu: "thingOfMenu",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "stopScriptsInSprite",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "stop scripts in [INPUT]",
+ arguments: {
+ INPUT: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "spriteMenu",
+ },
+ },
+ },
+ {
+ opcode: "stopScriptsInClone",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "stop scripts in clones with [INPUTA] set to [INPUTB]",
+ filter: [Scratch.TargetType.SPRITE],
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "variablesMenu",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "stopScriptsInMainSprite",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "stop scripts in main sprite",
+ filter: [Scratch.TargetType.SPRITE],
+ },
+
+ "---",
+
+ {
+ opcode: "deleteClonesInSprite",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "delete clones in [INPUT]",
+ arguments: {
+ INPUT: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "spriteMenu",
+ },
+ },
+ },
+ {
+ opcode: "deleteCloneWithVar",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "delete clones with [INPUTA] set to [INPUTB]",
+ filter: [Scratch.TargetType.SPRITE],
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "variablesMenu",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "1",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "isClone",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "is clone?",
+ filter: [Scratch.TargetType.SPRITE],
+ },
+
+ "---",
+
+ {
+ opcode: "cloneCount",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "clone count",
+ },
+ {
+ opcode: "spriteCloneCount",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "clone count of [INPUT]",
+ disableMonitor: true,
+ arguments: {
+ INPUT: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "spriteMenu",
+ },
+ },
+ },
+ ],
+ menus: {
+ spriteMenu: {
+ acceptReporters: true,
+ items: "getSprites",
+ },
+ // menus use acceptReporters: false for Scratch parity
+ variablesMenu: {
+ acceptReporters: false,
+ items: "getVariables",
+ },
+ thingOfMenu: {
+ acceptReporters: false,
+ items: [
+ {
+ text: "x position",
+ value: "x position",
+ },
+ {
+ text: "y position",
+ value: "y position",
+ },
+ {
+ text: "direction",
+ value: "direction",
+ },
+ {
+ text: "costume #",
+ value: "costume num",
+ },
+ {
+ text: "costume name",
+ value: "costume name",
+ },
+ {
+ text: "size",
+ value: "size",
+ },
+ {
+ text: "volume",
+ value: "volume",
+ },
+ ],
+ },
+ },
+ };
+ }
+
+ whenCloneStartsWithVar(args, util) {
+ // TODO: this is really not ideal. this should be an event-based hat ideally, but we don't have a good
+ // way to do that right now...
+ if (util.target.isOriginal) {
+ return false;
+ }
+ const variable = util.target.lookupVariableById(args.INPUTA);
+ const expectedValue = args.INPUTB;
+ if (variable) {
+ return Scratch.Cast.compare(variable.value, expectedValue) === 0;
+ }
+ return false;
+ }
+
+ createCloneWithVar(args, util) {
+ // @ts-expect-error - not typed yet
+ Scratch.vm.runtime.ext_scratch3_control._createClone(
+ util.target.sprite.name,
+ util.target
+ );
+ const clones = util.target.sprite.clones;
+ const cloneNum = clones.length - 1;
+ const cloneVariable = clones[cloneNum].lookupVariableById(args.INPUTA);
+ if (cloneVariable) {
+ cloneVariable.value = args.INPUTB;
+ }
+ }
+
+ touchingCloneWithVar(args, util) {
+ const drawableCandidates = util.target.sprite.clones
+ .filter((clone) => {
+ const variable = clone.lookupVariableById(args.INPUTA);
+ return (
+ variable && Scratch.Cast.compare(variable.value, args.INPUTB) === 0
+ );
+ })
+ .map((clone) => clone.drawableID);
+ if (drawableCandidates.length === 0) {
+ return false;
+ }
+ return Scratch.vm.renderer.isTouchingDrawables(
+ util.target.drawableID,
+ drawableCandidates
+ );
+ }
+
+ touchingMainSprite(args, util) {
+ if (util.target.isOriginal) {
+ return false;
+ }
+ const main = util.target.sprite.clones[0];
+ const drawableCandidates = [main.drawableID];
+ return Scratch.vm.renderer.isTouchingDrawables(
+ util.target.drawableID,
+ drawableCandidates
+ );
+ }
+
+ setVariableOfClone(args, util) {
+ const newVariableValue = args.INPUTB;
+ const expectedVarValue = args.INPUTD;
+ const clones = util.target.sprite.clones;
+ for (let index = 1; index < clones.length; index++) {
+ const checkVar = clones[index].lookupVariableById(args.INPUTC);
+ if (
+ checkVar &&
+ Scratch.Cast.compare(checkVar.value, expectedVarValue) === 0
+ ) {
+ const editVar = clones[index].lookupVariableById(args.INPUTA);
+ if (editVar) {
+ editVar.value = newVariableValue;
+ }
+ }
+ }
+ }
+
+ getVariableOfClone(args, util) {
+ const clone = this.getCloneFromVariable(
+ args.INPUTB,
+ args.INPUTC,
+ util.target.sprite.clones
+ );
+ if (!clone) {
+ return "";
+ }
+ // guaranteed to exist by getCloneFromVariable
+ const cloneVar = clone.lookupVariableById(args.INPUTA);
+ return cloneVar.value;
+ }
+
+ setVariableOfMainSprite(args, util) {
+ const main = util.target.sprite.clones[0];
+ const variableObj = main.lookupVariableById(args.INPUTA);
+ if (variableObj) {
+ variableObj.value = args.INPUTB;
+ }
+ }
+
+ getVariableOfMainSprite(args, util) {
+ const main = util.target.sprite.clones[0];
+ const variableObj = main.lookupVariableById(args.INPUT);
+ if (variableObj) {
+ return variableObj.value;
+ }
+ return "";
+ }
+
+ cloneExists(args, util) {
+ const clone = this.getCloneFromVariable(
+ args.INPUTA,
+ args.INPUTB,
+ util.target.sprite.clones
+ );
+ return !!clone;
+ }
+
+ getThingOfClone(args, util) {
+ const clone = this.getCloneFromVariable(
+ args.INPUTB,
+ args.INPUTC,
+ util.target.sprite.clones
+ );
+ return getThingOfTarget(clone, args.INPUTA);
+ }
+
+ getThingOfMainSprite(args, util) {
+ const main = util.target.sprite.clones[0];
+ return getThingOfTarget(main, args.INPUT);
+ }
+
+ stopScriptsInSprite(args) {
+ const targetObj = Scratch.vm.runtime.getSpriteTargetByName(args.INPUT);
+ if (targetObj) {
+ Scratch.vm.runtime.stopForTarget(targetObj);
+ }
+ }
+
+ stopScriptsInMainSprite(args, util) {
+ Scratch.vm.runtime.stopForTarget(util.target.sprite.clones[0]);
+ }
+
+ stopScriptsInClone(args, util) {
+ const clones = util.target.sprite.clones;
+ let expectedValue = args.INPUTB;
+ for (let index = 1; index < clones.length; index++) {
+ const cloneVariable = clones[index].lookupVariableById(args.INPUTA);
+ if (
+ cloneVariable &&
+ Scratch.Cast.compare(cloneVariable.value, expectedValue) === 0
+ ) {
+ Scratch.vm.runtime.stopForTarget(clones[index]);
+ }
+ }
+ }
+
+ deleteClonesInSprite(args, util) {
+ const target = Scratch.vm.runtime.getSpriteTargetByName(args.INPUT);
+ if (!target) {
+ return;
+ }
+ const clones = target.sprite.clones;
+ for (let index = clones.length - 1; index > 0; index--) {
+ Scratch.vm.runtime.disposeTarget(clones[index]);
+ }
+ }
+
+ deleteCloneWithVar(args, util) {
+ const clones = util.target.sprite.clones;
+ const expectedValue = args.INPUTB;
+ for (let index = clones.length - 1; index > 0; index--) {
+ const cloneVar = clones[index].lookupVariableById(args.INPUTA);
+ if (
+ cloneVar &&
+ Scratch.Cast.compare(cloneVar.value, expectedValue) === 0
+ ) {
+ Scratch.vm.runtime.disposeTarget(clones[index]);
+ }
+ }
+ }
+
+ isClone(args, util) {
+ return !util.target.isOriginal;
+ }
+
+ cloneCount(args, util) {
+ return Scratch.vm.runtime._cloneCounter;
+ }
+
+ spriteCloneCount(args, util) {
+ const target = Scratch.vm.runtime.getSpriteTargetByName(args.INPUT);
+ if (target) {
+ return target.sprite.clones.length - 1;
+ }
+ return 0;
+ }
+
+ /**
+ * @param {string} variableId
+ * @param {unknown} expectedValue
+ * @param {VM.Target[]} clones
+ * @returns {VM.Target|null}
+ */
+ getCloneFromVariable(variableId, expectedValue, clones) {
+ for (let index = 1; index < clones.length; index++) {
+ const cloneVar = clones[index].lookupVariableById(variableId);
+ if (
+ cloneVar &&
+ Scratch.Cast.compare(cloneVar.value, expectedValue) === 0
+ ) {
+ return clones[index];
+ }
+ }
+ return null;
+ }
+
+ getSprites() {
+ let spriteNames = [];
+ const targets = Scratch.vm.runtime.targets;
+ const myself = Scratch.vm.runtime.getEditingTarget().sprite.name;
+ for (let index = 1; index < targets.length; index++) {
+ const curTarget = targets[index].sprite;
+ let display = curTarget.name;
+ if (myself === curTarget.name) {
+ display = "myself";
+ }
+ if (targets[index].isOriginal) {
+ const jsonOBJ = {
+ text: display,
+ value: curTarget.name,
+ };
+ spriteNames.push(jsonOBJ);
+ }
+ }
+ if (spriteNames.length > 0) {
+ return spriteNames;
+ } else {
+ return [{ text: "", value: 0 }]; //this should never happen but it's a failsafe
+ }
+ }
+
+ getSpriteObj(name) {
+ //This is unused but I'm leaving it in for potential future blocks
+ const spriteObj = Scratch.vm.runtime.getSpriteTargetByName(name);
+ return JSON.stringify(spriteObj);
+ }
+
+ getVariables() {
+ // @ts-expect-error - Blockly not typed yet
+ // eslint-disable-next-line no-undef
+ const variables =
+ typeof Blockly === "undefined"
+ ? []
+ : Blockly.getMainWorkspace()
+ .getVariableMap()
+ .getVariablesOfType("")
+ .filter((model) => model.isLocal)
+ .map((model) => ({
+ text: model.name,
+ value: model.getId(),
+ }));
+ if (variables.length > 0) {
+ return variables;
+ } else {
+ return [{ text: "", value: "" }];
+ }
+ }
+ }
+ Scratch.extensions.register(new ClonesPlus());
+})(Scratch);
diff --git a/extensions/Lily/CommentBlocks.js b/extensions/Lily/CommentBlocks.js
index f94fbf668a..bba99b6571 100644
--- a/extensions/Lily/CommentBlocks.js
+++ b/extensions/Lily/CommentBlocks.js
@@ -1,88 +1,104 @@
// Name: Comment Blocks
+// ID: lmscomments
// Description: Annotate your scripts.
// By: LilyMakesThings
(function (Scratch) {
- 'use strict';
+ "use strict";
class CommentBlocks {
getInfo() {
return {
- id: 'lmscomments',
- name: 'Comment Blocks',
- color1: '#e4db8c',
- color2: '#c6be79',
- color3: '#a8a167',
+ id: "lmscomments",
+ name: "Comment Blocks",
+ color1: "#e4db8c",
+ color2: "#c6be79",
+ color3: "#a8a167",
blocks: [
{
- opcode: 'commentHat',
+ opcode: "commentHat",
blockType: Scratch.BlockType.HAT,
- text: '// [COMMENT]',
+ text: "// [COMMENT]",
isEdgeActivated: false,
arguments: {
COMMENT: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'comment'
- }
- }
+ defaultValue: "comment",
+ },
+ },
},
{
- opcode: 'commentCommand',
+ opcode: "commentCommand",
blockType: Scratch.BlockType.COMMAND,
- text: '// [COMMENT]',
+ text: "// [COMMENT]",
arguments: {
COMMENT: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'comment'
- }
- }
+ defaultValue: "comment",
+ },
+ },
},
{
- opcode: 'commentReporter',
+ opcode: "commentC",
+ blockType: Scratch.BlockType.CONDITIONAL,
+ text: "// [COMMENT]",
+ arguments: {
+ COMMENT: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "comment",
+ },
+ },
+ },
+ {
+ opcode: "commentReporter",
blockType: Scratch.BlockType.REPORTER,
- text: '[INPUT] // [COMMENT]',
+ text: "[INPUT] // [COMMENT]",
arguments: {
COMMENT: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'comment'
+ defaultValue: "comment",
},
INPUT: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'commentBoolean',
+ opcode: "commentBoolean",
blockType: Scratch.BlockType.BOOLEAN,
- text: '[INPUT] // [COMMENT]',
+ text: "[INPUT] // [COMMENT]",
arguments: {
COMMENT: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'comment'
+ defaultValue: "comment",
},
INPUT: {
- type: Scratch.ArgumentType.BOOLEAN
- }
- }
- }
- ]
+ type: Scratch.ArgumentType.BOOLEAN,
+ },
+ },
+ },
+ ],
};
}
- commentHat () {
+ commentHat() {
// no-op
}
- commentCommand () {
+ commentCommand() {
// no-op
}
- commentReporter (args) {
+ commentC(args, util) {
+ return true;
+ }
+
+ commentReporter(args) {
return args.INPUT;
}
- commentBoolean (args) {
+ commentBoolean(args) {
return args.INPUT || false;
}
}
diff --git a/extensions/Lily/LooksPlus.js b/extensions/Lily/LooksPlus.js
index 65d3a94e7d..49e17834f9 100644
--- a/extensions/Lily/LooksPlus.js
+++ b/extensions/Lily/LooksPlus.js
@@ -1,15 +1,19 @@
// Name: Looks Plus
+// ID: lmsLooksPlus
// Description: Expands upon the looks category, allowing you to show/hide, get costume data and edit SVG skins on sprites.
// By: LilyMakesThings
(function (Scratch) {
- 'use strict';
+ "use strict";
- const menuIconURI = 'data:image/png;base64,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';
+ const menuIconURI =
+ "data:image/png;base64,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";
const requireNonPackagedRuntime = (blockName) => {
if (Scratch.vm.runtime.isPackaged) {
- alert(`To use the Looks+ ${blockName} block, the creator of the packaged project must uncheck "Remove raw asset data after loading to save RAM" under advanced settings in the packager.`);
+ alert(
+ `To use the Looks+ ${blockName} block, the creator of the packaged project must uncheck "Remove raw asset data after loading to save RAM" under advanced settings in the packager.`
+ );
return false;
}
return true;
@@ -31,169 +35,169 @@
class LooksPlus {
getInfo() {
return {
- id: 'lmsLooksPlus',
- name: 'Looks+',
- color1: '#9966FF',
- color2: '#855CD6',
- color3: '#774DCB',
+ id: "lmsLooksPlus",
+ name: "Looks+",
+ color1: "#9966FF",
+ color2: "#855CD6",
+ color3: "#774DCB",
menuIconURI: menuIconURI,
blocks: [
{
- opcode: 'showSprite',
+ opcode: "showSprite",
blockType: Scratch.BlockType.COMMAND,
- text: 'show [TARGET]',
+ text: "show [TARGET]",
arguments: {
TARGET: {
type: Scratch.ArgumentType.STRING,
- menu: 'spriteMenu'
- }
- }
+ menu: "spriteMenu",
+ },
+ },
},
{
- opcode: 'hideSprite',
+ opcode: "hideSprite",
blockType: Scratch.BlockType.COMMAND,
- text: 'hide [TARGET]',
+ text: "hide [TARGET]",
arguments: {
TARGET: {
type: Scratch.ArgumentType.STRING,
- menu: 'spriteMenu'
- }
- }
+ menu: "spriteMenu",
+ },
+ },
},
{
- opcode: 'spriteVisible',
+ opcode: "spriteVisible",
blockType: Scratch.BlockType.BOOLEAN,
- text: '[TARGET] visible?',
+ text: "[TARGET] visible?",
arguments: {
TARGET: {
type: Scratch.ArgumentType.STRING,
- menu: 'spriteMenu'
- }
- }
+ menu: "spriteMenu",
+ },
+ },
},
- '---',
+ "---",
{
- opcode: 'setLayerTo',
+ opcode: "setLayerTo",
blockType: Scratch.BlockType.COMMAND,
- text: 'set layer # of [TARGET] to [LAYER]',
+ text: "set layer # of [TARGET] to [LAYER]",
arguments: {
TARGET: {
type: Scratch.ArgumentType.STRING,
- menu: 'spriteMenu'
+ menu: "spriteMenu",
},
LAYER: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- }
- }
+ defaultValue: "1",
+ },
+ },
},
{
- opcode: 'spriteLayerNumber',
+ opcode: "spriteLayerNumber",
blockType: Scratch.BlockType.REPORTER,
- text: 'layer # of [TARGET]',
+ text: "layer # of [TARGET]",
arguments: {
TARGET: {
type: Scratch.ArgumentType.STRING,
- menu: 'spriteMenu'
- }
- }
+ menu: "spriteMenu",
+ },
+ },
},
{
- opcode: 'effectValue',
+ opcode: "effectValue",
blockType: Scratch.BlockType.REPORTER,
- text: '[EFFECT] effect of [TARGET]',
+ text: "[EFFECT] effect of [TARGET]",
arguments: {
EFFECT: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'color',
- menu: 'effectMenu'
+ defaultValue: "color",
+ menu: "effectMenu",
},
TARGET: {
type: Scratch.ArgumentType.STRING,
- menu: 'spriteMenu'
- }
- }
+ menu: "spriteMenu",
+ },
+ },
},
- '---',
+ "---",
{
- opcode: 'targetCostumeNumber',
+ opcode: "targetCostumeNumber",
blockType: Scratch.BlockType.REPORTER,
- text: '# of costumes in [TARGET]',
+ text: "# of costumes in [TARGET]",
arguments: {
TARGET: {
type: Scratch.ArgumentType.STRING,
- menu: 'spriteMenu'
- }
- }
+ menu: "spriteMenu",
+ },
+ },
},
{
- opcode: 'costumeAttribute',
+ opcode: "costumeAttribute",
blockType: Scratch.BlockType.REPORTER,
- text: '[ATTRIBUTE] of [COSTUME]',
+ text: "[ATTRIBUTE] of [COSTUME]",
arguments: {
ATTRIBUTE: {
type: Scratch.ArgumentType.STRING,
- menu: 'costumeAttribute'
+ menu: "costumeAttribute",
},
COSTUME: {
- type: Scratch.ArgumentType.COSTUME
- }
- }
+ type: Scratch.ArgumentType.COSTUME,
+ },
+ },
},
- '---',
+ "---",
{
- opcode: 'snapshotStage',
+ opcode: "snapshotStage",
blockType: Scratch.BlockType.REPORTER,
- text: 'snapshot stage',
- disableMonitor: true
+ text: "snapshot stage",
+ disableMonitor: true,
},
- '---',
+ "---",
{
- opcode: 'replaceCostumeContent',
+ opcode: "replaceCostumeContent",
blockType: Scratch.BlockType.COMMAND,
- text: 'set [TYPE] for [COSTUME] to [CONTENT]',
+ text: "set [TYPE] for [COSTUME] to [CONTENT]",
arguments: {
TYPE: {
type: Scratch.ArgumentType.STRING,
- menu: 'SVGPNG',
- defaultValue: 'SVG'
+ menu: "SVGPNG",
+ defaultValue: "SVG",
},
COSTUME: {
- type: Scratch.ArgumentType.COSTUME
+ type: Scratch.ArgumentType.COSTUME,
},
CONTENT: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'restoreCostumeContent',
+ opcode: "restoreCostumeContent",
blockType: Scratch.BlockType.COMMAND,
- text: 'restore content for [COSTUME]',
+ text: "restore content for [COSTUME]",
arguments: {
COSTUME: {
- type: Scratch.ArgumentType.COSTUME
- }
- }
+ type: Scratch.ArgumentType.COSTUME,
+ },
+ },
},
{
- opcode: 'costumeContent',
+ opcode: "costumeContent",
blockType: Scratch.BlockType.REPORTER,
- text: '[CONTENT] of costume # [COSTUME] of [TARGET]',
+ text: "[CONTENT] of costume # [COSTUME] of [TARGET]",
arguments: {
CONTENT: {
type: Scratch.ArgumentType.STRING,
- menu: 'contentType',
- defaultValue: 'content'
+ menu: "contentType",
+ defaultValue: "content",
},
COSTUME: {
type: Scratch.ArgumentType.NUMBER,
@@ -201,43 +205,43 @@
},
TARGET: {
type: Scratch.ArgumentType.STRING,
- menu: 'spriteMenu'
- }
- }
+ menu: "spriteMenu",
+ },
+ },
},
- '---',
+ "---",
{
- opcode: 'replaceColors',
+ opcode: "replaceColors",
blockType: Scratch.BlockType.REPORTER,
- text: 'replace [COLOR1] with [COLOR2] in [SVG]',
+ text: "replace [COLOR1] with [COLOR2] in [SVG]",
arguments: {
COLOR1: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#FCB1E3'
+ defaultValue: "#FCB1E3",
},
COLOR2: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#8ECAFF'
+ defaultValue: "#8ECAFF",
},
SVG: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'colorHex',
+ opcode: "colorHex",
blockType: Scratch.BlockType.REPORTER,
- text: 'hex of [COLOR]',
+ text: "hex of [COLOR]",
arguments: {
COLOR: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#FFD983'
- }
- }
- }
+ defaultValue: "#FFD983",
+ },
+ },
+ },
],
menus: {
effectMenu: {
@@ -245,87 +249,87 @@
acceptReporters: false,
items: [
{
- text: 'color',
- value: 'color'
+ text: "color",
+ value: "color",
},
{
- text: 'fisheye',
- value: 'fisheye'
+ text: "fisheye",
+ value: "fisheye",
},
{
- text: 'whirl',
- value: 'whirl'
+ text: "whirl",
+ value: "whirl",
},
{
- text: 'pixelate',
- value: 'pixelate'
+ text: "pixelate",
+ value: "pixelate",
},
{
- text: 'mosaic',
- value: 'mosaic'
+ text: "mosaic",
+ value: "mosaic",
},
{
- text: 'brightness',
- value: 'brightness'
+ text: "brightness",
+ value: "brightness",
},
{
- text: 'ghost',
- value: 'ghost'
- }
- ]
+ text: "ghost",
+ value: "ghost",
+ },
+ ],
},
costumeAttribute: {
acceptReporters: false,
items: [
{
- text: 'width',
- value: 'width'
+ text: "width",
+ value: "width",
},
{
- text: 'height',
- value: 'height'
+ text: "height",
+ value: "height",
},
{
- text: 'format',
- value: 'format'
+ text: "format",
+ value: "format",
},
{
- text: 'rotation center x',
- value: 'rotationCenterX'
+ text: "rotation center x",
+ value: "rotationCenterX",
},
{
- text: 'rotation center y',
- value: 'rotationCenterY'
- }
- ]
+ text: "rotation center y",
+ value: "rotationCenterY",
+ },
+ ],
},
contentType: {
acceptReporters: false,
items: [
{
- text: 'content',
- value: 'content'
+ text: "content",
+ value: "content",
},
{
- text: 'dataURI',
- value: 'dataURI'
- }
- ]
+ text: "dataURI",
+ value: "dataURI",
+ },
+ ],
},
SVGPNG: {
acceptReporters: false,
items: [
{
- text: 'SVG',
- value: 'SVG'
- }
- ]
+ text: "SVG",
+ value: "SVG",
+ },
+ ],
},
spriteMenu: {
acceptReporters: true,
- items: 'getSprites'
- }
- }
+ items: "getSprites",
+ },
+ },
};
}
@@ -358,8 +362,8 @@
}
const drawableID = target.drawableID;
const layerOrder = target.getLayerOrder();
- const newLayer = (args.LAYER - layerOrder);
- target.renderer.setDrawableOrder(drawableID, newLayer, 'sprite', true);
+ const newLayer = args.LAYER - layerOrder;
+ target.renderer.setDrawableOrder(drawableID, newLayer, "sprite", true);
}
spriteLayerNumber(args, util) {
@@ -388,26 +392,26 @@
const costumeIndex = this.getCostumeInput(args.COSTUME, util.target);
const costume = util.target.sprite.costumes[costumeIndex];
if (!costume) {
- console.error('Costume doesn\'t exist');
+ console.error("Costume doesn't exist");
return 0;
}
const attribute = args.ATTRIBUTE;
- if (attribute === 'width') {
+ if (attribute === "width") {
return Math.ceil(Scratch.Cast.toNumber(costume.size[0]));
- } else if (attribute === 'height') {
+ } else if (attribute === "height") {
return Math.ceil(Scratch.Cast.toNumber(costume.size[1]));
- } else if (attribute === 'format') {
- if (!requireNonPackagedRuntime('costume format')) {
- return 'unknown';
+ } else if (attribute === "format") {
+ if (!requireNonPackagedRuntime("costume format")) {
+ return "unknown";
}
return costume.asset.assetType.runtimeFormat;
- } else if (attribute === 'rotationCenterX') {
+ } else if (attribute === "rotationCenterX") {
return costume.rotationCenterX;
- } else if (attribute === 'rotationCenterY') {
+ } else if (attribute === "rotationCenterY") {
return costume.rotationCenterY;
} else {
- return '';
+ return "";
}
}
@@ -420,8 +424,8 @@
}
snapshotStage(args, util) {
- return new Promise(resolve => {
- Scratch.vm.runtime.renderer.requestSnapshot(uri => {
+ return new Promise((resolve) => {
+ Scratch.vm.runtime.renderer.requestSnapshot((uri) => {
resolve(uri);
});
});
@@ -431,24 +435,27 @@
const costumeIndex = this.getCostumeInput(args.COSTUME, util.target);
const costume = util.target.sprite.costumes[costumeIndex];
if (!costume) {
- console.error('Costume doesn\'t exist');
+ console.error("Costume doesn't exist");
return;
}
//This is here to ensure no changes are made to bitmap costumes, as changes are irreversible
//Check will be removed when it's possible to edit bitmap skins
const format = costume.asset.assetType.runtimeFormat;
- if (format !== 'svg') {
- console.error('Costume is not vector');
+ if (format !== "svg") {
+ console.error("Costume is not vector");
return;
}
const contentType = args.TYPE;
const content = args.CONTENT;
- if (contentType === 'SVG') {
- Scratch.vm.runtime.renderer.updateSVGSkin(costume.skinId, Scratch.Cast.toString(content));
+ if (contentType === "SVG") {
+ Scratch.vm.runtime.renderer.updateSVGSkin(
+ costume.skinId,
+ Scratch.Cast.toString(content)
+ );
} else {
- console.error('Options other than SVG are currently unavailable');
+ console.error("Options other than SVG are currently unavailable");
return;
}
Scratch.vm.emitTargetsUpdate();
@@ -458,47 +465,50 @@
const costumeIndex = this.getCostumeInput(args.COSTUME, util.target);
const costume = util.target.sprite.costumes[costumeIndex];
if (!costume) {
- console.error('Costume doesn\'t exist');
+ console.error("Costume doesn't exist");
return;
}
- if (!requireNonPackagedRuntime('restore costume content')) {
+ if (!requireNonPackagedRuntime("restore costume content")) {
return;
}
//This is here to ensure no changes are made to bitmap costumes, as changes are irreversible
//Check will be removed when it's possible to edit bitmap skins
const format = costume.asset.assetType.runtimeFormat;
- if (format !== 'svg') {
- console.error('Costume is not vector');
+ if (format !== "svg") {
+ console.error("Costume is not vector");
return;
}
const content = costume.asset.decodeText();
const rotationCenterX = costume.rotationCenterX;
const rotationCenterY = costume.rotationCenterY;
- util.target.renderer.updateSVGSkin(costume.skinId, content, [rotationCenterX, rotationCenterY]);
+ util.target.renderer.updateSVGSkin(costume.skinId, content, [
+ rotationCenterX,
+ rotationCenterY,
+ ]);
}
costumeContent(args, util) {
const target = getSpriteTargetByName(util, args.TARGET);
if (!target) {
- console.error('Target does not exist');
- return '';
+ console.error("Target does not exist");
+ return "";
}
- if (!requireNonPackagedRuntime('costume content')) {
- return '';
+ if (!requireNonPackagedRuntime("costume content")) {
+ return "";
}
- const costume = target.sprite.costumes[(args.COSTUME - 1)];
+ const costume = target.sprite.costumes[args.COSTUME - 1];
if (!costume) {
- console.error('Target costume does not exist');
- return '';
+ console.error("Target costume does not exist");
+ return "";
}
const format = args.CONTENT;
- if (format === 'content') {
+ if (format === "content") {
return costume.asset.decodeText();
} else {
return costume.asset.encodeDataURI();
@@ -510,7 +520,7 @@
const color1 = args.COLOR1;
const color2 = args.COLOR2;
try {
- return svg.replace(new RegExp(color1, 'gi'), color2);
+ return svg.replace(new RegExp(color1, "gi"), color2);
} catch (e) {
// regex was invalid, don't replace anything
return svg;
@@ -522,7 +532,7 @@
}
getCostumeInput(costume, target) {
- if (typeof costume === 'number') {
+ if (typeof costume === "number") {
costume = Math.round(costume - 1);
if (costume === Infinity || costume === -Infinity || !costume) {
costume = 0;
@@ -535,8 +545,8 @@
}
wrapClamp(n, min, max) {
- const range = (max - min) + 1;
- return n - (Math.floor((n - min) / range) * range);
+ const range = max - min + 1;
+ return n - Math.floor((n - min) / range) * range;
}
getSprites() {
@@ -549,13 +559,13 @@
const targetName = target.getName();
if (targetName === myself) {
spriteNames.unshift({
- text: 'this sprite',
- value: targetName
+ text: "this sprite",
+ value: targetName,
});
} else {
spriteNames.push({
text: targetName,
- value: targetName
+ value: targetName,
});
}
}
@@ -563,7 +573,7 @@
if (spriteNames.length > 0) {
return spriteNames;
} else {
- return [{text: "", value: 0}]; //this should never happen but it's a failsafe
+ return [{ text: "", value: 0 }]; //this should never happen but it's a failsafe
}
}
}
diff --git a/extensions/Lily/McUtils.js b/extensions/Lily/McUtils.js
index a51f7000b5..9e4d80758e 100644
--- a/extensions/Lily/McUtils.js
+++ b/extensions/Lily/McUtils.js
@@ -1,4 +1,5 @@
// Name: McUtils
+// ID: lmsmcutils
// Description: Helpful utilities for any fast food employee.
// By: LilyMakesThings
@@ -6,80 +7,80 @@
* Credit to NexusKitten (NamelessCat) for the idea
*/
-(function(Scratch) {
- 'use strict';
+(function (Scratch) {
+ "use strict";
class lmsmcutils {
getInfo() {
return {
- id: 'lmsmcutils',
- name: 'McUtils',
- color1: '#ec2020',
- color3: '#ffe427',
+ id: "lmsmcutils",
+ name: "McUtils",
+ color1: "#ec2020",
+ color3: "#ffe427",
blocks: [
{
- opcode: 'managerReporter',
+ opcode: "managerReporter",
blockType: Scratch.BlockType.REPORTER,
- text: 'if [INPUTA] is manager then [INPUTB] else [INPUTC]',
+ text: "if [INPUTA] is manager then [INPUTB] else [INPUTC]",
arguments: {
INPUTA: {
- type: Scratch.ArgumentType.BOOLEAN
+ type: Scratch.ArgumentType.BOOLEAN,
},
INPUTB: {
- type: Scratch.ArgumentType.STRING
+ type: Scratch.ArgumentType.STRING,
},
INPUTC: {
- type: Scratch.ArgumentType.STRING
- }
- }
+ type: Scratch.ArgumentType.STRING,
+ },
+ },
},
{
- opcode: 'icecreammachine',
+ opcode: "icecreammachine",
blockType: Scratch.BlockType.BOOLEAN,
- text: 'is ice cream machine [INPUT]',
+ text: "is ice cream machine [INPUT]",
arguments: {
INPUT: {
type: Scratch.ArgumentType.STRING,
- menu: 'iceCreamMenu'
- }
- }
+ menu: "iceCreamMenu",
+ },
+ },
},
{
- opcode: 'talkToManager',
+ opcode: "talkToManager",
blockType: Scratch.BlockType.BOOLEAN,
- text: 'talk to manager [INPUT]',
+ text: "talk to manager [INPUT]",
arguments: {
INPUT: {
type: Scratch.ArgumentType.STRING,
- }
- }
+ },
+ },
},
{
- opcode: 'placeOrder',
+ opcode: "placeOrder",
blockType: Scratch.BlockType.REPORTER,
- text: 'place order [INPUT]',
+ text: "place order [INPUT]",
arguments: {
INPUT: {
type: Scratch.ArgumentType.STRING,
- }
- }
- }
+ },
+ },
+ },
],
menus: {
iceCreamMenu: {
acceptReporters: true,
items: [
{
- text: 'working',
- value: 'working'
+ text: "working",
+ value: "working",
},
{
- text: 'broken',
- value: 'broken'
- }
- ]
- }
- }
+ text: "broken",
+ value: "broken",
+ },
+ ],
+ },
+ },
};
}
@@ -92,7 +93,7 @@
}
icecreammachine(args, util) {
- if (args.INPUT === 'working') {
+ if (args.INPUT === "working") {
return false;
} else {
return true;
@@ -104,7 +105,7 @@
}
placeOrder(args, util) {
- if (args.INPUT.includes('ice cream')) {
+ if (args.INPUT.includes("ice cream")) {
return false;
} else {
return args.INPUT;
diff --git a/extensions/Lily/MoreEvents.js b/extensions/Lily/MoreEvents.js
new file mode 100644
index 0000000000..cd1f329b65
--- /dev/null
+++ b/extensions/Lily/MoreEvents.js
@@ -0,0 +1,595 @@
+// Name: More Events
+// ID: lmsMoreEvents
+// Description: Start your scripts in new ways.
+// By: LilyMakesThings
+
+(function (Scratch) {
+ "use strict";
+
+ const vm = Scratch.vm;
+ const runtime = vm.runtime;
+
+ const stopIcon =
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAAQlBMVEUAAAC/UFC8Q0OzTU24SEi4SEi3SEi4R0e4SEi4SEi4SEi4SEi7SUm8SUnMTk7MT0/OT0/PT0/gVVXiVVXsWVn///+CoOd2AAAAC3RSTlMAEBMUu7zLz9D8/dIXnJwAAAABYktHRBXl2PmjAAAAxklEQVRIx+3WwRKDIBAD0JWqVEOtWv7/W3twOqKwELzW3N9wYhORMMYiztgZUZMUAKxqmh5Kno/MG256nzI59Z2mB+BWH+XzUt5RhWoyQjFZkTQFkTBFERlCnAwlDoYUgaHFblpaeL86AK0MvNjMIABmT2cGIAAWniw3ucm/k9ovduEjXzgXtUfJmtrTt9VZzYH9FSB/xvfKZMsiLFmuko61zBTfucjL9RpXf6nEU2MhPxXS86J+kORmjz6V6seViOnG8oT7ApMcjsYZwhXCAAAAAElFTkSuQmCC";
+
+ // Source:
+ // https://github.com/TurboWarp/scratch-vm/blob/develop/src/io/keyboard.js
+ // https://github.com/TurboWarp/scratch-blocks/blob/develop/blocks_vertical/event.js
+ const validKeyboardInputs = [
+ // Special Inputs
+ { text: "space", value: "space" },
+ { text: "up arrow", value: "up arrow" },
+ { text: "down arrow", value: "down arrow" },
+ { text: "right arrow", value: "right arrow" },
+ { text: "left arrow", value: "left arrow" },
+ { text: "enter", value: "enter" },
+ // TW: Extra Special Inputs
+ { text: "backspace", value: "backspace" },
+ { text: "delete", value: "delete" },
+ { text: "shift", value: "shift" },
+ { text: "caps lock", value: "caps lock" },
+ { text: "scroll lock", value: "scroll lock" },
+ { text: "control", value: "control" },
+ { text: "escape", value: "escape" },
+ { text: "insert", value: "insert" },
+ { text: "home", value: "home" },
+ { text: "end", value: "end" },
+ { text: "page up", value: "page up" },
+ { text: "page down", value: "page down" },
+ // Letter Keyboard Inputs
+ { text: "a", value: "a" },
+ { text: "b", value: "b" },
+ { text: "c", value: "c" },
+ { text: "d", value: "d" },
+ { text: "e", value: "e" },
+ { text: "f", value: "f" },
+ { text: "g", value: "g" },
+ { text: "h", value: "h" },
+ { text: "i", value: "i" },
+ { text: "j", value: "j" },
+ { text: "k", value: "k" },
+ { text: "l", value: "l" },
+ { text: "m", value: "m" },
+ { text: "n", value: "n" },
+ { text: "o", value: "o" },
+ { text: "p", value: "p" },
+ { text: "q", value: "q" },
+ { text: "r", value: "r" },
+ { text: "s", value: "s" },
+ { text: "t", value: "t" },
+ { text: "u", value: "u" },
+ { text: "v", value: "v" },
+ { text: "w", value: "w" },
+ { text: "x", value: "x" },
+ { text: "y", value: "y" },
+ { text: "z", value: "z" },
+ // Number Keyboard Inputs
+ { text: "0", value: "0" },
+ { text: "1", value: "1" },
+ { text: "2", value: "2" },
+ { text: "3", value: "3" },
+ { text: "4", value: "4" },
+ { text: "5", value: "5" },
+ { text: "6", value: "6" },
+ { text: "7", value: "7" },
+ { text: "8", value: "8" },
+ { text: "9", value: "9" },
+ ];
+
+ var lastValues = {};
+ var runTimer = 0;
+
+ class MoreEvents {
+ constructor() {
+ // Stop Sign Clicked contributed by @CST1229
+ runtime.shouldExecuteStopClicked = true;
+ runtime.on("BEFORE_EXECUTE", () => {
+ runTimer++;
+ runtime.shouldExecuteStopClicked = false;
+
+ runtime.startHats("lmsMoreEvents_forever");
+ runtime.startHats("lmsMoreEvents_whileTrueFalse");
+ runtime.startHats("lmsMoreEvents_whenValueChanged");
+ runtime.startHats("lmsMoreEvents_everyDuration");
+ runtime.startHats("lmsMoreEvents_whileKeyPressed");
+ });
+ runtime.on("PROJECT_START", () => {
+ runTimer = 0;
+ });
+ runtime.on("PROJECT_STOP_ALL", () => {
+ runTimer = 0;
+ if (runtime.shouldExecuteStopClicked)
+ queueMicrotask(() =>
+ runtime.startHats("lmsMoreEvents_whenStopClicked")
+ );
+ });
+ runtime.on("AFTER_EXECUTE", () => {
+ runtime.shouldExecuteStopClicked = true;
+ });
+ const originalGreenFlag = vm.greenFlag;
+ vm.greenFlag = function () {
+ runtime.shouldExecuteStopClicked = false;
+ originalGreenFlag.call(this);
+ };
+ }
+
+ getInfo() {
+ return {
+ id: "lmsMoreEvents",
+ name: "More Events",
+ color1: "#FFBF00",
+ color2: "#E6AC00",
+ color3: "#CC9900",
+ blocks: [
+ {
+ opcode: "whenStopClicked",
+ blockType: Scratch.BlockType.EVENT,
+ text: "when [STOP] clicked",
+ isEdgeActivated: false,
+ arguments: {
+ STOP: {
+ type: Scratch.ArgumentType.IMAGE,
+ dataURI: stopIcon,
+ },
+ },
+ },
+ {
+ opcode: "forever",
+ blockType: Scratch.BlockType.EVENT,
+ text: "forever",
+ isEdgeActivated: false,
+ },
+
+ "---",
+
+ {
+ opcode: "whenTrueFalse",
+ blockType: Scratch.BlockType.HAT,
+ text: "when [CONDITION] becomes [STATE]",
+ isEdgeActivated: true,
+ arguments: {
+ CONDITION: {
+ type: Scratch.ArgumentType.BOOLEAN,
+ },
+ STATE: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "boolean",
+ },
+ },
+ },
+ {
+ opcode: "whileTrueFalse",
+ blockType: Scratch.BlockType.HAT,
+ text: "while [CONDITION] is [STATE]",
+ isEdgeActivated: false,
+ arguments: {
+ CONDITION: {
+ type: Scratch.ArgumentType.BOOLEAN,
+ },
+ STATE: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "boolean",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "whenValueChanged",
+ blockType: Scratch.BlockType.HAT,
+ text: "when [INPUT] is changed",
+ isEdgeActivated: false,
+ arguments: {
+ INPUT: {
+ // Intentional:
+ // Encourages people to place a block
+ // (as opposed to typing a value)
+ type: null,
+ },
+ },
+ },
+ {
+ opcode: "everyDuration",
+ blockType: Scratch.BlockType.HAT,
+ text: "every [DURATION] frames",
+ isEdgeActivated: false,
+ arguments: {
+ DURATION: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: 3,
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "whenKeyAction",
+ blockType: Scratch.BlockType.HAT,
+ text: "when [KEY_OPTION] key [ACTION]",
+ isEdgeActivated: true,
+ arguments: {
+ KEY_OPTION: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "space",
+ menu: "keyboardButtons",
+ },
+ ACTION: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "action",
+ },
+ },
+ },
+ {
+ opcode: "whileKeyPressed",
+ blockType: Scratch.BlockType.HAT,
+ text: "while [KEY_OPTION] key pressed",
+ isEdgeActivated: false,
+ arguments: {
+ KEY_OPTION: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "space",
+ menu: "keyboardButtons",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "broadcastToTarget",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "broadcast [BROADCAST_OPTION] to [TARGET]",
+ arguments: {
+ BROADCAST_OPTION: {
+ type: null,
+ },
+ TARGET: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "targetMenu",
+ },
+ },
+ hideFromPalette: true,
+ },
+ {
+ opcode: "broadcastToTargetAndWait",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "broadcast [BROADCAST_OPTION] to [TARGET] and wait",
+ arguments: {
+ BROADCAST_OPTION: {
+ type: null,
+ },
+ TARGET: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "targetMenu",
+ },
+ },
+ hideFromPalette: true,
+ },
+
+ "---",
+
+ {
+ opcode: "broadcastData",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "broadcast [BROADCAST_OPTION] with data [DATA]",
+ arguments: {
+ BROADCAST_OPTION: {
+ type: null,
+ },
+ DATA: {
+ type: Scratch.ArgumentType.STRING,
+ },
+ },
+ hideFromPalette: true,
+ },
+ {
+ opcode: "broadcastDataAndWait",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "broadcast [BROADCAST_OPTION] with data [DATA] and wait",
+ arguments: {
+ BROADCAST_OPTION: {
+ type: null,
+ },
+ DATA: {
+ type: Scratch.ArgumentType.STRING,
+ },
+ },
+ hideFromPalette: true,
+ },
+ {
+ blockType: Scratch.BlockType.XML,
+ xml: ' ',
+ },
+ {
+ opcode: "receivedData",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "received data",
+ disableMonitor: true,
+ allowDropAnywhere: true,
+ },
+
+ "---",
+
+ {
+ opcode: "broadcastDataToTarget",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "broadcast [BROADCAST_OPTION] to [TARGET] with data [DATA]",
+ func: "broadcastToTarget",
+ arguments: {
+ BROADCAST_OPTION: {
+ type: null,
+ },
+ TARGET: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "targetMenu",
+ },
+ DATA: {
+ type: Scratch.ArgumentType.STRING,
+ },
+ },
+ hideFromPalette: true,
+ },
+ {
+ opcode: "broadcastDataToTargetAndWait",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "broadcast [BROADCAST_OPTION] to [TARGET] with data [DATA] and wait",
+ func: "broadcastToTargetAndWait",
+ arguments: {
+ BROADCAST_OPTION: {
+ type: null,
+ },
+ TARGET: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "targetMenu",
+ },
+ DATA: {
+ type: Scratch.ArgumentType.STRING,
+ },
+ },
+ hideFromPalette: true,
+ },
+ {
+ blockType: Scratch.BlockType.XML,
+ xml: ' ',
+ },
+ ],
+ menus: {
+ // Targets have acceptReporters: true
+ targetMenu: {
+ acceptReporters: true,
+ items: "_getTargets",
+ },
+ keyboardButtons: {
+ acceptReporters: true,
+ items: validKeyboardInputs,
+ },
+ // Attributes have acceptReporters: false
+ action: {
+ acceptReporters: false,
+ items: ["hit", "released"],
+ },
+ boolean: {
+ acceptReporters: false,
+ items: ["true", "false"],
+ },
+ state: {
+ acceptReporters: false,
+ items: ["enabled", "disabled"],
+ },
+ },
+ };
+ }
+
+ whenTrueFalse(args) {
+ return args.STATE === "true" ? args.CONDITION : !args.CONDITION;
+ }
+
+ whileTrueFalse(args) {
+ return args.STATE === "true" ? args.CONDITION : !args.CONDITION;
+ }
+
+ whenValueChanged(args, util) {
+ const blockId = util.thread.peekStack();
+ if (!lastValues[blockId])
+ lastValues[blockId] = Scratch.Cast.toString(args.INPUT);
+ if (lastValues[blockId] !== Scratch.Cast.toString(args.INPUT)) {
+ lastValues[blockId] = Scratch.Cast.toString(args.INPUT);
+ return true;
+ }
+ return false;
+ }
+
+ everyDuration(args, util) {
+ const duration = Math.max(
+ Math.round(Scratch.Cast.toNumber(args.DURATION)),
+ 0
+ );
+ return !!(runTimer % duration === 0);
+ }
+
+ whenKeyAction(args, util) {
+ const key = Scratch.Cast.toString(args.KEY_OPTION).toLowerCase();
+ const pressed = util.ioQuery("keyboard", "getKeyIsDown", [key]);
+ return args.ACTION === "released" ? !pressed : pressed;
+ }
+
+ whileKeyPressed(args, util) {
+ const key = Scratch.Cast.toString(args.KEY_OPTION).toLowerCase();
+ return util.ioQuery("keyboard", "getKeyIsDown", [key]);
+ }
+
+ broadcastToTarget(args, util) {
+ const broadcastOption = Scratch.Cast.toString(args.BROADCAST_OPTION);
+ if (!broadcastOption) return;
+
+ const data = Scratch.Cast.toString(args.DATA);
+ console.log(data);
+
+ const cloneTargets = this._getTargetFromMenu(args.TARGET).sprite.clones;
+ let startedThreads = [];
+
+ for (const clone of cloneTargets) {
+ startedThreads = [
+ ...startedThreads,
+ ...util.startHats(
+ "event_whenbroadcastreceived",
+ {
+ BROADCAST_OPTION: broadcastOption,
+ },
+ clone
+ ),
+ ];
+ if (data) {
+ startedThreads.forEach((thread) => (thread.receivedData = args.DATA));
+ }
+ }
+ }
+
+ broadcastToTargetAndWait(args, util) {
+ if (!util.stackFrame.broadcastVar) {
+ util.stackFrame.broadcastVar = Scratch.Cast.toString(
+ args.BROADCAST_OPTION
+ );
+ }
+
+ const spriteTarget = this._getTargetFromMenu(args.TARGET);
+ if (!spriteTarget) return;
+ const cloneTargets = spriteTarget.sprite.clones;
+
+ const data = Scratch.Cast.toString(args.DATA);
+
+ if (util.stackFrame.broadcastVar) {
+ const broadcastOption = util.stackFrame.broadcastVar;
+ if (!util.stackFrame.startedThreads) {
+ util.stackFrame.startedThreads = [];
+ for (const clone of cloneTargets) {
+ util.stackFrame.startedThreads = [
+ ...util.stackFrame.startedThreads,
+ ...util.startHats(
+ "event_whenbroadcastreceived",
+ {
+ BROADCAST_OPTION: broadcastOption,
+ },
+ clone
+ ),
+ ];
+ if (data) {
+ util.stackFrame.startedThreads.forEach(
+ (thread) => (thread.receivedData = args.DATA)
+ );
+ }
+ }
+ if (util.stackFrame.startedThreads.length === 0) {
+ return;
+ }
+ }
+
+ const waiting = util.stackFrame.startedThreads.some(
+ (thread) => runtime.threads.indexOf(thread) !== -1
+ );
+ if (waiting) {
+ if (
+ util.stackFrame.startedThreads.every((thread) =>
+ runtime.isWaitingThread(thread)
+ )
+ ) {
+ util.yieldTick();
+ } else {
+ util.yield();
+ }
+ }
+ }
+ }
+
+ broadcastData(args, util) {
+ const broadcast = Scratch.Cast.toString(args.BROADCAST_OPTION);
+ if (!broadcast) return;
+
+ const data = Scratch.Cast.toString(args.DATA);
+
+ let threads = util.startHats("event_whenbroadcastreceived", {
+ BROADCAST_OPTION: broadcast,
+ });
+ threads.forEach((thread) => (thread.receivedData = data));
+ }
+
+ broadcastDataAndWait(args, util) {
+ const data = Scratch.Cast.toString(args.DATA);
+
+ if (!util.stackFrame.broadcastVar) {
+ util.stackFrame.broadcastVar = Scratch.Cast.toString(
+ args.BROADCAST_OPTION
+ );
+ }
+
+ if (util.stackFrame.broadcastVar) {
+ const broadcastOption = util.stackFrame.broadcastVar;
+ if (!util.stackFrame.startedThreads) {
+ util.stackFrame.startedThreads = util.startHats(
+ "event_whenbroadcastreceived",
+ {
+ BROADCAST_OPTION: broadcastOption,
+ }
+ );
+ if (util.stackFrame.startedThreads.length === 0) {
+ return;
+ } else {
+ util.stackFrame.startedThreads.forEach(
+ (thread) => (thread.receivedData = data)
+ );
+ }
+ }
+
+ const waiting = util.stackFrame.startedThreads.some(
+ (thread) => runtime.threads.indexOf(thread) !== -1
+ );
+ if (waiting) {
+ if (
+ util.stackFrame.startedThreads.every((thread) =>
+ runtime.isWaitingThread(thread)
+ )
+ ) {
+ util.yieldTick();
+ } else {
+ util.yield();
+ }
+ }
+ }
+ }
+
+ receivedData(args, util) {
+ const received = util.thread.receivedData;
+ return received ? received : "";
+ }
+
+ _getTargetFromMenu(targetName) {
+ let target = Scratch.vm.runtime.getSpriteTargetByName(targetName);
+ if (targetName === "_stage_") target = runtime.getTargetForStage();
+ return target;
+ }
+
+ _getTargets() {
+ const spriteNames = [{ text: "Stage", value: "_stage_" }];
+ const targets = Scratch.vm.runtime.targets;
+ for (let index = 1; index < targets.length; index++) {
+ const target = targets[index];
+ if (target.isOriginal) {
+ const targetName = target.getName();
+ spriteNames.push({
+ text: targetName,
+ value: targetName,
+ });
+ }
+ }
+ if (spriteNames.length > 0) {
+ return spriteNames;
+ } else {
+ return [{ text: "", value: 0 }]; //this should never happen but it's a failsafe
+ }
+ }
+ }
+
+ Scratch.extensions.register(new MoreEvents());
+})(Scratch);
diff --git a/extensions/Lily/MoreTimers.js b/extensions/Lily/MoreTimers.js
index fad8572c82..c6417906b9 100644
--- a/extensions/Lily/MoreTimers.js
+++ b/extensions/Lily/MoreTimers.js
@@ -1,283 +1,287 @@
-// Name: More Timers
-// Description: Control several timers at once.
-// By: LilyMakesThings
-
-(function (Scratch) {
- 'use strict';
-
- const vm = Scratch.vm;
-
- /**
- * @typedef Timer
- * @property {number} startTime
- * @property {number} pauseTime
- * @property {boolean} paused
- */
-
- /** @type {Record} */
- let timers = Object.create(null);
-
- /**
- * @param {Timer} timer
- * @return {number}
- */
- const timerValue = timer => {
- return ((timer.paused ? 0 : (Date.now() - timer.startTime)) + timer.pauseTime) / 1000;
- };
-
- class Timers {
- constructor() {
- vm.runtime.on('PROJECT_START', () => {
- timers = Object.create(null);
- });
- }
-
- getInfo() {
- return {
- id: 'lmsTimers',
- name: 'More Timers',
- color1: '#5cb1d6',
- color2: '#428faf',
- color3: '#3281a3',
- blocks: [
- {
- opcode: 'whenTimerOp',
- blockType: Scratch.BlockType.HAT,
- extensions: ['colours_sensing'],
- text: 'when timer [TIMER] [OP] [NUM]',
- arguments: {
- TIMER: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'timer'
- },
- OP: {
- type: Scratch.ArgumentType.STRING,
- menu: 'operation'
- },
- NUM: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '5'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'startResetTimer',
- blockType: Scratch.BlockType.COMMAND,
- extensions: ['colours_sensing'],
- text: 'start/reset timer [TIMER]',
- arguments: {
- TIMER: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'timer'
- }
- }
- },
- {
- opcode: 'valueOfTimer',
- blockType: Scratch.BlockType.REPORTER,
- extensions: ['colours_sensing'],
- text: 'timer [TIMER]',
- arguments: {
- TIMER: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'timer'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'pauseTimer',
- blockType: Scratch.BlockType.COMMAND,
- extensions: ['colours_sensing'],
- text: 'pause timer [TIMER]',
- arguments: {
- TIMER: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'timer'
- }
- }
- },
- {
- opcode: 'resumeTimer',
- blockType: Scratch.BlockType.COMMAND,
- extensions: ['colours_sensing'],
- text: 'resume timer [TIMER]',
- arguments: {
- TIMER: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'timer'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'setTimer',
- blockType: Scratch.BlockType.COMMAND,
- extensions: ['colours_sensing'],
- text: 'set timer [TIMER] to [NUM]',
- arguments: {
- TIMER: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'timer'
- },
- NUM: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- }
- }
- },
- {
- opcode: 'changeTimer',
- blockType: Scratch.BlockType.COMMAND,
- extensions: ['colours_sensing'],
- text: 'change timer [TIMER] by [NUM]',
- arguments: {
- TIMER: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'timer'
- },
- NUM: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'removeTimer',
- blockType: Scratch.BlockType.COMMAND,
- extensions: ['colours_sensing'],
- text: 'remove timer [TIMER]',
- arguments: {
- TIMER: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'timer'
- },
- }
- },
- {
- opcode: 'removeTimers',
- blockType: Scratch.BlockType.COMMAND,
- extensions: ['colours_sensing'],
- text: 'remove all timers'
- },
- {
- opcode: 'timerExists',
- blockType: Scratch.BlockType.BOOLEAN,
- extensions: ['colours_sensing'],
- text: 'timer [TIMER] exists?',
- arguments: {
- TIMER: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'timer'
- },
- }
- },
- {
- opcode: 'listExistingTimers',
- blockType: Scratch.BlockType.REPORTER,
- extensions: ['colours_sensing'],
- text: 'list existing timers',
- disableMonitor: false
- }
- ],
- menus: {
- operation: {
- // false for Scratch parity
- acceptReporters: false,
- items: ['>','<']
- }
- }
- };
- }
-
- whenTimerOp(args) {
- if (!timers[args.TIMER]) return false;
- const value = timerValue(timers[args.TIMER]);
- if (args.OP === '>') return value > args.NUM;
- if (args.OP === '<') return value < args.NUM;
- return false;
- }
-
- startResetTimer(args) {
- timers[args.TIMER] = {
- startTime: Date.now(),
- pauseTime: 0,
- paused: false
- };
- }
-
- pauseTimer(args) {
- const timer = timers[args.TIMER];
- if (!timer) return;
- timer.pauseTime = timerValue(timer) * 1000;
- timer.paused = true;
- }
-
- resumeTimer(args) {
- const timer = timers[args.TIMER];
- if (!timer) return;
- if (timer.paused === false) return;
- timer.paused = false;
- timer.startTime = Date.now();
- }
-
- valueOfTimer(args) {
- if (!timers[args.TIMER]) return '';
- return timerValue(timers[args.TIMER]);
- }
-
- setTimer(args) {
- timers[args.TIMER] = {
- paused: false,
- startTime: Date.now(),
- pauseTime: Scratch.Cast.toNumber(args.NUM) * 1000
- };
- }
-
- changeTimer(args) {
- if (!timers[args.TIMER]) this.startResetTimer(args);
- timers[args.TIMER].pauseTime += Scratch.Cast.toNumber(args.NUM) * 1000;
- }
-
- removeTimers(args) {
- timers = Object.create(null);
- }
-
- removeTimer(args) {
- Reflect.deleteProperty(timers, args.TIMER);
- }
-
- timerExists(args) {
- return !!timers[args.TIMER];
- }
-
- listExistingTimers(args) {
- return Object.keys(timers).join(',');
- }
- }
-
- // "Extension" option reimplementation by Xeltalliv
- // https://github.com/Xeltalliv/extensions/blob/examples/examples/extension-colors.js
-
- // const cbfsb = Scratch.vm.runtime._convertBlockForScratchBlocks.bind(Scratch.vm.runtime);
- // Scratch.vm.runtime._convertBlockForScratchBlocks = function(blockInfo, categoryInfo) {
- // const res = cbfsb(blockInfo, categoryInfo);
- // if (blockInfo.extensions) {
- // if (!res.json.extensions) res.json.extensions = [];
- // res.json.extensions.push(...blockInfo.extensions);
- // }
- // return res;
- // };
-
- Scratch.extensions.register(new Timers());
-})(Scratch);
+// Name: More Timers
+// ID: lmsTimers
+// Description: Control several timers at once.
+// By: LilyMakesThings
+
+(function (Scratch) {
+ "use strict";
+
+ const vm = Scratch.vm;
+
+ /**
+ * @typedef Timer
+ * @property {number} startTime
+ * @property {number} pauseTime
+ * @property {boolean} paused
+ */
+
+ /** @type {Record} */
+ let timers = Object.create(null);
+
+ /**
+ * @param {Timer} timer
+ * @return {number}
+ */
+ const timerValue = (timer) => {
+ return (
+ ((timer.paused ? 0 : Date.now() - timer.startTime) + timer.pauseTime) /
+ 1000
+ );
+ };
+
+ class Timers {
+ constructor() {
+ vm.runtime.on("PROJECT_START", () => {
+ timers = Object.create(null);
+ });
+ }
+
+ getInfo() {
+ return {
+ id: "lmsTimers",
+ name: "More Timers",
+ color1: "#5cb1d6",
+ color2: "#428faf",
+ color3: "#3281a3",
+ blocks: [
+ {
+ opcode: "whenTimerOp",
+ blockType: Scratch.BlockType.HAT,
+ extensions: ["colours_sensing"],
+ text: "when timer [TIMER] [OP] [NUM]",
+ arguments: {
+ TIMER: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "timer",
+ },
+ OP: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "operation",
+ },
+ NUM: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "5",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "startResetTimer",
+ blockType: Scratch.BlockType.COMMAND,
+ extensions: ["colours_sensing"],
+ text: "start/reset timer [TIMER]",
+ arguments: {
+ TIMER: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "timer",
+ },
+ },
+ },
+ {
+ opcode: "valueOfTimer",
+ blockType: Scratch.BlockType.REPORTER,
+ extensions: ["colours_sensing"],
+ text: "timer [TIMER]",
+ arguments: {
+ TIMER: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "timer",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "pauseTimer",
+ blockType: Scratch.BlockType.COMMAND,
+ extensions: ["colours_sensing"],
+ text: "pause timer [TIMER]",
+ arguments: {
+ TIMER: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "timer",
+ },
+ },
+ },
+ {
+ opcode: "resumeTimer",
+ blockType: Scratch.BlockType.COMMAND,
+ extensions: ["colours_sensing"],
+ text: "resume timer [TIMER]",
+ arguments: {
+ TIMER: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "timer",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "setTimer",
+ blockType: Scratch.BlockType.COMMAND,
+ extensions: ["colours_sensing"],
+ text: "set timer [TIMER] to [NUM]",
+ arguments: {
+ TIMER: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "timer",
+ },
+ NUM: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ },
+ },
+ {
+ opcode: "changeTimer",
+ blockType: Scratch.BlockType.COMMAND,
+ extensions: ["colours_sensing"],
+ text: "change timer [TIMER] by [NUM]",
+ arguments: {
+ TIMER: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "timer",
+ },
+ NUM: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "removeTimer",
+ blockType: Scratch.BlockType.COMMAND,
+ extensions: ["colours_sensing"],
+ text: "remove timer [TIMER]",
+ arguments: {
+ TIMER: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "timer",
+ },
+ },
+ },
+ {
+ opcode: "removeTimers",
+ blockType: Scratch.BlockType.COMMAND,
+ extensions: ["colours_sensing"],
+ text: "remove all timers",
+ },
+ {
+ opcode: "timerExists",
+ blockType: Scratch.BlockType.BOOLEAN,
+ extensions: ["colours_sensing"],
+ text: "timer [TIMER] exists?",
+ arguments: {
+ TIMER: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "timer",
+ },
+ },
+ },
+ {
+ opcode: "listExistingTimers",
+ blockType: Scratch.BlockType.REPORTER,
+ extensions: ["colours_sensing"],
+ text: "list existing timers",
+ disableMonitor: false,
+ },
+ ],
+ menus: {
+ operation: {
+ // false for Scratch parity
+ acceptReporters: false,
+ items: [">", "<"],
+ },
+ },
+ };
+ }
+
+ whenTimerOp(args) {
+ if (!timers[args.TIMER]) return false;
+ const value = timerValue(timers[args.TIMER]);
+ if (args.OP === ">") return value > args.NUM;
+ if (args.OP === "<") return value < args.NUM;
+ return false;
+ }
+
+ startResetTimer(args) {
+ timers[args.TIMER] = {
+ startTime: Date.now(),
+ pauseTime: 0,
+ paused: false,
+ };
+ }
+
+ pauseTimer(args) {
+ const timer = timers[args.TIMER];
+ if (!timer) return;
+ timer.pauseTime = timerValue(timer) * 1000;
+ timer.paused = true;
+ }
+
+ resumeTimer(args) {
+ const timer = timers[args.TIMER];
+ if (!timer) return;
+ if (timer.paused === false) return;
+ timer.paused = false;
+ timer.startTime = Date.now();
+ }
+
+ valueOfTimer(args) {
+ if (!timers[args.TIMER]) return "";
+ return timerValue(timers[args.TIMER]);
+ }
+
+ setTimer(args) {
+ timers[args.TIMER] = {
+ paused: false,
+ startTime: Date.now(),
+ pauseTime: Scratch.Cast.toNumber(args.NUM) * 1000,
+ };
+ }
+
+ changeTimer(args) {
+ if (!timers[args.TIMER]) this.startResetTimer(args);
+ timers[args.TIMER].pauseTime += Scratch.Cast.toNumber(args.NUM) * 1000;
+ }
+
+ removeTimers(args) {
+ timers = Object.create(null);
+ }
+
+ removeTimer(args) {
+ Reflect.deleteProperty(timers, args.TIMER);
+ }
+
+ timerExists(args) {
+ return !!timers[args.TIMER];
+ }
+
+ listExistingTimers(args) {
+ return Object.keys(timers).join(",");
+ }
+ }
+
+ // "Extension" option reimplementation by Xeltalliv
+ // https://github.com/Xeltalliv/extensions/blob/examples/examples/extension-colors.js
+
+ // const cbfsb = Scratch.vm.runtime._convertBlockForScratchBlocks.bind(Scratch.vm.runtime);
+ // Scratch.vm.runtime._convertBlockForScratchBlocks = function(blockInfo, categoryInfo) {
+ // const res = cbfsb(blockInfo, categoryInfo);
+ // if (blockInfo.extensions) {
+ // if (!res.json.extensions) res.json.extensions = [];
+ // res.json.extensions.push(...blockInfo.extensions);
+ // }
+ // return res;
+ // };
+
+ Scratch.extensions.register(new Timers());
+})(Scratch);
diff --git a/extensions/Lily/Skins.js b/extensions/Lily/Skins.js
index 7449ed2995..2c28f6cc94 100644
--- a/extensions/Lily/Skins.js
+++ b/extensions/Lily/Skins.js
@@ -1,444 +1,459 @@
-// Name: Skins
-// Description: Have your sprites render as other images or costumes.
-// By: LilyMakesThings
-
-(function (Scratch) {
- 'use strict';
-
- const requireNonPackagedRuntime = (blockName) => {
- if (Scratch.vm.runtime.isPackaged) {
- alert(`To use the Skins ${blockName} block, the creator of the packaged project must uncheck "Remove raw asset data after loading to save RAM" under advanced settings in the packager.`);
- return false;
- }
- return true;
- };
-
- /**
- * @param {RenderWebGL.SVGSkin} svgSkin
- * @returns {Promise}
- */
- const svgSkinFinishedLoading = svgSkin => new Promise(resolve => {
- if (svgSkin._svgImageLoaded) {
- resolve();
- } else {
- svgSkin._svgImage.addEventListener('load', () => {
- resolve();
- });
- svgSkin._svgImage.addEventListener('error', () => {
- resolve();
- });
- }
- });
-
- const vm = Scratch.vm;
- const runtime = vm.runtime;
- const renderer = runtime.renderer;
- const Cast = Scratch.Cast;
-
- var createdSkins = [];
-
- class Skins {
- constructor() {
- runtime.on('PROJECT_START', () => {
- this._refreshTargets();
- });
-
- runtime.on('PROJECT_STOP_ALL', () => {
- this._refreshTargets();
- });
- }
-
- getInfo() {
- return {
- id: 'lmsSkins',
- name: 'Skins',
- color1: '#6b56ff',
- blocks: [
- {
- opcode: 'registerSVGSkin',
- blockType: Scratch.BlockType.COMMAND,
- text: 'create SVG skin [SVG] as [NAME]',
- arguments: {
- SVG: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- },
- NAME: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'my skin'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'registerCostumeSkin',
- blockType: Scratch.BlockType.COMMAND,
- text: 'load skin from [COSTUME] as [NAME]',
- arguments: {
- COSTUME: {
- type: Scratch.ArgumentType.COSTUME
- },
- NAME: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'my skin'
- }
- }
- },
- {
- opcode: 'registerURLSkin',
- blockType: Scratch.BlockType.COMMAND,
- text: 'load skin from URL [URL] as [NAME]',
- arguments: {
- URL: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'https://extensions.turbowarp.org/dango.png'
- },
- NAME: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'my skin'
- }
- }
- },
- {
- opcode: 'getSkinLoaded',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'skin [NAME] is loaded?',
- arguments: {
- NAME: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'my skin'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'setSkin',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set skin of [TARGET] to [NAME]',
- arguments: {
- TARGET: {
- type: Scratch.ArgumentType.STRING,
- menu: 'targetMenu'
- },
- NAME: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'my skin'
- }
- }
- },
- {
- opcode: 'restoreSkin',
- blockType: Scratch.BlockType.COMMAND,
- text: 'restore skin of [TARGET]',
- arguments: {
- TARGET: {
- type: Scratch.ArgumentType.STRING,
- menu: 'targetMenu'
- }
- }
- },
- {
- opcode: 'restoreTargets',
- blockType: Scratch.BlockType.COMMAND,
- text: 'restore targets with skin [NAME]',
- arguments: {
- NAME: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'my skin'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'getCurrentSkin',
- blockType: Scratch.BlockType.REPORTER,
- text: 'current skin of [TARGET]',
- arguments: {
- TARGET: {
- type: Scratch.ArgumentType.STRING,
- menu: 'targetMenu'
- }
- }
- },
- {
- opcode: 'getSkinAttribute',
- blockType: Scratch.BlockType.REPORTER,
- text: '[ATTRIBUTE] of skin [NAME]',
- arguments: {
- ATTRIBUTE: {
- type: Scratch.ArgumentType.STRING,
- menu: 'skinAttributes'
- },
- NAME: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'my skin'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'deleteSkin',
- blockType: Scratch.BlockType.COMMAND,
- text: 'delete skin [NAME]',
- arguments: {
- NAME: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'my skin'
- }
- }
- },
- {
- opcode: 'deleteAllSkins',
- blockType: Scratch.BlockType.COMMAND,
- text: 'delete all skins'
- }
- ],
- menus: {
- targetMenu: {
- acceptReporters: true,
- items: '_getTargets'
- },
- skinAttributes: {
- acceptReporters: true,
- items: ['width', 'height']
- }
- }
- };
- }
-
- async registerSVGSkin (args) {
- const skinName = Cast.toString(args.NAME);
- const svgData = Cast.toString(args.SVG);
-
- let oldSkinId = null;
- if (createdSkins[skinName]) {
- oldSkinId = createdSkins[skinName];
- }
-
- // This generally takes a few frames, so yield the block
- const skinId = renderer.createSVGSkin(svgData);
- createdSkins[skinName] = skinId;
-
- await svgSkinFinishedLoading(renderer._allSkins[skinId]);
-
- if (oldSkinId) {
- this._refreshTargetsFromID(oldSkinId, false, skinId);
- renderer.destroySkin(oldSkinId);
- }
- }
-
- async registerCostumeSkin (args, util) {
- if (!requireNonPackagedRuntime('add costume skin')) {
- return;
- }
-
- const skinName = Cast.toString(args.NAME);
- const costumeIndex = util.target.getCostumeIndexByName(args.COSTUME);
- if (costumeIndex === -1) return;
- const costume = util.target.sprite.costumes[costumeIndex];
-
- const url = costume.asset.encodeDataURI();
- const rotationCenterX = costume.rotationCenterX;
- const rotationCenterY = costume.rotationCenterY;
-
- let rotationCenter = [rotationCenterX, rotationCenterY];
- if (!rotationCenterX || !rotationCenterY) rotationCenter = null;
-
- let oldSkinId = null;
- if (createdSkins[skinName]) {
- oldSkinId = createdSkins[skinName];
- }
-
- const skinId = await this._createURLSkin(url, rotationCenter);
- createdSkins[skinName] = skinId;
-
- if (oldSkinId) {
- this._refreshTargetsFromID(oldSkinId, false, skinId);
- renderer.destroySkin(oldSkinId);
- }
- }
-
- async registerURLSkin (args) {
- const skinName = Cast.toString(args.NAME);
- const url = Cast.toString(args.URL);
-
- let oldSkinId = null;
- if (createdSkins[skinName]) {
- oldSkinId = createdSkins[skinName];
- }
-
- const skinId = await this._createURLSkin(url);
- if (!skinId) return;
- createdSkins[skinName] = skinId;
-
- if (oldSkinId) {
- this._refreshTargetsFromID(oldSkinId, false, skinId);
- renderer.destroySkin(oldSkinId);
- }
- }
-
- getSkinLoaded (args) {
- const skinName = Cast.toString(args.NAME);
- return !!(createdSkins[skinName]);
- }
-
- setSkin (args, util) {
- const skinName = Cast.toString(args.NAME);
- if (!createdSkins[skinName]) return;
-
- const targetName = Cast.toString(args.TARGET);
- const target = this._getTargetFromMenu(targetName, util);
- if (!target) return;
- const drawableID = target.drawableID;
-
- const skinId = createdSkins[skinName];
- renderer._allDrawables[drawableID].skin = renderer._allSkins[skinId];
- }
-
- restoreSkin (args, util) {
- const targetName = Cast.toString(args.TARGET);
- const target = this._getTargetFromMenu(targetName, util);
- if (!target) return;
- target.updateAllDrawableProperties();
- }
-
- getCurrentSkin (args, util) {
- const targetName = Cast.toString(args.TARGET);
- const target = this._getTargetFromMenu(targetName, util);
- if (!target) return;
- const drawableID = target.drawableID;
-
- const skinId = renderer._allDrawables[drawableID].skin._id;
- const skinName = this._getSkinNameFromID(skinId);
- return (skinName) ? skinName : '';
- }
-
- getSkinAttribute (args) {
- const skins = renderer._allSkins;
- const skinName = Cast.toString(args.NAME);
-
- if (!createdSkins[skinName]) return 0;
- const skinId = createdSkins[skinName];
- if (!skins[skinId]) return 0;
-
- const size = skins[skinId].size;
- const attribute = Cast.toString(args.ATTRIBUTE).toLowerCase();
-
- switch (attribute) {
- case ('width'): return Math.ceil(size[0]);
- case ('height'): return Math.ceil(size[1]);
- default: return 0;
- }
- }
-
- deleteSkin (args) {
- const skinName = Cast.toString(args.NAME);
- if (!createdSkins[skinName]) return;
- const skinId = createdSkins[skinName];
-
- this._refreshTargetsFromID(skinId, true);
- renderer.destroySkin(skinId);
- Reflect.deleteProperty(createdSkins, skinName);
- }
-
- deleteAllSkins () {
- this._refreshTargets();
- for (let i = 0; i < createdSkins.length; i++) renderer.destroySkin(createdSkins[i]);
- createdSkins = [];
- }
-
- restoreTargets (args) {
- const skinName = Cast.toString(args.NAME);
- if (!createdSkins[skinName]) return;
- const skinId = createdSkins[skinName];
-
- this._refreshTargetsFromID(skinId, true);
- }
-
- // Utility Functions
-
- _refreshTargetsFromID (skinId, reset, newId) {
- const drawables = renderer._allDrawables;
- const skins = renderer._allSkins;
-
- for (const target of runtime.targets) {
- const drawableID = target.drawableID;
- const targetSkin = drawables[drawableID].skin.id;
-
- if (targetSkin === skinId) {
- target.updateAllDrawableProperties();
- if (!reset) drawables[drawableID].skin = (newId) ? skins[newId] : skins[skinId];
- }
- }
- }
-
- _refreshTargets () {
- for (const target of runtime.targets) {
- target.updateAllDrawableProperties();
- }
- }
-
- _getSkinNameFromID (skinId) {
- for (const skinName in createdSkins) {
- if (createdSkins[skinName] === skinId) return skinName;
- }
- }
-
- _getTargetFromMenu (targetName, util) {
- let target = Scratch.vm.runtime.getSpriteTargetByName(targetName);
- if (targetName === '_myself_') target = util.target;
- if (targetName === '_stage_') target = runtime.getTargetForStage();
- return target;
- }
-
- async _createURLSkin (URL, rotationCenter) {
- let imageData;
- if (await Scratch.canFetch(URL)) {
- imageData = await Scratch.fetch(URL);
- } else {
- return;
- }
-
- const contentType = imageData.headers.get("Content-Type");
- if (contentType === 'image/svg+xml') {
- return renderer.createSVGSkin(await imageData.text(), rotationCenter);
- } else if (contentType === 'image/png' || contentType === 'image/jpeg' || contentType === 'image/bmp') {
- // eslint-disable-next-line no-restricted-syntax
- const output = new Image();
- output.src = URL;
- output.crossOrigin = 'anonymous';
- await output.decode();
- return renderer.createBitmapSkin(output);
- }
- }
-
- _getTargets() {
- const spriteNames = [
- {text: 'myself', value: '_myself_'},
- {text: 'Stage', value: '_stage_'}
- ];
- const targets = Scratch.vm.runtime.targets;
- for (let index = 1; index < targets.length; index++) {
- const target = targets[index];
- if (target.isOriginal) {
- const targetName = target.getName();
- spriteNames.push({
- text: targetName,
- value: targetName
- });
- }
- }
- return spriteNames;
- }
-
- }
- Scratch.extensions.register(new Skins());
-})(Scratch);
+// Name: Skins
+// ID: lmsSkins
+// Description: Have your sprites render as other images or costumes.
+// By: LilyMakesThings
+
+(function (Scratch) {
+ "use strict";
+
+ const requireNonPackagedRuntime = (blockName) => {
+ if (Scratch.vm.runtime.isPackaged) {
+ alert(
+ `To use the Skins ${blockName} block, the creator of the packaged project must uncheck "Remove raw asset data after loading to save RAM" under advanced settings in the packager.`
+ );
+ return false;
+ }
+ return true;
+ };
+
+ /**
+ * @param {RenderWebGL.SVGSkin} svgSkin
+ * @returns {Promise}
+ */
+ const svgSkinFinishedLoading = (svgSkin) =>
+ new Promise((resolve) => {
+ if (svgSkin._svgImageLoaded) {
+ resolve();
+ } else {
+ svgSkin._svgImage.addEventListener("load", () => {
+ resolve();
+ });
+ svgSkin._svgImage.addEventListener("error", () => {
+ resolve();
+ });
+ }
+ });
+
+ const vm = Scratch.vm;
+ const runtime = vm.runtime;
+ const renderer = runtime.renderer;
+ const Cast = Scratch.Cast;
+
+ var createdSkins = [];
+
+ class Skins {
+ constructor() {
+ runtime.on("PROJECT_START", () => {
+ this._refreshTargets();
+ });
+
+ runtime.on("PROJECT_STOP_ALL", () => {
+ this._refreshTargets();
+ });
+ }
+
+ getInfo() {
+ return {
+ id: "lmsSkins",
+ name: "Skins",
+ color1: "#6b56ff",
+ color2: "#604de6",
+ color3: "#5645cc",
+ docsURI: "https://extensions.turbowarp.org/Lily/Skins",
+ blocks: [
+ {
+ opcode: "registerSVGSkin",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "create SVG skin [SVG] as [NAME]",
+ arguments: {
+ SVG: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "",
+ },
+ NAME: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my skin",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "registerCostumeSkin",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "load skin from [COSTUME] as [NAME]",
+ arguments: {
+ COSTUME: {
+ type: Scratch.ArgumentType.COSTUME,
+ },
+ NAME: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my skin",
+ },
+ },
+ },
+ {
+ opcode: "registerURLSkin",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "load skin from URL [URL] as [NAME]",
+ arguments: {
+ URL: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "https://extensions.turbowarp.org/dango.png",
+ },
+ NAME: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my skin",
+ },
+ },
+ },
+ {
+ opcode: "getSkinLoaded",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "skin [NAME] is loaded?",
+ arguments: {
+ NAME: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my skin",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "setSkin",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set skin of [TARGET] to [NAME]",
+ arguments: {
+ TARGET: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "targetMenu",
+ },
+ NAME: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my skin",
+ },
+ },
+ },
+ {
+ opcode: "restoreSkin",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "restore skin of [TARGET]",
+ arguments: {
+ TARGET: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "targetMenu",
+ },
+ },
+ },
+ {
+ opcode: "restoreTargets",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "restore targets with skin [NAME]",
+ arguments: {
+ NAME: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my skin",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "getCurrentSkin",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "current skin of [TARGET]",
+ arguments: {
+ TARGET: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "targetMenu",
+ },
+ },
+ },
+ {
+ opcode: "getSkinAttribute",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[ATTRIBUTE] of skin [NAME]",
+ arguments: {
+ ATTRIBUTE: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "skinAttributes",
+ },
+ NAME: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my skin",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "deleteSkin",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "delete skin [NAME]",
+ arguments: {
+ NAME: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my skin",
+ },
+ },
+ },
+ {
+ opcode: "deleteAllSkins",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "delete all skins",
+ },
+ ],
+ menus: {
+ targetMenu: {
+ acceptReporters: true,
+ items: "_getTargets",
+ },
+ skinAttributes: {
+ acceptReporters: true,
+ items: ["width", "height"],
+ },
+ },
+ };
+ }
+
+ async registerSVGSkin(args) {
+ const skinName = Cast.toString(args.NAME);
+ const svgData = Cast.toString(args.SVG);
+
+ let oldSkinId = null;
+ if (createdSkins[skinName]) {
+ oldSkinId = createdSkins[skinName];
+ }
+
+ // This generally takes a few frames, so yield the block
+ const skinId = renderer.createSVGSkin(svgData);
+ createdSkins[skinName] = skinId;
+
+ await svgSkinFinishedLoading(renderer._allSkins[skinId]);
+
+ if (oldSkinId) {
+ this._refreshTargetsFromID(oldSkinId, false, skinId);
+ renderer.destroySkin(oldSkinId);
+ }
+ }
+
+ async registerCostumeSkin(args, util) {
+ if (!requireNonPackagedRuntime("add costume skin")) {
+ return;
+ }
+
+ const skinName = Cast.toString(args.NAME);
+ const costumeIndex = util.target.getCostumeIndexByName(args.COSTUME);
+ if (costumeIndex === -1) return;
+ const costume = util.target.sprite.costumes[costumeIndex];
+
+ const url = costume.asset.encodeDataURI();
+ const rotationCenterX = costume.rotationCenterX;
+ const rotationCenterY = costume.rotationCenterY;
+
+ let rotationCenter = [rotationCenterX, rotationCenterY];
+ if (!rotationCenterX || !rotationCenterY) rotationCenter = null;
+
+ let oldSkinId = null;
+ if (createdSkins[skinName]) {
+ oldSkinId = createdSkins[skinName];
+ }
+
+ const skinId = await this._createURLSkin(url, rotationCenter);
+ createdSkins[skinName] = skinId;
+
+ if (oldSkinId) {
+ this._refreshTargetsFromID(oldSkinId, false, skinId);
+ renderer.destroySkin(oldSkinId);
+ }
+ }
+
+ async registerURLSkin(args) {
+ const skinName = Cast.toString(args.NAME);
+ const url = Cast.toString(args.URL);
+
+ let oldSkinId = null;
+ if (createdSkins[skinName]) {
+ oldSkinId = createdSkins[skinName];
+ }
+
+ const skinId = await this._createURLSkin(url);
+ if (!skinId) return;
+ createdSkins[skinName] = skinId;
+
+ if (oldSkinId) {
+ this._refreshTargetsFromID(oldSkinId, false, skinId);
+ renderer.destroySkin(oldSkinId);
+ }
+ }
+
+ getSkinLoaded(args) {
+ const skinName = Cast.toString(args.NAME);
+ return !!createdSkins[skinName];
+ }
+
+ setSkin(args, util) {
+ const skinName = Cast.toString(args.NAME);
+ if (!createdSkins[skinName]) return;
+
+ const targetName = Cast.toString(args.TARGET);
+ const target = this._getTargetFromMenu(targetName, util);
+ if (!target) return;
+ const drawableID = target.drawableID;
+
+ const skinId = createdSkins[skinName];
+ renderer._allDrawables[drawableID].skin = renderer._allSkins[skinId];
+ }
+
+ restoreSkin(args, util) {
+ const targetName = Cast.toString(args.TARGET);
+ const target = this._getTargetFromMenu(targetName, util);
+ if (!target) return;
+ target.updateAllDrawableProperties();
+ }
+
+ getCurrentSkin(args, util) {
+ const targetName = Cast.toString(args.TARGET);
+ const target = this._getTargetFromMenu(targetName, util);
+ if (!target) return;
+ const drawableID = target.drawableID;
+
+ const skinId = renderer._allDrawables[drawableID].skin._id;
+ const skinName = this._getSkinNameFromID(skinId);
+ return skinName ? skinName : "";
+ }
+
+ getSkinAttribute(args) {
+ const skins = renderer._allSkins;
+ const skinName = Cast.toString(args.NAME);
+
+ if (!createdSkins[skinName]) return 0;
+ const skinId = createdSkins[skinName];
+ if (!skins[skinId]) return 0;
+
+ const size = skins[skinId].size;
+ const attribute = Cast.toString(args.ATTRIBUTE).toLowerCase();
+
+ switch (attribute) {
+ case "width":
+ return Math.ceil(size[0]);
+ case "height":
+ return Math.ceil(size[1]);
+ default:
+ return 0;
+ }
+ }
+
+ deleteSkin(args) {
+ const skinName = Cast.toString(args.NAME);
+ if (!createdSkins[skinName]) return;
+ const skinId = createdSkins[skinName];
+
+ this._refreshTargetsFromID(skinId, true);
+ renderer.destroySkin(skinId);
+ Reflect.deleteProperty(createdSkins, skinName);
+ }
+
+ deleteAllSkins() {
+ this._refreshTargets();
+ for (let i = 0; i < createdSkins.length; i++)
+ renderer.destroySkin(createdSkins[i]);
+ createdSkins = [];
+ }
+
+ restoreTargets(args) {
+ const skinName = Cast.toString(args.NAME);
+ if (!createdSkins[skinName]) return;
+ const skinId = createdSkins[skinName];
+
+ this._refreshTargetsFromID(skinId, true);
+ }
+
+ // Utility Functions
+
+ _refreshTargetsFromID(skinId, reset, newId) {
+ const drawables = renderer._allDrawables;
+ const skins = renderer._allSkins;
+
+ for (const target of runtime.targets) {
+ const drawableID = target.drawableID;
+ const targetSkin = drawables[drawableID].skin.id;
+
+ if (targetSkin === skinId) {
+ target.updateAllDrawableProperties();
+ if (!reset)
+ drawables[drawableID].skin = newId ? skins[newId] : skins[skinId];
+ }
+ }
+ }
+
+ _refreshTargets() {
+ for (const target of runtime.targets) {
+ target.updateAllDrawableProperties();
+ }
+ }
+
+ _getSkinNameFromID(skinId) {
+ for (const skinName in createdSkins) {
+ if (createdSkins[skinName] === skinId) return skinName;
+ }
+ }
+
+ _getTargetFromMenu(targetName, util) {
+ let target = Scratch.vm.runtime.getSpriteTargetByName(targetName);
+ if (targetName === "_myself_") target = util.target;
+ if (targetName === "_stage_") target = runtime.getTargetForStage();
+ return target;
+ }
+
+ async _createURLSkin(URL, rotationCenter) {
+ let imageData;
+ if (await Scratch.canFetch(URL)) {
+ imageData = await Scratch.fetch(URL);
+ } else {
+ return;
+ }
+
+ const contentType = imageData.headers.get("Content-Type");
+ if (contentType === "image/svg+xml") {
+ return renderer.createSVGSkin(await imageData.text(), rotationCenter);
+ } else if (
+ contentType === "image/png" ||
+ contentType === "image/jpeg" ||
+ contentType === "image/bmp"
+ ) {
+ // eslint-disable-next-line no-restricted-syntax
+ const output = new Image();
+ output.src = URL;
+ output.crossOrigin = "anonymous";
+ await output.decode();
+ return renderer.createBitmapSkin(output);
+ }
+ }
+
+ _getTargets() {
+ const spriteNames = [
+ { text: "myself", value: "_myself_" },
+ { text: "Stage", value: "_stage_" },
+ ];
+ const targets = Scratch.vm.runtime.targets;
+ for (let index = 1; index < targets.length; index++) {
+ const target = targets[index];
+ if (target.isOriginal) {
+ const targetName = target.getName();
+ spriteNames.push({
+ text: targetName,
+ value: targetName,
+ });
+ }
+ }
+ return spriteNames;
+ }
+ }
+ Scratch.extensions.register(new Skins());
+})(Scratch);
diff --git a/extensions/Lily/SoundExpanded.js b/extensions/Lily/SoundExpanded.js
new file mode 100644
index 0000000000..aefe95072b
--- /dev/null
+++ b/extensions/Lily/SoundExpanded.js
@@ -0,0 +1,359 @@
+// Name: Sound Expanded
+// Description: Adds more sound-related blocks.
+// ID: lmsSoundExpanded
+// By: LilyMakesThings
+
+(function (Scratch) {
+ "use strict";
+
+ const vm = Scratch.vm;
+ const runtime = vm.runtime;
+ const soundCategory = runtime.ext_scratch3_sound;
+
+ class SoundExpanded {
+ getInfo() {
+ return {
+ id: "lmsSoundExpanded",
+ color1: "#CF63CF",
+ color2: "#C94FC9",
+ color3: "#BD42BD",
+ name: "Sound Expanded",
+ blocks: [
+ {
+ opcode: "startLooping",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "start looping [SOUND]",
+ arguments: {
+ SOUND: {
+ type: Scratch.ArgumentType.SOUND,
+ },
+ START: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: 0,
+ },
+ },
+ },
+ {
+ opcode: "stopLooping",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "end looping [SOUND]",
+ arguments: {
+ SOUND: {
+ type: Scratch.ArgumentType.SOUND,
+ },
+ },
+ },
+ {
+ opcode: "isLooping",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[SOUND] is looping?",
+ arguments: {
+ SOUND: {
+ type: Scratch.ArgumentType.SOUND,
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "stopSound",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "stop sound [SOUND]",
+ arguments: {
+ SOUND: {
+ type: Scratch.ArgumentType.SOUND,
+ },
+ },
+ },
+ {
+ opcode: "pauseSounds",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "pause all sounds",
+ arguments: {
+ SOUND: {
+ type: Scratch.ArgumentType.SOUND,
+ },
+ },
+ },
+ {
+ opcode: "resumeSounds",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "resume all sounds",
+ arguments: {
+ SOUND: {
+ type: Scratch.ArgumentType.SOUND,
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "isSoundPlaying",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "sound [SOUND] is playing?",
+ arguments: {
+ SOUND: {
+ type: Scratch.ArgumentType.SOUND,
+ },
+ },
+ },
+ {
+ opcode: "attributeOfSound",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[ATTRIBUTE] of [SOUND]",
+ arguments: {
+ ATTRIBUTE: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "attribute",
+ },
+ SOUND: {
+ type: Scratch.ArgumentType.SOUND,
+ },
+ },
+ },
+ {
+ opcode: "getSoundEffect",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[EFFECT] of [TARGET]",
+ arguments: {
+ EFFECT: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "effect",
+ },
+ TARGET: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "targets",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "setProjectVolume",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set project volume to [VALUE]%",
+ arguments: {
+ VALUE: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: 100,
+ },
+ },
+ },
+ {
+ opcode: "changeProjectVolume",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "change project volume by [VALUE]",
+ arguments: {
+ VALUE: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: -10,
+ },
+ },
+ },
+ {
+ opcode: "getProjectVolume",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "project volume",
+ },
+ ],
+ menus: {
+ attribute: {
+ acceptReporters: false,
+ items: ["length", "channels", "sample rate", "dataURI"],
+ },
+ effect: {
+ acceptReporters: false,
+ items: ["pitch", "pan"],
+ },
+ targets: {
+ acceptReporters: true,
+ items: "_getTargets",
+ },
+ },
+ };
+ }
+
+ startLooping(args, util) {
+ const index = this._getSoundIndex(args.SOUND, util);
+ if (index < 0) return 0;
+ const target = util.target;
+ const sprite = util.target.sprite;
+
+ const soundId = sprite.sounds[index].soundId;
+ const soundPlayer = sprite.soundBank.soundPlayers[soundId];
+
+ if (!soundPlayer.isPlaying) {
+ soundCategory._addWaitingSound(target.id, soundId);
+ sprite.soundBank.playSound(util.target, soundId);
+ }
+
+ if (!soundPlayer.outputNode) return;
+ soundPlayer.outputNode.loop = true;
+ }
+
+ stopLooping(args, util) {
+ const index = this._getSoundIndex(args.SOUND, util);
+ if (index < 0) return false;
+ const sprite = util.target.sprite;
+
+ const soundId = sprite.sounds[index].soundId;
+ const soundPlayer = sprite.soundBank.soundPlayers[soundId];
+
+ if (!soundPlayer.outputNode) return;
+ soundPlayer.outputNode.loop = false;
+ }
+
+ isLooping(args, util) {
+ const index = this._getSoundIndex(args.SOUND, util);
+ if (index < 0) return false;
+ const sprite = util.target.sprite;
+
+ const soundId = sprite.sounds[index].soundId;
+ const soundPlayer = sprite.soundBank.soundPlayers[soundId];
+
+ if (!soundPlayer.outputNode) return false;
+ return soundPlayer.outputNode.loop;
+ }
+
+ stopSound(args, util) {
+ const index = this._getSoundIndex(args.SOUND, util);
+ if (index < 0) return 0;
+ const target = util.target;
+ const sprite = target.sprite;
+
+ const soundId = sprite.sounds[index].soundId;
+ const soundBank = sprite.soundBank;
+ soundBank.stop(target, soundId);
+ }
+
+ pauseSounds(args, util) {
+ this._toggleSoundState(args, util, true);
+ }
+
+ resumeSounds(args, util) {
+ this._toggleSoundState(args, util, false);
+ }
+
+ _toggleSoundState(args, util, state) {
+ const sprite = util.target.sprite;
+ const audioContext = sprite.soundBank.audioEngine.audioContext;
+
+ if (state) {
+ audioContext.suspend();
+ return;
+ } else {
+ audioContext.resume();
+ return;
+ }
+ }
+
+ isSoundPlaying(args, util) {
+ const index = this._getSoundIndex(args.SOUND, util);
+ if (index < 0) return false;
+ const sprite = util.target.sprite;
+
+ const soundId = sprite.sounds[index].soundId;
+ const soundPlayers = sprite.soundBank.soundPlayers;
+ return soundPlayers[soundId].isPlaying;
+ }
+
+ attributeOfSound(args, util) {
+ const index = this._getSoundIndex(args.SOUND, util);
+ if (index < 0) return 0;
+ const sprite = util.target.sprite;
+
+ const sound = sprite.sounds[index];
+ const soundId = sound.soundId;
+ const soundPlayer = sprite.soundBank.soundPlayers[soundId];
+ const soundBuffer = soundPlayer.buffer;
+
+ switch (args.ATTRIBUTE) {
+ case "length":
+ return Math.round(soundBuffer.duration * 100) / 100;
+ case "channels":
+ return soundBuffer.numberOfChannels;
+ case "sample rate":
+ return soundBuffer.sampleRate;
+ case "dataURI":
+ return sound.asset.encodeDataURI();
+ }
+ }
+
+ getSoundEffect(args, util) {
+ let target = Scratch.vm.runtime.getSpriteTargetByName(args.TARGET);
+ if (args.TARGET === "_myself_") target = util.target;
+ if (args.TARGET === "_stage_") target = runtime.getTargetForStage();
+ const effects = target.soundEffects;
+ if (!effects) return 0;
+ return effects[args.EFFECT];
+ }
+
+ setProjectVolume(args) {
+ const value = Scratch.Cast.toNumber(args.VALUE);
+ const newVolume = this._wrapClamp(value / 100, 0, 1);
+ runtime.audioEngine.inputNode.gain.value = newVolume;
+ }
+
+ changeProjectVolume(args) {
+ const value = Scratch.Cast.toNumber(args.VALUE) / 100;
+ const volume = runtime.audioEngine.inputNode.gain.value;
+ const newVolume = Scratch.Cast.toNumber(
+ Math.min(Math.max(volume + value, 1), 0)
+ );
+ runtime.audioEngine.inputNode.gain.value = newVolume;
+ }
+
+ getProjectVolume() {
+ const volume = runtime.audioEngine.inputNode.gain.value;
+ return Math.round(volume * 10000) / 100;
+ }
+
+ /* Utility Functions */
+
+ _getSoundIndex(soundName, util) {
+ const len = util.target.sprite.sounds.length;
+ if (len === 0) {
+ return -1;
+ }
+ const index = this._getSoundIndexByName(soundName, util);
+ if (index !== -1) {
+ return index;
+ }
+ const oneIndexedIndex = parseInt(soundName, 10);
+ if (!isNaN(oneIndexedIndex)) {
+ return this._wrapClamp(oneIndexedIndex - 1, 0, len - 1);
+ }
+ return -1;
+ }
+
+ _getSoundIndexByName(soundName, util) {
+ const sounds = util.target.sprite.sounds;
+ for (let i = 0; i < sounds.length; i++) {
+ if (sounds[i].name === soundName) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ _wrapClamp(n, min, max) {
+ const range = max - min + 1;
+ return n - Math.floor((n - min) / range) * range;
+ }
+
+ _getTargets() {
+ let spriteNames = [
+ { text: "myself", value: "_myself_" },
+ { text: "Stage", value: "_stage_" },
+ ];
+ const targets = Scratch.vm.runtime.targets
+ .filter((target) => target.isOriginal && !target.isStage)
+ .map((target) => target.getName());
+ spriteNames = spriteNames.concat(targets);
+ return spriteNames;
+ }
+ }
+
+ Scratch.extensions.register(new SoundExpanded());
+})(Scratch);
diff --git a/extensions/Lily/TempVariables.js b/extensions/Lily/TempVariables.js
index 0e40763ee1..a64d63d8d4 100644
--- a/extensions/Lily/TempVariables.js
+++ b/extensions/Lily/TempVariables.js
@@ -1,7 +1,7 @@
-(function(Scratch) {
- 'use strict';
+(function (Scratch) {
+ "use strict";
- const menuIconURI = '';
+ const menuIconURI = "";
// Object.create(null) prevents "variable [toString]" from returning a function
let variables = Object.create(null);
@@ -9,114 +9,114 @@
class TempVars {
getInfo() {
return {
- id: 'lmstempvars',
- name: 'Temporary Variables',
- color1: '#FF791A',
- color2: '#E15D00',
+ id: "lmstempvars",
+ name: "Temporary Variables",
+ color1: "#FF791A",
+ color2: "#E15D00",
menuIconURI: menuIconURI,
blocks: [
{
- opcode: 'setVariableTo',
+ opcode: "setVariableTo",
blockType: Scratch.BlockType.COMMAND,
- text: 'set variable [INPUTA] to [INPUTB]',
+ text: "set variable [INPUTA] to [INPUTB]",
arguments: {
INPUTA: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'my variable'
+ defaultValue: "my variable",
},
INPUTB: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '0'
- }
- }
+ defaultValue: "0",
+ },
+ },
},
{
- opcode: 'changeVariableBy',
+ opcode: "changeVariableBy",
blockType: Scratch.BlockType.COMMAND,
- text: 'change variable [INPUTA] by [INPUTB]',
+ text: "change variable [INPUTA] by [INPUTB]",
arguments: {
INPUTA: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'my variable'
+ defaultValue: "my variable",
},
INPUTB: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '1'
- }
- }
+ defaultValue: "1",
+ },
+ },
},
{
- opcode: 'getVariable',
+ opcode: "getVariable",
blockType: Scratch.BlockType.REPORTER,
- text: 'variable [INPUT]',
+ text: "variable [INPUT]",
disableMonitor: true,
arguments: {
INPUT: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'my variable'
- }
- }
+ defaultValue: "my variable",
+ },
+ },
},
- '---',
+ "---",
{
- opcode: 'deleteVariable',
+ opcode: "deleteVariable",
blockType: Scratch.BlockType.COMMAND,
- text: 'delete variable [INPUT]',
+ text: "delete variable [INPUT]",
arguments: {
INPUT: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'my variable'
- }
- }
+ defaultValue: "my variable",
+ },
+ },
},
{
- opcode: 'deleteAllVariables',
+ opcode: "deleteAllVariables",
blockType: Scratch.BlockType.COMMAND,
- text: 'delete all variables',
+ text: "delete all variables",
},
{
- opcode: 'listVariables',
+ opcode: "listVariables",
blockType: Scratch.BlockType.REPORTER,
- text: 'list active variables',
+ text: "list active variables",
disableMonitor: true,
- }
- ]
+ },
+ ],
};
}
- getVariable (args) {
+ getVariable(args) {
if (args.INPUT in variables) {
- return (variables[args.INPUT]);
+ return variables[args.INPUT];
} else {
- return '';
+ return "";
}
}
- setVariableTo (args) {
+ setVariableTo(args) {
variables[args.INPUTA] = args.INPUTB;
}
- changeVariableBy (args) {
+ changeVariableBy(args) {
if (args.INPUTA in variables) {
const prev = Scratch.Cast.toNumber(variables[args.INPUTA]);
const next = Scratch.Cast.toNumber(args.INPUTB);
- variables[args.INPUTA] = (prev + next);
+ variables[args.INPUTA] = prev + next;
} else {
variables[args.INPUTA] = args.INPUTB;
}
}
- listVariables (args, util) {
- return Object.keys(variables).join(',');
+ listVariables(args, util) {
+ return Object.keys(variables).join(",");
}
- deleteVariable (args) {
+ deleteVariable(args) {
Reflect.deleteProperty(variables, args.INPUT);
}
- deleteAllVariables () {
+ deleteAllVariables() {
variables = Object.create(null);
}
}
diff --git a/extensions/Lily/TempVariables2.js b/extensions/Lily/TempVariables2.js
index 959fb6c89b..a7dfb04db3 100644
--- a/extensions/Lily/TempVariables2.js
+++ b/extensions/Lily/TempVariables2.js
@@ -1,306 +1,304 @@
-// Name: Temporary Variables
-// Description: Create disposable runtime or thread variables.
-// By: LilyMakesThings
-
-(function(Scratch) {
- 'use strict';
-
- const menuIconURI = '';
-
- // Object.create(null) prevents "variable [toString]" from returning a function
- let runtimeVariables = Object.create(null);
-
- // Credit to skyhigh173 for the idea of this
- const label = (name, hidden) => ({
- blockType: Scratch.BlockType.LABEL,
- text: name,
- hideFromPalette: hidden
- });
-
- function resetRuntimeVariables() {
- runtimeVariables = Object.create(null);
- }
-
- class TempVars {
- constructor() {
- Scratch.vm.runtime.on('PROJECT_START', () => {
- resetRuntimeVariables();
- });
-
- Scratch.vm.runtime.on('PROJECT_STOP_ALL', () => {
- resetRuntimeVariables();
- });
- }
-
- getInfo() {
- return {
- id: 'lmsTempVars2',
- name: 'Temporary Variables',
- color1: '#FF791A',
- color2: '#E15D00',
- menuIconURI: menuIconURI, // I intend on making one later
- blocks: [
-
- label('Thread Variables', false),
-
- {
- opcode: 'setThreadVariable',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set thread var [VAR] to [STRING]',
- arguments: {
- VAR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'variable'
- },
- STRING: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '0'
- }
- },
- },
- {
- opcode: 'changeThreadVariable',
- blockType: Scratch.BlockType.COMMAND,
- text: 'change thread var [VAR] by [NUM]',
- arguments: {
- VAR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'variable'
- },
- NUM: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- }
- },
- },
-
- '---',
-
- {
- opcode: 'getThreadVariable',
- blockType: Scratch.BlockType.REPORTER,
- text: 'thread var [VAR]',
- arguments: {
- VAR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'variable'
- }
- }
- },
- {
- opcode: 'threadVariableExists',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'thread var [VAR] exists?',
- arguments: {
- VAR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'variable'
- }
- }
- },
-
- '---',
-
- /* Add this when the compiler supports it
- {
- opcode: 'forEachThreadVariable',
- blockType: Scratch.BlockType.LOOP,
- text: 'for each [VAR] in [NUM]',
- arguments: {
- VAR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'thread variable'
- },
- NUM: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- }
- }
- },
- */
- {
- opcode: 'listThreadVariables',
- blockType: Scratch.BlockType.REPORTER,
- text: 'list active thread variables',
- disableMonitor: true
- },
-
- '---',
-
- label('Runtime Variables', false),
-
- {
- opcode: 'setRuntimeVariable',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set runtime var [VAR] to [STRING]',
- arguments: {
- VAR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'variable'
- },
- STRING: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '0'
- }
- }
- },
- {
- opcode: 'changeRuntimeVariable',
- blockType: Scratch.BlockType.COMMAND,
- text: 'change runtime var [VAR] by [NUM]',
- arguments: {
- VAR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'variable'
- },
- NUM: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '1'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'getRuntimeVariable',
- blockType: Scratch.BlockType.REPORTER,
- text: 'runtime var [VAR]',
- disableMonitor: true,
- arguments: {
- VAR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'variable'
- }
- }
- },
- {
- opcode: 'runtimeVariableExists',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'runtime var [VAR] exists?',
- arguments: {
- VAR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'variable'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'deleteRuntimeVariable',
- blockType: Scratch.BlockType.COMMAND,
- text: 'delete runtime var [VAR]',
- arguments: {
- VAR: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'variable'
- }
- }
- },
- {
- opcode: 'deleteAllRuntimeVariables',
- blockType: Scratch.BlockType.COMMAND,
- text: 'delete all runtime variables',
- },
- {
- opcode: 'listRuntimeVariables',
- blockType: Scratch.BlockType.REPORTER,
- text: 'list active runtime variables'
- }
- ]
- };
- }
-
- /* THREAD VARIABLES */
-
- setThreadVariable(args, util) {
- const thread = util.thread;
- if (!thread.variables) thread.variables = Object.create(null);
- const vars = thread.variables;
- vars[args.VAR] = args.STRING;
- }
-
- changeThreadVariable(args, util) {
- const thread = util.thread;
- if (!thread.variables) thread.variables = Object.create(null);
- const vars = thread.variables;
- const prev = Scratch.Cast.toNumber(vars[args.VAR]);
- const next = Scratch.Cast.toNumber(args.NUM);
- vars[args.VAR] = prev + next;
- }
-
- getThreadVariable (args, util) {
- const thread = util.thread;
- if (!thread.variables) thread.variables = Object.create(null);
- const vars = thread.variables;
- const varValue = vars[args.VAR];
- if (typeof varValue === 'undefined') return '';
- return varValue;
- }
-
- threadVariableExists (args, util) {
- const thread = util.thread;
- if (!thread.variables) thread.variables = Object.create(null);
- const vars = thread.variables;
- const varValue = vars[args.VAR];
- return !(typeof varValue === 'undefined');
- }
-
- forEachThreadVariable(args, util) {
- const thread = util.thread;
- if (!thread.variables) thread.variables = Object.create(null);
- const vars = thread.variables;
- if (typeof util.stackFrame.index === 'undefined') {
- util.stackFrame.index = 0;
- }
- if (util.stackFrame.index < Number(args.NUM)) {
- util.stackFrame.index++;
- vars[args.VAR] = util.stackFrame.index;
- util.startBranch(1, true);
- }
- }
-
- listThreadVariables(args, util) {
- const thread = util.thread;
- if (!thread.variables) thread.variables = Object.create(null);
- const vars = thread.variables;
- return Object.keys(vars).join(',');
- }
-
- /* RUNTIME VARIABLES */
-
- setRuntimeVariable (args) {
- runtimeVariables[args.VAR] = args.STRING;
- }
-
- changeRuntimeVariable (args) {
- const prev = Scratch.Cast.toNumber(runtimeVariables[args.VAR]);
- const next = Scratch.Cast.toNumber(args.NUM);
- runtimeVariables[args.VAR] = prev + next;
- }
-
- getRuntimeVariable (args) {
- if (!(args.VAR in runtimeVariables)) return '';
- return runtimeVariables[args.VAR];
- }
-
- runtimeVariableExists (args) {
- return (args.VAR in runtimeVariables);
- }
-
- listRuntimeVariables (args, util) {
- return Object.keys(runtimeVariables).join(',');
- }
-
- deleteRuntimeVariable (args) {
- Reflect.deleteProperty(runtimeVariables, args.VAR);
- }
-
- deleteAllRuntimeVariables () {
- runtimeVariables = Object.create(null);
- }
- }
- Scratch.extensions.register(new TempVars());
-})(Scratch);
+// Name: Temporary Variables
+// ID: lmsTempVars2
+// Description: Create disposable runtime or thread variables.
+// By: LilyMakesThings
+
+(function (Scratch) {
+ "use strict";
+
+ const menuIconURI = "";
+
+ // Object.create(null) prevents "variable [toString]" from returning a function
+ let runtimeVariables = Object.create(null);
+
+ // Credit to skyhigh173 for the idea of this
+ const label = (name, hidden) => ({
+ blockType: Scratch.BlockType.LABEL,
+ text: name,
+ hideFromPalette: hidden,
+ });
+
+ function resetRuntimeVariables() {
+ runtimeVariables = Object.create(null);
+ }
+
+ class TempVars {
+ constructor() {
+ Scratch.vm.runtime.on("PROJECT_START", () => {
+ resetRuntimeVariables();
+ });
+
+ Scratch.vm.runtime.on("PROJECT_STOP_ALL", () => {
+ resetRuntimeVariables();
+ });
+ }
+
+ getInfo() {
+ return {
+ id: "lmsTempVars2",
+ name: "Temporary Variables",
+ color1: "#FF791A",
+ color2: "#E15D00",
+ menuIconURI: menuIconURI, // I intend on making one later
+ blocks: [
+ label("Thread Variables", false),
+
+ {
+ opcode: "setThreadVariable",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set thread var [VAR] to [STRING]",
+ arguments: {
+ VAR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "variable",
+ },
+ STRING: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "0",
+ },
+ },
+ },
+ {
+ opcode: "changeThreadVariable",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "change thread var [VAR] by [NUM]",
+ arguments: {
+ VAR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "variable",
+ },
+ NUM: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "getThreadVariable",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "thread var [VAR]",
+ arguments: {
+ VAR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "variable",
+ },
+ },
+ },
+ {
+ opcode: "threadVariableExists",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "thread var [VAR] exists?",
+ arguments: {
+ VAR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "variable",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "forEachThreadVariable",
+ blockType: Scratch.BlockType.LOOP,
+ text: "for each [VAR] in [NUM]",
+ arguments: {
+ VAR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "thread variable",
+ },
+ NUM: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ },
+ },
+ {
+ opcode: "listThreadVariables",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "list active thread variables",
+ disableMonitor: true,
+ },
+
+ "---",
+
+ label("Runtime Variables", false),
+
+ {
+ opcode: "setRuntimeVariable",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set runtime var [VAR] to [STRING]",
+ arguments: {
+ VAR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "variable",
+ },
+ STRING: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "0",
+ },
+ },
+ },
+ {
+ opcode: "changeRuntimeVariable",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "change runtime var [VAR] by [NUM]",
+ arguments: {
+ VAR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "variable",
+ },
+ NUM: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "1",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "getRuntimeVariable",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "runtime var [VAR]",
+ disableMonitor: true,
+ arguments: {
+ VAR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "variable",
+ },
+ },
+ },
+ {
+ opcode: "runtimeVariableExists",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "runtime var [VAR] exists?",
+ arguments: {
+ VAR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "variable",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "deleteRuntimeVariable",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "delete runtime var [VAR]",
+ arguments: {
+ VAR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "variable",
+ },
+ },
+ },
+ {
+ opcode: "deleteAllRuntimeVariables",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "delete all runtime variables",
+ },
+ {
+ opcode: "listRuntimeVariables",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "list active runtime variables",
+ },
+ ],
+ };
+ }
+
+ /* THREAD VARIABLES */
+
+ setThreadVariable(args, util) {
+ const thread = util.thread;
+ if (!thread.variables) thread.variables = Object.create(null);
+ const vars = thread.variables;
+ vars[args.VAR] = args.STRING;
+ }
+
+ changeThreadVariable(args, util) {
+ const thread = util.thread;
+ if (!thread.variables) thread.variables = Object.create(null);
+ const vars = thread.variables;
+ const prev = Scratch.Cast.toNumber(vars[args.VAR]);
+ const next = Scratch.Cast.toNumber(args.NUM);
+ vars[args.VAR] = prev + next;
+ }
+
+ getThreadVariable(args, util) {
+ const thread = util.thread;
+ if (!thread.variables) thread.variables = Object.create(null);
+ const vars = thread.variables;
+ const varValue = vars[args.VAR];
+ if (typeof varValue === "undefined") return "";
+ return varValue;
+ }
+
+ threadVariableExists(args, util) {
+ const thread = util.thread;
+ if (!thread.variables) thread.variables = Object.create(null);
+ const vars = thread.variables;
+ const varValue = vars[args.VAR];
+ return !(typeof varValue === "undefined");
+ }
+
+ forEachThreadVariable(args, util) {
+ const thread = util.thread;
+ if (!thread.variables) thread.variables = Object.create(null);
+ const vars = thread.variables;
+ if (typeof util.stackFrame.index === "undefined") {
+ util.stackFrame.index = 0;
+ }
+ if (util.stackFrame.index < Number(args.NUM)) {
+ util.stackFrame.index++;
+ vars[args.VAR] = util.stackFrame.index;
+ return true;
+ }
+ }
+
+ listThreadVariables(args, util) {
+ const thread = util.thread;
+ if (!thread.variables) thread.variables = Object.create(null);
+ const vars = thread.variables;
+ return Object.keys(vars).join(",");
+ }
+
+ /* RUNTIME VARIABLES */
+
+ setRuntimeVariable(args) {
+ runtimeVariables[args.VAR] = args.STRING;
+ }
+
+ changeRuntimeVariable(args) {
+ const prev = Scratch.Cast.toNumber(runtimeVariables[args.VAR]);
+ const next = Scratch.Cast.toNumber(args.NUM);
+ runtimeVariables[args.VAR] = prev + next;
+ }
+
+ getRuntimeVariable(args) {
+ if (!(args.VAR in runtimeVariables)) return "";
+ return runtimeVariables[args.VAR];
+ }
+
+ runtimeVariableExists(args) {
+ return args.VAR in runtimeVariables;
+ }
+
+ listRuntimeVariables(args, util) {
+ return Object.keys(runtimeVariables).join(",");
+ }
+
+ deleteRuntimeVariable(args) {
+ Reflect.deleteProperty(runtimeVariables, args.VAR);
+ }
+
+ deleteAllRuntimeVariables() {
+ runtimeVariables = Object.create(null);
+ }
+ }
+ Scratch.extensions.register(new TempVars());
+})(Scratch);
diff --git a/extensions/Lily/lmsutils.js b/extensions/Lily/lmsutils.js
index f72f40e4ae..9f3be6ba3f 100644
--- a/extensions/Lily/lmsutils.js
+++ b/extensions/Lily/lmsutils.js
@@ -1,1354 +1,1382 @@
-(function(Scratch) {
- 'use strict';
- const menuIconURI = 'data:image/png;base64,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';
-
- let hideLegacyBlocks = true;
-
- var vars = {};
- vars['variables'] = Object.create(null);
-
-
- if (!Scratch.extensions.unsandboxed) {
- throw new Error('This extension must run unsandboxed');
- }
-
- class LMSUtils {
- constructor(runtime) {
- this.runtime = runtime;
- }
- getInfo() {
- return {
- id: 'lmsutilsblocks',
- name: 'LMS Utilities',
- color1: '#3bb2ed',
- color2: '#37a1de',
- color3: '#3693d9',
- menuIconURI: menuIconURI,
- blocks: [
- {
- opcode: 'whenBooleanHat',
- blockType: Scratch.BlockType.HAT,
- text: 'when [INPUT] is true',
- isEdgeActivated: true,
- arguments: {
- INPUT: {
- type: Scratch.ArgumentType.BOOLEAN,
- defaultValue: ''
- }
- }
- },
- {
- opcode: 'whenKeyString',
- blockType: Scratch.BlockType.HAT,
- text: 'when key [KEY_OPTION] pressed',
- isEdgeActivated: true,
- arguments: {
- KEY_OPTION: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'enter'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'keyStringPressed',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'key [KEY_OPTION] pressed?',
- arguments: {
- KEY_OPTION: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'enter'
- }
- }
- },
- {
- opcode: 'trueFalseBoolean',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[TRUEFALSE]',
- arguments: {
- TRUEFALSE: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'true',
- menu: 'trueFalseMenu'
- }
- }
- },
- {
- opcode: 'stringIf',
- blockType: Scratch.BlockType.REPORTER,
- text: 'if [BOOLEAN] then [INPUTA]',
- disableMonitor: true,
- arguments: {
- BOOLEAN: {
- type: Scratch.ArgumentType.BOOLEAN,
- defaultValue: ''
- },
- INPUTA: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'apple'
- }
- }
- },
- {
- opcode: 'stringIfElse',
- blockType: Scratch.BlockType.REPORTER,
- text: 'if [BOOLEAN] then [INPUTA] else [INPUTB]',
- disableMonitor: true,
- arguments: {
- BOOLEAN: {
- type: Scratch.ArgumentType.BOOLEAN,
- defaultValue: ''
- },
- INPUTA: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'apple'
- },
- INPUTB: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'banana'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'getEffectValue',
- blockType: Scratch.BlockType.REPORTER,
- text: 'effect [INPUT]',
- arguments: {
- INPUT: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'color',
- menu: 'colorMenu'
- }
- }
- },
- {
- opcode: 'clonesBeingUsed',
- blockType: Scratch.BlockType.REPORTER,
- text: 'clone count',
- },
- {
- opcode: 'isClone',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'is clone?',
- filter: [Scratch.TargetType.SPRITE]
- },
- {
- opcode: 'spriteClicked',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'sprite clicked?',
- filter: [Scratch.TargetType.SPRITE]
- },
-
- '---',
-
- {
- opcode: 'lettersToOf',
- blockType: Scratch.BlockType.REPORTER,
- text: 'letters [INPUTA] to [INPUTB] of [STRING]',
- disableMonitor: true,
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- INPUTB: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '3'
- },
- STRING: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'suspicious'
- }
- }
- },
- {
- opcode: 'replaceWords',
- blockType: Scratch.BlockType.REPORTER,
- text: 'replace first [INPUTA] with [INPUTB] in [STRING]',
- disableMonitor: true,
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Scratch'
- },
- INPUTB: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Turbowarp'
- },
- STRING: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Scratch is brilliant!'
- }
- }
- },
- {
- opcode: 'findIndexOfString',
- blockType: Scratch.BlockType.REPORTER,
- text: 'index of [INPUTA] in [INPUTB]',
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'brilliant'
- },
- INPUTB: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Turbowarp is brilliant!'
- }
- }
- },
- {
- opcode: 'itemOfFromString',
- blockType: Scratch.BlockType.REPORTER,
- text: 'item [INPUTA] of [INPUTB] split by [INPUTC]',
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '2'
- },
- INPUTB: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'apple|banana'
- },
- INPUTC: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '|'
- }
- }
- },
- {
- opcode: 'stringToUpperCase',
- blockType: Scratch.BlockType.REPORTER,
- text: '[STRING] to uppercase',
- disableMonitor: true,
- arguments: {
- STRING: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'apple'
- }
- }
- },
- {
- opcode: 'stringToLowerCase',
- blockType: Scratch.BlockType.REPORTER,
- text: '[STRING] to lowercase',
- disableMonitor: true,
- arguments: {
- STRING: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'APPLE'
- }
- }
- },
- {
- opcode: 'reverseString',
- blockType: Scratch.BlockType.REPORTER,
- text: 'reverse [STRING]',
- disableMonitor: true,
- arguments: {
- STRING: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'prawobrut'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'norBoolean',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[INPUTA] nor [INPUTB]',
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.BOOLEAN,
- defaultValue: ''
- },
- INPUTB: {
- type: Scratch.ArgumentType.BOOLEAN,
- defaultValue: ''
- }
- }
- },
- {
- opcode: 'xorBoolean',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[INPUTA] xor [INPUTB]',
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.BOOLEAN,
- defaultValue: ''
- },
- INPUTB: {
- type: Scratch.ArgumentType.BOOLEAN,
- defaultValue: ''
- }
- }
- },
- {
- opcode: 'xnorBoolean',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[INPUTA] xnor [INPUTB]',
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.BOOLEAN,
- defaultValue: ''
- },
- INPUTB: {
- type: Scratch.ArgumentType.BOOLEAN,
- defaultValue: ''
- }
- }
- },
- {
- opcode: 'nandBoolean',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[INPUTA] nand [INPUTB]',
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.BOOLEAN,
- defaultValue: ''
- },
- INPUTB: {
- type: Scratch.ArgumentType.BOOLEAN,
- defaultValue: ''
- }
- }
- },
-
- '---',
-
- {
- opcode: 'stringReporter',
- blockType: Scratch.BlockType.REPORTER,
- text: '[STRING]',
- disableMonitor: true,
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- STRING: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'apple'
- }
- }
- },
- {
- opcode: 'colourHex',
- blockType: Scratch.BlockType.REPORTER,
- text: 'color [COLOUR]',
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- COLOUR: {
- type: Scratch.ArgumentType.COLOR,
- defaultValue: '#0088ff'
- }
- }
- },
- {
- opcode: 'angleReporter',
- blockType: Scratch.BlockType.REPORTER,
- text: 'angle [ANGLE]',
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- ANGLE: {
- type: Scratch.ArgumentType.ANGLE,
- defaultValue: '90'
- }
- }
- },
- {
- opcode: 'matrixReporter',
- blockType: Scratch.BlockType.REPORTER,
- text: 'matrix [MATRIX]',
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- MATRIX: {
- type: Scratch.ArgumentType.MATRIX,
- defaultValue: '0101001010000001000101110'
- }
- }
- },
- {
- opcode: 'noteReporter',
- blockType: Scratch.BlockType.REPORTER,
- text: 'note [NOTE]',
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- NOTE: {
- type: Scratch.ArgumentType.NOTE,
- defaultValue: ''
- }
- }
- },
- {
- opcode: 'newlineCharacter',
- blockType: Scratch.BlockType.REPORTER,
- text: 'newline character',
- hideFromPalette: hideLegacyBlocks,
- disableMonitor: true
- },
-
- '---',
-
- {
- opcode: 'equalsExactly',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[ONE] === [TWO]',
- arguments: {
- ONE: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'apple'
- },
- TWO: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'banana'
- }
- }
- },
- {
- opcode: 'notEqualTo',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[INPUTA] ≠ [INPUTB]',
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'apple'
- },
- INPUTB: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'banana'
- }
- }
- },
- {
- opcode: 'moreThanEqual',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[INPUTA] ≥ [INPUTB]',
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '16'
- },
- INPUTB: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '25'
- }
- }
- },
- {
- opcode: 'lessThanEqual',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[INPUTA] ≤ [INPUTB]',
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '16'
- },
- INPUTB: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '25'
- }
- }
- },
- {
- opcode: 'stringCheckBoolean',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[INPUT] is [DROPDOWN]',
- arguments: {
- INPUT: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'apple'
- },
- DROPDOWN: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'text',
- menu: 'stringCheckMenu'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'encodeToBlock',
- blockType: Scratch.BlockType.REPORTER,
- text: 'encode [STRING] to [DROPDOWN]',
- disableMonitor: true,
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- STRING: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- },
- DROPDOWN: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'base64',
- menu: 'conversionMenu'
- }
- }
- },
- {
- opcode: 'decodeFromBlock',
- blockType: Scratch.BlockType.REPORTER,
- text: 'decode [STRING] from [DROPDOWN]',
- disableMonitor: true,
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- STRING: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- },
- DROPDOWN: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'base64',
- menu: 'conversionMenu'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'negativeReporter',
- blockType: Scratch.BlockType.REPORTER,
- text: '- [INPUT]',
- disableMonitor: true,
- arguments: {
- INPUT: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- }
- }
- },
- {
- opcode: 'exponentBlock',
- blockType: Scratch.BlockType.REPORTER,
- text: '[INPUTA] ^ [INPUTB]',
- disableMonitor: true,
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- },
- INPUTB: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- }
- }
- },
- {
- opcode: 'rootBlock',
- blockType: Scratch.BlockType.REPORTER,
- text: '[INPUTA] √ [INPUTB]',
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- },
- INPUTB: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- }
- }
- },
- {
- opcode: 'normaliseValue',
- blockType: Scratch.BlockType.REPORTER,
- text: 'normalise [INPUT]',
- disableMonitor: true,
- arguments: {
- INPUT: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '100'
- }
- }
- },
- {
- opcode: 'clampNumber',
- blockType: Scratch.BlockType.REPORTER,
- text: 'clamp [INPUTA] between [INPUTB] and [INPUTC]',
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '100'
- },
- INPUTB: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '25'
- },
- INPUTC: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '50'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'setVariableTo',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set variable [INPUTA] to [INPUTB]',
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'my variable'
- },
- INPUTB: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '0'
- }
- }
- },
- {
- opcode: 'changeVariableBy',
- blockType: Scratch.BlockType.COMMAND,
- text: 'change variable [INPUTA] by [INPUTB]',
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'my variable'
- },
- INPUTB: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '1'
- }
- }
- },
- {
- opcode: 'getVariable',
- blockType: Scratch.BlockType.REPORTER,
- text: 'variable [INPUT]',
- disableMonitor: true,
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- INPUT: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'my variable'
- }
- }
- },
- {
- opcode: 'deleteVariable',
- blockType: Scratch.BlockType.COMMAND,
- text: 'delete variable [INPUT]',
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- INPUT: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'my variable'
- }
- }
- },
- {
- opcode: 'deleteAllVariables',
- blockType: Scratch.BlockType.COMMAND,
- text: 'delete all variables',
- hideFromPalette: hideLegacyBlocks
- },
- {
- opcode: 'listVariables',
- blockType: Scratch.BlockType.REPORTER,
- text: 'list active variables',
- disableMonitor: true,
- hideFromPalette: hideLegacyBlocks
- },
-
- '---',
-
- {
- opcode: 'greenFlag',
- blockType: Scratch.BlockType.COMMAND,
- text: 'green flag',
- hideFromPalette: hideLegacyBlocks
- },
- {
- opcode: 'setUsername',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set username to [INPUT]',
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- INPUT: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'LilyMakesThings'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'setSpriteSVG',
- blockType: Scratch.BlockType.COMMAND,
- text: 'replace SVG data for costume [INPUTA] with [INPUTB]',
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- INPUTB: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
- },
-
- '---',
-
- {
- opcode: 'alertBlock',
- blockType: Scratch.BlockType.COMMAND,
- text: 'alert [STRING]',
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- STRING: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'A red spy is in the base!'
- }
- }
- },
- {
- opcode: 'inputPromptBlock',
- blockType: Scratch.BlockType.REPORTER,
- text: 'prompt [STRING]',
- hideFromPalette: hideLegacyBlocks,
- disableMonitor: true,
- arguments: {
- STRING: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'The code is 1, 1, 1.. err... 1!'
- }
- }
- },
- {
- opcode: 'confirmationBlock',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'confirm [STRING]',
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- STRING: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Are you the red spy?'
- }
- }
- },
-
- '---',
-
- {
- opcode: 'goToLink',
- blockType: Scratch.BlockType.COMMAND,
- text: 'open link [INPUT] in new tab',
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- INPUT: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
- },
- {
- opcode: 'redirectToLink',
- blockType: Scratch.BlockType.COMMAND,
- text: 'redirect to link [INPUT]',
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- INPUT: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
- },
-
- '---',
-
- {
- opcode: 'setClipboard',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set [STRING] to clipboard',
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- STRING: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'apple',
- }
- }
- },
- {
- opcode: 'readClipboard',
- blockType: Scratch.BlockType.REPORTER,
- text: 'clipboard',
- hideFromPalette: hideLegacyBlocks
- },
-
- '---',
-
- {
- opcode: 'isUserMobile',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'is mobile?'
- },
- {
- opcode: 'screenReporter',
- blockType: Scratch.BlockType.REPORTER,
- text: 'screen [DROPDOWN]',
- disableMonitor: true,
- arguments: {
- DROPDOWN: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'width',
- menu: 'screenReporterMenu'
- }
- }
- },
- {
- opcode: 'windowReporter',
- blockType: Scratch.BlockType.REPORTER,
- text: 'window [DROPDOWN]',
- disableMonitor: true,
- arguments: {
- DROPDOWN: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'width',
- menu: 'screenReporterMenu'
- }
- }
- },
- {
- opcode: 'osBrowserDetails',
- blockType: Scratch.BlockType.REPORTER,
- text: 'get [DROPDOWN] of user',
- disableMonitor: true,
- arguments: {
- DROPDOWN: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'operating system',
- menu: 'osBrowserMenu'
- }
- }
- },
- {
- opcode: 'projectURL',
- blockType: Scratch.BlockType.REPORTER,
- text: 'project URL',
- disableMonitor: true,
- },
-
- '---',
-
- {
- opcode: 'consoleLog',
- blockType: Scratch.BlockType.COMMAND,
- text: 'console [DROPDOWN] [INPUT]',
- disableMonitor: true,
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- DROPDOWN: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'log',
- menu: 'consoleLogMenu'
- },
- INPUT: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Apple'
- }
- }
- },
- {
- opcode: 'clearConsole',
- blockType: Scratch.BlockType.COMMAND,
- text: 'clear console',
- hideFromPalette: hideLegacyBlocks
- },
-
- '---',
-
- {
- opcode: 'commentHat',
- blockType: Scratch.BlockType.HAT,
- text: '// [STRING]',
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- STRING: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'comment',
- }
- }
- },
- {
- opcode: 'commentCommand',
- blockType: Scratch.BlockType.COMMAND,
- text: '// [STRING]',
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- STRING: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'comment',
- }
- }
- },
- {
- opcode: 'commentString',
- blockType: Scratch.BlockType.REPORTER,
- text: '// [INPUTA] [INPUTB]',
- disableMonitor: true,
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'comment'
- },
- INPUTB: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'input'
- }
- }
- },
- {
- opcode: 'commentBool',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '// [INPUTA] [INPUTB]',
- hideFromPalette: hideLegacyBlocks,
- arguments: {
- INPUTA: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'comment'
- },
- INPUTB: {
- type: Scratch.ArgumentType.BOOLEAN,
- }
- }
- },
-
- '---',
-
- {
- func: 'showLegacyBlocks',
- blockType: Scratch.BlockType.BUTTON,
- text: 'Show Legacy Blocks',
- hideFromPalette: !hideLegacyBlocks
- },
- {
- func: 'hideLegacyBlocks',
- blockType: Scratch.BlockType.BUTTON,
- text: 'Hide Legacy Blocks',
- hideFromPalette: hideLegacyBlocks
- },
- ],
- menus: {
- conversionMenu: {
- acceptReporters: true,
- items: ['base64', 'binary']
- },
- trueFalseMenu: {
- acceptReporters: true,
- items: ['true', 'false', 'random']
- },
- screenReporterMenu: {
- acceptReporters: true,
- items: ['width', 'height']
- },
- windowReporterMenu: {
- acceptReporters: true,
- items: ['width', 'height']
- },
- stringCheckMenu: {
- acceptReporters: true,
- items: ['text', 'number', 'uppercase', 'lowercase']
- },
- osBrowserMenu: {
- acceptReporters: true,
- items: ['operating system', 'browser']
- },
- consoleLogMenu: {
- acceptReporters: false,
- items: ['log', 'error', 'warn']
- },
- colorMenu: {
- acceptReporters: true,
- items: ['color', 'fisheye', 'whirl', 'pixelate', 'mosaic', 'brightness', 'ghost']
- },
- }
- };
- }
-
- showLegacyBlocks() {
- if (confirm('Are you sure you want to show legacy blocks? \n \n These blocks were removed because they were buggy or implemented better in other extensions.')) {
- hideLegacyBlocks = false;
- Scratch.vm.extensionManager.refreshBlocks();
- } else {
- //
- }
- }
-
- hideLegacyBlocks() {
- hideLegacyBlocks = true;
- Scratch.vm.extensionManager.refreshBlocks();
- }
-
- whenBooleanHat(args) {
- return args.INPUT;
- }
-
- whenKeyString(args, util) {
- return util.ioQuery('keyboard', 'getKeyIsDown', [args.KEY_OPTION]);
- }
-
- keyStringPressed(args, util) {
- return util.ioQuery('keyboard', 'getKeyIsDown', [args.KEY_OPTION]);
- }
-
- trueFalseBoolean(args) {
- if (args.TRUEFALSE === 'random') return Math.random() > 0.5;
- if (args.TRUEFALSE === 'true') return true;
- return false;
- }
-
- stringIf(args) {
- if (args.BOOLEAN) return args.INPUTA;
- return '';
- }
-
- stringIfElse(args) {
- if (args.BOOLEAN) return args.INPUTA;
- return args.INPUTB;
- }
-
- getEffectValue (args, util) {
- return util.target.effects[args.INPUT];
- }
-
- clonesBeingUsed(args, util) {
- return Scratch.vm.runtime._cloneCounter;
- }
-
- isClone(args, util) {
- return util.target.isOriginal ? false : true;
- }
-
- spriteClicked(args, util) {
- return (util.ioQuery('mouse', 'getIsDown') && util.target.isTouchingObject('_mouse_'));
- }
-
- lettersToOf(args) {
- var string = args.STRING.toString();
- var input1 = args.INPUTA - 1;
- var input2 = args.INPUTB;
- return string.slice(input1, input2);
- }
-
- replaceWords(args) {
- var input1 = args.INPUTA;
- var input2 = args.INPUTB;
- var string = args.STRING;
- return string.replace(input1, input2);
- }
-
- findIndexOfString (args) {
- var input1 = args.INPUTA;
- var input2 = args.INPUTB;
- if (input2.includes(input1)) return (input2.indexOf(input1) + 1);
- return '';
- }
-
- itemOfFromString (args, util) {
- var input1 = (args.INPUTA - 1);
- var input2 = String(args.INPUTB);
- var input3 = args.INPUTC;
- var output = input2.split(input3)[input1] || '';
- return output;
- }
-
- stringToUpperCase(args) {
- return args.STRING.toUpperCase();
- }
-
- stringToLowerCase(args) {
- return args.STRING.toLowerCase();
- }
-
- reverseString(args) {
- var input = args.STRING;
- var splitInput = input.split('');
- var reversedInput = splitInput.reverse();
- var joinedArray = reversedInput.join('');
- return joinedArray;
- }
-
- norBoolean(args) {
- return !(args.INPUTA || args.INPUTB);
- }
-
- xorBoolean(args) {
- return (args.INPUTA !== args.INPUTB);
- }
-
- xnorBoolean(args) {
- return (args.INPUTA === args.INPUTB);
- }
-
- nandBoolean(args) {
- return !(args.INPUTA && args.INPUTB);
- }
-
- stringReporter(args) {
- return args.STRING;
- }
-
- colourHex(args) {
- return args.COLOUR;
- }
-
- angleReporter(args) {
- return args.ANGLE;
- }
-
- matrixReporter(args) {
- return args.MATRIX;
- }
-
- noteReporter(args) {
- return args.NOTE;
- }
-
- newlineCharacter() {
- return '\n';
- }
-
- equalsExactly(args) {
- return args.ONE === args.TWO;
- }
-
- notEqualTo(args) {
- return (args.INPUTA != args.INPUTB);
- }
-
- moreThanEqual(args) {
- return (args.INPUTA >= args.INPUTB);
- }
-
- lessThanEqual(args) {
- return (args.INPUTA <= args.INPUTB);
- }
-
- stringCheckBoolean(args) {
- const input = args.INPUT;
- const dropdown = args.DROPDOWN;
- if (dropdown === 'text') return isNaN(input);
- if (dropdown === 'number') return !isNaN(input);
- if (dropdown === 'uppercase') return (input == input.toUpperCase());
- if (dropdown === 'lowercase') return (input == input.toLowerCase());
- return false;
- }
-
- encodeToBlock(args) {
- if (args.STRING === '') return '';
- if (args.DROPDOWN === 'base64') return btoa(args.STRING);
- if (args.DROPDOWN === 'binary') {
- return args.STRING.split('').map(function (char) {
- return char.charCodeAt(0).toString(2);
- }).join(' ');
- }
- return '';
- }
-
- decodeFromBlock(args) {
- if (args.STRING === '') return '';
- if (args.DROPDOWN === 'base64') return atob(args.STRING);
- if (args.DROPDOWN === 'binary') {
- var output = args.STRING.toString();
- return output.split(' ').map((x) => x = String.fromCharCode(parseInt(x, 2))).join('');
- }
- return '';
- }
-
- negativeReporter (args) {
- return (args.INPUT * -1);
- }
-
- exponentBlock(args) {
- return Math.pow(args.INPUTA, args.INPUTB);
- }
-
- rootBlock(args) {
- return Math.pow(args.INPUTB, 1 / args.INPUTA);
- }
-
- normaliseValue(args) {
- var input1 = args.INPUT;
- var input2 = Math.abs(input1);
- var output = (input1 / input2);
- if (isNaN(output)) return '0';
- return output;
- }
-
- clampNumber (args) {
- var input1 = args.INPUTA;
- var input2 = args.INPUTB;
- var input3 = args.INPUTC;
- return Math.min(Math.max(input1, input2), input3);
- }
-
- setVariableTo (args) {
- vars['variables'][args.INPUTA] = args.INPUTB;
- }
-
- changeVariableBy (args) {
- if (args.INPUTA in vars['variables']) {
- var prev = vars['variables'][args.INPUTA];
- var next = args.INPUTB;
- vars['variables'][args.INPUTA] = (prev + next);
- } else {
- vars['variables'][args.INPUTA] = args.INPUTB;
- }
- }
-
- getVariable (args) {
- if (args.INPUT in vars['variables']) return (vars['variables'][args.INPUT]);
- return '';
- }
-
- deleteVariable (args) {
- Reflect.deleteProperty(vars['variables'], args.INPUT);
- }
-
- deleteAllVariables () {
- Reflect.deleteProperty(vars, 'variables');
- vars['variables'] = {};
- }
-
- greenFlag(args, util) {
- util.runtime.greenFlag();
- }
-
- setUsername(args, util) {
- util.runtime.ioDevices.userData._username = args.INPUT;
- }
-
- setSpriteSVG (args, util) {
- try {
- Scratch.vm.runtime.renderer.updateSVGSkin(util.target.sprite.costumes[args.INPUTA - 1].skinId,args.INPUTB);
- } catch (error){
- return;
- }
- Scratch.vm.emitTargetsUpdate();
- }
-
- alertBlock(args) {
- alert(args.STRING);
- }
-
- inputPromptBlock(args) {
- return prompt(args.STRING);
- }
-
- confirmationBlock(args) {
- return confirm(args.STRING);
- }
-
- goToLink(args) {
- Scratch.openWindow(args.INPUT);
- }
-
- redirectToLink(args) {
- Scratch.redirect(args.INPUT);
- }
-
- setClipboard(args) {
- if (navigator.clipboard && navigator.clipboard.writeText) {
- navigator.clipboard.writeText(args.STRING);
- }
- }
-
- readClipboard(args) {
- if (navigator.clipboard && navigator.clipboard.readText) {
- return Scratch.canReadClipboard().then(allowed => {
- if (allowed) {
- return navigator.clipboard.readText();
- }
- return '';
- });
- }
- return '';
- }
-
- isUserMobile (args, util) {
- return navigator.userAgent.includes('Mobile');
- }
-
- screenReporter(args) {
- if (args.DROPDOWN === 'width') return screen.width;
- if (args.DROPDOWN === 'height') return screen.height;
- return '';
- }
-
- windowReporter(args) {
- if (args.DROPDOWN === 'width') return window.innerWidth;
- if (args.DROPDOWN === 'height') return window.innerHeight;
- return '';
- }
-
- osBrowserDetails(args) {
- var user = navigator.userAgent;
- if (args.DROPDOWN === 'operating system') {
- if (user.includes('Mac OS')) return 'macOS';
- if (user.includes('CrOS')) return 'ChromeOS';
- if (user.includes('Linux')) return 'Linux';
- if (user.includes('Windows')) return 'Windows';
- if (user.includes('iPad')) return 'iOS';
- if (user.includes('iPod')) return 'iOS';
- if (user.includes('iPhone')) return 'iOS';
- if (user.includes('Android')) return 'Android';
- return 'Other';
- }
- if (args.DROPDOWN === 'browser') {
- if (user.includes('Chrome')) return 'Chrome';
- if (user.includes('MSIE')) return 'Internet Explorer';
- if (user.includes('Firefox')) return 'Firefox';
- if (user.includes('Safari')) return 'Safari';
- return 'Other';
- }
- }
-
- projectURL() {
- return window.location.href;
- }
-
- consoleLog(args) {
- if (args.DROPDOWN === 'log') {
- console.log(args.INPUT);
- } else if (args.DROPDOWN === 'error') {
- console.error(args.INPUT);
- } else if (args.DROPDOWN === 'warn') {
- console.warn(args.INPUT);
- }
- }
-
- clearConsole() {
- console.clear();
- }
-
- commentHat () {
- // no-op
- }
-
- commentCommand () {
- // no-op
- }
-
- commentString (args) {
- return args.INPUT;
- }
-
- commentBool (args) {
- return args.INPUT || false;
- }
- }
- Scratch.extensions.register(new LMSUtils());
-})(Scratch);
+// Name: Lily's Toolbox
+// ID: lmsutilsblocks
+// Description: Previously called LMS Utilities.
+// By: LilyMakesThings
+
+(function (Scratch) {
+ "use strict";
+ const menuIconURI =
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAV4AAAFeCAYAAADNK3caAAAABmJLR0QA/wD/AP+gvaeTAAAv2ElEQVR42u2dB5hUVdKGe3LOuSeCYEJRQWVd+REQQQUDigkMqIioKOquomDAjBgQsyCiIIqomLNgACWNpLG7B0YEYc2JYABB6q+60+AAEzrd0/fc+9XzvA8rq9N9z9T5+nSdCi4XDKbQ2tRRUmUttSrz0KHMMcyZZV4aVuqlm5mHyr30HP/dLP67aj/L+Z9X8p9fMr/4+Y0hP781+Psv/f/u8h3/vY9m8s+dJj9bXkNey3jN+tc+VN6LvCf8ZmAwmLbWsZoSKlZQ6woPdS710CksdMNZ4B5jQX3PL4pbG4imlRDhri730XT+czQL9mC3j46r8FFHdzWl4jcLg8EsYeW15HZ7qYdxivTSZP8pc7NFhTUctvo/NF7jP0fJBwp/sLRzEcXCC2AwmDlGFCNCw8IziL+uP1zuoTn8v9fZUGCDZR2vx2xZE1kbXqN9Za3gMDAYLLRwAX/FlpOs/6v3jxDZgNngD6uMMr4NrKUUeBQMBtvNRBxYKI5mwbiXhWMe8xcENGLIWs7l8MTd/CHWC0IMgznY5AJMLpD8J9oNEEhl/GGciPnSUb5VwBNhMJufajkW2Zs3/IPMFxBAy1DH3zYe4D+PxWkYBrOBSX6qpET5Mw7WQ+SsfxqWzAn+gDwbKWwwmE5GFCc5tJI7i8wDvTMm5ANTPjjbeSgRjg2DWU9sY/jy5shSHz3hLwSAcNmLn5mJpbXUDelqMFiUrcpDxUZlGGK2TosJj3LXUDl2AAym7nQbK/mh/mwEpH05l62SHSFVdK4PKB4bAwYzwaQ8l9O/buAN9xVEB+zCavGNSi+VYKfAYBGwslpq778o+xMCA1pgs5HB4qP9sXNgsBDMyEyQpixe2gZBAcEi/TQkIwKXcTBYCyZpQ0YrRR8tgHiACLFUKhSrVlEydhgM1sCk0IHDCZfxJvkaQgFMwUNrWYCHouk7zPEmXcCkSsk/XQHiAFSwRrrOQYBhzjNOCfNPZ6iDEIAoZkIMRioazEmCuxwbH1iELw0B5lJzbFCY7cw/IqcGGx1Y9RKODwXdsVNhtjC+1GjjrzLD5gY68Fp5De2BnQvT0oqWUprU1LMjb8JmBtpNzfDROL6Ay8ROhukTx+VMBeY7bGCgOTJrbxjivzBLG/dTOJgddRE2LLAZC/mOogN2OMxacVwZFuml0dIxCpsU2JQtEn4o8FA6djws+qdcL3VBehhwUAXcygovHYWdD4uKVS2mbH/XMDSxAc5rwsOZOjzxJA9KAFN5yj0Nl2cA0LdGI3YYzEyT9Bp2tinYcADsfPqtWEY5UAhYxI2/Vh0m8S1sNAAa5Su574BSwCJj3ETEXwiBjAUAmudvyXzAOHpYWFZZS63YmT7BhgIgCOob+beFgsCCNnac85nfsJEACImNHPsdCCWBBXqBlsROMwEbB4CIMFkKjKAssCbNXUPl7CjzsVkAiCiLJGwHhYHtHlrwUVfme2wSAEzhJw499ITSwOqNx1+zUwxH1gIAprNVMoSkgx+Ex9nxXCmIeAkbAgCl/R5eyK+lDCiQE0MLy6mUnWAxNgIAUaGmxEuVUCIniW4ttedP3bVwfgCiyjcVPuoIRXKAcYC/F//CN8DpAbAEv7l9dByUyd6ZC4ONhs5wdgCsdenmo6FQKDtmLtT3W4CTA2DdUuNxyHiwT+aCVKLNgGMDoIX4Pi97FsqlsbmrKZV/me/AoQHQKt1sFma7aWryi+NPz5lwZAC05GPJs4eSaWTSDZ9/cXPhvABoTTXmumliRUupkE+6S+C0ANgCT6WXSqBsFjb5BfEv6nM4KwC2orZ0KZVB4Sxo8ovBTDQAbHvhtlLK/KF0FrLiOirgceteOCgAtmZFlYeKoXgWsKrFlM3TTT+DUwLgCJbx6TcXyhfd4ohM/2A9OCQAzmEe2kpGtzjiQzghAM6j3ENzOIMpDUqo0Np5KJEX/004IADOhe913kV5sSrjJhrovQAAME6+PpqOxjoKjC/SxsLhAAANGANlNNH8/XThaACAXbuaoZ+vKaLrpWPL0MQcANA4W/nC7XgoZSQzGLzUgRd2I5wLANAMG90eOgiKGYmTrkwDxmBKAEBgfMPiWwHlDLdAgsdAw5kAAEGwFAUWoaeNyZy0F+BEAIAQeFk0BEIapJV6aAScBwAQBldDSYMT3SPllhKOAwAIg7+5wKIXFDWQDAYOjPOC/QinAQBEgJ8ra6kVlLUZq1pFybxQC+EsAIAIsqhsLaVAYZsukpgIJwEAmMB4KGzjons+nAMAYGI3s7OhtA0b39TQHrwwG+AcAAAzK9uYtlBcsQ8onhdjHpwCAKCgmc6CjtWUgNOuj26DQwAAlOGhUY4W3QoPdY5ovm7NVip+u47yx79NOTeNp6wrR1PmRddT5rBbKfu6Byl/wjvknvszHA8Ah+f38sn3CEeKbuuVlMULsDqsYPnSzVTwxPuUcd5VlNjhcIpJSSMpNm6W2FhK6tSd8sa9CAcEwLmsqVhGOU7MYngm1EUrfnM5pQ8YSrFZuS0LbTMkH96T3PN+gRMC4EymOK0k+NRQFso9fx2lnXIBueLiwxLchiTsub/xc+GEADjysu1kZ5x0PZTL+XTfBX3Kfd1Hce7KiAluQ1L7DIADAuBMvnVEyIGHVT4Z7OKUfPQNxeUXmyK6BjExxqUcnBAARzLB3iGGWurGD7kt2IVJOeok80TXT+aQ6+CAADiTbTxerIc9u45VUyo/4BdBn3bfX21kIpgtvBLGgAMC4FhW2LKRDl+o3RPKgmTf8LDporsdhBsAcHRhxR32iut+ToeEWiiR0vNkZcKbe8dTcD4AnMsWmWhuD9Ulii2TfpghLkbCvh2UCW/aqRfC+QBwNvNFs/RPH/PRBeEsRHz5HsqEN2Gv9nA8AJzePtJH52gtujJiWfLkwlmExAMPUya8UphRumQTnA8AJwsv1xm0qaNMnU+7d4W7CKl9+qsTXqZoxmI4HwAOR7om6tzcfFO4C5B54Uilwps7ejIcDwCwibMc2ugnvB56NRILkHPL40qFVzqdwekAACy8L+iWs9s9Ug9fMOVjpcKb3LkXHA4AUN+YS5uKNqI4fsM1EXvw+b8avRRUCW9cQQkcDgCwncVapJfx8fzMSD98XEmF0lOve873cDgAQH2WA7ex1eG0WxvpB0/u2kep8BZMmgmHAwBsZ7kM5bXyhdp5Zjx4xuARSoU3+5qxcDYAQMOLtjMtKboyMpnf3EozHjr37meVCm/aSefB0QAADamz5KmX4yBDzHrootc8SoU3cb+D4WgAgJ2LKvgbvaVEl8vrkviNrTHtoWu2UExSsjLhjUlOMUbFw9kAAA1Y3c5DiVbKZLjM7IdO2OcgpademfEGRwMA7JLhMMQSoiufAPyG/mf2A6eeeI5S4c0bOx2OBgDYla/kPiv6mQw+GqjigbOuulup8GZedD2cDACwOz7qb4UOZEtUPGz+4+8qFd6U7ifAwQAAjQnvEq5ZiInmabeXqoctmf2dUuGNL2sFBwMANBXr7R69FDIvvavyYWNzC9WJL/eHcC9YDycDADTGG9EKMezPL75N5cMmHdZD6am3cOoncDAAQGNsK6ul9urDDF56SvXDZgy8Uqnw5tz4CBwMANAUE9WKbi25+UU3q37QnNsmKRXe9DMuhnMBAJpiU6WXSlSGGW6MxoMWvfCZUuFN6tAZzgUAaLqM2EvXqWr9GCulc1G5SeQJwDIJWJXwxmZkcVeibXAwAEDjmuSlVUoapXN58DHRfND41vsoPfWWvL8aDgYAaJIKLx1lvvB6aUY0HzL1mNOUCm/+Q6/CuQAATYcbfDTdVNGt8lAxv9Bf0XzIzGG3qi0d5teDcwEAmmFz0VIqNK9gwkfXRvsh5QSqUnjlhA3HAgA0e+r10H/NulSL4Reoi/YDSsxVaekwx5ThWACAFlhuSv8Grk0+0hIPyFkGsRnZ6sQ3Lo5KF/0BxwIAtJRa1sUM4Z1klQeU/FqVp17JH4ZjAQCaPxTSY5EfZOmln63ygFJRprR0mCvm4FgAgBb4MaJN0vlSrY+VHlB6KKgUXukRAacCAASQWtYrkrm7U6z0cNI1TGnpMHdFg1MBAJQ1zvFPEF5nqTK9hRuMfrnKSodzC+BQAIBA+DUik4j5pu5EKz6gTIhQWjrMEzDgVE3w+d9U9PIyyh0zlWfj3UVZV46mzKE3UfaIccbfFT43n9xzf8Y6Aaf0b+gdiTDDM1Z8OJmJprR0mGe+wal2xv3pT5Rx/tUBTwaJr9qT0voNotzRk6nk42+xhsCuaWVPhSe6aymFf9BGKz6cTAFWKbwy5RhO9Q8FT35ghGBCXc+Y+ARK6XWK8XOwnsBmrJMQrW2yGRqSN3a62tLhE8+BQ/kpmrGYYpJTIra2ie07GT8TawuQ3VAfZnjQqg9W/EatUuFN2OcgOJTEr7iKL2GPfU2oEIynjEHDqaxmK9YZ2CHcMDYc4f3Csg/HGzQmOVWZ8MYkJvFrbnG8Q2Vedoup65zW91xsXGAHakMNM+xp9YdL3O9gtaXDr3mcfdpd/GfAF2lhVQrePAEbF2hPxQpqHcppd5jVHyztpPOUCm/u3c86+0LtifeVjVySjAlsXqB5uOGiUAZavmX1B8u+Zqza0uHBI5wdZrj0ZnUTns+5ApsX6C28Hno1lDSy3y1/Aps0U6nwJnft42hHSu7cS11MPSGRit/5AhsY6MxvQaWVRXugZTAJ/CqFN66kwtHVaRICUJrC12cANi/Q+16E+5gH03v3Hl0eLK7QrVQM3PN/dWbuLl8sqlxng9hYKnpxETYw0JnRwcR3F+Drb+MUTPnYkQ4kPReUC690hju0KzYv0DnOOyeY+O5mXR4s47yrlApB9nUPOtKBMgZfGxXhNfpkTHjHfmvKI6xKPvrGqNgrfL7aoOSD/6GAxH5sqlpFyYGcdrtqdRLjhisqRSDt1AudebHGF4vREt6EvQ80Yszax/uWbqbcO6dQcpdjm54byBV8ccXllNjhcEo7bQjl3DqRit+ug4Dp3Eyqlg4PJL47QqvY40tLlIpA4oGHOdJ55GIxWsJr5FCzYOm8ftIaM77VXqFPu65oY6QzFr+1AmKmH1cHUjjxhm6nCOl0pSzNKS3D+Jqo+jlLZq2l/PFvU9Z/x1DayedT0iFHUHx5a4rNzDFOT3H5xUYln8yjK5j8UWQ/sflCUWXj+UaFp7SKSpds0nLj5T3wUuTK2/nCMfWY05Bqp5NG+eiV5lWX58KXWWioZaAktN1PqQgUv7PSvK8l834xLvBkrlz66RdRUsf/M8Q16Eupfx1ZHzOMRL40v59oiu6O1pxX36PdpjOKfFgsI34ASE2n3DuegrDpwY+irU3qboWH2un4YKl9+isVADnBRKLvgYyNl80jF4SSnRFXVBbZUyJP6YjE5Ay5ULSC8MZm5eqTzseXZOkDLjV3TfhbiHxAQ9g0iPMup72bu1i7QMeHklEzKgVAxtoEswGlhWXefc9T5iU3UkrPk41JDK64OCXvNaVnv/B7YvCFohWE1yjbltaRVv9q+dlvlNLteGUVfnLPAXGzPOc3N1/tER0fKv/RN5Ru/qbEzAgTPD2bsq+9j8VqsHEzrbJ1ZVOnIhH+sLrA8YWiVYRXGrCXzFpjWV+UkUaqu+bJJA8Im9XTB+mBpoWXk311fCi5eFJ60cM3zHKBlX39Q0bKT1KHzk2nB1mA9DMvCyvfNDY901LPk9Z3oGWr++QSUPmHUVKy0aAeAmdpPmruYm29rg8m8T+rCl/UY6P8oRBqE3e5PbfcM0kpscVGBUnDpmh++MrrQ9wsza+NXrBV1lIrnR9M0qsgsk1TOH1haKlQD75syeeRIgTLFPHc/qQRa43memQPvxfiZvULthoqbyzMcILOD2X6DbLmZF11V2g9ePki0arPJI3Zo132Kxem0c5xNi58LxwJcbP6pauXejd2sXa9zg8l42IgsM1cwHQ/IaR1lYtEqz5T4n6HRKWYZXvhTuoJZ1tmLTIvuh7iZnV8dM3uwuuj6VqXZE6bB4Ftrt8BF5mEsq7hlLkqKSW+Z5r6r4ycS5zUqZul1iF75AMQNutnNkxtrFS4VutjPOdOmlEhZKsLtmDXlG/KVeUbh1MkorKUuOTDrylhrwMstw75D70KYbM+NTuJbjsPJfJfbtX9weIr20Jkm6G0emNw3yKmL9DiubJH3q8mXeyVGqNzmBXXoPjN5RA26/OX6wOKbzjqp40dHizlqJMgsBGcniEtCbU4zecWkHvBenPTxSa+Z7l85obVa6XL/oKw6VBz4KXKHcLr9lIPOzyUccMMgW36xBvk5kw/a5g2z5Y55DpTez6r7IAXdPx+r/YQNX0u2Lpq36Nht5zTcS9CYJs5FQWdG92puz7PZ5QSr418dzEu/7ZCulizQ0GPOxOCpssoIB8NbJjRcJsdHko69UNkmzgV7dsh+GpA/gqv0zNKf4yIdhfjtpxa5Gj/506Imj6ZDTc1jPFOtcVD8XgY6VMKoW1kc15+e9C399o9J2dgSL+ESGTIJHc7Tpvnzh//FgRNlxOvl55qmEr2iV0eLPGAf0FoG5ne4J6/LriObzztQstCkSNPDC9H99OfKPGgf2v1zPIhCVHTho8bCu83dnmwtFMugNg2IK6ghPu1Lg2+xzGPGNL1maU1Z2gNgVbW90rWKT+bm0NBzLQKNaw1RFdGD/NfbLPLg+XcNB6Cu/30x+l1oY7/ST3+LG2fO7F9p6BLiWUYZWxuoXbPKhV0EDSt+LtNHSW5KlZQazs9WNHrXmdnL3Bv1pSjT6XCZz4Nb46dBauzghrPdP+MoDqwRb1hfTR6LYOoUOWjKrlYO9ReR3lu3J2T7xyh5fzSxHYdKb3/JZQ3djqVLtwQgRv9LRSTmKR3XJtDBoHkLefc8jhfysVr+5zSHApiphcVPuoownuM3R5MOnHZNV6bdFgP45QjQzILn682hmaaURprh/XKGfVoyy0dde+zzCESiJl2ubw9Xdwj8iy7PZhxitG5oQ2Pc5d5bZKXKgn8clkUkZNsoJVaY6baozEQx2wbWzejpSMXHWj/jNwUSlLfIGbaVa/1F+G93Had3rlu36p19TuFCdIyjPQ3ycTIHjHOGN/invND1Ncv44JrbPMtIfPSm3cWXW4UlNz5aHukCXJTKAiZlpkNl4rw3mLHh7PS10gp15V6+tQ+/Snrijso/+HXqPjdL6PWxLslko/obZ8YOBfUlMz+7p+WjnsfaJ+slZ4nQ8T0FN5R2o50b7H6iHu0SvNvu8ZhzcSqrQ9DvvkfMJSKXv2c4koqbPVcmZeMgojpyYPaT55oNiH+rRWmiUhcfnG9wJ5zhRFTlt61doi3SetIqzeFCSXzI5oTgE1LmeOmUBAxLWevPStZDbNs3f+SCwhSevQN/YKGY8VSQmpcdPF4lYJJs4yyUruuV8GUj5V9M0CRS5jNz/lgASHTsl/De9IScoETHrbopSXG6VQGJDaWoyo34FLxJLfdWVeOpvxHXqfi91Y5zinkw0XV12SIZxin+JQ0oykUhExL5orwLnHkcZ/TjKR/q3veL+je37DXBZ/sleSfcmhG9+q4qJdFw191ZZGEGnxYCKC6u5t7zveUfc1YiGiovYc5BRH+qi2fi/CuxEKA7dVcklusop+EvJZ821DxenZE8r7hs9pSJ8K7FgsB6tsifqGsj8KOYo3zroKQhtL68qkP4bP68pX04v0BCwGMLl0PvKRENJL/fdQ/WSdc3CDz0iCmQYZqbJxZ44B0su9EeNdhMYBR7Tf0JjXxyX6Ddr7QO20IxDSYVLyiMvir3vwiwvsHFgIYXd169otK/wQpn9a5NaNqkrscC3/Vm99FeLdiIYAQ32ovJcKRe/uTu0+8sEO3MEVknH81/FVvtkJ4QX3cadEfxpReJRdDT36w+8Uen3qlmRCENYAPrjFPw2dtILwINQAuaFiortSVh0o2WryhaaxXOp6p/NAIZXgpsF6oAZdrgHJunaiueXcTlYLStlG3DIe4olLKf/QNpS1GpZE7fFb3yzUffY+FAOlnX26JG3mJX+o0Wr3o5WVGzFrZ6XrP/eGvtkgnQwEFYJI6dVfTY4A7vTXblpKr2WT0kQ7jmSQ8Y3xo8aBRVa8rl5DwVzsUUKBkGDCxuQVqhKP3GS3nE3O6mbVnuRXsFGeVhjWqXjvrP3fCX+1QMszHXi8WwtmUfPSNulQonufW4lcxnosWl1dk2QGaEl7Y8V45Xq0yLp0//i34rC2a5Di0LST4h/zxb6tr7nLDw4H1Bebpypa7SOMPg6JXanbu8/ziIqXvQRr7w2ft0BbSIY3QQdNkXXWXuhMbN5gP6AKCZ+bFuSutI7r8XorfXL57NgiPfVJ5mQd/tQVGI/SZWAhnk3r8WepyUHnoZMApbrc9YY0x6q335qb5axrPPT71QmXvI6lTN/irPbIa3pUpw89hMZyNykkQ7gXrA39vNVtZ9PaJ7qSHdh25afsPTb5H+f+VTUvmydXwV1vwjFyuPYyFcDA1WxqdQWdWClbQrSrvez5qopt0yBHNflBIIYOqtRNybp4Af7UDHnpAhPdmLIZzkcsilaW1wTvpNqXpWjs6gHU7zuhf0XyZ9QKl76nwufmW9iX3/F+NcvBdMb4x8O8R+22H8I6SkuFhWAznknv3s8qEI6Xb8aFlXUx4R6nApZ54jvFNoMUY9I2PqHtfUmr92W9R/3Yk07pzbptEGYNHUMrRp1LCvh3qc8BbarDE71/+vYR9DqKUXqcY/33unVOo+I1a54myj4ZKAcWZECDnkjH4WnUxygGXWr6yTqrQAh2bnnby+eou+Crbqr8E4hzlgkkzKePc/1Bih8M5XznVnBBURhal9Ohr9AtxwmSNch+dIcJ7NATIuSR37aOu6orT1kIOibzwmekdwHZt0N7ipSSHTpR9WzjqJGVim/fgy8apNDY9U3mIR4pRJFOk+K0Vtt1zFV46ylX+OR0CAXIucSUVyjZV3tjpYb1X04oq+Gtw9nUPBt2/OCY+QdnaZV5yo7nVizO/MhoUxeUXWyN3mkMXqX0GUNFrHtvtObeXOrgqa6kVBMiZyGWIKyZG3eXQtHlhXkpsi3hDGvn6nHf/jOD7Fz87V6kQ5Y170RzB5fzk9NMvsm4Tev5QTD329GZT+rQr0fdSpatqFSXzP/wNIXIeBZM/UlvuyhOFI1Jpd+XoyMQWs/Oo8Ok5oZ2+R96vdO2K366LbEhh8Z+UOeQ6pelw4ZZrB1r1aHH+blNHSS4x/oevIUTOQ75eK4vdJSVH9PY6965n+FImO/R0scN7UvF7q0Kv9uPMB2Vrl5IW8IVfQB+4k2YZl3XaTfvgb2fBxuEtyBrXduN/+ARC5DxUlruacSsvXdUk9CCiHljcMJ5SjjzRSE8Lu9qv7X7qquf2PzRC+aPbKPOyW4yv7zrPnEs/a5jO++6jf4TXR09DiJyHNCVXVgV2WA8TY9XrjNxS6TmRsMe+xglxe3wwvrTKyNyQ0ICMForI13TOp1U1GFRI6zco/Pe8cIORR22XgZ85N43XM5XMS0/tEF6uXrsVQuTA5ucKJz2knXSefWLjT89WKjLZI8aF/cGUeOBhtpq0LJeiWrbIlKq1BqGGQRAip2U0rFO6UTKH3mSf2Pjwe5WuXcFTH4b+e+ZRSon7HWLLMfdZV9yhY/HEwB3Cy3llPSBGDuvRwAUJKjeJDIS0TRtNzi9VuXYhV3Nxia+qir9okNzlWB3LhY/YIbzlNbQHxMhZ5N33gtpTG9+k22Xt4lvtpXB8fFnI7zN9wFDbiq5xb9Chs37fND1UsUN4O1ZTAv/lFgiSc8i66m61eajvfGGPEA23iVSZFZD8f8eElm7HDWjsLLrhNF2KIptdXCzqamgYegnhNe0iJC3DaGpui4s1PrmrFJeMQcODT7PjSyeVF6fRIuPc/+rmP0tduxoL7zQIkoPaQY6ebItUMvUfWHepjY3zyTXoxkd8Sra76Bpl1A+9opv/TNlNeDm/bCQEyTmUvL/aNg1elF6sHXOaUnFpOEo+oNg9i5ETRFe6mJUu+l03/7l6N+F1++g4CJKzSGjTTs1k4QhUilnmYq18D3Xiwo1rZLxQ4FkMW5X9TqMe3+Um7Brm8B6zm/BKxxyIkcN6NSho9CInk6AGXFr5Yo1zYlV2c0vYc3/Lho+iTcGUj/XzoeVUupvw8m1bDP+fv0KQnEPpkk2mN/M2JjrYpWJt4ntqRxBxvnBQJeAKJx5Hk5Bm90Wfn11NGV+wzYYgOQvp0BVf3tqckS7cdjFS/REscbHGlVJKK7O4/WXA/YGnfuKY027eAy/p6D8fNie8D0GMHHjRxn1yk4/oHfEWfvkPv2ardUrpebJSgcl/7M3AiyW4mbkTRDfxgH/pORzTR/c3Kbzo2eBspNF0REapyyid6x+y3fqovFgzGsfPWhtwu0epcLO98HJbz6IXF+nZlcxD5zUpvCU1tA8ECBS9tNSYPiyjuIO9TErY6wBjsoXt1oX7HqicsRablRv474vFyAmn3cyLb9D3PsVHezYpvP4Ltp8hPqBhFzO5VJLCgbTThhjJ+Yn7HWzEhQURZyndlFzdwmc+tXUsXGkfgkOOCDw75YaHoz4ZIr5qTyNclXbKBcbYe2lgIzH+iJVOd+6lc/Xjj6KtruaM/6XXITgA7FoqPFPtlIUzLwv4vYnYRatJjVTWuef+3GRecc6tEymu0B1eXLfD4UbzeY395yVXS8ZH4mux0QCIbtOZYCYsqG5yLhM+ZOZdwF+zWTQzL7reaF4efEpdfx0r1HaN7/63ReHl0uEu2GgA7CK8Y6YqFbfC6Quteekn2SpBZFvs2rwn6/LbjXuAFmf0cevNvPtn2CNf3keHtSi8ZWspRdqXYbMB0EB475mmtg/Bsr8Cfm+xGVnq2lTydOaIpDDO/Mr4MMsYeCWlHnu68XPlTxk5b0zc0DFlrHE27Rjn3qL4emkeNhsADVLtxr+tLnZ6aNeg3pvKbAsJGcAfguJjV6BW6qG7sWAA7FxeHZueqWa45TVjgxPeEGKnIV/68VQL+ENQjXHuCFh4+V8+GosGwC4tIY8703xx4+KTkllrgnpfKpuexxWUGCmG8IcAP7A91D1g4a1aRcn8H/2OhQOgQS7v23UUk5pubrvDnv2Cr6jjHFqlaWSduoU+gNNZ/BZwfLdBnPdNLBwAu8Z63zJPfOPiqGjG4qDfU3Lno5Xn8EplXeYlo6j4jVr4RdO87ArWONxwGRYOgMZKqpdQfEUbS8xXMxrknHFxVCvX4vKK6rMSTjzHmIMmzyFkXX6b0WGtITmjHqW8e5+j/MffpcLnq6n43S+ptHqjXcMMQ4IXXi+1xSYDoOnLNhGSSF24JXftE9y0iQbk3PaE9n0YJE5tlJ8fdZKRYiZN+iWTROeQRsUKau0Kxfg/rsMmA6AZAV64gYVvktFbIaTpFPzfSA+M0sV/hh57fmuFrZvjxFe2NZrCixgXTptnNCyyvF/w1HZXqMbhhgewuQAIsKEQ9yyQ1pqZF47kC6ju9a0aOW7b6AmPix5kcGbh03Mi8tpx+cWOaYQem5Nv9B8ueHq2dcuEvXRvyMLLqt0bGwqA8NpJSpWWxDMFmRos/xzpyiwZ/ugU4d01wyKY8mplwuujniELrz+tbD02EADWJu++FxwpvDvCNf0GcTOdP6zy+/g16DSy3cINPnoajg2AxWOKfDEXyf63WsaCW+9DRa97rZDNMMkVrnFLs+Ph2ABYH6fMXWs2/puRXX8BF90y4WPCFt52HkrkH/YLHBsAq+cXLzVKjiG+WdGM+/4imumKhPEN3ZNwbAA06CfBjcOdLrzb84LlIjMKv4MJrkgZ/7Bj4dQAaNBPgivBYhISIb4S8+XqQveC9WpTCr3UI2LC27GaEviH/gTHBkCDWO/Zl0N4t48O4o5yCtf+R9cHFO+KpPEPfRxODYAGGQ5cBZewV3sIr59g5sOFWTTxiCvSVlpL3eDUAGgScuAyYrNbWOpCnLvS6K1hem8GD3WOuPDKXHj+4Svg1ADogZz0kOXgn+px7X1mr3etaKTLDONiimvg0ADoQ86ox0Jr3mO3LIfcAmPMvIklwle6zLJWNVTEL/IXHBoAfZAZbhBfjvWOnmzWGm8urqMCl5nGL/IinBkAzXo5jJ1OMSlpjhbe5H8fZdal2nMusw2DMAHQtbKNJ2eUt3au+BqDRNdaO3e3mUu2WH6x1XBkADRMNVv0O2UMHuHYIgsTLtm+FE10qTC+ZLsBTgyAxulmby6ntL4DKSYp2VHCm9Lz5EiHGUa6VFl5LbkloAwHBkBvZGpG1tX3UNLBXSgmPsH+Ob08mDOCTeg3VXmo2KXSpOckHBcAG4UhON2qYOJ7xoRg6W1rV/GV4hLLNcQJomPZfvzC2+CwANi34Y5kQ2RccI0xvl1yYW2RVjZmaiTWZxtXqu3rioZxrPctOCgAzqFk1hrKf+xNyr7hYeNknNr7DErscDgP9izV5sIu6/LbIrEWr7miZZJGAWcEAOyAh3u65/1CJe+vNsbwFD43n/Iff9c4OefcNJ4yL72ZUnqdYsRaoyW8aacODv85fdTVFU3jN7EIDgcACIrP/6aCKR9TSvcT1BdSdO4V7vuvdkXbeAT8WXAkAEDI/SRufESp8CbsdUC4KWSnR114jSbpHloLBwJA46q21zxGT4f0My+j9HOuMP534dRPOHSwVcnrx5fvoU542+4XzntdHfFm52Fcsg2F8wKgXzw29/YnjRE5zXX1kokWErM1870kHdpVnfC2aRdObHewyyomUzU55LAKzgyAJqlib9cZX7kDFqy4eCODoWjGYlPeT+JB/1YnvHvsG/JpN2IThCN46h0MhwbA+kiRRGxGdsiNZtL6DSL3pz9FrnBj6WZjKrAy4d23Q2ixXR8NdFnN/LHelXBsAKxL4fSFERkJFJudZzRZl+yEcN9T7p1T1PZr6NE3lPe5wjKx3d2q2fgTAc4NgFWLH9ZGPH82sX0nKnr187Cq40TEVQqvXB6GENvt77KsEcXxqdcHJwfAYnBjGCn7NUPIpGIt8+IbjMKJYN5T/oR3+PKuUH1ryBHjgl0/j7LWj2HEevvD0QGwFlLia7agyYSL9DMu5obrS5uO5/K037wHXjLtQyAQCp6eHVwM2kP9XJa3+kbpS+HsAFin2Y3qsT9x+cWU2mcAZV11lyH6mUOuM0bvxKRlRLVPg8S3S5f9FUyxxGemTQ+OeDVbLXWDwwNgDVKPPwsj3reXCx/RO9gqtS4unYzf9Aw4PQBRPu2+Ucs5uHEQ3RDiu1ybMM2lm1WsoNb85v+E8wOA064ViElMIvcnPwa6dn9U+ajKpaPxmx8N5wcgOpQu3EAxySkQXT8Scw7itHuzS1fLr6UMfohvsAkAUI80LIfghpTN8L+ipZTm0tnKPXQuNgEA6sm66m4Irp+kTt2DyHmmAS7trT69bCE2AgCKhZdH3EB06/tLBNHcZ6426WMtppd9TgfwA/2FzQCAOvLuex6iK6N++p4b6JptcXvoIJedjB9qDDYDAOpwL1ivzQBK0wo5isoCzmTgC7VbXXazsrWUwg9Xhw0BgMJ0smNPd276WHxC/RSNwNZqedUqSnbZ0WQyJz/gNmwIABQVULyz0shfdaLwZl19T6DrtI37MXR32dn4ISdiQwCg8JLtP3c6TnQzBo8IpsH5oy67W+uVlMUP+zU2BAAKQw59+jtGdIPst/ttxTLKcTnBpM0aNgMACqvYFv1ByV372Ft0Y2Ioc9itwa5NX5eTjJOUp2JDAKAQHtWe1negPS/SkpIpd8zUYDuPPeVymknIAZOJAVAMz0iT+KecDu0iuvGt9+bm60uCXYsv29RRpsuJ5q6lw3kBtmJDAKCW3HumcQOdVFsUR5R+9luwz7+l1EeHuZxsfOq9BRsBAPVIGW18eWs9CyNKKij/oVdDe3Yf3ehyvPHIZKmPxkYAIBqXbr9TxqDhRrGBFrFcrsSTrIXS6o0hPS837Zpj2THtqs3fNH0DNgIAUTr98oj2lCNPtG7slxvdSEqcFISE8Zwb+VK/DRS3gXES80BsAACiLMA8ITil1ymWGRck2Qpp/QYZHwxhP58t2j2aU9U2Ac4PQPQpmbWGMi++gZvMlEZFcBP23J+y/juG3HO+j0xIxUsPQ2GbME7vSOLA9wI4PgBWyf3dYlxiSbMdU0fE8wk78YB/UebQm0JJDWuJeaItUNjmUsxqqJwX6gc4PQDWq34rmDSTMi+6npI7H220XAw1XhvnrjR+hpyq8x95ndxzfzbnffvo+9KlVAZlDaykuLvk2sHZAbB4z9/566jolRpDPHNuedwYn5515WhDUOVPIXvk/ZR71zNU8MT7VPS6l0qXbFL1/rZIR0QoanDx3uFwbABAqPCF/ZVQ0mCN5x7xwk2HAwEAQmCGbWanqTb/ePhlcCIAQBBx3SUFHkqHgoaT31tLbs6/WwuHAgAEwNdyQQ/ljESmA0/+NKpO4FQAgKbZwBfzB0IxI3nZ5qFjkOkAAGiCrW4fHQelNEN8fXQBHAwAsFsGg5cuhkKam+N7DxwNANCA0VBG89PMYvnk+zycDQDAPRimiSZAGBVYOw8l8qK/DscDwNG8gx4MquO9aymFF/4DOB8ADozpckPzoqWUBiWMgsnC81eN2XBEABzFPCmuggJG0WRaMd9ofgZnBMARLOXU0lwonwWsuI4K+BfigVMCYGuWV3moGIpnpTQz7rnJn4Qr4ZwA2JIvypZTKZTOgiafhvwLqoGTAmAjPORDM3PrX7gV8i9rMRwWAFuwWEKJUDYdTr6LKZt/YZ/CaQHQmoWlPsqDommWasbZDu/BeQHQko+QMqapSVULf2K+AicGQCvelAIpKJjGJuXFGCEEgD69F2TPQrnsYDx7iW9GR8GxAbAwPhqHhjc2NP7lDmL+gpMDYCm2op+uzY3DDj35F70ezg6AJdjI4YXeUCYnnHx9tD//wtfA6QGIKl+7vdQBiuSkk69ML/bSIjg/AFFhGQ+xrYASOdAKPJSOjAcAlPOydBWEAjk948FLw3DpBoD5l2jMcNlzEB5YfejBS13YKb7F5gDAFH7keG4PKA1s90s3bjvHDjIXmwSAiFJd4qVKKAysSZMyY0nkxmYBICItHR9DJRosYOPcwrPZcTZg8wAQEutZdAdASWBBm3w9wjBNAIJmHotuGygILHT7gOLlJhZZDwC0yBZmdMdqSoBwwCJz8eahTuxUddhcADTK6goPdYZSwMy4eMuUywJsMgD+QYqQZOoLFAJm7unXRycj5xcA+prpC0WAKTMpefSnnf2NDQgcxjZmMuahwaJm5R76Pxk/jc0IHMIXpR46EjsfFnWrWkXJ/gkXm7ExgW0zFvgbngyRxY6HWcr4JHAgO+cCbFJgM+aW1VJ77HCYdY07L7EAn8LO+hU2LND+8sxHgzEHDaaNuasp1R9++BMbGGjGH1IIkV9LGdjJMD0FuIbK5QbYfxOMTQ2szmuVtdQKOxdmC+OvbEewUy/GxgaWLILw0meSoYOdCrNl/Nfto+NYhJdgswMrwE2gvNKJD3FcmBMEONZ/Abccmx9EiS/9F2dx2JAwpwowmu8AZc1sDMHlrnvYgDBHm3To5xjbRUhBA2YKLn/IX4iWjTBYIydgIwYsjaQhFCAyLDZiuDjhwmAtm/Q2ldQepKGBUJrY8Deo9+RDHDsJBgvBeBO19XdB+wOCAlpAeoVM5g/tdtg5MFgErNJLJXyKuY6/Nq6CwIBdMxTYN0ZWeagYOwUGMysO7KUe/mo4nIIdfLqVyQ9GOAEpYTCYwjjwMsqR1CDehMsgRI5Bcr+Hc3vGQuwAGCy6p+AY/qrZxT8P7keIk+34gU+3j2KYJAxmXRGOMzIi6i/kfoBoacsvEk6SUAJyb2EwHUW4/iS8DmJmedZtF1spqoEDw2CaG4+mT+Kvq714Y9/H1ELkLALP6+Mw0b38u+kpvyN4KgxmY5Oeq3IxJzfjLADrIYLK+F2KG+SCzL2c9oYnwmAOPg37U9RGc0/WOfznJghkxPiTc69n88n2Du6V0B0hBBgM1rhxXX+FjzqyaAzzn4hxSRc4640TLY99kg8zmUANh4LBYMGbNHDnr8V8Gj6PheVB5iP/rTsyD7z0IYvsA7I2CB3AYDDTrbyW3P4QxTB/JV21TYd7bmFxXWk0LZKTLGcdVKyg1vKBBC+AwWCWMKmok1CFv8n7cEln83/9FvHaauHTa7U/tDJaLh7lQ8UQWLRUhMFgOptcLpV4qVKE2Uhv81F/FuRL5SQpYQy+iHrWf+tf7cdjCHa9aP/kF8iNDQRzo//vfmrw73m2//f8897lP5+RcIDxGvJa/Jry2vIe5L3gwgum2v4fWd7IWKJ/Y7MAAAAASUVORK5CYII=";
+
+ let hideLegacyBlocks = true;
+
+ var vars = {};
+ vars["variables"] = Object.create(null);
+
+ if (!Scratch.extensions.unsandboxed) {
+ throw new Error("This extension must run unsandboxed");
+ }
+
+ class LMSUtils {
+ constructor(runtime) {
+ this.runtime = runtime;
+ }
+ getInfo() {
+ return {
+ id: "lmsutilsblocks",
+ name: "Lily's Toolbox",
+ color1: "#3bb2ed",
+ color2: "#37a1de",
+ color3: "#3693d9",
+ menuIconURI: menuIconURI,
+ blocks: [
+ {
+ opcode: "whenBooleanHat",
+ blockType: Scratch.BlockType.HAT,
+ text: "when [INPUT] is true",
+ isEdgeActivated: true,
+ arguments: {
+ INPUT: {
+ type: Scratch.ArgumentType.BOOLEAN,
+ defaultValue: "",
+ },
+ },
+ },
+ {
+ opcode: "whenKeyString",
+ blockType: Scratch.BlockType.HAT,
+ text: "when key [KEY_OPTION] pressed",
+ isEdgeActivated: true,
+ arguments: {
+ KEY_OPTION: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "enter",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "keyStringPressed",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "key [KEY_OPTION] pressed?",
+ arguments: {
+ KEY_OPTION: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "enter",
+ },
+ },
+ },
+ {
+ opcode: "trueFalseBoolean",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[TRUEFALSE]",
+ arguments: {
+ TRUEFALSE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "true",
+ menu: "trueFalseMenu",
+ },
+ },
+ },
+ {
+ opcode: "stringIf",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "if [BOOLEAN] then [INPUTA]",
+ disableMonitor: true,
+ arguments: {
+ BOOLEAN: {
+ type: Scratch.ArgumentType.BOOLEAN,
+ defaultValue: "",
+ },
+ INPUTA: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "apple",
+ },
+ },
+ },
+ {
+ opcode: "stringIfElse",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "if [BOOLEAN] then [INPUTA] else [INPUTB]",
+ disableMonitor: true,
+ arguments: {
+ BOOLEAN: {
+ type: Scratch.ArgumentType.BOOLEAN,
+ defaultValue: "",
+ },
+ INPUTA: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "apple",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "banana",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "getEffectValue",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "effect [INPUT]",
+ arguments: {
+ INPUT: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "color",
+ menu: "colorMenu",
+ },
+ },
+ },
+ {
+ opcode: "clonesBeingUsed",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "clone count",
+ },
+ {
+ opcode: "isClone",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "is clone?",
+ filter: [Scratch.TargetType.SPRITE],
+ },
+ {
+ opcode: "spriteClicked",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "sprite clicked?",
+ filter: [Scratch.TargetType.SPRITE],
+ },
+
+ "---",
+
+ {
+ opcode: "lettersToOf",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "letters [INPUTA] to [INPUTB] of [STRING]",
+ disableMonitor: true,
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "3",
+ },
+ STRING: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "suspicious",
+ },
+ },
+ },
+ {
+ opcode: "replaceWords",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "replace first [INPUTA] with [INPUTB] in [STRING]",
+ disableMonitor: true,
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Scratch",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Turbowarp",
+ },
+ STRING: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Scratch is brilliant!",
+ },
+ },
+ },
+ {
+ opcode: "findIndexOfString",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "index of [INPUTA] in [INPUTB]",
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "brilliant",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Turbowarp is brilliant!",
+ },
+ },
+ },
+ {
+ opcode: "itemOfFromString",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "item [INPUTA] of [INPUTB] split by [INPUTC]",
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "2",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "apple|banana",
+ },
+ INPUTC: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "|",
+ },
+ },
+ },
+ {
+ opcode: "stringToUpperCase",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[STRING] to uppercase",
+ disableMonitor: true,
+ arguments: {
+ STRING: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "apple",
+ },
+ },
+ },
+ {
+ opcode: "stringToLowerCase",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[STRING] to lowercase",
+ disableMonitor: true,
+ arguments: {
+ STRING: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "APPLE",
+ },
+ },
+ },
+ {
+ opcode: "reverseString",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "reverse [STRING]",
+ disableMonitor: true,
+ arguments: {
+ STRING: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "prawobrut",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "norBoolean",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[INPUTA] nor [INPUTB]",
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.BOOLEAN,
+ defaultValue: "",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.BOOLEAN,
+ defaultValue: "",
+ },
+ },
+ },
+ {
+ opcode: "xorBoolean",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[INPUTA] xor [INPUTB]",
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.BOOLEAN,
+ defaultValue: "",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.BOOLEAN,
+ defaultValue: "",
+ },
+ },
+ },
+ {
+ opcode: "xnorBoolean",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[INPUTA] xnor [INPUTB]",
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.BOOLEAN,
+ defaultValue: "",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.BOOLEAN,
+ defaultValue: "",
+ },
+ },
+ },
+ {
+ opcode: "nandBoolean",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[INPUTA] nand [INPUTB]",
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.BOOLEAN,
+ defaultValue: "",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.BOOLEAN,
+ defaultValue: "",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "stringReporter",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[STRING]",
+ disableMonitor: true,
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ STRING: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "apple",
+ },
+ },
+ },
+ {
+ opcode: "colourHex",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "color [COLOUR]",
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ COLOUR: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: "#0088ff",
+ },
+ },
+ },
+ {
+ opcode: "angleReporter",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "angle [ANGLE]",
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ ANGLE: {
+ type: Scratch.ArgumentType.ANGLE,
+ defaultValue: "90",
+ },
+ },
+ },
+ {
+ opcode: "matrixReporter",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "matrix [MATRIX]",
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ MATRIX: {
+ type: Scratch.ArgumentType.MATRIX,
+ defaultValue: "0101001010000001000101110",
+ },
+ },
+ },
+ {
+ opcode: "noteReporter",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "note [NOTE]",
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ NOTE: {
+ type: Scratch.ArgumentType.NOTE,
+ defaultValue: "",
+ },
+ },
+ },
+ {
+ opcode: "newlineCharacter",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "newline character",
+ hideFromPalette: hideLegacyBlocks,
+ disableMonitor: true,
+ },
+
+ "---",
+
+ {
+ opcode: "equalsExactly",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[ONE] === [TWO]",
+ arguments: {
+ ONE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "apple",
+ },
+ TWO: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "banana",
+ },
+ },
+ },
+ {
+ opcode: "notEqualTo",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[INPUTA] ≠ [INPUTB]",
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "apple",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "banana",
+ },
+ },
+ },
+ {
+ opcode: "moreThanEqual",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[INPUTA] ≥ [INPUTB]",
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "16",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "25",
+ },
+ },
+ },
+ {
+ opcode: "lessThanEqual",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[INPUTA] ≤ [INPUTB]",
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "16",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "25",
+ },
+ },
+ },
+ {
+ opcode: "stringCheckBoolean",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[INPUT] is [DROPDOWN]",
+ arguments: {
+ INPUT: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "apple",
+ },
+ DROPDOWN: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "text",
+ menu: "stringCheckMenu",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "encodeToBlock",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "encode [STRING] to [DROPDOWN]",
+ disableMonitor: true,
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ STRING: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "",
+ },
+ DROPDOWN: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "base64",
+ menu: "conversionMenu",
+ },
+ },
+ },
+ {
+ opcode: "decodeFromBlock",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "decode [STRING] from [DROPDOWN]",
+ disableMonitor: true,
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ STRING: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "",
+ },
+ DROPDOWN: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "base64",
+ menu: "conversionMenu",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "negativeReporter",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "- [INPUT]",
+ disableMonitor: true,
+ arguments: {
+ INPUT: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "",
+ },
+ },
+ },
+ {
+ opcode: "exponentBlock",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[INPUTA] ^ [INPUTB]",
+ disableMonitor: true,
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "",
+ },
+ },
+ },
+ {
+ opcode: "rootBlock",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[INPUTA] √ [INPUTB]",
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "",
+ },
+ },
+ },
+ {
+ opcode: "normaliseValue",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "normalise [INPUT]",
+ disableMonitor: true,
+ arguments: {
+ INPUT: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "100",
+ },
+ },
+ },
+ {
+ opcode: "clampNumber",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "clamp [INPUTA] between [INPUTB] and [INPUTC]",
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "100",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "25",
+ },
+ INPUTC: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "50",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "setVariableTo",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set variable [INPUTA] to [INPUTB]",
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my variable",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "0",
+ },
+ },
+ },
+ {
+ opcode: "changeVariableBy",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "change variable [INPUTA] by [INPUTB]",
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my variable",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "getVariable",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "variable [INPUT]",
+ disableMonitor: true,
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ INPUT: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my variable",
+ },
+ },
+ },
+ {
+ opcode: "deleteVariable",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "delete variable [INPUT]",
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ INPUT: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my variable",
+ },
+ },
+ },
+ {
+ opcode: "deleteAllVariables",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "delete all variables",
+ hideFromPalette: hideLegacyBlocks,
+ },
+ {
+ opcode: "listVariables",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "list active variables",
+ disableMonitor: true,
+ hideFromPalette: hideLegacyBlocks,
+ },
+
+ "---",
+
+ {
+ opcode: "greenFlag",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "green flag",
+ hideFromPalette: hideLegacyBlocks,
+ },
+ {
+ opcode: "setUsername",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set username to [INPUT]",
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ INPUT: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "LilyMakesThings",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "setSpriteSVG",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "replace SVG data for costume [INPUTA] with [INPUTB]",
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "alertBlock",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "alert [STRING]",
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ STRING: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "A red spy is in the base!",
+ },
+ },
+ },
+ {
+ opcode: "inputPromptBlock",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "prompt [STRING]",
+ hideFromPalette: hideLegacyBlocks,
+ disableMonitor: true,
+ arguments: {
+ STRING: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "The code is 1, 1, 1.. err... 1!",
+ },
+ },
+ },
+ {
+ opcode: "confirmationBlock",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "confirm [STRING]",
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ STRING: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Are you the red spy?",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "goToLink",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "open link [INPUT] in new tab",
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ INPUT: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "",
+ },
+ },
+ },
+ {
+ opcode: "redirectToLink",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "redirect to link [INPUT]",
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ INPUT: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "",
+ },
+ },
+ },
+
+ "---",
+
+ {
+ opcode: "setClipboard",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set [STRING] to clipboard",
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ STRING: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "apple",
+ },
+ },
+ },
+ {
+ opcode: "readClipboard",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "clipboard",
+ hideFromPalette: hideLegacyBlocks,
+ },
+
+ "---",
+
+ {
+ opcode: "isUserMobile",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "is mobile?",
+ },
+ {
+ opcode: "screenReporter",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "screen [DROPDOWN]",
+ disableMonitor: true,
+ arguments: {
+ DROPDOWN: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "width",
+ menu: "screenReporterMenu",
+ },
+ },
+ },
+ {
+ opcode: "windowReporter",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "window [DROPDOWN]",
+ disableMonitor: true,
+ arguments: {
+ DROPDOWN: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "width",
+ menu: "screenReporterMenu",
+ },
+ },
+ },
+ {
+ opcode: "osBrowserDetails",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "get [DROPDOWN] of user",
+ disableMonitor: true,
+ arguments: {
+ DROPDOWN: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "operating system",
+ menu: "osBrowserMenu",
+ },
+ },
+ },
+ {
+ opcode: "projectURL",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "project URL",
+ disableMonitor: true,
+ },
+
+ "---",
+
+ {
+ opcode: "consoleLog",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "console [DROPDOWN] [INPUT]",
+ disableMonitor: true,
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ DROPDOWN: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "log",
+ menu: "consoleLogMenu",
+ },
+ INPUT: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Apple",
+ },
+ },
+ },
+ {
+ opcode: "clearConsole",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "clear console",
+ hideFromPalette: hideLegacyBlocks,
+ },
+
+ "---",
+
+ {
+ opcode: "commentHat",
+ blockType: Scratch.BlockType.HAT,
+ text: "// [STRING]",
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ STRING: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "comment",
+ },
+ },
+ },
+ {
+ opcode: "commentCommand",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "// [STRING]",
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ STRING: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "comment",
+ },
+ },
+ },
+ {
+ opcode: "commentString",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "// [INPUTA] [INPUTB]",
+ disableMonitor: true,
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "comment",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "input",
+ },
+ },
+ },
+ {
+ opcode: "commentBool",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "// [INPUTA] [INPUTB]",
+ hideFromPalette: hideLegacyBlocks,
+ arguments: {
+ INPUTA: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "comment",
+ },
+ INPUTB: {
+ type: Scratch.ArgumentType.BOOLEAN,
+ },
+ },
+ },
+
+ "---",
+
+ {
+ func: "showLegacyBlocks",
+ blockType: Scratch.BlockType.BUTTON,
+ text: "Show Legacy Blocks",
+ hideFromPalette: !hideLegacyBlocks,
+ },
+ {
+ func: "hideLegacyBlocks",
+ blockType: Scratch.BlockType.BUTTON,
+ text: "Hide Legacy Blocks",
+ hideFromPalette: hideLegacyBlocks,
+ },
+ ],
+ menus: {
+ conversionMenu: {
+ acceptReporters: true,
+ items: ["base64", "binary"],
+ },
+ trueFalseMenu: {
+ acceptReporters: true,
+ items: ["true", "false", "random"],
+ },
+ screenReporterMenu: {
+ acceptReporters: true,
+ items: ["width", "height"],
+ },
+ windowReporterMenu: {
+ acceptReporters: true,
+ items: ["width", "height"],
+ },
+ stringCheckMenu: {
+ acceptReporters: true,
+ items: ["text", "number", "uppercase", "lowercase"],
+ },
+ osBrowserMenu: {
+ acceptReporters: true,
+ items: ["operating system", "browser"],
+ },
+ consoleLogMenu: {
+ acceptReporters: false,
+ items: ["log", "error", "warn"],
+ },
+ colorMenu: {
+ acceptReporters: true,
+ items: [
+ "color",
+ "fisheye",
+ "whirl",
+ "pixelate",
+ "mosaic",
+ "brightness",
+ "ghost",
+ ],
+ },
+ },
+ };
+ }
+
+ showLegacyBlocks() {
+ if (
+ confirm(
+ "Are you sure you want to show legacy blocks? \n \n These blocks were removed because they were buggy or implemented better in other extensions."
+ )
+ ) {
+ hideLegacyBlocks = false;
+ Scratch.vm.extensionManager.refreshBlocks();
+ } else {
+ //
+ }
+ }
+
+ hideLegacyBlocks() {
+ hideLegacyBlocks = true;
+ Scratch.vm.extensionManager.refreshBlocks();
+ }
+
+ whenBooleanHat(args) {
+ return args.INPUT;
+ }
+
+ whenKeyString(args, util) {
+ return util.ioQuery("keyboard", "getKeyIsDown", [args.KEY_OPTION]);
+ }
+
+ keyStringPressed(args, util) {
+ return util.ioQuery("keyboard", "getKeyIsDown", [args.KEY_OPTION]);
+ }
+
+ trueFalseBoolean(args) {
+ if (args.TRUEFALSE === "random") return Math.random() > 0.5;
+ if (args.TRUEFALSE === "true") return true;
+ return false;
+ }
+
+ stringIf(args) {
+ if (args.BOOLEAN) return args.INPUTA;
+ return "";
+ }
+
+ stringIfElse(args) {
+ if (args.BOOLEAN) return args.INPUTA;
+ return args.INPUTB;
+ }
+
+ getEffectValue(args, util) {
+ return util.target.effects[args.INPUT];
+ }
+
+ clonesBeingUsed(args, util) {
+ return Scratch.vm.runtime._cloneCounter;
+ }
+
+ isClone(args, util) {
+ return util.target.isOriginal ? false : true;
+ }
+
+ spriteClicked(args, util) {
+ return (
+ util.ioQuery("mouse", "getIsDown") &&
+ util.target.isTouchingObject("_mouse_")
+ );
+ }
+
+ lettersToOf(args) {
+ var string = args.STRING.toString();
+ var input1 = args.INPUTA - 1;
+ var input2 = args.INPUTB;
+ return string.slice(input1, input2);
+ }
+
+ replaceWords(args) {
+ var input1 = args.INPUTA;
+ var input2 = args.INPUTB;
+ var string = args.STRING;
+ return string.replace(input1, input2);
+ }
+
+ findIndexOfString(args) {
+ var input1 = args.INPUTA;
+ var input2 = args.INPUTB;
+ if (input2.includes(input1)) return input2.indexOf(input1) + 1;
+ return "";
+ }
+
+ itemOfFromString(args, util) {
+ var input1 = args.INPUTA - 1;
+ var input2 = String(args.INPUTB);
+ var input3 = args.INPUTC;
+ var output = input2.split(input3)[input1] || "";
+ return output;
+ }
+
+ stringToUpperCase(args) {
+ return args.STRING.toUpperCase();
+ }
+
+ stringToLowerCase(args) {
+ return args.STRING.toLowerCase();
+ }
+
+ reverseString(args) {
+ var input = args.STRING;
+ var splitInput = input.split("");
+ var reversedInput = splitInput.reverse();
+ var joinedArray = reversedInput.join("");
+ return joinedArray;
+ }
+
+ norBoolean(args) {
+ return !(args.INPUTA || args.INPUTB);
+ }
+
+ xorBoolean(args) {
+ return args.INPUTA !== args.INPUTB;
+ }
+
+ xnorBoolean(args) {
+ return args.INPUTA === args.INPUTB;
+ }
+
+ nandBoolean(args) {
+ return !(args.INPUTA && args.INPUTB);
+ }
+
+ stringReporter(args) {
+ return args.STRING;
+ }
+
+ colourHex(args) {
+ return args.COLOUR;
+ }
+
+ angleReporter(args) {
+ return args.ANGLE;
+ }
+
+ matrixReporter(args) {
+ return args.MATRIX;
+ }
+
+ noteReporter(args) {
+ return args.NOTE;
+ }
+
+ newlineCharacter() {
+ return "\n";
+ }
+
+ equalsExactly(args) {
+ return args.ONE === args.TWO;
+ }
+
+ notEqualTo(args) {
+ return args.INPUTA != args.INPUTB;
+ }
+
+ moreThanEqual(args) {
+ return args.INPUTA >= args.INPUTB;
+ }
+
+ lessThanEqual(args) {
+ return args.INPUTA <= args.INPUTB;
+ }
+
+ stringCheckBoolean(args) {
+ const input = args.INPUT;
+ const dropdown = args.DROPDOWN;
+ if (dropdown === "text") return isNaN(input);
+ if (dropdown === "number") return !isNaN(input);
+ if (dropdown === "uppercase") return input == input.toUpperCase();
+ if (dropdown === "lowercase") return input == input.toLowerCase();
+ return false;
+ }
+
+ encodeToBlock(args) {
+ if (args.STRING === "") return "";
+ if (args.DROPDOWN === "base64") return btoa(args.STRING);
+ if (args.DROPDOWN === "binary") {
+ return args.STRING.split("")
+ .map(function (char) {
+ return char.charCodeAt(0).toString(2);
+ })
+ .join(" ");
+ }
+ return "";
+ }
+
+ decodeFromBlock(args) {
+ if (args.STRING === "") return "";
+ if (args.DROPDOWN === "base64") return atob(args.STRING);
+ if (args.DROPDOWN === "binary") {
+ var output = args.STRING.toString();
+ return output
+ .split(" ")
+ .map((x) => (x = String.fromCharCode(parseInt(x, 2))))
+ .join("");
+ }
+ return "";
+ }
+
+ negativeReporter(args) {
+ return args.INPUT * -1;
+ }
+
+ exponentBlock(args) {
+ return Math.pow(args.INPUTA, args.INPUTB);
+ }
+
+ rootBlock(args) {
+ return Math.pow(args.INPUTB, 1 / args.INPUTA);
+ }
+
+ normaliseValue(args) {
+ var input1 = args.INPUT;
+ var input2 = Math.abs(input1);
+ var output = input1 / input2;
+ if (isNaN(output)) return "0";
+ return output;
+ }
+
+ clampNumber(args) {
+ var input1 = args.INPUTA;
+ var input2 = args.INPUTB;
+ var input3 = args.INPUTC;
+ return Math.min(Math.max(input1, input2), input3);
+ }
+
+ setVariableTo(args) {
+ vars["variables"][args.INPUTA] = args.INPUTB;
+ }
+
+ changeVariableBy(args) {
+ if (args.INPUTA in vars["variables"]) {
+ var prev = vars["variables"][args.INPUTA];
+ var next = args.INPUTB;
+ vars["variables"][args.INPUTA] = prev + next;
+ } else {
+ vars["variables"][args.INPUTA] = args.INPUTB;
+ }
+ }
+
+ getVariable(args) {
+ if (args.INPUT in vars["variables"]) return vars["variables"][args.INPUT];
+ return "";
+ }
+
+ deleteVariable(args) {
+ Reflect.deleteProperty(vars["variables"], args.INPUT);
+ }
+
+ deleteAllVariables() {
+ Reflect.deleteProperty(vars, "variables");
+ vars["variables"] = {};
+ }
+
+ greenFlag(args, util) {
+ util.runtime.greenFlag();
+ }
+
+ setUsername(args, util) {
+ util.runtime.ioDevices.userData._username = args.INPUT;
+ }
+
+ setSpriteSVG(args, util) {
+ try {
+ Scratch.vm.runtime.renderer.updateSVGSkin(
+ util.target.sprite.costumes[args.INPUTA - 1].skinId,
+ args.INPUTB
+ );
+ } catch (error) {
+ return;
+ }
+ Scratch.vm.emitTargetsUpdate();
+ }
+
+ alertBlock(args) {
+ alert(args.STRING);
+ }
+
+ inputPromptBlock(args) {
+ return prompt(args.STRING);
+ }
+
+ confirmationBlock(args) {
+ return confirm(args.STRING);
+ }
+
+ goToLink(args) {
+ Scratch.openWindow(args.INPUT);
+ }
+
+ redirectToLink(args) {
+ Scratch.redirect(args.INPUT);
+ }
+
+ setClipboard(args) {
+ if (navigator.clipboard && navigator.clipboard.writeText) {
+ navigator.clipboard.writeText(args.STRING);
+ }
+ }
+
+ readClipboard(args) {
+ if (navigator.clipboard && navigator.clipboard.readText) {
+ return Scratch.canReadClipboard().then((allowed) => {
+ if (allowed) {
+ return navigator.clipboard.readText();
+ }
+ return "";
+ });
+ }
+ return "";
+ }
+
+ isUserMobile(args, util) {
+ return navigator.userAgent.includes("Mobile");
+ }
+
+ screenReporter(args) {
+ if (args.DROPDOWN === "width") return screen.width;
+ if (args.DROPDOWN === "height") return screen.height;
+ return "";
+ }
+
+ windowReporter(args) {
+ if (args.DROPDOWN === "width") return window.innerWidth;
+ if (args.DROPDOWN === "height") return window.innerHeight;
+ return "";
+ }
+
+ osBrowserDetails(args) {
+ var user = navigator.userAgent;
+ if (args.DROPDOWN === "operating system") {
+ if (user.includes("Mac OS")) return "macOS";
+ if (user.includes("CrOS")) return "ChromeOS";
+ if (user.includes("Linux")) return "Linux";
+ if (user.includes("Windows")) return "Windows";
+ if (user.includes("iPad")) return "iOS";
+ if (user.includes("iPod")) return "iOS";
+ if (user.includes("iPhone")) return "iOS";
+ if (user.includes("Android")) return "Android";
+ return "Other";
+ }
+ if (args.DROPDOWN === "browser") {
+ if (user.includes("Chrome")) return "Chrome";
+ if (user.includes("MSIE")) return "Internet Explorer";
+ if (user.includes("Firefox")) return "Firefox";
+ if (user.includes("Safari")) return "Safari";
+ return "Other";
+ }
+ }
+
+ projectURL() {
+ return window.location.href;
+ }
+
+ consoleLog(args) {
+ if (args.DROPDOWN === "log") {
+ console.log(args.INPUT);
+ } else if (args.DROPDOWN === "error") {
+ console.error(args.INPUT);
+ } else if (args.DROPDOWN === "warn") {
+ console.warn(args.INPUT);
+ }
+ }
+
+ clearConsole() {
+ console.clear();
+ }
+
+ commentHat() {
+ // no-op
+ }
+
+ commentCommand() {
+ // no-op
+ }
+
+ commentString(args) {
+ return args.INPUT;
+ }
+
+ commentBool(args) {
+ return args.INPUT || false;
+ }
+ }
+ Scratch.extensions.register(new LMSUtils());
+})(Scratch);
diff --git a/extensions/Longboost/color_channels.js b/extensions/Longboost/color_channels.js
index 0a3bd925cc..0d0c6e2523 100644
--- a/extensions/Longboost/color_channels.js
+++ b/extensions/Longboost/color_channels.js
@@ -1,94 +1,97 @@
// Name: RGB Channels
+// ID: lbdrawtest
// Description: Only render or stamp certain RGB channels.
-(function(Scratch) {
- 'use strict';
+(function (Scratch) {
+ "use strict";
const renderer = Scratch.vm.renderer;
const gl = renderer._gl;
let channel_array = [true, true, true, true];
class LBdrawtest {
getInfo() {
return {
- id: 'lbdrawtest',
- name: 'RGB Channels',
- menuIconURI: 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIzMyIgaGVpZ2h0PSIzMyIgdmlld0JveD0iMCwwLDMzLDMzIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjIzLjUsLTE2My41KSI+PGcgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aXNQYWludGluZ0xheWVyJnF1b3Q7OnRydWV9IiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIHN0eWxlPSJtaXgtYmxlbmQtbW9kZTogbm9ybWFsIj48cGF0aCBkPSJNMjI0LDE4MGMwLC04LjgzNjU2IDcuMTYzNDQsLTE2IDE2LC0xNmM4LjgzNjU2LDAgMTYsNy4xNjM0NCAxNiwxNmMwLDguODM2NTYgLTcuMTYzNDQsMTYgLTE2LDE2Yy04LjgzNjU2LDAgLTE2LC03LjE2MzQ0IC0xNiwtMTZ6IiBmaWxsPSIjYWFhYWFhIiBzdHJva2U9IiM4ODg4ODgiIHN0cm9rZS13aWR0aD0iMSIvPjxwYXRoIGQ9Ik0yMzMuOTAyMDQsMTgxLjQ4NjkyYzAsLTQuNDE4MjggMy41ODE3MiwtOCA4LC04YzQuNDE4MjgsMCA4LDMuNTgxNzIgOCw4YzAsNC40MTgyOCAtMy41ODE3Miw4IC04LDhjLTQuNDE4MjgsMCAtOCwtMy41ODE3MiAtOCwtOHoiIGZpbGw9IiMwMDAwZmYiIHN0cm9rZT0iIzNjMDBmZiIgc3Ryb2tlLXdpZHRoPSIwLjUiLz48cGF0aCBkPSJNMjMxLjk2MjQ1LDE3OS40NDEzNWMwLC00LjQxODI4IDMuNTgxNzIsLTggOCwtOGM0LjQxODI4LDAgOCwzLjU4MTcyIDgsOGMwLDQuNDE4MjggLTMuNTgxNzIsOCAtOCw4Yy00LjQxODI4LDAgLTgsLTMuNTgxNzIgLTgsLTh6IiBmaWxsPSIjMDBmZjAwIiBzdHJva2U9IiMwMGZmM2QiIHN0cm9rZS13aWR0aD0iMSIvPjxwYXRoIGQ9Ik0yMzAuMjI1OSwxNzcuNjIwOThjMCwtNC40MTgyOCAzLjU4MTcyLC04IDgsLThjNC40MTgyOCwwIDgsMy41ODE3MiA4LDhjMCw0LjQxODI4IC0zLjU4MTcyLDggLTgsOGMtNC40MTgyOCwwIC04LC0zLjU4MTcyIC04LC04eiIgZmlsbD0iI2ZmMDAwMCIgc3Ryb2tlPSIjZmYzZDAwIiBzdHJva2Utd2lkdGg9IjEiLz48L2c+PC9nPjwvc3ZnPgo=',
- blockIconURI: 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIyMC40MjYxNCIgaGVpZ2h0PSIyMC42MTU5NCIgdmlld0JveD0iMCwwLDIwLjQyNjE0LDIwLjYxNTk0Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjI5LjcyNTksLTE2OS4xMjA5OCkiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTIzMy45MDIwNCwxODEuNDg2OTJjMCwtNC40MTgyOCAzLjU4MTcyLC04IDgsLThjNC40MTgyOCwwIDgsMy41ODE3MiA4LDhjMCw0LjQxODI4IC0zLjU4MTcyLDggLTgsOGMtNC40MTgyOCwwIC04LC0zLjU4MTcyIC04LC04eiIgZmlsbD0iIzAwMDBmZiIgc3Ryb2tlPSIjM2MwMGZmIiBzdHJva2Utd2lkdGg9IjAuNSIvPjxwYXRoIGQ9Ik0yMzEuOTYyNDUsMTc5LjQ0MTM1YzAsLTQuNDE4MjggMy41ODE3MiwtOCA4LC04YzQuNDE4MjgsMCA4LDMuNTgxNzIgOCw4YzAsNC40MTgyOCAtMy41ODE3Miw4IC04LDhjLTQuNDE4MjgsMCAtOCwtMy41ODE3MiAtOCwtOHoiIGZpbGw9IiMwMGZmMDAiIHN0cm9rZT0iIzAwZmYzZCIgc3Ryb2tlLXdpZHRoPSIxIi8+PHBhdGggZD0iTTIzMC4yMjU5LDE3Ny42MjA5OGMwLC00LjQxODI4IDMuNTgxNzIsLTggOCwtOGM0LjQxODI4LDAgOCwzLjU4MTcyIDgsOGMwLDQuNDE4MjggLTMuNTgxNzIsOCAtOCw4Yy00LjQxODI4LDAgLTgsLTMuNTgxNzIgLTgsLTh6IiBmaWxsPSIjZmYwMDAwIiBzdHJva2U9IiNmZjNkMDAiIHN0cm9rZS13aWR0aD0iMSIvPjwvZz48L2c+PC9zdmc+Cg==',
- color1: '#aaaaaa',
- color2: '#888888',
- color3: '#888888',
+ id: "lbdrawtest",
+ name: "RGB Channels",
+ menuIconURI:
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIzMyIgaGVpZ2h0PSIzMyIgdmlld0JveD0iMCwwLDMzLDMzIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjIzLjUsLTE2My41KSI+PGcgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aXNQYWludGluZ0xheWVyJnF1b3Q7OnRydWV9IiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIHN0eWxlPSJtaXgtYmxlbmQtbW9kZTogbm9ybWFsIj48cGF0aCBkPSJNMjI0LDE4MGMwLC04LjgzNjU2IDcuMTYzNDQsLTE2IDE2LC0xNmM4LjgzNjU2LDAgMTYsNy4xNjM0NCAxNiwxNmMwLDguODM2NTYgLTcuMTYzNDQsMTYgLTE2LDE2Yy04LjgzNjU2LDAgLTE2LC03LjE2MzQ0IC0xNiwtMTZ6IiBmaWxsPSIjYWFhYWFhIiBzdHJva2U9IiM4ODg4ODgiIHN0cm9rZS13aWR0aD0iMSIvPjxwYXRoIGQ9Ik0yMzMuOTAyMDQsMTgxLjQ4NjkyYzAsLTQuNDE4MjggMy41ODE3MiwtOCA4LC04YzQuNDE4MjgsMCA4LDMuNTgxNzIgOCw4YzAsNC40MTgyOCAtMy41ODE3Miw4IC04LDhjLTQuNDE4MjgsMCAtOCwtMy41ODE3MiAtOCwtOHoiIGZpbGw9IiMwMDAwZmYiIHN0cm9rZT0iIzNjMDBmZiIgc3Ryb2tlLXdpZHRoPSIwLjUiLz48cGF0aCBkPSJNMjMxLjk2MjQ1LDE3OS40NDEzNWMwLC00LjQxODI4IDMuNTgxNzIsLTggOCwtOGM0LjQxODI4LDAgOCwzLjU4MTcyIDgsOGMwLDQuNDE4MjggLTMuNTgxNzIsOCAtOCw4Yy00LjQxODI4LDAgLTgsLTMuNTgxNzIgLTgsLTh6IiBmaWxsPSIjMDBmZjAwIiBzdHJva2U9IiMwMGZmM2QiIHN0cm9rZS13aWR0aD0iMSIvPjxwYXRoIGQ9Ik0yMzAuMjI1OSwxNzcuNjIwOThjMCwtNC40MTgyOCAzLjU4MTcyLC04IDgsLThjNC40MTgyOCwwIDgsMy41ODE3MiA4LDhjMCw0LjQxODI4IC0zLjU4MTcyLDggLTgsOGMtNC40MTgyOCwwIC04LC0zLjU4MTcyIC04LC04eiIgZmlsbD0iI2ZmMDAwMCIgc3Ryb2tlPSIjZmYzZDAwIiBzdHJva2Utd2lkdGg9IjEiLz48L2c+PC9nPjwvc3ZnPgo=",
+ blockIconURI:
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIyMC40MjYxNCIgaGVpZ2h0PSIyMC42MTU5NCIgdmlld0JveD0iMCwwLDIwLjQyNjE0LDIwLjYxNTk0Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjI5LjcyNTksLTE2OS4xMjA5OCkiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTIzMy45MDIwNCwxODEuNDg2OTJjMCwtNC40MTgyOCAzLjU4MTcyLC04IDgsLThjNC40MTgyOCwwIDgsMy41ODE3MiA4LDhjMCw0LjQxODI4IC0zLjU4MTcyLDggLTgsOGMtNC40MTgyOCwwIC04LC0zLjU4MTcyIC04LC04eiIgZmlsbD0iIzAwMDBmZiIgc3Ryb2tlPSIjM2MwMGZmIiBzdHJva2Utd2lkdGg9IjAuNSIvPjxwYXRoIGQ9Ik0yMzEuOTYyNDUsMTc5LjQ0MTM1YzAsLTQuNDE4MjggMy41ODE3MiwtOCA4LC04YzQuNDE4MjgsMCA4LDMuNTgxNzIgOCw4YzAsNC40MTgyOCAtMy41ODE3Miw4IC04LDhjLTQuNDE4MjgsMCAtOCwtMy41ODE3MiAtOCwtOHoiIGZpbGw9IiMwMGZmMDAiIHN0cm9rZT0iIzAwZmYzZCIgc3Ryb2tlLXdpZHRoPSIxIi8+PHBhdGggZD0iTTIzMC4yMjU5LDE3Ny42MjA5OGMwLC00LjQxODI4IDMuNTgxNzIsLTggOCwtOGM0LjQxODI4LDAgOCwzLjU4MTcyIDgsOGMwLDQuNDE4MjggLTMuNTgxNzIsOCAtOCw4Yy00LjQxODI4LDAgLTgsLTMuNTgxNzIgLTgsLTh6IiBmaWxsPSIjZmYwMDAwIiBzdHJva2U9IiNmZjNkMDAiIHN0cm9rZS13aWR0aD0iMSIvPjwvZz48L2c+PC9zdmc+Cg==",
+ color1: "#aaaaaa",
+ color2: "#888888",
+ color3: "#888888",
blocks: [
{
- opcode: 'true',
+ opcode: "true",
blockType: Scratch.BlockType.BOOLEAN,
- text: 'true'
+ text: "true",
},
{
- opcode: 'false',
+ opcode: "false",
blockType: Scratch.BlockType.BOOLEAN,
- text: 'false',
- hideFromPalette: true
+ text: "false",
+ hideFromPalette: true,
},
{
- opcode: 'enabledCheck',
+ opcode: "enabledCheck",
blockType: Scratch.BlockType.BOOLEAN,
- text: '[COLOR] channel enabled?',
+ text: "[COLOR] channel enabled?",
arguments: {
COLOR: {
type: Scratch.ArgumentType.STRING,
- menu: 'COLOR_MENU'
- }
- }
+ menu: "COLOR_MENU",
+ },
+ },
},
{
- opcode: 'draw',
+ opcode: "draw",
blockType: Scratch.BlockType.COMMAND,
- text: 'only draw colors:[R]green:[G]blue:[B]',
+ text: "only draw colors:[R]green:[G]blue:[B]",
arguments: {
R: {
- type: Scratch.ArgumentType.BOOLEAN
+ type: Scratch.ArgumentType.BOOLEAN,
},
G: {
- type: Scratch.ArgumentType.BOOLEAN
+ type: Scratch.ArgumentType.BOOLEAN,
},
B: {
- type: Scratch.ArgumentType.BOOLEAN
- }
- }
+ type: Scratch.ArgumentType.BOOLEAN,
+ },
+ },
},
{
- opcode: 'drawOneColor',
+ opcode: "drawOneColor",
blockType: Scratch.BlockType.COMMAND,
- text: 'only draw [COLOR]',
+ text: "only draw [COLOR]",
arguments: {
COLOR: {
type: Scratch.ArgumentType.STRING,
- menu: 'COLOR_MENU'
- }
- }
+ menu: "COLOR_MENU",
+ },
+ },
},
{
- opcode: 'drawDepth',
+ opcode: "drawDepth",
blockType: Scratch.BlockType.COMMAND,
- text: 'enable depth mask?[DRAW]',
+ text: "enable depth mask?[DRAW]",
hideFromPalette: true,
arguments: {
DRAW: {
- type: Scratch.ArgumentType.BOOLEAN
- }
- }
+ type: Scratch.ArgumentType.BOOLEAN,
+ },
+ },
},
{
- opcode: 'clearEffects',
+ opcode: "clearEffects",
blockType: Scratch.BlockType.COMMAND,
- text: 'clear color draw effects',
- }
+ text: "clear color draw effects",
+ },
],
menus: {
COLOR_MENU: {
acceptReporters: true,
- items: ['red', 'green', 'blue']
- }
- }
+ items: ["red", "green", "blue"],
+ },
+ },
};
}
@@ -100,44 +103,63 @@
return false;
}
- enabledCheck({COLOR}) {
- if ((COLOR == 'red' && channel_array[0]) || (COLOR == 'green' && channel_array[1]) || (COLOR == 'blue' && channel_array[2])) {
+ enabledCheck({ COLOR }) {
+ if (
+ (COLOR == "red" && channel_array[0]) ||
+ (COLOR == "green" && channel_array[1]) ||
+ (COLOR == "blue" && channel_array[2])
+ ) {
return true;
} else {
return false;
}
}
- draw({R, G, B}) {
+ draw({ R, G, B }) {
channel_array = [R, G, B, true];
- gl.colorMask(channel_array[0], channel_array[1], channel_array[2], channel_array[3]);
+ gl.colorMask(
+ channel_array[0],
+ channel_array[1],
+ channel_array[2],
+ channel_array[3]
+ );
Scratch.vm.renderer.dirty = true;
}
- drawOneColor({COLOR}) {
- if (COLOR == 'red') {
+ drawOneColor({ COLOR }) {
+ if (COLOR == "red") {
channel_array = [true, false, false, true];
- } else if (COLOR == 'green') {
+ } else if (COLOR == "green") {
channel_array = [false, true, false, true];
} else {
channel_array = [false, false, true, true];
}
- gl.colorMask(channel_array[0], channel_array[1], channel_array[2], channel_array[3]);
+ gl.colorMask(
+ channel_array[0],
+ channel_array[1],
+ channel_array[2],
+ channel_array[3]
+ );
Scratch.vm.renderer.dirty = true;
}
- drawDepth({DRAW}) {
+ drawDepth({ DRAW }) {
gl.depthMask(DRAW);
Scratch.vm.renderer.dirty = true;
}
clearEffects() {
channel_array = [true, true, true, true];
- gl.colorMask(channel_array[0], channel_array[1], channel_array[2], channel_array[3]);
+ gl.colorMask(
+ channel_array[0],
+ channel_array[1],
+ channel_array[2],
+ channel_array[3]
+ );
gl.depthMask(true);
Scratch.vm.renderer.dirty = true;
}
}
Scratch.extensions.register(new LBdrawtest());
-})(Scratch);
\ No newline at end of file
+})(Scratch);
diff --git a/extensions/Medericoder/textcase.js b/extensions/Medericoder/textcase.js
index 2745ad484c..c08c3577be 100644
--- a/extensions/Medericoder/textcase.js
+++ b/extensions/Medericoder/textcase.js
@@ -15,332 +15,384 @@
*/
(function (Scratch) {
- 'use strict';
- function funchangecase (text, format) {
- if (format === 'uppercase') {
- return text.toString().toUpperCase();
- } else if (format === 'lowercase') {
- return text.toString().toLowerCase();
- } else if (format === 'invert') {
- let x = "";
- for (let i = 0; i < text.length; i++) {
- if (text.toString()[i] === text.toString()[i].toUpperCase()) {
- x += text.toString()[i].toLowerCase();
- } else {
- x += text.toString()[i].toUpperCase();
- }
- }
- return x;
- } else if (format === 'begin') {
- let x = "";
- for (let i = 0; i < text.length; i++) {
- if (i == 0) {
- x += text.toString()[i].toUpperCase();
- } else {
- x += text.toString()[i];
- }
- }
- return x;
- } else if (format === 'begin words') {
- let x = "";
- for (let i = 0; i < text.length; i++) {
- if (i == 0) {
- x += text.toString()[i].toUpperCase();
- } else if (text.toString()[i - 1].toLowerCase() === text.toString()[i - 1].toUpperCase()) {
- x += text.toString()[i].toUpperCase();
- } else {
- x += text.toString()[i];
- }
- }
- return x;
- } else if (format === 'begin sentences') {
- let x = "";
- let mybool = true;
- for (let i = 0; i < text.length; i++) {
- if (mybool){
- x += text.toString()[i].toUpperCase();
- if (!(text.toString()[i].toLowerCase() === text.toString()[i].toUpperCase())){
- mybool = false;
- }
- } else if (text.toString()[i] == '.' || text.toString()[i] == '!' || text.toString()[i] == '?') {
- x += text.toString()[i];
- mybool = true;
- } else {
- x += text.toString()[i];
- }
- }
- return x;
- } else if (format === 'begin only') {
- let x = "";
- for (let i = 0; i < text.length; i++) {
- if (i == 0) {
- x += text.toString()[i].toUpperCase();
- } else {
- x += text.toString()[i].toLowerCase();
- }
- }
- return x;
- } else if (format === 'begin words only') {
- let x = "";
- for (let i = 0; i < text.length; i++) {
- if (i == 0) {
- x += text.toString()[i].toUpperCase();
- } else if (text.toString()[i - 1].toLowerCase() === text.toString()[i - 1].toUpperCase()) {
- x += text.toString()[i].toUpperCase();
- } else {
- x += text.toString()[i].toLowerCase();
- }
- }
- return x;
- } else if (format === 'begin sentences only') {
- let x = "";
- let mybool = true;
- for (let i = 0; i < text.length; i++) {
- if (mybool){
- x += text.toString()[i].toUpperCase();
- if (!(text.toString()[i].toLowerCase() === text.toString()[i].toUpperCase())){
- mybool = false;
- }
- } else if (text.toString()[i] == '.' || text.toString()[i] == '!' || text.toString()[i] == '?') {
- x += text.toString()[i];
- mybool = true;
- } else {
- x += text.toString()[i].toLowerCase();
- }
- }
- return x;
- } else if (format === '1/2 up') {
- let x = "";
- for (let i = 0; i < text.length; i++) {
- if (i % 2 == 0){
- x += text.toString()[i].toUpperCase();
- } else {
- x += text.toString()[i].toLowerCase();
- }
- }
- return x;
- } else if (format === '1/2 low') {
- let x = "";
- for (let i = 0; i < text.length; i++) {
- if (i % 2 == 1){
- x += text.toString()[i].toUpperCase();
- } else {
- x += text.toString()[i].toLowerCase();
- }
- }
- return x;
- } else if (format === '1/2 up letters only') {
- let x = "";
- let noletters = 0;
- for (let i = 0; i < text.length; i++) {
- if (text.toString()[i].toUpperCase() === text.toString()[i].toLowerCase()){
- noletters += 1;
- x += text.toString()[i];
- } else if ((i - noletters) % 2 == 0){
- x += text.toString()[i].toUpperCase();
- } else {
- x += text.toString()[i].toLowerCase();
- }
- }
- return x;
- } else if (format === '1/2 low letters only') {
- let x = "";
- let noletters = 0;
- for (let i = 0; i < text.length; i++) {
- if (text.toString()[i].toUpperCase() === text.toString()[i].toLowerCase()){
- noletters += 1;
- x += text.toString()[i];
- } else if ((i - noletters) % 2 == 1){
- x += text.toString()[i].toUpperCase();
- } else {
- x += text.toString()[i].toLowerCase();
- }
- }
- return x;
- } else if (format === 'random') {
- let x = "";
- for (let i = 0; i < text.length; i++) {
- if (Math.random() < 0.5){
- x += text.toString()[i].toUpperCase();
- } else {
- x += text.toString()[i].toLowerCase();
- }
- }
- return x;
+ "use strict";
+ function funchangecase(text, format) {
+ if (format === "uppercase") {
+ return text.toString().toUpperCase();
+ } else if (format === "lowercase") {
+ return text.toString().toLowerCase();
+ } else if (format === "invert") {
+ let x = "";
+ for (let i = 0; i < text.length; i++) {
+ if (text.toString()[i] === text.toString()[i].toUpperCase()) {
+ x += text.toString()[i].toLowerCase();
} else {
- return text.toString();
+ x += text.toString()[i].toUpperCase();
}
- }
-
- function fungetcase (args) {
- var low = 0;
- var up = 0;
- for (let i = 0; i < args.TEXT.length; i++) {
- if (args.TEXT.toString()[i].toLowerCase() === args.TEXT.toString()[i].toUpperCase()) {
- low += 0;
- up += 0;
- } else if (args.TEXT.toString()[i].toLowerCase() === args.TEXT.toString()[i]) {
- low += 1;
- } else {
- up += 1;
- }
+ }
+ return x;
+ } else if (format === "begin") {
+ let x = "";
+ for (let i = 0; i < text.length; i++) {
+ if (i == 0) {
+ x += text.toString()[i].toUpperCase();
+ } else {
+ x += text.toString()[i];
+ }
+ }
+ return x;
+ } else if (format === "begin words") {
+ let x = "";
+ for (let i = 0; i < text.length; i++) {
+ if (i == 0) {
+ x += text.toString()[i].toUpperCase();
+ } else if (
+ text.toString()[i - 1].toLowerCase() ===
+ text.toString()[i - 1].toUpperCase()
+ ) {
+ x += text.toString()[i].toUpperCase();
+ } else {
+ x += text.toString()[i];
+ }
+ }
+ return x;
+ } else if (format === "begin sentences") {
+ let x = "";
+ let mybool = true;
+ for (let i = 0; i < text.length; i++) {
+ if (mybool) {
+ x += text.toString()[i].toUpperCase();
+ if (
+ !(
+ text.toString()[i].toLowerCase() ===
+ text.toString()[i].toUpperCase()
+ )
+ ) {
+ mybool = false;
+ }
+ } else if (
+ text.toString()[i] == "." ||
+ text.toString()[i] == "!" ||
+ text.toString()[i] == "?"
+ ) {
+ x += text.toString()[i];
+ mybool = true;
+ } else {
+ x += text.toString()[i];
+ }
+ }
+ return x;
+ } else if (format === "begin only") {
+ let x = "";
+ for (let i = 0; i < text.length; i++) {
+ if (i == 0) {
+ x += text.toString()[i].toUpperCase();
+ } else {
+ x += text.toString()[i].toLowerCase();
+ }
+ }
+ return x;
+ } else if (format === "begin words only") {
+ let x = "";
+ for (let i = 0; i < text.length; i++) {
+ if (i == 0) {
+ x += text.toString()[i].toUpperCase();
+ } else if (
+ text.toString()[i - 1].toLowerCase() ===
+ text.toString()[i - 1].toUpperCase()
+ ) {
+ x += text.toString()[i].toUpperCase();
+ } else {
+ x += text.toString()[i].toLowerCase();
+ }
+ }
+ return x;
+ } else if (format === "begin sentences only") {
+ let x = "";
+ let mybool = true;
+ for (let i = 0; i < text.length; i++) {
+ if (mybool) {
+ x += text.toString()[i].toUpperCase();
+ if (
+ !(
+ text.toString()[i].toLowerCase() ===
+ text.toString()[i].toUpperCase()
+ )
+ ) {
+ mybool = false;
+ }
+ } else if (
+ text.toString()[i] == "." ||
+ text.toString()[i] == "!" ||
+ text.toString()[i] == "?"
+ ) {
+ x += text.toString()[i];
+ mybool = true;
+ } else {
+ x += text.toString()[i].toLowerCase();
+ }
+ }
+ return x;
+ } else if (format === "1/2 up") {
+ let x = "";
+ for (let i = 0; i < text.length; i++) {
+ if (i % 2 == 0) {
+ x += text.toString()[i].toUpperCase();
+ } else {
+ x += text.toString()[i].toLowerCase();
+ }
+ }
+ return x;
+ } else if (format === "1/2 low") {
+ let x = "";
+ for (let i = 0; i < text.length; i++) {
+ if (i % 2 == 1) {
+ x += text.toString()[i].toUpperCase();
+ } else {
+ x += text.toString()[i].toLowerCase();
+ }
+ }
+ return x;
+ } else if (format === "1/2 up letters only") {
+ let x = "";
+ let noletters = 0;
+ for (let i = 0; i < text.length; i++) {
+ if (
+ text.toString()[i].toUpperCase() === text.toString()[i].toLowerCase()
+ ) {
+ noletters += 1;
+ x += text.toString()[i];
+ } else if ((i - noletters) % 2 == 0) {
+ x += text.toString()[i].toUpperCase();
+ } else {
+ x += text.toString()[i].toLowerCase();
}
- if (up == 0 && low == 0) {
- return '';
- } else if (up > 0 && low > 0) {
- if (up == low) {
- return 'mixed';
- } else if (up > low) {
- return 'mixed (upper)';
- } else {
- return 'mixed (lower)';
- }
- } else if (up > low) {
- return 'upper';
+ }
+ return x;
+ } else if (format === "1/2 low letters only") {
+ let x = "";
+ let noletters = 0;
+ for (let i = 0; i < text.length; i++) {
+ if (
+ text.toString()[i].toUpperCase() === text.toString()[i].toLowerCase()
+ ) {
+ noletters += 1;
+ x += text.toString()[i];
+ } else if ((i - noletters) % 2 == 1) {
+ x += text.toString()[i].toUpperCase();
} else {
- return 'lower';
+ x += text.toString()[i].toLowerCase();
}
+ }
+ return x;
+ } else if (format === "random") {
+ let x = "";
+ for (let i = 0; i < text.length; i++) {
+ if (Math.random() < 0.5) {
+ x += text.toString()[i].toUpperCase();
+ } else {
+ x += text.toString()[i].toLowerCase();
+ }
+ }
+ return x;
+ } else {
+ return text.toString();
}
+ }
- class TextCase {
- getInfo() {
- return {
- id: 'medericodertextcase',
- name: 'Text Case',
- blocks: [
- {
- opcode: 'strictlyequal',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[TEXT1] ≡ [TEXT2]',
- arguments: {
- TEXT1: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Hello'
- },
- TEXT2: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'hello'
- }
- }
- },
- {
- opcode: 'quasiequal',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[TEXT1] ≈ [TEXT2]',
- arguments: {
- TEXT1: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Hello'
- },
- TEXT2: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'hello'
- }
- }
- },
- '---',
- {
- opcode: 'changecase',
- blockType: Scratch.BlockType.REPORTER,
- text: 'convert [TEXT] to case [FORMAT]',
- arguments: {
- TEXT: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Where is the apple? It is here!'
- },
- FORMAT: {
- type: Scratch.ArgumentType.STRING,
- menu: 'FORMAT_MENU'
- }
- }
- },
- {
- opcode: 'getcase',
- blockType: Scratch.BlockType.REPORTER,
- text: 'get case from [TEXT]',
- arguments: {
- TEXT: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'hello world'
- }
- }
- },
- {
- opcode: 'iscase',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'is [TEXT] [FORMAT]',
- arguments: {
- TEXT: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'hello world'
- },
- FORMAT: {
- type: Scratch.ArgumentType.STRING,
- menu: 'FORMAT_MENU'
- }
- }
- },
- '---',
- {
- opcode: 'glitch',
- blockType: Scratch.BlockType.REPORTER,
- text: 'glitch [TEXT] level [PROBA]%',
- arguments: {
- TEXT: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Hello'
- },
- PROBA: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: 10
- }
- }
- }
- ],
- menus: {
- FORMAT_MENU: {
- acceptReporters: true,
- items: ['uppercase', 'lowercase', 'invert', 'begin', 'begin words', 'begin sentences', 'begin only', 'begin words only', 'begin sentences only', '1/2 up', '1/2 low', '1/2 up letters only', '1/2 low letters only', 'random', 'identity']
- }
- }
- };
- }
+ function fungetcase(args) {
+ var low = 0;
+ var up = 0;
+ for (let i = 0; i < args.TEXT.length; i++) {
+ if (
+ args.TEXT.toString()[i].toLowerCase() ===
+ args.TEXT.toString()[i].toUpperCase()
+ ) {
+ low += 0;
+ up += 0;
+ } else if (
+ args.TEXT.toString()[i].toLowerCase() === args.TEXT.toString()[i]
+ ) {
+ low += 1;
+ } else {
+ up += 1;
+ }
+ }
+ if (up == 0 && low == 0) {
+ return "";
+ } else if (up > 0 && low > 0) {
+ if (up == low) {
+ return "mixed";
+ } else if (up > low) {
+ return "mixed (upper)";
+ } else {
+ return "mixed (lower)";
+ }
+ } else if (up > low) {
+ return "upper";
+ } else {
+ return "lower";
+ }
+ }
- changecase (args) {
- return funchangecase(args.TEXT, args.FORMAT);
- }
+ class TextCase {
+ getInfo() {
+ return {
+ id: "medericodertextcase",
+ name: "Text Case",
+ blocks: [
+ {
+ opcode: "strictlyequal",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[TEXT1] ≡ [TEXT2]",
+ arguments: {
+ TEXT1: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Hello",
+ },
+ TEXT2: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "hello",
+ },
+ },
+ },
+ {
+ opcode: "quasiequal",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[TEXT1] ≈ [TEXT2]",
+ arguments: {
+ TEXT1: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Hello",
+ },
+ TEXT2: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "hello",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "changecase",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "convert [TEXT] to case [FORMAT]",
+ arguments: {
+ TEXT: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Where is the apple? It is here!",
+ },
+ FORMAT: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "FORMAT_MENU",
+ },
+ },
+ },
+ {
+ opcode: "getcase",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "get case from [TEXT]",
+ arguments: {
+ TEXT: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "hello world",
+ },
+ },
+ },
+ {
+ opcode: "iscase",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "is [TEXT] [FORMAT]",
+ arguments: {
+ TEXT: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "hello world",
+ },
+ FORMAT: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "FORMAT_MENU",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "glitch",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "glitch [TEXT] level [PROBA]%",
+ arguments: {
+ TEXT: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Hello",
+ },
+ PROBA: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: 10,
+ },
+ },
+ },
+ ],
+ menus: {
+ FORMAT_MENU: {
+ acceptReporters: true,
+ items: [
+ "uppercase",
+ "lowercase",
+ "invert",
+ "begin",
+ "begin words",
+ "begin sentences",
+ "begin only",
+ "begin words only",
+ "begin sentences only",
+ "1/2 up",
+ "1/2 low",
+ "1/2 up letters only",
+ "1/2 low letters only",
+ "random",
+ "identity",
+ ],
+ },
+ },
+ };
+ }
- getcase (args) {
- return fungetcase(args);
- }
+ changecase(args) {
+ return funchangecase(args.TEXT, args.FORMAT);
+ }
- iscase (args) {
- if (args.FORMAT == 'random'){
- return fungetcase(args).includes('mixed');
- } else {
- return args.TEXT.toString() === funchangecase(args.TEXT, args.FORMAT);
- }
- }
+ getcase(args) {
+ return fungetcase(args);
+ }
- strictlyequal (args) {
- return args.TEXT1 === args.TEXT2;
- }
+ iscase(args) {
+ if (args.FORMAT == "random") {
+ return fungetcase(args).includes("mixed");
+ } else {
+ return args.TEXT.toString() === funchangecase(args.TEXT, args.FORMAT);
+ }
+ }
- quasiequal (args) {
- return args.TEXT1.toString().toLowerCase() === args.TEXT2.toString().toLowerCase();
- }
+ strictlyequal(args) {
+ return args.TEXT1 === args.TEXT2;
+ }
+
+ quasiequal(args) {
+ return (
+ args.TEXT1.toString().toLowerCase() ===
+ args.TEXT2.toString().toLowerCase()
+ );
+ }
- glitch (args) {
- var x = '';
- for (let i = 0; i < args.TEXT.toString().length; i++){
- if (Math.random() * 100 < args.PROBA){
- x += funchangecase(args.TEXT.toString()[i],'invert');
- } else {
- x += args.TEXT.toString()[i];
- }
- }
- return x;
+ glitch(args) {
+ var x = "";
+ for (let i = 0; i < args.TEXT.toString().length; i++) {
+ if (Math.random() * 100 < args.PROBA) {
+ x += funchangecase(args.TEXT.toString()[i], "invert");
+ } else {
+ x += args.TEXT.toString()[i];
}
+ }
+ return x;
}
- Scratch.extensions.register(new TextCase());
+ }
+ Scratch.extensions.register(new TextCase());
})(Scratch);
diff --git a/extensions/NOname-awa/cn-number.js b/extensions/NOname-awa/cn-number.js
index de029ed77d..a3db7e1208 100644
--- a/extensions/NOname-awa/cn-number.js
+++ b/extensions/NOname-awa/cn-number.js
@@ -1,32 +1,46 @@
(function (Scratch) {
- 'use strict';
+ "use strict";
- let Number2, null2, units, Number_in, uppercase, i, N_Z, o, j, After_decimal_point, unit, k, C_Number, m, n;
+ let Number2,
+ null2,
+ units,
+ Number_in,
+ uppercase,
+ i,
+ N_Z,
+ o,
+ j,
+ After_decimal_point,
+ unit,
+ k,
+ C_Number,
+ m,
+ n;
class CNNUMBER {
getInfo() {
return {
- id: 'nonameawacnnumber',
- name: '中文数字',
- color1: '#cb0000',
- color2: '#a20000',
- color3: '#a20000',
+ id: "nonameawacnnumber",
+ name: "中文数字",
+ color1: "#cb0000",
+ color2: "#a20000",
+ color3: "#a20000",
blocks: [
{
- opcode: 'CN_number',
+ opcode: "CN_number",
blockType: Scratch.BlockType.REPORTER,
disableMonitor: true,
- text: '中文数字 [a] 大写 [u]',
+ text: "中文数字 [a] 大写 [u]",
arguments: {
a: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '2020'
+ defaultValue: "2020",
},
u: {
type: Scratch.ArgumentType.STRING,
- menu: 'uppercase'
+ menu: "uppercase",
},
- }
+ },
},
],
menus: {
@@ -34,16 +48,16 @@
acceptReporters: true,
items: [
{
- text: '关闭',
- value: '0'
+ text: "关闭",
+ value: "0",
},
{
- text: '打开',
- value: '1'
- }
- ]
- }
- }
+ text: "打开",
+ value: "1",
+ },
+ ],
+ },
+ },
};
}
CN_number(args) {
@@ -54,12 +68,11 @@
}
}
-
const main = (Number2, null2, units) => {
if (Number2 == 0) {
- return null2 ? '零' : '';
+ return null2 ? "零" : "";
}
- i = '';
+ i = "";
let j_start = String(Number2).length;
let j_inc = 1;
if (j_start > 1) {
@@ -67,18 +80,51 @@
}
for (j = j_start; j_inc >= 0 ? j <= 1 : j >= 1; j += j_inc) {
if (units) {
- k = String(Number2).charAt(((String(Number2).length - (j - 1)) - 1));
+ k = String(Number2).charAt(String(Number2).length - (j - 1) - 1);
if (Number2 >= 10000) {
- return '';
+ return "";
}
- i = String(i) + String(String(Number2).slice((-j)).charAt(0) == '2' && j >= 3 ? '两' : (k == '0' ? (j == 1 ? '' : (i.slice(-1) == '零' ? '' : (String(Number2).slice(-1) == '0' && (j != 3 || String(Number2).charAt(2) == '0') ? ' ' : '零'))) : C_Number[(k - 1)]));
- i = String(i) + String(i.slice(-1) == '零' || i.slice(-1) == ' ' ? '' : (unit[(j - 1)] == '个' ? '' : unit[(j - 1)]));
+ i =
+ String(i) +
+ String(
+ String(Number2).slice(-j).charAt(0) == "2" && j >= 3
+ ? "两"
+ : k == "0"
+ ? j == 1
+ ? ""
+ : i.slice(-1) == "零"
+ ? ""
+ : String(Number2).slice(-1) == "0" &&
+ (j != 3 || String(Number2).charAt(2) == "0")
+ ? " "
+ : "零"
+ : C_Number[k - 1]
+ );
+ i =
+ String(i) +
+ String(
+ i.slice(-1) == "零" || i.slice(-1) == " "
+ ? ""
+ : unit[j - 1] == "个"
+ ? ""
+ : unit[j - 1]
+ );
} else {
if (j != 1) {
- k = String(Number2).slice((-(j - 1))).charAt(0);
- i = String(i) + String(k == 0 ? '零' : C_Number[([k].reduce(function (x, y) {
- return x + y;
- }) - 1)]);
+ k = String(Number2)
+ .slice(-(j - 1))
+ .charAt(0);
+ i =
+ String(i) +
+ String(
+ k == 0
+ ? "零"
+ : C_Number[
+ [k].reduce(function (x, y) {
+ return x + y;
+ }) - 1
+ ]
+ );
}
}
}
@@ -89,33 +135,57 @@
};
const CN_NUMBER = (Number_in, uppercase) => {
- if (String(Math.abs(Number_in >= 0 ? Math.floor(Number_in) : Math.ceil(Number_in))).length > 20) {
- return '';
+ if (
+ String(
+ Math.abs(Number_in >= 0 ? Math.floor(Number_in) : Math.ceil(Number_in))
+ ).length > 20
+ ) {
+ return "";
}
- N_Z = Math.abs(Number_in >= 0 ? Math.floor(Number_in) : Math.ceil(Number_in));
- After_decimal_point = '.' + String(String(Number_in).split('.')[1]);
- unit = (uppercase ? '个、拾、佰、仟、万、亿、兆、京' : '个、十、百、千、万、亿、兆、京').split('、');
- C_Number = (uppercase ? '壹、贰、叁、肆、伍、陆、柒、捌、玖、拾' : '一、二、三、四、五、六、七、八、九、十').split('、');
- m = '';
- o = '';
+ N_Z = Math.abs(
+ Number_in >= 0 ? Math.floor(Number_in) : Math.ceil(Number_in)
+ );
+ After_decimal_point = "." + String(String(Number_in).split(".")[1]);
+ unit = (
+ uppercase
+ ? "个、拾、佰、仟、万、亿、兆、京"
+ : "个、十、百、千、万、亿、兆、京"
+ ).split("、");
+ C_Number = (
+ uppercase
+ ? "壹、贰、叁、肆、伍、陆、柒、捌、玖、拾"
+ : "一、二、三、四、五、六、七、八、九、十"
+ ).split("、");
+ m = "";
+ o = "";
let n_end = String(N_Z).length;
let n_inc = 1;
if (1 > n_end) {
n_inc = -n_inc;
}
for (n = 1; n_inc >= 0 ? n <= n_end : n >= n_end; n += n_inc) {
- m = [n % 4 == 0 ? ' ' : '', String(N_Z).slice((-n)).charAt(0), m].join('');
+ m = [n % 4 == 0 ? " " : "", String(N_Z).slice(-n).charAt(0), m].join("");
}
- m = m.trim().split(' ');
+ m = m.trim().split(" ");
let n_end2 = m.length;
let n_inc2 = 1;
if (1 > n_end2) {
n_inc2 = -n_inc2;
}
for (n = 1; n_inc2 >= 0 ? n <= n_end2 : n >= n_end2; n += n_inc2) {
- o = [m.length == n ? '' : unit[((n + 4) - 1)], main(m.slice((-n))[0], m.length == n, true), o].join('');
+ o = [
+ m.length == n ? "" : unit[n + 4 - 1],
+ main(m.slice(-n)[0], m.length == n, true),
+ o,
+ ].join("");
}
- return [Number_in < 0 ? '负' : '', o, Number_in % 1 != 0 ? '点' + String(main(After_decimal_point, false, false)) : ''].join('');
+ return [
+ Number_in < 0 ? "负" : "",
+ o,
+ Number_in % 1 != 0
+ ? "点" + String(main(After_decimal_point, false, false))
+ : "",
+ ].join("");
};
Scratch.extensions.register(new CNNUMBER());
diff --git a/extensions/NOname-awa/global-coordinate.js b/extensions/NOname-awa/global-coordinate.js
index d546845192..3613073ce8 100644
--- a/extensions/NOname-awa/global-coordinate.js
+++ b/extensions/NOname-awa/global-coordinate.js
@@ -1,474 +1,511 @@
(function (Scratch) {
- 'use strict';
- const icon = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIiB2aWV3Qm94PSItMjcuNTEzODgsLTI3LjUxMzg4LDIwMCwyMDAiPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNjcuNTEzNzgsLTEwNy41MTM3OCkiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTMxMC40ODYyMiwxMDkuNjM4Nzd2MzEuNzQ1MTdsLTEwLjA0MDU2LC0xMi4zODA2MmwtMTQuNzc1MSwxNS4yNzg5OWwtMTAuMDc3ODIsLTEwLjA3NzgybDE1LjI2Mjg3LC0xNC43NjAwMWwtMTIuMTE0NTUsLTkuODA1NzF6TTIwNC4yODIzMSwyMjUuNjcwNTVsLTE1LjI3MDkzLDE0Ljc2NzA1bDEyLjI0NzU3LDkuOTIzNjRoLTMxLjc0NTE3di0zMS43NDUxN2w5LjkyMzY3LDEyLjI0NzYxbDE0Ljc2NzA1LC0xNS4yNzA5M3pNMTY5LjYzODc3LDEwOS41MTM3OGgzMS43NDUxN2wtMTIuMzgwNjIsMTAuMDQwNTdsMTUuMjc4OTksMTQuNzc1MWwtMTAuMDc3ODIsMTAuMDc3ODJsLTE0Ljc2LC0xNS4yNjI4N2wtOS44MDU3MSwxMi4xMTQ1NnpNMjg1LjY3MDU1LDIxNS43MTc2OGwxNC43NjcwNCwxNS4yNzA5NGw5LjkyMzY0LC0xMi4yNDc1N3YzMS43NDUxN2gtMzEuNzQ1MTdsMTIuMjQ3NiwtOS45MjM2NmwtMTUuMjcwOTMsLTE0Ljc2NzA1eiIgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aW5kZXgmcXVvdDs6bnVsbH0iIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzFhNjI2MiIgc3Ryb2tlLXdpZHRoPSI0Ii8+PHBhdGggZD0iTTI3OS45NTE2NCwyMDcuODA0OWMtMC44MjY0OSwwLjgzNDc5IC0xLjY3MzE0LDEuNjg5OTQgLTIuNDk5NjMsMi41MjQ3MmMtMS4yNzM0LDEuMDk2NjYgLTIuNjE0OTMsMi4xMTE2MiAtNC4wMTY1NSwzLjAzODhjLTAuODA5NzcsMC41NjE0MiAtMS42NTI5NCwxLjA3MzA5IC0yLjUyNDg1LDEuNTMyMTFsLTEuMzczOCwwLjc3Njc0Yy01LjYwODg3LDIuODUxNTIgLTExLjc3OTcyLDQuNDI1MjcgLTE4LjA2OTE1LDQuNjA4MjVjLTUuMzM4NDMsMC4xOTYxMyAtMTAuNjY1MTUsLTAuNjI5OTggLTE1LjY5MzQsLTIuNDMzODRjLTEuOTk0NDEsLTAuNzc4NzUgLTMuOTM0NywtMS42ODk1NCAtNS44MDc5OCwtMi43MjYzNWMtMS42MDU0NiwtMC45ODQ3MyAtMi45ODc1NSwtMS44NjkyNiAtNC4xMjY1NCwtMi43NTUwMWMtMC42NDA0MSwtMC40NTg0OSAtMS4yNTE4NiwtMC45NTYxNCAtMS44MzA4NSwtMS40OTAwOWwtMC40MDcyMSwtMC40MDMxNmwtMC42NzE5MSwtMC42NjUyMmMtMi4xNjQ5NCwtMS44NjUzOSAtMi40MjUxOSwtNS4xMjU3NyAtMC41ODM1NCwtNy4zMTA5NGwwLjIyMTc0LC0wLjIyMzk3YzEuNzUzMTYsLTEuNzg3OTEgNC41MzcyOCwtMi4wNTgxNSA2LjYwMTU2LC0wLjY0MDc5YzAsMCAwLjI4NTA1LDAuMjgyMjMgMC44MzM1OCwwLjU4MzM4YzAsMCAwLjI2NDY4LDAuMjYyMDUgMC41MDkwMSwwLjUwMzk1YzAuNDIxMTYsMC4zMDQzMiAwLjg2MjUyLDAuNTc5NjUgMS4zMjEwMiwwLjgyNDA2YzAuODUzMzMsMC40ODE5OSAxLjkyOTgyLDEuMDIzNjIgMy4xODg3MywxLjU4NDYyYzEuMzcyMDUsMC41NzY1MiAyLjc4MjUzLDEuMDU2ODggNC4yMjEyNiwxLjQzNzYzYzMuNjExOSwwLjg1NjQ3IDcuMzU1ODMsMS4wMDI5MSAxMS4wMjM2LDAuNDMxMTdjNC4wNzgyLC0wLjU5NjU5IDcuOTY5NDQsLTIuMTA0OTMgMTEuMzg0MzMsLTQuNDEyNzh2MGwwLjk2ODgxLC0wLjczNDJjMC40ODcwNSwtMC4zMjk3OCAwLjk1MjIsLTAuNjkwNzYgMS4zOTI1NCwtMS4wODA3NGwxLjM3MDc2LC0xLjM4NDUzbDAuODA2MzMsLTAuODE0NDNjMS4zMDExLC0xLjQzNDcxIDIuNDQ3NjksLTMuMDAyMyAzLjQyMDgzLC00LjY3Njg5YzEuNjk4OTgsLTIuOTA4MzMgMi44MTE3NCwtNi4xMjEzNSAzLjI3NTM4LC05LjQ1NzUyYzAuMDc3MzksLTAuNzI5NzQgMC4xNTQ5OSwtMS40MTg5OCAwLjE3MTcxLC0yLjEyODE2bC04LjEwMzgsMC4wNDA0OGMtMS42NDk5MywwLjAwNjQzIC0zLjE0MjcyLC0wLjk3NzQzIC0zLjc4NzQyLC0yLjQ5NjE5Yy0wLjY0NDcsLTEuNTE4NzYgLTAuMzE1NSwtMy4yNzYwOCAwLjgzNTI0LC00LjQ1ODQ4bDE3LjgxOTkxLC0xNy45OTg4NmMwLjc2NjA1LC0wLjc4NDY4IDEuODE0NjgsLTEuMjI5NTMgMi45MTEyNywtMS4yMzVjMS4wOTY1OCwtMC4wMDU0OCAyLjE0OTYxLDAuNDI4ODggMi45MjM0NywxLjIwNTg2bDE3Ljk3ODUsMTcuNzk5NzdjMS42MDAyMiwxLjYwNjQ3IDEuNjA0MTYsNC4yMDMyNyAwLjAwODc4LDUuODE0NTdjLTAuNzYwMTEsMC43NjM0MiAtMS43OTM2NywxLjE5MTYxIC0yLjg3MDk4LDEuMTg5NDNsLTguNjkxNDIsMC4wMjMxN2MtMC4yMjM5NiwxLjk2MDIgLTAuNTcyOSwzLjkwNDExIC0xLjA0NDcsNS44MTk4M2MtMS4zNzg4NSw1LjU0NTA5IC0zLjgxNTUyLDEwLjc3MTc0IC03LjE3NjE4LDE1LjM5MjkyYy0xLjE4NzcxLDEuNTYzNjYgLTIuNDk0NDQsMy4wMzMyNyAtMy45MDg0OCw0LjM5NTY4eiIgZmlsbD0iIzFhNjI2MiIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiLz48cGF0aCBkPSJNMjIzLjcyODU0LDIwMy40MjU5OGwtMC4xNDExMSwwLjE0MjUzYy0xLjM2NDc3LDEuNTc0NTUgLTEuMTk4ODUsMy45NTYzNyAwLjM3MTA0LDUuMzI2NTJsMC42NzE5LDAuNjY1MjJjMC43MDQ5OCwwLjY1MTA3IDEuNDQ1MjcsMS4yNjI4NiAyLjIxNzQ5LDEuODMyNTljMS4wOTgyNywwLjg0NTQzIDIuNDM5NjQsMS42ODk2NSAzLjk2Mzg2LDIuNjM0MjZjMS43NzY0MSwwLjk4MDUxIDMuNjE0NzYsMS44NDQzNSA1LjUwMzM4LDIuNTg2MDRjNC44Mzk0OSwxLjgwOTQ3IDkuOTgxOSwyLjY3MTA5IDE1LjE0Njg5LDIuNTM3ODhjNi4wNTE3NCwtMC4yMTEyOCAxMS45ODExNSwtMS43NjMyNSAxNy4zNjAzNywtNC41NDM5M2MxLjMzMTI5LC0wLjcxMDMgMi42MjU3MiwtMS40ODc2MiAzLjg3ODI4LC0yLjMyOTAyYzEuMzM4LC0wLjg4MzY2IDIuNjE4ODgsLTEuODUwOTQgMy44MzQ5MywtMi44OTYwOGMwLjgwNjMzLC0wLjgxNDQzIDEuNjMyODIsLTEuNjQ5MjEgMi40MzkxNSwtMi40NjM2NGMxLjM2MDAyLC0xLjM1MTk4IDIuNjEyNjgsLTIuODA3ODQgMy43NDY2MSwtNC4zNTQzNWMzLjI2NDM5LC00LjQzMzE3IDUuNjQ2NjIsLTkuNDUyMjYgNy4wMTcxNCwtMTQuNzg0MzRjMC40OTIzOSwtMi4xOTY3NCAwLjgyNjU3LC00LjQyNTk3IDAuOTk5OTQsLTYuNjcwNTNsMC4wMTg1NCwtMC4zNDQ1MWw5Ljk4ODAzLC0wLjAyOTY0YzAuNjcxMTUsLTAuMDIwODMgMS4zMDg0MSwtMC4yOTk3OSAxLjc3OSwtMC43Nzg3NmMwLjUwNjEzLC0wLjUwMDMzIDAuNzkxMDcsLTEuMTgyMzUgMC43OTEzMiwtMS44OTQwNGMwLjAwMDI1LC0wLjcxMTY5IC0wLjI4NDIyLC0xLjM5MzkgLTAuNzg5OTksLTEuODk0NThsLTE3Ljk3ODUsLTE3Ljc5OTc2Yy0xLjAzNzgyLC0xLjAyNzYyIC0yLjcwOTQ1LC0xLjAyODMyIC0zLjc0ODEsLTAuMDAxNTNsLTE3LjgxOTkzLDE3Ljk5ODg2Yy0wLjc1MzMxLDAuNzY5NDQgLTAuOTY5NDgsMS45MTY2NCAtMC41NDc4NSwyLjkwNzQ2YzAuNDIxNjMsMC45OTA4MyAxLjM5ODE1LDEuNjMwNDggMi40NzQ5MiwxLjYyMTE2bDkuNDgxNDUsLTAuMDQ3MzZjMC4wMTM0NywxLjI2NzYxIC0wLjA2ODI0LDIuNTM0NDYgLTAuMjQ0NDUsMy43ODk4NGMtMC40NTcwMSwzLjUyNzYyIC0xLjU5NjAzLDYuOTMyNjIgLTMuMzUzNTksMTAuMDI1MTljLTEuMDE3OTYsMS43NjEzNyAtMi4yMTg0NiwzLjQxMDczIC0zLjU4MTY5LDQuOTIwODFsLTAuOTQ3NDUsMC45NTY5NmMtMC40NDM0OSwwLjQ0NzkzIC0wLjg2NjgsMC44NzU1MSAtMS4yNDk4MiwxLjI2MjM2djBjLTAuNTQ0MjYsMC41NDk3NCAtMi4wOTk0OSwxLjUwOTczIC0yLjU4MzMsMS45OTgzOGMtMy41OTM1MywyLjQyMTIyIC03LjY4NDAyLDQuMDA2MjUgLTExLjk3MDc0LDQuNjM4NTZjLTMuODQ0MDYsMC41NjI2MyAtNy43NjEwNCwwLjM2ODg2IC0xMS41MzA3OSwtMC41NzA0NWMtMS41MDk1MiwtMC40MTQ5OSAtMi45ODgwMSwtMC45MzU2NiAtNC40MjQ0NiwtMS41NTgxOGMtMS4yOTk2NCwtMC42MDEzMSAtMi4zOTY2OSwtMS4yMDM2MyAtMy4yMDkxLC0xLjYwNDc4Yy0wLjgxMjQxLC0wLjQwMTE0IC0xLjU4NTUxLC0xLjA0NTYgLTIuMTM0MDIsLTEuMzQ2NzZjLTAuNTQ4NTIsLTAuMzAxMTUgLTAuNzkyODUsLTAuNTQzMDYgLTAuNzkyODUsLTAuNTQzMDZjLTEuNDk3ODMsLTEuMDE2MjkgLTMuNTA3NjQsLTAuODE4ODIgLTQuNzc5MDIsMC40Njk1OU0yMjEuNTQ5OTMsMjAxLjI2OTA0djBjMi4xOTA0NywtMi4zODA3IDUuNzg1MTMsLTIuODE5MjggOC40ODM3NywtMS4wMzUxMmMwLjE0MjMzLDAuMTAwNTkgMC41Njk5LDAuNTIzOSAwLjgzMzU3LDAuNTgzMzdjMC4yNjM2NywwLjA1OTQ3IDAuNDQ2OTMsMC4yNDA4OSAwLjY1MDUzLDAuNDQyNDdjMC4zNjM2MSwwLjI2NTIgMC43NDM3NiwwLjUwNjkxIDEuMTM4MTcsMC43MjM2OGwwLjE0MjUzLDAuMTQxMTFjMC43OTI0NSwwLjQ2MjAxIDEuOTkwNywxLjA0MzU4IDIuODg0MTMsMS40NDQzMWMxLjMxMjUxLDAuNTUyNjggMi42NjIsMS4wMTMwNSA0LjAzODYyLDEuMzc3NzZjMy40MTkyMSwwLjg0ODE2IDYuOTcwNzUsMS4wMjMxIDEwLjQ1NjczLDAuNTE1MDRjMy44MTY3NiwtMC41Njk1MiA3LjQ2MTY4LC0xLjk3MTA1IDEwLjY3Njc2LC00LjEwNTM0bDEuMDA5OTQsLTAuNjEyODRsMS4xNzA3OSwtMC44NTY3N2wwLjM2Mjg0LC0wLjM2NjQ5YzAuMzIyNTMsLTAuMzI1NzcgMC42ODUzOCwtMC42OTIyNiAxLjA0ODIyLC0xLjA1ODc1bDAuNzI1NywtMC43MzI5OGMxLjIxNTg2LC0xLjM1NjQzIDIuMjg4MDIsLTIuODM1MTMgMy4xOTkzLC00LjQxMjRjMS42MTQyMiwtMi43ODM3MiAyLjY1OTM4LC01Ljg2MDM2IDMuMDc0ODMsLTkuMDUxMzFsMC4wNTgyNSwtMC41MDY3OGwtNi40ODMwNCwwLjAzMjM5Yy0yLjI0MDY5LDAuMDE5ODggLTQuMjcxOTEsLTEuMzE0NDIgLTUuMTQzNDQsLTMuMzc4NzhjLTAuODcxNTMsLTIuMDY0MzYgLTAuNDExMDksLTQuNDUwNjMgMS4xNjU5MywtNi4wNDI1bDE3LjkyMDcxLC0xOC4xMDA2NmMyLjE1MjAxLC0yLjE1MTIxIDUuNjM3NjksLTIuMTU5NTggNy44LC0wLjAxODcxbDE3LjkxNzQyLDE3LjczOTI5YzIuMTYyNDYsMi4xNjMxNiAyLjE3MDkyLDUuNjY2OTcgMC4wMTg5MSw3Ljg0MDUyYy0xLjAzMTM5LDEuMDQwNTkgLTIuNDM2OTcsMS42MjQxNCAtMy45MDIwOSwxLjYyMDAzbC03LjQzNTMzLDAuMDE2ODhjLTAuMjM1MDIsMS42MjE5OCAtMC41Mzc3LDMuMjE3MjcgLTAuOTA4MDUsNC43ODU5Yy0xLjQ3MjIsNS42NzA1OSAtNC4wMTc2OSwxMS4wMDY0MyAtNy40OTg3MSwxNS43MTg2OWMtMS4yMDcyNywxLjY0NzM4IC0yLjU0MDcxLDMuMTk4NDQgLTMuOTg4Myw0LjYzOTJjLTAuODI2NDksMC44MzQ3OSAtMS42OTMzLDEuNzEwMyAtMi42NDA3MywyLjY2NzI2Yy0xLjI3NDIxLDEuMDk1NjcgLTIuNjE1NjgsMi4xMTA1OCAtNC4wMTY1NiwzLjAzODhjLTAuODczMTUsMC42MjA2IC0xLjc4Mzc2LDEuMTg2NzUgLTIuNzI2NjMsMS42OTUybC0xLjUxNDQ5LDEuMDAwMzFsLTAuMTIwOTUsMC4xMjIxNmMtNS43NjQ2MiwyLjk2ODk3IC0xMi4xMTQ0Miw0LjYyNjU0IC0xOC41OTQ2OSw0Ljg1Mzk5Yy01LjU1MjUxLDAuMTM5MjMgLTExLjA4MDEsLTAuNzg5MTkgLTE2LjI4MjQ0LC0yLjczNDhjLTIuMDAwMTYsLTAuNzg4NDQgLTMuOTQ3MDQsLTEuNzA1ODUgLTUuODI4MzQsLTIuNzQ2NTF2MGMtMS41ODUzLC0xLjAwNTA4IC0zLjAwNzkyLC0xLjg4OTQyIC00LjEyNjU0LC0yLjc1NTAxYy0wLjY5NjQyLC0wLjQ5NjIgLTEuMzYyMTYsLTEuMDM0MTQgLTEuOTkzNTMsLTEuNjEwODRjLTAuMTIyMTYsLTAuMTIwOTUgLTAuMjQ0MzMsLTAuMjQxOSAtMC4zNDYxNCwtMC4zNDI2OWwtMC43NTMzNSwtMC43NDU4NmMtMS4zMjUxMiwtMS4xNTc2MiAtMi4xMzQ3MywtMi43OTUyMyAtMi4yNDk5NSwtNC41NTEwMmMtMC4xMTUyMiwtMS43NTU3OSAwLjQ3MzQ1LC0zLjQ4NTE2IDEuNjM1OTMsLTQuODA2MDNsMC4yODIyMiwtMC4yODUwNXoiIGZpbGw9IiMxYTYyNjIiIHN0cm9rZT0iIzFhNjI2MiIgc3Ryb2tlLXdpZHRoPSIzIi8+PHBhdGggZD0iTTIyMy41NDQ1MiwyMDMuMzAwMzNjMS4yNzEzOCwtMS4yODg0MSAzLjM2MzY0LC0xLjUxODEzIDQuODYxNDgsLTAuNTAxODRjMCwwIDAuMjQzOTYsMC4yNDEwMyAwLjc5MjQ5LDAuNTQyMmMwLjU0ODUyLDAuMzAxMTUgMS4zMjA1NSwwLjk0MzMzIDIuMTMyOTUsMS4zNDQ0NmMwLjgxMjQxLDAuNDAxMTQgMS45MDc2NiwxLjAwMDIzIDMuMjA3MywxLjYwMTU1YzEuNDM2NDUsMC42MjI1MiAyLjkxMjAzLDEuMTM5MDEgNC40MjE1NiwxLjU1Mzk5YzMuNzY5NzYsMC45MzkzMiA3LjY3NjQzLDEuMTIzNTggMTEuNTIwNSwwLjU2MDk1YzQuMjg2NzIsLTAuNjMyMyA4LjM1NjgsLTIuMjI0NTcgMTEuOTUwMzIsLTQuNjQ1NzljMC40ODM4LC0wLjQ4ODY2IDIuMDMxMjIsLTEuNDQ4NjcgMi41NzU0OSwtMS45OTg0MXYwYzAuMzgzLC0wLjM4Njg2IDAuODAxNTksLTAuODEzNzYgMS4yNDUwNiwtMS4yNjE3MWwwLjk0MzM4LC0wLjk1NTk2YzEuMzYzMjMsLTEuNTEwMDggMi41NDM3MiwtMy4xNDcwMiAzLjU2MTY5LC00LjkwODM5YzEuNzU3NTUsLTMuMDkyNTcgMi44ODczNiwtNi40NTI3NCAzLjM0NDM3LC05Ljk4MDM2YzAuMTc2MjEsLTEuMjU1MzggMC4yNjM1LC0yLjUxMTQ2IDAuMjUwMDIsLTMuNzc5MDdsLTkuNDUxNCwwLjA2NTY3Yy0xLjA3Njc3LDAuMDA5MzIgLTIuMDQwMTUsLTAuNjMwNDcgLTIuNDYxNzgsLTEuNjIxMjljLTAuNDIxNjMsLTAuOTkwODMgLTAuMjE1ODEsLTIuMjI1ODkgMC41Mzc1MSwtMi45OTUzMmwxNy44NDUwNiwtMTcuOTkzMjRjMS4wMzg2NiwtMS4wMjY3NyAyLjcxMzY2LC0xLjAyMzk3IDMuNzUxNDgsMC4wMDM2NWwxNy45OTU1NywxNy44MjI5NWMwLjUwNTc5LDAuNTAwNjkgMC43OTA4NiwxLjE4NTA0IDAuNzkwNiwxLjg5NjczYy0wLjAwMDI1LDAuNzExNjkgLTAuMjg0NzgsMS4zOTU0OCAtMC43OTA5LDEuODk1ODNjLTAuNDcwNTksMC40Nzg5NyAtMS4xOTQ0OSwwLjc3NjEyIC0xLjg2NTY2LDAuNzk2OTRsLTkuOTg3MjksMC4wMzI2OGwwLjA2Njg5LDAuMzIxMTljLTAuMTczMzgsMi4yNDQ1NiAtMC41MDYwNyw0LjQ4NDM2IC0wLjk5ODQ1LDYuNjgxMDljLTEuMzcwNTIsNS4zMzIwNyAtMy43NjE3MywxMC4zNzg3NyAtNy4wMjYxMiwxNC44MTE5M2MtMS4xMzM5MiwxLjU0NjUyIC0yLjM5NCwzLjAxMDAzIC0zLjc1NDAyLDQuMzYyMDFjLTAuODA2MzMsMC44MTQ0MyAtMS42Mzc5MSwxLjY1Mjc3IC0yLjQ0NDI0LDIuNDY3MmMtMS4yMTYwNiwxLjA0NTE0IC0yLjUwNDQxLDIuMDE2IC0zLjg0MjQzLDIuODk5NjdjLTEuMjUyNTcsMC44NDE0IC0yLjU1NDA5LDEuNjIwNjggLTMuODg1MzgsMi4zMzA5N2MtNS4zNzkyNCwyLjc4MDY4IC0xMS4zMzMzMiw0LjMzMDIxIC0xNy4zODUwNiw0LjU0MTQ5Yy01LjE2NSwwLjEzMzIxIC0xMC4zMjE3NiwtMC43Mzc3NCAtMTUuMTYxMjUsLTIuNTQ3MjFjLTEuODg4NjMsLTAuNzQxNjkgLTMuNzMxMDIsLTEuNjEwMDcgLTUuNTA3NDQsLTIuNTkwNThjLTEuNTI0MjEsLTAuOTQ0NiAtMi44NjgyMiwtMS43OTI1OCAtMy45NjY0OCwtMi42MzgwMmMtMC43NzIyMSwtMC41Njk3MyAtMS41MTM4NiwtMS4xODM4NSAtMi4yMTg4NiwtMS44MzQ5MmwtMC42NzIzMiwtMC42NjYwMWMtMS41Njk4OSwtMS4zNzAxNCAtMS43Mzc4NiwtMy43NTY1NyAtMC4zNzMwOSwtNS4zMzExM2wwLjE0MTA3LC0wLjE0MjYxIiBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtub0hvdmVyJnF1b3Q7OmZhbHNlLCZxdW90O29yaWdJdGVtJnF1b3Q7OlsmcXVvdDtQYXRoJnF1b3Q7LHsmcXVvdDthcHBseU1hdHJpeCZxdW90Ozp0cnVlLCZxdW90O3NlZ21lbnRzJnF1b3Q7OltbMjA0LjMzNDA1LDE2MC4xMDgwNF0sW1syMDQuMTQwNTEsMTU5Ljk5MDM5XSxbMCwwXSxbLTIuMDg3MjUsLTEuMDg2NTFdXSxbWzE5OC4zODY3NiwxNjEuNzkzODddLFsxLjA5MzYzLC0yLjA4MzUzXSxbMCwwXV0sW1sxOTcuODMyMTMsMTYyLjcwNjI2XSxbMCwwXSxbLTAuNTMwNDIsMC45NDUwM11dLFtbMTk2LjQwMDI0LDE2NS42MjIzNV0sWzAuNDIzNDUsLTAuOTk3NTZdLFstMC42NDA4NSwxLjQyNzk2XV0sW1sxOTQuNTQ1NDEsMTcwLjY2Njg2XSxbMC42MzgxNiwtMS45MjE4NF0sWy0wLjYxMTUsMi4yMDgyOF1dLFtbMTkzLjE0NzAxLDE3Ny4zODk4M10sWzAuMzE5NzcsLTIuMjY4OTZdLFstMC43MTkzNSw1Ljc5MDJdXSxbWzE5NC4zMjg3OCwxOTQuNjkzMjFdLFstMS40OTk5MSwtNS42Mzg2NF0sWzEuODE4MDYsNi41OTIyNF1dLFtbMjAzLjg2OTczLDIxMi41NzIwOV0sWy00LjQ2NDIsLTUuMTgwMTZdLFsxLjEyOTEyLDEuMjc2MjNdXSxbWzIwNy40NDQ0NCwyMTYuMjIxODhdLFstMS4yNTI1LC0xLjE1NTM5XSxbMS4zMjEzMiwxLjIzODE3XV0sW1syMTEuNjMwNjksMjE5LjY3NTQzXSxbLTEuNDY2NzQsLTEuMDYxOV0sWzEuMTA1OTMsMC42NzIyOV1dLFtbMjE0Ljk3NjEzLDIyMS43MDkxXSxbLTEuMTA1OTMsLTAuNjcyMjldLFsxLjg0MTUyLDEuMTM5NjJdXSxbWzIyMC43NDEwOSwyMjQuNjgzNDZdLFstMS45OTU5NiwtMC44NDAyOF0sWzUuNzI1MTYsMi40MjQwMV1dLFtbMjM4LjgyMDA5LDIyOC41MTddLFstNi4yMTYyNSwtMC4xMDgxXSxbMi41NDIwNywtMC4wMzQ4M11dLFtbMjQ2LjQwOTQ3LDIyNy44NjcyNl0sWy0yLjUxMSwwLjM5Nzc4XSxbMCwwXV0sWzI0Ni43OTI3NiwyMjcuNzk3MzNdLFtbMjQ5LjQ0Mjg3LDIzOC43NjEwM10sWzAsMF0sWzAuMTk4NzcsMC43MzE3OF1dLFtbMjUwLjc2NDUzLDI0MC41MTEwOV0sWy0wLjY0OTQ2LC0wLjM5MTRdLFswLjY4MjI0LDAuNDI0ODRdXSxbWzI1My4wNTI0NCwyNDAuODgzOTddLFstMC43ODE4MywwLjE4NjIzXSxbMC43ODE4MywtMC4xODYyM11dLFtbMjU0LjkyNjU0LDIzOS41MTk2N10sWy0wLjQxNzQ0LDAuNjg2OF0sWzAsMF1dLFtbMjY5Ljc2NzI5LDIxNS4xMDYyMl0sWzAsMF0sWzAuODU2ODIsLTEuNDA5MzFdXSxbWzI2OC43ODY3MSwyMTAuOTg4NjddLFsxLjQwMDA3LDAuODcxODRdLFswLDBdXSxbWzI0NC4zNDU2MSwxOTYuMTMxMTFdLFswLDBdLFstMS4wNDI2MSwtMC42MjU4NF1dLFtbMjQxLjAwODMxLDE5Ni4yOTEyN10sWzAuOTc3ODksLTAuNzIyODFdLFstMC45Nzc4OSwwLjcyMjgxXV0sW1syMzkuODc2MTIsMTk5LjQzNDcyXSxbLTAuMjkyNDIsLTEuMTgwMzRdLFswLDBdXSxbWzI0Mi40MTI5NSwyMDkuODM3MzFdLFswLDBdLFstMS4zODg4OSwwLjM0N11dLFtbMjM4LjE4NTg5LDIxMC41NjE5OV0sWzEuNDI1MTcsLTAuMTM1NDNdLFstMy45OTQ3MywwLjQyMjQ4XV0sW1syMjYuMjk0NzMsMjA5LjUwNTQ5XSxbMy44NTc2NywxLjEyMDE0XSxbLTIuMjAxNTgsLTAuNjU2NTldXSxbWzIxOS45NTA3NiwyMDYuODYwNzNdLFsyLjAxNjAyLDEuMTAxNzFdLFswLDBdXSxbWzIxOC42NTEyOCwyMDYuMDcwNzldLFswLDBdLFstMC42MDgyNiwtMC4zNjk3Nl1dLFtbMjE2LjkzNzA5LDIwNS4wMjg3NF0sWzAuNTI1MzIsMC4zMTkzM10sWzAsMF1dLFtbMjE2LjkzNzA5LDIwNS4wMjg3NF0sWzAsMF0sWy0wLjc0NjUsLTAuNDUzNzldXSxbWzIxNC4wNjQ5NCwyMDIuNzE0OF0sWzAuNjYzNTYsMC40MDMzN10sWy0zLjYwMTM3LC0zLjMxMjgyXV0sW1syMDUuODMyNSwxOTAuNzgxMDNdLFsxLjgxNzk4LDQuNTQzMDldLFstMS42MjU0NCwtNC4wNzUxMV1dLFtbMjAzLjQzNzI2LDE3Ny45NjU0XSxbLTAuMDQzODcsNC4zODcxXSxbMC4wNjAyNSwtMS43NjY5MV1dLFtbMjAzLjk4OTM0LDE3Mi42OTY5NV0sWy0wLjMwNzM2LDEuNzQxMDNdLFswLjMxOTkyLC0xLjU4NTE5XV0sW1syMDQuOTExMTIsMTY4Ljc1MTMyXSxbLTAuMjI3NzMsMC45OTc1Ml0sWzAuMjI3NzMsLTAuOTk3NTJdXSxbWzIwNS44MzEyMSwxNjYuMDU0MjNdLFstMC4xODcwNiwwLjY4MTQ1XSxbMC4xODcwNiwtMC42ODE0NV1dLFtbMjA2LjIxOTk2LDE2NS4wNDA5OV0sWzAsMF0sWzAuNzIzODIsLTEuOTExNjVdXSxbWzIwNC40NTE3LDE1OS45MTQ1XSxbMS43NDg0NSwxLjA1ODkxXSxbMCwwXV1dLCZxdW90O2ZpbGxDb2xvciZxdW90OzpbMCwwLDAsMV19XX0iIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIwLjUiLz48cGF0aCBkPSJNMjU3LjM4NTY5LDE1Ny42MjIwNmMtMS43NTg0NywxLjc3Njc3IC00LjUzNDIsMi4wNDYyMiAtNi42MDE1NiwwLjY0MDc5bC0xLjMwMTA3LC0wLjg4NDk0Yy0wLjQ2MjY4LC0wLjM0NjI3IC0wLjk1MTk4LC0wLjY1NTQ1IC0xLjQ2MzM0LC0wLjkyNDY1Yy0wLjg1MzMzLC0wLjQ4MTk5IC0xLjkyOTgyLC0xLjAyMzYxIC0zLjE4ODc0LC0xLjU4NDZjLTEuMzc2MzIsLTAuNTY1MTkgLTIuNzg2MDgsLTEuMDQ1MzEgLTQuMjIxMjYsLTEuNDM3NjNjLTMuNTcyOTYsLTAuODc3MjkgLTcuMjgxNDEsLTEuMDU4MjUgLTEwLjkyMjgsLTAuNTMyOThjLTQuMDg3MTQsMC41OTg4NiAtNy45ODU5MSwyLjExNDM5IC0xMS40MDQ1LDQuNDMzMTNsLTAuOTI4NDksMC42OTM0OGMtMC40OTQxMywwLjMzNjIgLTAuOTY2MDIsMC43MDQgLTEuNDEyNywxLjEwMTFsLTEuMzUwNiwxLjM2NDE3bC0wLjgyNjQ5LDAuODM0NzljLTEuMjk3NDIsMS40Mzc3MSAtMi40NDM3LDMuMDA0OTEgLTMuNDIwODMsNC42NzY4OWMtMS42ODA5OCwyLjkwMjA1IC0yLjc5MjczLDYuMDk4MiAtMy4yNzU1OSw5LjQxNzAxYy0wLjA3NzU5LDAuNjg5MjIgLTAuMTM1MDMsMS4zNTgxIC0wLjE1MTc1LDIuMDY3MjhsOC4xMDM4LC0wLjA0MDQ4YzEuNjQ5OTMsLTAuMDA2NDMgMy4xNDI3MiwwLjk3NzQzIDMuNzg3NDIsMi40OTYyMWMwLjY0NDcsMS41MTg3NiAwLjMxNTUsMy4yNzYwNyAtMC44MzUyNCw0LjQ1ODQ4bC0xNy44ODA0LDE4LjA1OTk0Yy0wLjc1OTEsMC43Nzc1NiAtMS43OTYwMSwxLjIyMTczIC0yLjg4MjYxLDEuMjM0NzVjLTEuMDg2NiwwLjAxMzAyIC0yLjEzMzg3LC0wLjQwNjE1IC0yLjkxMTQsLTEuMTY1MjlsLTE3Ljk3ODQ5LC0xNy43OTk3NWMtMS4xNjI0OSwtMS4xNzA4NSAtMS41MDkyMywtMi45MjQ3OSAtMC44Nzk3NCwtNC40NDk5MmMwLjYyOTQ5LC0xLjUyNTEzIDIuMTEyMzgsLTIuNTIzODcgMy43NjIyOSwtMi41MzM5Mmw4LjczMTU0LC0wLjEwNDRjMC4yMjA5LC0xLjk2NTg5IDAuNTYyOTksLTMuOTE2MjggMS4wMjQzNCwtNS44Mzk5OWMxLjM3NTg3LC01LjUzNjI0IDMuODA1NDgsLTEwLjc1NTUzIDcuMTU2MDIsLTE1LjM3MjU2YzEuMTMxNCwtMS41NDg5NCAyLjM3Njk1LC0zLjAxMTE5IDMuNzI2MjUsLTQuMzc0NTNjMC44MjY0OSwtMC44MzQ3OSAxLjY3MzE0LC0xLjY4OTk0IDIuNDk5NjMsLTIuNTI0NzJjMS4yNzg0NiwtMS4xMDUzNyAyLjYyNjgzLC0yLjEyNzIyIDQuMDM2NzIsLTMuMDU5MTZjMC44MDk3LC0wLjU2MTU1IDEuNjUyODgsLTEuMDczMjEgMi41MjQ4NCwtMS41MzIxMmwxLjM3MzgsLTAuNzc2NzRjNS41OTA3NCwtMi44OTg2IDExLjc1NDg3LC00LjUyMDg5IDE4LjA0ODE5LC00Ljc0OTk2YzUuMzUxOTMsLTAuMTMyODggMTAuNjc5NjYsMC43NjIxIDE1LjY5NDQxLDIuNjM2NDRjMi4wNzI2NSwwLjc2Mzc4IDQuMDgyNCwxLjY4ODU0IDYuMDEwNzksMi43NjU4NWMxLjQyOTQ4LDAuODE5ODkgMi44MDc1MSwxLjcyNjQxIDQuMTI2MzQsMi43MTQ0OWMwLjYzOTcyLDAuNDU5NCAxLjI1MTExLDAuOTU3MDEgMS44MzA4NSwxLjQ5MDA5bDAuMzY2NDksMC4zNjI4NGMwLjUwOTAxLDAuNTAzOTUgMC43NTMzNSwwLjc0NTg2IDAuNzUzMzUsMC43NDU4NmMyLjExNzIyLDEuODg5MTggMi4zMjI5OCw1LjEyOTE2IDAuNDYxNzgsNy4yNzEwM2wtMC4yMDE1OSwwLjIwMzYxeiIgZmlsbD0iIzFhNjI2MiIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiLz48cGF0aCBkPSJNMjAwLjk3ODczLDE1Mi45NDAxNmMtMS4zNTkwOSwxLjM1Mjg0IC0yLjYxMTY4LDIuODA4NjIgLTMuNzQ2NjEsNC4zNTQzNWMtMy4yNTg2LDQuNDI2NDUgLTUuNjM0MTksOS40MzkwNiAtNi45OTY5OCwxNC43NjM5N2MtMC40OTIyOSwyLjE5Njc2IC0wLjgyNjQ1LDQuNDI1OTggLTAuOTk5OTQsNi42NzA1M2wtMC4wMTg1NCwwLjM0NDUxbC05Ljk4ODAzLDAuMDI5NjRjLTEuMDk0ODUsLTAuMDE1NTcgLTIuMDg3NzgsMC42NDAyNCAtMi41MDMyNCwxLjY1MzMzYy0wLjQxNTQ1LDEuMDEzMSAtMC4xNjg4MywyLjE3NzIzIDAuNjIxNzMsMi45MzQ4NGwxOC4wMzk1OCwxNy44NjAyM2MxLjA0NTc4LDEuMDM1MzggMi43MzI4OCwxLjAyNjk1IDMuNzY4MjYsLTAuMDE4ODJsMTcuNzc5NiwtMTcuOTU4MTRjMC43NjUyMywtMC43NjQ5NiAwLjk5MTg5LC0xLjkxNjczIDAuNTczNTksLTIuOTE0NjJjLTAuNDE4MzEsLTAuOTk3ODggLTEuMzk4NTIsLTEuNjQzNzIgLTIuNDgwNSwtMS42MzQzNmwtOS41MDE4MSwwLjAyNzJjMC4wMTA3NCwtMS4yNjA4MSAwLjA5OTE4LC0yLjUxOTc1IDAuMjY0ODEsLTMuNzY5NjljMC40Njk2MiwtMy41MjQ3IDEuNjA3ODgsLTYuOTI3MzQgMy4zNTM2LC0xMC4wMjUxOWMxLjAyNjYzLC0xLjc2NzExIDIuMjMzNywtMy40MjMwMyAzLjYwMTg2LC00Ljk0MTE3bDAuOTQ3NDMsLTAuOTU2OTVjMC40NDM0OSwtMC40NDc5MyAwLjg2NjgsLTAuODc1NTEgMS4yNDk4MiwtMS4yNjIzNnYwYzAuNTQ0MjYsLTAuNTQ5NzQgMi4wOTk1LC0xLjUwOTc0IDIuNTYzMTQsLTEuOTc4MDNjMy41OTcyNSwtMi40MzIxIDcuNjk1MjUsLTQuMDI0MzIgMTEuOTkwOTEsLTQuNjU4OTJjMy44NDQ1OSwtMC41NTE3MSA3Ljc1OTQzLC0wLjM1ODA0IDExLjUzMDc5LDAuNTcwNDVjMS41MTgzNiwwLjQxMjQ2IDMuMDA5NTQsMC45MTkyNSA0LjQ2NDc4LDEuNTE3NDVjMS4yOTk2NCwwLjYwMTMxIDIuMjc0NzMsMS4xMjMxOSAzLjA0NjYxLDEuNTI0NTRjMC43MzM5LDAuNDA4NzYgMS40NDAwNSwwLjg2NTQ2IDIuMTEzODYsMS4zNjcxMmMwLjU0ODUyLDAuMzAxMTUgMC43OTI4NSwwLjU0MzA2IDAuNzkyODUsMC41NDMwNmMxLjQ5MTg0LDAuOTk4NzQgMy40ODAyNiwwLjgxMTAyIDQuNzU4ODUsLTAuNDQ5MjRsMC4yMDE1OCwtMC4yMDM2YzAuNjU0MDIsLTAuNzU0NTIgMC45ODA0NCwtMS43Mzg1NiAwLjkwNzAzLC0yLjczNDM3Yy0wLjA3MzQxLC0wLjk5NTgyIC0wLjU0MDYyLC0xLjkyMTMyIC0xLjI5ODIyLC0yLjU3MTc5YzAsMCAtMC4yMjM5NywtMC4yMjE3NCAtMC42NzE5LC0wLjY2NTIyYy0wLjcwNTA2LC0wLjY1MDk5IC0xLjQ0NTM0LC0xLjI2Mjc3IC0yLjIxNzQ5LC0xLjgzMjU5Yy0xLjI2OTU0LC0wLjk0MTA5IC0yLjU5MzAyLC0xLjgwNzE1IC0zLjk2MzY2LC0yLjU5Mzc0Yy0xLjc2MDc2LC0xLjAwMzIxIC0zLjU5MzUzLC0xLjg3NDM5IC01LjQ4MzIzLC0yLjYwNjRjLTQuODM5NDksLTEuODA5NDcgLTkuOTgxOSwtMi42NzEwOSAtMTUuMTQ2ODksLTIuNTM3ODhjLTYuMDUxNzQsMC4yMTEyOCAtMTEuOTgxMTUsMS43NjMyNSAtMTcuMzYwMzgsNC41NDM5M2MtMS4zMzEzNSwwLjcxMDIgLTIuNjI1NzYsMS40ODc1NSAtMy44NzgyOCwyLjMyOTAyYy0xLjM0NjEsMC44ODg2OCAtMi42MzM3NiwxLjg2Mjg1IC0zLjg1NTA5LDIuOTE2NDRjLTAuODA2MzMsMC44MTQ0MyAtMS42MzI4MiwxLjY0OTIxIC0yLjQzOTE1LDIuNDYzNjRNMTk5LjE2NjQyLDE1MS4xMDU1NnYwYzAuODA2MzMsLTAuODE0NDMgMS42NzMxNCwtMS42ODk5NCAyLjYyMDU3LC0yLjY0NjljMS4yNzkzOSwtMS4xMDQyMiAyLjYyNzcsLTIuMTI2IDQuMDM2NzIsLTMuMDU5MTdjMC44OTQ1MSwtMC42MDIwOSAxLjgyNTUzLC0xLjE0ODA0IDIuNzg3NzEsLTEuNjM0NzNsMS4zMzMyOCwtMC43NzY1NGwwLjEyMDk1LC0wLjEyMjE2YzUuNzY0NjIsLTIuOTY4OTcgMTIuMTE0NDIsLTQuNjI2NTUgMTguNTk0NjksLTQuODUzOTljNS41NTI1MSwtMC4xMzkyMyAxMS4wODAxLDAuNzg5MTkgMTYuMjgyNDQsMi43MzQ4YzIuMDM3NjksMC43OTM2NiA0LjAxMzEzLDEuNzM4NjggNS45MDk3OCwyLjgyNzE0YzEuNDQxNzYsMC44MjA5OCAyLjgzMzE5LDEuNzI3MzUgNC4xNjY4NSwyLjcxNDI5YzAuNjk2NDIsMC40OTYyIDEuMzYyMTYsMS4wMzQxNCAxLjk5MzUzLDEuNjEwODRjMC4xMjIxNiwwLjEyMDk1IC0wLjA0MDcxLC0wLjA0MDMxIDAuMzQ2MTQsMC4zNDI2OWMwLjM4Njg2LDAuMzgzIDAuNjUxNTUsMC42NDUwNyAwLjc3MzcxLDAuNzY2MDFjMi42OTIxNiwyLjQxMTA3IDIuOTY0NzQsNi41MzEzMSAwLjYxMzYyLDkuMjc2MDFsLTAuNDgzODEsMC40ODg2NmMtMi4yNjcwNSwyLjI2MzMzIC01LjgyNTksMi41ODc3NiAtOC40NjQ4MywwLjc3MTY0bC0wLjcxMTYsLTAuNTAyOTVsLTAuNDY4MjksLTAuNDYzNjVjLTAuNDAwNTUsLTAuMjk3NDIgLTAuODIxNjQsLTAuNTY2MTQgLTEuMjYwMTQsLTAuODA0MDlsLTAuMTQyNTMsLTAuMTQxMTFjLTAuNzMxNTcsLTAuNDQyMDcgLTEuOTA5NDUsLTEuMDAzNDYgLTIuODg0MTMsLTEuNDQ0MzFjLTEuMzEwNjksLTAuNTMzNTEgLTIuNjUyNTgsLTAuOTg2OSAtNC4wMTgyNywtMS4zNTc2MWMtMy40MjAwNSwtMC44NDI0MSAtNi45NzA0NiwtMS4wMTcyOSAtMTAuNDU2NzMsLTAuNTE1MDRjLTMuODMwMDcsMC41NTUxIC03LjQ4NjA2LDEuOTY1MTcgLTEwLjY5NjkyLDQuMTI1N2wtMS4wMDkzNCwwLjczNDRsLTEuMTcwNzksMC44NTY3N2wtMC4zNjI4NCwwLjM2NjQ5bC0xLjA2ODM4LDEuMDc5MTJsLTAuODA2MzMsMC44MTQ0M2MtMS4xOTQ5OSwxLjM4MDcyIC0yLjI0NjQ4LDIuODc5MzggLTMuMTM4MjIsNC40NzI4N2MtMS42MDIyNywyLjc0OTA0IC0yLjY2MDE3LDUuNzgwODkgLTMuMTE1OTUsOC45Mjk5NmwtMC4wNTgyNSwwLjUwNjc4bDYuNTYzNDcsLTAuMTU0MzVjMi4yNDU5NSwtMC4wMTk5OSA0LjI4MDg3LDEuMzIwNTIgNS4xNDkxLDMuMzkxOTdjMC44NjgyMywyLjA3MTQ1IDAuMzk3MjQsNC40NjIyOSAtMS4xOTE3Niw2LjA0OTY3bC0xNy43OTk3NSwxNy45Nzg0OWMtMi4xNDg2OCwyLjE3MDI3IC01LjY0OTksMi4xODc3NiAtNy44MjAxNiwwLjAzOTA3bC0xNy45Nzg1LC0xNy43OTk3NmMtMS42MDQ3OCwtMS41NzE0MiAtMi4wOTk2NCwtMy45NTc0NSAtMS4yNTIxNSwtNi4wMzc0N2MwLjg0NzQ5LC0yLjA4MDAzIDIuODY4OTIsLTMuNDQwOCA1LjExNDk2LC0zLjQ0MzI1bDcuMzUzNDksLTAuMTc4NTZjMC4yMzUwMiwtMS42MjE5OCAwLjUzNzcsLTMuMjE3MjYgMC45MDgwNiwtNC43ODU4OWMxLjQ2ODUxLC01LjY2MTk4IDQuMDA3LC0xMC45OTA1OSA3LjQ3ODU1LC0xNS42OTgzNGMxLjIwOTM5LC0xLjY0NTY3IDIuNTQyNzEsLTMuMTk2NTkgMy45ODgzLC00LjYzOTJ6IiBmaWxsPSIjMWE2MjYyIiBzdHJva2U9IiMxYTYyNjIiIHN0cm9rZS13aWR0aD0iMyIvPjxwYXRoIGQ9Ik0yMDAuOTcxNjgsMTUzLjAwNjkxYzAuODA2MzMsLTAuODE0NDMgMS42Mzc5MywtMS42NTI3OCAyLjQ0NDI2LC0yLjQ2NzIxYzEuMjIxMzIsLTEuMDUzNiAyLjUxNjU1LC0yLjAzMTM1IDMuODYyNjUsLTIuOTIwMDRjMS4yNTI1MiwtMC44NDE0NiAyLjU1NDA2LC0xLjYyMDc1IDMuODg1NCwtMi4zMzA5NWM1LjM3OTI0LC0yLjc4MDY4IDExLjMzMzMsLTQuMzMwMTQgMTcuMzg1MDUsLTQuNTQxNDJjNS4xNjUsLTAuMTMzMjEgMTAuMzIxNywwLjczNzc2IDE1LjE2MTE5LDIuNTQ3MjNjMS44ODk2OSwwLjczMjAxIDMuNzI2NSwxLjYwNzczIDUuNDg3MjcsMi42MTA5NGMxLjM3MDY0LDAuNzg2NTggMi42OTY3MSwxLjY1NjM3IDMuOTY2MjUsMi41OTc0NmMwLjc3MjE1LDAuNTY5ODEgMS41MTM3OCwxLjE4MzkzIDIuMjE4ODYsMS44MzQ5MmMwLjQ0NzkzLDAuNDQzNDkgMC42NzIzMiwwLjY2NjAxIDAuNjcyMzIsMC42NjYwMWMwLjc1NzYsMC42NTA0NyAxLjIyNjAyLDEuNTc4NTQgMS4yOTk0MywyLjU3NDM1YzAuMDczNDEsMC45OTU4MiAtMC4yNTIxNywxLjk4MTg5IC0wLjkwNjIsMi43MzY0MmwtMC4yMDE1NCwwLjIwMzczYy0xLjI3ODU5LDEuMjYwMjYgLTMuMzQ5MzcsMS40ODA0OSAtNC44NDEyLDAuNDgxNzVjMCwwIC0wLjI0Mzk2LC0wLjI0MTAzIC0wLjc5MjQ5LC0wLjU0MjJjLTAuNjczODEsLTAuNTAxNjYgLTEuMzc4ODcsLTAuOTU2MDYgLTIuMTEyNzgsLTEuMzY0ODJjLTAuNzcxODgsLTAuNDAxMzQgLTEuNzQ1MjYsLTAuOTIwMTYgLTMuMDQ0ODksLTEuNTIxNDhjLTEuNDU1MjQsLTAuNTk4MiAtMi45NDM1MSwtMS4xMDA4NCAtNC40NjE4OSwtMS41MTMzYy0zLjc3MTM2LC0wLjkyODUgLTcuNjc1OTUsLTEuMTEyNjYgLTExLjUyMDUzLC0wLjU2MDk1Yy00LjI5NTY2LDAuNjM0NiAtOC4zNzMyNywyLjIzNDA3IC0xMS45NzA1Myw0LjY2NjE2Yy0wLjQ2MzY0LDAuNDY4MyAtMi4wMTExMywxLjQyODM1IC0yLjU1NTQsMS45NzgwOXYwYy0wLjM4MywwLjM4Njg2IC0wLjgwMTYsMC44MTM3OCAtMS4yNDUwOSwxLjI2MTcybC0wLjk0MzM4LDAuOTU1OThjLTEuMzY4MTUsMS41MTgxNSAtMi41NTUwOSwzLjE2MTY1IC0zLjU4MTcxLDQuOTI4NzZjLTEuNzQ1NzMsMy4wOTc4NSAtMi44NzQ3MSw2LjQ1NTU0IC0zLjM0NDM0LDkuOTgwMjVjLTAuMTY1NjMsMS4yNDk5NCAtMC4yNTk2LDIuNDk4MTggLTAuMjcwMzUsMy43NTlsOS40NzE1NywtMC4wNDU1NmMxLjA4MTk4LC0wLjAwOTM3IDIuMDQ5MDYsMC42MzY2NyAyLjQ2NzM3LDEuNjM0NTRjMC40MTgzMSwwLjk5Nzg4IDAuMjAyMTcsMi4yMzc0OCAtMC41NjMwNiwzLjAwMjQ2bC0xNy44MDQ3MywxNy45NTI1NWMtMS4wMzUzOCwxLjA0NTc4IC0yLjcyNTg2LDEuMDUyMDkgLTMuNzcxNjUsMC4wMTY3MWwtMTguMDU2NywtMTcuODgzNWMtMC43OTA1NiwtMC43NTc1OSAtMS4wMzgwNCwtMS45MjQ5MSAtMC42MjI1OCwtMi45MzhjMC40MTU0NSwtMS4wMTMxIDEuNDk0OTEsLTEuNjg3NjkgMi41ODk3NiwtMS42NzIxM2w5Ljk4NzMsLTAuMDMyNjdsLTAuMDY2OTQsLTAuMzIxMzZjMC4xNzM0OCwtMi4yNDQ1NCAwLjUwNjE5LC00LjQ4NDM4IDAuOTk4NDcsLTYuNjgxMTRjMS4zNjI4LC01LjMyNDkgMy43NDc0NiwtMTAuMzY1MTEgNy4wMDYwNSwtMTQuNzkxNTVjMS4xMzQ5MiwtMS41NDU3MyAyLjM5NDk1LC0zLjAwOTE0IDMuNzU0MDQsLTQuMzYxOTgiIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O25vSG92ZXImcXVvdDs6ZmFsc2UsJnF1b3Q7b3JpZ0l0ZW0mcXVvdDs6WyZxdW90O1BhdGgmcXVvdDsseyZxdW90O2FwcGx5TWF0cml4JnF1b3Q7OnRydWUsJnF1b3Q7c2VnbWVudHMmcXVvdDs6W1tbMjY1LjEzOTExLDEzOC4zNzk3OV0sWzAsMF0sWy0xLjg0MjIyLC0xLjEzODM3XV0sW1syNTkuMzc0MTUsMTM1LjQwNTQzXSxbMS45OTUzNSwwLjg0MTU4XSxbLTUuNzE2MjYsLTIuNDE5NDFdXSxbWzI0MS4zMjI4LDEzMS41ODg3XSxbNi4yMDYzNSwwLjEwMTQ5XSxbLTIuNTQyMDcsMC4wMzQ5NF1dLFtbMjMzLjczMzQyLDEzMi4yMzg0NF0sWzIuNTExMDEsLTAuMzk3NjZdLFswLDBdXSxbMjMzLjM1MDEzLDEzMi4zMDgzN10sW1syMzAuNzAwMDIsMTIxLjM0NDY3XSxbMCwwXSxbLTAuMjY5ODIsLTEuMjA2NzRdXSxbWzIyOC4yMjc4OCwxMTkuMDI4MjRdLFsxLjIyMTcyLDAuMTkwODZdLFstMS4yMjE3MiwtMC4xOTA4Nl1dLFtbMjI1LjE2NzAyLDEyMC40ODAzMV0sWzAuNjI1MDEsLTEuMDY2OTRdLFswLDBdXSxbWzIxMC4yNzU4NSwxNDQuOTc2NzFdLFswLDBdLFstMC44NjMyNiwxLjQyMDA5XV0sW1syMTEuMjg0MDcsMTQ5LjExMTA3XSxbLTEuNDIwMDksLTAuODYzMjZdLFswLDBdXSxbWzIzNS42Njk4OCwxNjMuOTM1MDFdLFswLDBdLFsxLjA0MDgzLDAuNjQwMV1dLFtbMjM5LjAyMTc5LDE2My44MDEyNF0sWy0wLjk4NjUxLDAuNzIxMDFdLFswLjk4NjUxLC0wLjcyMTAxXV0sW1syNDAuMTY3MDEsMTYwLjY0ODJdLFswLjI5Mzg0LDEuMTg2MDVdLFswLDBdXSxbWzIzNy42NDcsMTUwLjIxNzk3XSxbMCwwXSxbMS4zODc3NywtMC4zMTg2Ml1dLFtbMjQxLjg1NzI1LDE0OS41MjA5M10sWy0xLjQxNjQxLDAuMTQ1NjNdLFszLjk5NDgzLC0wLjQwNzg1XV0sW1syNTMuNzQ4NCwxNTAuNTc3NDRdLFstMy44NjAzNywtMS4xMDU3Nl0sWzIuMjEwMTUsMC42NjQ2MV1dLFtbMjYwLjEyMDAzLDE1My4yMzkwMV0sWy0yLjAyNjE4LC0xLjEwNV0sWzAsMF1dLFtbMjYxLjQxOTUsMTU0LjAyODk0XSxbMCwwXSxbMC42MDgyNiwwLjM2OTc2XV0sW1syNjMuMTMzNjksMTU1LjA3MDk5XSxbLTAuNTI1MzIsLTAuMzE5MzNdLFswLDBdXSxbWzI2My4xMzM2OSwxNTUuMDcwOTldLFswLDBdLFswLjc0NjUsMC40NTM3OV1dLFtbMjY1Ljk3ODIsMTU3LjM2ODExXSxbLTAuNjM1OTEsLTAuMzg2NTZdLFszLjYxNDI5LDMuMzE0MDZdXSxbWzI3NC4yMzgyOSwxNjkuMzE4N10sWy0xLjgyMjg0LC00LjU1MjNdLFsxLjYxMzU4LDQuMDc4NTRdXSxbWzI3Ni42MzM1MywxODIuMTM0MzNdLFswLjAzMTU2LC00LjM4NjAyXSxbLTAuMDU1MTUsMS43NzU5Nl1dLFtbMjc2LjEzNjc0LDE4Ny40MzY0XSxbMC4yNzU3MywtMS43NTUzXSxbLTAuMzE5OTIsMS41ODUxOV1dLFtbMjc1LjI2MDUyLDE5MS4xODI1Ml0sWzAuMjM4NTgsLTAuOTUzMDZdLFstMC4yNTY2NywwLjkxMzI4XV0sW1syNzQuMzEyNzYsMTkzLjg2MjhdLFswLjM3NDQ3LC0wLjg3MTYzXSxbLTAuMTg3MDYsMC42ODE0NV1dLFtbMjczLjkyNDAyLDE5NC44NzYwNF0sWzAsMF0sWy0wLjcwNjExLDEuOTAwNDddXSxbWzI3NS42NjQ2MywxOTkuOTg1NzNdLFstMS43MTk0MywtMS4wNzQyMV0sWzAsMF1dLFtbMjc1Ljk0MTEyLDIwMC4xNTM4XSxbMCwwXSxbMS4wMDAyMSwwLjUyMDY4XV0sW1syNzkuMTgyNDIsMjAwLjQzMzU0XSxbLTEuMDc0NjMsMC4zNDE2MV0sWzEuMDc0NjMsLTAuMzQxNjFdXSxbWzI4MS42NjcyMiwxOTguMzMzNTJdLFstMC41MTU5NywxLjAwMjY2XSxbMCwwXV0sW1syODIuMjIxODUsMTk3LjQyMTEyXSxbLTAuMzY5NzYsMC42MDgyNl0sWzAuNTMwMzEsLTAuOTQ1MDldXSxbWzI4My42NTM3NCwxOTQuNTA1MDNdLFstMC40MjM1NiwwLjk5NzUxXSxbMC43MDEwNCwtMS42NDExN11dLFtbMjg1LjQ2NDExLDE4OS40NzEzN10sWy0wLjUwNDgzLDEuNzExNzNdLFswLjY0MDU1LC0yLjE5NzA0XV0sW1syODYuODkwMTYsMTgyLjc2NTJdLFstMC4zMDg4NSwyLjI2NzU5XSxbMC43MTkzNSwtNS43OTAyXV0sW1syODUuNzA4MzksMTY1LjQ2MTgyXSxbMS40OTk5MSw1LjYzODY0XSxbLTEuODE4MDYsLTYuNTkyMjRdXSxbWzI3Ni4xNjc0NCwxNDcuNTgyOTNdLFs0LjQ2NDIsNS4xODAxNl0sWy0xLjEyOTAzLC0xLjI3NjMxXV0sW1syNzIuNTkyNzMsMTQzLjkzMzE1XSxbMS4yNTI1NiwxLjE1NTMyXSxbLTEuMzI4OTUsLTEuMjQ1NzRdXSxbWzI2OC4zNzg4MywxNDAuNDYyOF0sWzEuNDc3NCwxLjA2NTQ2XSxbLTEuMTA1OTMsLTAuNjcyMjldXSxbWzI2NS4wMzMzOSwxMzguNDI5MTNdLFsxLjEwNTkzLDAuNjcyMjldLFswLDBdXV0sJnF1b3Q7ZmlsbENvbG9yJnF1b3Q7OlswLDAsMCwxXX1dfSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjAuNSIvPjwvZz48L2c+PC9zdmc+PCEtLXJvdGF0aW9uQ2VudGVyOjcyLjQ4NjIxOTk5OTk5OTk3OjcyLjQ4NjIyLS0+';
- const tr_icon = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIyMHB4IiBoZWlnaHQ9IjIwcHgiIHZpZXdCb3g9Ii0yLjI2MzMxLC0yLjQxMTU2LDIwLDIwIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjMyLjI3NjYyLC0xNzIuNTczMTIpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGw9IiNmZmZmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWRhc2hhcnJheT0iIiBzdHJva2UtZGFzaG9mZnNldD0iMCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0yNDMuOTE0MTksMTgzLjk1OTEzYzAuMzY1OSwwIDAuNjcyNDYsMC4yOTYxOSAwLjY3MjQ2LDAuNjcwMTFjMCwwLjM2NDA4IC0wLjMwNjU2LDAuNjU5MjkgLTAuNjcyNDYsMC42NTkyOWMtMC4zNjU5LDAgLTAuNjYyNTcsLTAuMjk1MjEgLTAuNjYyNTcsLTAuNjU5MjljMCwtMC4zNzM5MiAwLjI5NjY3LC0wLjY3MDExIDAuNjYyNTcsLTAuNjcwMTF6TTIzNS4wNDMwMiwxNzkuMDUwMDN2MC4wMDk4NGMtMC4xODc4OSwwLjQ4MjE2IC0wLjcwMTE0LDAuNzc3MzYgLTEuMjI1MjcsMC42NDk0NGMtMC41NjQ2NywtMC4xMjc5MiAtMC45MjA2OCwtMC42OTg2NCAtMC43OTExMywtMS4yNTk1MmMwLjU0MzksLTIuMzQxOTMgMi40NDI2MiwtNC4zODg2NiA0Ljg5NDE0LC01LjEyNjY3YzEuMjE3MzUsLTAuMzczOTIgMi41NDE1MiwtMC40MjMxMiAzLjgxNzIyLC0wLjE0ODU4YzEuMDk4NjgsMC4yMzYxNiAyLjE0NTk1LDAuNzM4MDEgMy4wNDU4NiwxLjQzNjY1bDAuOTg4OTEsLTAuOTg0YzAuNDU0OSwtMC40NDE4MiAxLjIyNzI1LC0wLjEyNzkyIDEuMjI3MjUsMC41MTI2NnY0LjY5MjcyYzAsMC4zOTQ1OSAtMC4zMjYzNCwwLjcxOTMxIC0wLjcyMjg5LDAuNzE5MzFoLTQuNzE2MTRjLTAuNjQzNzksMCAtMC45NjAyNCwtMC43Njg1MSAtMC41MTUyMywtMS4yMjExNWwxLjE1NzAzLC0xLjE1MDNjLTAuOTA5OCwtMC44MzY0MSAtMi4yNDM4NSwtMS4yNjkzNiAtMy41Nzk4NywtMS4wNDMwNWMtMS41MjI5MywwLjIyNTM0IC0yLjk3NjYzLDEuMzQ3MSAtMy41Nzk4NywyLjkxMjY2ek0yNDEuMjkzMzcsMTg1LjQwNjZjMC40MTUzNSwwIDAuNzQxNjksMC4zMjQ3MyAwLjc0MTY5LDAuNzM4MDFjMCwwLjQxMzI4IC0wLjMyNjM0LDAuNzQ3ODQgLTAuNzQxNjksMC43NDc4NGMtMC40MTUzNSwwIC0wLjc1MTU4LC0wLjMzNDU2IC0wLjc1MTU4LC0wLjc0Nzg0YzAsLTAuNDEzMjggMC4zMzYyMywtMC43MzgwMSAwLjc1MTU4LC0wLjczODAxek0yMzUuNjE2NTksMTgzLjgxMjMyYzAuNTA1MzQsMCAwLjkwOTgxLDAuNDAzNDQgMC45MDk4MSwwLjkwNTI4YzAsMC40OTIgLTAuNDA0NDcsMC44OTU0NCAtMC45MDk4MSwwLjg5NTQ0Yy0wLjUwNDM1LDAgLTAuOTA5OCwtMC40MDM0NSAtMC45MDk4LC0wLjg5NTQ0YzAsLTAuNTAxODUgMC40MDU0NSwtMC45MDUyOCAwLjkwOTgsLTAuOTA1Mjh6TTIzNC4wMzQ2MywxODEuMTU1NDFjMC41NDM5LDAgMC45ODg5MSwwLjQ0MjggMC45ODg5MSwwLjk4NGMwLDAuNTUxMDQgLTAuNDQ1MDEsMC45ODQgLTAuOTg4OTEsMC45ODRjLTAuNTQzOSwwIC0wLjk4ODkyLC0wLjQzMjk2IC0wLjk4ODkyLC0wLjk4NGMwLC0wLjU0MTIgMC40NDUwMiwtMC45ODQgMC45ODg5MiwtMC45ODR6TTIzOC4yNjc2OCwxODUuMzQ2ODdjMC40NTQ5LDAgMC44MjA4LDAuMzczOTMgMC44MjA4LDAuODI2NTdjMCwwLjQ1MjY0IC0wLjM2NTksMC44MjY1NiAtMC44MjA4LDAuODI2NTZjLTAuNDU0OSwwIC0wLjgzMTY3LC0wLjM3MzkyIC0wLjgzMTY3LC0wLjgyNjU2YzAsLTAuNDUyNjQgMC4zNzY3NywtMC44MjY1NyAwLjgzMTY3LC0wLjgyNjU3eiIgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aW5kZXgmcXVvdDs6bnVsbH0iIHN0cm9rZT0iIzI1ODM4MyIgc3Ryb2tlLXdpZHRoPSIxLjUiLz48cGF0aCBkPSJNMjQzLjkxNDE5LDE4My45NTkxM2MwLjM2NTksMCAwLjY3MjQ2LDAuMjk2MTkgMC42NzI0NiwwLjY3MDExYzAsMC4zNjQwOCAtMC4zMDY1NiwwLjY1OTI4IC0wLjY3MjQ2LDAuNjU5MjhjLTAuMzY1OSwwIC0wLjY2MjU3LC0wLjI5NTIgLTAuNjYyNTcsLTAuNjU5MjhjMCwtMC4zNzM5MiAwLjI5NjY3LC0wLjY3MDExIDAuNjYyNTcsLTAuNjcwMTF6TTIzNS4wNDMwMiwxNzkuMDUwMDN2MC4wMDk4NGMtMC4xODc4OSwwLjQ4MjE2IC0wLjcwMTE0LDAuNzc3MzYgLTEuMjI1MjcsMC42NDk0NGMtMC41NjQ2NywtMC4xMjc5MiAtMC45MjA2OCwtMC42OTg2NCAtMC43OTExMywtMS4yNTk1M2MwLjU0MzksLTIuMzQxOTMgMi40NDI2MiwtNC4zODg2NiA0Ljg5NDE0LC01LjEyNjY2YzEuMjE3MzYsLTAuMzczOTIgMi41NDE1MSwtMC40MjMxMiAzLjgxNzIxLC0wLjE0ODU4YzEuMDk4NjksMC4yMzYxNiAyLjE0NTk1LDAuNzM4IDMuMDQ1ODYsMS40MzY2NWwwLjk4ODkyLC0wLjk4NGMwLjQ1NDksLTAuNDQxODIgMS4yMjcyNCwtMC4xMjc5MiAxLjIyNzI0LDAuNTEyNjd2NC42OTI3MmMwLDAuMzk0NTkgLTAuMzI2MzQsMC43MTkzMSAtMC43MjI5LDAuNzE5MzFoLTQuNzE2MTRjLTAuNjQzNzgsMCAtMC45NjAyNCwtMC43Njg1MSAtMC41MTUyMywtMS4yMjExNWwxLjE1NzAzLC0xLjE1MDNjLTAuOTA5OCwtMC44MzY0IC0yLjI0Mzg1LC0xLjI2OTM3IC0zLjU3OTg3LC0xLjA0MzA1Yy0xLjUyMjkzLDAuMjI1MzQgLTIuOTc2NjQsMS4zNDcxIC0zLjU3OTg4LDIuOTEyNjV6TTI0MS4yOTMzNiwxODUuNDA2NmMwLjQxNTM0LDAgMC43NDE2OSwwLjMyNDcyIDAuNzQxNjksMC43MzhjMCwwLjQxMzI4IC0wLjMyNjM0LDAuNzQ3ODQgLTAuNzQxNjksMC43NDc4NGMtMC40MTUzNCwwIC0wLjc1MTU4LC0wLjMzNDU2IC0wLjc1MTU4LC0wLjc0Nzg0YzAsLTAuNDEzMjggMC4zMzYyMywtMC43MzggMC43NTE1OCwtMC43Mzh6TTIzNS42MTY1OSwxODMuODEyMzJjMC41MDUzNCwwIDAuOTA5OCwwLjQwMzQ0IDAuOTA5OCwwLjkwNTI4YzAsMC40OTIgLTAuNDA0NDcsMC44OTU0NCAtMC45MDk4LDAuODk1NDRjLTAuNTA0MzUsMCAtMC45MDk4LC0wLjQwMzQ0IC0wLjkwOTgsLTAuODk1NDRjMCwtMC41MDE4NCAwLjQwNTQ2LC0wLjkwNTI4IDAuOTA5OCwtMC45MDUyOHpNMjM0LjAzNDYyLDE4MS4xNTU0MWMwLjU0MzksMCAwLjk4ODkyLDAuNDQyOCAwLjk4ODkyLDAuOTg0YzAsMC41NTEwNCAtMC40NDUwMSwwLjk4NCAtMC45ODg5MiwwLjk4NGMtMC41NDM5LDAgLTAuOTg4OTIsLTAuNDMyOTYgLTAuOTg4OTIsLTAuOTg0YzAsLTAuNTQxMiAwLjQ0NTAxLC0wLjk4NCAwLjk4ODkyLC0wLjk4NHpNMjM4LjI2NzY4LDE4NS4zNDY4N2MwLjQ1NDksMCAwLjgyMDgsMC4zNzM5MiAwLjgyMDgsMC44MjY1NmMwLDAuNDUyNjQgLTAuMzY1OSwwLjgyNjU2IC0wLjgyMDgsMC44MjY1NmMtMC40NTQ5LDAgLTAuODMxNjgsLTAuMzczOTIgLTAuODMxNjgsLTAuODI2NTZjMCwtMC40NTI2NCAwLjM3Njc4LC0wLjgyNjU2IDAuODMxNjgsLTAuODI2NTZ6IiBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpbmRleCZxdW90OzpudWxsfSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiLz48L2c+PC9nPjwvc3ZnPjwhLS1yb3RhdGlvbkNlbnRlcjo3LjcyMzM3OTk5OTk5OTk5Mjo3LjQyNjg4MDAwMDAwMDAxMS0tPg==';
- const tl_icon = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIyMHB4IiBoZWlnaHQ9IjIwcHgiIHZpZXdCb3g9Ii0yLjI2MzMxLC0yLjQxMTU2LDIwLDIwIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjM0LjUxMzMxLC0xNzUuMDgyNykiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbD0iI2ZmZmZmZiIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTIzOS4wMTE2OSwxODcuMDQwOGMwLDAuMzY0MDggLTAuMjk2NjcsMC42NTkyOSAtMC42NjI1NywwLjY1OTI5Yy0wLjM2NTksMCAtMC42NzI0NiwtMC4yOTUyMSAtMC42NzI0NiwtMC42NTkyOWMwLC0wLjM3MzkyIDAuMzA2NTYsLTAuNjcwMTEgMC42NzI0NiwtMC42NzAxMWMwLjM2NTksMCAwLjY2MjU3LDAuMjk2MTkgMC42NjI1NywwLjY3MDExek0yNDcuMjIwMjgsMTgxLjQ2MTZjLTAuNjAzMjQsLTEuNTY1NTYgLTIuMDU2OTQsLTIuNjg3MzIgLTMuNTc5ODcsLTIuOTEyNjZjLTEuMzM2MDIsLTAuMjI2MzEgLTIuNjcwMDcsMC4yMDY2NCAtMy41Nzk4NywxLjA0MzA1bDEuMTU3MDMsMS4xNTAzYzAuNDQ1MDEsMC40NTI2NCAwLjEyODU2LDEuMjIxMTUgLTAuNTE1MjMsMS4yMjExNWgtNC43MTYxNGMtMC4zOTY1NSwwIC0wLjcyMjg5LC0wLjMyNDcyIC0wLjcyMjg5LC0wLjcxOTMxdi00LjY5MjcyYzAsLTAuNjQwNTggMC43NzIzNSwtMC45NTQ0OCAxLjIyNzI1LC0wLjUxMjY2bDAuOTg4OTEsMC45ODRjMC44OTk5MSwtMC42OTg2NCAxLjk0NzE4LC0xLjIwMDQ5IDMuMDQ1ODYsLTEuNDM2NjVjMS4yNzU3LC0wLjI3NDU0IDIuNTk5ODcsLTAuMjI1MzQgMy44MTcyMiwwLjE0ODU4YzIuNDUxNTIsMC43MzgwMSA0LjM1MDI0LDIuNzg0NzQgNC44OTQxNCw1LjEyNjY3YzAuMTI5NTUsMC41NjA4OCAtMC4yMjY0NiwxLjEzMTYgLTAuNzkxMTMsMS4yNTk1MmMtMC41MjQxMywwLjEyNzkyIC0xLjAzNzM4LC0wLjE2NzI4IC0xLjIyNTI3LC0wLjY0OTQ0di0wLjAwOTg0ek0yNDEuNzIxNTIsMTg4LjU1NjE3YzAsMC40MTMyOCAtMC4zMzYyMywwLjc0Nzg0IC0wLjc1MTU4LDAuNzQ3ODRjLTAuNDE1MzUsMCAtMC43NDE2OSwtMC4zMzQ1NiAtMC43NDE2OSwtMC43NDc4NGMwLC0wLjQxMzI4IDAuMzI2MzQsLTAuNzM4MDEgMC43NDE2OSwtMC43MzgwMWMwLjQxNTM1LDAgMC43NTE1OCwwLjMyNDczIDAuNzUxNTgsMC43MzgwMXpNMjQ3LjU1NjUyLDE4Ny4xMjkxNmMwLDAuNDkxOTkgLTAuNDA1NDUsMC44OTU0NCAtMC45MDk4LDAuODk1NDRjLTAuNTA1MzQsMCAtMC45MDk4MSwtMC40MDM0NCAtMC45MDk4MSwtMC44OTU0NGMwLC0wLjUwMTg0IDAuNDA0NDcsLTAuOTA1MjggMC45MDk4MSwtMC45MDUyOGMwLjUwNDM1LDAgMC45MDk4LDAuNDAzNDMgMC45MDk4LDAuOTA1Mjh6TTI0OS4yMTc2LDE4NC41NTA5N2MwLDAuNTUxMDQgLTAuNDQ1MDIsMC45ODQgLTAuOTg4OTIsMC45ODRjLTAuNTQzOSwwIC0wLjk4ODkxLC0wLjQzMjk2IC0wLjk4ODkxLC0wLjk4NGMwLC0wLjU0MTIgMC40NDUwMSwtMC45ODQgMC45ODg5MSwtMC45ODRjMC41NDM5LDAgMC45ODg5MiwwLjQ0MjggMC45ODg5MiwwLjk4NHpNMjQ0LjgyNzMsMTg4LjU4NWMwLDAuNDUyNjQgLTAuMzc2NzcsMC44MjY1NiAtMC44MzE2NywwLjgyNjU2Yy0wLjQ1NDksMCAtMC44MjA4LC0wLjM3MzkyIC0wLjgyMDgsLTAuODI2NTZjMCwtMC40NTI2NCAwLjM2NTksLTAuODI2NTcgMC44MjA4LC0wLjgyNjU3YzAuNDU0OSwwIDAuODMxNjcsMC4zNzM5MyAwLjgzMTY3LDAuODI2NTd6IiBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpbmRleCZxdW90OzpudWxsfSIgc3Ryb2tlPSIjMjU4MzgzIiBzdHJva2Utd2lkdGg9IjEuNSIvPjxwYXRoIGQ9Ik0yMzkuMDExNjksMTg3LjA0MDhjMCwwLjM2NDA4IC0wLjI5NjY3LDAuNjU5MjggLTAuNjYyNTcsMC42NTkyOGMtMC4zNjU5LDAgLTAuNjcyNDYsLTAuMjk1MiAtMC42NzI0NiwtMC42NTkyOGMwLC0wLjM3MzkyIDAuMzA2NTYsLTAuNjcwMTEgMC42NzI0NiwtMC42NzAxMWMwLjM2NTksMCAwLjY2MjU3LDAuMjk2MTkgMC42NjI1NywwLjY3MDExek0yNDcuMjIwMzEsMTgxLjQ2MTZjLTAuNjAzMjQsLTEuNTY1NTUgLTIuMDU2OTUsLTIuNjg3MzEgLTMuNTc5ODgsLTIuOTEyNjVjLTEuMzM2MDIsLTAuMjI2MzIgLTIuNjcwMDcsMC4yMDY2NSAtMy41Nzk4NywxLjA0MzA1bDEuMTU3MDMsMS4xNTAzYzAuNDQ1MDEsMC40NTI2NCAwLjEyODU1LDEuMjIxMTUgLTAuNTE1MjMsMS4yMjExNWgtNC43MTYxNGMtMC4zOTY1NiwwIC0wLjcyMjksLTAuMzI0NzIgLTAuNzIyOSwtMC43MTkzMXYtNC42OTI3MmMwLC0wLjY0MDU5IDAuNzcyMzQsLTAuOTU0NDkgMS4yMjcyNCwtMC41MTI2N2wwLjk4ODkyLDAuOTg0YzAuODk5OTEsLTAuNjk4NjUgMS45NDcxNywtMS4yMDA0OSAzLjA0NTg2LC0xLjQzNjY1YzEuMjc1NywtMC4yNzQ1NCAyLjU5OTg1LC0wLjIyNTM0IDMuODE3MjEsMC4xNDg1OGMyLjQ1MTUyLDAuNzM4IDQuMzUwMjQsMi43ODQ3MyA0Ljg5NDE0LDUuMTI2NjZjMC4xMjk1NSwwLjU2MDg5IC0wLjIyNjQ2LDEuMTMxNjEgLTAuNzkxMTMsMS4yNTk1M2MtMC41MjQxMywwLjEyNzkyIC0xLjAzNzM4LC0wLjE2NzI4IC0xLjIyNTI3LC0wLjY0OTQ0di0wLjAwOTg0ek0yNDEuNzIxNTMsMTg4LjU1NjE2YzAsMC40MTMyOCAtMC4zMzYyNCwwLjc0Nzg0IC0wLjc1MTU4LDAuNzQ3ODRjLTAuNDE1MzUsMCAtMC43NDE2OSwtMC4zMzQ1NiAtMC43NDE2OSwtMC43NDc4NGMwLC0wLjQxMzI4IDAuMzI2MzUsLTAuNzM4IDAuNzQxNjksLTAuNzM4YzAuNDE1MzUsMCAwLjc1MTU4LDAuMzI0NzIgMC43NTE1OCwwLjczOHpNMjQ3LjU1NjUyLDE4Ny4xMjkxNmMwLDAuNDkyIC0wLjQwNTQ1LDAuODk1NDQgLTAuOTA5OCwwLjg5NTQ0Yy0wLjUwNTMzLDAgLTAuOTA5OCwtMC40MDM0NCAtMC45MDk4LC0wLjg5NTQ0YzAsLTAuNTAxODQgMC40MDQ0NiwtMC45MDUyOCAwLjkwOTgsLTAuOTA1MjhjMC41MDQzNCwwIDAuOTA5OCwwLjQwMzQ0IDAuOTA5OCwwLjkwNTI4ek0yNDkuMjE3NjEsMTg0LjU1MDk3YzAsMC41NTEwNCAtMC40NDUwMiwwLjk4NCAtMC45ODg5MiwwLjk4NGMtMC41NDM5MSwwIC0wLjk4ODkyLC0wLjQzMjk2IC0wLjk4ODkyLC0wLjk4NGMwLC0wLjU0MTIgMC40NDUwMiwtMC45ODQgMC45ODg5MiwtMC45ODRjMC41NDM5MSwwIDAuOTg4OTIsMC40NDI4IDAuOTg4OTIsMC45ODR6TTI0NC44MjczMSwxODguNTg0OTljMCwwLjQ1MjY0IC0wLjM3Njc4LDAuODI2NTYgLTAuODMxNjgsMC44MjY1NmMtMC40NTQ5LDAgLTAuODIwOCwtMC4zNzM5MiAtMC44MjA4LC0wLjgyNjU2YzAsLTAuNDUyNjQgMC4zNjU5LC0wLjgyNjU2IDAuODIwOCwtMC44MjY1NmMwLjQ1NDksMCAwLjgzMTY4LDAuMzczOTIgMC44MzE2OCwwLjgyNjU2eiIgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aW5kZXgmcXVvdDs6bnVsbH0iIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIi8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6NS40ODY2OTE0OTU5NDA0Mjo0LjkxNzMwNDExMjkyMTc1NS0tPg==';
- let gx = [0];
- let gy = [0];
- let gr = [90];
- let gs = [100];
- let rm = [0];
- class Global_Coordinate {
- getInfo() {
- return {
- id: 'globalCoordinate',
- color1: '#2ea4a4',
- menuIconURI: icon,
- name: 'Global Coordinate',
- blocks: [
- {
- opcode: 'SET',
- filter: [Scratch.TargetType.SPRITE],
- blockType: Scratch.BlockType.COMMAND,
- blockIconURI: icon,
- text: 'go to x: [x] y: [y] direction [r] size [s] - use screens [screen]',
- arguments: {
- x: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- y: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- r: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '90'
- },
- s: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '100'
- },
- screen: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- }
- },
- {
- opcode: 'rotation_mode',
- filter: [Scratch.TargetType.SPRITE],
- blockType: Scratch.BlockType.COMMAND,
- text: 'set screens [screen] \'s rotation mode to [m]',
- arguments: {
- screen: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- m: {
- type: Scratch.ArgumentType.NUMBER,
- menu: 'rotation_mode'
- }
- }
- },
- {
- opcode: 'set',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set screens [screen] \'s x: [x] y: [y] direction: [r] size: [s]',
- arguments: {
- x: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- y: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- r: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '90'
- },
- s: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '100'
- },
- screen: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- }
- },
- '---',
- {
- opcode: 'Set_Co',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set screens [screen] \'s x [x] y: [y]',
- arguments: {
- x: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- y: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- screen: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- }
- },
- {
- opcode: 'Set_GX',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set screens [screen] \'s x to [x]',
- arguments: {
- x: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- screen: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- }
- },
- {
- opcode: 'Set_GY',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set screens [screen] \'s y to: [y]',
- arguments: {
- y: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- screen: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- }
- },
- '---',
- {
- opcode: 'CX',
- blockType: Scratch.BlockType.COMMAND,
- text: 'change screens [screen] \'s x by [x]',
- arguments: {
- x: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- screen: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- }
- },
- {
- opcode: 'CY',
- blockType: Scratch.BlockType.COMMAND,
- text: 'change screens [screen] \'s y by [y]',
- arguments: {
- y: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- screen: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- }
- },
- '---',
- {
- opcode: 'Set_GR',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set screens [screen] \'s direction [r]',
- arguments: {
- r: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '90'
- },
- screen: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- }
- },
- {
- opcode: 'TR',
- blockType: Scratch.BlockType.COMMAND,
- text: 'turn [tr_icon] [r] degrees - screens [screen]',
- arguments: {
- r: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '15'
- },
- tr_icon: {
- type: Scratch.ArgumentType.IMAGE,
- dataURI: tr_icon
- },
- screen: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- }
- },
- {
- opcode: 'TL',
- blockType: Scratch.BlockType.COMMAND,
- text: 'turn [tr_icon] [r] degrees - screens [screen]',
- arguments: {
- r: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '15'
- },
- tr_icon: {
- type: Scratch.ArgumentType.IMAGE,
- dataURI: tl_icon
- },
- screen: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- }
- },
- '---',
- {
- opcode: 'Set_si',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set screens [screen] \'s size [s]',
- arguments: {
- s: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '100'
- },
- screen: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- }
- },
- {
- opcode: 'CS',
- blockType: Scratch.BlockType.COMMAND,
- text: 'change screens [screen] \'s size by [s]',
- arguments: {
- s: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- screen: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- }
- },
- '---',
- {
- opcode: 'x',
- blockType: Scratch.BlockType.REPORTER,
- text: 'screens [screen] x',
- arguments: {
- screen: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- }
- },
- {
- opcode: 'y',
- blockType: Scratch.BlockType.REPORTER,
- text: 'screens [screen] y',
- arguments: {
- screen: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- }
- },
- {
- opcode: 'r',
- blockType: Scratch.BlockType.REPORTER,
- text: 'screens [screen] direction',
- arguments: {
- screen: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- }
- },
- {
- opcode: 's',
- blockType: Scratch.BlockType.REPORTER,
- text: 'screens [screen] size',
- arguments: {
- screen: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- }
- },
- {
- opcode: 'rm',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'screens [screen] rotation mode is screen?',
- arguments: {
- screen: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- }
- },
- ],
- menus: {
- rotation_mode: {
- acceptReporters: true,
- items: [
- {
- text: 'center of stage',
- value: '0'
- },
- {
- text: 'center of screen',
- value: '1'
- }
- ]
- }
- }
- };
- }
- SET(args, { target }) {
- if (isNaN(args.x)) {
- args.x = 0;
- }
- if (isNaN(args.y)) {
- args.y = 0;
- }
- if (isNaN(args.r)) {
- args.r = 0;
- }
- if (isNaN(args.s)) {
- args.s = 0;
- }
- target.setSize((args.s / 100) * gs[args.screen - 1]);
- target.setDirection((- ((args.r - (gr[args.screen - 1] - 90) - 90))) + 90);
- if (rm[args.screen - 1] == 1) {
- target.setXY(
- (gx[args.screen - 1] / 100 * gs[args.screen - 1]) + (gs[args.screen - 1] / 100) * ((- (args.x)) * Math.cos((((- ((gr[args.screen - 1] - 90 - 90))) + 90)) / 180 * Math.PI) - (- (args.y)) * Math.sin((((- ((gr[args.screen - 1] - 90 - 90))) + 90)) / 180 * Math.PI)),
- (gy[args.screen - 1] / 100 * gs[args.screen - 1]) + (gs[args.screen - 1] / 100) * ((- (args.x)) * Math.sin((((- ((gr[args.screen - 1] - 90 - 90))) + 90)) / 180 * Math.PI) + (- (args.y)) * Math.cos((((- ((gr[args.screen - 1] - 90 - 90))) + 90)) / 180 * Math.PI)),
- true
- );
- } else {
- target.setXY(
- (gs[args.screen - 1] / 100) * ((- (args.x + gx[args.screen - 1])) * Math.cos((((- ((gr[args.screen - 1] - 90 - 90))) + 90)) / 180 * Math.PI) - (- (args.y + gy[args.screen - 1])) * Math.sin((((- ((gr[args.screen - 1] - 90 - 90))) + 90)) / 180 * Math.PI)),
- (gs[args.screen - 1] / 100) * ((- (args.x + gx[args.screen - 1])) * Math.sin((((- ((gr[args.screen - 1] - 90 - 90))) + 90)) / 180 * Math.PI) + (- (args.y + gy[args.screen - 1])) * Math.cos((((- ((gr[args.screen - 1] - 90 - 90))) + 90)) / 180 * Math.PI)),
- true
- );
- }
- }
- rotation_mode(args) {
- rm[args.screen - 1] = args.m;
- }
- set({ x, y, r, s, screen }) {
- if (!isNaN(x)) {
- gx[screen - 1] = x;
- } else {
- gx[screen - 1] = 0;
- }
- if (!isNaN(y)) {
- gy[screen - 1] = y;
- } else {
- gy[screen - 1] = 0;
- }
- if (!isNaN(r)) {
- gr[screen - 1] = r;
- } else {
- gr[screen - 1] = 0;
- }
- if (!isNaN(s)) {
- gs[screen - 1] = s;
- } else {
- gs[screen - 1] = 0;
- }
- }
- Set_Co({ x, y, screen }) {
- if (!isNaN(x + y)) {
- gx[screen - 1] = x;
- gy[screen - 1] = y;
- } else {
- if (isNaN(x)) {
- x = 0;
- }
- if (isNaN(y)) {
- y = 0;
- }
- }
- }
- Set_GX({ x, screen }) {
- if (!isNaN(x)) {
- gx[screen - 1] = x;
- } else {
- gx[screen - 1] = 0;
- }
- }
- Set_GY({ y, screen }) {
- if (!isNaN(y)) {
- gy[screen - 1] = y;
- } else {
- gy[screen - 1] = 0;
- }
- }
- CX({ x, screen }) {
- if (!isNaN(x)) {
- gx[screen - 1] += x;
- }
- }
- CY({ y, screen }) {
- if (!isNaN(y)) {
- gy[screen - 1] += y;
- }
- }
- Set_GR({ r, screen }) {
- if (!isNaN(r)) {
- gr[screen - 1] = r;
- } else {
- gr[screen - 1] = 0;
- }
- }
- TR({ r, screen }) {
- if (!isNaN(r)) {
- gr[screen - 1] += r;
- }
- }
- TL({ r, screen }) {
- if (!isNaN(r)) {
- gr[screen - 1] -= r;
- }
- }
- Set_si({ s, screen }) {
- if (!isNaN(s)) {
- gs[screen - 1] = s;
- } else {
- gs[screen - 1] = 0;
- }
- if (gs[screen - 1] < 0) {
- gs[screen - 1] = 0;
- }
- }
- CS({ s, screen }) {
- if (!isNaN(s)) {
- gs[screen - 1] += s;
- }
- if (gs[screen - 1] < 0) {
- gs[screen - 1] = 0;
- }
- }
- x({ screen }) {
- return gx[screen - 1];
- }
- y({ screen }) {
- return gy[screen - 1];
- }
- r({ screen }) {
- return gr[screen - 1];
- }
- s({ screen }) {
- return gs[screen - 1];
+ "use strict";
+ const icon =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIiB2aWV3Qm94PSItMjcuNTEzODgsLTI3LjUxMzg4LDIwMCwyMDAiPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNjcuNTEzNzgsLTEwNy41MTM3OCkiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTMxMC40ODYyMiwxMDkuNjM4Nzd2MzEuNzQ1MTdsLTEwLjA0MDU2LC0xMi4zODA2MmwtMTQuNzc1MSwxNS4yNzg5OWwtMTAuMDc3ODIsLTEwLjA3NzgybDE1LjI2Mjg3LC0xNC43NjAwMWwtMTIuMTE0NTUsLTkuODA1NzF6TTIwNC4yODIzMSwyMjUuNjcwNTVsLTE1LjI3MDkzLDE0Ljc2NzA1bDEyLjI0NzU3LDkuOTIzNjRoLTMxLjc0NTE3di0zMS43NDUxN2w5LjkyMzY3LDEyLjI0NzYxbDE0Ljc2NzA1LC0xNS4yNzA5M3pNMTY5LjYzODc3LDEwOS41MTM3OGgzMS43NDUxN2wtMTIuMzgwNjIsMTAuMDQwNTdsMTUuMjc4OTksMTQuNzc1MWwtMTAuMDc3ODIsMTAuMDc3ODJsLTE0Ljc2LC0xNS4yNjI4N2wtOS44MDU3MSwxMi4xMTQ1NnpNMjg1LjY3MDU1LDIxNS43MTc2OGwxNC43NjcwNCwxNS4yNzA5NGw5LjkyMzY0LC0xMi4yNDc1N3YzMS43NDUxN2gtMzEuNzQ1MTdsMTIuMjQ3NiwtOS45MjM2NmwtMTUuMjcwOTMsLTE0Ljc2NzA1eiIgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aW5kZXgmcXVvdDs6bnVsbH0iIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzFhNjI2MiIgc3Ryb2tlLXdpZHRoPSI0Ii8+PHBhdGggZD0iTTI3OS45NTE2NCwyMDcuODA0OWMtMC44MjY0OSwwLjgzNDc5IC0xLjY3MzE0LDEuNjg5OTQgLTIuNDk5NjMsMi41MjQ3MmMtMS4yNzM0LDEuMDk2NjYgLTIuNjE0OTMsMi4xMTE2MiAtNC4wMTY1NSwzLjAzODhjLTAuODA5NzcsMC41NjE0MiAtMS42NTI5NCwxLjA3MzA5IC0yLjUyNDg1LDEuNTMyMTFsLTEuMzczOCwwLjc3Njc0Yy01LjYwODg3LDIuODUxNTIgLTExLjc3OTcyLDQuNDI1MjcgLTE4LjA2OTE1LDQuNjA4MjVjLTUuMzM4NDMsMC4xOTYxMyAtMTAuNjY1MTUsLTAuNjI5OTggLTE1LjY5MzQsLTIuNDMzODRjLTEuOTk0NDEsLTAuNzc4NzUgLTMuOTM0NywtMS42ODk1NCAtNS44MDc5OCwtMi43MjYzNWMtMS42MDU0NiwtMC45ODQ3MyAtMi45ODc1NSwtMS44NjkyNiAtNC4xMjY1NCwtMi43NTUwMWMtMC42NDA0MSwtMC40NTg0OSAtMS4yNTE4NiwtMC45NTYxNCAtMS44MzA4NSwtMS40OTAwOWwtMC40MDcyMSwtMC40MDMxNmwtMC42NzE5MSwtMC42NjUyMmMtMi4xNjQ5NCwtMS44NjUzOSAtMi40MjUxOSwtNS4xMjU3NyAtMC41ODM1NCwtNy4zMTA5NGwwLjIyMTc0LC0wLjIyMzk3YzEuNzUzMTYsLTEuNzg3OTEgNC41MzcyOCwtMi4wNTgxNSA2LjYwMTU2LC0wLjY0MDc5YzAsMCAwLjI4NTA1LDAuMjgyMjMgMC44MzM1OCwwLjU4MzM4YzAsMCAwLjI2NDY4LDAuMjYyMDUgMC41MDkwMSwwLjUwMzk1YzAuNDIxMTYsMC4zMDQzMiAwLjg2MjUyLDAuNTc5NjUgMS4zMjEwMiwwLjgyNDA2YzAuODUzMzMsMC40ODE5OSAxLjkyOTgyLDEuMDIzNjIgMy4xODg3MywxLjU4NDYyYzEuMzcyMDUsMC41NzY1MiAyLjc4MjUzLDEuMDU2ODggNC4yMjEyNiwxLjQzNzYzYzMuNjExOSwwLjg1NjQ3IDcuMzU1ODMsMS4wMDI5MSAxMS4wMjM2LDAuNDMxMTdjNC4wNzgyLC0wLjU5NjU5IDcuOTY5NDQsLTIuMTA0OTMgMTEuMzg0MzMsLTQuNDEyNzh2MGwwLjk2ODgxLC0wLjczNDJjMC40ODcwNSwtMC4zMjk3OCAwLjk1MjIsLTAuNjkwNzYgMS4zOTI1NCwtMS4wODA3NGwxLjM3MDc2LC0xLjM4NDUzbDAuODA2MzMsLTAuODE0NDNjMS4zMDExLC0xLjQzNDcxIDIuNDQ3NjksLTMuMDAyMyAzLjQyMDgzLC00LjY3Njg5YzEuNjk4OTgsLTIuOTA4MzMgMi44MTE3NCwtNi4xMjEzNSAzLjI3NTM4LC05LjQ1NzUyYzAuMDc3MzksLTAuNzI5NzQgMC4xNTQ5OSwtMS40MTg5OCAwLjE3MTcxLC0yLjEyODE2bC04LjEwMzgsMC4wNDA0OGMtMS42NDk5MywwLjAwNjQzIC0zLjE0MjcyLC0wLjk3NzQzIC0zLjc4NzQyLC0yLjQ5NjE5Yy0wLjY0NDcsLTEuNTE4NzYgLTAuMzE1NSwtMy4yNzYwOCAwLjgzNTI0LC00LjQ1ODQ4bDE3LjgxOTkxLC0xNy45OTg4NmMwLjc2NjA1LC0wLjc4NDY4IDEuODE0NjgsLTEuMjI5NTMgMi45MTEyNywtMS4yMzVjMS4wOTY1OCwtMC4wMDU0OCAyLjE0OTYxLDAuNDI4ODggMi45MjM0NywxLjIwNTg2bDE3Ljk3ODUsMTcuNzk5NzdjMS42MDAyMiwxLjYwNjQ3IDEuNjA0MTYsNC4yMDMyNyAwLjAwODc4LDUuODE0NTdjLTAuNzYwMTEsMC43NjM0MiAtMS43OTM2NywxLjE5MTYxIC0yLjg3MDk4LDEuMTg5NDNsLTguNjkxNDIsMC4wMjMxN2MtMC4yMjM5NiwxLjk2MDIgLTAuNTcyOSwzLjkwNDExIC0xLjA0NDcsNS44MTk4M2MtMS4zNzg4NSw1LjU0NTA5IC0zLjgxNTUyLDEwLjc3MTc0IC03LjE3NjE4LDE1LjM5MjkyYy0xLjE4NzcxLDEuNTYzNjYgLTIuNDk0NDQsMy4wMzMyNyAtMy45MDg0OCw0LjM5NTY4eiIgZmlsbD0iIzFhNjI2MiIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiLz48cGF0aCBkPSJNMjIzLjcyODU0LDIwMy40MjU5OGwtMC4xNDExMSwwLjE0MjUzYy0xLjM2NDc3LDEuNTc0NTUgLTEuMTk4ODUsMy45NTYzNyAwLjM3MTA0LDUuMzI2NTJsMC42NzE5LDAuNjY1MjJjMC43MDQ5OCwwLjY1MTA3IDEuNDQ1MjcsMS4yNjI4NiAyLjIxNzQ5LDEuODMyNTljMS4wOTgyNywwLjg0NTQzIDIuNDM5NjQsMS42ODk2NSAzLjk2Mzg2LDIuNjM0MjZjMS43NzY0MSwwLjk4MDUxIDMuNjE0NzYsMS44NDQzNSA1LjUwMzM4LDIuNTg2MDRjNC44Mzk0OSwxLjgwOTQ3IDkuOTgxOSwyLjY3MTA5IDE1LjE0Njg5LDIuNTM3ODhjNi4wNTE3NCwtMC4yMTEyOCAxMS45ODExNSwtMS43NjMyNSAxNy4zNjAzNywtNC41NDM5M2MxLjMzMTI5LC0wLjcxMDMgMi42MjU3MiwtMS40ODc2MiAzLjg3ODI4LC0yLjMyOTAyYzEuMzM4LC0wLjg4MzY2IDIuNjE4ODgsLTEuODUwOTQgMy44MzQ5MywtMi44OTYwOGMwLjgwNjMzLC0wLjgxNDQzIDEuNjMyODIsLTEuNjQ5MjEgMi40MzkxNSwtMi40NjM2NGMxLjM2MDAyLC0xLjM1MTk4IDIuNjEyNjgsLTIuODA3ODQgMy43NDY2MSwtNC4zNTQzNWMzLjI2NDM5LC00LjQzMzE3IDUuNjQ2NjIsLTkuNDUyMjYgNy4wMTcxNCwtMTQuNzg0MzRjMC40OTIzOSwtMi4xOTY3NCAwLjgyNjU3LC00LjQyNTk3IDAuOTk5OTQsLTYuNjcwNTNsMC4wMTg1NCwtMC4zNDQ1MWw5Ljk4ODAzLC0wLjAyOTY0YzAuNjcxMTUsLTAuMDIwODMgMS4zMDg0MSwtMC4yOTk3OSAxLjc3OSwtMC43Nzg3NmMwLjUwNjEzLC0wLjUwMDMzIDAuNzkxMDcsLTEuMTgyMzUgMC43OTEzMiwtMS44OTQwNGMwLjAwMDI1LC0wLjcxMTY5IC0wLjI4NDIyLC0xLjM5MzkgLTAuNzg5OTksLTEuODk0NThsLTE3Ljk3ODUsLTE3Ljc5OTc2Yy0xLjAzNzgyLC0xLjAyNzYyIC0yLjcwOTQ1LC0xLjAyODMyIC0zLjc0ODEsLTAuMDAxNTNsLTE3LjgxOTkzLDE3Ljk5ODg2Yy0wLjc1MzMxLDAuNzY5NDQgLTAuOTY5NDgsMS45MTY2NCAtMC41NDc4NSwyLjkwNzQ2YzAuNDIxNjMsMC45OTA4MyAxLjM5ODE1LDEuNjMwNDggMi40NzQ5MiwxLjYyMTE2bDkuNDgxNDUsLTAuMDQ3MzZjMC4wMTM0NywxLjI2NzYxIC0wLjA2ODI0LDIuNTM0NDYgLTAuMjQ0NDUsMy43ODk4NGMtMC40NTcwMSwzLjUyNzYyIC0xLjU5NjAzLDYuOTMyNjIgLTMuMzUzNTksMTAuMDI1MTljLTEuMDE3OTYsMS43NjEzNyAtMi4yMTg0NiwzLjQxMDczIC0zLjU4MTY5LDQuOTIwODFsLTAuOTQ3NDUsMC45NTY5NmMtMC40NDM0OSwwLjQ0NzkzIC0wLjg2NjgsMC44NzU1MSAtMS4yNDk4MiwxLjI2MjM2djBjLTAuNTQ0MjYsMC41NDk3NCAtMi4wOTk0OSwxLjUwOTczIC0yLjU4MzMsMS45OTgzOGMtMy41OTM1MywyLjQyMTIyIC03LjY4NDAyLDQuMDA2MjUgLTExLjk3MDc0LDQuNjM4NTZjLTMuODQ0MDYsMC41NjI2MyAtNy43NjEwNCwwLjM2ODg2IC0xMS41MzA3OSwtMC41NzA0NWMtMS41MDk1MiwtMC40MTQ5OSAtMi45ODgwMSwtMC45MzU2NiAtNC40MjQ0NiwtMS41NTgxOGMtMS4yOTk2NCwtMC42MDEzMSAtMi4zOTY2OSwtMS4yMDM2MyAtMy4yMDkxLC0xLjYwNDc4Yy0wLjgxMjQxLC0wLjQwMTE0IC0xLjU4NTUxLC0xLjA0NTYgLTIuMTM0MDIsLTEuMzQ2NzZjLTAuNTQ4NTIsLTAuMzAxMTUgLTAuNzkyODUsLTAuNTQzMDYgLTAuNzkyODUsLTAuNTQzMDZjLTEuNDk3ODMsLTEuMDE2MjkgLTMuNTA3NjQsLTAuODE4ODIgLTQuNzc5MDIsMC40Njk1OU0yMjEuNTQ5OTMsMjAxLjI2OTA0djBjMi4xOTA0NywtMi4zODA3IDUuNzg1MTMsLTIuODE5MjggOC40ODM3NywtMS4wMzUxMmMwLjE0MjMzLDAuMTAwNTkgMC41Njk5LDAuNTIzOSAwLjgzMzU3LDAuNTgzMzdjMC4yNjM2NywwLjA1OTQ3IDAuNDQ2OTMsMC4yNDA4OSAwLjY1MDUzLDAuNDQyNDdjMC4zNjM2MSwwLjI2NTIgMC43NDM3NiwwLjUwNjkxIDEuMTM4MTcsMC43MjM2OGwwLjE0MjUzLDAuMTQxMTFjMC43OTI0NSwwLjQ2MjAxIDEuOTkwNywxLjA0MzU4IDIuODg0MTMsMS40NDQzMWMxLjMxMjUxLDAuNTUyNjggMi42NjIsMS4wMTMwNSA0LjAzODYyLDEuMzc3NzZjMy40MTkyMSwwLjg0ODE2IDYuOTcwNzUsMS4wMjMxIDEwLjQ1NjczLDAuNTE1MDRjMy44MTY3NiwtMC41Njk1MiA3LjQ2MTY4LC0xLjk3MTA1IDEwLjY3Njc2LC00LjEwNTM0bDEuMDA5OTQsLTAuNjEyODRsMS4xNzA3OSwtMC44NTY3N2wwLjM2Mjg0LC0wLjM2NjQ5YzAuMzIyNTMsLTAuMzI1NzcgMC42ODUzOCwtMC42OTIyNiAxLjA0ODIyLC0xLjA1ODc1bDAuNzI1NywtMC43MzI5OGMxLjIxNTg2LC0xLjM1NjQzIDIuMjg4MDIsLTIuODM1MTMgMy4xOTkzLC00LjQxMjRjMS42MTQyMiwtMi43ODM3MiAyLjY1OTM4LC01Ljg2MDM2IDMuMDc0ODMsLTkuMDUxMzFsMC4wNTgyNSwtMC41MDY3OGwtNi40ODMwNCwwLjAzMjM5Yy0yLjI0MDY5LDAuMDE5ODggLTQuMjcxOTEsLTEuMzE0NDIgLTUuMTQzNDQsLTMuMzc4NzhjLTAuODcxNTMsLTIuMDY0MzYgLTAuNDExMDksLTQuNDUwNjMgMS4xNjU5MywtNi4wNDI1bDE3LjkyMDcxLC0xOC4xMDA2NmMyLjE1MjAxLC0yLjE1MTIxIDUuNjM3NjksLTIuMTU5NTggNy44LC0wLjAxODcxbDE3LjkxNzQyLDE3LjczOTI5YzIuMTYyNDYsMi4xNjMxNiAyLjE3MDkyLDUuNjY2OTcgMC4wMTg5MSw3Ljg0MDUyYy0xLjAzMTM5LDEuMDQwNTkgLTIuNDM2OTcsMS42MjQxNCAtMy45MDIwOSwxLjYyMDAzbC03LjQzNTMzLDAuMDE2ODhjLTAuMjM1MDIsMS42MjE5OCAtMC41Mzc3LDMuMjE3MjcgLTAuOTA4MDUsNC43ODU5Yy0xLjQ3MjIsNS42NzA1OSAtNC4wMTc2OSwxMS4wMDY0MyAtNy40OTg3MSwxNS43MTg2OWMtMS4yMDcyNywxLjY0NzM4IC0yLjU0MDcxLDMuMTk4NDQgLTMuOTg4Myw0LjYzOTJjLTAuODI2NDksMC44MzQ3OSAtMS42OTMzLDEuNzEwMyAtMi42NDA3MywyLjY2NzI2Yy0xLjI3NDIxLDEuMDk1NjcgLTIuNjE1NjgsMi4xMTA1OCAtNC4wMTY1NiwzLjAzODhjLTAuODczMTUsMC42MjA2IC0xLjc4Mzc2LDEuMTg2NzUgLTIuNzI2NjMsMS42OTUybC0xLjUxNDQ5LDEuMDAwMzFsLTAuMTIwOTUsMC4xMjIxNmMtNS43NjQ2MiwyLjk2ODk3IC0xMi4xMTQ0Miw0LjYyNjU0IC0xOC41OTQ2OSw0Ljg1Mzk5Yy01LjU1MjUxLDAuMTM5MjMgLTExLjA4MDEsLTAuNzg5MTkgLTE2LjI4MjQ0LC0yLjczNDhjLTIuMDAwMTYsLTAuNzg4NDQgLTMuOTQ3MDQsLTEuNzA1ODUgLTUuODI4MzQsLTIuNzQ2NTF2MGMtMS41ODUzLC0xLjAwNTA4IC0zLjAwNzkyLC0xLjg4OTQyIC00LjEyNjU0LC0yLjc1NTAxYy0wLjY5NjQyLC0wLjQ5NjIgLTEuMzYyMTYsLTEuMDM0MTQgLTEuOTkzNTMsLTEuNjEwODRjLTAuMTIyMTYsLTAuMTIwOTUgLTAuMjQ0MzMsLTAuMjQxOSAtMC4zNDYxNCwtMC4zNDI2OWwtMC43NTMzNSwtMC43NDU4NmMtMS4zMjUxMiwtMS4xNTc2MiAtMi4xMzQ3MywtMi43OTUyMyAtMi4yNDk5NSwtNC41NTEwMmMtMC4xMTUyMiwtMS43NTU3OSAwLjQ3MzQ1LC0zLjQ4NTE2IDEuNjM1OTMsLTQuODA2MDNsMC4yODIyMiwtMC4yODUwNXoiIGZpbGw9IiMxYTYyNjIiIHN0cm9rZT0iIzFhNjI2MiIgc3Ryb2tlLXdpZHRoPSIzIi8+PHBhdGggZD0iTTIyMy41NDQ1MiwyMDMuMzAwMzNjMS4yNzEzOCwtMS4yODg0MSAzLjM2MzY0LC0xLjUxODEzIDQuODYxNDgsLTAuNTAxODRjMCwwIDAuMjQzOTYsMC4yNDEwMyAwLjc5MjQ5LDAuNTQyMmMwLjU0ODUyLDAuMzAxMTUgMS4zMjA1NSwwLjk0MzMzIDIuMTMyOTUsMS4zNDQ0NmMwLjgxMjQxLDAuNDAxMTQgMS45MDc2NiwxLjAwMDIzIDMuMjA3MywxLjYwMTU1YzEuNDM2NDUsMC42MjI1MiAyLjkxMjAzLDEuMTM5MDEgNC40MjE1NiwxLjU1Mzk5YzMuNzY5NzYsMC45MzkzMiA3LjY3NjQzLDEuMTIzNTggMTEuNTIwNSwwLjU2MDk1YzQuMjg2NzIsLTAuNjMyMyA4LjM1NjgsLTIuMjI0NTcgMTEuOTUwMzIsLTQuNjQ1NzljMC40ODM4LC0wLjQ4ODY2IDIuMDMxMjIsLTEuNDQ4NjcgMi41NzU0OSwtMS45OTg0MXYwYzAuMzgzLC0wLjM4Njg2IDAuODAxNTksLTAuODEzNzYgMS4yNDUwNiwtMS4yNjE3MWwwLjk0MzM4LC0wLjk1NTk2YzEuMzYzMjMsLTEuNTEwMDggMi41NDM3MiwtMy4xNDcwMiAzLjU2MTY5LC00LjkwODM5YzEuNzU3NTUsLTMuMDkyNTcgMi44ODczNiwtNi40NTI3NCAzLjM0NDM3LC05Ljk4MDM2YzAuMTc2MjEsLTEuMjU1MzggMC4yNjM1LC0yLjUxMTQ2IDAuMjUwMDIsLTMuNzc5MDdsLTkuNDUxNCwwLjA2NTY3Yy0xLjA3Njc3LDAuMDA5MzIgLTIuMDQwMTUsLTAuNjMwNDcgLTIuNDYxNzgsLTEuNjIxMjljLTAuNDIxNjMsLTAuOTkwODMgLTAuMjE1ODEsLTIuMjI1ODkgMC41Mzc1MSwtMi45OTUzMmwxNy44NDUwNiwtMTcuOTkzMjRjMS4wMzg2NiwtMS4wMjY3NyAyLjcxMzY2LC0xLjAyMzk3IDMuNzUxNDgsMC4wMDM2NWwxNy45OTU1NywxNy44MjI5NWMwLjUwNTc5LDAuNTAwNjkgMC43OTA4NiwxLjE4NTA0IDAuNzkwNiwxLjg5NjczYy0wLjAwMDI1LDAuNzExNjkgLTAuMjg0NzgsMS4zOTU0OCAtMC43OTA5LDEuODk1ODNjLTAuNDcwNTksMC40Nzg5NyAtMS4xOTQ0OSwwLjc3NjEyIC0xLjg2NTY2LDAuNzk2OTRsLTkuOTg3MjksMC4wMzI2OGwwLjA2Njg5LDAuMzIxMTljLTAuMTczMzgsMi4yNDQ1NiAtMC41MDYwNyw0LjQ4NDM2IC0wLjk5ODQ1LDYuNjgxMDljLTEuMzcwNTIsNS4zMzIwNyAtMy43NjE3MywxMC4zNzg3NyAtNy4wMjYxMiwxNC44MTE5M2MtMS4xMzM5MiwxLjU0NjUyIC0yLjM5NCwzLjAxMDAzIC0zLjc1NDAyLDQuMzYyMDFjLTAuODA2MzMsMC44MTQ0MyAtMS42Mzc5MSwxLjY1Mjc3IC0yLjQ0NDI0LDIuNDY3MmMtMS4yMTYwNiwxLjA0NTE0IC0yLjUwNDQxLDIuMDE2IC0zLjg0MjQzLDIuODk5NjdjLTEuMjUyNTcsMC44NDE0IC0yLjU1NDA5LDEuNjIwNjggLTMuODg1MzgsMi4zMzA5N2MtNS4zNzkyNCwyLjc4MDY4IC0xMS4zMzMzMiw0LjMzMDIxIC0xNy4zODUwNiw0LjU0MTQ5Yy01LjE2NSwwLjEzMzIxIC0xMC4zMjE3NiwtMC43Mzc3NCAtMTUuMTYxMjUsLTIuNTQ3MjFjLTEuODg4NjMsLTAuNzQxNjkgLTMuNzMxMDIsLTEuNjEwMDcgLTUuNTA3NDQsLTIuNTkwNThjLTEuNTI0MjEsLTAuOTQ0NiAtMi44NjgyMiwtMS43OTI1OCAtMy45NjY0OCwtMi42MzgwMmMtMC43NzIyMSwtMC41Njk3MyAtMS41MTM4NiwtMS4xODM4NSAtMi4yMTg4NiwtMS44MzQ5MmwtMC42NzIzMiwtMC42NjYwMWMtMS41Njk4OSwtMS4zNzAxNCAtMS43Mzc4NiwtMy43NTY1NyAtMC4zNzMwOSwtNS4zMzExM2wwLjE0MTA3LC0wLjE0MjYxIiBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtub0hvdmVyJnF1b3Q7OmZhbHNlLCZxdW90O29yaWdJdGVtJnF1b3Q7OlsmcXVvdDtQYXRoJnF1b3Q7LHsmcXVvdDthcHBseU1hdHJpeCZxdW90Ozp0cnVlLCZxdW90O3NlZ21lbnRzJnF1b3Q7OltbMjA0LjMzNDA1LDE2MC4xMDgwNF0sW1syMDQuMTQwNTEsMTU5Ljk5MDM5XSxbMCwwXSxbLTIuMDg3MjUsLTEuMDg2NTFdXSxbWzE5OC4zODY3NiwxNjEuNzkzODddLFsxLjA5MzYzLC0yLjA4MzUzXSxbMCwwXV0sW1sxOTcuODMyMTMsMTYyLjcwNjI2XSxbMCwwXSxbLTAuNTMwNDIsMC45NDUwM11dLFtbMTk2LjQwMDI0LDE2NS42MjIzNV0sWzAuNDIzNDUsLTAuOTk3NTZdLFstMC42NDA4NSwxLjQyNzk2XV0sW1sxOTQuNTQ1NDEsMTcwLjY2Njg2XSxbMC42MzgxNiwtMS45MjE4NF0sWy0wLjYxMTUsMi4yMDgyOF1dLFtbMTkzLjE0NzAxLDE3Ny4zODk4M10sWzAuMzE5NzcsLTIuMjY4OTZdLFstMC43MTkzNSw1Ljc5MDJdXSxbWzE5NC4zMjg3OCwxOTQuNjkzMjFdLFstMS40OTk5MSwtNS42Mzg2NF0sWzEuODE4MDYsNi41OTIyNF1dLFtbMjAzLjg2OTczLDIxMi41NzIwOV0sWy00LjQ2NDIsLTUuMTgwMTZdLFsxLjEyOTEyLDEuMjc2MjNdXSxbWzIwNy40NDQ0NCwyMTYuMjIxODhdLFstMS4yNTI1LC0xLjE1NTM5XSxbMS4zMjEzMiwxLjIzODE3XV0sW1syMTEuNjMwNjksMjE5LjY3NTQzXSxbLTEuNDY2NzQsLTEuMDYxOV0sWzEuMTA1OTMsMC42NzIyOV1dLFtbMjE0Ljk3NjEzLDIyMS43MDkxXSxbLTEuMTA1OTMsLTAuNjcyMjldLFsxLjg0MTUyLDEuMTM5NjJdXSxbWzIyMC43NDEwOSwyMjQuNjgzNDZdLFstMS45OTU5NiwtMC44NDAyOF0sWzUuNzI1MTYsMi40MjQwMV1dLFtbMjM4LjgyMDA5LDIyOC41MTddLFstNi4yMTYyNSwtMC4xMDgxXSxbMi41NDIwNywtMC4wMzQ4M11dLFtbMjQ2LjQwOTQ3LDIyNy44NjcyNl0sWy0yLjUxMSwwLjM5Nzc4XSxbMCwwXV0sWzI0Ni43OTI3NiwyMjcuNzk3MzNdLFtbMjQ5LjQ0Mjg3LDIzOC43NjEwM10sWzAsMF0sWzAuMTk4NzcsMC43MzE3OF1dLFtbMjUwLjc2NDUzLDI0MC41MTEwOV0sWy0wLjY0OTQ2LC0wLjM5MTRdLFswLjY4MjI0LDAuNDI0ODRdXSxbWzI1My4wNTI0NCwyNDAuODgzOTddLFstMC43ODE4MywwLjE4NjIzXSxbMC43ODE4MywtMC4xODYyM11dLFtbMjU0LjkyNjU0LDIzOS41MTk2N10sWy0wLjQxNzQ0LDAuNjg2OF0sWzAsMF1dLFtbMjY5Ljc2NzI5LDIxNS4xMDYyMl0sWzAsMF0sWzAuODU2ODIsLTEuNDA5MzFdXSxbWzI2OC43ODY3MSwyMTAuOTg4NjddLFsxLjQwMDA3LDAuODcxODRdLFswLDBdXSxbWzI0NC4zNDU2MSwxOTYuMTMxMTFdLFswLDBdLFstMS4wNDI2MSwtMC42MjU4NF1dLFtbMjQxLjAwODMxLDE5Ni4yOTEyN10sWzAuOTc3ODksLTAuNzIyODFdLFstMC45Nzc4OSwwLjcyMjgxXV0sW1syMzkuODc2MTIsMTk5LjQzNDcyXSxbLTAuMjkyNDIsLTEuMTgwMzRdLFswLDBdXSxbWzI0Mi40MTI5NSwyMDkuODM3MzFdLFswLDBdLFstMS4zODg4OSwwLjM0N11dLFtbMjM4LjE4NTg5LDIxMC41NjE5OV0sWzEuNDI1MTcsLTAuMTM1NDNdLFstMy45OTQ3MywwLjQyMjQ4XV0sW1syMjYuMjk0NzMsMjA5LjUwNTQ5XSxbMy44NTc2NywxLjEyMDE0XSxbLTIuMjAxNTgsLTAuNjU2NTldXSxbWzIxOS45NTA3NiwyMDYuODYwNzNdLFsyLjAxNjAyLDEuMTAxNzFdLFswLDBdXSxbWzIxOC42NTEyOCwyMDYuMDcwNzldLFswLDBdLFstMC42MDgyNiwtMC4zNjk3Nl1dLFtbMjE2LjkzNzA5LDIwNS4wMjg3NF0sWzAuNTI1MzIsMC4zMTkzM10sWzAsMF1dLFtbMjE2LjkzNzA5LDIwNS4wMjg3NF0sWzAsMF0sWy0wLjc0NjUsLTAuNDUzNzldXSxbWzIxNC4wNjQ5NCwyMDIuNzE0OF0sWzAuNjYzNTYsMC40MDMzN10sWy0zLjYwMTM3LC0zLjMxMjgyXV0sW1syMDUuODMyNSwxOTAuNzgxMDNdLFsxLjgxNzk4LDQuNTQzMDldLFstMS42MjU0NCwtNC4wNzUxMV1dLFtbMjAzLjQzNzI2LDE3Ny45NjU0XSxbLTAuMDQzODcsNC4zODcxXSxbMC4wNjAyNSwtMS43NjY5MV1dLFtbMjAzLjk4OTM0LDE3Mi42OTY5NV0sWy0wLjMwNzM2LDEuNzQxMDNdLFswLjMxOTkyLC0xLjU4NTE5XV0sW1syMDQuOTExMTIsMTY4Ljc1MTMyXSxbLTAuMjI3NzMsMC45OTc1Ml0sWzAuMjI3NzMsLTAuOTk3NTJdXSxbWzIwNS44MzEyMSwxNjYuMDU0MjNdLFstMC4xODcwNiwwLjY4MTQ1XSxbMC4xODcwNiwtMC42ODE0NV1dLFtbMjA2LjIxOTk2LDE2NS4wNDA5OV0sWzAsMF0sWzAuNzIzODIsLTEuOTExNjVdXSxbWzIwNC40NTE3LDE1OS45MTQ1XSxbMS43NDg0NSwxLjA1ODkxXSxbMCwwXV1dLCZxdW90O2ZpbGxDb2xvciZxdW90OzpbMCwwLDAsMV19XX0iIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIwLjUiLz48cGF0aCBkPSJNMjU3LjM4NTY5LDE1Ny42MjIwNmMtMS43NTg0NywxLjc3Njc3IC00LjUzNDIsMi4wNDYyMiAtNi42MDE1NiwwLjY0MDc5bC0xLjMwMTA3LC0wLjg4NDk0Yy0wLjQ2MjY4LC0wLjM0NjI3IC0wLjk1MTk4LC0wLjY1NTQ1IC0xLjQ2MzM0LC0wLjkyNDY1Yy0wLjg1MzMzLC0wLjQ4MTk5IC0xLjkyOTgyLC0xLjAyMzYxIC0zLjE4ODc0LC0xLjU4NDZjLTEuMzc2MzIsLTAuNTY1MTkgLTIuNzg2MDgsLTEuMDQ1MzEgLTQuMjIxMjYsLTEuNDM3NjNjLTMuNTcyOTYsLTAuODc3MjkgLTcuMjgxNDEsLTEuMDU4MjUgLTEwLjkyMjgsLTAuNTMyOThjLTQuMDg3MTQsMC41OTg4NiAtNy45ODU5MSwyLjExNDM5IC0xMS40MDQ1LDQuNDMzMTNsLTAuOTI4NDksMC42OTM0OGMtMC40OTQxMywwLjMzNjIgLTAuOTY2MDIsMC43MDQgLTEuNDEyNywxLjEwMTFsLTEuMzUwNiwxLjM2NDE3bC0wLjgyNjQ5LDAuODM0NzljLTEuMjk3NDIsMS40Mzc3MSAtMi40NDM3LDMuMDA0OTEgLTMuNDIwODMsNC42NzY4OWMtMS42ODA5OCwyLjkwMjA1IC0yLjc5MjczLDYuMDk4MiAtMy4yNzU1OSw5LjQxNzAxYy0wLjA3NzU5LDAuNjg5MjIgLTAuMTM1MDMsMS4zNTgxIC0wLjE1MTc1LDIuMDY3MjhsOC4xMDM4LC0wLjA0MDQ4YzEuNjQ5OTMsLTAuMDA2NDMgMy4xNDI3MiwwLjk3NzQzIDMuNzg3NDIsMi40OTYyMWMwLjY0NDcsMS41MTg3NiAwLjMxNTUsMy4yNzYwNyAtMC44MzUyNCw0LjQ1ODQ4bC0xNy44ODA0LDE4LjA1OTk0Yy0wLjc1OTEsMC43Nzc1NiAtMS43OTYwMSwxLjIyMTczIC0yLjg4MjYxLDEuMjM0NzVjLTEuMDg2NiwwLjAxMzAyIC0yLjEzMzg3LC0wLjQwNjE1IC0yLjkxMTQsLTEuMTY1MjlsLTE3Ljk3ODQ5LC0xNy43OTk3NWMtMS4xNjI0OSwtMS4xNzA4NSAtMS41MDkyMywtMi45MjQ3OSAtMC44Nzk3NCwtNC40NDk5MmMwLjYyOTQ5LC0xLjUyNTEzIDIuMTEyMzgsLTIuNTIzODcgMy43NjIyOSwtMi41MzM5Mmw4LjczMTU0LC0wLjEwNDRjMC4yMjA5LC0xLjk2NTg5IDAuNTYyOTksLTMuOTE2MjggMS4wMjQzNCwtNS44Mzk5OWMxLjM3NTg3LC01LjUzNjI0IDMuODA1NDgsLTEwLjc1NTUzIDcuMTU2MDIsLTE1LjM3MjU2YzEuMTMxNCwtMS41NDg5NCAyLjM3Njk1LC0zLjAxMTE5IDMuNzI2MjUsLTQuMzc0NTNjMC44MjY0OSwtMC44MzQ3OSAxLjY3MzE0LC0xLjY4OTk0IDIuNDk5NjMsLTIuNTI0NzJjMS4yNzg0NiwtMS4xMDUzNyAyLjYyNjgzLC0yLjEyNzIyIDQuMDM2NzIsLTMuMDU5MTZjMC44MDk3LC0wLjU2MTU1IDEuNjUyODgsLTEuMDczMjEgMi41MjQ4NCwtMS41MzIxMmwxLjM3MzgsLTAuNzc2NzRjNS41OTA3NCwtMi44OTg2IDExLjc1NDg3LC00LjUyMDg5IDE4LjA0ODE5LC00Ljc0OTk2YzUuMzUxOTMsLTAuMTMyODggMTAuNjc5NjYsMC43NjIxIDE1LjY5NDQxLDIuNjM2NDRjMi4wNzI2NSwwLjc2Mzc4IDQuMDgyNCwxLjY4ODU0IDYuMDEwNzksMi43NjU4NWMxLjQyOTQ4LDAuODE5ODkgMi44MDc1MSwxLjcyNjQxIDQuMTI2MzQsMi43MTQ0OWMwLjYzOTcyLDAuNDU5NCAxLjI1MTExLDAuOTU3MDEgMS44MzA4NSwxLjQ5MDA5bDAuMzY2NDksMC4zNjI4NGMwLjUwOTAxLDAuNTAzOTUgMC43NTMzNSwwLjc0NTg2IDAuNzUzMzUsMC43NDU4NmMyLjExNzIyLDEuODg5MTggMi4zMjI5OCw1LjEyOTE2IDAuNDYxNzgsNy4yNzEwM2wtMC4yMDE1OSwwLjIwMzYxeiIgZmlsbD0iIzFhNjI2MiIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiLz48cGF0aCBkPSJNMjAwLjk3ODczLDE1Mi45NDAxNmMtMS4zNTkwOSwxLjM1Mjg0IC0yLjYxMTY4LDIuODA4NjIgLTMuNzQ2NjEsNC4zNTQzNWMtMy4yNTg2LDQuNDI2NDUgLTUuNjM0MTksOS40MzkwNiAtNi45OTY5OCwxNC43NjM5N2MtMC40OTIyOSwyLjE5Njc2IC0wLjgyNjQ1LDQuNDI1OTggLTAuOTk5OTQsNi42NzA1M2wtMC4wMTg1NCwwLjM0NDUxbC05Ljk4ODAzLDAuMDI5NjRjLTEuMDk0ODUsLTAuMDE1NTcgLTIuMDg3NzgsMC42NDAyNCAtMi41MDMyNCwxLjY1MzMzYy0wLjQxNTQ1LDEuMDEzMSAtMC4xNjg4MywyLjE3NzIzIDAuNjIxNzMsMi45MzQ4NGwxOC4wMzk1OCwxNy44NjAyM2MxLjA0NTc4LDEuMDM1MzggMi43MzI4OCwxLjAyNjk1IDMuNzY4MjYsLTAuMDE4ODJsMTcuNzc5NiwtMTcuOTU4MTRjMC43NjUyMywtMC43NjQ5NiAwLjk5MTg5LC0xLjkxNjczIDAuNTczNTksLTIuOTE0NjJjLTAuNDE4MzEsLTAuOTk3ODggLTEuMzk4NTIsLTEuNjQzNzIgLTIuNDgwNSwtMS42MzQzNmwtOS41MDE4MSwwLjAyNzJjMC4wMTA3NCwtMS4yNjA4MSAwLjA5OTE4LC0yLjUxOTc1IDAuMjY0ODEsLTMuNzY5NjljMC40Njk2MiwtMy41MjQ3IDEuNjA3ODgsLTYuOTI3MzQgMy4zNTM2LC0xMC4wMjUxOWMxLjAyNjYzLC0xLjc2NzExIDIuMjMzNywtMy40MjMwMyAzLjYwMTg2LC00Ljk0MTE3bDAuOTQ3NDMsLTAuOTU2OTVjMC40NDM0OSwtMC40NDc5MyAwLjg2NjgsLTAuODc1NTEgMS4yNDk4MiwtMS4yNjIzNnYwYzAuNTQ0MjYsLTAuNTQ5NzQgMi4wOTk1LC0xLjUwOTc0IDIuNTYzMTQsLTEuOTc4MDNjMy41OTcyNSwtMi40MzIxIDcuNjk1MjUsLTQuMDI0MzIgMTEuOTkwOTEsLTQuNjU4OTJjMy44NDQ1OSwtMC41NTE3MSA3Ljc1OTQzLC0wLjM1ODA0IDExLjUzMDc5LDAuNTcwNDVjMS41MTgzNiwwLjQxMjQ2IDMuMDA5NTQsMC45MTkyNSA0LjQ2NDc4LDEuNTE3NDVjMS4yOTk2NCwwLjYwMTMxIDIuMjc0NzMsMS4xMjMxOSAzLjA0NjYxLDEuNTI0NTRjMC43MzM5LDAuNDA4NzYgMS40NDAwNSwwLjg2NTQ2IDIuMTEzODYsMS4zNjcxMmMwLjU0ODUyLDAuMzAxMTUgMC43OTI4NSwwLjU0MzA2IDAuNzkyODUsMC41NDMwNmMxLjQ5MTg0LDAuOTk4NzQgMy40ODAyNiwwLjgxMTAyIDQuNzU4ODUsLTAuNDQ5MjRsMC4yMDE1OCwtMC4yMDM2YzAuNjU0MDIsLTAuNzU0NTIgMC45ODA0NCwtMS43Mzg1NiAwLjkwNzAzLC0yLjczNDM3Yy0wLjA3MzQxLC0wLjk5NTgyIC0wLjU0MDYyLC0xLjkyMTMyIC0xLjI5ODIyLC0yLjU3MTc5YzAsMCAtMC4yMjM5NywtMC4yMjE3NCAtMC42NzE5LC0wLjY2NTIyYy0wLjcwNTA2LC0wLjY1MDk5IC0xLjQ0NTM0LC0xLjI2Mjc3IC0yLjIxNzQ5LC0xLjgzMjU5Yy0xLjI2OTU0LC0wLjk0MTA5IC0yLjU5MzAyLC0xLjgwNzE1IC0zLjk2MzY2LC0yLjU5Mzc0Yy0xLjc2MDc2LC0xLjAwMzIxIC0zLjU5MzUzLC0xLjg3NDM5IC01LjQ4MzIzLC0yLjYwNjRjLTQuODM5NDksLTEuODA5NDcgLTkuOTgxOSwtMi42NzEwOSAtMTUuMTQ2ODksLTIuNTM3ODhjLTYuMDUxNzQsMC4yMTEyOCAtMTEuOTgxMTUsMS43NjMyNSAtMTcuMzYwMzgsNC41NDM5M2MtMS4zMzEzNSwwLjcxMDIgLTIuNjI1NzYsMS40ODc1NSAtMy44NzgyOCwyLjMyOTAyYy0xLjM0NjEsMC44ODg2OCAtMi42MzM3NiwxLjg2Mjg1IC0zLjg1NTA5LDIuOTE2NDRjLTAuODA2MzMsMC44MTQ0MyAtMS42MzI4MiwxLjY0OTIxIC0yLjQzOTE1LDIuNDYzNjRNMTk5LjE2NjQyLDE1MS4xMDU1NnYwYzAuODA2MzMsLTAuODE0NDMgMS42NzMxNCwtMS42ODk5NCAyLjYyMDU3LC0yLjY0NjljMS4yNzkzOSwtMS4xMDQyMiAyLjYyNzcsLTIuMTI2IDQuMDM2NzIsLTMuMDU5MTdjMC44OTQ1MSwtMC42MDIwOSAxLjgyNTUzLC0xLjE0ODA0IDIuNzg3NzEsLTEuNjM0NzNsMS4zMzMyOCwtMC43NzY1NGwwLjEyMDk1LC0wLjEyMjE2YzUuNzY0NjIsLTIuOTY4OTcgMTIuMTE0NDIsLTQuNjI2NTUgMTguNTk0NjksLTQuODUzOTljNS41NTI1MSwtMC4xMzkyMyAxMS4wODAxLDAuNzg5MTkgMTYuMjgyNDQsMi43MzQ4YzIuMDM3NjksMC43OTM2NiA0LjAxMzEzLDEuNzM4NjggNS45MDk3OCwyLjgyNzE0YzEuNDQxNzYsMC44MjA5OCAyLjgzMzE5LDEuNzI3MzUgNC4xNjY4NSwyLjcxNDI5YzAuNjk2NDIsMC40OTYyIDEuMzYyMTYsMS4wMzQxNCAxLjk5MzUzLDEuNjEwODRjMC4xMjIxNiwwLjEyMDk1IC0wLjA0MDcxLC0wLjA0MDMxIDAuMzQ2MTQsMC4zNDI2OWMwLjM4Njg2LDAuMzgzIDAuNjUxNTUsMC42NDUwNyAwLjc3MzcxLDAuNzY2MDFjMi42OTIxNiwyLjQxMTA3IDIuOTY0NzQsNi41MzEzMSAwLjYxMzYyLDkuMjc2MDFsLTAuNDgzODEsMC40ODg2NmMtMi4yNjcwNSwyLjI2MzMzIC01LjgyNTksMi41ODc3NiAtOC40NjQ4MywwLjc3MTY0bC0wLjcxMTYsLTAuNTAyOTVsLTAuNDY4MjksLTAuNDYzNjVjLTAuNDAwNTUsLTAuMjk3NDIgLTAuODIxNjQsLTAuNTY2MTQgLTEuMjYwMTQsLTAuODA0MDlsLTAuMTQyNTMsLTAuMTQxMTFjLTAuNzMxNTcsLTAuNDQyMDcgLTEuOTA5NDUsLTEuMDAzNDYgLTIuODg0MTMsLTEuNDQ0MzFjLTEuMzEwNjksLTAuNTMzNTEgLTIuNjUyNTgsLTAuOTg2OSAtNC4wMTgyNywtMS4zNTc2MWMtMy40MjAwNSwtMC44NDI0MSAtNi45NzA0NiwtMS4wMTcyOSAtMTAuNDU2NzMsLTAuNTE1MDRjLTMuODMwMDcsMC41NTUxIC03LjQ4NjA2LDEuOTY1MTcgLTEwLjY5NjkyLDQuMTI1N2wtMS4wMDkzNCwwLjczNDRsLTEuMTcwNzksMC44NTY3N2wtMC4zNjI4NCwwLjM2NjQ5bC0xLjA2ODM4LDEuMDc5MTJsLTAuODA2MzMsMC44MTQ0M2MtMS4xOTQ5OSwxLjM4MDcyIC0yLjI0NjQ4LDIuODc5MzggLTMuMTM4MjIsNC40NzI4N2MtMS42MDIyNywyLjc0OTA0IC0yLjY2MDE3LDUuNzgwODkgLTMuMTE1OTUsOC45Mjk5NmwtMC4wNTgyNSwwLjUwNjc4bDYuNTYzNDcsLTAuMTU0MzVjMi4yNDU5NSwtMC4wMTk5OSA0LjI4MDg3LDEuMzIwNTIgNS4xNDkxLDMuMzkxOTdjMC44NjgyMywyLjA3MTQ1IDAuMzk3MjQsNC40NjIyOSAtMS4xOTE3Niw2LjA0OTY3bC0xNy43OTk3NSwxNy45Nzg0OWMtMi4xNDg2OCwyLjE3MDI3IC01LjY0OTksMi4xODc3NiAtNy44MjAxNiwwLjAzOTA3bC0xNy45Nzg1LC0xNy43OTk3NmMtMS42MDQ3OCwtMS41NzE0MiAtMi4wOTk2NCwtMy45NTc0NSAtMS4yNTIxNSwtNi4wMzc0N2MwLjg0NzQ5LC0yLjA4MDAzIDIuODY4OTIsLTMuNDQwOCA1LjExNDk2LC0zLjQ0MzI1bDcuMzUzNDksLTAuMTc4NTZjMC4yMzUwMiwtMS42MjE5OCAwLjUzNzcsLTMuMjE3MjYgMC45MDgwNiwtNC43ODU4OWMxLjQ2ODUxLC01LjY2MTk4IDQuMDA3LC0xMC45OTA1OSA3LjQ3ODU1LC0xNS42OTgzNGMxLjIwOTM5LC0xLjY0NTY3IDIuNTQyNzEsLTMuMTk2NTkgMy45ODgzLC00LjYzOTJ6IiBmaWxsPSIjMWE2MjYyIiBzdHJva2U9IiMxYTYyNjIiIHN0cm9rZS13aWR0aD0iMyIvPjxwYXRoIGQ9Ik0yMDAuOTcxNjgsMTUzLjAwNjkxYzAuODA2MzMsLTAuODE0NDMgMS42Mzc5MywtMS42NTI3OCAyLjQ0NDI2LC0yLjQ2NzIxYzEuMjIxMzIsLTEuMDUzNiAyLjUxNjU1LC0yLjAzMTM1IDMuODYyNjUsLTIuOTIwMDRjMS4yNTI1MiwtMC44NDE0NiAyLjU1NDA2LC0xLjYyMDc1IDMuODg1NCwtMi4zMzA5NWM1LjM3OTI0LC0yLjc4MDY4IDExLjMzMzMsLTQuMzMwMTQgMTcuMzg1MDUsLTQuNTQxNDJjNS4xNjUsLTAuMTMzMjEgMTAuMzIxNywwLjczNzc2IDE1LjE2MTE5LDIuNTQ3MjNjMS44ODk2OSwwLjczMjAxIDMuNzI2NSwxLjYwNzczIDUuNDg3MjcsMi42MTA5NGMxLjM3MDY0LDAuNzg2NTggMi42OTY3MSwxLjY1NjM3IDMuOTY2MjUsMi41OTc0NmMwLjc3MjE1LDAuNTY5ODEgMS41MTM3OCwxLjE4MzkzIDIuMjE4ODYsMS44MzQ5MmMwLjQ0NzkzLDAuNDQzNDkgMC42NzIzMiwwLjY2NjAxIDAuNjcyMzIsMC42NjYwMWMwLjc1NzYsMC42NTA0NyAxLjIyNjAyLDEuNTc4NTQgMS4yOTk0MywyLjU3NDM1YzAuMDczNDEsMC45OTU4MiAtMC4yNTIxNywxLjk4MTg5IC0wLjkwNjIsMi43MzY0MmwtMC4yMDE1NCwwLjIwMzczYy0xLjI3ODU5LDEuMjYwMjYgLTMuMzQ5MzcsMS40ODA0OSAtNC44NDEyLDAuNDgxNzVjMCwwIC0wLjI0Mzk2LC0wLjI0MTAzIC0wLjc5MjQ5LC0wLjU0MjJjLTAuNjczODEsLTAuNTAxNjYgLTEuMzc4ODcsLTAuOTU2MDYgLTIuMTEyNzgsLTEuMzY0ODJjLTAuNzcxODgsLTAuNDAxMzQgLTEuNzQ1MjYsLTAuOTIwMTYgLTMuMDQ0ODksLTEuNTIxNDhjLTEuNDU1MjQsLTAuNTk4MiAtMi45NDM1MSwtMS4xMDA4NCAtNC40NjE4OSwtMS41MTMzYy0zLjc3MTM2LC0wLjkyODUgLTcuNjc1OTUsLTEuMTEyNjYgLTExLjUyMDUzLC0wLjU2MDk1Yy00LjI5NTY2LDAuNjM0NiAtOC4zNzMyNywyLjIzNDA3IC0xMS45NzA1Myw0LjY2NjE2Yy0wLjQ2MzY0LDAuNDY4MyAtMi4wMTExMywxLjQyODM1IC0yLjU1NTQsMS45NzgwOXYwYy0wLjM4MywwLjM4Njg2IC0wLjgwMTYsMC44MTM3OCAtMS4yNDUwOSwxLjI2MTcybC0wLjk0MzM4LDAuOTU1OThjLTEuMzY4MTUsMS41MTgxNSAtMi41NTUwOSwzLjE2MTY1IC0zLjU4MTcxLDQuOTI4NzZjLTEuNzQ1NzMsMy4wOTc4NSAtMi44NzQ3MSw2LjQ1NTU0IC0zLjM0NDM0LDkuOTgwMjVjLTAuMTY1NjMsMS4yNDk5NCAtMC4yNTk2LDIuNDk4MTggLTAuMjcwMzUsMy43NTlsOS40NzE1NywtMC4wNDU1NmMxLjA4MTk4LC0wLjAwOTM3IDIuMDQ5MDYsMC42MzY2NyAyLjQ2NzM3LDEuNjM0NTRjMC40MTgzMSwwLjk5Nzg4IDAuMjAyMTcsMi4yMzc0OCAtMC41NjMwNiwzLjAwMjQ2bC0xNy44MDQ3MywxNy45NTI1NWMtMS4wMzUzOCwxLjA0NTc4IC0yLjcyNTg2LDEuMDUyMDkgLTMuNzcxNjUsMC4wMTY3MWwtMTguMDU2NywtMTcuODgzNWMtMC43OTA1NiwtMC43NTc1OSAtMS4wMzgwNCwtMS45MjQ5MSAtMC42MjI1OCwtMi45MzhjMC40MTU0NSwtMS4wMTMxIDEuNDk0OTEsLTEuNjg3NjkgMi41ODk3NiwtMS42NzIxM2w5Ljk4NzMsLTAuMDMyNjdsLTAuMDY2OTQsLTAuMzIxMzZjMC4xNzM0OCwtMi4yNDQ1NCAwLjUwNjE5LC00LjQ4NDM4IDAuOTk4NDcsLTYuNjgxMTRjMS4zNjI4LC01LjMyNDkgMy43NDc0NiwtMTAuMzY1MTEgNy4wMDYwNSwtMTQuNzkxNTVjMS4xMzQ5MiwtMS41NDU3MyAyLjM5NDk1LC0zLjAwOTE0IDMuNzU0MDQsLTQuMzYxOTgiIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O25vSG92ZXImcXVvdDs6ZmFsc2UsJnF1b3Q7b3JpZ0l0ZW0mcXVvdDs6WyZxdW90O1BhdGgmcXVvdDsseyZxdW90O2FwcGx5TWF0cml4JnF1b3Q7OnRydWUsJnF1b3Q7c2VnbWVudHMmcXVvdDs6W1tbMjY1LjEzOTExLDEzOC4zNzk3OV0sWzAsMF0sWy0xLjg0MjIyLC0xLjEzODM3XV0sW1syNTkuMzc0MTUsMTM1LjQwNTQzXSxbMS45OTUzNSwwLjg0MTU4XSxbLTUuNzE2MjYsLTIuNDE5NDFdXSxbWzI0MS4zMjI4LDEzMS41ODg3XSxbNi4yMDYzNSwwLjEwMTQ5XSxbLTIuNTQyMDcsMC4wMzQ5NF1dLFtbMjMzLjczMzQyLDEzMi4yMzg0NF0sWzIuNTExMDEsLTAuMzk3NjZdLFswLDBdXSxbMjMzLjM1MDEzLDEzMi4zMDgzN10sW1syMzAuNzAwMDIsMTIxLjM0NDY3XSxbMCwwXSxbLTAuMjY5ODIsLTEuMjA2NzRdXSxbWzIyOC4yMjc4OCwxMTkuMDI4MjRdLFsxLjIyMTcyLDAuMTkwODZdLFstMS4yMjE3MiwtMC4xOTA4Nl1dLFtbMjI1LjE2NzAyLDEyMC40ODAzMV0sWzAuNjI1MDEsLTEuMDY2OTRdLFswLDBdXSxbWzIxMC4yNzU4NSwxNDQuOTc2NzFdLFswLDBdLFstMC44NjMyNiwxLjQyMDA5XV0sW1syMTEuMjg0MDcsMTQ5LjExMTA3XSxbLTEuNDIwMDksLTAuODYzMjZdLFswLDBdXSxbWzIzNS42Njk4OCwxNjMuOTM1MDFdLFswLDBdLFsxLjA0MDgzLDAuNjQwMV1dLFtbMjM5LjAyMTc5LDE2My44MDEyNF0sWy0wLjk4NjUxLDAuNzIxMDFdLFswLjk4NjUxLC0wLjcyMTAxXV0sW1syNDAuMTY3MDEsMTYwLjY0ODJdLFswLjI5Mzg0LDEuMTg2MDVdLFswLDBdXSxbWzIzNy42NDcsMTUwLjIxNzk3XSxbMCwwXSxbMS4zODc3NywtMC4zMTg2Ml1dLFtbMjQxLjg1NzI1LDE0OS41MjA5M10sWy0xLjQxNjQxLDAuMTQ1NjNdLFszLjk5NDgzLC0wLjQwNzg1XV0sW1syNTMuNzQ4NCwxNTAuNTc3NDRdLFstMy44NjAzNywtMS4xMDU3Nl0sWzIuMjEwMTUsMC42NjQ2MV1dLFtbMjYwLjEyMDAzLDE1My4yMzkwMV0sWy0yLjAyNjE4LC0xLjEwNV0sWzAsMF1dLFtbMjYxLjQxOTUsMTU0LjAyODk0XSxbMCwwXSxbMC42MDgyNiwwLjM2OTc2XV0sW1syNjMuMTMzNjksMTU1LjA3MDk5XSxbLTAuNTI1MzIsLTAuMzE5MzNdLFswLDBdXSxbWzI2My4xMzM2OSwxNTUuMDcwOTldLFswLDBdLFswLjc0NjUsMC40NTM3OV1dLFtbMjY1Ljk3ODIsMTU3LjM2ODExXSxbLTAuNjM1OTEsLTAuMzg2NTZdLFszLjYxNDI5LDMuMzE0MDZdXSxbWzI3NC4yMzgyOSwxNjkuMzE4N10sWy0xLjgyMjg0LC00LjU1MjNdLFsxLjYxMzU4LDQuMDc4NTRdXSxbWzI3Ni42MzM1MywxODIuMTM0MzNdLFswLjAzMTU2LC00LjM4NjAyXSxbLTAuMDU1MTUsMS43NzU5Nl1dLFtbMjc2LjEzNjc0LDE4Ny40MzY0XSxbMC4yNzU3MywtMS43NTUzXSxbLTAuMzE5OTIsMS41ODUxOV1dLFtbMjc1LjI2MDUyLDE5MS4xODI1Ml0sWzAuMjM4NTgsLTAuOTUzMDZdLFstMC4yNTY2NywwLjkxMzI4XV0sW1syNzQuMzEyNzYsMTkzLjg2MjhdLFswLjM3NDQ3LC0wLjg3MTYzXSxbLTAuMTg3MDYsMC42ODE0NV1dLFtbMjczLjkyNDAyLDE5NC44NzYwNF0sWzAsMF0sWy0wLjcwNjExLDEuOTAwNDddXSxbWzI3NS42NjQ2MywxOTkuOTg1NzNdLFstMS43MTk0MywtMS4wNzQyMV0sWzAsMF1dLFtbMjc1Ljk0MTEyLDIwMC4xNTM4XSxbMCwwXSxbMS4wMDAyMSwwLjUyMDY4XV0sW1syNzkuMTgyNDIsMjAwLjQzMzU0XSxbLTEuMDc0NjMsMC4zNDE2MV0sWzEuMDc0NjMsLTAuMzQxNjFdXSxbWzI4MS42NjcyMiwxOTguMzMzNTJdLFstMC41MTU5NywxLjAwMjY2XSxbMCwwXV0sW1syODIuMjIxODUsMTk3LjQyMTEyXSxbLTAuMzY5NzYsMC42MDgyNl0sWzAuNTMwMzEsLTAuOTQ1MDldXSxbWzI4My42NTM3NCwxOTQuNTA1MDNdLFstMC40MjM1NiwwLjk5NzUxXSxbMC43MDEwNCwtMS42NDExN11dLFtbMjg1LjQ2NDExLDE4OS40NzEzN10sWy0wLjUwNDgzLDEuNzExNzNdLFswLjY0MDU1LC0yLjE5NzA0XV0sW1syODYuODkwMTYsMTgyLjc2NTJdLFstMC4zMDg4NSwyLjI2NzU5XSxbMC43MTkzNSwtNS43OTAyXV0sW1syODUuNzA4MzksMTY1LjQ2MTgyXSxbMS40OTk5MSw1LjYzODY0XSxbLTEuODE4MDYsLTYuNTkyMjRdXSxbWzI3Ni4xNjc0NCwxNDcuNTgyOTNdLFs0LjQ2NDIsNS4xODAxNl0sWy0xLjEyOTAzLC0xLjI3NjMxXV0sW1syNzIuNTkyNzMsMTQzLjkzMzE1XSxbMS4yNTI1NiwxLjE1NTMyXSxbLTEuMzI4OTUsLTEuMjQ1NzRdXSxbWzI2OC4zNzg4MywxNDAuNDYyOF0sWzEuNDc3NCwxLjA2NTQ2XSxbLTEuMTA1OTMsLTAuNjcyMjldXSxbWzI2NS4wMzMzOSwxMzguNDI5MTNdLFsxLjEwNTkzLDAuNjcyMjldLFswLDBdXV0sJnF1b3Q7ZmlsbENvbG9yJnF1b3Q7OlswLDAsMCwxXX1dfSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjAuNSIvPjwvZz48L2c+PC9zdmc+PCEtLXJvdGF0aW9uQ2VudGVyOjcyLjQ4NjIxOTk5OTk5OTk3OjcyLjQ4NjIyLS0+";
+ const tr_icon =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIyMHB4IiBoZWlnaHQ9IjIwcHgiIHZpZXdCb3g9Ii0yLjI2MzMxLC0yLjQxMTU2LDIwLDIwIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjMyLjI3NjYyLC0xNzIuNTczMTIpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGw9IiNmZmZmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWRhc2hhcnJheT0iIiBzdHJva2UtZGFzaG9mZnNldD0iMCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0yNDMuOTE0MTksMTgzLjk1OTEzYzAuMzY1OSwwIDAuNjcyNDYsMC4yOTYxOSAwLjY3MjQ2LDAuNjcwMTFjMCwwLjM2NDA4IC0wLjMwNjU2LDAuNjU5MjkgLTAuNjcyNDYsMC42NTkyOWMtMC4zNjU5LDAgLTAuNjYyNTcsLTAuMjk1MjEgLTAuNjYyNTcsLTAuNjU5MjljMCwtMC4zNzM5MiAwLjI5NjY3LC0wLjY3MDExIDAuNjYyNTcsLTAuNjcwMTF6TTIzNS4wNDMwMiwxNzkuMDUwMDN2MC4wMDk4NGMtMC4xODc4OSwwLjQ4MjE2IC0wLjcwMTE0LDAuNzc3MzYgLTEuMjI1MjcsMC42NDk0NGMtMC41NjQ2NywtMC4xMjc5MiAtMC45MjA2OCwtMC42OTg2NCAtMC43OTExMywtMS4yNTk1MmMwLjU0MzksLTIuMzQxOTMgMi40NDI2MiwtNC4zODg2NiA0Ljg5NDE0LC01LjEyNjY3YzEuMjE3MzUsLTAuMzczOTIgMi41NDE1MiwtMC40MjMxMiAzLjgxNzIyLC0wLjE0ODU4YzEuMDk4NjgsMC4yMzYxNiAyLjE0NTk1LDAuNzM4MDEgMy4wNDU4NiwxLjQzNjY1bDAuOTg4OTEsLTAuOTg0YzAuNDU0OSwtMC40NDE4MiAxLjIyNzI1LC0wLjEyNzkyIDEuMjI3MjUsMC41MTI2NnY0LjY5MjcyYzAsMC4zOTQ1OSAtMC4zMjYzNCwwLjcxOTMxIC0wLjcyMjg5LDAuNzE5MzFoLTQuNzE2MTRjLTAuNjQzNzksMCAtMC45NjAyNCwtMC43Njg1MSAtMC41MTUyMywtMS4yMjExNWwxLjE1NzAzLC0xLjE1MDNjLTAuOTA5OCwtMC44MzY0MSAtMi4yNDM4NSwtMS4yNjkzNiAtMy41Nzk4NywtMS4wNDMwNWMtMS41MjI5MywwLjIyNTM0IC0yLjk3NjYzLDEuMzQ3MSAtMy41Nzk4NywyLjkxMjY2ek0yNDEuMjkzMzcsMTg1LjQwNjZjMC40MTUzNSwwIDAuNzQxNjksMC4zMjQ3MyAwLjc0MTY5LDAuNzM4MDFjMCwwLjQxMzI4IC0wLjMyNjM0LDAuNzQ3ODQgLTAuNzQxNjksMC43NDc4NGMtMC40MTUzNSwwIC0wLjc1MTU4LC0wLjMzNDU2IC0wLjc1MTU4LC0wLjc0Nzg0YzAsLTAuNDEzMjggMC4zMzYyMywtMC43MzgwMSAwLjc1MTU4LC0wLjczODAxek0yMzUuNjE2NTksMTgzLjgxMjMyYzAuNTA1MzQsMCAwLjkwOTgxLDAuNDAzNDQgMC45MDk4MSwwLjkwNTI4YzAsMC40OTIgLTAuNDA0NDcsMC44OTU0NCAtMC45MDk4MSwwLjg5NTQ0Yy0wLjUwNDM1LDAgLTAuOTA5OCwtMC40MDM0NSAtMC45MDk4LC0wLjg5NTQ0YzAsLTAuNTAxODUgMC40MDU0NSwtMC45MDUyOCAwLjkwOTgsLTAuOTA1Mjh6TTIzNC4wMzQ2MywxODEuMTU1NDFjMC41NDM5LDAgMC45ODg5MSwwLjQ0MjggMC45ODg5MSwwLjk4NGMwLDAuNTUxMDQgLTAuNDQ1MDEsMC45ODQgLTAuOTg4OTEsMC45ODRjLTAuNTQzOSwwIC0wLjk4ODkyLC0wLjQzMjk2IC0wLjk4ODkyLC0wLjk4NGMwLC0wLjU0MTIgMC40NDUwMiwtMC45ODQgMC45ODg5MiwtMC45ODR6TTIzOC4yNjc2OCwxODUuMzQ2ODdjMC40NTQ5LDAgMC44MjA4LDAuMzczOTMgMC44MjA4LDAuODI2NTdjMCwwLjQ1MjY0IC0wLjM2NTksMC44MjY1NiAtMC44MjA4LDAuODI2NTZjLTAuNDU0OSwwIC0wLjgzMTY3LC0wLjM3MzkyIC0wLjgzMTY3LC0wLjgyNjU2YzAsLTAuNDUyNjQgMC4zNzY3NywtMC44MjY1NyAwLjgzMTY3LC0wLjgyNjU3eiIgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aW5kZXgmcXVvdDs6bnVsbH0iIHN0cm9rZT0iIzI1ODM4MyIgc3Ryb2tlLXdpZHRoPSIxLjUiLz48cGF0aCBkPSJNMjQzLjkxNDE5LDE4My45NTkxM2MwLjM2NTksMCAwLjY3MjQ2LDAuMjk2MTkgMC42NzI0NiwwLjY3MDExYzAsMC4zNjQwOCAtMC4zMDY1NiwwLjY1OTI4IC0wLjY3MjQ2LDAuNjU5MjhjLTAuMzY1OSwwIC0wLjY2MjU3LC0wLjI5NTIgLTAuNjYyNTcsLTAuNjU5MjhjMCwtMC4zNzM5MiAwLjI5NjY3LC0wLjY3MDExIDAuNjYyNTcsLTAuNjcwMTF6TTIzNS4wNDMwMiwxNzkuMDUwMDN2MC4wMDk4NGMtMC4xODc4OSwwLjQ4MjE2IC0wLjcwMTE0LDAuNzc3MzYgLTEuMjI1MjcsMC42NDk0NGMtMC41NjQ2NywtMC4xMjc5MiAtMC45MjA2OCwtMC42OTg2NCAtMC43OTExMywtMS4yNTk1M2MwLjU0MzksLTIuMzQxOTMgMi40NDI2MiwtNC4zODg2NiA0Ljg5NDE0LC01LjEyNjY2YzEuMjE3MzYsLTAuMzczOTIgMi41NDE1MSwtMC40MjMxMiAzLjgxNzIxLC0wLjE0ODU4YzEuMDk4NjksMC4yMzYxNiAyLjE0NTk1LDAuNzM4IDMuMDQ1ODYsMS40MzY2NWwwLjk4ODkyLC0wLjk4NGMwLjQ1NDksLTAuNDQxODIgMS4yMjcyNCwtMC4xMjc5MiAxLjIyNzI0LDAuNTEyNjd2NC42OTI3MmMwLDAuMzk0NTkgLTAuMzI2MzQsMC43MTkzMSAtMC43MjI5LDAuNzE5MzFoLTQuNzE2MTRjLTAuNjQzNzgsMCAtMC45NjAyNCwtMC43Njg1MSAtMC41MTUyMywtMS4yMjExNWwxLjE1NzAzLC0xLjE1MDNjLTAuOTA5OCwtMC44MzY0IC0yLjI0Mzg1LC0xLjI2OTM3IC0zLjU3OTg3LC0xLjA0MzA1Yy0xLjUyMjkzLDAuMjI1MzQgLTIuOTc2NjQsMS4zNDcxIC0zLjU3OTg4LDIuOTEyNjV6TTI0MS4yOTMzNiwxODUuNDA2NmMwLjQxNTM0LDAgMC43NDE2OSwwLjMyNDcyIDAuNzQxNjksMC43MzhjMCwwLjQxMzI4IC0wLjMyNjM0LDAuNzQ3ODQgLTAuNzQxNjksMC43NDc4NGMtMC40MTUzNCwwIC0wLjc1MTU4LC0wLjMzNDU2IC0wLjc1MTU4LC0wLjc0Nzg0YzAsLTAuNDEzMjggMC4zMzYyMywtMC43MzggMC43NTE1OCwtMC43Mzh6TTIzNS42MTY1OSwxODMuODEyMzJjMC41MDUzNCwwIDAuOTA5OCwwLjQwMzQ0IDAuOTA5OCwwLjkwNTI4YzAsMC40OTIgLTAuNDA0NDcsMC44OTU0NCAtMC45MDk4LDAuODk1NDRjLTAuNTA0MzUsMCAtMC45MDk4LC0wLjQwMzQ0IC0wLjkwOTgsLTAuODk1NDRjMCwtMC41MDE4NCAwLjQwNTQ2LC0wLjkwNTI4IDAuOTA5OCwtMC45MDUyOHpNMjM0LjAzNDYyLDE4MS4xNTU0MWMwLjU0MzksMCAwLjk4ODkyLDAuNDQyOCAwLjk4ODkyLDAuOTg0YzAsMC41NTEwNCAtMC40NDUwMSwwLjk4NCAtMC45ODg5MiwwLjk4NGMtMC41NDM5LDAgLTAuOTg4OTIsLTAuNDMyOTYgLTAuOTg4OTIsLTAuOTg0YzAsLTAuNTQxMiAwLjQ0NTAxLC0wLjk4NCAwLjk4ODkyLC0wLjk4NHpNMjM4LjI2NzY4LDE4NS4zNDY4N2MwLjQ1NDksMCAwLjgyMDgsMC4zNzM5MiAwLjgyMDgsMC44MjY1NmMwLDAuNDUyNjQgLTAuMzY1OSwwLjgyNjU2IC0wLjgyMDgsMC44MjY1NmMtMC40NTQ5LDAgLTAuODMxNjgsLTAuMzczOTIgLTAuODMxNjgsLTAuODI2NTZjMCwtMC40NTI2NCAwLjM3Njc4LC0wLjgyNjU2IDAuODMxNjgsLTAuODI2NTZ6IiBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpbmRleCZxdW90OzpudWxsfSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiLz48L2c+PC9nPjwvc3ZnPjwhLS1yb3RhdGlvbkNlbnRlcjo3LjcyMzM3OTk5OTk5OTk5Mjo3LjQyNjg4MDAwMDAwMDAxMS0tPg==";
+ const tl_icon =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIyMHB4IiBoZWlnaHQ9IjIwcHgiIHZpZXdCb3g9Ii0yLjI2MzMxLC0yLjQxMTU2LDIwLDIwIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjM0LjUxMzMxLC0xNzUuMDgyNykiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbD0iI2ZmZmZmZiIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTIzOS4wMTE2OSwxODcuMDQwOGMwLDAuMzY0MDggLTAuMjk2NjcsMC42NTkyOSAtMC42NjI1NywwLjY1OTI5Yy0wLjM2NTksMCAtMC42NzI0NiwtMC4yOTUyMSAtMC42NzI0NiwtMC42NTkyOWMwLC0wLjM3MzkyIDAuMzA2NTYsLTAuNjcwMTEgMC42NzI0NiwtMC42NzAxMWMwLjM2NTksMCAwLjY2MjU3LDAuMjk2MTkgMC42NjI1NywwLjY3MDExek0yNDcuMjIwMjgsMTgxLjQ2MTZjLTAuNjAzMjQsLTEuNTY1NTYgLTIuMDU2OTQsLTIuNjg3MzIgLTMuNTc5ODcsLTIuOTEyNjZjLTEuMzM2MDIsLTAuMjI2MzEgLTIuNjcwMDcsMC4yMDY2NCAtMy41Nzk4NywxLjA0MzA1bDEuMTU3MDMsMS4xNTAzYzAuNDQ1MDEsMC40NTI2NCAwLjEyODU2LDEuMjIxMTUgLTAuNTE1MjMsMS4yMjExNWgtNC43MTYxNGMtMC4zOTY1NSwwIC0wLjcyMjg5LC0wLjMyNDcyIC0wLjcyMjg5LC0wLjcxOTMxdi00LjY5MjcyYzAsLTAuNjQwNTggMC43NzIzNSwtMC45NTQ0OCAxLjIyNzI1LC0wLjUxMjY2bDAuOTg4OTEsMC45ODRjMC44OTk5MSwtMC42OTg2NCAxLjk0NzE4LC0xLjIwMDQ5IDMuMDQ1ODYsLTEuNDM2NjVjMS4yNzU3LC0wLjI3NDU0IDIuNTk5ODcsLTAuMjI1MzQgMy44MTcyMiwwLjE0ODU4YzIuNDUxNTIsMC43MzgwMSA0LjM1MDI0LDIuNzg0NzQgNC44OTQxNCw1LjEyNjY3YzAuMTI5NTUsMC41NjA4OCAtMC4yMjY0NiwxLjEzMTYgLTAuNzkxMTMsMS4yNTk1MmMtMC41MjQxMywwLjEyNzkyIC0xLjAzNzM4LC0wLjE2NzI4IC0xLjIyNTI3LC0wLjY0OTQ0di0wLjAwOTg0ek0yNDEuNzIxNTIsMTg4LjU1NjE3YzAsMC40MTMyOCAtMC4zMzYyMywwLjc0Nzg0IC0wLjc1MTU4LDAuNzQ3ODRjLTAuNDE1MzUsMCAtMC43NDE2OSwtMC4zMzQ1NiAtMC43NDE2OSwtMC43NDc4NGMwLC0wLjQxMzI4IDAuMzI2MzQsLTAuNzM4MDEgMC43NDE2OSwtMC43MzgwMWMwLjQxNTM1LDAgMC43NTE1OCwwLjMyNDczIDAuNzUxNTgsMC43MzgwMXpNMjQ3LjU1NjUyLDE4Ny4xMjkxNmMwLDAuNDkxOTkgLTAuNDA1NDUsMC44OTU0NCAtMC45MDk4LDAuODk1NDRjLTAuNTA1MzQsMCAtMC45MDk4MSwtMC40MDM0NCAtMC45MDk4MSwtMC44OTU0NGMwLC0wLjUwMTg0IDAuNDA0NDcsLTAuOTA1MjggMC45MDk4MSwtMC45MDUyOGMwLjUwNDM1LDAgMC45MDk4LDAuNDAzNDMgMC45MDk4LDAuOTA1Mjh6TTI0OS4yMTc2LDE4NC41NTA5N2MwLDAuNTUxMDQgLTAuNDQ1MDIsMC45ODQgLTAuOTg4OTIsMC45ODRjLTAuNTQzOSwwIC0wLjk4ODkxLC0wLjQzMjk2IC0wLjk4ODkxLC0wLjk4NGMwLC0wLjU0MTIgMC40NDUwMSwtMC45ODQgMC45ODg5MSwtMC45ODRjMC41NDM5LDAgMC45ODg5MiwwLjQ0MjggMC45ODg5MiwwLjk4NHpNMjQ0LjgyNzMsMTg4LjU4NWMwLDAuNDUyNjQgLTAuMzc2NzcsMC44MjY1NiAtMC44MzE2NywwLjgyNjU2Yy0wLjQ1NDksMCAtMC44MjA4LC0wLjM3MzkyIC0wLjgyMDgsLTAuODI2NTZjMCwtMC40NTI2NCAwLjM2NTksLTAuODI2NTcgMC44MjA4LC0wLjgyNjU3YzAuNDU0OSwwIDAuODMxNjcsMC4zNzM5MyAwLjgzMTY3LDAuODI2NTd6IiBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpbmRleCZxdW90OzpudWxsfSIgc3Ryb2tlPSIjMjU4MzgzIiBzdHJva2Utd2lkdGg9IjEuNSIvPjxwYXRoIGQ9Ik0yMzkuMDExNjksMTg3LjA0MDhjMCwwLjM2NDA4IC0wLjI5NjY3LDAuNjU5MjggLTAuNjYyNTcsMC42NTkyOGMtMC4zNjU5LDAgLTAuNjcyNDYsLTAuMjk1MiAtMC42NzI0NiwtMC42NTkyOGMwLC0wLjM3MzkyIDAuMzA2NTYsLTAuNjcwMTEgMC42NzI0NiwtMC42NzAxMWMwLjM2NTksMCAwLjY2MjU3LDAuMjk2MTkgMC42NjI1NywwLjY3MDExek0yNDcuMjIwMzEsMTgxLjQ2MTZjLTAuNjAzMjQsLTEuNTY1NTUgLTIuMDU2OTUsLTIuNjg3MzEgLTMuNTc5ODgsLTIuOTEyNjVjLTEuMzM2MDIsLTAuMjI2MzIgLTIuNjcwMDcsMC4yMDY2NSAtMy41Nzk4NywxLjA0MzA1bDEuMTU3MDMsMS4xNTAzYzAuNDQ1MDEsMC40NTI2NCAwLjEyODU1LDEuMjIxMTUgLTAuNTE1MjMsMS4yMjExNWgtNC43MTYxNGMtMC4zOTY1NiwwIC0wLjcyMjksLTAuMzI0NzIgLTAuNzIyOSwtMC43MTkzMXYtNC42OTI3MmMwLC0wLjY0MDU5IDAuNzcyMzQsLTAuOTU0NDkgMS4yMjcyNCwtMC41MTI2N2wwLjk4ODkyLDAuOTg0YzAuODk5OTEsLTAuNjk4NjUgMS45NDcxNywtMS4yMDA0OSAzLjA0NTg2LC0xLjQzNjY1YzEuMjc1NywtMC4yNzQ1NCAyLjU5OTg1LC0wLjIyNTM0IDMuODE3MjEsMC4xNDg1OGMyLjQ1MTUyLDAuNzM4IDQuMzUwMjQsMi43ODQ3MyA0Ljg5NDE0LDUuMTI2NjZjMC4xMjk1NSwwLjU2MDg5IC0wLjIyNjQ2LDEuMTMxNjEgLTAuNzkxMTMsMS4yNTk1M2MtMC41MjQxMywwLjEyNzkyIC0xLjAzNzM4LC0wLjE2NzI4IC0xLjIyNTI3LC0wLjY0OTQ0di0wLjAwOTg0ek0yNDEuNzIxNTMsMTg4LjU1NjE2YzAsMC40MTMyOCAtMC4zMzYyNCwwLjc0Nzg0IC0wLjc1MTU4LDAuNzQ3ODRjLTAuNDE1MzUsMCAtMC43NDE2OSwtMC4zMzQ1NiAtMC43NDE2OSwtMC43NDc4NGMwLC0wLjQxMzI4IDAuMzI2MzUsLTAuNzM4IDAuNzQxNjksLTAuNzM4YzAuNDE1MzUsMCAwLjc1MTU4LDAuMzI0NzIgMC43NTE1OCwwLjczOHpNMjQ3LjU1NjUyLDE4Ny4xMjkxNmMwLDAuNDkyIC0wLjQwNTQ1LDAuODk1NDQgLTAuOTA5OCwwLjg5NTQ0Yy0wLjUwNTMzLDAgLTAuOTA5OCwtMC40MDM0NCAtMC45MDk4LC0wLjg5NTQ0YzAsLTAuNTAxODQgMC40MDQ0NiwtMC45MDUyOCAwLjkwOTgsLTAuOTA1MjhjMC41MDQzNCwwIDAuOTA5OCwwLjQwMzQ0IDAuOTA5OCwwLjkwNTI4ek0yNDkuMjE3NjEsMTg0LjU1MDk3YzAsMC41NTEwNCAtMC40NDUwMiwwLjk4NCAtMC45ODg5MiwwLjk4NGMtMC41NDM5MSwwIC0wLjk4ODkyLC0wLjQzMjk2IC0wLjk4ODkyLC0wLjk4NGMwLC0wLjU0MTIgMC40NDUwMiwtMC45ODQgMC45ODg5MiwtMC45ODRjMC41NDM5MSwwIDAuOTg4OTIsMC40NDI4IDAuOTg4OTIsMC45ODR6TTI0NC44MjczMSwxODguNTg0OTljMCwwLjQ1MjY0IC0wLjM3Njc4LDAuODI2NTYgLTAuODMxNjgsMC44MjY1NmMtMC40NTQ5LDAgLTAuODIwOCwtMC4zNzM5MiAtMC44MjA4LC0wLjgyNjU2YzAsLTAuNDUyNjQgMC4zNjU5LC0wLjgyNjU2IDAuODIwOCwtMC44MjY1NmMwLjQ1NDksMCAwLjgzMTY4LDAuMzczOTIgMC44MzE2OCwwLjgyNjU2eiIgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aW5kZXgmcXVvdDs6bnVsbH0iIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIi8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6NS40ODY2OTE0OTU5NDA0Mjo0LjkxNzMwNDExMjkyMTc1NS0tPg==";
+ let gx = [0];
+ let gy = [0];
+ let gr = [90];
+ let gs = [100];
+ let rm = [0];
+ class Global_Coordinate {
+ getInfo() {
+ return {
+ id: "globalCoordinate",
+ color1: "#2ea4a4",
+ menuIconURI: icon,
+ name: "Global Coordinate",
+ blocks: [
+ {
+ opcode: "SET",
+ filter: [Scratch.TargetType.SPRITE],
+ blockType: Scratch.BlockType.COMMAND,
+ blockIconURI: icon,
+ text: "go to x: [x] y: [y] direction [r] size [s] - use screens [screen]",
+ arguments: {
+ x: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ y: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ r: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "90",
+ },
+ s: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "100",
+ },
+ screen: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "rotation_mode",
+ filter: [Scratch.TargetType.SPRITE],
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set screens [screen] 's rotation mode to [m]",
+ arguments: {
+ screen: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ m: {
+ type: Scratch.ArgumentType.NUMBER,
+ menu: "rotation_mode",
+ },
+ },
+ },
+ {
+ opcode: "set",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set screens [screen] 's x: [x] y: [y] direction: [r] size: [s]",
+ arguments: {
+ x: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ y: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ r: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "90",
+ },
+ s: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "100",
+ },
+ screen: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "Set_Co",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set screens [screen] 's x [x] y: [y]",
+ arguments: {
+ x: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ y: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ screen: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "Set_GX",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set screens [screen] 's x to [x]",
+ arguments: {
+ x: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ screen: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "Set_GY",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set screens [screen] 's y to: [y]",
+ arguments: {
+ y: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ screen: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "CX",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "change screens [screen] 's x by [x]",
+ arguments: {
+ x: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ screen: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "CY",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "change screens [screen] 's y by [y]",
+ arguments: {
+ y: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ screen: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "Set_GR",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set screens [screen] 's direction [r]",
+ arguments: {
+ r: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "90",
+ },
+ screen: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "TR",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "turn [tr_icon] [r] degrees - screens [screen]",
+ arguments: {
+ r: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "15",
+ },
+ tr_icon: {
+ type: Scratch.ArgumentType.IMAGE,
+ dataURI: tr_icon,
+ },
+ screen: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "TL",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "turn [tr_icon] [r] degrees - screens [screen]",
+ arguments: {
+ r: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "15",
+ },
+ tr_icon: {
+ type: Scratch.ArgumentType.IMAGE,
+ dataURI: tl_icon,
+ },
+ screen: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "Set_si",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set screens [screen] 's size [s]",
+ arguments: {
+ s: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "100",
+ },
+ screen: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "CS",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "change screens [screen] 's size by [s]",
+ arguments: {
+ s: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ screen: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "x",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "screens [screen] x",
+ arguments: {
+ screen: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "y",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "screens [screen] y",
+ arguments: {
+ screen: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "r",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "screens [screen] direction",
+ arguments: {
+ screen: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "s",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "screens [screen] size",
+ arguments: {
+ screen: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "rm",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "screens [screen] rotation mode is screen?",
+ arguments: {
+ screen: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ },
+ },
+ ],
+ menus: {
+ rotation_mode: {
+ acceptReporters: true,
+ items: [
+ {
+ text: "center of stage",
+ value: "0",
+ },
+ {
+ text: "center of screen",
+ value: "1",
+ },
+ ],
+ },
+ },
+ };
+ }
+ SET(args, { target }) {
+ if (isNaN(args.x)) {
+ args.x = 0;
+ }
+ if (isNaN(args.y)) {
+ args.y = 0;
+ }
+ if (isNaN(args.r)) {
+ args.r = 0;
+ }
+ if (isNaN(args.s)) {
+ args.s = 0;
+ }
+ target.setSize((args.s / 100) * gs[args.screen - 1]);
+ target.setDirection(-(args.r - (gr[args.screen - 1] - 90) - 90) + 90);
+ if (rm[args.screen - 1] == 1) {
+ target.setXY(
+ (gx[args.screen - 1] / 100) * gs[args.screen - 1] +
+ (gs[args.screen - 1] / 100) *
+ (-args.x *
+ Math.cos(
+ ((-(gr[args.screen - 1] - 90 - 90) + 90) / 180) * Math.PI
+ ) -
+ -args.y *
+ Math.sin(
+ ((-(gr[args.screen - 1] - 90 - 90) + 90) / 180) * Math.PI
+ )),
+ (gy[args.screen - 1] / 100) * gs[args.screen - 1] +
+ (gs[args.screen - 1] / 100) *
+ (-args.x *
+ Math.sin(
+ ((-(gr[args.screen - 1] - 90 - 90) + 90) / 180) * Math.PI
+ ) +
+ -args.y *
+ Math.cos(
+ ((-(gr[args.screen - 1] - 90 - 90) + 90) / 180) * Math.PI
+ )),
+ true
+ );
+ } else {
+ target.setXY(
+ (gs[args.screen - 1] / 100) *
+ (-(args.x + gx[args.screen - 1]) *
+ Math.cos(
+ ((-(gr[args.screen - 1] - 90 - 90) + 90) / 180) * Math.PI
+ ) -
+ -(args.y + gy[args.screen - 1]) *
+ Math.sin(
+ ((-(gr[args.screen - 1] - 90 - 90) + 90) / 180) * Math.PI
+ )),
+ (gs[args.screen - 1] / 100) *
+ (-(args.x + gx[args.screen - 1]) *
+ Math.sin(
+ ((-(gr[args.screen - 1] - 90 - 90) + 90) / 180) * Math.PI
+ ) +
+ -(args.y + gy[args.screen - 1]) *
+ Math.cos(
+ ((-(gr[args.screen - 1] - 90 - 90) + 90) / 180) * Math.PI
+ )),
+ true
+ );
+ }
+ }
+ rotation_mode(args) {
+ rm[args.screen - 1] = args.m;
+ }
+ set({ x, y, r, s, screen }) {
+ if (!isNaN(x)) {
+ gx[screen - 1] = x;
+ } else {
+ gx[screen - 1] = 0;
+ }
+ if (!isNaN(y)) {
+ gy[screen - 1] = y;
+ } else {
+ gy[screen - 1] = 0;
+ }
+ if (!isNaN(r)) {
+ gr[screen - 1] = r;
+ } else {
+ gr[screen - 1] = 0;
+ }
+ if (!isNaN(s)) {
+ gs[screen - 1] = s;
+ } else {
+ gs[screen - 1] = 0;
+ }
+ }
+ Set_Co({ x, y, screen }) {
+ if (!isNaN(x + y)) {
+ gx[screen - 1] = x;
+ gy[screen - 1] = y;
+ } else {
+ if (isNaN(x)) {
+ x = 0;
}
- rm({ screen }) {
- return rm[screen - 1] == 1;
+ if (isNaN(y)) {
+ y = 0;
}
+ }
+ }
+ Set_GX({ x, screen }) {
+ if (!isNaN(x)) {
+ gx[screen - 1] = x;
+ } else {
+ gx[screen - 1] = 0;
+ }
+ }
+ Set_GY({ y, screen }) {
+ if (!isNaN(y)) {
+ gy[screen - 1] = y;
+ } else {
+ gy[screen - 1] = 0;
+ }
+ }
+ CX({ x, screen }) {
+ if (!isNaN(x)) {
+ gx[screen - 1] += x;
+ }
+ }
+ CY({ y, screen }) {
+ if (!isNaN(y)) {
+ gy[screen - 1] += y;
+ }
+ }
+ Set_GR({ r, screen }) {
+ if (!isNaN(r)) {
+ gr[screen - 1] = r;
+ } else {
+ gr[screen - 1] = 0;
+ }
+ }
+ TR({ r, screen }) {
+ if (!isNaN(r)) {
+ gr[screen - 1] += r;
+ }
+ }
+ TL({ r, screen }) {
+ if (!isNaN(r)) {
+ gr[screen - 1] -= r;
+ }
+ }
+ Set_si({ s, screen }) {
+ if (!isNaN(s)) {
+ gs[screen - 1] = s;
+ } else {
+ gs[screen - 1] = 0;
+ }
+ if (gs[screen - 1] < 0) {
+ gs[screen - 1] = 0;
+ }
+ }
+ CS({ s, screen }) {
+ if (!isNaN(s)) {
+ gs[screen - 1] += s;
+ }
+ if (gs[screen - 1] < 0) {
+ gs[screen - 1] = 0;
+ }
+ }
+ x({ screen }) {
+ return gx[screen - 1];
+ }
+ y({ screen }) {
+ return gy[screen - 1];
+ }
+ r({ screen }) {
+ return gr[screen - 1];
+ }
+ s({ screen }) {
+ return gs[screen - 1];
+ }
+ rm({ screen }) {
+ return rm[screen - 1] == 1;
}
- Scratch.extensions.register(new Global_Coordinate());
+ }
+ Scratch.extensions.register(new Global_Coordinate());
})(Scratch);
diff --git a/extensions/NOname-awa/graphics2d.js b/extensions/NOname-awa/graphics2d.js
index 929c86c41f..44bf8d5f3a 100644
--- a/extensions/NOname-awa/graphics2d.js
+++ b/extensions/NOname-awa/graphics2d.js
@@ -1,364 +1,462 @@
-// Name: Graphics 2D
-// Description: Blocks to compute lengths, angles, and areas in two dimensions.
-// By: NOname-awa
-
-(function (Scratch) {
- 'use strict';
- Scratch.translate.setup({
- zh: {
- name: '图形 2D',
- line_section: '线段([x1],[y1])到([x2],[y2])',
- triangle: '三角形([x1],[y1])([x2],[y2])([x3],[y3])的 [CS]',
- triangle_s: '三角形 [s1] [s2] [s3] 的面积',
- quadrilateral: '四边形([x1],[y1])([x2],[y2])([x3],[y3])([x4],[y4])的 [CS]',
- graph: '图形 [graph] 的 [CS]',
- round: '[rd] 为 [a] 的圆的 [CS]',
- pi: '派',
- radius: '半径',
- diameter: '直径',
- area: '面积',
- circumference: '周长',
- }
- });
- class graph {
- getInfo() {
- return {
- id: 'nonameawagraph',
- name: Scratch.translate({ id: 'name', default: 'Graphics 2D' }),
- color1: '#ff976c',
- color2: '#cc7956',
- color3: '#e58861',
- blocks: [
- {
- opcode: 'line_section',
- blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate({ id: 'line_section', default: 'length from ([x1],[y1]) to ([x2],[y2])' }),
- arguments: {
- x1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '-100'
- },
- y1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- x2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- y2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- }
- },
- {
- opcode: 'vertical',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ⊥ [b]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.ANGLE,
- defaultValue: '0'
- },
- b: {
- type: Scratch.ArgumentType.ANGLE,
- defaultValue: '90'
- },
- }
- },
- '---',
- {
- opcode: 'triangle',
- blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate({ id: 'triangle', default: 'triangle ([x1],[y1]) ([x2],[y2]) ([x3],[y3]) \'s [CS]' }),
- arguments: {
- x1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- y1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- x2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- y2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- x3: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- y3: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- CS: {
- type: Scratch.ArgumentType.STRING,
- menu: 'cs'
- },
- },
- },
- {
- opcode: 'triangle_s',
- blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate({ id: 'triangle_s', default: 'triangle [s1] [s2] [s3] \'s area' }),
- arguments: {
- s1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '3'
- },
- s2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '4'
- },
- s3: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '5'
- },
- },
- },
- {
- opcode: 'quadrilateral',
- blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate({ id: 'quadrilateral', default: 'quadrangle ([x1],[y1]) ([x2],[y2]) ([x3],[y3]) ([x4],[y4]) \'s [CS]' }),
- arguments: {
- x1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- y1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- x2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- y2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- x3: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- y3: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- x4: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- y4: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- CS: {
- type: Scratch.ArgumentType.STRING,
- menu: 'cs'
- },
- },
- },
- {
- opcode: 'graph',
- blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate({ id: 'graph', default: 'graph [graph] \'s [CS]' }),
- arguments: {
- graph: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '[[0,0], [0,2], [2,4], [4,2], [4,0]]'
- },
- CS: {
- type: Scratch.ArgumentType.STRING,
- menu: 'cs'
- },
- },
- },
- '---',
- {
- opcode: 'round',
- blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate({ id: 'round', default: 'circle of [rd][a]\'s [CS]' }),
- arguments: {
- rd: {
- type: Scratch.ArgumentType.STRING,
- menu: 'rd'
- },
- a: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- CS: {
- type: Scratch.ArgumentType.STRING,
- menu: 'cs'
- },
- },
- },
- '---',
- {
- opcode: 'pi',
- disableMonitor: true,
- blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate({ id: 'pi', default: 'pi' }),
- },
- ],
- menus: {
- rd: {
- acceptReporters: true,
- items: [
- {
- text: Scratch.translate({ id: 'radius', default: 'radius' }),
- value: 'r'
- },
- {
- text: Scratch.translate({ id: 'diameter', default: 'diameter' }),
- value: 'd'
- }
- ]
- },
- cs: {
- acceptReporters: true,
- items: [
- {
- text: Scratch.translate({ id: 'area', default: 'area' }),
- value: 's'
- },
- {
- text: Scratch.translate({ id: 'circumference', default: 'circumference' }),
- value: 'c'
- }
- ]
- }
- }
- };
- }
- line_section(args) {
- return Math.sqrt(Math.pow(args.x1 - args.x2, 2) + Math.pow(args.y1 - args.y2, 2));
- }
- vertical(args) {
- if (isNaN(args.a) || isNaN(args.b)) {
- return false;
- } else {
- return ((args.a - (args.b - 90)) % 180) == 0;
- }
- }
- triangle(args) {
- if (args.CS == 's') {
- let points = [[args.x1, args.y1], [args.x2, args.y2], [args.x3, args.y3]];
- let area = 0;
- let n = points.length;
- for (let i = 0; i < n; i++) {
- let x1 = points[i][0];
- let y1 = points[i][1];
- let x2 = points[(i + 1) % n][0];
- let y2 = points[(i + 1) % n][1];
- area += x1 * y2;
- area -= x2 * y1;
- }
- area = Math.abs(area) / 2;
- return (area);
- }
- if (args.CS == 'c') {
- let i = 0;
- i += Math.sqrt(Math.pow(args.x1 - args.x2, 2) + Math.pow(args.y1 - args.y2, 2));
- i += Math.sqrt(Math.pow(args.x2 - args.x3, 2) + Math.pow(args.y2 - args.y3, 2));
- i += Math.sqrt(Math.pow(args.x3 - args.x1, 2) + Math.pow(args.y3 - args.y1, 2));
- return i;
- }
- return 0;
- }
- triangle_s(args) {
- const s = (args.s1 + args.s2 + args.s3) / 2;
- const area = Math.sqrt(s * (s - args.s1) * (s - args.s2) * (s - args.s3));
- return area;
- }
- quadrilateral(args) {
- if (args.CS == 's') {
- let points = [[args.x1, args.y1], [args.x2, args.y2], [args.x3, args.y3], [args.x4, args.y4]];
- let area = 0;
- let n = points.length;
- for (let i = 0; i < n; i++) {
- let x1 = points[i][0];
- let y1 = points[i][1];
- let x2 = points[(i + 1) % n][0];
- let y2 = points[(i + 1) % n][1];
- area += x1 * y2;
- area -= x2 * y1;
- }
- area = Math.abs(area) / 2;
- return (area);
- }
- if (args.CS == 'c') {
- let i = 0;
- i += Math.sqrt(Math.pow(args.x1 - args.x2, 2) + Math.pow(args.y1 - args.y2, 2));
- i += Math.sqrt(Math.pow(args.x2 - args.x3, 2) + Math.pow(args.y2 - args.y3, 2));
- i += Math.sqrt(Math.pow(args.x3 - args.x4, 2) + Math.pow(args.y3 - args.y4, 2));
- i += Math.sqrt(Math.pow(args.x4 - args.x1, 2) + Math.pow(args.y4 - args.y1, 2));
- return i;
- }
- return 0;
- }
- graph(args) {
- let points;
- try {
- points = JSON.parse(args.graph);
- } catch (error) {
- return 0;
- }
- if (!Array.isArray(points)) {
- return 0;
- }
- let n = points.length;
- if (args.CS == 's') {
- let area = 0;
- for (let i = 0; i < n; i++) {
- let x1 = points[i][0];
- let y1 = points[i][1];
- let x2 = points[(i + 1) % n][0];
- let y2 = points[(i + 1) % n][1];
- area += x1 * y2;
- area -= x2 * y1;
- }
- area = Math.abs(area) / 2;
- return (area);
- }
- if (args.CS == 'c') {
- let x1, x2, y1, y2;
- let j = 0;
- j = 0;
- var i_end = n - 1;
- var i_inc = 1;
- if (0 > i_end) {
- i_inc = -i_inc;
- }
- for (let i = 0; i_inc >= 0 ? i <= i_end : i >= i_end; i += i_inc) {
- x1 = points[((i + 1) - 1)][0];
- x2 = i == n - 1 ? points[0][0] : points[((i + 2) - 1)][0];
- y1 = points[((i + 1) - 1)][1];
- y2 = i == n - 1 ? points[0][1] : points[((i + 2) - 1)][1];
- j = (typeof j == 'number' ? j : 0) + Math.sqrt(Math.pow(Math.abs(x1 - x2), 2) + Math.pow(Math.abs(y1 - y2), 2));
- }
- return j;
- }
- return 0;
- }
- round(args) {
- if (args.CS == 'c') {
- return 2 * Math.PI * (args.rd == 'r' ? args.a : args.a / 2);
- }
- if (args.CS == 's') {
- return Math.PI * ((args.rd == 'r' ? args.a : args.a / 2) ** 2);
- }
- }
- pi() {
- return Math.PI;
- }
- }
- Scratch.extensions.register(new graph());
-})(Scratch);
+// Name: Graphics 2D
+// ID: nonameawagraph
+// Description: Blocks to compute lengths, angles, and areas in two dimensions.
+// By: NOname-awa
+
+(function (Scratch) {
+ "use strict";
+ Scratch.translate.setup({
+ zh: {
+ name: "图形 2D",
+ line_section: "([x1],[y1])到([x2],[y2])的距离",
+ ray_direction: "([x1],[y1])到([x2],[y2])的方向",
+ triangle: "三角形([x1],[y1])([x2],[y2])([x3],[y3])的 [CS]",
+ triangle_s: "三角形 [s1] [s2] [s3] 的面积",
+ quadrilateral:
+ "四边形([x1],[y1])([x2],[y2])([x3],[y3])([x4],[y4])的 [CS]",
+ graph: "图形 [graph] 的 [CS]",
+ round: "[rd] 为 [a] 的圆的 [CS]",
+ pi: "派",
+ radius: "半径",
+ diameter: "直径",
+ area: "面积",
+ circumference: "周长",
+ },
+ });
+ class graph {
+ getInfo() {
+ return {
+ id: "nonameawagraph",
+ name: Scratch.translate({ id: "name", default: "Graphics 2D" }),
+ color1: "#ff976c",
+ color2: "#cc7956",
+ color3: "#e58861",
+ blocks: [
+ {
+ opcode: "line_section",
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate({
+ id: "line_section",
+ default: "length from ([x1],[y1]) to ([x2],[y2])",
+ }),
+ arguments: {
+ x1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "-100",
+ },
+ y1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ x2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ y2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ },
+ },
+ {
+ opcode: "ray_direction",
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate({
+ id: "ray_direction",
+ default: "direction of ([x1],[y1]) to ([x2],[y2])",
+ }),
+ arguments: {
+ x1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ y1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ x2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "100",
+ },
+ y2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ },
+ },
+ {
+ opcode: "vertical",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] ⊥ [b]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.ANGLE,
+ defaultValue: "0",
+ },
+ b: {
+ type: Scratch.ArgumentType.ANGLE,
+ defaultValue: "90",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "triangle",
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate({
+ id: "triangle",
+ default: "triangle ([x1],[y1]) ([x2],[y2]) ([x3],[y3]) 's [CS]",
+ }),
+ arguments: {
+ x1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ y1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ x2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ y2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ x3: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ y3: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ CS: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "cs",
+ },
+ },
+ },
+ {
+ opcode: "triangle_s",
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate({
+ id: "triangle_s",
+ default: "triangle [s1] [s2] [s3] 's area",
+ }),
+ arguments: {
+ s1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "3",
+ },
+ s2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "4",
+ },
+ s3: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "5",
+ },
+ },
+ },
+ {
+ opcode: "quadrilateral",
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate({
+ id: "quadrilateral",
+ default:
+ "quadrangle ([x1],[y1]) ([x2],[y2]) ([x3],[y3]) ([x4],[y4]) 's [CS]",
+ }),
+ arguments: {
+ x1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ y1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ x2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ y2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ x3: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ y3: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ x4: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ y4: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ CS: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "cs",
+ },
+ },
+ },
+ {
+ opcode: "graph",
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate({
+ id: "graph",
+ default: "graph [graph] 's [CS]",
+ }),
+ arguments: {
+ graph: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "[[0,0], [0,2], [2,4], [4,2], [4,0]]",
+ },
+ CS: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "cs",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "round",
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate({
+ id: "round",
+ default: "circle of [rd][a]'s [CS]",
+ }),
+ arguments: {
+ rd: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "rd",
+ },
+ a: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ CS: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "cs",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "pi",
+ disableMonitor: true,
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate({ id: "pi", default: "pi" }),
+ },
+ ],
+ menus: {
+ rd: {
+ acceptReporters: true,
+ items: [
+ {
+ text: Scratch.translate({ id: "radius", default: "radius" }),
+ value: "r",
+ },
+ {
+ text: Scratch.translate({
+ id: "diameter",
+ default: "diameter",
+ }),
+ value: "d",
+ },
+ ],
+ },
+ cs: {
+ acceptReporters: true,
+ items: [
+ {
+ text: Scratch.translate({ id: "area", default: "area" }),
+ value: "s",
+ },
+ {
+ text: Scratch.translate({
+ id: "circumference",
+ default: "circumference",
+ }),
+ value: "c",
+ },
+ ],
+ },
+ },
+ };
+ }
+ line_section(args) {
+ return Math.sqrt(
+ Math.pow(args.x1 - args.x2, 2) + Math.pow(args.y1 - args.y2, 2)
+ );
+ }
+ ray_direction(args) {
+ // Added by NexusKitten
+ // 由 NexusKitten 添加
+ const dx =
+ Scratch.Cast.toNumber(args.x2) - Scratch.Cast.toNumber(args.x1);
+ const dy =
+ Scratch.Cast.toNumber(args.y2) - Scratch.Cast.toNumber(args.y1);
+ if (dx === 0 && dy === 0) {
+ return 0;
+ } else if (dy < 0) {
+ return (180 / Math.PI) * Math.atan(dx / dy) + 180;
+ } else {
+ return (180 / Math.PI) * Math.atan(dx / dy);
+ }
+ }
+ vertical(args) {
+ if (isNaN(args.a) || isNaN(args.b)) {
+ return false;
+ } else {
+ return (args.a - (args.b - 90)) % 180 == 0;
+ }
+ }
+ triangle(args) {
+ if (args.CS == "s") {
+ let points = [
+ [args.x1, args.y1],
+ [args.x2, args.y2],
+ [args.x3, args.y3],
+ ];
+ let area = 0;
+ let n = points.length;
+ for (let i = 0; i < n; i++) {
+ let x1 = points[i][0];
+ let y1 = points[i][1];
+ let x2 = points[(i + 1) % n][0];
+ let y2 = points[(i + 1) % n][1];
+ area += x1 * y2;
+ area -= x2 * y1;
+ }
+ area = Math.abs(area) / 2;
+ return area;
+ }
+ if (args.CS == "c") {
+ let i = 0;
+ i += Math.sqrt(
+ Math.pow(args.x1 - args.x2, 2) + Math.pow(args.y1 - args.y2, 2)
+ );
+ i += Math.sqrt(
+ Math.pow(args.x2 - args.x3, 2) + Math.pow(args.y2 - args.y3, 2)
+ );
+ i += Math.sqrt(
+ Math.pow(args.x3 - args.x1, 2) + Math.pow(args.y3 - args.y1, 2)
+ );
+ return i;
+ }
+ return 0;
+ }
+ triangle_s(args) {
+ const s = (args.s1 + args.s2 + args.s3) / 2;
+ const area = Math.sqrt(s * (s - args.s1) * (s - args.s2) * (s - args.s3));
+ return area;
+ }
+ quadrilateral(args) {
+ if (args.CS == "s") {
+ let points = [
+ [args.x1, args.y1],
+ [args.x2, args.y2],
+ [args.x3, args.y3],
+ [args.x4, args.y4],
+ ];
+ let area = 0;
+ let n = points.length;
+ for (let i = 0; i < n; i++) {
+ let x1 = points[i][0];
+ let y1 = points[i][1];
+ let x2 = points[(i + 1) % n][0];
+ let y2 = points[(i + 1) % n][1];
+ area += x1 * y2;
+ area -= x2 * y1;
+ }
+ area = Math.abs(area) / 2;
+ return area;
+ }
+ if (args.CS == "c") {
+ let i = 0;
+ i += Math.sqrt(
+ Math.pow(args.x1 - args.x2, 2) + Math.pow(args.y1 - args.y2, 2)
+ );
+ i += Math.sqrt(
+ Math.pow(args.x2 - args.x3, 2) + Math.pow(args.y2 - args.y3, 2)
+ );
+ i += Math.sqrt(
+ Math.pow(args.x3 - args.x4, 2) + Math.pow(args.y3 - args.y4, 2)
+ );
+ i += Math.sqrt(
+ Math.pow(args.x4 - args.x1, 2) + Math.pow(args.y4 - args.y1, 2)
+ );
+ return i;
+ }
+ return 0;
+ }
+ graph(args) {
+ let points;
+ try {
+ points = JSON.parse(args.graph);
+ } catch (error) {
+ return 0;
+ }
+ if (!Array.isArray(points)) {
+ return 0;
+ }
+ let n = points.length;
+ if (args.CS == "s") {
+ let area = 0;
+ for (let i = 0; i < n; i++) {
+ let x1 = points[i][0];
+ let y1 = points[i][1];
+ let x2 = points[(i + 1) % n][0];
+ let y2 = points[(i + 1) % n][1];
+ area += x1 * y2;
+ area -= x2 * y1;
+ }
+ area = Math.abs(area) / 2;
+ return area;
+ }
+ if (args.CS == "c") {
+ let x1, x2, y1, y2;
+ let j = 0;
+ j = 0;
+ var i_end = n - 1;
+ var i_inc = 1;
+ if (0 > i_end) {
+ i_inc = -i_inc;
+ }
+ for (let i = 0; i_inc >= 0 ? i <= i_end : i >= i_end; i += i_inc) {
+ x1 = points[i + 1 - 1][0];
+ x2 = i == n - 1 ? points[0][0] : points[i + 2 - 1][0];
+ y1 = points[i + 1 - 1][1];
+ y2 = i == n - 1 ? points[0][1] : points[i + 2 - 1][1];
+ j =
+ (typeof j == "number" ? j : 0) +
+ Math.sqrt(
+ Math.pow(Math.abs(x1 - x2), 2) + Math.pow(Math.abs(y1 - y2), 2)
+ );
+ }
+ return j;
+ }
+ return 0;
+ }
+ round(args) {
+ if (args.CS == "c") {
+ return 2 * Math.PI * (args.rd == "r" ? args.a : args.a / 2);
+ }
+ if (args.CS == "s") {
+ return Math.PI * (args.rd == "r" ? args.a : args.a / 2) ** 2;
+ }
+ }
+ pi() {
+ return Math.PI;
+ }
+ }
+ Scratch.extensions.register(new graph());
+})(Scratch);
diff --git a/extensions/NOname-awa/math-and-string.js b/extensions/NOname-awa/math-and-string.js
index 7ea26b9b7a..2793a40fc8 100644
--- a/extensions/NOname-awa/math-and-string.js
+++ b/extensions/NOname-awa/math-and-string.js
@@ -1,1123 +1,1202 @@
(function (Scratch) {
- 'use strict';
- class MathAndString {
- getInfo() {
- return {
- color1: '#5ac900',
- color2: '#48a100',
- color3: '#48a100',
- id: 'nonameawamathandstring',
- name: 'Math And String',
- blocks: [
- {
- opcode: 'exponent',
- blockType: Scratch.BlockType.REPORTER,
- text: '[A] ^ [B]',
- arguments: {
- A: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '2'
- },
- B: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- }
- }
- },
- {
- opcode: 'negative',
- blockType: Scratch.BlockType.REPORTER,
- text: '- [A]',
- arguments: {
- A: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '2'
- },
- }
- },
- {
- opcode: 'n_th_Root',
- blockType: Scratch.BlockType.REPORTER,
- text: '[A] √ [B]',
- arguments: {
- A: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '3'
- },
- B: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '8'
- }
- }
- }, '---',
- {
- opcode: 'astrict',
- blockType: Scratch.BlockType.REPORTER,
- text: 'constrain [A] low [B] high [C]',
- arguments: {
- A: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '50'
- },
- B: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- C: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '100'
- }
- }
- },
- {
- opcode: 'round',
- blockType: Scratch.BlockType.REPORTER,
- text: 'Round [A] to [B] decimal places',
- arguments: {
- A: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '3.14'
- },
- B: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- C: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '100'
- }
- }
- }, "---",
- {
- opcode: 'boolean',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- },
- }
- },
- {
- opcode: 'booleanToInt',
- blockType: Scratch.BlockType.REPORTER,
- text: '[a]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.BOOLEAN,
- },
- }
- },
- '---',
- {
- opcode: 'equal',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ⩵ [b]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'A'
- },
- b: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'a'
- },
- }
- },
- {
- opcode: 'equalNegative',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] =- [b]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '5'
- },
- b: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '-5'
- },
- }
- },
- {
- opcode: 'equalPlusMinus',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] =± [b]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '5'
- },
- b: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '-5'
- },
- }
- },
- {
- opcode: 'notEqual',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ≠ [b]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- },
- b: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- },
- }
- },
- {
- opcode: 'almostEqual2n',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ≈ [b]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '5.5'
- },
- b: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '6'
- },
- }
- },
- {
- opcode: 'almostEqual3n',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ≈ [b] ± [c]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '5'
- },
- b: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '6'
- },
- c: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- }
- },
- {
- opcode: 'xor',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ^ [b]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.BOOLEAN,
- },
- b: {
- type: Scratch.ArgumentType.BOOLEAN,
- },
- }
- },
- '---',
- {
- opcode: 'equalOrGreater',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ≥ [b]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- },
- b: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '50'
- },
- }
- },
- {
- opcode: 'equalOrLess',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ≤ [b]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- },
- b: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '50'
- },
- }
- },
- {
- opcode: 'between',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] < [b] < [c]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- },
- b: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- },
- c: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- },
- }
- },
- {
- opcode: 'betweenEqual',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ≤ [b] ≤ [c]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- },
- b: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- },
- c: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- },
- }
- }, '---',
- {
- opcode: 'vertical',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ⊥ [b]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.ANGLE,
- defaultValue: '0'
- },
- b: {
- type: Scratch.ArgumentType.ANGLE,
- defaultValue: '90'
- },
- }
- }, '---', {
- opcode: 'text',
- blockType: Scratch.BlockType.REPORTER,
- text: '[a]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- },
- }
- }, '---', {
- opcode: 'repeat',
- blockType: Scratch.BlockType.REPORTER,
- text: 'repeat [text] [n] times',
- arguments: {
- text: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Text '
- },
- n: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '3'
- },
- }
- }, {
- opcode: 'trim',
- blockType: Scratch.BlockType.REPORTER,
- text: 'trim spaces from both sides of [text]',
- arguments: {
- text: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ' Text '
- },
- }
- }, {
- opcode: 'intercept',
- blockType: Scratch.BlockType.REPORTER,
- text: 'in text [text] get substring from [h] to [e]',
- arguments: {
- text: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'this is text test'
- },
- h: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '6'
- },
- e: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '7'
- },
- }
- }, {
- opcode: "replace",
- blockType: Scratch.BlockType.REPORTER,
- text: "replace [o] of [text] with [n]",
- arguments: {
- o: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: "world"
- },
- text: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: "Hello world!"
- },
- n: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: "Scratch"
- }
- }
- }, {
- opcode: 'Split',
- blockType: Scratch.BlockType.REPORTER,
- text: 'divide [text] according to [symbol] to take the [n] th item',
- arguments: {
- text: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'a_b_c'
- },
- symbol: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '_'
- },
- n: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '2'
- },
- }
- }, '---', {
- opcode: 'toUpperCase',
- blockType: Scratch.BlockType.REPORTER,
- text: 'UPPER CASE [text]',
- arguments: {
- text: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Text'
- },
- }
- }, {
- opcode: 'toLowerCase',
- blockType: Scratch.BlockType.REPORTER,
- text: 'lower case [text]',
- arguments: {
- text: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Text'
- },
- }
- }, {
- opcode: 'textToTitleCase',
- blockType: Scratch.BlockType.REPORTER,
- text: 'Title Case [text]',
- arguments: {
- text: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'the text test'
- },
- }
- }, '---', {
- opcode: 'indexOf',
- blockType: Scratch.BlockType.REPORTER,
- text: '[a] the first appearance in [text]',
- arguments: {
- text: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'The text test'
- },
- a: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'te'
- },
- }
- }, {
- opcode: 'lastIndexOf',
- blockType: Scratch.BlockType.REPORTER,
- text: '[a] the position of last occurrence in [text]',
- arguments: {
- text: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'The text test'
- },
- a: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'te'
- },
- }
- }, {
- opcode: 'countKeyword',
- blockType: Scratch.BlockType.REPORTER,
- text: '[a] the number of occurrences in [text]',
- arguments: {
- text: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'The text test'
- },
- a: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'te'
- },
- }
- }, '---', {
- opcode: 'startsWith',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'does [a] begin with a text?',
- arguments: {
- a: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Abc123'
- },
- }
- },
- {
- opcode: 'matchTextWithPattern',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'match [text] as [pattern] - [flags]',
- arguments: {
- text: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'abc'
- },
- pattern: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '[ -~]'
- },
- flags: {
- type: Scratch.ArgumentType.STRING,
- menu: 'flags'
- },
- }
- }, '---', {
- opcode: 'ascii',
- blockType: Scratch.BlockType.REPORTER,
- text: '[a]\'s ascii',
- arguments: {
- a: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'a'
- },
- }
- }, {
- opcode: 'ascii_',
- blockType: Scratch.BlockType.REPORTER,
- text: 'ascii is [a] \'s text',
- arguments: {
- a: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '97'
- },
- }
- }, '---', {
- opcode: 'line_segment',
- blockType: Scratch.BlockType.REPORTER,
- text: 'line segment ([x1],[y1]) to ([x2],[y2])',
- arguments: {
- x1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '-100'
- },
- y1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- x2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- y2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- }
- },
- '---',
- {
- opcode: 'triangle',
- blockType: Scratch.BlockType.REPORTER,
- text: 'triangle ([x1],[y1]) ([x2],[y2]) ([x3],[y3]) \'s [CS]',
- arguments: {
- x1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- y1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- x2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- y2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- x3: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- y3: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- CS: {
- type: Scratch.ArgumentType.STRING,
- menu: 'cs'
- },
- },
- },
- {
- opcode: 'triangle_s',
- blockType: Scratch.BlockType.REPORTER,
- text: 'triangle [s1] [s2] [s3] \'s square',
- arguments: {
- s1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '3'
- },
- s2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '4'
- },
- s3: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '5'
- },
- },
- },
- {
- opcode: 'rectangle',
- blockType: Scratch.BlockType.REPORTER,
- text: 'rectangle ([x1],[y1]) ([x2],[y2]) ([x3],[y3]) ([x4],[y4]) \'s [CS]',
- arguments: {
- x1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- y1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- x2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- y2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- x3: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- y3: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- x4: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- y4: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- CS: {
- type: Scratch.ArgumentType.STRING,
- menu: 'cs'
- },
- },
- },
- {
- opcode: 'graph',
- blockType: Scratch.BlockType.REPORTER,
- text: 'graph [graph] \'s [CS]',
- arguments: {
- graph: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '[[0,0], [0,2], [2,4], [4,2], [4,0]]'
- },
- CS: {
- type: Scratch.ArgumentType.STRING,
- menu: 'cs'
- },
- },
- },
- '---',
- {
- opcode: 'circle',
- blockType: Scratch.BlockType.REPORTER,
- text: 'circle: [rd][a] \'s [CS]',
- arguments: {
- rd: {
- type: Scratch.ArgumentType.STRING,
- menu: 'rd'
- },
- a: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- CS: {
- type: Scratch.ArgumentType.STRING,
- menu: 'cs'
- },
- },
- },
- '---',
- {
- opcode: 'words',
- blockType: Scratch.BlockType.REPORTER,
- disableMonitor: true,
- text: 'sort unique words in [text] as [language]',
- arguments: {
- text: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'movie dog restaurant book school'
- },
- language: {
- type: Scratch.ArgumentType.STRING,
- menu: 'language'
- }
- }
- }, '---',
- {
- opcode: 'true',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'true',
- },
- {
- opcode: 'false',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'false',
- },
- {
- opcode: 'new_line',
- disableMonitor: true,
- blockType: Scratch.BlockType.REPORTER,
- text: '\\n',
- },
- {
- opcode: 'pi',
- disableMonitor: true,
- blockType: Scratch.BlockType.REPORTER,
- text: 'π',
- },
- {
- opcode: 'phi',
- disableMonitor: true,
- blockType: Scratch.BlockType.REPORTER,
- text: 'φ',
- },
- {
- opcode: 'e',
- disableMonitor: true,
- blockType: Scratch.BlockType.REPORTER,
- text: 'e',
- },
- {
- opcode: 'infinity',
- disableMonitor: true,
- blockType: Scratch.BlockType.REPORTER,
- text: '∞',
- },
- ],
- menus: {
- rd: {
- acceptReporters: true,
- items: [
- {
- text: 'radius (r)',
- value: 'r'
- },
- {
- text: 'diameter (d)',
- value: 'd'
- }
- ]
- },
- cs: {
- acceptReporters: true,
- items: [
- {
- text: 'square (s)',
- value: 's'
- },
- {
- text: 'circumference (c)',
- value: 'c'
- }
- ]
- },
- language: {
- acceptReporters: true,
- items: [
- {
- text: 'English (en)',
- value: 'en'
- },
- {
- text: 'Chinese (zh)',
- value: 'zh'
- }
- ]
- },
- flags: {
- acceptReporters: false,
- items: [
- {
- text: 'global (g)',
- value: 'g'
- },
- {
- text: 'ignoring case (i)',
- value: 'i'
- }
- ]
- }
- }
- };
- }
- exponent({ A, B }) {
- return A ** B;
- }
- negative({ A }) {
- return 0 - A;
- }
- n_th_Root({ A, B }) {
- return Math.pow(B, 1 / A);
- }
- astrict({ A, B, C }) {
- return Math.min(Math.max(A, B), C);
- }
- round(args) {
- return args.A.toFixed(args.B);
- }
+ "use strict";
+ class MathAndString {
+ getInfo() {
+ return {
+ color1: "#5ac900",
+ color2: "#48a100",
+ color3: "#48a100",
+ id: "nonameawamathandstring",
+ name: "Math And String",
+ blocks: [
+ {
+ opcode: "exponent",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[A] ^ [B]",
+ arguments: {
+ A: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "2",
+ },
+ B: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ },
+ },
+ {
+ opcode: "negative",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "- [A]",
+ arguments: {
+ A: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "2",
+ },
+ },
+ },
+ {
+ opcode: "n_th_Root",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[A] √ [B]",
+ arguments: {
+ A: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "3",
+ },
+ B: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "8",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "astrict",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "constrain [A] low [B] high [C]",
+ arguments: {
+ A: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "50",
+ },
+ B: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ C: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "100",
+ },
+ },
+ },
+ {
+ opcode: "round",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "Round [A] to [B] decimal places",
+ arguments: {
+ A: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "3.14",
+ },
+ B: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ C: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "100",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "boolean",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "",
+ },
+ },
+ },
+ {
+ opcode: "booleanToInt",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[a]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.BOOLEAN,
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "equal",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] ⩵ [b]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "A",
+ },
+ b: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "a",
+ },
+ },
+ },
+ {
+ opcode: "equalNegative",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] =- [b]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "5",
+ },
+ b: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "-5",
+ },
+ },
+ },
+ {
+ opcode: "equalPlusMinus",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] =± [b]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "5",
+ },
+ b: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "-5",
+ },
+ },
+ },
+ {
+ opcode: "notEqual",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] ≠ [b]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "",
+ },
+ b: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "",
+ },
+ },
+ },
+ {
+ opcode: "almostEqual2n",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] ≈ [b]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "5.5",
+ },
+ b: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "6",
+ },
+ },
+ },
+ {
+ opcode: "almostEqual3n",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] ≈ [b] ± [c]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "5",
+ },
+ b: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "6",
+ },
+ c: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "xor",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] ^ [b]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.BOOLEAN,
+ },
+ b: {
+ type: Scratch.ArgumentType.BOOLEAN,
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "equalOrGreater",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] ≥ [b]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "",
+ },
+ b: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "50",
+ },
+ },
+ },
+ {
+ opcode: "equalOrLess",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] ≤ [b]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "",
+ },
+ b: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "50",
+ },
+ },
+ },
+ {
+ opcode: "between",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] < [b] < [c]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "",
+ },
+ b: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "",
+ },
+ c: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "",
+ },
+ },
+ },
+ {
+ opcode: "betweenEqual",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] ≤ [b] ≤ [c]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "",
+ },
+ b: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "",
+ },
+ c: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "vertical",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] ⊥ [b]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.ANGLE,
+ defaultValue: "0",
+ },
+ b: {
+ type: Scratch.ArgumentType.ANGLE,
+ defaultValue: "90",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "text",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[a]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "repeat",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "repeat [text] [n] times",
+ arguments: {
+ text: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Text ",
+ },
+ n: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "3",
+ },
+ },
+ },
+ {
+ opcode: "trim",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "trim spaces from both sides of [text]",
+ arguments: {
+ text: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: " Text ",
+ },
+ },
+ },
+ {
+ opcode: "intercept",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "in text [text] get substring from [h] to [e]",
+ arguments: {
+ text: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "this is text test",
+ },
+ h: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "6",
+ },
+ e: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "7",
+ },
+ },
+ },
+ {
+ opcode: "replace",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "replace [o] of [text] with [n]",
+ arguments: {
+ o: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "world",
+ },
+ text: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Hello world!",
+ },
+ n: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Scratch",
+ },
+ },
+ },
+ {
+ opcode: "Split",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "divide [text] according to [symbol] to take the [n] th item",
+ arguments: {
+ text: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "a_b_c",
+ },
+ symbol: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "_",
+ },
+ n: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "2",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "toUpperCase",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "UPPER CASE [text]",
+ arguments: {
+ text: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Text",
+ },
+ },
+ },
+ {
+ opcode: "toLowerCase",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "lower case [text]",
+ arguments: {
+ text: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Text",
+ },
+ },
+ },
+ {
+ opcode: "textToTitleCase",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "Title Case [text]",
+ arguments: {
+ text: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "the text test",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "indexOf",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[a] the first appearance in [text]",
+ arguments: {
+ text: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "The text test",
+ },
+ a: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "te",
+ },
+ },
+ },
+ {
+ opcode: "lastIndexOf",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[a] the position of last occurrence in [text]",
+ arguments: {
+ text: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "The text test",
+ },
+ a: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "te",
+ },
+ },
+ },
+ {
+ opcode: "countKeyword",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[a] the number of occurrences in [text]",
+ arguments: {
+ text: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "The text test",
+ },
+ a: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "te",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "startsWith",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "does [a] begin with a text?",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Abc123",
+ },
+ },
+ },
+ {
+ opcode: "matchTextWithPattern",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "match [text] as [pattern] - [flags]",
+ arguments: {
+ text: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "abc",
+ },
+ pattern: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "[ -~]",
+ },
+ flags: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "flags",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "ascii",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[a]'s ascii",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "a",
+ },
+ },
+ },
+ {
+ opcode: "ascii_",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "ascii is [a] 's text",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "97",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "line_segment",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "line segment ([x1],[y1]) to ([x2],[y2])",
+ arguments: {
+ x1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "-100",
+ },
+ y1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ x2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ y2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "triangle",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "triangle ([x1],[y1]) ([x2],[y2]) ([x3],[y3]) 's [CS]",
+ arguments: {
+ x1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ y1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ x2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ y2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ x3: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ y3: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ CS: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "cs",
+ },
+ },
+ },
+ {
+ opcode: "triangle_s",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "triangle [s1] [s2] [s3] 's square",
+ arguments: {
+ s1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "3",
+ },
+ s2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "4",
+ },
+ s3: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "5",
+ },
+ },
+ },
+ {
+ opcode: "rectangle",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "rectangle ([x1],[y1]) ([x2],[y2]) ([x3],[y3]) ([x4],[y4]) 's [CS]",
+ arguments: {
+ x1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ y1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ x2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ y2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ x3: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ y3: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ x4: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ y4: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ CS: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "cs",
+ },
+ },
+ },
+ {
+ opcode: "graph",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "graph [graph] 's [CS]",
+ arguments: {
+ graph: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "[[0,0], [0,2], [2,4], [4,2], [4,0]]",
+ },
+ CS: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "cs",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "circle",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "circle: [rd][a] 's [CS]",
+ arguments: {
+ rd: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "rd",
+ },
+ a: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ CS: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "cs",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "words",
+ blockType: Scratch.BlockType.REPORTER,
+ disableMonitor: true,
+ text: "sort unique words in [text] as [language]",
+ arguments: {
+ text: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "movie dog restaurant book school",
+ },
+ language: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "language",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "true",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "true",
+ },
+ {
+ opcode: "false",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "false",
+ },
+ {
+ opcode: "new_line",
+ disableMonitor: true,
+ blockType: Scratch.BlockType.REPORTER,
+ text: "\\n",
+ },
+ {
+ opcode: "pi",
+ disableMonitor: true,
+ blockType: Scratch.BlockType.REPORTER,
+ text: "π",
+ },
+ {
+ opcode: "phi",
+ disableMonitor: true,
+ blockType: Scratch.BlockType.REPORTER,
+ text: "φ",
+ },
+ {
+ opcode: "e",
+ disableMonitor: true,
+ blockType: Scratch.BlockType.REPORTER,
+ text: "e",
+ },
+ {
+ opcode: "infinity",
+ disableMonitor: true,
+ blockType: Scratch.BlockType.REPORTER,
+ text: "∞",
+ },
+ ],
+ menus: {
+ rd: {
+ acceptReporters: true,
+ items: [
+ {
+ text: "radius (r)",
+ value: "r",
+ },
+ {
+ text: "diameter (d)",
+ value: "d",
+ },
+ ],
+ },
+ cs: {
+ acceptReporters: true,
+ items: [
+ {
+ text: "square (s)",
+ value: "s",
+ },
+ {
+ text: "circumference (c)",
+ value: "c",
+ },
+ ],
+ },
+ language: {
+ acceptReporters: true,
+ items: [
+ {
+ text: "English (en)",
+ value: "en",
+ },
+ {
+ text: "Chinese (zh)",
+ value: "zh",
+ },
+ ],
+ },
+ flags: {
+ acceptReporters: false,
+ items: [
+ {
+ text: "global (g)",
+ value: "g",
+ },
+ {
+ text: "ignoring case (i)",
+ value: "i",
+ },
+ ],
+ },
+ },
+ };
+ }
+ exponent({ A, B }) {
+ return A ** B;
+ }
+ negative({ A }) {
+ return 0 - A;
+ }
+ n_th_Root({ A, B }) {
+ return Math.pow(B, 1 / A);
+ }
+ astrict({ A, B, C }) {
+ return Math.min(Math.max(A, B), C);
+ }
+ round(args) {
+ return args.A.toFixed(args.B);
+ }
- true() {
- return true;
- }
- false() {
- return false;
- }
- boolean(args) {
- return Scratch.Cast.toBoolean(args.a);
- }
- booleanToInt(args) {
- if (Scratch.Cast.toBoolean(args.a)) {
- return 1;
- }
- return 0;
- }
- equal(args) {
- return (args.a == args.b);
- }
- equalNegative(args) {
- if (isNaN(args.a) || isNaN(args.b)) {
- return false;
- } else {
- return (args.a == (0 - args.b));
- }
- }
- equalPlusMinus(args) {
- if (isNaN(args.a) || isNaN(args.b)) {
- return false;
- } else {
- return (args.a == (0 - args.b)) || (args.a == args.b);
- }
- }
- almostEqual2n(args) {
- return (Math.round(args.a) == Math.round(args.b));
- }
- almostEqual3n(args) {
- return (Math.abs(args.a - args.b) <= args.c);
- }
- between(args) {
- return (args.a < args.b) && (args.b < args.c);
- }
- betweenEqual(args) {
- return (args.a <= args.b) && (args.b <= args.c);
- }
- notEqual(args) {
- return (args.a != args.b);
- }
- xor(args) {
- return Scratch.Cast.toBoolean(args.a) !== Scratch.Cast.toBoolean(args.b);
- }
- equalOrGreater(args) {
- return (args.a >= args.b);
- }
- equalOrLess(args) {
- return (args.a <= args.b);
- }
- vertical(args) {
- if (isNaN(args.a) || isNaN(args.b)) {
- return false;
- } else {
- return ((args.a - (args.b - 90)) % 180) == 0;
- }
- }
- segment_one(args) {
- return Math.round(Math.sqrt(Math.pow(args.x1 - args.x2, 2) + Math.pow(args.y1 - args.y2, 2))) == args.n;
- }
- segment_two(args) {
- return Math.round(Math.sqrt(Math.pow(args.x11 - args.x12, 2) + Math.pow(args.y11 - args.y12, 2)))
- == Math.round(Math.sqrt(Math.pow(args.x21 - args.x22, 2) + Math.pow(args.y21 - args.y22, 2)));
- }
+ true() {
+ return true;
+ }
+ false() {
+ return false;
+ }
+ boolean(args) {
+ return Scratch.Cast.toBoolean(args.a);
+ }
+ booleanToInt(args) {
+ if (Scratch.Cast.toBoolean(args.a)) {
+ return 1;
+ }
+ return 0;
+ }
+ equal(args) {
+ return args.a == args.b;
+ }
+ equalNegative(args) {
+ if (isNaN(args.a) || isNaN(args.b)) {
+ return false;
+ } else {
+ return args.a == 0 - args.b;
+ }
+ }
+ equalPlusMinus(args) {
+ if (isNaN(args.a) || isNaN(args.b)) {
+ return false;
+ } else {
+ return args.a == 0 - args.b || args.a == args.b;
+ }
+ }
+ almostEqual2n(args) {
+ return Math.round(args.a) == Math.round(args.b);
+ }
+ almostEqual3n(args) {
+ return Math.abs(args.a - args.b) <= args.c;
+ }
+ between(args) {
+ return args.a < args.b && args.b < args.c;
+ }
+ betweenEqual(args) {
+ return args.a <= args.b && args.b <= args.c;
+ }
+ notEqual(args) {
+ return args.a != args.b;
+ }
+ xor(args) {
+ return Scratch.Cast.toBoolean(args.a) !== Scratch.Cast.toBoolean(args.b);
+ }
+ equalOrGreater(args) {
+ return args.a >= args.b;
+ }
+ equalOrLess(args) {
+ return args.a <= args.b;
+ }
+ vertical(args) {
+ if (isNaN(args.a) || isNaN(args.b)) {
+ return false;
+ } else {
+ return (args.a - (args.b - 90)) % 180 == 0;
+ }
+ }
+ segment_one(args) {
+ return (
+ Math.round(
+ Math.sqrt(
+ Math.pow(args.x1 - args.x2, 2) + Math.pow(args.y1 - args.y2, 2)
+ )
+ ) == args.n
+ );
+ }
+ segment_two(args) {
+ return (
+ Math.round(
+ Math.sqrt(
+ Math.pow(args.x11 - args.x12, 2) + Math.pow(args.y11 - args.y12, 2)
+ )
+ ) ==
+ Math.round(
+ Math.sqrt(
+ Math.pow(args.x21 - args.x22, 2) + Math.pow(args.y21 - args.y22, 2)
+ )
+ )
+ );
+ }
- ascii(args) {
- return args.a.charCodeAt();
- }
- ascii_(args) {
- return String.fromCharCode(args.a);
- }
- text(args) {
- return args.a;
- }
- repeat(args) {
- if (args.n > 0) {
- let repeat_i;
- let repeat_j = '';
- let repeat_i_inc = 1;
- if (1 > args.n) {
- repeat_i_inc = -repeat_i_inc;
- }
- for (repeat_i = 1; repeat_i_inc >= 0 ? repeat_i <= args.n : repeat_i >= args.n; repeat_i += repeat_i_inc) {
- repeat_j = String(repeat_j) + String(args.text);
- }
- return repeat_j;
- }
- return '';
- }
- intercept(args) {
- return args.text.slice((args.h - 1), args.e);
- }
- toUpperCase(args) {
- return args.text.toUpperCase();
- }
- toLowerCase(args) {
- return args.text.toLowerCase();
- }
- textToTitleCase(args) {
- return textToTitleCase(args.text);
- }
- 'trim'(args) {
- return args.text.trim();
- }
- new_line() {
- return '\n';
- }
- 'Split'(args) {
- const symbol = args.symbol === '.' ? '\\.' : args.symbol;
- if (args.text && typeof args.text.split === 'function' && args.text.split(symbol)[(args.n - 1)] != undefined) {
- return args.text.split(symbol)[(args.n - 1)];
- }
- return '';
- }
- indexOf(args) {
- return args.text.indexOf(args.a) + 1;
- }
- lastIndexOf(args) {
- return args.text.lastIndexOf(args.a) + 1;
- }
- replace(args) {
- return replaceText(args.text, args.o, args.n);
- }
- startsWith(args) {
- if (typeof args.a === 'string' && args.a.startsWith(args.a)) {
- return true;
- } else {
- return false;
- }
+ ascii(args) {
+ return args.a.charCodeAt();
+ }
+ ascii_(args) {
+ return String.fromCharCode(args.a);
+ }
+ text(args) {
+ return args.a;
+ }
+ repeat(args) {
+ if (args.n > 0) {
+ let repeat_i;
+ let repeat_j = "";
+ let repeat_i_inc = 1;
+ if (1 > args.n) {
+ repeat_i_inc = -repeat_i_inc;
}
- 'countKeyword'(args) {
- return countKeyword(args.text, args.a);
+ for (
+ repeat_i = 1;
+ repeat_i_inc >= 0 ? repeat_i <= args.n : repeat_i >= args.n;
+ repeat_i += repeat_i_inc
+ ) {
+ repeat_j = String(repeat_j) + String(args.text);
}
+ return repeat_j;
+ }
+ return "";
+ }
+ intercept(args) {
+ return args.text.slice(args.h - 1, args.e);
+ }
+ toUpperCase(args) {
+ return args.text.toUpperCase();
+ }
+ toLowerCase(args) {
+ return args.text.toLowerCase();
+ }
+ textToTitleCase(args) {
+ return textToTitleCase(args.text);
+ }
+ trim(args) {
+ return args.text.trim();
+ }
+ new_line() {
+ return "\n";
+ }
+ Split(args) {
+ const symbol = args.symbol === "." ? "\\." : args.symbol;
+ if (
+ args.text &&
+ typeof args.text.split === "function" &&
+ args.text.split(symbol)[args.n - 1] != undefined
+ ) {
+ return args.text.split(symbol)[args.n - 1];
+ }
+ return "";
+ }
+ indexOf(args) {
+ return args.text.indexOf(args.a) + 1;
+ }
+ lastIndexOf(args) {
+ return args.text.lastIndexOf(args.a) + 1;
+ }
+ replace(args) {
+ return replaceText(args.text, args.o, args.n);
+ }
+ startsWith(args) {
+ if (typeof args.a === "string" && args.a.startsWith(args.a)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ countKeyword(args) {
+ return countKeyword(args.text, args.a);
+ }
- line_segment(args) {
- return Math.sqrt(Math.pow(args.x1 - args.x2, 2) + Math.pow(args.y1 - args.y2, 2));
- }
- triangle(args) {
- if (args.CS == 's') {
- let points = [[args.x1, args.y1], [args.x2, args.y2], [args.x3, args.y3]];
- let area = 0;
- let n = points.length;
- for (let i = 0; i < n; i++) {
- let x1 = points[i][0];
- let y1 = points[i][1];
- let x2 = points[(i + 1) % n][0];
- let y2 = points[(i + 1) % n][1];
- area += x1 * y2;
- area -= x2 * y1;
- }
- area = Math.abs(area) / 2;
- return (area);
- }
- if (args.CS == 'c') {
- let i = 0;
- i += Math.sqrt(Math.pow(args.x1 - args.x2, 2) + Math.pow(args.y1 - args.y2, 2));
- i += Math.sqrt(Math.pow(args.x2 - args.x3, 2) + Math.pow(args.y2 - args.y3, 2));
- i += Math.sqrt(Math.pow(args.x3 - args.x1, 2) + Math.pow(args.y3 - args.y1, 2));
- return i;
- }
- return 0;
- }
- triangle_s(args) {
- const s = (args.s1 + args.s2 + args.s3) / 2;
- const area = Math.sqrt(s * (s - args.s1) * (s - args.s2) * (s - args.s3));
- return area;
+ line_segment(args) {
+ return Math.sqrt(
+ Math.pow(args.x1 - args.x2, 2) + Math.pow(args.y1 - args.y2, 2)
+ );
+ }
+ triangle(args) {
+ if (args.CS == "s") {
+ let points = [
+ [args.x1, args.y1],
+ [args.x2, args.y2],
+ [args.x3, args.y3],
+ ];
+ let area = 0;
+ let n = points.length;
+ for (let i = 0; i < n; i++) {
+ let x1 = points[i][0];
+ let y1 = points[i][1];
+ let x2 = points[(i + 1) % n][0];
+ let y2 = points[(i + 1) % n][1];
+ area += x1 * y2;
+ area -= x2 * y1;
}
- rectangle(args) {
- if (args.CS == 's') {
- let points = [[args.x1, args.y1], [args.x2, args.y2], [args.x3, args.y3], [args.x4, args.y4]];
- let area = 0;
- let n = points.length;
- for (let i = 0; i < n; i++) {
- let x1 = points[i][0];
- let y1 = points[i][1];
- let x2 = points[(i + 1) % n][0];
- let y2 = points[(i + 1) % n][1];
- area += x1 * y2;
- area -= x2 * y1;
- }
- area = Math.abs(area) / 2;
- return (area);
- }
- if (args.CS == 'c') {
- let i = 0;
- i += Math.sqrt(Math.pow(args.x1 - args.x2, 2) + Math.pow(args.y1 - args.y2, 2));
- i += Math.sqrt(Math.pow(args.x2 - args.x3, 2) + Math.pow(args.y2 - args.y3, 2));
- i += Math.sqrt(Math.pow(args.x3 - args.x4, 2) + Math.pow(args.y3 - args.y4, 2));
- i += Math.sqrt(Math.pow(args.x4 - args.x1, 2) + Math.pow(args.y4 - args.y1, 2));
- return i;
- }
- return 0;
+ area = Math.abs(area) / 2;
+ return area;
+ }
+ if (args.CS == "c") {
+ let i = 0;
+ i += Math.sqrt(
+ Math.pow(args.x1 - args.x2, 2) + Math.pow(args.y1 - args.y2, 2)
+ );
+ i += Math.sqrt(
+ Math.pow(args.x2 - args.x3, 2) + Math.pow(args.y2 - args.y3, 2)
+ );
+ i += Math.sqrt(
+ Math.pow(args.x3 - args.x1, 2) + Math.pow(args.y3 - args.y1, 2)
+ );
+ return i;
+ }
+ return 0;
+ }
+ triangle_s(args) {
+ const s = (args.s1 + args.s2 + args.s3) / 2;
+ const area = Math.sqrt(s * (s - args.s1) * (s - args.s2) * (s - args.s3));
+ return area;
+ }
+ rectangle(args) {
+ if (args.CS == "s") {
+ let points = [
+ [args.x1, args.y1],
+ [args.x2, args.y2],
+ [args.x3, args.y3],
+ [args.x4, args.y4],
+ ];
+ let area = 0;
+ let n = points.length;
+ for (let i = 0; i < n; i++) {
+ let x1 = points[i][0];
+ let y1 = points[i][1];
+ let x2 = points[(i + 1) % n][0];
+ let y2 = points[(i + 1) % n][1];
+ area += x1 * y2;
+ area -= x2 * y1;
}
- graph(args) {
- let points = JSON.parse(args.graph);
- let n = points.length;
- if (args.CS == 's') {
- let area = 0;
- for (let i = 0; i < n; i++) {
- let x1 = points[i][0];
- let y1 = points[i][1];
- let x2 = points[(i + 1) % n][0];
- let y2 = points[(i + 1) % n][1];
- area += x1 * y2;
- area -= x2 * y1;
- }
- area = Math.abs(area) / 2;
- return (area);
- }
- if (args.CS == 'c') {
- let x1, x2, y1, y2;
- let j = 0;
- j = 0;
- var i_end = n - 1;
- var i_inc = 1;
- if (0 > i_end) {
- i_inc = -i_inc;
- }
- for (let i = 0; i_inc >= 0 ? i <= i_end : i >= i_end; i += i_inc) {
- x1 = points[((i + 1) - 1)][0];
- x2 = i == n - 1 ? points[0][0] : points[((i + 2) - 1)][0];
- y1 = points[((i + 1) - 1)][1];
- y2 = i == n - 1 ? points[0][1] : points[((i + 2) - 1)][1];
- j = (typeof j == 'number' ? j : 0) + Math.sqrt(Math.pow(Math.abs(x1 - x2), 2) + Math.pow(Math.abs(y1 - y2), 2));
- }
- return j;
- }
- return 0;
+ area = Math.abs(area) / 2;
+ return area;
+ }
+ if (args.CS == "c") {
+ let i = 0;
+ i += Math.sqrt(
+ Math.pow(args.x1 - args.x2, 2) + Math.pow(args.y1 - args.y2, 2)
+ );
+ i += Math.sqrt(
+ Math.pow(args.x2 - args.x3, 2) + Math.pow(args.y2 - args.y3, 2)
+ );
+ i += Math.sqrt(
+ Math.pow(args.x3 - args.x4, 2) + Math.pow(args.y3 - args.y4, 2)
+ );
+ i += Math.sqrt(
+ Math.pow(args.x4 - args.x1, 2) + Math.pow(args.y4 - args.y1, 2)
+ );
+ return i;
+ }
+ return 0;
+ }
+ graph(args) {
+ let points = JSON.parse(args.graph);
+ let n = points.length;
+ if (args.CS == "s") {
+ let area = 0;
+ for (let i = 0; i < n; i++) {
+ let x1 = points[i][0];
+ let y1 = points[i][1];
+ let x2 = points[(i + 1) % n][0];
+ let y2 = points[(i + 1) % n][1];
+ area += x1 * y2;
+ area -= x2 * y1;
}
- circle(args) {
- if (args.CS == 'c') {
- return 2 * Math.PI * (args.rd == 'r' ? args.a : args.a / 2);
- }
- if (args.CS == 's') {
- return Math.PI * ((args.rd == 'r' ? args.a : args.a / 2) ** 2);
- }
+ area = Math.abs(area) / 2;
+ return area;
+ }
+ if (args.CS == "c") {
+ let x1, x2, y1, y2;
+ let j = 0;
+ j = 0;
+ var i_end = n - 1;
+ var i_inc = 1;
+ if (0 > i_end) {
+ i_inc = -i_inc;
}
- pi() {
- return Math.PI;
+ for (let i = 0; i_inc >= 0 ? i <= i_end : i >= i_end; i += i_inc) {
+ x1 = points[i + 1 - 1][0];
+ x2 = i == n - 1 ? points[0][0] : points[i + 2 - 1][0];
+ y1 = points[i + 1 - 1][1];
+ y2 = i == n - 1 ? points[0][1] : points[i + 2 - 1][1];
+ j =
+ (typeof j == "number" ? j : 0) +
+ Math.sqrt(
+ Math.pow(Math.abs(x1 - x2), 2) + Math.pow(Math.abs(y1 - y2), 2)
+ );
}
+ return j;
+ }
+ return 0;
+ }
+ circle(args) {
+ if (args.CS == "c") {
+ return 2 * Math.PI * (args.rd == "r" ? args.a : args.a / 2);
+ }
+ if (args.CS == "s") {
+ return Math.PI * (args.rd == "r" ? args.a : args.a / 2) ** 2;
+ }
+ }
+ pi() {
+ return Math.PI;
+ }
- words(args) {
- const text = Scratch.Cast.toString(args.text);
- const words = parse(text, args.language);
- return words.join(' ');
- }
+ words(args) {
+ const text = Scratch.Cast.toString(args.text);
+ const words = parse(text, args.language);
+ return words.join(" ");
+ }
- phi() {
- return (1 + Math.sqrt(5)) / 2;
- }
- e() {
- return Math.E;
- }
- infinity() {
- return 'Infinity';
- }
+ phi() {
+ return (1 + Math.sqrt(5)) / 2;
+ }
+ e() {
+ return Math.E;
+ }
+ infinity() {
+ return "Infinity";
+ }
- matchTextWithPattern({ text, pattern, flags }) {
- const regex = new RegExp(pattern, flags);
- return regex.test(text);
- }
+ matchTextWithPattern({ text, pattern, flags }) {
+ const regex = new RegExp(pattern, flags);
+ return regex.test(text);
}
+ }
- const textToTitleCase = (str) => {
- return str.replace(/\S+/g,
- function (txt) {
- return txt[0].toUpperCase() + txt.substring(1).toLowerCase();
-});
- };
+ const textToTitleCase = (str) => {
+ return str.replace(/\S+/g, function (txt) {
+ return txt[0].toUpperCase() + txt.substring(1).toLowerCase();
+ });
+ };
- const replaceText = (text, oldStr, newStr) => {
- return text.replace(new RegExp(oldStr, 'g'), newStr);
- };
+ const replaceText = (text, oldStr, newStr) => {
+ return text.replace(new RegExp(oldStr, "g"), newStr);
+ };
- const sortAndUniqueWords_en = (text) => {
- let words = text.toLowerCase().match(/\b\w+\b/g);
- words = Array.from(new Set(words));
- words.sort();
- return words.join(' ');
- };
+ const sortAndUniqueWords_en = (text) => {
+ let words = text.toLowerCase().match(/\b\w+\b/g);
+ words = Array.from(new Set(words));
+ words.sort();
+ return words.join(" ");
+ };
- const sortAndUniqueWords_cn = (text) => {
- let words = text.match(/[^\u4e00-\u9fa5]+|[\u4e00-\u9fa5]+/g);
- words = Array.from(new Set(words));
- words.sort(function (a, b) {
- return a.localeCompare(b, 'zh-Hans-CN', { sensitivity: 'accent' });
- });
- return words.join(' ');
- };
+ const sortAndUniqueWords_cn = (text) => {
+ let words = text.match(/[^\u4e00-\u9fa5]+|[\u4e00-\u9fa5]+/g);
+ words = Array.from(new Set(words));
+ words.sort(function (a, b) {
+ return a.localeCompare(b, "zh-Hans-CN", { sensitivity: "accent" });
+ });
+ return words.join(" ");
+ };
- const countKeyword = (sentence, keyword) => {
- const count = (sentence.match(new RegExp(keyword, 'gi')) || []).length;
- return count;
- };
+ const countKeyword = (sentence, keyword) => {
+ const count = (sentence.match(new RegExp(keyword, "gi")) || []).length;
+ return count;
+ };
- const parseEnglish = (text) => {
- const words = text.toLowerCase().match(/\b\w+\b/g);
- const uniques = Array.from(new Set(words));
- uniques.sort();
- return uniques;
- };
+ const parseEnglish = (text) => {
+ const words = text.toLowerCase().match(/\b\w+\b/g);
+ const uniques = Array.from(new Set(words));
+ uniques.sort();
+ return uniques;
+ };
- const parseChinese = (text) => {
- const words = text.match(/[^\u4e00-\u9fa5]+|[\u4e00-\u9fa5]+/g);
- const uniques = Array.from(new Set(words));
- uniques.sort(function (a, b) {
- return a.localeCompare(b, 'zh-Hans-CN', { sensitivity: 'accent' });
- });
- return uniques;
- };
+ const parseChinese = (text) => {
+ const words = text.match(/[^\u4e00-\u9fa5]+|[\u4e00-\u9fa5]+/g);
+ const uniques = Array.from(new Set(words));
+ uniques.sort(function (a, b) {
+ return a.localeCompare(b, "zh-Hans-CN", { sensitivity: "accent" });
+ });
+ return uniques;
+ };
- const parse = (text, language) => {
- if (language === 'zh') {
- return parseChinese(text);
- }
- return parseEnglish(text);
- };
+ const parse = (text, language) => {
+ if (language === "zh") {
+ return parseChinese(text);
+ }
+ return parseEnglish(text);
+ };
- Scratch.extensions.register(new MathAndString());
+ Scratch.extensions.register(new MathAndString());
})(Scratch);
diff --git a/extensions/NOname-awa/more-comparisons.js b/extensions/NOname-awa/more-comparisons.js
index 342a3cfc33..19a08b5e6b 100644
--- a/extensions/NOname-awa/more-comparisons.js
+++ b/extensions/NOname-awa/more-comparisons.js
@@ -1,538 +1,568 @@
-// Name: More Comparisons
-// Description: More comparison blocks.
-// By: NOname-awa
-
-(function(Scratch) {
- 'use strict';
- const quadrilateral = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSI3Ny4wMjc4MSIgaGVpZ2h0PSI1NC44MDY1NCIgdmlld0JveD0iMCwwLDc3LjAyNzgxLDU0LjgwNjU0Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjAxLjUwNDMsLTE1Mi4yMTk3MykiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIHN0eWxlPSJtaXgtYmxlbmQtbW9kZTogbm9ybWFsIj48cGF0aCBkPSJNMjI4LjE3ODc4LDE1NS40NzM3NGw0Ni40MDEwMywxOS44ODYxNmwtMjIuNTM3NjQsMjkuMTY2MzZoLTQ2LjYyMTk5eiIvPjwvZz48L2c+PC9zdmc+";
- class MoreComparisons {
- getInfo() {
- return {
- id: 'nonameawacomparisons',
- name: 'More Comparisons',
- color1: '#00a889',
- color2: '#1e8c76',
- color3: '#1e8c76',
- blocks: [
- {
- opcode: 'true',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'true',
- arguments: {}
- },
- {
- opcode: 'false',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'false',
- arguments: {},
- },
- {
- opcode: 'boolean',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- },
- }
- },
- {
- opcode: 'booleanToInt',
- blockType: Scratch.BlockType.REPORTER,
- text: '[a]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.BOOLEAN,
- },
- }
- },
- '---',
- {
- opcode: 'equal',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ⩵ [b]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'A'
- },
- b: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'a'
- },
- }
- },
- {
- opcode: 'equalNegative',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] =- [b]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '5'
- },
- b: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '-5'
- },
- }
- },
- {
- opcode: 'equalPlusMinus',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] =± [b]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '5'
- },
- b: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '-5'
- },
- }
- },
- {
- opcode: 'notEqual',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ≠ [b]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '\n'
- },
- b: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '\n'
- },
- }
- },
- {
- opcode: 'almostEqual2n',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ≈ [b]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '5.5'
- },
- b: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '6'
- },
- }
- },
- {
- opcode: 'almostEqual3n',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ≈ [b] ± [c]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '5'
- },
- b: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '6'
- },
- c: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '1'
- },
- }
- },
- {
- opcode: 'xor',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ^ [b]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.BOOLEAN,
- },
- b: {
- type: Scratch.ArgumentType.BOOLEAN,
- },
- }
- },
- '---',
- {
- opcode: 'equalOrGreater',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ≥ [b]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '\n'
- },
- b: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '50'
- },
- }
- },
- {
- opcode: 'equalOrLess',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ≤ [b]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '\n'
- },
- b: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '50'
- },
- }
- },
- {
- opcode: 'between',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] < [b] < [c]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '\n'
- },
- b: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '\n'
- },
- c: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '\n'
- },
- }
- },
- {
- opcode: 'betweenEqual',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ≤ [b] ≤ [c]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '\n'
- },
- b: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '\n'
- },
- c: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '\n'
- },
- }
- },
- '---',
- {
- opcode: 'vertical',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ⊥ [b]',
- arguments: {
- a: {
- type: Scratch.ArgumentType.ANGLE,
- defaultValue: '0'
- },
- b: {
- type: Scratch.ArgumentType.ANGLE,
- defaultValue: '90'
- },
- }
- },
- {
- opcode: 'segment_one',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '⎵ ([x1],[y1]) ([x2],[y2]) = [n]',
- arguments: {
- x1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '-100'
- },
- y1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- x2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- y2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
-
- n: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '100'
- },
- }
- },
- {
- opcode: 'segment_two',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '⎵ ([x11],[y11]) ([x12],[y12]) = ⎵ ([x21],[y21]) ([x22],[y22])',
- arguments: {
- x11: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '-100'
- },
- y11: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- x12: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- y12: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '-100'
- },
-
- x21: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '100'
- },
- y21: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- x22: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- y22: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '100'
- },
- }
- },
- {
- opcode: 'segment',
- blockType: Scratch.BlockType.REPORTER,
- text: '⎵ ([x1],[y1]) ([x2],[y2])',
- arguments: {
- x1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '-100'
- },
- y1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- x2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- y2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- }
- },
- '---',
- {
- opcode: 'Squadrilateral_one',
- blockType: Scratch.BlockType.BOOLEAN,
- text: '[IMAGE] ([x1],[y1]) ([x2],[y2]) ([x3],[y3]) ([x4],[y4]) = [n]',
- arguments: {
- x1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- y1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- x2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- y2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- x3: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- y3: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- x4: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- y4: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
-
- n: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '100'
- },
-
- IMAGE: {
- type: Scratch.ArgumentType.IMAGE,
- dataURI: quadrilateral,
- flipRTL: true
- }
- },
- },
- {
- opcode: 'Squadrilateral',
- blockType: Scratch.BlockType.REPORTER,
- text: '[IMAGE] ([x1],[y1]) ([x2],[y2]) ([x3],[y3]) ([x4],[y4])',
- arguments: {
- x1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- y1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- x2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- y2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- x3: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- y3: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- x4: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- y4: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
-
- IMAGE: {
- type: Scratch.ArgumentType.IMAGE,
- dataURI: quadrilateral,
- flipRTL: true
- }
- },
- }
- ]
- };
- }
- true(){
- return true;
- }
- false(){
- return false;
- }
- boolean(args){
- return Scratch.Cast.toBoolean(args.a);
- }
- booleanToInt(args){
- if (Scratch.Cast.toBoolean(args.a)) {
- return 1;
- }
- return 0;
- }
- equal(args) {
- return (args.a == args.b);
- }
- equalNegative(args) {
- if (isNaN(args.a) || isNaN(args.b)){
- return false;
- } else {
- return (args.a == (0 - args.b));
- }
- }
- equalPlusMinus(args) {
- if (isNaN(args.a) || isNaN(args.b)){
- return false;
- } else {
- return (args.a == (0 - args.b)) || (args.a == args.b);
- }
- }
- almostEqual2n(args) {
- return (Math.round(args.a) == Math.round(args.b));
- }
- almostEqual3n(args) {
- return (Math.abs(args.a - args.b) <= args.c);
- }
- between(args) {
- return (args.a < args.b) && (args.b < args.c);
- }
- betweenEqual(args) {
- return (args.a <= args.b) && (args.b <= args.c);
- }
- notEqual(args){
- return (args.a != args.b);
- }
- xor(args){
- return Scratch.Cast.toBoolean(args.a) !== Scratch.Cast.toBoolean(args.b);
- }
- equalOrGreater(args) {
- return (args.a >= args.b);
- }
- equalOrLess(args) {
- return (args.a <= args.b);
- }
- vertical(args) {
- if (isNaN(args.a) || isNaN(args.b)){
- return false;
- } else {
- return ((args.a - (args.b - 90)) % 180) == 0;
- }
- }
- segment_one(args) {
- return Math.round(Math.sqrt(Math.pow(args.x1 - args.x2, 2) + Math.pow(args.y1 - args.y2, 2))) == args.n;
- }
- segment_two(args) {
- return Math.round(Math.sqrt(Math.pow(args.x11 - args.x12, 2) + Math.pow(args.y11 - args.y12, 2)))
- == Math.round(Math.sqrt(Math.pow(args.x21 - args.x22, 2) + Math.pow(args.y21 - args.y22, 2)));
- }
- segment(args) {
- return Math.sqrt(Math.pow(args.x1 - args.x2, 2) + Math.pow(args.y1 - args.y2, 2));
- }
- Squadrilateral_one(args) {
- let points = [[args.x1,args.y1], [args.x2,args.y2], [args.x3,args.y3], [args.x4,args.y4]];
- let area = 0;
- let n = points.length;
- for (let i = 0; i < n; i++) {
- let x1 = points[i][0];
- let y1 = points[i][1];
- let x2 = points[(i + 1) % n][0];
- let y2 = points[(i + 1) % n][1];
- area += x1 * y2;
- area -= x2 * y1;
- }
- area = Math.abs(area) / 2;
- return Math.round(area) == args.n;
- }
- Squadrilateral(args) {
- let points = [[args.x1,args.y1], [args.x2,args.y2], [args.x3,args.y3], [args.x4,args.y4]];
- let area = 0;
- let n = points.length;
- for (let i = 0; i < n; i++) {
- let x1 = points[i][0];
- let y1 = points[i][1];
- let x2 = points[(i + 1) % n][0];
- let y2 = points[(i + 1) % n][1];
- area += x1 * y2;
- area -= x2 * y1;
- }
- area = Math.abs(area) / 2;
- return (area);
- }
- }
- Scratch.extensions.register(new MoreComparisons());
-})(Scratch);
+// Name: More Comparisons
+// ID: nonameawacomparisons
+// Description: More comparison blocks.
+// By: NOname-awa
+
+(function (Scratch) {
+ "use strict";
+ const quadrilateral =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSI3Ny4wMjc4MSIgaGVpZ2h0PSI1NC44MDY1NCIgdmlld0JveD0iMCwwLDc3LjAyNzgxLDU0LjgwNjU0Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjAxLjUwNDMsLTE1Mi4yMTk3MykiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIHN0eWxlPSJtaXgtYmxlbmQtbW9kZTogbm9ybWFsIj48cGF0aCBkPSJNMjI4LjE3ODc4LDE1NS40NzM3NGw0Ni40MDEwMywxOS44ODYxNmwtMjIuNTM3NjQsMjkuMTY2MzZoLTQ2LjYyMTk5eiIvPjwvZz48L2c+PC9zdmc+";
+ class MoreComparisons {
+ getInfo() {
+ return {
+ id: "nonameawacomparisons",
+ name: "More Comparisons",
+ color1: "#00a889",
+ color2: "#1e8c76",
+ color3: "#1e8c76",
+ blocks: [
+ {
+ opcode: "true",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "true",
+ arguments: {},
+ },
+ {
+ opcode: "false",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "false",
+ arguments: {},
+ },
+ {
+ opcode: "boolean",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "",
+ },
+ },
+ },
+ {
+ opcode: "booleanToInt",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[a]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.BOOLEAN,
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "equal",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] ⩵ [b]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "A",
+ },
+ b: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "a",
+ },
+ },
+ },
+ {
+ opcode: "equalNegative",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] =- [b]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "5",
+ },
+ b: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "-5",
+ },
+ },
+ },
+ {
+ opcode: "equalPlusMinus",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] =± [b]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "5",
+ },
+ b: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "-5",
+ },
+ },
+ },
+ {
+ opcode: "notEqual",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] ≠ [b]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "\n",
+ },
+ b: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "\n",
+ },
+ },
+ },
+ {
+ opcode: "almostEqual2n",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] ≈ [b]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "5.5",
+ },
+ b: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "6",
+ },
+ },
+ },
+ {
+ opcode: "almostEqual3n",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] ≈ [b] ± [c]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "5",
+ },
+ b: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "6",
+ },
+ c: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "1",
+ },
+ },
+ },
+ {
+ opcode: "xor",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] ^ [b]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.BOOLEAN,
+ },
+ b: {
+ type: Scratch.ArgumentType.BOOLEAN,
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "equalOrGreater",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] ≥ [b]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "\n",
+ },
+ b: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "50",
+ },
+ },
+ },
+ {
+ opcode: "equalOrLess",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] ≤ [b]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "\n",
+ },
+ b: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "50",
+ },
+ },
+ },
+ {
+ opcode: "between",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] < [b] < [c]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "\n",
+ },
+ b: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "\n",
+ },
+ c: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "\n",
+ },
+ },
+ },
+ {
+ opcode: "betweenEqual",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] ≤ [b] ≤ [c]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "\n",
+ },
+ b: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "\n",
+ },
+ c: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "\n",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "vertical",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[a] ⊥ [b]",
+ arguments: {
+ a: {
+ type: Scratch.ArgumentType.ANGLE,
+ defaultValue: "0",
+ },
+ b: {
+ type: Scratch.ArgumentType.ANGLE,
+ defaultValue: "90",
+ },
+ },
+ },
+ {
+ opcode: "segment_one",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "⎵ ([x1],[y1]) ([x2],[y2]) = [n]",
+ arguments: {
+ x1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "-100",
+ },
+ y1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ x2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ y2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+
+ n: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "100",
+ },
+ },
+ },
+ {
+ opcode: "segment_two",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "⎵ ([x11],[y11]) ([x12],[y12]) = ⎵ ([x21],[y21]) ([x22],[y22])",
+ arguments: {
+ x11: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "-100",
+ },
+ y11: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ x12: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ y12: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "-100",
+ },
+
+ x21: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "100",
+ },
+ y21: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ x22: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ y22: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "100",
+ },
+ },
+ },
+ {
+ opcode: "segment",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "⎵ ([x1],[y1]) ([x2],[y2])",
+ arguments: {
+ x1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "-100",
+ },
+ y1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ x2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ y2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "Squadrilateral_one",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[IMAGE] ([x1],[y1]) ([x2],[y2]) ([x3],[y3]) ([x4],[y4]) = [n]",
+ arguments: {
+ x1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ y1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ x2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ y2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ x3: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ y3: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ x4: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ y4: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+
+ n: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "100",
+ },
+
+ IMAGE: {
+ type: Scratch.ArgumentType.IMAGE,
+ dataURI: quadrilateral,
+ flipRTL: true,
+ },
+ },
+ },
+ {
+ opcode: "Squadrilateral",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[IMAGE] ([x1],[y1]) ([x2],[y2]) ([x3],[y3]) ([x4],[y4])",
+ arguments: {
+ x1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ y1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ x2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ y2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ x3: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ y3: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ x4: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ y4: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+
+ IMAGE: {
+ type: Scratch.ArgumentType.IMAGE,
+ dataURI: quadrilateral,
+ flipRTL: true,
+ },
+ },
+ },
+ ],
+ };
+ }
+ true() {
+ return true;
+ }
+ false() {
+ return false;
+ }
+ boolean(args) {
+ return Scratch.Cast.toBoolean(args.a);
+ }
+ booleanToInt(args) {
+ if (Scratch.Cast.toBoolean(args.a)) {
+ return 1;
+ }
+ return 0;
+ }
+ equal(args) {
+ return args.a == args.b;
+ }
+ equalNegative(args) {
+ if (isNaN(args.a) || isNaN(args.b)) {
+ return false;
+ } else {
+ return args.a == 0 - args.b;
+ }
+ }
+ equalPlusMinus(args) {
+ if (isNaN(args.a) || isNaN(args.b)) {
+ return false;
+ } else {
+ return args.a == 0 - args.b || args.a == args.b;
+ }
+ }
+ almostEqual2n(args) {
+ return Math.round(args.a) == Math.round(args.b);
+ }
+ almostEqual3n(args) {
+ return Math.abs(args.a - args.b) <= args.c;
+ }
+ between(args) {
+ return args.a < args.b && args.b < args.c;
+ }
+ betweenEqual(args) {
+ return args.a <= args.b && args.b <= args.c;
+ }
+ notEqual(args) {
+ return args.a != args.b;
+ }
+ xor(args) {
+ return Scratch.Cast.toBoolean(args.a) !== Scratch.Cast.toBoolean(args.b);
+ }
+ equalOrGreater(args) {
+ return args.a >= args.b;
+ }
+ equalOrLess(args) {
+ return args.a <= args.b;
+ }
+ vertical(args) {
+ if (isNaN(args.a) || isNaN(args.b)) {
+ return false;
+ } else {
+ return (args.a - (args.b - 90)) % 180 == 0;
+ }
+ }
+ segment_one(args) {
+ return (
+ Math.round(
+ Math.sqrt(
+ Math.pow(args.x1 - args.x2, 2) + Math.pow(args.y1 - args.y2, 2)
+ )
+ ) == args.n
+ );
+ }
+ segment_two(args) {
+ return (
+ Math.round(
+ Math.sqrt(
+ Math.pow(args.x11 - args.x12, 2) + Math.pow(args.y11 - args.y12, 2)
+ )
+ ) ==
+ Math.round(
+ Math.sqrt(
+ Math.pow(args.x21 - args.x22, 2) + Math.pow(args.y21 - args.y22, 2)
+ )
+ )
+ );
+ }
+ segment(args) {
+ return Math.sqrt(
+ Math.pow(args.x1 - args.x2, 2) + Math.pow(args.y1 - args.y2, 2)
+ );
+ }
+ Squadrilateral_one(args) {
+ let points = [
+ [args.x1, args.y1],
+ [args.x2, args.y2],
+ [args.x3, args.y3],
+ [args.x4, args.y4],
+ ];
+ let area = 0;
+ let n = points.length;
+ for (let i = 0; i < n; i++) {
+ let x1 = points[i][0];
+ let y1 = points[i][1];
+ let x2 = points[(i + 1) % n][0];
+ let y2 = points[(i + 1) % n][1];
+ area += x1 * y2;
+ area -= x2 * y1;
+ }
+ area = Math.abs(area) / 2;
+ return Math.round(area) == args.n;
+ }
+ Squadrilateral(args) {
+ let points = [
+ [args.x1, args.y1],
+ [args.x2, args.y2],
+ [args.x3, args.y3],
+ [args.x4, args.y4],
+ ];
+ let area = 0;
+ let n = points.length;
+ for (let i = 0; i < n; i++) {
+ let x1 = points[i][0];
+ let y1 = points[i][1];
+ let x2 = points[(i + 1) % n][0];
+ let y2 = points[(i + 1) % n][1];
+ area += x1 * y2;
+ area -= x2 * y1;
+ }
+ area = Math.abs(area) / 2;
+ return area;
+ }
+ }
+ Scratch.extensions.register(new MoreComparisons());
+})(Scratch);
diff --git a/extensions/NOname-awa/regular-expression.js b/extensions/NOname-awa/regular-expression.js
index 9e8aadcdcc..0d79427d1c 100644
--- a/extensions/NOname-awa/regular-expression.js
+++ b/extensions/NOname-awa/regular-expression.js
@@ -1,241 +1,246 @@
(function (Scratch) {
- 'use strict';
- let args = ["", "", "",""];
- class RegularExpression {
- getInfo() {
- return {
- color1: '#0081d3',
- color2: '#0067a9',
- color3: '#0067a9',
- id: 'nonameawaregex',
- name: 'Regular Expression',
- blocks: [
- {
- opcode: 'set',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set a: [one] b: [two] c: [three]',
- arguments: {
- one: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- },
- two: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- },
- three: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
- },
- {
- opcode: 'matchText',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'matchText: b in a [flags]',
- arguments: {
- flags: {
- type: Scratch.ArgumentType.STRING,
- menu: 'flags'
- }
- }
- },
- {
- opcode: 'searchText',
- blockType: Scratch.BlockType.REPORTER,
- text: 'searchText: b in a',
- },
- {
- opcode: 'replaceText',
- blockType: Scratch.BlockType.REPORTER,
- text: 'replaceText: b in a with c',
- }, '---',
- {
- opcode: 'matchTextWithPattern',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'match [pattern] in [text] - [flags]',
- arguments: {
- text: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- },
- pattern: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- },
- flags: {
- type: Scratch.ArgumentType.STRING,
- menu: 'flags'
- }
- }
- },
- {
- opcode: 'searchTextWithPattern',
- blockType: Scratch.BlockType.REPORTER,
- text: 'search [pattern] in [text] ',
- arguments: {
- text: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- },
- pattern: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
- },
- {
- opcode: 'replaceTextWithPattern',
- blockType: Scratch.BlockType.REPORTER,
- text: 'replace [pattern] in [text] with [replacement]',
- arguments: {
- text: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- },
- pattern: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- },
- replacement: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
- }, '---',
- {
- opcode: 'constant',
- blockType: Scratch.BlockType.REPORTER,
- text: '[constant]',
- arguments: {
- constant: {
- type: Scratch.ArgumentType.STRING,
- menu: 'constant'
- }
- }
- }, '---',
- {
- opcode: 'text',
- blockType: Scratch.BlockType.REPORTER,
- text: 'a',
- },
- {
- opcode: 'pattern',
- blockType: Scratch.BlockType.REPORTER,
- text: 'b',
- },
- {
- opcode: 'attach',
- blockType: Scratch.BlockType.REPORTER,
- text: 'c',
- }
- ],
- menus: {
- flags: {
- acceptReporters: false,
- items: [
- {
- text: 'global (g)',
- value: 'g'
- },
- {
- text: 'ignore case (i)',
- value: 'i'
- }
- ]
- },
- constant: {
- acceptReporters: true,
- items: [
- {
- text: 'english',
- value: '[a-zA-Z]'
- },
- {
- text: 'english uppercase',
- value: '[A-Z]'
- },
- {
- text: 'english lowercase',
- value: '[a-z]'
- },
- {
- text: 'number',
- value: '[0-9]'
- },
- {
- text: 'numeric integer',
- value: '^-?[1-9]\\d*$'
- },
- {
- text: 'positive integer',
- value: '^[1-9]\\d*$'
- },
- {
- text: 'negative integer',
- value: '^-[1-9]\\d*$'
- },
- {
- text: 'non-negative integers',
- value: '^[1-9]\\d*|0$'
- },
- {
- text: 'non-positive integer',
- value: '^-[1-9]\\d*|0$'
- },
- {
- text: 'chinese',
- value: '[\u4e00-\u9fa5]'
- },
- {
- text: 'double-byte',
- value: '[^\x00-\xff]'
- }
- ]
- }
- }
- };
- }
- set({ one, two, three }) {
- args[1] = one;
- args[2] = two;
- args[3] = three;
- }
- matchText({ flags }) {
- const regex = new RegExp(args[2], flags);
- return regex.test(args[1]);
- }
- searchText() {
- return args[1].toString().search(args[2].toString());
- }
- replaceText() {
- return args[1].toString().replace(args[2].toString(), args[3].toString());
- }
+ "use strict";
+ let args = ["", "", "", ""];
+ class RegularExpression {
+ getInfo() {
+ return {
+ color1: "#0081d3",
+ color2: "#0067a9",
+ color3: "#0067a9",
+ id: "nonameawaregex",
+ name: "Regular Expression",
+ blocks: [
+ {
+ opcode: "set",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set a: [one] b: [two] c: [three]",
+ arguments: {
+ one: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "",
+ },
+ two: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "",
+ },
+ three: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "",
+ },
+ },
+ },
+ {
+ opcode: "matchText",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "matchText: b in a [flags]",
+ arguments: {
+ flags: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "flags",
+ },
+ },
+ },
+ {
+ opcode: "searchText",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "searchText: b in a",
+ },
+ {
+ opcode: "replaceText",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "replaceText: b in a with c",
+ },
+ "---",
+ {
+ opcode: "matchTextWithPattern",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "match [pattern] in [text] - [flags]",
+ arguments: {
+ text: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "",
+ },
+ pattern: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "",
+ },
+ flags: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "flags",
+ },
+ },
+ },
+ {
+ opcode: "searchTextWithPattern",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "search [pattern] in [text] ",
+ arguments: {
+ text: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "",
+ },
+ pattern: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "",
+ },
+ },
+ },
+ {
+ opcode: "replaceTextWithPattern",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "replace [pattern] in [text] with [replacement]",
+ arguments: {
+ text: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "",
+ },
+ pattern: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "",
+ },
+ replacement: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "constant",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[constant]",
+ arguments: {
+ constant: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "constant",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "text",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "a",
+ },
+ {
+ opcode: "pattern",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "b",
+ },
+ {
+ opcode: "attach",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "c",
+ },
+ ],
+ menus: {
+ flags: {
+ acceptReporters: false,
+ items: [
+ {
+ text: "global (g)",
+ value: "g",
+ },
+ {
+ text: "ignore case (i)",
+ value: "i",
+ },
+ ],
+ },
+ constant: {
+ acceptReporters: true,
+ items: [
+ {
+ text: "english",
+ value: "[a-zA-Z]",
+ },
+ {
+ text: "english uppercase",
+ value: "[A-Z]",
+ },
+ {
+ text: "english lowercase",
+ value: "[a-z]",
+ },
+ {
+ text: "number",
+ value: "[0-9]",
+ },
+ {
+ text: "numeric integer",
+ value: "^-?[1-9]\\d*$",
+ },
+ {
+ text: "positive integer",
+ value: "^[1-9]\\d*$",
+ },
+ {
+ text: "negative integer",
+ value: "^-[1-9]\\d*$",
+ },
+ {
+ text: "non-negative integers",
+ value: "^[1-9]\\d*|0$",
+ },
+ {
+ text: "non-positive integer",
+ value: "^-[1-9]\\d*|0$",
+ },
+ {
+ text: "chinese",
+ value: "[\u4e00-\u9fa5]",
+ },
+ {
+ text: "double-byte",
+ value: "[^\x00-\xff]",
+ },
+ ],
+ },
+ },
+ };
+ }
+ set({ one, two, three }) {
+ args[1] = one;
+ args[2] = two;
+ args[3] = three;
+ }
+ matchText({ flags }) {
+ const regex = new RegExp(args[2], flags);
+ return regex.test(args[1]);
+ }
+ searchText() {
+ return args[1].toString().search(args[2].toString());
+ }
+ replaceText() {
+ return args[1].toString().replace(args[2].toString(), args[3].toString());
+ }
- matchTextWithPattern({ text, pattern, flags }) {
- const regex = new RegExp(pattern, flags);
- return regex.test(text);
- }
- searchTextWithPattern({ text, pattern }) {
- return text.toString().search(pattern.toString());
- }
- replaceTextWithPattern({ text, pattern, replacement }) {
- return text.toString().replace(pattern.toString(), replacement.toString());
- }
+ matchTextWithPattern({ text, pattern, flags }) {
+ const regex = new RegExp(pattern, flags);
+ return regex.test(text);
+ }
+ searchTextWithPattern({ text, pattern }) {
+ return text.toString().search(pattern.toString());
+ }
+ replaceTextWithPattern({ text, pattern, replacement }) {
+ return text
+ .toString()
+ .replace(pattern.toString(), replacement.toString());
+ }
- constant({ constant }) {
- return constant;
- }
+ constant({ constant }) {
+ return constant;
+ }
- text() {
- return args[1];
- }
- pattern() {
- return args[2];
- }
- attach() {
- return args[3];
- }
+ text() {
+ return args[1];
+ }
+ pattern() {
+ return args[2];
+ }
+ attach() {
+ return args[3];
}
- Scratch.extensions.register(new RegularExpression());
+ }
+ Scratch.extensions.register(new RegularExpression());
})(Scratch);
diff --git a/extensions/NOname-awa/sort-unique-words.js b/extensions/NOname-awa/sort-unique-words.js
index f900d9ee26..d4bcc388b7 100644
--- a/extensions/NOname-awa/sort-unique-words.js
+++ b/extensions/NOname-awa/sort-unique-words.js
@@ -1,78 +1,78 @@
-(function(Scratch) {
- 'use strict';
-
- const parseEnglish = (text) => {
- const words = text.toLowerCase().match(/\b\w+\b/g);
- const uniques = Array.from(new Set(words));
- uniques.sort();
- return uniques;
- };
-
- const parseChinese = (text) => {
- const words = text.match(/[^\u4e00-\u9fa5]+|[\u4e00-\u9fa5]+/g);
- const uniques = Array.from(new Set(words));
- uniques.sort(function(a, b) {
- return a.localeCompare(b, 'zh-Hans-CN', {sensitivity: 'accent'});
- });
- return uniques;
- };
-
- const parse = (text, language) => {
- if (language === 'zh') {
- return parseChinese(text);
- }
- return parseEnglish(text);
- };
-
- class SortUniqueWords {
- getInfo() {
- return {
- id: 'nonameawasortuniquewords',
- name: 'Sort Unique Words',
- color1: '#5a8b9e',
- color2: '#427081',
- color3: '#427081',
- blocks: [
- {
- opcode: 'words',
- blockType: Scratch.BlockType.REPORTER,
- disableMonitor: true,
- text: 'sort unique words in [text] as [language]',
- arguments: {
- text: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'movie dog restaurant book school'
- },
- language: {
- type: Scratch.ArgumentType.STRING,
- menu: 'language'
- }
- }
- }
- ],
- menus: {
- language: {
- acceptReporters: true,
- items: [
- {
- text: 'English (en)',
- value: 'en'
- },
- {
- text: 'Chinese (zh)',
- value: 'zh'
- }
- ]
- }
- }
- };
- }
- words(args) {
- const text = Scratch.Cast.toString(args.text);
- const words = parse(text, args.language);
- return words.join(' ');
- }
- }
-
- Scratch.extensions.register(new SortUniqueWords());
-})(Scratch);
+(function (Scratch) {
+ "use strict";
+
+ const parseEnglish = (text) => {
+ const words = text.toLowerCase().match(/\b\w+\b/g);
+ const uniques = Array.from(new Set(words));
+ uniques.sort();
+ return uniques;
+ };
+
+ const parseChinese = (text) => {
+ const words = text.match(/[^\u4e00-\u9fa5]+|[\u4e00-\u9fa5]+/g);
+ const uniques = Array.from(new Set(words));
+ uniques.sort(function (a, b) {
+ return a.localeCompare(b, "zh-Hans-CN", { sensitivity: "accent" });
+ });
+ return uniques;
+ };
+
+ const parse = (text, language) => {
+ if (language === "zh") {
+ return parseChinese(text);
+ }
+ return parseEnglish(text);
+ };
+
+ class SortUniqueWords {
+ getInfo() {
+ return {
+ id: "nonameawasortuniquewords",
+ name: "Sort Unique Words",
+ color1: "#5a8b9e",
+ color2: "#427081",
+ color3: "#427081",
+ blocks: [
+ {
+ opcode: "words",
+ blockType: Scratch.BlockType.REPORTER,
+ disableMonitor: true,
+ text: "sort unique words in [text] as [language]",
+ arguments: {
+ text: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "movie dog restaurant book school",
+ },
+ language: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "language",
+ },
+ },
+ },
+ ],
+ menus: {
+ language: {
+ acceptReporters: true,
+ items: [
+ {
+ text: "English (en)",
+ value: "en",
+ },
+ {
+ text: "Chinese (zh)",
+ value: "zh",
+ },
+ ],
+ },
+ },
+ };
+ }
+ words(args) {
+ const text = Scratch.Cast.toString(args.text);
+ const words = parse(text, args.language);
+ return words.join(" ");
+ }
+ }
+
+ Scratch.extensions.register(new SortUniqueWords());
+})(Scratch);
diff --git a/extensions/NexusKitten/controlcontrols.js b/extensions/NexusKitten/controlcontrols.js
new file mode 100644
index 0000000000..3a4910012e
--- /dev/null
+++ b/extensions/NexusKitten/controlcontrols.js
@@ -0,0 +1,165 @@
+// Name: Control Controls
+// ID: nkcontrols
+// Description: Show and hide the project's controls.
+// By: NamelessCat
+
+(function (Scratch) {
+ "use strict";
+
+ if (!Scratch.extensions.unsandboxed) {
+ throw new Error("Control Controls must run unsandboxed");
+ }
+
+ var fullScreen;
+ var greenFlag;
+ var pauseButton;
+ var stopButton;
+
+ const getButtons = () => {
+ fullScreen = undefined;
+ greenFlag = undefined;
+ pauseButton = undefined;
+ stopButton = undefined;
+
+ const rightButtons = document.querySelectorAll(
+ '[class*="stage-header_stage-button_"]'
+ );
+ fullScreen = rightButtons[rightButtons.length - 1];
+ if (!fullScreen) {
+ fullScreen =
+ document.querySelector(".fullscreen-button") ||
+ document.querySelector(".standalone-fullscreen-button");
+ }
+
+ greenFlag =
+ document.querySelector('[class*="green-flag_green-flag_"]') ||
+ document.querySelector(".green-flag-button");
+ pauseButton =
+ document.querySelector(".pause-btn") ||
+ document.querySelector(".pause-button");
+ stopButton =
+ document.querySelector('[class*="stop-all_stop-all_"]') ||
+ document.querySelector(".stop-all-button");
+ };
+
+ class controlcontrols {
+ getInfo() {
+ return {
+ id: "nkcontrols",
+ name: "Control Controls",
+ color1: "#ffab19",
+ color2: "#ec9c13",
+ color3: "#b87d17",
+ blocks: [
+ {
+ opcode: "showOption",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "show [OPTION]",
+ arguments: {
+ OPTION: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "OPTION",
+ },
+ },
+ },
+ {
+ opcode: "hideOption",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "hide [OPTION]",
+ arguments: {
+ OPTION: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "OPTION",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "optionShown",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[OPTION] shown?",
+ arguments: {
+ OPTION: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "OPTION",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "optionExists",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "[OPTION] exists?",
+ arguments: {
+ OPTION: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "OPTION",
+ },
+ },
+ },
+ ],
+ menus: {
+ OPTION: {
+ acceptReporters: true,
+ items: ["green flag", "pause", "stop", "fullscreen"],
+ },
+ },
+ };
+ }
+
+ showOption(args) {
+ getButtons();
+ if (args.OPTION === "green flag" && greenFlag) {
+ greenFlag.style.display = "block";
+ } else if (args.OPTION === "pause" && pauseButton) {
+ pauseButton.style.display = "block";
+ } else if (args.OPTION === "stop" && stopButton) {
+ stopButton.style.display = "block";
+ } else if (args.OPTION === "fullscreen" && fullScreen) {
+ fullScreen.style.display = "block";
+ }
+ }
+
+ hideOption(args) {
+ getButtons();
+ if (args.OPTION === "green flag" && greenFlag) {
+ greenFlag.style.display = "none";
+ } else if (args.OPTION === "pause" && pauseButton) {
+ pauseButton.style.display = "none";
+ } else if (args.OPTION === "stop" && stopButton) {
+ stopButton.style.display = "none";
+ } else if (args.OPTION === "fullscreen" && fullScreen) {
+ fullScreen.style.display = "none";
+ }
+ }
+
+ optionShown(args) {
+ getButtons();
+ if (args.OPTION === "green flag" && greenFlag) {
+ return greenFlag.style.display !== "none";
+ } else if (args.OPTION === "pause" && pauseButton) {
+ return pauseButton.style.display !== "none";
+ } else if (args.OPTION === "stop" && stopButton) {
+ return stopButton.style.display !== "none";
+ } else if (args.OPTION === "fullscreen" && fullScreen) {
+ return fullScreen.style.display !== "none";
+ }
+ return false;
+ }
+
+ optionExists(args) {
+ getButtons();
+ if (args.OPTION === "green flag" && greenFlag) {
+ return true;
+ } else if (args.OPTION === "pause" && pauseButton) {
+ return true;
+ } else if (args.OPTION === "stop" && stopButton) {
+ return true;
+ } else if (args.OPTION === "fullscreen" && fullScreen) {
+ return true;
+ }
+ return false;
+ }
+ }
+ Scratch.extensions.register(new controlcontrols());
+})(Scratch);
diff --git a/extensions/NexusKitten/moremotion.js b/extensions/NexusKitten/moremotion.js
index 0d6e1fe507..d3aee71d53 100644
--- a/extensions/NexusKitten/moremotion.js
+++ b/extensions/NexusKitten/moremotion.js
@@ -1,364 +1,392 @@
-// Name: More Motion
-// Description: More motion-related blocks.
-// By: NamelessCat
-
-(function(Scratch) {
- 'use strict';
-
- if (!Scratch.extensions.unsandboxed) {
- throw new Error('More Motion must run unsandboxed');
- }
-
- class nkmoremotion {
- getInfo() {
- return {
- id: 'nkmoremotion',
- name: 'More Motion',
- color1: '#4c97ff',
- color2: '#3373cc',
- blocks: [
- {
- filter: [Scratch.TargetType.STAGE],
- blockType: Scratch.BlockType.LABEL,
- text: 'Stage selected: no motion blocks'
- },
- {
- filter: [Scratch.TargetType.SPRITE],
- opcode: 'changexy',
- blockType: Scratch.BlockType.COMMAND,
- text: 'change x: [X] y: [Y]',
- arguments: {
- X: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- Y: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- }
- }
- },
- {
- filter: [Scratch.TargetType.SPRITE],
- opcode: 'pointto',
- blockType: Scratch.BlockType.COMMAND,
- text: 'point towards x: [X] y: [Y]',
- arguments: {
- X: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- Y: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- }
- }
- },
- {
- filter: [Scratch.TargetType.SPRITE],
- opcode: 'rotationStyle',
- blockType: Scratch.BlockType.REPORTER,
- text: 'rotation style',
- disableMonitor: true
- },
- '---',
- {
- filter: [Scratch.TargetType.SPRITE],
- opcode: 'fence',
- blockType: Scratch.BlockType.COMMAND,
- text: 'manually fence'
- },
- '---',
- {
- filter: [Scratch.TargetType.SPRITE],
- opcode: 'steptowards',
- blockType: Scratch.BlockType.COMMAND,
- text: 'move [STEPS] steps towards x: [X] y: [Y]',
- arguments: {
- STEPS: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- X: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- Y: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- }
- }
- },
- {
- filter: [Scratch.TargetType.SPRITE],
- opcode: 'tweentowards',
- blockType: Scratch.BlockType.COMMAND,
- text: 'move [PERCENT]% of the way to x: [X] y: [Y]',
- arguments: {
- PERCENT: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10'
- },
- X: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- Y: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- }
- }
- },
- '---',
- {
- filter: [Scratch.TargetType.SPRITE],
- opcode: 'directionto',
- blockType: Scratch.BlockType.REPORTER,
- text: 'direction to x: [X] y: [Y]',
- arguments: {
- X: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- Y: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- }
- }
- },
- {
- filter: [Scratch.TargetType.SPRITE],
- opcode: 'distanceto',
- blockType: Scratch.BlockType.REPORTER,
- text: 'distance from x: [X] y: [Y]',
- arguments: {
- X: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- Y: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- }
- }
- },
- {
- filter: [Scratch.TargetType.SPRITE],
- opcode: 'spritewh',
- blockType: Scratch.BlockType.REPORTER,
- text: 'sprite [WHAT]',
- disableMonitor: true,
- arguments: {
- WHAT: {
- type: Scratch.ArgumentType.STRING,
- menu: 'WHAT'
- }
- }
- },
- '---',
- {
- filter: [Scratch.TargetType.SPRITE],
- opcode: 'touchingxy',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'touching x: [X] y: [Y]?',
- arguments: {
- X: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- Y: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- }
- }
- },
- {
- filter: [Scratch.TargetType.SPRITE],
- opcode: 'touchingrect',
- blockType: Scratch.BlockType.BOOLEAN,
- text: 'touching rectangle x1: [X1] y1: [Y1] x2: [X2] y2: [Y2]?',
- arguments: {
- X1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '-100'
- },
- Y1: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '-100'
- },
- X2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '100'
- },
- Y2: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '100'
- }
- }
- },
- ],
- menus: {
- WHAT: {
- acceptreporters: true,
- items: [
- 'width',
- 'height',
- 'costume width',
- 'costume height'
- ]
- }
- }
- };
- }
-
- changexy(args, util) {
- const x = Scratch.Cast.toNumber(args.X);
- const y = Scratch.Cast.toNumber(args.Y);
- util.target.setXY(util.target.x + x, util.target.y + y);
- }
-
- // LORAX APPROVED
- pointto(args, util) {
- const x = Scratch.Cast.toNumber(args.X);
- const y = Scratch.Cast.toNumber(args.Y);
- if (util.target.y > y) {
- util.target.setDirection(((180 / Math.PI) * Math.atan((x - util.target.x) / (y - util.target.y))) + 180);
- } else {
- util.target.setDirection(((180 / Math.PI) * Math.atan((x - util.target.x) / (y - util.target.y))));
- }
- }
-
- rotationStyle(args, util) {
- return util.target.rotationStyle;
- }
-
- fence(args, util) {
- const newpos = Scratch.vm.renderer.getFencedPositionOfDrawable(util.target.drawableID, [util.target.x, util.target.y]);
- util.target.setXY(newpos[0], newpos[1]);
- }
-
- directionto(args, util) {
- const x = Scratch.Cast.toNumber(args.X);
- const y = Scratch.Cast.toNumber(args.Y);
- if (util.target.y > y) {
- return ((180 / Math.PI) * Math.atan((x - util.target.x) / (y - util.target.y))) + 180;
- } else {
- return ((180 / Math.PI) * Math.atan((x - util.target.x) / (y - util.target.y)));
- }
- }
-
- distanceto(args, util) {
- const x = Scratch.Cast.toNumber(args.X);
- const y = Scratch.Cast.toNumber(args.Y);
- // Shoutout to Pythagoras!
- return Math.sqrt(((x - util.target.x) ** 2) + ((y - util.target.y) ** 2));
- }
-
- steptowards(args, util) {
- const x = Scratch.Cast.toNumber(args.X);
- const y = Scratch.Cast.toNumber(args.Y);
- const steps = Scratch.Cast.toNumber(args.STEPS);
- const val = steps / (Math.sqrt(((x - util.target.x) ** 2) + ((y - util.target.y) ** 2)));
- if (val >= 1) {
- util.target.setXY(x, y);
- } else {
- util.target.setXY(((x - util.target.x) * (val)) + util.target.x, ((y - util.target.y) * (val)) + util.target.y);
- }
- }
-
- tweentowards(args, util) {
- const x = Scratch.Cast.toNumber(args.X);
- const y = Scratch.Cast.toNumber(args.Y);
- const val = Scratch.Cast.toNumber(args.PERCENT);
- // Essentially a smooth glide script.
- util.target.setXY(((x - util.target.x) * (val / 100)) + util.target.x, ((y - util.target.y) * (val / 100)) + util.target.y);
- }
-
- touchingrect(args, util) {
- let left = Scratch.Cast.toNumber(args.X1);
- let right = Scratch.Cast.toNumber(args.X2);
- let bottom = Scratch.Cast.toNumber(args.Y1);
- let top = Scratch.Cast.toNumber(args.Y2);
-
- // Fix argument order if they got it backwards
- if (left > right) {
- let temp = left;
- left = right;
- right = temp;
- }
- if (bottom > top) {
- let temp = bottom;
- bottom = top;
- bottom = temp;
- }
-
- const drawable = Scratch.vm.renderer._allDrawables[util.target.drawableID];
- if (!drawable) {
- return false;
- }
-
- // See renderer.isTouchingDrawables
-
- const drawableBounds = drawable.getFastBounds();
- drawableBounds.snapToInt();
-
- // This is bad, need to rewrite this when renderer exports Rectangle
- const Rectangle = Object.getPrototypeOf(drawableBounds).constructor;
-
- /** @type {RenderWebGL.Rectangle} */
- const containsBounds = new Rectangle();
- containsBounds.initFromBounds(left, right, bottom, top);
- containsBounds.snapToInt();
-
- if (!containsBounds.intersects(drawableBounds)) {
- return false;
- }
-
- drawable.updateCPURenderAttributes();
-
- /** @type {RenderWebGL.Rectangle} */
- const intersectingBounds = Rectangle.intersect(drawableBounds, containsBounds);
- for (let x = intersectingBounds.left; x < intersectingBounds.right; x++) {
- for (let y = intersectingBounds.bottom; y < intersectingBounds.top; y++) {
- // technically should be a twgl vec3, but does not actually need to be
- if (drawable.isTouching([x, y])) {
- return true;
- }
- }
- }
- return false;
- }
-
- touchingxy(args, util) {
- const x = Scratch.Cast.toNumber(args.X);
- const y = Scratch.Cast.toNumber(args.Y);
- const drawable = Scratch.vm.renderer._allDrawables[util.target.drawableID];
- if (!drawable) {
- return false;
- }
- // Position should technically be a twgl vec3, but it doesn't actually need to be
- drawable.updateCPURenderAttributes();
- return drawable.isTouching([x, y]);
- }
-
- spritewh(args, util) {
- if (args.WHAT === 'width' || args.WHAT === 'height') {
- const bounds = Scratch.vm.renderer.getBounds(util.target.drawableID);
- if (args.WHAT === 'width') {
- return Math.ceil(bounds.width);
- } else {
- return Math.ceil(bounds.height);
- }
- } else if (args.WHAT === 'costume width' || args.WHAT === 'costume height') {
- const costume = util.target.sprite.costumes[util.target.currentCostume];
- if (args.WHAT === 'costume width') {
- return Math.ceil(costume.size[0]);
- } else {
- return Math.ceil(costume.size[1]);
- }
- }
- }
- }
-
- Scratch.extensions.register(new nkmoremotion());
-})(Scratch);
+// Name: More Motion
+// ID: nkmoremotion
+// Description: More motion-related blocks.
+// By: NamelessCat
+
+(function (Scratch) {
+ "use strict";
+
+ if (!Scratch.extensions.unsandboxed) {
+ throw new Error("More Motion must run unsandboxed");
+ }
+
+ // @ts-expect-error - not typed yet
+ const Rectangle = Scratch.vm.renderer.exports.Rectangle;
+
+ class nkmoremotion {
+ getInfo() {
+ return {
+ id: "nkmoremotion",
+ name: "More Motion",
+ color1: "#4c97ff",
+ color2: "#3373cc",
+ blocks: [
+ {
+ filter: [Scratch.TargetType.STAGE],
+ blockType: Scratch.BlockType.LABEL,
+ text: "Stage selected: no motion blocks",
+ },
+ {
+ filter: [Scratch.TargetType.SPRITE],
+ opcode: "changexy",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "change x: [X] y: [Y]",
+ arguments: {
+ X: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ Y: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ },
+ },
+ {
+ filter: [Scratch.TargetType.SPRITE],
+ opcode: "pointto",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "point towards x: [X] y: [Y]",
+ arguments: {
+ X: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ Y: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ },
+ },
+ {
+ filter: [Scratch.TargetType.SPRITE],
+ opcode: "rotationStyle",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "rotation style",
+ disableMonitor: true,
+ },
+ "---",
+ {
+ filter: [Scratch.TargetType.SPRITE],
+ opcode: "fence",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "manually fence",
+ },
+ "---",
+ {
+ filter: [Scratch.TargetType.SPRITE],
+ opcode: "steptowards",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "move [STEPS] steps towards x: [X] y: [Y]",
+ arguments: {
+ STEPS: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ X: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ Y: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ },
+ },
+ {
+ filter: [Scratch.TargetType.SPRITE],
+ opcode: "tweentowards",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "move [PERCENT]% of the way to x: [X] y: [Y]",
+ arguments: {
+ PERCENT: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "10",
+ },
+ X: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ Y: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ },
+ },
+ "---",
+ {
+ filter: [Scratch.TargetType.SPRITE],
+ opcode: "directionto",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "direction to x: [X] y: [Y]",
+ arguments: {
+ X: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ Y: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ },
+ },
+ {
+ filter: [Scratch.TargetType.SPRITE],
+ opcode: "distanceto",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "distance from x: [X] y: [Y]",
+ arguments: {
+ X: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ Y: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ },
+ },
+ {
+ filter: [Scratch.TargetType.SPRITE],
+ opcode: "spritewh",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "sprite [WHAT]",
+ disableMonitor: true,
+ arguments: {
+ WHAT: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "WHAT",
+ },
+ },
+ },
+ "---",
+ {
+ filter: [Scratch.TargetType.SPRITE],
+ opcode: "touchingxy",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "touching x: [X] y: [Y]?",
+ arguments: {
+ X: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ Y: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ },
+ },
+ {
+ filter: [Scratch.TargetType.SPRITE],
+ opcode: "touchingrect",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "touching rectangle x1: [X1] y1: [Y1] x2: [X2] y2: [Y2]?",
+ arguments: {
+ X1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "-100",
+ },
+ Y1: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "-100",
+ },
+ X2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "100",
+ },
+ Y2: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "100",
+ },
+ },
+ },
+ ],
+ menus: {
+ WHAT: {
+ acceptreporters: true,
+ items: ["width", "height", "costume width", "costume height"],
+ },
+ },
+ };
+ }
+
+ changexy(args, util) {
+ const x = Scratch.Cast.toNumber(args.X);
+ const y = Scratch.Cast.toNumber(args.Y);
+ util.target.setXY(util.target.x + x, util.target.y + y);
+ }
+
+ // LORAX APPROVED
+ pointto(args, util) {
+ const x = Scratch.Cast.toNumber(args.X);
+ const y = Scratch.Cast.toNumber(args.Y);
+ if (util.target.y > y) {
+ util.target.setDirection(
+ (180 / Math.PI) *
+ Math.atan((x - util.target.x) / (y - util.target.y)) +
+ 180
+ );
+ } else {
+ util.target.setDirection(
+ (180 / Math.PI) * Math.atan((x - util.target.x) / (y - util.target.y))
+ );
+ }
+ }
+
+ rotationStyle(args, util) {
+ return util.target.rotationStyle;
+ }
+
+ fence(args, util) {
+ const newpos = Scratch.vm.renderer.getFencedPositionOfDrawable(
+ util.target.drawableID,
+ [util.target.x, util.target.y]
+ );
+ util.target.setXY(newpos[0], newpos[1]);
+ }
+
+ directionto(args, util) {
+ const x = Scratch.Cast.toNumber(args.X);
+ const y = Scratch.Cast.toNumber(args.Y);
+ if (util.target.y > y) {
+ return (
+ (180 / Math.PI) *
+ Math.atan((x - util.target.x) / (y - util.target.y)) +
+ 180
+ );
+ } else {
+ return (
+ (180 / Math.PI) * Math.atan((x - util.target.x) / (y - util.target.y))
+ );
+ }
+ }
+
+ distanceto(args, util) {
+ const x = Scratch.Cast.toNumber(args.X);
+ const y = Scratch.Cast.toNumber(args.Y);
+ // Shoutout to Pythagoras!
+ return Math.sqrt((x - util.target.x) ** 2 + (y - util.target.y) ** 2);
+ }
+
+ steptowards(args, util) {
+ const x = Scratch.Cast.toNumber(args.X);
+ const y = Scratch.Cast.toNumber(args.Y);
+ const steps = Scratch.Cast.toNumber(args.STEPS);
+ const val =
+ steps / Math.sqrt((x - util.target.x) ** 2 + (y - util.target.y) ** 2);
+ if (val >= 1) {
+ util.target.setXY(x, y);
+ } else {
+ util.target.setXY(
+ (x - util.target.x) * val + util.target.x,
+ (y - util.target.y) * val + util.target.y
+ );
+ }
+ }
+
+ tweentowards(args, util) {
+ const x = Scratch.Cast.toNumber(args.X);
+ const y = Scratch.Cast.toNumber(args.Y);
+ const val = Scratch.Cast.toNumber(args.PERCENT);
+ // Essentially a smooth glide script.
+ util.target.setXY(
+ (x - util.target.x) * (val / 100) + util.target.x,
+ (y - util.target.y) * (val / 100) + util.target.y
+ );
+ }
+
+ touchingrect(args, util) {
+ let left = Scratch.Cast.toNumber(args.X1);
+ let right = Scratch.Cast.toNumber(args.X2);
+ let bottom = Scratch.Cast.toNumber(args.Y1);
+ let top = Scratch.Cast.toNumber(args.Y2);
+
+ // Fix argument order if they got it backwards
+ if (left > right) {
+ let temp = left;
+ left = right;
+ right = temp;
+ }
+ if (bottom > top) {
+ let temp = bottom;
+ bottom = top;
+ bottom = temp;
+ }
+
+ const drawable =
+ Scratch.vm.renderer._allDrawables[util.target.drawableID];
+ if (!drawable) {
+ return false;
+ }
+
+ // See renderer.isTouchingDrawables
+
+ const drawableBounds = drawable.getFastBounds();
+ drawableBounds.snapToInt();
+
+ const containsBounds = new Rectangle();
+ containsBounds.initFromBounds(left, right, bottom, top);
+ containsBounds.snapToInt();
+
+ if (!containsBounds.intersects(drawableBounds)) {
+ return false;
+ }
+
+ drawable.updateCPURenderAttributes();
+
+ const intersectingBounds = Rectangle.intersect(
+ drawableBounds,
+ containsBounds
+ );
+ for (let x = intersectingBounds.left; x < intersectingBounds.right; x++) {
+ for (
+ let y = intersectingBounds.bottom;
+ y < intersectingBounds.top;
+ y++
+ ) {
+ // technically should be a twgl vec3, but does not actually need to be
+ if (drawable.isTouching([x, y])) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ touchingxy(args, util) {
+ const x = Scratch.Cast.toNumber(args.X);
+ const y = Scratch.Cast.toNumber(args.Y);
+ const drawable =
+ Scratch.vm.renderer._allDrawables[util.target.drawableID];
+ if (!drawable) {
+ return false;
+ }
+ // Position should technically be a twgl vec3, but it doesn't actually need to be
+ drawable.updateCPURenderAttributes();
+ return drawable.isTouching([x, y]);
+ }
+
+ spritewh(args, util) {
+ if (args.WHAT === "width" || args.WHAT === "height") {
+ const bounds = Scratch.vm.renderer.getBounds(util.target.drawableID);
+ if (args.WHAT === "width") {
+ return Math.ceil(bounds.width);
+ } else {
+ return Math.ceil(bounds.height);
+ }
+ } else if (
+ args.WHAT === "costume width" ||
+ args.WHAT === "costume height"
+ ) {
+ const costume = util.target.sprite.costumes[util.target.currentCostume];
+ if (args.WHAT === "costume width") {
+ return Math.ceil(costume.size[0]);
+ } else {
+ return Math.ceil(costume.size[1]);
+ }
+ }
+ }
+ }
+
+ Scratch.extensions.register(new nkmoremotion());
+})(Scratch);
diff --git a/extensions/NexusKitten/sgrab.js b/extensions/NexusKitten/sgrab.js
index 594401326c..6066bea0d2 100644
--- a/extensions/NexusKitten/sgrab.js
+++ b/extensions/NexusKitten/sgrab.js
@@ -1,253 +1,269 @@
-// Name: S-Grab
-// Description: Get information about Scratch projects and Scratch users.
-// By: NamelessCat
-
-(function(Scratch) {
- 'use strict';
-
- if (!Scratch.extensions.unsandboxed) {
- throw new Error('This Extension must run unsandboxed');
- }
-
- const icon = "data:image/png;base64,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";
-
- class nexuskittensgrab {
- getInfo() {
- return {
- id: 'nexuskittensgrab',
- name: 'S-Grab',
- menuIconURI: icon,
- color1: '#ECA90B',
- color2: '#EBAF00',
- blocks: [
- {
- opcode: 'usergrab',
- blockType: Scratch.BlockType.REPORTER,
- text: 'grab [WHAT] count of user [WHO]',
- arguments: {
- WHAT: {
- type: Scratch.ArgumentType.STRING,
- menu: 'WHAT'
- },
- WHO: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'john'
- }
- }
- },
- {
- opcode: 'rankusergrab',
- blockType: Scratch.BlockType.REPORTER,
- text: 'global [WHAT] ranking for [WHO]',
- arguments: {
- WHAT: {
- type: Scratch.ArgumentType.STRING,
- menu: 'WHAT2'
- },
- WHO: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'john'
- }
- }
- },
- {
- opcode: 'usergrab2',
- blockType: Scratch.BlockType.REPORTER,
- text: '[WHAT] of user [WHO]',
- arguments: {
- WHAT: {
- type: Scratch.ArgumentType.STRING,
- menu: 'WHAT5'
- },
- WHO: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'john'
- }
- }
- },
- '---',
- {
- opcode: 'projectgrab',
- blockType: Scratch.BlockType.REPORTER,
- text: 'grab [WHAT] count of project id [WHO]',
- arguments: {
- WHAT: {
- type: Scratch.ArgumentType.STRING,
- menu: 'WHAT3'
- },
- WHO: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '717954208'
- }
- }
- },
- {
- opcode: 'rankprojectgrab',
- blockType: Scratch.BlockType.REPORTER,
- text: 'global [WHAT] ranking for project id [WHO]',
- arguments: {
- WHAT: {
- type: Scratch.ArgumentType.STRING,
- menu: 'WHAT4'
- },
- WHO: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '717954208'
- }
- }
- },
- {
- opcode: 'idtoname',
- blockType: Scratch.BlockType.REPORTER,
- text: 'name of project id [WHO]',
- arguments: {
- WHO: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '717954208'
- }
- }
- },
- {
- opcode: 'idtoowner',
- blockType: Scratch.BlockType.REPORTER,
- text: 'creator of project id [WHO]',
- arguments: {
- WHO: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '717954208'
- }
- }
- },
- ],
- menus: {
- WHAT: {
- acceptReporters: true,
- items: ['follower', 'following']
- },
- WHAT2: {
- acceptReporters: true,
- items: ['follower', 'love', 'favorite', 'view']
- },
- WHAT3: {
- acceptReporters: true,
- items: ['love', 'favorite', 'view']
- },
- WHAT4: {
- acceptReporters: true,
- items: ['love', 'favorite', 'view']
- },
- WHAT5: {
- acceptReporters: true,
- items: ['about me', 'wiwo', 'location', 'status']
- }
- }
- };
- }
- async usergrab(args) {
- try {
- const response = await Scratch.fetch('https://scratchdb.lefty.one/v3/user/info/' + args.WHO);
- const jsonData = await response.json();
- if (args.WHAT === 'follower') {
- return jsonData.statistics.followers;
- } else if (args.WHAT === 'following') {
- return jsonData.statistics.following;
- } else {
- return '';
- }
- } catch (error){
- return '';
- }
- }
- async rankusergrab(args) {
- try {
- const response = await Scratch.fetch('https://scratchdb.lefty.one/v3/user/info/' + args.WHO);
- const jsonData = await response.json();
- if (args.WHAT === 'follower') {
- return jsonData.statistics.ranks.followers;
- } else if (args.WHAT === 'love') {
- return jsonData.statistics.ranks.loves;
- } else if (args.WHAT === 'favorite') {
- return jsonData.statistics.ranks.favorites;
- } else if (args.WHAT === 'view') {
- return jsonData.statistics.ranks.views;
- } else {
- return '';
- }
- } catch (error){
- return '';
- }
- }
- async usergrab2(args) {
- try {
- const response = await Scratch.fetch('https://scratchdb.lefty.one/v3/user/info/' + args.WHO);
- const jsonData = await response.json();
- if (args.WHAT === 'about me') {
- return jsonData.bio;
- } else if (args.WHAT === 'wiwo') {
- return jsonData.work;
- } else if (args.WHAT === 'location') {
- return jsonData.country;
- } else if (args.WHAT === 'status') {
- return jsonData.status;
- } else {
- return '';
- }
- } catch (error){
- return '';
- }
- }
- async projectgrab(args) {
- try {
- const response = await Scratch.fetch('https://scratchdb.lefty.one/v3/project/info/' + args.WHO);
- const jsonData = await response.json();
- if (args.WHAT === 'love') {
- return jsonData.statistics.loves;
- } else if (args.WHAT === 'favorite') {
- return jsonData.statistics.favorites;
- } else if (args.WHAT === 'view') {
- return jsonData.statistics.views;
- } else {
- return '';
- }
- } catch (error){
- return '';
- }
- }
- async rankprojectgrab(args) {
- try {
- const response = await Scratch.fetch('https://scratchdb.lefty.one/v3/project/info/' + args.WHO);
- const jsonData = await response.json();
- if (args.WHAT === 'love') {
- return jsonData.statistics.ranks.loves;
- } else if (args.WHAT === 'favorite') {
- return jsonData.statistics.ranks.favorites;
- } else if (args.WHAT === 'view') {
- return jsonData.statistics.ranks.views;
- } else {
- return '';
- }
- } catch (error){
- return '';
- }
- }
- async idtoname(args) {
- try {
- const response = await Scratch.fetch('https://scratchdb.lefty.one/v3/project/info/' + args.WHO);
- const jsonData = await response.json();
- return jsonData.title;
- } catch (error){
- return '';
- }
- }
- async idtoowner(args) {
- try {
- const response = await Scratch.fetch('https://scratchdb.lefty.one/v3/project/info/' + args.WHO);
- const jsonData = await response.json();
- return jsonData.username;
- } catch (error){
- return '';
- }
- }
- }
- Scratch.extensions.register(new nexuskittensgrab());
-})(Scratch);
+// Name: S-Grab
+// ID: nexuskittensgrab
+// Description: Get information about Scratch projects and Scratch users.
+// By: NamelessCat
+
+(function (Scratch) {
+ "use strict";
+
+ if (!Scratch.extensions.unsandboxed) {
+ throw new Error("This Extension must run unsandboxed");
+ }
+
+ const icon =
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAswAAALMCAYAAADw0eQaAAAAAXNSR0IArs4c6QAAIABJREFUeF7svef7BTd1Lrp/HxLAGHAFjE1L+XRzcC85yUlybnpCekLvLaGlnfJHnJwkgOm4gOnpvd9eDqaT5H5KoRoMxjbFpiTPk30fSVszGu3RHmlGWlpaeveX3zO/mdFI71paWnq1tHS2ww8IAAEgAAQ2I/D13/r2/eZCChTwgCf9j7MCxaJIIAAEgEBXCMCQdiVuNBYIAIEYBLg6vzF1z/EMnOwcKKIMIAAEJCEAh1mSNNEWIAAETiLw9Xd/+353ttvt9/vd2dnZbr/f7c70tfm7Uxwx7kfjA8caHQ4IAIFeEIDD3Iuk0U4g0AECvTPD3EQMh5qbRFAfIAAE1iIAh3ktcngPCACBKggop3hghA81wPWBIW8Ijwc+GbHVVToQPgoEgMAqBOAwr4INLwEBIECBABhjCpT5fAOMNB9ZoCZAAAhMEYDDDI0AAkCgOgJfe/cNKopYBxGb2OJDMDGugcfZ2Q6OdPUuigoAge4RgMPcvQoAACBAiwBYY1q8pX4NTrRUyaJdQIAnAnCYecoFtQICYhAwMccqOcWZSUJhs1DgGnhk1IcHPvk9GM/EWA00BAjwQwAGhp9MUCMg0CwCYI+bFZ3IioOFFilWNAoIVEEADnMV2PFRICADATf22MYg46+T0NkmdsZfk+C6Mg7IzCHD7qAVQKAGAnCYa6CObwKBRhEYGGS7J8+2A9fGHwQeBoFG9AEMdKOGCNUGAhUQgMNcAXR8Egi0ggAY5DFbBweGtDZDK/37YKBbsUyoJxCgRwAOMz3m+CIQYI0A4pBZiweVI0IA7DMR0PgMEGgEATjMjQgK1QQCpRD42ru/fW+YQ/uzsaa4NggAD+jHfocsHKUsEMoFAm0gAIe5DTmhlkAgKwLKSTbp3WyaN/wFHtCH2P4A9jmrOUJhQKAJBOAwNyEmVBIIbEMAsciIRUYMdrksHYh93maf8DYQaAEBOMwtSAl1BAIrEDAHhoA5BnMM5piyH8B5XmGs8AoQaAABOMwNCAlVBAKxCBgmGT8gAAQ4IIC4Zw5SQB2AQB4E4DDnwRGlAIFqCJhNe/gBASDAGQEwz5ylg7oBgWUE4DAvY4QngABLBEY2ORSbaauN+/P5g4GPQQD6QakfYJ1ZmlNUCggsIgCHeREiPAAE+CCgs1scDlLz3T1cT90/4AE83OkAR30A68zHtqImQGAJATjMSwjhPhCojIBmkr2jhu0mpuH/uD85mhr4mM2e0A+PQD/0ZY768cCnvAfjcWVbi88DgVMIoINCP4AAQwQQl8xQKKgSECBCAMwzEdD4DBBIQAAOcwJYeBQIlEZgiEv2GGOfYcb1gTm0AgFeE4Yd+iFDPxDvXNrionwgEI8AHOZ4rPAkECiCANjkIrCiUCAgCgGwzqLEicY0iAAc5gaFhirLQECxyZQHKuAADxzgAX1r/yCfByHWWcYAgFY0hwAc5uZEhgq3jMDIJiOGADEErvmFPkAf0vUBrHPLowHq3hoCcJhbkxjq2yQCX33XDXswvGB4wfC2z/By7MeIdW5yWEClG0MADnNjAkN120IA8cltyQu1BQItIwDGuWXpoe7cEYDDzF1CqF9zCCg2ublKo8JAAAiIQgCxzqLEicYwQAAOMwMhoAoyEDBsso1Fxd/wkcuho5jxf+gP+k3ufoNwDRnjC1pRHwE4zPVlgBo0joDPKPtHV/vNw33jFoZ+wAf4QD/y9w8wzo0PNKh+dQTgMFcXASrQKgLzjLJtTYgpw32DAPCZZxKhH9CPsv0Dcc6tjjiod20E4DDXlgC+3xwCiFFuTmSoMBAAAh4CYJyhEkAgDQE4zGl44elOETBp4c6GvGjqxBFcA4/dIU8c9AH9oWV7ANa504ENzU5CAA5zElx4uDcEwCb3JnG0Fwj0iwBY535lj5YvIwCHeRkjPNEpAjpGGQyiPmkCDCoY1JYZVOhvvP4+EEdvdzriodlLCMBhXkII97tDAKxydyJHg4EAEPAQANsMlQACUwTgMEMjgMABga+96wbFJ+/Odme7vZP47Dg7MO4DnzHxGfTDz3mC/iGpf8BxxhAJBAwCcJihCd0jAEa5exUAAEAACCwgAMcZKtI7AnCYe9eAjtv/tXd9+14xQZZRxl/DDAIH4AA9QD8I2QE4zh0Pmp03HQ5z5wrQY/PBKPcodbQZCACBnAjAcc6JJspqAQE4zC1ICXXMggAc5SwwohAgAASAwIAAHGcoQy8IwGHuRdIdtxOOcsfCR9OBABAgQQCOMwnM+EhFBOAwVwQfny6LwOgo2zwG9nu4NnkNgIdBAPoAfUB/yGUP4DiXHddQej0E4DDXwx5fLojAV96pjrIeTrK2548M+a8O55EM/7fX2o+ceQ/39fklwA/6gf4B+3BkN337CKe54OCGoqshAIe5GvT4cAkEEH5RAlWUCQSAABBIRwCOczpmeIMvAnCY+coGNUtAwDLKPkOM6yljDjyAh7uCAn2APlDoAxznhMEMj7JFAA4zW9GgYjEIgFGOQQnPAAEgAATqIwDHub4MUIP1CMBhXo8d3qyMwFffaY+yVqG19qABu4UL1+bgAeBhQq+hD9AH9AcO9uCcp74HfkflsROfX4cAFHcdbnirIgJglSuCj08DASAABDIgALY5A4goghQBOMykcONjWxCYMspjMjAneYPDqOI+cBmSekAvnOR50Avoxcg017eTYJy3jIp4lxIBOMyUaONbqxEAq7waOrwIBIAAEGCNANhm1uJB5Q4IwGGGKrBG4CvvvH5fnwMBJwdOjhMnB32EPsrUR7DNrIfj7isHh7l7FeALgGaVZY4LGO8hV8wD4ffDDszYgQdhUyDfQbnzmsFh7lwBODZf5VQeRxJbQ2tZcW0QAB7G44Q+QB/QHyTag3Oeejv8E44DdMd1gkJ2LHyOTVcb+7QfBAYSDCT0AP0AdqBrO4DYZo6jdL91gsPcr+xZtRyxyvAMuvYMMENEHg8wBUGmBLHNrIbrbisDh7lb0fNouD3Seu9u7Tvb7XDtuI/AA/qA/jG6k+gP3fYHxDfzGLd7rQUc5l4lz6Dd01hlMKxgWBGDgRgM2AHYgdN2ALHNDAbvTqsAh7lTwddstsp+McsgW+YoxCDhvmGWgM88wwb9gH6gf3RjHxCmUXMU7/PbcJj7lHu1Vk9jlW01LKOAa4MA8DBMK/QB+oD+AHsQtodgm6sN5V1+GA5zl2Kv02gTgoEfEAACQAAIAIE8CIBpzoMjSllGAA7zMkZ4YiMCiFVGbC5icxGbi9hc2IGSdgBs88aBGq8vIgCHeREiPLAFAZ0F45AsCAvsWGDHAjsW2GEPEHBUKuAKbPOW0RrvLiEAh3kJIdxfhcBX3nE9DiABoVSSUAJhCf2CfmHhYtYOgG1eNWzjpQUE4DBDRbIjgFjl7JCiQCAABIAAEEhAAGxzAlh4NAoBOMxRMOGhGAR0BgzNeOBkgW5PFoD8of/o/zh5idHJU+c87Xb4OTEDOJ5ZRACKtAgRHohBAKxyDEp4BggAASAABKgRANtMjbjM78FhlilX0lapeGWE0iGkFiG1CKmFHYAd4GoHHgymmdQvkPgxOMwSpUrUJrDKREDjM0AACAABIJAFAbDNWWDsshA4zF2KfXuj73/H9UO6OIQsImSRUcgiQuixhQBbCNQR4Yd0nrDPx/YZWTS2+wA9lgCHuUepb2wzmOWNAOJ1IAAEgAAQqIoAmOaq8Df5cTjMTYqtXqVdZtnWAgcR4CCCUgcRKB2DfkG/oF+jzUd/yNcfENdcz5do8ctwmFuUWoU6m4NIHHXRa/C4HkQBPKAP6A+jZUJ/QH9oqD+Aba7gVDT4STjMDQqNusqhLBg+w+zvjsZ9g0Bo1zjwAT7QD/QP2If57DLU9hFsM7Vn0d734DC3JzPSGg/xyjZflP06rs1IDzwMAtAH6AP6A+xB4/YQTDOpe9Hcx+AwNycymgqrWGWaL+ErQAAIAAEgAAT4IAC2mY8sONUEDjMnaTCpi2KV9/v97swGE5yd7XANPKAPh8Vz9AfYA9hH8eMDnGYmDgmjasBhZiQMDlUBs8xBCqgDEAACQAAI1EYATnNtCfD6PhxmXvKoWhsdr3yIRdWM8pDx/sAw62uziw33gQ/0w54Qgv4x2gPYB9hHWePDOThSu6pfwunjcJg5SaNSXcAqVwIenwUCQAAIAIEmEADb3ISYilYSDnNRePkXbg4iOdspahl/gQP0AP0AdgB2AHZg3g6c87T3wGfi79YUqyGEXwxa/gWDWeYvI9QQCAABIAAE+CAAppmPLKhrAoeZGnEm3wOzDCYRTCKYRDCJsAOwA+l2AEwzE0eGuBpwmIkB5/A5zSzbgybwN3zUWOgILvx//mgu4AJcYE9gTzqxAw9++u3wnzg4NIR1gMAJwebwKROGYS1aqEa4bzwf4DOPAPQD+oH+AfsA+4jwDA5eDV0d4DDTYV31S/e//Xq19jj+QkyQfQL355ki4GMQgH5AP+aYRPQP9I8O7QMc56ruDdnH4TCTQV3vQ/e/44Y91oqxVoy14k7WiocVJLQXdg92j8ruwWmu5+NQfRkOMxXSlb4zMMue3dwfAjN0tWbGVdw3sAAf6Af6x/GKAuwD7APs4/H4AKe5kqND9Fk4zERA1/gM0sbVQB3fBAJAAAgAgV4RgNMsV/JwmIXKNsQsIzvGPGMKXIALIhlOxKZjZZ9qZR/fERBJBKdZpmMFh1mgXMeYZT+mANfTGAvgATzcmCToA/QB+nCcRcnPioPraZaceTzgNMtzruAwC5PpfW9XR11jqwsIMRBV6AewA7ADsAM17QByNctysOAwC5LnJGbZjhS2fbh2duk4MwrgYxCAfkA/5tJOon+gf8A+bLKPYJrlOFlwmIXIEswymJSaTAqYPOgf9A+MPuzAvB0A0yzD0YLDLECOs9kwfMbQbyfuTxlF4DNFAPoB/Tg1OkA/oB/Qj7D3MNM/wDS372zBYW5chopZxpoh1gw3rRkiJgMxKYjJOaSJQQwKxpNy48m5T78dPlfDPheE16jwkDYOadCQBg27W5EOEXYAdqA9OwC2uU3HCw5zg3JTzvKBVp6tvZ/kxn8I96dJgYDPFIFT+vHgp71nVY+5/x03DO9B/6B/sF/hboT+0Uf/ANu8aiip+hIc5qrwp38czDIYpRKM0jlPXecIp2vw8htfeecN2D2F3VPYRYldlOLtAJjm5fGA0xNwmDlJY6EuJl4ZIylG0vUj6VqGuHY3uf8d12OmVGKmBHsCvYJeVY3pOPfp74UfVnuAifw+BBUJVO3HkDYObnKqm9yqcxzb177yjhvg7sHdg7tX1d2DXU61y3PPI+1crNWv+xwc5rr4R319yizbV3DSBE6amJ408eCn3R6lT1IfcuOkkTUE9gH2ASfRjLaOf38A08x/ZILDzFxGYJbBYIQYDOkM8pauqZznHMwPAqDQ/6BHfAMBz0nYhNzCihSY5i1Wv/y7cJjLY7z6CxNmWeXFOHNMN667xANOcnp30swz+kuX/WVw9SB/1vLnaNfMitVhykyoP2Ca02081RtwmKmQTvzOeCDJyPDYIvy0Q7iepiGSiEcKk5Koal097jLP6E8GAYn9xU1bh/bxsY8cHeMUA0hlP8A0p0iF7lk4zHRYR38J2TCwCKqGuN5jkqM7zMoHJwwStg9i+yCCeLJvH2zdQV4yLSWz94BpXkKf/j4cZnrMT34RMct9x0yCSabvkC3ENiKWum+70Ir8Yb92u5z2BEwz/Xhw6otwmBnJ4763X7c3RNfZIebSTvhxbWJQ8+PBgcVVTKd0JoZRNwtWRbNF6H+wP7C/0eMPB/vJ2bbc//brN/enc5+BPM1cZAyHmYskdrudG7fMqFpNVgUOaJNiY1HpaXo6FlVCJYAAGwRgW9eJYq1dwRHa6/Au8RYc5hKorijzvrddtx+ZZcso4++Q3cDuUvb+wnivUDa8EoXAJLtGQP+W9BP3D9l9gJ/DNLZn12Fno0xG1ENr7Aqc5ihoiz8Eh7k4xMsfALO8jJF6AkY7Dic8lReBtcxQ3lqgNIkI1LBpsfpco24SZXyqTbGyUGXAaa6vHXCYK8vgy2+7bo+cEMeJ8R/89L5Prauslvj8DAIqHrGVzVeoZ71NgufCdsF+rEAgxr4gnnkFsBlfgcOcEczUosAsTxEDo5GqQXi+BgIprFCN+uGb5RGArSqPca9fWLIvYJrraQYc5krYW2bZfP5sp9JjjMLo4xpMTCXlw2ezIKB3wHfcf3uwV1jpytJVUMgKBE7Zl3OfcTt8txWYbn0FoG9FcMX7PTPLYGZWKAxeYY3AEiPEuvKo3IAAbBOUgSMCIfsCppleWnCYiTF3Y5btpyUf3QoWmVjB8LlqCNx3YJwl92ezHsbnqOUt9YFtqtZV8OEVCMzZl4cgR/MKJNe/Aod5PXbJb2pmuYPdOA9++nuSscELQEACAve//YZ6u82we/h497Bnb8EiS+hlfbfBpKUbZ64Iz6DTBzjMRFjPZcPwGWZ/vGvtPhgbImXCZ9gjYNkglwFtvX+3WH/YJPZdBRVciYDLOCN7xkoQE1+Dw5wI2JrHJTPLYGzWaATe6QEBnwkaj3a3R7zjb+4VN9ijHnoW2ugiYO0MmObyegGHuTDGilku/IkqxYO5qQI7PtogAi7b3GD12VcZtoi9iFDBwghYG4OY5rJAw2Eui+/uvrddf3CYbdCR/WCb14hPLqwwKF4kAiZFlGtu2+z/o3Dq1R82SGQXQaMyIHB2dgafLgOOoSIAbkFwpbDLYHAKKgmK7gYBMM3bRA07tA0/vN0PAnCcy8gaDnMZXB1mud08TGByCikHiu0WgUkWDYuClDxtBdoDG9RtV0HDNyIAp3kjgDOvw2HOj+lOM8v+iqX/Hcb3weQUUAoUCQQcBO572/XTCI2G7IOuakH7BfuDrgIE8iAApzkPji4XkLfEzksz6ePs0dbt/AWT07niovnkCCi2Wc2sW7QXJeoNR5lcBfHBThCA45xH0GCY8+CoS2kxZhmDVEYFQFFAIBGB3uOaYX8SFQaPA4GVCMBpXgmc8xoc5u0YDs5yS0wRGOVMgkcxQGAjAj0yzbA/G5UGrwOBFQjAaV4BGhzmbaDNvf3lt16n1lYXj2bNnah/TXnnPuP2/ACgRCAABDYhoNlm7yhnaddglDepCF4GAlkQgOO8DkYwzOtwm7z1ZZ1rmf9Ih8Eqg7BRBBAoiIAJ0Whk5p1Qz3Of/p6CqKFoIAAEUhGA05yK2Ol92umldfhGC8wyGOUOFRNNbhYBSUwzJunNqiEq3gECcJrThAyGOQ2vWWbZxi5bZojLNQarDcLFq0CgIgIu08zFnqTYN8QoV1QefBoIJCIAxzkOMDjMcTgdPfWlt6r0ceFfwTSl+qNL5YNVXilYvAYEmCCgczUHfkv9v9Z92B0myoNqAIFEBOA0LwMGh3kZo9knuKaQA6u8UqB4DQgwRKCVtHOwOwyVB1UCAokIwGk+DRgc5kSFUo/ruGWGP7A7DIWCKgGBjQicYpo3Fr35ddiczRCiACDACgE4zWFxwGFOVFXDLPPaxQ52J1GIeBwINIYAt+wZyHrRmAKhukAgAQE4zfNgwWFOUCLFLHNLHgeGJ0GAeBQINIyAYppr2x/Ym4YVCFUHAgkIwGk+BgsOc4ICaXa59oh1+D5Y5QTB4VEgIACBmunmYG8EKBCaAAQSEYDTPAUMDnOkAtmsGBz85YfgpL5IqeExICALgS+/7XrSgDAwyrL0B60BAqkIwGkeEYPDHKE9+iQ/FYwxoHW2G661B+1c6/LK3QfTEyEwPAIEBCNw39tvKG5/EKMsWIHQNCCQiACcZgMYHOYFxVHMcqJuFXsczHIxaFEwEGgKAcU0l/rBzpRCFuUCgXYRgNMMh/mk9gbTx9kkGaG3M9/Hsmi7RgY1BwKlEAimm1tpf2BnSkkK5QIBGQj07jSDYT6hxyF2eeV4NHwp5X2wPTIMDVoBBEogMMc0p9gXWyfYmRLSQZlAQB4CPTvNcJgD+jxhl/0RiOgajI88Y4MWAYGcCGiWeYM9go3JKQ2UBQT6QKBXpxkO84x+G2bZ5sOwD9Beg/Hpw/CglUBgKwKGZU63Tw95xnu3fhrvAwEg0CkCPTrNcJg9ZbfMcvrwMw0I3/I+WJ9OLRCaDQRWImDTzcVM72FfVoKM14AAEBgQgMMMZdjVzooBZhlKCASAwBoEYjJnwL6sQRbvAAEgMIdAb04zGGZHC9y45S0MsSpyzfsYzGCUgAAQWIuAmzXDtz9gldeiiveAABA4hUBPTjMc5oMmfOm2a/fjAST24BG6v3CWYZRaQOBf/+b5upr7veouo/lYuv6G77u5heY1X8cvv/U65yClsx3sSvMiRQOAAHsEenGa4TBbh9k5oGTDpnNdWur7GNTY24PuKmgdY4qGw5nOh7J7dDbsSj5cURIQAAKnEejBaYbDvNuZuGV99LVilK3Ha6/9v3nvY1CDGaqNgHKOB4b40A9qXn/D999SG5Kmv6+cZtiVpkWIygOB5hCAw9ycyNIrrJxlywhT/8Wgli4vvLEdAUr2eGttwT5vRRDvAwEgAARoEJDuNHfPME9il20whWaaB6p5jAnMeB/OMk0HxldGBP7lr5+nL1JDhrg8/41gnqHOQAAIAAHWCEh2mrt2mGulkIOzzLq/i6rcvyon2U4ABf0F8yxKTdEYILAKgTWrZbAdq6COfgkOczRU7TyomeWMjHEsI/2QZ97eDkioaZMIWCa5yconVhqscyJgeBwINILA1Bn2EiXaPUdDW7bf/4bvw96JXKoh1WnukmEGs5yrW6AcTgj86988z9m06sReUAfnV/geBjtOmoi6AIE0BNYwxWlfSH8aTHQ6Zu4bEp3mPh3m267b73f7IZbzeHw/2+W+/5Bnvneb9uFtIBBAwDDKTsy90uyBgfH+b58Teh+MM7oJEOCPgLsKxmWPhEVtrj7I3JOuU3CY0zFj98bALgf8iKMVHv85+0DC+wjDYKcGIiqk4pNDatj7/zHAiVBxNEIQAoZFPlgmf8Le0DVWs+KVUprT3BXD/KXbrtvbDmsY5JFbLnkNdjm+g9V4UrMd2mBbe24Z2rLX3/gDt65u7r/81XPJ60uNz9bvbcF3tWDwIhAAAhME7MT+FIOr3OgW72NFa1nZJTnNfTnMzml+AzXnM8aZr5ERY7lDUT3hM7K+gfZDc2rdP2WElXMfWsLkUn9u9cOgRtXD8B0gYBAw2Xl8wsEJFZslKNq9j3jnsObDYW7QKnzxtmvJDyh5KOKWq2iKZl/xAwIOAmCboQ5AoDwCPWXomUMTk/N5HZPiNHfDMOtwDMLd+2CWyxtn+4VaIRVbQwbwftmQlzl8v/H714fB0Gk0vtQLAu6KEdXwVMKpq9EOKrzWfAd7KI57sASnuQuHWTnL06wXfhaMvNdglssPd2CRy2Ms9Qtgm6VKll+7/uWv1WrXjMvFNEtNqjPdWvuCmw4LZRlKxZOfBuerERzmfFgWKwnMcjFoSQuebnI7zqKGdBF95V3eKm8wzaTdV/zHemBYfecPWXri0t7DaR67f+tOs3iGWcUuU1prsMv50P66jkU22UxsFhP3r2VucB/4rNWPB2zIVJJP01FSSwhgdaslafGpK1a2jCxadppFO8wmjVzo5x2lefRY+n3kW95unDAYbccQJaQhgIEsDa/enh73SDihFQ3lDdbEAuqrPLXqeaDBNsNhZmk/wSyzFMtspf7lr56j7bkN9VN2DdfAg1IfvvEH3txOh0FNiyKASXtReFH4brfreZIOhplhFzjNLuevMNjldEwxMKVjhjfKIdDzIFYO1TZKxh4J7IGgzKKl9mD0bG9adZpFhmRodjnh6OpJR7H2PeH9hz7rvW2MCkxq+fW/fA6TmqAaQGCKwAN+EExzLzqhnOS52PfQngj8f36vBHBZj0uvTjMcZiZW9ou3XXc4oMRuFrMr/WWuwSzHCV45yTrUwhIZ6iRqXAMPhvrwAIRnxHXqBp/CqlaDQhNeZTjN7QhYHMM8xC77e/YKXSMrxmllB5vcjjFATUcEwDTL0YaRSbYT9rPdfr93JuwzefhxH/gMhE55/ejRaW6RZRblMJsDSmbTxG9N2zr7/kOfebucUSVzSwZG2TLJ+GsGIODQDA5wmjMbBcLiwCQTgo1PZUOgN8e5NadZlMP8xbccci7b7DFWjQtcg1metxFf/8tnO1OWggLQUxiUbxAooODAd/eAH3xLtoEQBdEggBUtGpzxlXII9DRRh8NcTo9OlmxCMdac+r6Okwa7PBUHBqpKio/PFkWgp8GrKJCFC8ceCewJkbQnppc0l3CYCxvGueKDWTF8AtL3p1feR1YM31l+9ribz94qFDM+fBnlm/kh8DYIFNQHMM0VjHrEJzFJjwAJjzSLQC+T9ZacZhEhGSZ2eWSKbZob/+jkHNdglkf7YwYsn6H3PTjcn658AJ+ph9uGfvQyeLXgXdiDjrAnAHsiJO8J6cXmwGEmtLpD3DLRN8EuG6C/9hfPJgyAOXbL1wXSoBzgti5w6wE/hHhmIhMb/Mz8BB0avU6jgVsLuMFprm11pt9vnmGmjF3GRr/dzmzqm1kC9/XaXyLH/SkCwGcaUtKAfiA0o87ghdCLOrjjqzwQ6MFpboVlbtphBrtM26G//hfPLpKej26rJhhm8ErbeCUwzXQ2Z5icz30SE87mJpwTMUJ+SfLrYbLegtPctsN823V7Kheo99hl5SwjBmMmZBseaFd60cPARecSz39pGnrhx/zjen6Jr+Cu19klRXyPete1dNsDh7mg5f3CW649HIFNM173HLv8tb94VkFJomgg0BYCD/yh29qqcCO1xQoWFf2D77TKc0he4YLDXMhQB9PIFVrb79VZnhvAfH6njRwH4axjqD9yeLhmI1a/JQ9chcx2sFgdegHCkpqwxPcaTMsJlpnaOk2/12RIhopdppwhPuxjclT2AAAgAElEQVRZ760rpQpf/9qfPyspxuqoiohRA36nrEvj+gGWOY9RQrYdmhVSyvGyEG+FiMBDROADBWfs4c4yN+cw641+QwJOG1N6FkhIuf1+j5kxDLO839l81vh7BjygD0f9AU7zeqcZeyKcE+3hYSJGJHFGI5lp5uw0N+cwu7HL1lxbe1Piujd2WTPL+AEBILCIwAN/GLHMiyDNPIA9EWtQwztAYERA8mQdDnMmTUcauUxABorBQFYWX5QuDwHJA1duaWlW+fBDyDJCiBsMIWalv1JDM+AwZ7K8fuyyzyj7K1tb7/fELh8zy40HmR7tIvKVEO1DkPX2IGuwzMvGfXYiju6H7re9+4WVrxP9kjhhh8O8bFMXn5iyyyWDMFRVznYPfdbti3WS8oAe0BJjqPA8YhCxC8fskYDTHLaEyrZgDwT2QGBPTJk9QVKz9XB1mpuJYf7Cm9VmP8cwF17T64tdfib8X/i/8H9XnkvzoB9+q5S5c7Z2YC9ENihREBA4iYDECTsc5g1K74ZiUMTA9ZR3+Wt//kzPVfQRRqZiZCpek6k4tGQhT78eCId5Yt0nK1YUBlt9ozCBgvIPwwTkaRBgpG8Iy9jgXCa+2gTDrDJjHLWrYIxSL+yycpYRiYE8qMhqtT0iCU6zsdDHE3BYGFgYWJjtFuZ0P5LGMoNhTvTk7eM2dplqQtcLu4yBDQM5BvJ8A7m0ASvVXA8HHaFboVvl61al/Uwx5UtjmeEwp1rgw/ND7LI6208fWGIjCMpc98Auf1WFYbicffk9lPge8B4tgEB96zmOGStVYvwurDg2vJdFmg3i6DSzDsnAEdgrZxkLrxl2GT8gAARyItBjWAZWqkCpg1LnQalLsz9wmBNHJx27TGiPHvbs9ybWsL3Hv/pnz2h4Ds3DMGGAgBzmOMUH/cjb2jMIG2p8kln2FwB9q4P70wVT4DPN0gP9SNYPMMwbjFnkq2wZZvcIbKrhWXo4xoRZpgoKx65qdruque3yllKfB/5IH+nlTMo4xBiJjjGCfB0Xqp0YMkksMxjmSC9exy1TUsu7s510dtkwy/gBASBQEgHpLPNX/8yGc/WTNhBpJZFWMpxHjlfa1QcJm7Rzc5pZMsyzaeRKjnK73U46u+yGYtiTt+xAgGtzEhfwMAMj9GG9PkgbsFyz665Q2T3YwwLSYQkd14cFJeChQwqgD7T6IIlhVsjBYV5wfA27rH60DIZkhhnscuHZFooHAgcEpDLM2PtAuJkGZ26uPHOTKniT93ck2SA4zEsO89whJYWHY8ns8nSgM0CODKIFdhqjhfuWYQQ+VmPcmFXoR1g/JA1WVvv15j67CQt/zWYs4AAcmOqBpFUuOMwnnF83dlktkauB2TLNJa8f9uz3FXbJ6xX/1T99xiHSwNnlpzu6xdfJa63xNte4D3xM/4N+GDMU338e9KNysmVgdaqe7caXgcBaBCRN3Dk5zaximKnTyFl/XGo4Bga7teYG7wGB9QhIGazMBj+EIiCNJO8QBMjnWD5gmdfb71Nv8nKYkR0jq5Q1u3wY8Eoy9MpgoXy6FRHgzVvfHvSjb8/aj6kL0xNtP+2kXwncN35a6Ad8gE9F/ZAyadcLnGfu9lFqazj9HhuHeTwCm/ZcDanxy0MoBgz6PAIY0DCgFRrQWh6ssCpVd0DG14FADgRatkF++79423W78579Pha+KotKKIDuffM1+3FhwTJIoQXBfPelxi9/5U+fnqPfoQwgEETgnAUmtVcdXMKFq0qpSbZdKcLfMb2i3eSKv+PKDvSDt35I2kfxxbdcuzvvOe9n4auyqMSYSo52KBEbu+yEYiAGETGYOWP81hhiNzSoF31cgxOt9Zt+7WhFCieBTldggAfwcL2lBvShNRsUsn9ffMt1+tZ5z6nPMrNxmGvMWM8Tmh2jV2avpsMh/ds5WNOe9DIHXlQ69dU/fTq29tFGAgJv4F0823ZLNmjJ1n3hLdfuzmfAMld3mGfZZf/odh/NTPclMsxq8MMPCOREIOcmtl70MydmOWXplzWmnYQHU9yDQbKJXhaYWLRTCsOsbBYXlrm6w6xil0sOCKGyxbLLf/I0ta0UmfVxskAWPSjBUmimWbh8SuCW2072xPjnxg7lAQHuCLRgg2IxVAyz+tVmmas6zLVilxXwUtllROzmjNhlQRRUI75KGdweQgBaYJh7YftjB2U81x4CMf2sZz2PwacFqVuGuXYsc1WHeWSX/RiL8tfnPfu9LehJUh2/8icqHAMuM1zmPGu/5zzxHUn6l/LwV9RKSLWpQB58TtX/nCfyzsWsbUWhtHpaD5C2EfgW0q+1E/nJikon+rkWqxRbTvGsZZhrs8zVHGawy3nVTBmD8m4A3PFepiOlmQnp+loavy3Ww2X4Q+V04k8EYUT7+fn7ufpUT/qfC7Mt9ibHuy7DXJNlruYw33vrNSqRYRUP7LznvC+HDFmVYRg7/IBAHgRKssuqhtL1tTR+a6U8ZfZtKeVX9Iyhx/cMAsA7VR9yr9hM7Y9ceXC1Q6n26wtvvnayoFcrlrmew6wPKlGJ0EMLmzYxeP770sIxxlAMDEiUA5I1RhLxzz1A+QayhwGL22Bll6S1e3AijyzuAx9O+lEqrCAUoiFJ/0thl+rwbn3+C2+5bsKv1srJXMVh1rHLlda2JWbHkM7Wbe1sKe+XdnJakVVpHMAyp2jl9mdb0bvtLUUJ0hAoZYt66BOlsKPWMZ9hVv7j+c+lP/2visOs4pcr+csis2P00PFLdFAuxoSj/Ciw4djunHpGgWFMfXtI4yc9TWGv7SvNkGobJDgNa2n8YuxPjmd8hln5j+dXOPmP3GHWscsVf9Lil+//46fuzs7Odvv9Hn8jcODixIS6ABd5UuCkBivJevvgH3tnRUtnPm0nJQ2c5DtJ2oH64iRq5ZyUtkPS+0dp/KgMnGaYZ37ULDO9w/zma/cmxH40iZTX8uKXsdnvVKdt2WDUZGApcKvZPgpDT4HhqXZIx5dChhTfoNKTFvWhNDYtYpKik6XxS6nLlmdNWjnjrrr+IjXLTOowD+xypU2p0thlpTyGkTwcnGaTjhyYVqugPd6XYigMS/hUsyJLKF8K/JTuagMoVH9rM8yawfc2VVubMBy0iPuTzUSl8aHoV6mOiasnpdufWn5pvO7/k6eRyj+1/Vv7b2n8UnVt7fMThtnzHylZZlKHuWbsslI8sMtr1bWN96QYhxDalGwIBZaU7amhwRQYctCVGti28M2a8t+CD6d+WRpDTm3dIrPQu6XxK1HnuTLnYpit30zJMpM6zIhfzqtelqHLW2p7pdVm8qgRo5A7BaYU7aCWjfs9Cgzn2icd15oyXfp2LZkv1WvL/Zr6RIFnzfZtkUvMuxT4xdRj6zOhGGZVrkiG2RxUYtde6f9KY5eVonzlj586n53PX+L2z4cRcl+KMVhrTErK/5wnlt+wVrL+kxCWSvpfQz8HxszGXFgLMVwftA33TSxQBnwo+spaG5H7PWr9omBIdZuE9g8K/HLr2CzDrA8uCfuNVCwzGcMMdjmvWkmeFS8hVcMRWapTrfsl9aA0ziXrXksetRlm6ZhykKuqQ+m+waWdoXpQ6RkFzlRtqSFTCvwo2nWKYaZkmUkc5nvffO2+dh5JaQyz3uxX52Txqt89h0GqLgoDkfqNEvpQ2tgGGWYhel0aP19HJkzZsKtP7RalX9Grbe9LfB+2Z94qldI7KrxL1Z9Dv+uFYVb9nSI0g8ZhvvWaageVHBJcp/of7J+//4+e4qZNGJeUJgPlkFZBxP0H//i72MulZgU1U5JR/qXxvv+Pn3JYEQ85dG3rb2n8XF2TzJLV7FPUk56abd367dw6SIV97npvxTHn+1QY5qzzXFn3vvnaRaLuAoKT/2gcZssw2yZbxoPoWlo6OckdPNTxpHT80oYlp26UxjxnXUvjuqb80vjZOoWYens/tBKF+waBOXyo2M01esX9HauPW/UL/Sesn+7pb6f6NxWGpXXShGQcWhrwH0UwzDZ2uebJTfIc5gMz51skwdcP/jGwy7FGaWBuN+pDaYZUr5K4HsvG+g74VMrz7n+/NH72e2bi4WVs9Tf14f7UNQ7gI8XBiLUVJZ+b1cujTZZ+puHD9X6/Q/85EXQZ2b8l6bMbxxzyJ0s7zcUZZmTHyG+SpobIll9zSqLqUO77kjp9fm2YL1E7zRsPGy49SZkyzOX0xyBEX35p/FSr3Nh1YfMN0vkPGOVylinEOC/pK5XdX1u/pfpzuE+FYTntGUtWuZiX9iaUzpZR1GG+R6WSc+b1NRTo/Oe8j0KWZN+474+eshjLI2kzIBXLQCZA4g+t1RcK3BXDHOCXRPz/3MIx9wNDX2U6sHU6xuN9Cj0n7vJsP7dGXynk49ohafQTBX4UCqdimGP9x5KxzEUd5tqp5BTA4sIx3M1+sRrEZIk6WuMP9ZXS2SkMSugbk82hCfpCwo4iJGO1ahi5zuURHpe0cT+MjyTmbbUSVXhxWPkK5gGf6i+FnCabpUMhI5H1Pc7rXbc/UuBHoUZLaeXcOpQMyyjqMPsMs898+uN3iftgmOc3tbTA7JVm6Cg6OodvrGFyKSYra+rVgt4qO1YaP7tyII0RK92e0nLh0N9bqEOs/paWl2W96QO2aFZYSuNHpWtzWTJC/mOTDPOUXa6zT1sau6wUxF3WolLWGt+R0tFrYDf3zVS9KY1/an244Bhbj5L4jen4hCSsJoohKymTWL3AcyMCMXpceqVrmp5VXn8qjR+VPk+yZAxBqWHK9fznvq8IGVykUNWMe25RR2HXVcDznystfvnJR5vhRQR7zujJuT/+bqq+2M137vujeP0pjf99f/jkdpc+IuxaSfwMMx/i2i2suG+RKCmLboxHwYYqttndlGu6l9Hf0quM2iZOHBVZ/ac0fgXVYlL0vbeqo7Hj/clSLHMxhxnxy/lVac2midJLnCXKBxOUX3dsibFLkKVlELskW0K/VJmll2BL4Sedmc+t+aXkkLuevZcX0uvS8pPen0rjR6W3KTHMqk6l4piLOMw6djlMcBiMC9+XFrusIEthCFtmns/9CZ7scir+LbejNCsHhjl9qJkwcZF5WIOGVvj7Upi1dC1p9405/S4tx2OGObBJr9E85qXxo9K2gWH2GRTfj3TuX/C892f3b7MXqOp7763X7o2hDv38tA3+c9vvn/ec91LJkuQ7qmOfOWsSakairv2/doBs+f6DGYVjWNwtnmvx5TTTt0v6p/SndH19XLfiy+39EvhJZ8NyGdIS2Oeq21w5W+TaWltjcLR4ULRtC/Yxban9DAWGFG2cMsxx/mEJlrmIwzwwzPX85Z00hlkzcp38ajOzJbGu3TarQkttLF3Ppe+3ruq58ZOOVw5558Y8R51i+1vOb7llccbkVJuVvlPUXXq/osCwlO665WqG2f7i/OVdEwyzcpYTYrOLRQ5Iy5AhfQnbKkLpUIClzk2Fc+12KhyGwaLCpsvJQFUnic6xAc6Y1zKnfPWKwH6v0yqbFcgzXHt4cGPSlH5zlBen1bslW0xxf1jpEtq/pMh7Lq1cjJ+Zm2XOzjBPsmOEZgSFD9IQlx1Ds8ultynxKL/WjPi+P3zS5qOk12TWrNXeicOs++lU/qXrVQvvcRAuq+858ZPOgm11jHJivaUuLcqJC3ZbcN/ybosyS2mvFPmeZJhP+JO5WeasDrPKjMHhYAGEY6R0KV7P1ujgtY1mjTafWiqmqE9tzEtrfS4MB6bSMayaucS1np/mZPLX6sT9f/RkEfLIpbNrcazxnvT+xaF/5JDrWoZZmcmcKeayOsyaXR5+ZRmcU4yrPIb5Sdogz52Ea/8v4f5DfvK3cvStpDK+/AdPMkunlfE99yfo2z46zVP9OpdADj7utfHP/f1c8hyZeA5URMwiKE09azt3kuWSS3eTDHGlhyXLUeexZppxKlXc9956zeoV4Jwsc16HWaWTs79K/rI0dnlcOq8EKKFAaxjqkemsj2+N9rtOsw0poTCyCMlYHjKks/DLCMw/QaGfobr1JpOaWK/Vj5T3pMtTivyCaeUi/E2WDPOUXU5R2bzPSmOXQ0yczyxLuKZmmJXTxg232k6zwoNCDtL1eqsc7/uDJ01D4n0z6c/vOrm/Fde1o42Z4Dm/zvCvhftaecW+dyTXkAPWaP+SIrdJDHOscJ3ncrHM2RhmnUru8Cu8py94krj6vDSGWfqSkQ2toZ4Jc8a1ppFTuFB8nzP+SWewBvL8bNVn6czXijGv2vIyZGGktVWn18i85Ds9yFWKzFQM8xb/Mle2jHwO8y1XqyjQYbe9f5CAf+JUqfsSGeaSRoNL2RSspttWxXBy/lHjQY0Fd/y34rFFfopddo99qklAKBw4fJ8irt6XudVRDu3npA9bdHtrv8r5vnT5SpGTkvkYw2yWdlL9R1YM88mjsGn2gGh/XJqzrFk4Qvyc+Q75dylYTWtsW8CVEo+cg1BsWUPIgVD93iK/iX6Glojrh9yvyaI4qkdC/bdgGauP7nPAfzmLKbVM1shx6Z1JOEaCPupyG3i+xiRzCfO194cY5g3jRQ6nOQvDbOKXQxrkt9AfAfLdP/+5718rD5bvmRnwBg2p6QEn1PshP/nbpPi3gis1LpRC+PIf/JzDXfLJvpCjv22Rm3TmPVXHqFky4J8mIWr5pNXu9NPSZd2ybFzJmfjl7f7lBc/7wGZ/d3MBqmFgmHN247EszcIdfg1MaLV7vqa+1DNhu+S9tr6UBAM1NmU0eVpqS/hT67N05j1hHr2j1v0WVp5S8KPiS6jllMtGSZd3q3Lx5avjlzPwhiwY5rtvuXqvj2rd7c2RrRX/XiCOYVYsnHyPeQsjl2o8DbPZxpKaGvAosUnFcu3zWgaCZ4BrZTboJouo4bpRs9Ts2Ii90mpELZsOan/LeFDLa63tcd+T3t/W2qEc2OYsQzHMufzKrU7zZoYZ6eRyqsZYlrtkbSci1pBLuqY2tArX1vCTYvisdhuGeaQMWpPHUn3Xystl3jMQKlQEY5HvULFjOr1hF4FvOXK/nCb6qG351pFXen9rTR4heW5NKeeWW91h5sIwS9vw9+Xf/7nyFo7BSLHWuVhrLFvE9SE/RRvjvRbb2PcmDLNAz3CNvKYM58wKiA+u4DzAVDbhCHMXY8H4zq6wZdIvKtnF2ppTz0mX/xo7lAPX3GWAYc6NqMq//Nz3FSi1XpFuZx4ZLVMfSdfUBnbKMLeDJxXjRqHxI8PcDv6GEY+r7xqddvWydmhbze9T6TnwLhdCuUb/KeyO+40e5N+CHGLkLoZhVuzyfDD22c4coRZa68p/X1L8smJBxzyDRqWkXlPOgjW7fPi1hiclTjFGbMsz0vU7VVauXm7BtfV3U3Fb217gvRa5+PeoZBlfo+mTPegAdxnEyu6eW6/xYoG2+Y8XPn99toxNMcyIX44Vedpz0pes7USKukO3jis1XmlaG/9063JYCnpNkVNrm1CdGWfWPMxUbBjwHhiDrPKb28Sb0g/irUeeJ4fQvJFBKY7H6jRShjFLqh9Vf8ojjdOl5GSY1Ze2xDFvcpjvdvMva0bZoZQJryWxy0qgLgPn7w61PSe0a7Sl+w/9qd+h6G/DNwyrMD0pqCV8qfEqJRzp+v2QBL0ema7EETF1BGX8PJVjNWUV+8Xb9Gua9lPJNtVWffn3fzbNAyXCa2zHNvlwxT1VTur5e1Qe5oz+5IUb8jGvdpi5sMsKUJHxy2lZfVKzALF4nrJTTwbL5SxJLPAZjItTX0rM1hi3mHcGhtk+3LA8dBO8+sfKSDrTvsTEU640tbjZNxY/7s/F9ocY25HrGen6AIb5tKasZZlXO8x333y12jWQJaH01nIueJ6cE/6+9Ps/a+BwJpiz/kTj91NYuBxGUuE68c8axU8Cy6zYHcn6HSsjjYPY8w7jkvxQ2AHgXH+YppBzyjgxjLNC+x83vFNk4z57zy3XxBmSREO6lmVe7zDfcs1+zl/2Q4LyHXw9H8Kjyr9AUIaMcakoUQMaG3qpWQdJuLZuDN3QmPpDef5+FiOfsD7WsKCuJaf7fgxOawda972pvtG1b36k7/P71PZ+SW+k9z9ueC/JI3RfhWOUsE6VGWZ/1yLdtbT45S/93s8WmVFx86djWbi1Hc1/TxKuD/1p2tjvXDKw5Wi2P7+fyqbfxMhn0MfQuXKNroAMESoL9adyll0mUcIKUyy+HFdwqG3+Kbslvf9R9a/cY4NfnmGY8/uTFz7v/avI4lUvqfjlbSHp+bYciItfdkIHSitjrfKpO7NhE2T9qDHMhZ5EWfjYxMhG0orHmpkKBQMmfSVjDe61Z6oUco+xVdL7HxecY2Rx6hmbIaOEv7nGaV7lMOv4ZSY/UfHLil2WTL0dFlce+tO/S6o9mk0QhmsMi0kKcuTHvvR7PyM0atCY9Bi5TPXRX6KXfx2DUaQ6BR8b9Uw+nqaFJVwK1z3IVz6F/Jf0R7p+cMB4SQYx9zXDXOi3Jh/zKodZM8xDmg87zx07lNm0ZtPMxd4fUZl/f/6+tPhlC5vkv9Sd2W4yG1Z27ApP43+pccxht5SzKE0Obnti2eWhf9vpw8FcDpEqQq9j8NmqZ5P+3hm+LegPh9CMwQ4J1A8O+G7tw/Z9N4bZ/m+df3nsn5I4zJzYZQWgLIZZsW/qJyzPltceSoZ5ymhOupzTp9vEmxLHXAZQsjwURksykc6wL63kUEzyJK4oLeHa2n0KPThlsyT3wyUblMuWU5RTkmFW9U91mpMZZnVYCZNscqKyYyjh+Uzo4N4FmNAW71MwTG5HPsVotoifu/JgHLS2NgC68pCAv57eOv1zSb+lM+ynVsYodLVnfFtauVnqJyWdMakrjlb+FP2spHzcsv0sGbn3iqdmy0h3mBG/XERXzIxX/o969isdV2o8t2poz/KQ3vZTukGhpz3ju7Vf1nifQifm2iVdT2rhmluHSrPLNAzzzdccDiyxQXb+Xz8oqNx9meEYskMyKDvzaBjbDLkYDdDp+lNiutUo9hyS0XOoAAXr1TO+rYVk2PpS6IVvs6TrSUvjwanxZHIkdnDTxzb/MjVTRhLDjPjlre5C+P0v/e7PtJglKMlOP/RnqLNj/ExS/baeOFnrfWpct/QC6XoeGqyOWC0/6YAPqqD7FAN4z/jO9sdG9IdCN44dZmdcCBmzRvCbq34NTLeMCaF3KRjmVJY5zWG+5Zo9p+xcFzzvfSXkVKVM6ctEClTqjtwDpjVw3dJBpMsk2mHeAmJj71L0e+l61ZjIo6tLoRuzDnN0Ddt7sAamJVAyDHP5LKQXPj/+EJM0h/nmq4cDSwjasUjYXfi895eQE3mZinVTM5Gz3dnkr6Wc/f/b69buP+xnfo8U2y8q1n4G11bxO6UfrbDMX/zdnx70vDX9jemfITm47Q71Z4n/p9DLkP2UiOfcONFyOyn048hhDoy3LePo6gX1OFtqUFcMM4WfedHzPxDtB0c/qEC5W8UvkzRhOQ+HOHaZQjOWYS0qXkrjKH3p3++GrbAK0uUyJwfNfuY798GMTw2UR9HfB2a5ATw0A2R/qK+ZLxOvPPagL62MBUuO9j23XEty4FhKarloh/nzN1+t08lx6e+iNvz97k/r8c/+JG5Reyg5u/zTovGc05cWmAXDtBp/T6K+z8lgZECtn2tWksb225UlWfcp9HHK3MvCT7p+2PZR6In91nSFy/x3ZJZl6A8lnktO75b7bgxz6fllrNMc7TCbDX+VKUrn+2CYy8f25BQ39axXOpM5t9BDwehtMYATplXgisoc/j3qobIbFP1duj7ltL9MFoZnVzAp7VYP/ZESzy3jwdK7VAyz8mtj45ijHWbLMHMZ5yQxzGrWK/1HOevtAc+QvlDinKqz0uUyh730NtfUw16xTe133J+ntFk96AwlniV1iyqGWTnBYJhLSjJj2e4SNR/+Pn8oM2VIxpcOIS6S8QxNXClxTu0G0uXiD1Rje+dDLkb9lHWfYsCe2k1Z+IVCBKAvqRZn+nwP/ZGz/U+VXrMMswrH4MIs23pIYZiHGW/pIJ2K5VMMoG5n1JhWbK+uS8XvU+Mdawgn7E5FfErJ52E/O80Cc5LNajjP6yx+ByWg0L3Z/u0roVB8h2YKax+Z3lgAheFnm0WBY6y93/ocJcOs1CEmW0ZUSAan7BjWE5ESw9wDw0zdiaUzmUvMOTXesYZRulzCDHP+FRtuBIatDwXDJV2Plvq3xPvQmzw7xChwjLX3W5+jZJiVhY4Jy4hymFX8MrdNA1JyMH/xd37K2b1nVUxWHoGH/ezvb+07Se9/8XdUTLjUPAwKimX98NnOJAALPWx0Pa7+LcrP1fOxrW57fVfH7+9t36fq5+H+3TZ+xy6TLP1Yal9p/Rn1JmQ/29cfjnZ/zXBy983X5JlBJDALF71gOR9zlMOMI7HXiDzuHd2J3X7qLxn5Am/wPmUnPgpxEYDfGv3gyDLPhmQIko+r50O/Di0BdxCSEmcB057qIYRtNuRFoL7M5aUuZbd66Y+l8EvrpdufpjoW262pWIb5wufLOOFPCWvKRG1XNG4llGYN/PZKxzNFvtTYL9VNumzCDPMSMu3fp9I16TrUviZsb0FuXepJZ3Jjt12a60polmGe5l/2l4jqXIva8Ke2U56d7XbDX7vi7v/fXrd1n3rGOzL2MvAb9SJd/tyM52Sz1pHep7dv2m/qv2/xNu0M1cfvvzKuKfr5vP7IwK83fVlqby59Oh4P5OoLN3u/zlU2b00Z5uUQxFwhfEss82JIxudvUgeW8PpJYZjVzDchxKbJPPbUnfgLv/NTTeJUSg+o8T9lKaTr+3mHWH3p7fQjPW27S48SveEqcXNfqp1ba796HAeo+mHpfq7K1wxzhd9SHPOyw3w4EjtV0Us+L2vDXwWtIPzkWoO3poo9Lb3F4kOJ/1KdpMtnYJiHzY1LiMi4T6Fj0nVHhiaUa0WMjvWuIwwJWQkAACAASURBVDEYlZNQ3pLvvuWaKsQXGOa8csxa2knGxF/R9U/CbuD+eT9HnR3DYewbwEdH4oROOM9Ufy5G9IjxydS+0vjFlu/HLx9FZDTYf2P0k0q/pOvPkZ4J6x9on5f0oYB8wTBvd882MczhA0v8k5X8gb/sfTDM2xWDogSqwdS2pXeGISRTajnM1UO6bMAul7Mo0nWnHHIouScEONj5XHifZpjL+penDjA5GZLBMX5ZCURKDPMXfvsnc+kXy3LO+7k/IK2XdDzXgkkth7l6SpeNxVh6O13ZUulVT5iu7eN4r28EqPoiJcoc45hPO8wMDyxRa9hSHOZJSMZhicYq5H6/352dqZnUYSmnwfvUndhloiTgl0v+HJbqvvDbP6WTWkjSb1c+lt0ZQgeO+q/fn9u/pujfPeE57e/t6wfa447fZeUpiV12nXJu6eXAMFNOmbxvSWdOKAZUF1LpeG5RVWpZ+HWVLhswzFu0M/yudL0pgxpK7Q2B2va9FN41WOZTccxRDLNNE2y3Lda+FsUwWyZK4F/KTqwGVs1oCMQxR7tqMxDS5aPw1StGnegflT5J15te9AXtPDDMBewD5ThbyjE+Va7Kyaw2UVP5n6sc5s/fdJUKAnDawedsTikOs3T2hLIjS8dyq6GilEWvMcw96SCFPvWE59b+jff7RYCiL9ZG9+6brz54zENQX9HrkNMcZJgnG/7oDloxaCx8Dw5zbfWN+z5lR8bgelomlLKAwxzXP1p9ikqX0Kdb1RDUmxIBqv5I2Sb/W5PQDAJ/NJQpI85hronUzLdlOsx8GHwD+fb6nPdzf0imOV/47Z8oOuPMgUftFRtKefiCly4fNWhNHDybiPko0/YBmYbvUwzQX/itn9iZXaJ+Bt/28TPm1e7iRvumGYoh31T9qGnXyQZ44tP/wDBTSjbyW9IZFIqB1UItHctIlTr5GKU8jh1m2SkUc8inhTKodAj9uQVtQB1rI0DVH2u3035/kjXD/rMA45zMMN9101X7s51NEM3r70XP/wAX+W2qh2JRSh4hPsfPUH7v/CfRMcz3/tZPVDlKkxLPrfKklIffMSAf76Sv0AmPzP9/HlGfhr7I0JeW7ONW+1rjfar+uMnRyfjy52++ekfhl25jmDM2OEdRskIy5JoUyqWicclfLp45htAajIRhC4XLxV9iF3pNoT9aX4TiN/QDtG8acgM8VuFB0R9z+Gw5y6BINZfkMKsNf/vdnsSTX/MdMMxtuB+UjCYYqTh3ugYjIX0lpQazVGP6QdWfoS9t2Pde9J5rO6n6Y05nN1dZFEzzxS/44NEev9lNf1yPxLZgi2KY7cxa4F8q52zCSAnEUW9+ytSuGoxEP/KxRLqVl6xr+v4sC78xZkymfqB9tPpK1R9zObm5yynNNM+xzEGHWSUatwO1PTiBy/VFL5ARw6xYUck/qhmwdBxz6giVTNw69yGf7VllamdRWfr++U/6o5yqOFvWqCvy8VzCG/d5ngNhFLe+flL0x+IdfsMHPn/zNZpIKuWfXvxCMMwbxJP/VZ06SfCPagYsHcecKkIlE7fOPcrHH059GbZ2n0pvrK60hk/r8kX9pwhw1z+q/phz7ClRVkmWGQxzCYltKNOwKTWiEWmisqhmwNJxjItOjtOjegxzXP3E9Idgnt0DDo3dp9Kbe3/rx02ekMbwOdJb1D+QR7tN/ecmX6r+uMG9IXn18zddXZdh5h6/rKQgJoYZDHOWTtUjg7kFOGp2ogf51F+gLbuAT6Ezrp5IxxPtK6uv0vGl6I9bxhjKdylZ5qMY5rvedJXSNdY/MTHM71ZsyszPT8TtP9LI/fOfXD7mUUFzr49jI/gEO1nh+lPJxbavO/mwtp7rKkehM0d6sq6qeAsIdIMARb/kDqZimUv9/DjmI4fZMsyhBVRbsZr3pTDMNpTAJuK2S+9SrqlCMkwaqlEjpeBXSh+o5GJthXT5zIYQ6CV5i8BMiEFD96n0ZRJa1RA+0uWP9vHuv1T9s5RTmqNctQFwboto4OD5YCCs79/6ccwBhpkmxnVtbKZ4hjmHBjEog2r2C2YqTdhUcgkyzGnVxdOVEaDQF/ThykLG55tHgKKfcgbp8zddk31P2MUv/MDER15kmH2Pm8O1HIbZhmQ4eTk1wDKuqTqwZqZmGSmf4cO1QoCakRAtnw72MlL0Y+0w8+ZpUD/Ih/2eZGrbzsmBdmOZc8WwRzLMnGA4rotMhnkqYnsC4tj69u5f8OQ/JlOke9/9Y2aicfhJwE+FlpSQP4UD5AresIft6W8M/v5JpbadoRNMW7xP0Y/vefeP6ZNlW8THhoCh/pAfB/09n3DcJRvgIz+UO555MYb5rpuu3mu1H88tmR40dnBLat6/6Pnvj4SP92PSlyEpHTPpWObUZEq5qHpDNjmlR1sWla5AR2jliq/JRoCq33JD0cYy5/JPTzLMY4YMf5s+r2tZDLNh3kZGVM41BTNlO2yIofJn/Lg2+kVtUGXKx2qT7b/OtT6Bqv37FGyVcpaN/ZOHn+ltcvUD7eMr3wueQrfCy8lxHmOZnRBMvcK57tplmScxzCpDxhCSZxlmn1FmcA2GmZN6hutC6ZSBoUrTCcgmDa9en6bQE/TdXrUL7S6NAEX/Ld2G1PJVLPPAMGfwVy9+wbjxb+Iwt5CDWYEnhWG+510q7tZKdPUEaNyMsm4CVex9yhnugKVgPFdOkGflS83+b5jgF9PPbHimWvSGnqfow5O+2xA2qCoQaAEBij7MDYecscxBhvmuN11teGsbxGw3HTG7vugFkmKY5W6zp1jOdTvqdHOZXFxzbNenZB4kymV/CLkY9M/blj2cjGwfaPA+1UCrN+w2iI8KuZEsf7RPjnypx+LaDrRxmA8ddKP/6qaWA8NcUbLSmRWqAdeKUDqeOVWVUjaQS07J0ZVFoSPQDTp54kt9I0DRn7kgTMMw33S1e2Da2PZQ/kU/BMB/rtB9OQzzISRD4ZQrcWCA0apR/vnEmw5MarnDTyCeJ7LMJesPJeMwxyDW0Mec+A0M8kHPJF5TDLD3qrC00EFqzngiEV/N4ArWH7SPn3wp7X5t59ls/nNCXlf6p1EMM2d/Q1YMs9zQgQue8iekfcawVXLxzBGKYfGhcIamzL9EufiMgJxrKv24511PlLuJY7B+vLJM8d8UALyMnS5jT6jHZVInwPmYyzJv8WeXY5j1R0MDnE/hbT2tO/19MQzzu35MtHtHNehajVRslUS3rMQBW5Tsv1S5lBnOeLgzVPqhJrlwj0q6Rzz0qZz7h/at6T9U/buWs2y/O8Yyh1zmZf9z1mFuJUOGAgIMcwkXKr+rST2TBcMcrxeUkxl5cpkJofJHBt8+N3afqu8ahnnm1zh+RyFHjckf9Z8cHHusoAL084Kn0q4A13Cec8UyW6d52PQ3ZMio0arEb4JhbiPwgNIpc5f/493GNnAs0R5KhkEaw2x0je8KXI76UfTd+YlU/RXMHPhJ1w+0T0b/p+jnie5d1seHOOaNpdo45sFh/twbr9rrk6lCR2Iz+v/FL/zAxubzeF2zK/mJ3ZyhrpvqV2MGe887n8im/c7RZZtwLFEOFYOoepo4PZe5R3fYE0nVb3VfPfy2xBi60xeUN3X3gQfwONU/qPp6TY9Lscxb/doZhvkq5RI3sWlKTkiGHTDWRCHxjwqjdMpGhhmbiGKj+qjkMzKJVkqt67tsj5lOL5xwDHjMQ9rYyQIGPF54vIVnhFT9vZbTPB6Vvd6/BcNcS3rOd12GhUF1slehxuxVOqY5hUQlH0kyGReE9ruz3dmJBaJ2719IENt49zt/VCx+ZlhuV/6of3/yoxoLco5fKWXd9aard1siKMAwp6Bd6NnghpdC36MuttbMVTquueRIIR9xshAcQmUXGCkGT4ROncgPu54IYxf6JTnksJEF+ajAAYqxINe4taac42wZaYZ8lmEeKsJ8xVRMDPM7n+gwEZaRkPP3wqf+6Rrd3vyOGoxHhkcOnobRzNue0s7RyCTmrXduHGLK26yYjRRA0W+VXuAHBIAAHwQo+n2t1iqGeYt/+/AXflDv93OyZCCGmVqYwaVqf8LiV6yR+6WdsZC8hk1moQcawS+ojxnrX1JGsvS7jyDbkvrg6vM92mEehh/ZQeG64X3ozyhjtLdF/abq/9S+lvrelljmeYa5kaUgKQyzZVmOYTdMov21fL/WrNVlsFrGz85qpwtI+fSjlHwk4j8x8gLysE781UPjSumDi53WDaH4De1E+6b+ou8hAR+W+FD0/xrOsvrmXTddvTpkCQxzLak535W0GSoEZ60Zaw/Y5lThnHKSiX1azNtqy1w56DOnHgRXgHQ6uT7wRDsh56ggYkb94YJKoZQ5x7O5srIxzCoHc+nK5ixfFMMs3J7UnLFOmCzhOOeyt1vkJR7vnEaMaVkXPq38voO734H4ZabiR7WAgEaAwg7UgHoSy5xYgYe/6IMq0cZup47FHhI728279qAShtcXv0DGwSUKe7MpSi4PUXO2quIk4Sdv4zeW5Cddfwf9cZaQdX8Ver1lwhQz/gz6IhS/IYQK7RtCDiT3F6nyXrL7MX2d4zMqLMPqo04zl+DfDg5zawyzEoQYlnmObcm4qWtWaYnLrzlbnWWziNt/JAN831iq0I8lPvKnXhc+7c+Kj3F3v+NHkE9NNEUiv59soyDawYfCHhQ3ON4HMjHMV+8VxTx63Gc77tcXveD91FgX+V4PDF1p1uqUYHrAV/IKBYfhReuXUxGbAD/k97R6n2JiqyawreJj5Y36OyssMx0U+MjAh8IeFHGqThRqGOZ1/i0YZmppzXzvNONiXwi5RG3crz1TNSzznOvVBn5hRgP1NwiU7h8MDAVBFSj6qbF3+AEBINACAhQ2gRqHtSzzUQxzaAbtzxg5XF8kJI5ZO3PMD4rJUb+aLLPqkJPQjA7wnoQ8oL3TEJBUPHyKWeiSeunBMTxxxRqJ3F0sHNaIoF9r9Usiy6xO/Rv2oNi9eidWEK2/C4aZemoTZJgZVKRwFUoPxjHVB7sVgxKemUPg6BiGg6G1z7Z+n6J/uv3PbjKXgl/r8kf9vWNGhPXvLfKlsA2Uo042htl60mdnToyH44Fzui8mhnnY9Hek0uJOwuLQ8UamWT7eLZ40NRpOjvLxQ2DkXFMwSaPDnErx4/ltSyTAD/i5u6zT9IHDuJ3ToVb5mIc9egn+7QLD7A9YfpXr3peSJcOEC/yIXiIYJyRm04C064ueXn4XfkzH6gVvafpTpz19LClT9M3Pv13FL/eBJ9oJOUvJpnHR0/88Zlht5hnDMKf7rwjJYCLinkIFOM1We8Kdiaq3Ww2OhLfOon/4bagfVZ+c9LcN9Z2ElGdoP8qb8R8gHyzQOfaFykZQDRBrwjLgMFNJZ+E7A+Np7ZZKqK0YZqHXFGxWimh7w39gaoXqV972nWLK/JAMf3NRG/epGKRjhrkNfMpnYSmd5QXlI0vStixR3MbslPF97lk4zFsRrPh+b0wn19lqb3KoqPLtfFr4yjJVX7xbhWMgWQEiNYT3J8kRRxcyCanMMXhsdphNEPSZc1Qg72tJMcxKAT7/9h/OoQfNlEHFaq0BpDdZrMEI78hAgKofok/J0Be0ol8EqGwFBcKfe+NVyf7uI37+Q2dnOBabQjzL31DLlWop2c5YhrRLhxm5xOtWlnncTYI9yac3fZxrryYS1MlQO/XXdk851xcSbehRDrNE/Ix5lqMPaA/kGdJnSQ6z8sjWsMyDw7wlT5/6OOX70tjlHhlmO4VosROCLVueAIp/In2T9RQSJu9T9b+jPsOk/UE9Rf2mm958oIBPl/hQ2QuK8cN1mGP91+YYZonOsnWY1czOTj0Mo2WZi/GvtPuS4qJ0nOZh6tiL/Px2ymy/3NDTOgyzXDwRootQdcmh+lT2gtphjv3e2WffcOXeHlRiE/8O1wcHjtP9h7/wA7Fta+45zcJ0aHEveoasPI8xivf5t/0wRhbuI4sWpOwOSdX3Pv+2HzocxCQbT+n6gvb1rb9U9iJmDN36zOfeaPIxp/i3TTHMUtllK/iel/olLfVs7cju+z3rRE4cN5clNC8tRb+bEAFWEELxHPQM7UMe40x50ifz9sr9h8JebLbVkQWsimE+yTAfEpoGPXDi+w9/0QcjoWjzsSnD7FhczcTZo/8sUSPv/kXP+Is2BVex1iNTLV8/zAoYpf6HBSshhPNiov5m2OXpTwJ+rj+E9kG+LgKS9VvKOP05deKfzQ7nHK18yt9thmGWzi6DZTYISJrB1vKjwUpnRD64AutPUPyIA/73qQa++VAz/vhMJ2hl5JvL3h2HeAFfCvmFI7bk4i8lLGM1w9xCMOXDXyQ3dnmyBH9gY0LjtL8i4z8n4T7VQJ7RrWJf1F1v+yG9I0GCfqg2UPQPi5UNtZZ2TckwK3lJwy+1PVR4xxgjZQ9S64/nDQJS7UGsfDnpcYyun3pG5WNO8X/BMG9FPPP7J5fY/SVpwde5mJfM4hFTnF4mF6w/syFMqe0VI+35hlBNTOdCMoRDq5tHhS8Flr3KkALb1r4hSa9TWWadJSPFw66xS1Z67LLtMMoo9b0H91i7JHVOroYRejdv1U4yLv4WAp9BbeD+xc+k2TNg9WuCZwP42CWZWUbxRP0lMXBcbVaNellW3m4pWqsfrb8vSb9FMszdxC+rVGPKZT5spgxOZDq7D6eZbngYmecTU7du9I8O9xpfoupXPbGTVJjW0Bd8cx6BnvTbIiBFz1cyzHy7Qi/s8l1vVUvk4tO+rl6gkDSr5dvbpjXTjEq3Sx5u1G0rEkur58XP/Mu0F1Y+fddbf3Dlm+28RoVlO4j0VdMedNyVqBR9Nwxz/O/ss2+4am8JI45/e2CXe5yhxqvo9EkpM9u17a/xXo/6OZfFMTUEmvvzpCEZM1kPueMTWz9M5mtYJX7fnIS2CdZ3xetJGYfV4SUpfu9CDPMk6mxmN2H5+9IZZj0zTZGYky+w1/ekzG75mfzTNTKMsxMyJF1vWxNQQn2p+pBk5o0KwwSx4tHKCEjWd7kMc2hp/9i/Zc0wS08l5zJ3OBgq/WAoMDv1RodQmjpbo+b12UlobxPZS/pLxRApB0ISbvuDXlDhV6+H48trEdBMM+zHWvhI31vJMJPWMfpjXbDL0WjgwTkEwPLU1YteGJW6KOf/OlW/kagfVNjllzpKpEBAos77uEnpA6timCmUaM03JDPMbigGl6PHbYhHi/UB47Omh+V5xzIqLeuPYoSO6p8HHpalUA14Ep0HKuxYKg4qFYWARL2XGZJxdZQ8h+CMaR5mP2aj3rV4ZzlJTHg4FgEMZrFI5X9O1CBhHegBJi/IpPH7VP1k1AkZ+FHhlr93okRKBO667QfMBBz2gxL25G9N8zAv+7s6htn/ypBmNfB5ivuSs2MMjNwB34HRxbVGIAceYJyTbUeWF9yY1cH8nJ3pmL5WrrMAwbwQKsdP0iSKCjPmqoPqRSAgSe/nmiulL6gY5hT/95AlY9z8bl/2CRTKa8mxy7Yj2U0BI9577Sji2iCQCx8pHTvCRrN65HO3/UC7+rzf6/TTPt/g76Vu+f7Fz/orEn3RehBIL98afg8nwoxEMPhIUQSU3rem3yn2jcp+FBXSbrezMcyx/q3DMPPZ1y41HEP6rLO0cm8pH47zFvTWvdu0vg8W9GAXBV1f/EwaZ1lpzV1vVY7DmcPItIunFCdhXW/GW6kIGPvXrr6bkJL5+ksZT0eGOc7/HRjmVGUo+bxUhll3oMPAG0o7g/tm81VJfDDwley9x2UrvR9WDFrQ/8Mw4TNEUq4pmVKXYW4ZP9gMWpsh4WuWZbZuZ8v6P7fiRmlHSupDSqaM2RjmkpVbKhvs8hJCuJ8bASmz5dy45CyvObZZO/aWYHEY0snBLW3ep3T+hs1PswfetIMfbEROa9BHWXp1RROXsuyHao+U/jAXw3xKO8/ufP2VrI7Glsouz8U0SZtxSmmPlJkzt2GpFcaFG26560Ol367Ny90G6vKoMKNuF75XBgFJuj+HkJT+8Nk3XJV0YPLC0dih7Rrl/i+RYXZDMbo6Wlj40cmUTF0Zs05f6mnGMcTEEP8/sEmtnNUbIgVtxGDRv1SDnZK1jQxs/S/6Or2taPmLpza7SrAjUvrDNK3csmTAMBP0SumzTQIIm/4ElYPSCkhN9Ad/k5/v+jV8/+HP+msSVTFyDrjKjeGHPkyiMmI+8rm3fP8hD7MM/ff7sZT+sIJhPs7DXEtrRbLLOoG5cMoK7dtMEVJmLqjVv93vGqaZab9gWq1ccFENdpIYZuX2UOHGoX+iDusRkKb3c3ZHDsOceNKfimFerxp533zEz38wb4EMSvvcbd8vJq3SqTQzQ6iJ0DQ6tdoneZCeMJCc0raxsYhlDNjDn03EMCuWTdCPCjdBkHXZFM0uC/9J6QuKYU75sYphlrbhr4eZZuuxia3WX8wM/8TBFrkY1bRyZOZNtRM+ygkY2wnRyrzQUpyEFAcBz6Yh0AtBRmlH0iSQ9vSqGOa0T5R7WhrDrDvPJK2Mn0YJ18DHTTu0Xh9aNmDasZpN47Yej03llTNx1UumdPokMm2U+FVXFlQgCQGJ+h4CQEo/AMOcpOJlH/bTaKUcPSn9aF60b9S9EkcHt8ZAz+0qtwiVwCeofxPHXV4wM9WGPyW7kTCQgyMlfmVHJ5SeEwGzyU+Oni+lt5HSD5plmMWxyx3EMeU0OCirHAKtsAEcGJr9br87253tpP6l1AUlT4k4PuLZf1Ous6LkphD47Fu+T7S9CPVfSjtSUiGSGWZzcMl4FHGto5mlZciYzjhtHtlKS8xDPmR8v/cQEM7MAJsQppIWunLZlAMdhwlQKbgpcSzVBpS7HgHJuh2DihT9N2nl4v1fnYc5BqDSz0himHvvTKV1BeXnQYCj0avddwZGZb83J0DpHDczjHOj9yll/rm3fJ84/Fz9eMRzwDTnsUT5S/nsm78vqf8u9YsjJrnR/p/DvklaYUlmmD+rHGbXwz6YuL1WiPGEreG60H1JDLPE2L2lmCbcN9kDW8RhabDIP5ydLrF6/6FuMOH3KGVde/JTGlZKLEu3RVr50nWvprwk6f3nVFq5BP+XBcMsiV1Wimxj96wHFYqJxH3jYQKf+ZhZav3gwhxU7T9q05/gHyUrqli+Hn6UmFLhmVN2NfDJWX8qzFv5Tg15lsAmlV1WdTBHYx+IMVspSywP14XvS3KYMbMtodookwoBLuxBrX5kliwPWe6GkAw51w8n3LA2hmTIwW/I2743Jx/ba0pct9oCLRev/iWva0zEpyEZ8vWvpPx8e1hDnlt1fu596zCn+L9gmDNLYuiozokYwwTkIBlcGwNmQxiABy88uDAIKm5QeSTU+pHZJLAqjlK2vbF8lNguKRUn7Gvgwqn9S7Jq7X4NeZbAaBXDrCry2TdctdcxykMShcOuQaJrMMwl1AFlAoFtCNRmm2uwzGavhsNIOQ67hP9TMqETJlMYjgOjF2gXBc4t4EuBwyx7aDf9daZ3S3q59X4teW4byebfVvHL5ly5OH/3kb/wITUE7Ha1M2VIcpgxsy2h2iizFgK12QT6/tTqYelx9aaUp5FdXL2kPrcV78+++Xvb3U28O9ttbf9au9c6blz7Qy15rtWDU++lMswsHGZJznJPMXuUMVOSY0pDMZGc8K0Zs2bSORHGIMre80fqwNBPdkoMq2XK9B0PqVjVcLCkYllGE9NKrSHPtBrGPw2HOR6rIk+ioxaBFYUyQKCmoaTtVweP2c5kQtg3ev8Rz/lfyLTJsHyBX6P4Da1B/c0elAX5UuqbrYrWO8gnSj5L8nPv15BlSWMFh7kkuhFltxBTtjWGKfb92Fgnd5OkhBjRWHxafK6W00zdr0xX90dcGdeUMpxOdGTgNw4DaM/UI5vHg1LfRofZDQWy/4W8YuR1Sr/hMDsxzMcDuDm4JDyw57kvKSRjZFSOEpX4ifnEXecwjD3jZxw0++OrPzWM5hgLWx4fbe8ER94+4rmEDPOt39t5BDPXSFS6elHq2+AwQ++K9LtHEtqOCH5y0yN3vv7KZP/2khcz2PQny2HuI1H/dJkm//GwtEvwm/pdly/nmBylAkenE4GQDCEEFeWEZxKSIQS/YEgG2jdLYFLq28mQDMhnE8FcQ46pY0TK86nhGKrsyaY/yyTbqQnVtRSH+bO3fu8mhWxtBbh0B+oNT8j/tLmj0gcwzCnDzrLM+s6RQcfkcsUZDLOMFasacsxniY5LuvP1V+n0oSn+LhjmjBIZ09j4MVO+KWv/PhXDOJ+Wqn38TAtCQ1wb7aPSAbeLzsfEZu5fwj3m0hPdqbyczVdcPTrUq5hHV8vJmmz6g3yzyJfSbmR0y4JFbWOYX3fl3hwJe3ZI5KzSODnXengvc/8Rv/BBCnyKf+POW79XdOyjtTuURlCxirB3WexdERypY9po9EF2XrlHPvd/LW4L7QfuvPV/JvsWPsQPAUpdg86Vk38NOZZrjT6ob5V/e8mLP1z/4BIxIRl+CiWhaW2oZ5rB1FRC8Q3GSPoWhEn7q+lDqfbLDmHeUU94/fUSXE/XlyTjQT2hVljq0C2xOW7GQYAyJJvSZpR0lG3Za9hl9e5xDPNwFPbhoACCazDMfBlEP1tADQPYC3PfKpNObUzL60PAY2Y6YQkOMIEJBSVbdJJhLjXhCXmgQuTX0oT6kc+jW804YpihX1nyMFPaCwpnWU+qNMPs+F1ncf4uYpgzSmjYlNSq5xNRb2rnaJgRuonoI+rZRWwMIxyo9aJkjOIYimZD0GT9pZSVCaGRhR/aEy/PGs6WmqSZ0NL4euL5MF6U9iKjO3ayqM0Msyr9M6+7okoG0keKiWGWHa9Xw/ghLo3KhGz/DqV+FI2NlR3CvKNk/e68RbZN3N5rZJdAqWvDWAGdy6ZUNeSXrfKBglT+5TVr+opdVkUOh1ve+bor1ZSMPA+OJZMBLwAAIABJREFUlBhmu1Q8rgj6myb9rHNt3a8503Q3e0nF9zgmDfoRMp5T5nJq/rbqh2Km9I8ySJDwe9UmNkLxDIZIoL2kk7PFkAzIIzntrUSH+bOvv2qNv6zjlycOMxjmbXMbzaYM0w95Ay7lQOtLQjOKMHjs9YvSwBbrbyq4rQZzUCSPyTEDQi4jonaRMz1o12I/eeTz/rdtg2ri22ZFQ3b/pWofpZ1IFPOmx/MzzCNFU3yAlrLhT3qsXk1nWamjdHylxNBR6kmxWEXheZipZQT/ZdGvFDs/o3a6JsQK/OZNekVpJzZ5wIkvD/HLiQQcGOZEoJcelx6vR238XLylY7ukW63cp9SRUjphNgtR8Tj036Fk/e685T/CX+7XX95dQs4wQ99yzBOo5UY1vqnT/dZa3KMY5s+87sq9yspsj8SeTbtR4L4UlrnoRiQqjTrxnZozTunYMhBvtipQ6kkRvUBIRjZdwBK55KnXsmtGOTlTSgt9y6Nv1HLLZnAWChrSyYXSyJ3wb8EwZ5aSYlOKx7BUDpKu1ZGmhpAwZqgy3i3qUz2WOXGNLRAUPzDMKmeQMqAhhrDR+9R9+DO3/Md5xr5R/AZ9QP0X+8clz6eOYXYYZshnUT5z9o3aPmR2w04WV55h9j3uAtdismTYTX/LE+9NsUU11zgpnaFJSIa76U8wvlJiAaj0pEzMoqtgVgtDzE1796lkY5EZiYS5jtseftMpFOpvEJjvH5TO17yeQT6n5DOOsUZ+lPKidJT16sPrrzITCDuRSvwbYJipm6GPG6T/aIEvBpkUQTFstWKbEAu5NvKqzntUelJEL4Y8zHkY69HU8CiPmvX7zM1q5U39eLSfmzwk1+eS5//vBUba+SI/c/P3iF/hLb3iSCkvMsU4fMhkx1j/u+TFH/bTyl1ZJWW/mINLhpAM2RQoNUOlZ4dIF7R6s0INl5mKqSinFyMTYU2sZSZav6aSzZRhlotn6/pQsv6UkzM1MVMMYsn2SC6f2i6sd13XvWnCMdb/jjb9qaLUxj/qJScwzG1FaNToWEWYREHMf56tHvn0sG2GWfaEl9KJ0WPKwPzJxrXGxDRfjy1jQSgZS+jZ+v5FKaf1Luv6N8Oxy7bM0yF3ll1WT7tHbRwc5rEbhqpoi89xHwzzekWvYTDBMLclr1oDOYWeFGGYD+mBQrFuNrKg1fvUE17L/A0M/aH7tIpf6/Knqn8tPaNqnxT9pZbTerd3/Zs+u5zqv1p2ecZhvmIMywj5BcOaR4CiS7wvhmEeYvUcXHLMKObKSJW4X8aG96kZKlv1MRZSNr66vRvkw+V9CpZ5ohOujm/Cr0pk2vrRIPFNajbJMH/49YYApZ5Bx9ZrF6Wc1tdy25s6fjm0hcL3c2f810teYuKXgwzztuqlvS2LYU5re4tP15qRml3Q+LWCAIWelNCJvfA8zNQTXiyV97kiRalnZuLcJ85b2t2Hs7wtdlmNtxEMs5PZ2frV9iSTzNdSGGYFSw8dl9IQus4hBt62BgQKPSnX32aSOvgzFZ+xaOR+jUFylgFsFL9BzKi/F9A57QCUegb9mqFRFvSTUj41SR6THWObP7sYw0zZQCkMs8JswngJzaJEwRzO6d+dikVwI+6F4hsckBtrL4WelOhvhmGW+6sxUGLJXK4+hVpGqWfQrzT9opRNWs3yP701O8Zphvm1V5gFydDRgZZ4yXj/kS+WkYfZMMzy4/VqdbYesM1vLuqWSKErRfRC5wra7852Z+L+XvICuty4Vvs+c9P3iMNRqn7kahelnkG/4u0UpVzqjj673Z2vu9IE6mz0Vx8VF8NMQ2lJYpjtErEdaO3uLUnXFEvtcx3NDcmQhKdyzaS2h8phzo2f1j8a8zeqOuH3KORyFE5F2D7p8uPePugXT/vRk7OsV/wnuZfXG6BgDLP6yKdfe8V+q0ee+v4lYJlrT8aiv09tDAeWqgP2PloIjTxIoStlGGbFs9VI2rhlC098fR/1gv+DVIM+c9N3i8azTBbjeHly+z70i6Yfp8j9EuI+T2pgZj72mdddOR7YvoFhdjNkqM9M8jDrsAJ9eAntTxbLbI/odGH081y1e03hBIUZZnunXfxMC/qoP4WuTDeD5tIP2ZvuqZkmLSPJM5AUz6UDHKBfvOwHtTxovcf5r+WIXVYlu+zyrMOsGeaDvHMNP0vliWKYb/qew6ZMs9Q++kfOkoCe8bR3v/YsVcWqWTxbxG+YnzYq/9T6UzBNWie0gcnXvwy/LPdHIRcXvU/f9N1ywUTLjhCAfvFRCmpZcGi5jV3O4b+68csBhlkdXmI57BNTZ//oJp9CSLgvK7Wc3I1/FIzhqQ5XZPmdQw8XWgcKfSmiE9IzZVRYnlVhGfjJR6AGqQLdmterGrLgoOGaXU7wP2eDnw7vg2EuKFHDgBqmy+42lnRde7YqHV+zIiFHfyj0xQyWefub5pcF59mlkItvZo9YZsH46rZ32r4auqXghn6NPa6WDAq6VtFFu7HLRAyzjWGm6/FSYpgnbNf6TZlGzgzfrx0LJR1faXmmKfRl0Imc/WU/bvoLWWo66zhfgy3fr8E8+Szglvp37I8OysAVvxq6pUCBfhnVqIV/tEdb+MExdjlPD4limAu36ah4KTHM0mP1OMxcpWNM3fdKfo9CX4row0Ax5+AoVBlLuzjo7z/qhbSZMj79pu9yVI2+vYaBkCtPDu171Av/z5Lm5GTZ0K/9rib+1QTvfFixy7l/izHMerb2uiv3elPV2ZnZnOb8tbEhOe9LcZjdkAGJ28I5zF7tErxEfNtNJDW/14FCX8row3QPob/i4+8xbPE+hWzcwevTb/pud8/oZI9mi/i5e0xRf7MiSj0Jg34ZBCiIidyOaIny3NjlHP6p7yxb6uOo7ipTRokGhcqU4jAXYbsoBbHwLQ4dUzrGjMS9uSoU+lJMH4Rv/KvBRk1ZwM3qhQKYIVBDpywEvepWTcw5qR8Fuxx0mF2G2WeUS1xLOR5b+m5dalZqrkNKx5iTEdpaFwp9KaUPiqEYz1R1sgYJ+T/FZMbXHz25EYIf2jHtHzX0acowf5fo/urrW228t44NOd9XzLJllHP6p2CYc0pppizpAwKX2ax0nKU4ahT6opml4WjRfI6tyfM9/nLuKVSl1i6PQjZHDrMTx1y7/fj+9NSyrXjU0CdXv+5403eJ7q+ufGpjXdiNSi6+BLusKhHtMH/mtVfqfeJjZOKYtsmmS8t5X0pIhvQjYLnMaqXjLOXgMAp9UZOnInghJCN54Fp6oddl8yVcJNyv7cT1oFu1MeaopyaNXBn/NNphVsBQxjFLcZj1xpbJAS5WxUJDelv3OXXY+V3RPs5t4Xu86a/t+lPoi9GD/P3LLPGdYJgPK9L2iSOGroH7l76IPquB7bfDuQIHAFvET7p+pLSPoq8vOWzumCBRvy6tmIVkCfta9ynZZdVGd9Vx0mZzRHbIc8/3/0e++IO1sM7+XenMJwVjGCuUYsziCferCJMp+HsU+lJMD4QzzHrJscIA3AMTGGvDpDxXQ4/msJOuW1xw5qK3d77uquGAuGnkw3b/9JKXfGjWNw47zK+5wo3JWB90Z6mDQJCUFHZZs/KTXKNc1CpfPbh1WOl455NcnZIo9KWUDuhNf8J/l77o/yJv4R1v/A/k38QHyyJQQ4/mWiRdt7jgXFab4kvX7PKCf7n2/lw4xiLDHF/19U/KdJj9xPw+Pm3ep3CAUjQp7Cy1ie/Ydhn1p9CXeR3IgN/+38xmQvs7WuP1Yy7au35UBYd5kJdAPKXrS6h9FP08ZlyY2AKB+lWjv8bgXuOZUqEYti3pDrNlmIcBwwvg2Lqt9vC+TIe5hgqV/yYXw2hbWopdLI+k/C9Q6Epp+UtnmWswVtJZQPk9+7iFNfQIDHOPmmbaPGGWC/mn6Q7z5PCSckeZSnGYSw/eHLoHhROU2s4ecE/FhMPzFLpCInvheYMp5OTro5abcFx7aV8N/QnZN+l6BYbZOsxXHZIrWE3I758+KjWGWVVFZ8o4MMHDCkfmaykOs2JO5o4SDx3R2OL/uTAJrsHsAfcW9YpiIFUDZMl+xGHiUboONfo0WObSUqUrv4b+hFrXg15xwptOy8Yvfea1V+pUFaX8UX3E+0s/HNzbF7wxOMyFUZHiMJOwXYVlsVQ8hRO0VIe5+z1gvwaXmu9Q6Epxuets9OGkdRKyptRgrT79xv8gHlfpemPbV0N/ggxzB3rFCW/q8aV03LJtTygcQ91fdpgL93wpDrNmOm0W5sMMyF8waP0+59ltD/i3oj9UemIZpSPHNWv/CxhAn+LwXcBG7lPJyh9c73jDdx42VcrG93gbf2CJtkH9ufTn/29qn2nxe3e88TuNW9NI/0vVj1r9dRF4ggdms2IU8E/BMBMIszjbRdCGpU9QsIZLdQiyC8JT+q3FpcZ7VHpSus/ZTX/5I+TMMOlPqGtd1xiE3eVz6fhKbV8NvVmyZy5xUqs/lZQ3R8yXZJLjPhW7rOq6nmF+zRU6G+n80qNK06wSRG+7f8lLPpQDz+plSI+faqGjSpdBdSWPrACVrpDI+5CPOVNSoAFBTuXVYAo/9YbvnCxvcsJDCQn1mS4/z+FxGUeGWa1cCJZfjb4aafaLPabilo/9zDz+55z/eunaGGaFQOkjskWGZHgHZCPWsVhfOip4EpohUA4FVqCCB0uv0VuqGDs6OWfe5Vx6F/WK8i99UZ2ldTPhkY9v8V1KpXdBzZRPNSlOHTmCIRkld4kR4s8V91Q5xT5PySwvscvq/skYZvXAHa9RR2QHI202b94AwxyrOnWfa6WjkrCOdUXB+utUekImZzDMxfRNxzEffmB0lxldd7CujRdXptPqVG18Sn2fK+4ljMQ8s1x2E/ap+OUohxkMc5wq0DFeZRUmxGBSMYdxaJ9+Crvwy01wlxhuKj0h62+amSrIGCwBSnS/Bsus2UCi9uE7edO91NCXmLHBbCaV21+54h4jm5RnqJllW7dT8ctRDvMdr7ncGzF8TsC3eGn3HyUlhtnu+p4k5PfSZqijdhu9T8UcpnSqU89ODaePu72WI5/5gxJo20fFfmh2eeLIlpOvzvOs92qMnJEZj0e7J+E+lezcPqvimBWSEvCTrh9u+zjGLlu9srHx0vpnK/jnGr8//dorAjO8NP/yeEZ++v1T8ctRDrN6yD3AJPdMXVIMcy5l4VhOcw6zjo/EjwoBSv0gC8cYbKub04IKUdrv1HCYVQvdsAzaFuNraxGopSux9ZWuU9zxj5VT6Dmq9HH+SsRSOEa0w6zimLeCEHpfFMNsG1kyr4yVGnFeqhaXggbDKVAerPKSne12VPoxWTkg6m+mq3kraQLzvF728/9PKTMfLPdTb/iOeSZJIL6TWIFG28eZXbZKplcuGsU3uAnWsT81+imVYfi0Osmv0m+JXY52mMEwL0tw2J07PFoq7H+gvbw9m2W/R8kgLqMd/8TIRpbFp+dEVJS6UUWeh41/8VrX5pM1mCvpbGCbmhCudQ0dScVQuk61IINUmannazHLlmnOzDCX2Z0hhWEeY6dk7jlouZMqA1pGe7FniVIvaslRs1Ud/C77BXqGWcH6qdcrlhk/7gjU0o9UXKTrUytySJFbOGaZbuS+9KUfWcwat/iAavSnTx5gss1BFJNWzkmRNMTGWI0REBJAteSe0slinx124wuSB4eQDEpnWYmuWoiNCshQm//U3kK9rnMm9vrSCmEZeiLUCb4t608rjpp0fZIWklEjfVzqgSWu6xDldxxny8jj+YNh3jbhyCOFZaaU2jmKUsqEh6SvAFDpgf0OtT5Ulx9Y5oTelv6odFYwHRFeb7TiLPewYtGSLGK02MQtU49g0+/FxC+rtkQxzJrdKXSAiUiGOUZLGnuG2kEqAY/02LYSmPll1tKD2rJTDKhdObLZISVe12KvtMN8WImTjK/yC1prX2sO2h1v+A4TRSVUn1qTx9K4xIFhjolfTnSY/XzMeWYEYhhmx+BLTJxeayBd6myp992BWaKcSk7UaxlqnUkhj7nZUE71CuQ9eeIEoJf9wv+b2q2yPG9Y5n5wbmNtcb+rpQ9rlaoHPapli9fKZOm92jHMRRjmEiGgYhzmN3znhK6XlpOhFrO41NHW3Hc3j9n3pckrZ3tqyp7TUbfY/Lemt8W/g7CMeKyonmzVMZOuS63KJaS3bjq5Glu+sjPMNiwjNwMgxmEGw0xlw7N8B0xzHGFZe2WBlZzcnZb+jMTXyobv12IUP/X6fz+i2DB+uhEC6n/Zi+usNOQw8J963b8/HXDauHxq9dEcspkroybDHJMdwyWLozHws2X4jLN/hHvMfTEO8xu+Q++et5ZSHWVpj3x1/7Z6vybLGK2giQ8apnkqp1blY9uRq/4c5M1NPpPMJIm61trjtRgs6cxgS3pQSwdyYCRdj1qWTUi+fixzjP+onvH9zhBDHfJPY8Mx7Lei9VNnysgcYvaol3w4+vucH0QH5SydcN2kyy1VKlwMMUe56I1/joH2Dbqk65rsomIH3czXNZZoe/9+Tfmn2qy55z+pGObDT6L+tC6fIMuc2b+M8Vcvfdly/uVVDHOJI7LFMMxzIRlUU6StU6yI92svzecwoqEygpvKBMlv1nA47eMk30EeLPE/uFI5g8RVOxmWV2tQ1qEZDPEQvUnFwVvCcv9RSIYwfZIgI388rnUsdjGGWTXwjldffsjgP6ZZGvLk2Hw5w187EKiM/04+Hee+HIZZzWjdLH2yeigX5rGU42ziJ+XKL+SBcJPryCzz7D+WZS6lh5zKfXTFGFaXIeSESQ91qSn3XPj2oD8S5HTkNL/mijHv4kDkhf1HM7Fefz8lftmuLibpaG6WWRTDnIRkWw9zc6xKoMcxDKBEO7nKsgn8nXzM0tMS1mKYrc5PmOYKS7XS5TvXPinM5cAwC9ab2v2zxNhEzTKnsMurHOZPvfryfWgFf83/H/VSGTHMakYbnOiEJkAN/V+KIV3q5JLlyJmRaAd3dwSWHCVpQk8e/eL/sdRlit3/5Ou+/VC2xChUflHSNWWdW4lG3bFuDj+8xzav029J8rJY3PGaK0jnqSnxy6scZjDM811b+pI+V1Yyt6FV5U0T39sv8AwRGNs/X78W5NYc3vvse59j9qaQDiRWm2qzWHoiheNMSPSD84R6jZ23m0cl60/t/rlGLkvvULLMxRlm1VjlNKtYvmDE59nZLvY+GOYTITiMGOheGGa3M6tJ0GzoPSO5zNWvpYGvHWbZOdJYuS+N53FNqX9tJmvKFh56aEf4zzoYmdtfW8ZLTtSa+7N6E+I//A9kxveo/pnKlyg35TDH+o+z4oz0Py9LyI7hriUm62JOlllODLPdNCYzaKoFpjJZkSNeGFcOeMu1Rfk0e4St9pfn86xL/D+HQVk5P3N57SXiTd1ODvKNMMXJjxiGWXY/BcOcrBbDC6ns8qqQDM0wv/ryQ0ZS8+11ETjm3UsFxTCvFx3/N1tiLUugyXHXdcsy4Yhnkt5MLGDSm00+/OiX1ItltoB98rU2prlJCNlWmoNsS4DTi75Ik9+nX3MFSR52MobZOs1ml5v1mG3auLRrMQyzPYqTNxG5+mRzypAMxepSfi/WWNfetS+BTZCye33/b4e0mqEl3jZD3seu4NWfCws5LLMLw7dW3mkuco21wSnPTUIyBOuLNBnqGOYh1jDNn4z1R9ewy6sZZvWiypZhlRcM825nOqfc7QWUbKZhH/dVd+ifMsy2fqXlLc0QUuFWWi66/P2/dZe3+9EveU+Kv1LsWcMcCvaANHLl28dFnqUUZcowl8ezVh5/aQyzJmRVPubDb4t/aZ1cP0dKanYMty6r9PWOV19hdv1tZFSlMMx2N7dUwomS3XR3N1N+d01H0IxphuGNezvXYOO+I7F/aOO30f619D6ngVk7QxnGn5bwz9leaZPxOfukSaxO+qc0eX76NVcW7d9VGeYtg6mYGGZrwIV6zJQdcmLoKueCTdXtU0vGnByO1HatfV70ErreUlTUrrMrnxMr+cnX3sAOn1b0gZMc19qWpfd60g9p8nQZ5iU5r7m/Jn55U0iGps0Vy6xMlj2xw06Bh7QeBwrgxP1HvfRDa9rL7h03JMPucrZDqYRrSgbU7m528aP8PjvlarRCNgRDgv67rvHQHr3O57tI/oxZ3n1OEz/lFE2nLPLxNy0MuebL7eckv9KmrRf9kCZTc4DJsv+4xv9cG46x2WF245jXKr44hrkVimFFPak6ZWgpjer7a3UZ7xkEeloKtflCj7qTEzI5GznQ8P3HMIlldvvbJ157wzRioWF8tb4Uqr80JnLJ5h7phbOHzB4kIaV/cuyXS/IJ3S/JMK9llzc7zIZhDq1JxvV4WQyzEf/IqMm6Jg/LOPQmF0/KOqzt7L2+Z8MvpOq/leukfYNns2IGmjMolTg4gePkdboZsC95xATr9uYs68n70QqEXL2QJl8dx7zRv5x7/9KXfdg9cy9puF79ov3KVpZZCsNsZrIjnDZhujVk47VBrsX7lDPYEU9j4Fz8OA7WSb1O2MPKUZGg32v6r2KY53/+rnz/qfbvP+alt7PU5E+8RoVptI/vNOvCNv15zEt5ZDihVhijC3M/mfohSc4lWOYt7LL13TfpsHKYt5xMJIZh7mDXNqWjurQLHkzzpm6b5eWeQi9OErhqurAuDX1s2lCW5XMenJWjlOGYgKblY9tPSXRkMSyZC9Gb/zrpn5JYZsUw5z6pcQu7nM1h3qLfUhhms/wz5gcNCdpnnP3nON+nNLyWYT6FD2V9tui4tHddRrkl/R02kTgrFjnqb46lkrvUeyoPCHenuVe52HZzkI+avNSsx3TFQXY/rYlz7nEODPPMQCWFYR4d5o1nhdtpjJ9pm8E1OcNse+BC5nLKeuU2Ci2VNxwEUCKTPAP9HmSxon3q5D+bDKinvy0wWhOG0UnqJF1OHJwny/TX1JPe5M9B7rnGNZstIxfTXJ1hVsDoOOaVEzdJDLNiRQd/90SIlL8L2lcurvcpGV3NCgQi7OfwoaxbLmPQQjlWp926ctXPyfyKuv8FY5lbkPK2OnKNZfZb9YnXXL+toQ29zUEmLt616wPZN6S8TlXvePUV2RLdX/byj2zes7e5AOMwH/IxH+369ilCP4GLuS/FaZa+S5uSJVizu5myfm2an/har8E/m2VrNHuE8ZlXMgeNv9cSqyV9ib62c6p6gXFQx/G+tn749ZHcTznIP36kOf2kCcsIHekZ51/a97du+LNk6Oa2bWGY1Thx6cs+vLkOHArQbJzg8ZLS6A0M8wo8KevJQe9y1UG6/hb3w00ws/nJ3IR/sn2tDdQnWcdG5cdFBp949fVHK4S16zaRd6PyjbEvtXHONR6pckQyzIZlvlxFh+mRwj/Zy58h+PflMMyhFDY5VaheWZQMrmE41/8o67q+lvXf3Ipz/RbwqUE4xRyfOpasSasDtYTlek7Yh/CsXUcJco7pv7Vxjqlj7DNzDHOqf6n8z8s25F5265olJEMV+MkbVXq5MAHhT+jcazEM8yT/5wpqtDgFFlraiPs/ZUfMtYQGtvnYNElfkq4ZGrLf/9tAGGxJt5lrkwt1OY95Gc/czLED9MdffV1T8uOG9yn8Hvuy98aKochzivWm7g+1vsdNL7YIVLHMp/zHuQU9//kc8cvqO9kc5i0HmEhimId8jwJ3Y1M6n0Me1Qw4UtZ7i2Eo+W5OPKVnF9jWvkNYxlYL3/D7lBPrsn3G2STISB5c8T0Z8rDf7Tg4cUMdGclT63Dm+nDVkTX9NUd6uRzxy1kdZlXYp25UYRnpadXkMMzWwK7IS+XGPw5Ms1UvHuVRdsLp8lme9lPWf41hyPlOCfzG+uWRh9jy9uYkz7H32pMq7bhoriXf5+Ac5exPhjmtJz/ueMbi85jKLLPLgKP/5ewh5crSDvOGtKO52OX8DrOOY073mKUyzEOHPNuNJw05jGlr9ymZWvdI0xDjtxY/ynaUMyPTkucY5LX4WLzx/sGaJfZfbd3lRWQlR7pwd/K29M1hU1shObc0udeT80gcarertNxicSj9XG2ct/Qt/907XnPlYaq6jkDMxS7nd5hvvFxnVoqLiB2fu0xKlgzheT4pOyHFBg3K9uQ0IBTY5Kxvb2X1vvnPlXftuNVauqeYzJhf6/jEttNiwaG9qXWOkSPHZzhgnQMXN4Z5jX/56Az5l11XPUebhjKGWOaEmBwpDPPoyMhcsqZ2MCnx5Mw6j5v01s2wx6kp3l+zArYGP7P5b7qE3+u1lIE760ApoLBPvPq6A1E6H3IU0vfa+jBu/pPdP2vjnEvFh5CMFVklcrLL2RlmVaDNlpEyExDFMKc0PJWKr/w89RJrylJfriUu6kmBa1RqtDcXbihnXFpT/eTjN153eku14DywWqe99kkZvHM5Aa2Xs0W/a+uCZpg76H+PfXndrCS5dHwLw5wzfrmIw6wKnRxkYlE7wTjLY5hzqQqvcqidSQ6hByXazKFdvDRLVm2szowMXHSIp+h5R21HSZaW1WvNVr3moAfTTYoy+ycHnHNo6VqGObezXMxhVixzSp4UMQyzl+fRP8jFz8va2n3qDvhxdVqUcxAON/xCeNglv9bkyw3fFvFzV2E+fuO1h/Eiz9Gux7tD/BAb/zv87kthvXI4Ai2WYXQ6FHIYr3+19WDsm9YNmlsa5td/pq796fo99uXva1HFjur8qVer47ETYnwPS1yPfvnfZkub7CKdHdSBYfblGbgWlVYOIRnZ9AkhCo6dEKxXybuEK4cmheo7F7I0bDBKt/fTkA5B71NPvLMZpM4LmmyW26iPtXXgKKRkY3tW+HPF+3ftSUmu7rI2rVwzDLMCyrDMcT8pDLM1KDK3/O121EZOOp6R80mmWbnHvi1V31PkE8ovO2Wy4uxhD09JYb96kJVqY2495iD/3G3ipgscMM6BiWGY0345M2O4X85OWduMkQ3eAAAgAElEQVTC9SEmkUwQGOY2mMQeNv2JDiKN7I+x/RbPjf32VKy72WRkE7DbFW1c787OyCfhacMunh6Y2AL6W9uhm2z+K9A+pd/jeELf32vjm6v3TGKYI1dac2fHKBqSYRjmJygNmfEE7afHll/2so/kwrZqOdLzO9ZimKsKFR8HAgsIxPSLj914LeYXAV7gcUJiLaV1FMXARvonq56rLffS7avNT9TGN1d/OB3D7Guo+WpzDLOqtGKZ55szDV2XEpKxdfdw7Q526vs1jjSVjGfJgYizHklrd2y/kL78m2NwlMKI5cCidhkU+lpb3hRtrCnH2vjmarubVu6Ybp3Ssup+idjl4gyzYZmX45ilOMuqvTk3RXDbRBDDouXqILYcyXhyky/qcwiV8C3yievUTTUTRstfofVPSO34vhRmLLc9pCpP6ymB/j3uF+tmcTjJMBO0fxKxUaD/18Y3l76mxjCXYpdVe4rFMFuGeWn8kRK/rNopmRGNZdJydRLpeEpjWntrz5r+IJ3Rytn3pbBjOTEpXRa1ftaWMXV7S8vPL782vrnae4phnvMvm2WYNcv8qifsTfC7nbJN/4pimI/yrrpEvps0pL28AqlsWo7OcpzHVg6eJigJ7TEItNUftgxEH3vVNTm6RjdlPO4X399NW2s1tKZO1pRvzXZTyLomtjnbpxnmgP/o/78ku1ycYdYO842X708Nh1Ic5mBex5ggbjfo1J8yMXm/WkgGk/YHQxZQv+MgMqXDIcqZqX7HyndrPxg2/+0Vj3DmbKLH9RIeUhyAnM7E1rJq6+OWyefWtk9DpOT1Pyn9xTLMMfRS8w6zdZqHmYAdIg4zBjkOsz3Va2s35vl+DcMmfcmMp6RRqxACufqAdGartAZJcQRK43SqfC46WFOWXDAopQc1sc3ZpgnD7PmPLsP86F/Mf7Kf346iMcz2Y3PZMuyCtCiHeTbvoh+K4udhbeP+VmZtTQea5q8N5bFsA79wHl7UfxqyxbN/5NR/s6nqwGjhr2HaV+AgxSFYYxvXvqOcxLV4l3gv1yR0DR7S+6GU/uHGMLsLlz7jXJpdJgnJOGKY3ViU3dnuspd/eI2us3tHep7VGoZNep5MpH8LR25w2kRYImuDdHaL0kBLcQxKYsZV32rJjiseOXWgFrY526DKCsYw270vhz1yYhjmwWmeQVIUw5xbUxiVV8thZgQBqtIhAiX1vodBm1plpDgJuXBrQceoZdYCJjnkT41rjjrPlRGTVo6CXSZjmLXDfCJbxmUvF3LSn3sykoA8jm6ESUnHIdTRpOfJLJ2HE+WfYLAj+mfpPKbTgdty6ofeYFfihs6B+5MsqBH4PO6XPlBqDGddrtarCHx0KBQD/aJ27FrDx4govf9L0f+YLBniHGawzKxt7GLlajnMixXDA0CgAAJU+t4L21VARElFUjtlSZXL9HCrukQpm1YxWqMilLiuqV/sO0sMM5WzTMowW5Z5Ljbx0UIYZumdkboDSscz1mDgOVoEyPX8lVcHs/BxiuWWEnP/eCHM88eE6A2VPKTgFdsPqXAtbZ0/dePlJ+3jYwiyY9g2kmTJsB+bHJXtrDBIimMeBrijPKs7kwxgSALg533kf7/0ErXf8U4eJSwQ39b1Q0L9qZhlV9enS8QHCzEsqeNah2IUxONxv9hO6MbHXnW1GUEK4lGj/JKT1B77V0k8SzvIfvkmJMM7l9q5FsswW5ZZdcj9bj+cyy0lhlk6I0rdCaXjSW148L3TCFDrt1ubj77y6uG8w1gGCc+VzbJSm6EDI7rdYvXar6TELysN+NSNV0z8Rdd/pMiM4WohKcOsHeYbL3fPBNZ1EcUwr8gnWiK/5Zq8pkv1oHYopOfJXMIb99fl503FjVqvQ26AZg81c2IZRLvXB9eGUeWDR06dGRlQPu2rhXcOth94nu1y6uf2acv6EjjFL5PHMA+hGa96gjJ/g/1DDPN6haJ8k7oTgmGmlG6f36LW6VMoKzZs/J1Yg9QP4f4kawbwCK9ZN6ovj/+lDyYZJfSfkf+svTqSJLgTD3/yxsuDK2+Uscu2iuQM88AyOx6zmMNLVDofwT9q5wIOs2BlYtA0an2OabJahp9zh/13fXcZ96cIAJ/pdAL60Zd+SAnJUOEYIY/50b/4EXL/lfyDVm0/8aon7O0Km0yGWR4DlGO5LMZpsM+czFMLRkkco0TJGHJ0lq3ea6bM39Tl7xPHfRO6Eto/D3yAT6f6kcrMp4zJ1M/OM8xnuxrOsmp7NYf5k6+63Oz62+/kHI8NhjlrfwLDnBVOFLbbNRHb5272skKTN/2mnB4hgAX604++SWGXle0LMcyUmTHcgbOaw6wYZlsRMQyzkLyYp3bfU3XGXnc3I+9ucAVucxaJluL6PvqKqzDBAQJAAAgkI/D4X06L/U7+AOELimGe+9WIX67KMKuPa5ZZZcl4+YcJRVDuU4YRle3yUCxl94CjdD3h1D7qUKJcFuajr7ROcyjvrv0S7s/nJQY+BgHoR0/6ISkkQzPM+jeukdQKx6juMFuWGQwzJ/dimeErydSBWV7GH/l34/sL1YpILifZLwdMcylkUS4QkIeAJHZZk6ozDHMtdrm6w2xZZikMs2qP3uU+5FF1ZvZ2E4qAvyWckB5wk64XnNpHsRJCNdxqpln2whXaB/mCKcjAhEhil5V9HRlmY21rssssHGbFMkthmBWg01yQVEMq/Xdyscy94EUvoX6/mEs3uSCoWGZzMuo4otpr/6/1PHHf4AV8pjhAP8zMRGr/kMQwT9jlw4SyJrvMwmHW0SnqWDohP5tHVUhzgs3YyjL3gpN0PeDUvq06yaktCM3gLA3UDQjwQ0CSs2zZZXdiU9tZhsNcQOfNRp0MayuNrMGmsnm94YO15vJrzdKWIUNm6aOvuPJgFeyR4Va7LGPmXO/3JkJGWyPcNwMv8Bn0Afohrn980y9/qIBHU69IP34ZDrMjCyksc4/M6RKz1yMm9cxMP19e0juJSGAToESpok1AYDsCMhlmQz/Wjl220qmWh9lXDykOMwa07R0fJQCBJQSkDQ5L7XXv//NvXpnyOJ4FAkBAOALf9Cuy2GUlLs0wM4ldhsNcsAOZk7rGpWi7JGpDNXBtloiBh1kkhj7E60NqCFDBbl61aOU065N/7c87ym04GRr3DQLAZ3LUHfTjcHK4kP7xeJHhGGZ85BCKAYe54HAHlrkguCi6SwR6ZpRDAgfT3GVXQKOBwAQBkezyqy4ftoI95pf+lk0kBJuKSAnJUJqsU6XpKbxlNmw+ZmeegvvAB/pxYP7C/aOXDX1rfYCPqvCM0EFuzt7jgVE8MK24PjCMwAP603j/ebzUcIyDAwWGeWZ0EOUwT/Kmzq/5jHlVcX9uzRT42Ly7feoHGOV4F9owzXPZSNyFRNw/zl4EfAwCoaxOwIc7Pt/0Kx+ONxSNPPlJxS4ffpzYZdtT2MAozWlmAywqAgQaQQCO8jpBueEZ1v3x3R1cT90f4AE83OlCi/ogMhzjxisOBMCOVfwyHOZ1Y1PUW//8iitnT+byT1rC9fTkJeDRHx7SNqxEGYgCD/3zb6qB5vDzNrkdfQ73J5vggI+HAPSDvX5806/KY5eVFlqGmRu7DIe5wKBli8TGv4LgomgRCIBNzi9GnT1D8zO+xzNe4z7wgX603z8khmNoh/nAMHOKXXZXH/Jb7ZUlSgrJmOxgxxqpIY5bXPNqfc2OWf0lLiGuNHfFXvvn37jihLtsPht2F3Af+EA/uPcPqezyJ151uZ7wc2SXwTAXG7JMwYZlLn80cE9HcQPPNvUJbHJhY+MVr5xmPfLYH3cPAPWDvKCv0f1VLLv8qsvZOstwmAuPYciTWhhgFM8aAbDJdcXjMs3H0yx7YE5ouo375kAh4DOfQwP6UUs/vllo7LKylophfiyjvMu+BWeTh3kgQvYqQ6iM30df4RxhCwYFDEoHDAo28PGyXQPT3ObCBBaUIDcsoDozFqnMsrKa6ihsjnHLrkWHw1x4fDM710N5LvF/jIgyRkSwyYUNycbi/+k3xtymG4vC60AACFRC4Jt/9SOVvlz+s9zZZXYhGapCkjb+qfaMu9bhNmN6IGt6ACe5/CCS8wuabR5+WPLCklcHS16C9P2bBDvL2hk9O2NH4CIkI+cIFFkWTuLCSVISTmKTvBwY2ZWbfwxMc/MiRAM6REAys6zE2QK7DIaZqON99DevRK4MBKY0GZjz+F/5EFEvwWeoEDBMs4xQILQDcuxh7VZqGjlr81pgl1k6zDLDMq6QQDAe26VB2wPjL+4bBBoi2MEiU7mt9b/zT7+OuOb6UkANgMBpBL75P8mNW4bDnEH7pcUxK0gmx9YOHlQILP+kk7lImlPJRPD+9KQU4DdFYNQPOMgZDFbDRagQDa0NTkgzroEH9OFAdFi+o1L/kB6KofmkBmKXXe6PnbkX6TBPNtywgxwV6gQB6Ut7nYgxezP/6defgKCpJoOmEJIhNSTjm//T32bv5xwLhMOcQSoinWadYg4/IECHABhkOqxb/5LeELhXjI/DsM74Y7gPfAYGGvpx5K/n6B/SM2K4thIOc4aRQ6TD/BtX7PY7e0IS/pqTkoBDDhzAHGcwOihCI2DYZvyAABCogUAvzHJr4Ri6vjUUIuabEh1m1e5pLtQYJPAMEJgiAOcYGlEaAYRoINRBaqgD93bBYS5t3daXz9ZhVk2S6DQPm2wcmTWURGGSjMo2AfUvkwSlhw0f600X3qRAwLLNR8ecHEI3bB1w3zsGBfjo0B7oh0Egtn/AWaawauu/AYd5PXar3wTLvBo6cS+CLRYnUpENmoZp+Fl4cD3NygM8gIebxSpOH3pylpWRbCl22SUH2Rp4iQyzAttursGmbJnnJ/SQN5Ot0UDFiiLwj//93yGbBgy3TMNdSa7f8p//rmif5Vh4i86ydvI5gunWSbLTHAplwP/bPb+rp93N3G0H6lcGAc02H60xe6MJ7nsxGsBn4m1AP7R+9MYqD0xtQ7mXXSsKh7nMmBJVKk7aioKpmYfALDcjKlQ0AwL/+N9VNg1sjuO+iQz140lB9cgstxqK0URIhqqkVIZZte2ff+NyDDdCFnjBLGfwwFBEswj8k3KeefolqBfkwm5e1yuzDIeZwMRLdpqn6ZvceUz6poFRFHGbDPB8Hrx7Nn4E3R+faAgBE+OMHxAAAiEEemWWW3eWdf1bUGvJDrPCf9gEmMd/GxkVlGcQKDh/QBhGCxYEdaRGQDnOI7EZOqDIEn+4P3+AE/AxAT9y9OOb/3Mfx12H7E2rm/1cV4raliZ/T7zDjJO1knWCywtgl7lIAvXgiAAYZ45SQZ2oEeiZVR6czUY3+rm6AoaZuucEvjeGZtgHsI2Y+zZzOMtMOg+qwR6BKePMLqQUe0mE7CXhuAUVzvJhoRcOM52dls4y69CMX3/Cbn84IQp/VWJztemT5184y3R9H1+ShcA//pqKc+bo2mB3HOSSTy+/5b/8vayOu6E1rYdiNBWSoSrbg8NsneYNeolXCRCAs0wAMj4hHoFJuAYW1LgvqKF+7nr8gr6CVR7NlxRnWbWoiZCMIUhhr/hG+b+BabY8jGVacW14qYp49L5pQ37vQwtrIPCPv/Ztk8/6/ohfJ9w/PXgDnzr4gFU+th5wmGtY1I5YZjDNlRTsxGfBKvOTCWokEwHNPA+xWKHgWhurhfvz+a+346OcPx0+Mxupsr18w3zIkB8Y5XlbJMlZbo5h7ik0Q7UVO8x5OAQwhjzkgFr0h8A//Nq3Bc9DsWiEIo9x3yCQgk+IIZ2TA/Dd7b4VcconjRIc5so2u5dYZs0y66NnjcWzhIudketrx2LhvgnVyI0PQjAqd3h8HggcEPDDNgBMHgRSwgggA4N5CmZ5pNReKdKc5SYZ5t5YZjDNdQwFWOU6uOOrQCAWAcV6ugzqhPF0gnhnzy3q/P5Wh+8f/tu3DYxNT/iCUY7tnYrA0hSWqF+TDeqJZbbaZvKYhk48wv/nT8pahwucZVE2Do3pBAGwn2FBb3WQQyX3gnkp/KR2TYnOcrMMc48s88A050sTGQ5u6zgdKZxlqSY8T7s+/oon6H7z2F/q+4jbPGiWLeUf/tv/VPYDjEv/1v/6/5HWTiLW1BiSCqzgx6Q6y3CYCypNqaKxEbAMsnCUy+AqqdRPvPJyZ1P/Hk5zg8IdNq/5SR4avuYWJqBY56MkJ43gCyZ5e6eGw7wdw+wl9BiW4YZnaItkt/1pRnjGIuG+gewEPt/yX/4uu26iQFkIfPyVTwimvwLTLEfWnFnSVtlOzphazW0VW449T7Kz3DTD3GtYhttJwDZvMxlglbfhJ/3tKaMcjmB67C99RDoUaJ+DgGFQ92pT06a/vTpqufBbi3+vuJfuxNKdZTjMpTWIoPxTeUo7DkUO58PfIXcmgVo2/YmBUbatmE0D4DQRMc1NyxuVr49ACSYajjGdXHtwlpt3mMEymw4xfzJWI0FjwybG8vVFfBqdAW31S8pZttloLKcce43wjFaljnoDASCwBQE4zFvQI3y351hmH2a91HXiZKdeGWdum2IIuwc+FYmAzn6RwCjrjjbz/GN/GdkzIiHHY0AACAhAoBdnWQTDDJb5uMcNjHPoUFSVT3zYNDjjYgu5D0ZZgDUu3ISPv/LyQy/ZH5hlyyuPm2rNRDP+PmKaCwsNxQMBIMACgZ6cZTEOM5zm474znMTUIeX8rf/171kYE1SCLwITRrlQNcE2FwIWxQIBIFAdgd6cZTjM1VWOpgJzpzE5SelmK9HifTDKNPrU8lf0hj7FKQ9pGG3s/OGvDWrKdB9sc8vagroDASAwh0CPzrIohxks83LHVhk1go5CyEFg/n/EJy/LvfcnKNjkUxiDae5dA9F+ICAHgV6dZTjMcnQ4qSXu5kD7os8oc74Gk5wk7m4fVvHJw4kjduLnM8hE12Cau1VDNBwIiEIADrMgcSJrRpowS+S/TKtB3NPIqRmHU+9PfewVKi1c8GC+6llkwDb3rqFoPxBoF4GenWVxDPPAlqpjmPBLRsCGbNgTlHaHbBm1ruEkJ4uw2xd02MXgKdsYZes587qG09ytmqLhQKBZBHp3luEwN6u6NBWnZJ/hHNPIVNpXfEZ5mDQH8pFzuQ+nWZomoj1AQC4CcJaNbBUnI/KH0IxyYtUx0A6JPzDQh0+euoZjXE4uvZSsY5OZb0aNrR9im3vRWrQTCLSJgLK3j/vlvxXrK6ZIRTQIcJpTVAHPAgHeCCg22c7yT8Vc2ciMUGu43X8cTgfkrXioHRDoFAFlcx//K38n2k9MEa1oIOAwp6gCngUC/BConRKOChGEaFAhje8AASAQg4CyvY+DszyBSrTDrFoKpzmma+AZIMALAcsmzzHKPkMs5RpMMy8dRG2AQK8IWPsLdnmqAeIdZjjNvXZ5tLs1BEY2WYoLbCWQ1h6wza1pLuoLBOQg8PFXmPz1YJePZQqHWY6eoyVAoDkEBiaZ80k5etbtbZEufA22uTlVRoWBQPMIgFk+LcIuHGawzM33YzRAEAKKSfb9Tb95uG9SGIFtFqT4aAoQYIyAXeEDsxwWUjcOM5xmxj0VVRONgGYtCjOy0ssH4yy6i6BxQKAqAh/7zcPBT7sdsmKckERXDjOc5qp9Eh/vCIFesltQixSMMzXi+B4QkI2Aa6vBLiMk4wgBZM6QbQDQOnoEPvab/24M8tV73BxKGddZ8Xjcr/wtvYDxRSAABEQhoFll9TvY58f/6t93R6CmCrRbgOA0p6oKngcCIwL+kdTWPcbfw/gTOJo7Jz4I00CPBAJAYA0Cvv0GsxyHYrcOM8Iz4hQETwEBhcAQ45bT47PZ1vB3jPFegS8YZ/RRIAAEYhGY7CnZ73aP/1Wc5BeLHRzmWKTwHBDoBAE3i8UK/82s8BEwrPjOiDNimzvpnGgmENiAwNzKIA4niQe0a4cZLHO8ouBJuQiYWDa4uHTBFOVcfbDNcvspWgYEtiAwZ+fBLv//7d1tkts2FoVhaSl2Mp6q2J69xc7HLGGSOMneJnGqJnHspWiKYlOiZFGiSADCx9N/utQkQeC9F+DpIxC4j2jzgplovi9hnF0+gfG20+W3RgsuETC/WV4ggEBHYGq85yzfnx8E8xMzLwHenzyuyJfA4Q3ofKuoZgkIcJwTQHYLBDIlMPUc4CwvCxjBPOJGNC9LIlc9jgC3+HHsS7ozx7mkaKkrAusI3HoucJeX8SWYz7gRzcsSyVVpCAzrHe82u8129Hqdz3jMyQeOc5p+6i4IPIpA5ypfex5Yb3l5ZAjmC+yI5uUJ5cp4BM6XA7IchXcVl76ryXGO10+VjMAjCMx5PnCW10WGYJ7gRzSvSyxXhyVw3EnPahY1rGaRy6okHOew/VRpCDyCwJznA2d5fWQI5isMieb1CaaEdQRsGGJB51TfJBDP6/qqqxF4BAHOcjrqBPMN1kRzumR0p1MC47low5w0v/u5yjjE4/Ds6990RQQQyJzAPc8Hq2KECSbBPIMj0TwDklOCErAsXFCcCltAgOO8AJpLEEhA4J7nA7EcLiAE8x0sCec7YDl1EYFPv77moHKQs3LQOc6LurKLEAhO4N7nA7EcNgQE8508ieY7gTl9NoF7XIPZhToRgYAEuM4BYSoKgZkEljwbiOWZcO84jWC+A9ZwKtG8AJpLrhL49MvrfvGHQ5I9vezmc09gWBwEjyx4cJ0NaAjEJ9A5ykvGv+dvfqftIoQH1IVQieaF4Fx2QmCJcwAhAjkR4DrnFA11qYHAmucCZzleBhDMK9gSzSvguXRzcA8O64YNULabzW43cpy7btpZrI73BPDJNT84zwY2BJYT6J8Jy8c3zvJy9nOuJJjnULpyDtG8EmCDl69xDxrEpcmFEuA8Fxo41U5OINQzgbscN3QEcyC+hHMgkBUXs3cPbNRno77hC4OGfnOeKx7YNG0xgcO7KwGeC8/fmre8OBAzLySYZ4KacxrRPIdSm+d8+vVfqTZsc5/RO4IBnkN4Bub57Ov/tjkIaDUCTwRCPw84y2lSi2COwJlwjgC10CJDOgiUW2Dl1pDDm/M3G8/e2Fmw0OFNte8gEOsbRs7yHUFYeSrBvBLg1OVEcySwhRQ7OAhDda2KZtU8qwYeO++1/mDucyGDnGreJDDMTY41/nOWb4Yg6AkEc1CcnxdGOEcGnFnxe0fZDwIIBCPAgQ6GUkGJCKR4DnCWEwVzdBuCORFzwjkR6AfepnOVu5/+m/6jp+AzHvIhXH8wBzrcIHecS/t5fLyoOZ/zYTm4ROM/Z3l+bEKeSTCHpHmjLKI5IeyEt0rhJiRsjlshUBwBLvTtkK0Zp/D9nO8anrejNX0GZ3kNvXXXEszr+C26mnBehC27i04d5c9XSxsqPPVumeM9AXwurzYoP9bnR4tudDdv9mybo4vvfK7JrxbnmQ9cL2wjFZzvVPw4y4+VAQTzA/kTzg+Ev/DWj3IVFlbXZQggcIVAic5prmNQiSynUiNHxpzlxw9lBPPjY7AhnDMIwo0q9MvDDVuW+t1vzYwDDm3nwRoHe/8NVeX9aA2fVE+FEuLw/M1vtFqqhLhyH0HIIAhDFQjnjILxVJUcnYb8KKkRAgggsIxALGe6lrGbs7wsr2JcRTDHoLqwzI+/vN6/TP/sjZ2wFiIMdtnRUR4m2Q5Oms/9pGM8eoddPsgH/cF4EG88fP6Wuxzswb6yIIJ5JcDQl//986vd8FLBcztghcZ7s7yPv7y2oZ4N9XLeGE9+yk/5eeVl4Zo28OQu33xkJz2BYE6Ke97NPv78ejd+bz7WV1bzalP/WZ9+6dZPPliFjQzF2ju9PkdNj1xxFmf5XOL4Tiznpz0I5vxisq/R2GkeD3fEc5iATTnJh/nkE7LZ8Z7AlAzDBx/5oX8YHy7/mzZ3fCSWwzznQ5dCMIcmGrC8c6f50AWfVicgnu+DfXCSh9Udpr7cdryfo4zP5X8N5If80D+MD5HGR2L5vud6yrMJ5pS0F9xrymk+X9iceL4M99qc5OEKG2fYOOPSvwfyoyegf+gf+keajamI5QUiKeElBHNC2EtvdXSazx/hlz+3Lp6Pc5Ln8ToOhc4/lUh44DGWzPJBPsiHWM8LYnmpQkp3HcGcjvWqO11zmm+90lH7ahvdEnBebfJq061+4HiJrz7Ja3lbf94Sy6vkUbKLCeZkqNffaHJO850LTZXsQPeL0XuE1P8I8S+QPNfP9fP6xwFieb02SlUCwZyKdKD7/P3u1S6WXnz+9rdAtQxTTDf/mHXs/4NY+a5cqykaX4wvjxwHvnj7Ow0WRi4kKUWwkmAOe5OPP786vKM9VfLwf3mM40sd6vFWpTHr17VZ+b0/GSP++Mov/Uv/Mr6sG1+J5bC6KEVpBHMKyhHuEdNpfuR/3Bwfjo/84/waB4wDNY8DxHIEUZSgSII5AeSYt+jc5lhv7R7rPcwlHP7ic+8x4tETkA/yQX8wHhgPb42H5ivHVEPxyyaY4zOOfoe92+wHAQQQQAABBLIk8MU35itnGZg7KkUw3wEr51PXLDvnXXTvFtb/Lro1J/Rz/Vw/f8w4wFnOWT3NrxvBPJ9V9mcenGbfkPuG3DfkviH3Dfmtb8gdN6Mq+owyznL20ml2BQnm2ajKOJHT/BgHgXODOweXg2scMA6MxwHOchm6aW4tCea5pAo6r3eazxd+8vl0ISg88BgPf/JBPsiH42NOf1jbH7745j19VZBumlNVAZ1DqcBzLDtnWaaal2Wy7Jj8lt+WH8x1HLBsXIGiaUaVCeYZkEo9xeoZpUZOvRFAAAEESiRgznKJUZtXZ4J5Hqdiz/rMaR5aMjXZzvGeAD6XJyPKD/mhfxgfjI8Xx0fOcrFSaVbFCeZZmMo+6XROs9dSvJbi9TSvpxkHjAPGgZDjgDnLZeukObUnmOdQquCci3Oah3ZNjZuO9wTwufxckR/yQ/8wPhgfN5zlCkTSjCYQzP5AV2UAAAvwSURBVDMg1XTKcV7z+VvQ5610/PQtaXxOCcgP+XHt8SE/5Ef9+WG+ck3q6HZbCObbjKo7o1urOde3i9XL6gdWP7D6gXHAOJD7OEAsVyeNbjaIYL6JqM4TPrx7udtutptOOfuNgzzQD4wDxgHjwLxxgFiuUxfdahXBfItQxcctO1dxcDUNAQQQQCA4AWI5ONJiCiSYiwlVnIpymuc5CpwXnDiwHFjjQNvjALEcR4eUUirBXEqkItaT0xwRrqIRQAABBIonQCwXH8LVDSCYVyOsp4AP77qXAXeb7Xa72e12o+WStpvu75tt/9txfOSH/nF8Kcv4YHys9/nw5bfv6aR6ZM6qlkiEVfjqu5jbXF9MtQgBBBBA4H4CXOX7mdV8BcFcc3QXtq0Tzb2DeO4o9yvU7+dyOo6P/Dj7xkX/MD4YH2t5PnCWFwqIii8jmCsO7pqmcZrX0HMtAggggECpBDjLpUYubr0J5rh8iy/9w08vuy27Tn+GrVCnWud4v5U0PpcJyA/5oX8YHzIcH7nKxUuWqA0gmKPiraPw/RSNp42nhhYNn89/O94TwGf0zuiIh/yQH/qH8SHH8fHLb36nh+qQLNFaIUGioa2r4BOn+Wnu6qGFPveriAw/eOAhH/QH40FPoIDxkFiuS6/Eag3BHItspeUOc5unHOdzB9HnU0cRDzzGDqt8kA/y4ei4p+4P5ipXKlQiNYtgjgS25mIvzmuuucHahgACCCBQFQHzlasKZ5LGEMxJMNd3kw8/vdrtv2l7mtt8WGXO534/h2FVPjzwkA/6g/Ggn5mRyXjIWa5Pk6RoEcGcgnKl9+A0VxpYzUIAAQQqJcBZrjSwCZpFMCeAXPstxqtoTL397O+XV43ABZdhlT2/p1eP0E/0k7X9w4t9tSuR+O0jmOMzbuIO3OYmwqyRCCCAQHEEuMrFhSzLChPMWYalzEp9ePdql80kNZOIn5ZzymTSoHiIR06TWOVjM/n45bfWVy5TUeRXa4I5v5gUXyNuc/Eh1AAEEECgaAJc5aLDl2XlCeYsw1J+pbpVNPrlEQaH02885IP+YBwwDsQfB8xXLl9D5NgCgjnHqFRUp8FtttFJ/9LO8IMHHvJBfzAe9ARCjYdc5YrEQ4ZNIZgzDEptVdq7zaw11hprLb61pp/pZ432M2K5NuWQX3sI5vxiUm2NxnObzx2F80Y7furA4nNKQH7Ij2sPL/nRTn4QytVKhuwaRjBnF5K6K9S7zSG/hPOlJp7yKdyX2vqT/lROf7ICRt16IbfWEcy5RaSB+nRO82FVp2HL2KmtYx3vt5TF5/LWuvJDfugfTY4P//juPf3SgF7IqYkSLqdoNFYXc5stI2Juu2Vk7GFnHLhnHDAFozGhkFFzCeaMgtFiVf766eVu/7iYclDtu9G/woPPZYdZfsgP/aOZ8YFYblEl5NNmgjmfWDRdk/6FQE7LPU4LXvJFvnDoWxgHzFVuWh5k03iCOZtQqEhH4K8fX9rwhA6kA+nAFnSgPL+R5//41jxlyiAfAgRzPrFQkycCx7nNA5JBQfrcE8CjVxryQT7oD7WOB6ZfkAS5ESCYc4uI+hwIdPOb7cNgHwYzdex3YhxoZxyw+gURkCsBgjnXyKjXnoC5zb6b9928OTrmLrQxDpir7MGfMwGCOefoqNvRbe7mNvtBAAEEEKiOAFe5upBW2SCCucqw1tmo/YYnU8uInX9jeb4Mm+OnMxvwOV2mTn7Ij/HMF/0jWf/wYl+dz+saW0Uw1xjVytu0X0nDDwIIIIBAsQS4ysWGrtmKE8zNhr78hh8d5+2mezvwONPTZzzkg/4wzHw2HuQ0HnCUy3/2ttoCgrnVyFfSbm5zJYHUDAQQqJ4AV7n6EFfdQIK56vC207jxhifbbb+V9GA5+4yHfNAfjAf9JO1HjIfWVG7nWVxzSwnmmqPbYNs4zg0GXZMRQCBLAhzlLMOiUgsJEMwLwbksbwKXHOfBWZlyWBzvnWh8Ljvy8kN+6B/zxgeOct7PR7VbRoBgXsbNVQUQOHWbhzkaQ8V97ues4NETkA/yQX8IMR5wlQt4OKriIgIE8yJsLiqJgGkaJUVLXRFAoEQChHKJUVPnewgQzPfQcm7RBAjnosOn8gggkCEBQjnDoKhSFAIEcxSsCs2ZAOGcc3TUDQEESiBAKJcQJXUMSYBgDklTWUUR6IRzv6D/sLGB33jIB/3BOHBtHHjx3R90Q1FPOpUNRUDihyKpnGIJcJyLDZ2KI4BAIgIc5USg3SZbAgRztqFRsdQExsL56LAc9j85W1NicCIdP27BfIwYfvKjd6r1j9L7x4vv3tMJqR9G7pclAR0hy7Co1CMJcJwfSd+9EUAgBwIc5RyioA45ESCYc4qGumRD4M8fvtoddvA427Fiu91uduMdDBw/2dECH/mhf4x2ACpwfCCWs3kUqUhGBAjmjIKhKvkR4DbnFxM1QgCBOAQI5ThclVoHAYK5jjhqRWQCY8f53EH1+dRRxQOPscMsH/LPB0I58gNE8VUQIJirCKNGpCTAdU5J270QQCAGASI5BlVl1kyAYK45utoWlcDedd7/jN+DH68LMNze8c1n6yU8ocNP/ug/m5T948X31lGO+mBQeLUECOZqQ6thqQhccpwH2TxVB8f7fzPwuUxAfsiP0P2Do5zqieA+tRIgmGuNrHYlJ/DnDy93l52iKYfZ3/E6X6nY5+lvbPSXJf2Fo5z8UeCGlRIgmCsNrGY9joA5zo9j784IINAT4CjLBATCEiCYw/JUGgInBI7znIFBAAEE4hLgJsflq/S2CRDMbcdf6xMR6KZrnO1fMLUvir9vT/ZBwQOPzZV9guTHlpucaBh3m8YJEMyNJ4DmpyXQO87DXMzh3j73czPx6AnIB/kwrz9wlNOO3+7WNgGCue34a/0DCXRznTlnGw4hB5mDPL2T9mf948X37z23Hzhuu3W7BHS8dmOv5ZkQMM85k0CoBgIZE+AmZxwcVWuCAMHcRJg1shQC+6XpfCPvG/l538ibwVH5DBZucikjt3q2QIBgbiHK2lgcAa5zcSFTYQSCEeAmB0OpIASCESCYg6FUEAJxCPz548udd+JGbDnwHPgKHXhucpzxU6kIhCJAMIciqRwEEhAYnOfzrZN9Pt1KGQ88xg+3XPOBk5xg0HQLBAIRIJgDgVQMAqkJXN6Ke6jF1BbLjvcE8Lm8BbX8iJ0fnOTUI6X7IRCGAMEchqNSEHgYgUvznc8dtfPKOX7qwOJzSkB+hM8PbvLDhkg3RiAIAYI5CEaFIJAHgd519oMAAjkQ4CbnEAV1QCAMAYI5DEelIJAdgc55tjGKjVFsyZ52q/V//vsPz9XsRkMVQmA9AR17PUMlIJA9gdMtuYdlJvyenss8NcfZ3y/PfW6bCyc5+yFQBRFYTYBgXo1QAQiUR+B///lqx3lM6zziXQ9v85HLG/PUGIG1BAjmtQRdj0DhBE5X2xgak+tCXOrXExCf1AvnEcmFD3Sqj8BKAgTzSoAuR6A2Ap37XFubtAeBewmYi3wvMecjUDcBgrnu+GodAqsImPvc9tzc1uZ4m4u8arhwMQJVEyCYqw6vxiEQlsDefT7fmvr8Fo6fbl2NzymBjPKDixx2fFAaAjUTIJhrjq62IRCZAAeaA13Sqhkc5MgDguIRqJgAwVxxcDUNgdQEzH9OTdz9rhHgIMsPBBAIRYBgDkVSOQggcJFAtwpHt4PKflm1TedHbjeHz7tuY5HRZ8fxWZgfxLEBCAEEYhIgmGPSVTYCCHxGgAstKUIQIJBDUFQGAgjMJUAwzyXlPAQQiEag30jluPNg70D7PHjyrfMw9zha11MwAgjMJEAwzwTlNAQQSE+AG52e+SPvyDV+JH33RgCBawQIZvmBAAJFEehW5tiNtlYZtpweGuFzvwV17jyI46K6ncoi0DwBgrn5FAAAgXoIcKTziiVRnFc81AYBBJYTIJiXs3MlAggURmCYKz22qAdH+vi7X7Xj3KF1vHeuh1VNOj4EcWEdQHURQGAxAYJ5MToXIoBArQRad6oJ4VozW7sQQGApAYJ5KTnXIYAAAiMCuYps4leaIoAAAusJ/B9Lvf8/QrCq1AAAAABJRU5ErkJggg==";
+
+ class nexuskittensgrab {
+ getInfo() {
+ return {
+ id: "nexuskittensgrab",
+ name: "S-Grab",
+ menuIconURI: icon,
+ color1: "#ECA90B",
+ color2: "#EBAF00",
+ blocks: [
+ {
+ opcode: "usergrab",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "grab [WHAT] count of user [WHO]",
+ arguments: {
+ WHAT: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "WHAT",
+ },
+ WHO: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "john",
+ },
+ },
+ },
+ {
+ opcode: "rankusergrab",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "global [WHAT] ranking for [WHO]",
+ arguments: {
+ WHAT: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "WHAT2",
+ },
+ WHO: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "john",
+ },
+ },
+ },
+ {
+ opcode: "usergrab2",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "[WHAT] of user [WHO]",
+ arguments: {
+ WHAT: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "WHAT5",
+ },
+ WHO: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "john",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "projectgrab",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "grab [WHAT] count of project id [WHO]",
+ arguments: {
+ WHAT: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "WHAT3",
+ },
+ WHO: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "717954208",
+ },
+ },
+ },
+ {
+ opcode: "rankprojectgrab",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "global [WHAT] ranking for project id [WHO]",
+ arguments: {
+ WHAT: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "WHAT4",
+ },
+ WHO: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "717954208",
+ },
+ },
+ },
+ {
+ opcode: "idtoname",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "name of project id [WHO]",
+ arguments: {
+ WHO: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "717954208",
+ },
+ },
+ },
+ {
+ opcode: "idtoowner",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "creator of project id [WHO]",
+ arguments: {
+ WHO: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "717954208",
+ },
+ },
+ },
+ ],
+ menus: {
+ WHAT: {
+ acceptReporters: true,
+ items: ["follower", "following"],
+ },
+ WHAT2: {
+ acceptReporters: true,
+ items: ["follower", "love", "favorite", "view"],
+ },
+ WHAT3: {
+ acceptReporters: true,
+ items: ["love", "favorite", "view"],
+ },
+ WHAT4: {
+ acceptReporters: true,
+ items: ["love", "favorite", "view"],
+ },
+ WHAT5: {
+ acceptReporters: true,
+ items: ["about me", "wiwo", "location", "status"],
+ },
+ },
+ };
+ }
+ async usergrab(args) {
+ try {
+ const response = await Scratch.fetch(
+ "https://scratchdb.lefty.one/v3/user/info/" + args.WHO
+ );
+ const jsonData = await response.json();
+ if (args.WHAT === "follower") {
+ return jsonData.statistics.followers;
+ } else if (args.WHAT === "following") {
+ return jsonData.statistics.following;
+ } else {
+ return "";
+ }
+ } catch (error) {
+ return "";
+ }
+ }
+ async rankusergrab(args) {
+ try {
+ const response = await Scratch.fetch(
+ "https://scratchdb.lefty.one/v3/user/info/" + args.WHO
+ );
+ const jsonData = await response.json();
+ if (args.WHAT === "follower") {
+ return jsonData.statistics.ranks.followers;
+ } else if (args.WHAT === "love") {
+ return jsonData.statistics.ranks.loves;
+ } else if (args.WHAT === "favorite") {
+ return jsonData.statistics.ranks.favorites;
+ } else if (args.WHAT === "view") {
+ return jsonData.statistics.ranks.views;
+ } else {
+ return "";
+ }
+ } catch (error) {
+ return "";
+ }
+ }
+ async usergrab2(args) {
+ try {
+ const response = await Scratch.fetch(
+ "https://scratchdb.lefty.one/v3/user/info/" + args.WHO
+ );
+ const jsonData = await response.json();
+ if (args.WHAT === "about me") {
+ return jsonData.bio;
+ } else if (args.WHAT === "wiwo") {
+ return jsonData.work;
+ } else if (args.WHAT === "location") {
+ return jsonData.country;
+ } else if (args.WHAT === "status") {
+ return jsonData.status;
+ } else {
+ return "";
+ }
+ } catch (error) {
+ return "";
+ }
+ }
+ async projectgrab(args) {
+ try {
+ const response = await Scratch.fetch(
+ "https://scratchdb.lefty.one/v3/project/info/" + args.WHO
+ );
+ const jsonData = await response.json();
+ if (args.WHAT === "love") {
+ return jsonData.statistics.loves;
+ } else if (args.WHAT === "favorite") {
+ return jsonData.statistics.favorites;
+ } else if (args.WHAT === "view") {
+ return jsonData.statistics.views;
+ } else {
+ return "";
+ }
+ } catch (error) {
+ return "";
+ }
+ }
+ async rankprojectgrab(args) {
+ try {
+ const response = await Scratch.fetch(
+ "https://scratchdb.lefty.one/v3/project/info/" + args.WHO
+ );
+ const jsonData = await response.json();
+ if (args.WHAT === "love") {
+ return jsonData.statistics.ranks.loves;
+ } else if (args.WHAT === "favorite") {
+ return jsonData.statistics.ranks.favorites;
+ } else if (args.WHAT === "view") {
+ return jsonData.statistics.ranks.views;
+ } else {
+ return "";
+ }
+ } catch (error) {
+ return "";
+ }
+ }
+ async idtoname(args) {
+ try {
+ const response = await Scratch.fetch(
+ "https://scratchdb.lefty.one/v3/project/info/" + args.WHO
+ );
+ const jsonData = await response.json();
+ return jsonData.title;
+ } catch (error) {
+ return "";
+ }
+ }
+ async idtoowner(args) {
+ try {
+ const response = await Scratch.fetch(
+ "https://scratchdb.lefty.one/v3/project/info/" + args.WHO
+ );
+ const jsonData = await response.json();
+ return jsonData.username;
+ } catch (error) {
+ return "";
+ }
+ }
+ }
+ Scratch.extensions.register(new nexuskittensgrab());
+})(Scratch);
diff --git a/extensions/Skyhigh173/bigint.js b/extensions/Skyhigh173/bigint.js
index 30679902ab..acea113026 100644
--- a/extensions/Skyhigh173/bigint.js
+++ b/extensions/Skyhigh173/bigint.js
@@ -1,18 +1,19 @@
// Name: BigInt
+// ID: skyhigh173BigInt
// Description: Math blocks that work on infinitely large integers (no decimals).
// By: Skyhigh173
-(function(Scratch){
- 'use strict';
+(function (Scratch) {
+ "use strict";
/**
* @param {unknown} x
* @returns {bigint}
*/
- const bi = x => {
- if (typeof x === 'string') {
+ const bi = (x) => {
+ if (typeof x === "string") {
// Try to parse things like '8n'
- if (x.charAt(x.length - 1) === 'n') {
+ if (x.charAt(x.length - 1) === "n") {
try {
return BigInt(x.slice(0, -1));
} catch (e) {
@@ -20,9 +21,10 @@
}
}
// Must remove decimal using string operations. Math.trunc will convert to float
- // which ruins the point of using bigints.
- const decimalIndex = x.indexOf('.');
- const withoutDecimal = decimalIndex === -1 ? x : x.substring(0, decimalIndex);
+ // which ruins the point of using bigints.
+ const decimalIndex = x.indexOf(".");
+ const withoutDecimal =
+ decimalIndex === -1 ? x : x.substring(0, decimalIndex);
try {
return BigInt(withoutDecimal);
} catch (e) {
@@ -39,335 +41,335 @@
};
const makeLabel = (text) => ({
- blockType: 'label',
- text: text
+ blockType: "label",
+ text: text,
});
class BigIntExtension {
getInfo() {
return {
- id: 'skyhigh173BigInt',
- name: 'BigInt',
- color1: '#59C093',
+ id: "skyhigh173BigInt",
+ name: "BigInt",
+ color1: "#59C093",
blocks: [
{
- opcode: 'from',
+ opcode: "from",
blockType: Scratch.BlockType.REPORTER,
- text: 'To BigInt [text]',
+ text: "To BigInt [text]",
arguments: {
text: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'to',
+ opcode: "to",
blockType: Scratch.BlockType.REPORTER,
- text: 'To Number [text]',
+ text: "To Number [text]",
arguments: {
text: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
- makeLabel('Arithmetic'),
+ makeLabel("Arithmetic"),
{
- opcode: 'add',
+ opcode: "add",
blockType: Scratch.BlockType.REPORTER,
- text: '[a] + [b]',
+ text: "[a] + [b]",
arguments: {
a: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
+ defaultValue: "",
},
b: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'sub',
+ opcode: "sub",
blockType: Scratch.BlockType.REPORTER,
- text: '[a] - [b]',
+ text: "[a] - [b]",
arguments: {
a: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
+ defaultValue: "",
},
b: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'mul',
+ opcode: "mul",
blockType: Scratch.BlockType.REPORTER,
- text: '[a] * [b]',
+ text: "[a] * [b]",
arguments: {
a: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
+ defaultValue: "",
},
b: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'div',
+ opcode: "div",
blockType: Scratch.BlockType.REPORTER,
- text: '[a] / [b]',
+ text: "[a] / [b]",
arguments: {
a: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
+ defaultValue: "",
},
b: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'pow',
+ opcode: "pow",
blockType: Scratch.BlockType.REPORTER,
- text: '[a] ** [b]',
+ text: "[a] ** [b]",
arguments: {
a: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
+ defaultValue: "",
},
b: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'mod',
+ opcode: "mod",
blockType: Scratch.BlockType.REPORTER,
- text: '[a] mod [b]',
+ text: "[a] mod [b]",
arguments: {
a: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
+ defaultValue: "",
},
b: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'select',
+ opcode: "select",
blockType: Scratch.BlockType.REPORTER,
- text: '[a] [sel] [b]',
+ text: "[a] [sel] [b]",
arguments: {
a: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
+ defaultValue: "",
},
b: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
+ defaultValue: "",
},
sel: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '+',
- menu: 'op'
- }
- }
+ defaultValue: "+",
+ menu: "op",
+ },
+ },
},
- makeLabel('Logic'),
+ makeLabel("Logic"),
{
- opcode: 'lt',
+ opcode: "lt",
blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] < [b]',
+ text: "[a] < [b]",
arguments: {
a: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
+ defaultValue: "",
},
b: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'le',
+ opcode: "le",
blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ≤ [b]',
+ text: "[a] ≤ [b]",
arguments: {
a: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
+ defaultValue: "",
},
b: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'eq',
+ opcode: "eq",
blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] = [b]',
+ text: "[a] = [b]",
arguments: {
a: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
+ defaultValue: "",
},
b: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'neq',
+ opcode: "neq",
blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ≠ [b]',
+ text: "[a] ≠ [b]",
arguments: {
a: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
+ defaultValue: "",
},
b: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'ge',
+ opcode: "ge",
blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] ≥ [b]',
+ text: "[a] ≥ [b]",
arguments: {
a: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
+ defaultValue: "",
},
b: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'gt',
+ opcode: "gt",
blockType: Scratch.BlockType.BOOLEAN,
- text: '[a] > [b]',
+ text: "[a] > [b]",
arguments: {
a: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
+ defaultValue: "",
},
b: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
- makeLabel('Bitwise'),
+ makeLabel("Bitwise"),
{
- opcode: 'and',
+ opcode: "and",
blockType: Scratch.BlockType.REPORTER,
- text: '[a] & [b]',
+ text: "[a] & [b]",
arguments: {
a: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
+ defaultValue: "",
},
b: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'or',
+ opcode: "or",
blockType: Scratch.BlockType.REPORTER,
- text: '[a] | [b]',
+ text: "[a] | [b]",
arguments: {
a: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
+ defaultValue: "",
},
b: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'xor',
+ opcode: "xor",
blockType: Scratch.BlockType.REPORTER,
- text: '[a] ^ [b]',
+ text: "[a] ^ [b]",
arguments: {
a: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
+ defaultValue: "",
},
b: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'ls',
+ opcode: "ls",
blockType: Scratch.BlockType.REPORTER,
- text: '[a] << [b]',
+ text: "[a] << [b]",
arguments: {
a: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
+ defaultValue: "",
},
b: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'rs',
+ opcode: "rs",
blockType: Scratch.BlockType.REPORTER,
- text: '[a] >> [b]',
+ text: "[a] >> [b]",
arguments: {
a: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
+ defaultValue: "",
},
b: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'not',
+ opcode: "not",
blockType: Scratch.BlockType.REPORTER,
- text: '~ [a]',
+ text: "~ [a]",
arguments: {
a: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ''
+ defaultValue: "",
},
- }
+ },
},
],
menus: {
op: {
- items: ['+', '-', '*', '/', '%', '^'],
- acceptReporters: true
- }
- }
+ items: ["+", "-", "*", "/", "%", "^"],
+ acceptReporters: true,
+ },
+ },
};
}
from({ text }) {
@@ -386,14 +388,14 @@
return (bi(a) * bi(b)).toString();
}
div({ a, b }) {
- if (Number(b) == 0) return 'NaN';
+ if (Number(b) == 0) return "NaN";
return (bi(a) / bi(b)).toString();
}
pow({ a, b }) {
return (bi(a) ** bi(b)).toString();
}
mod({ a, b }) {
- if (Number(b) == 0) return 'NaN';
+ if (Number(b) == 0) return "NaN";
return (bi(a) % bi(b)).toString();
}
@@ -418,19 +420,25 @@
select({ a, sel, b }) {
switch (sel) {
- case '+': return (bi(a) + bi(b)).toString();
- case '-': return (bi(a) - bi(b)).toString();
- case '*': return (bi(a) * bi(b)).toString();
- case '/': {
- if (Number(b) == 0) return 'NaN';
- return (bi(a) / bi(b)).toString();
- }
- case '%': {
- if (Number(b) == 0) return 'NaN';
- return (bi(a) % bi(b)).toString();
- }
- case '^': case '**': return (bi(a) ** bi(b)).toString();
- default: return '0';
+ case "+":
+ return (bi(a) + bi(b)).toString();
+ case "-":
+ return (bi(a) - bi(b)).toString();
+ case "*":
+ return (bi(a) * bi(b)).toString();
+ case "/": {
+ if (Number(b) == 0) return "NaN";
+ return (bi(a) / bi(b)).toString();
+ }
+ case "%": {
+ if (Number(b) == 0) return "NaN";
+ return (bi(a) % bi(b)).toString();
+ }
+ case "^":
+ case "**":
+ return (bi(a) ** bi(b)).toString();
+ default:
+ return "0";
}
}
diff --git a/extensions/Skyhigh173/json.js b/extensions/Skyhigh173/json.js
index 9ea47b5cd0..8fe25e8f4c 100644
--- a/extensions/Skyhigh173/json.js
+++ b/extensions/Skyhigh173/json.js
@@ -1,529 +1,557 @@
// Name: JSON
+// ID: skyhigh173JSON
// Description: Handle JSON strings and arrays.
// By: Skyhigh173
-(function(Scratch) {
- 'use strict';
+(function (Scratch) {
+ "use strict";
/*
- * JSON extension v2.5 by skyhigh173 (English Version)
- * Do not remove this comment
- */
+ * JSON extension v2.5 by skyhigh173 (English Version)
+ * Do not remove this comment
+ */
const vm = Scratch.vm;
- const hasOwn = (obj, property) => Object.prototype.hasOwnProperty.call(obj, property);
+ const hasOwn = (obj, property) =>
+ Object.prototype.hasOwnProperty.call(obj, property);
const makeLabel = (text) => ({
- blockType: 'label',
- text: text
+ blockType: "label",
+ text: text,
});
class JSONS {
getInfo() {
return {
- id: 'skyhigh173JSON',
- name: 'JSON',
- color1: '#3271D0',
+ id: "skyhigh173JSON",
+ name: "JSON",
+ color1: "#3271D0",
blocks: [
- makeLabel('General Utils'),
+ makeLabel("General Utils"),
{
- opcode: 'json_is_valid',
+ opcode: "json_is_valid",
blockType: Scratch.BlockType.BOOLEAN,
- text: 'is JSON [json] valid',
+ text: "is JSON [json] valid",
arguments: {
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '{"key":"value"}'
- }
- }
+ defaultValue: '{"key":"value"}',
+ },
+ },
},
{
- opcode: 'json_is',
+ opcode: "json_is",
blockType: Scratch.BlockType.BOOLEAN,
- text: 'is [json] [types]',
+ text: "is [json] [types]",
arguments: {
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '{"key":"value"}'
+ defaultValue: '{"key":"value"}',
},
types: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'Object',
- menu: 'types'
+ defaultValue: "Object",
+ menu: "types",
},
- }
+ },
},
- '---',
+ "---",
{
- opcode: 'json_get_all',
+ opcode: "json_get_all",
blockType: Scratch.BlockType.REPORTER,
- text: 'get all [Stype] of [json]',
+ text: "get all [Stype] of [json]",
arguments: {
Stype: {
type: Scratch.ArgumentType.STRING,
- menu: 'get_all'
+ menu: "get_all",
},
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '{"key":"value","key2":"value2"}'
- }
- }
+ defaultValue: '{"key":"value","key2":"value2"}',
+ },
+ },
},
{
- opcode: 'json_new',
+ opcode: "json_new",
blockType: Scratch.BlockType.REPORTER,
- text: 'new [json]',
+ text: "new [json]",
arguments: {
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'Object',
- menu: 'types'
- }
- }
+ defaultValue: "Object",
+ menu: "types",
+ },
+ },
},
- '---',
+ "---",
{
- opcode: 'json_has_key',
+ opcode: "json_has_key",
blockType: Scratch.BlockType.BOOLEAN,
- text: '[json] contains key [key]?',
+ text: "[json] contains key [key]?",
arguments: {
key: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'key2'
+ defaultValue: "key2",
},
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '{"key":"value"}'
- }
- }
+ defaultValue: '{"key":"value"}',
+ },
+ },
},
{
- opcode: 'json_has_value',
+ opcode: "json_has_value",
blockType: Scratch.BlockType.BOOLEAN,
- text: '[json] contains value [value]?',
+ text: "[json] contains value [value]?",
arguments: {
value: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'scratch'
+ defaultValue: "scratch",
},
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '["TurboWarp","scratch"]'
- }
- }
+ defaultValue: '["TurboWarp","scratch"]',
+ },
+ },
},
{
- opcode: 'json_equal',
+ opcode: "json_equal",
blockType: Scratch.BlockType.BOOLEAN,
- text: '[json1] [equal] [json2]',
+ text: "[json1] [equal] [json2]",
arguments: {
json1: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '{"a":0,"b":1}'
+ defaultValue: '{"a":0,"b":1}',
},
json2: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '{"b":1,"a":0}'
+ defaultValue: '{"b":1,"a":0}',
},
equal: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '=',
- menu: 'equal'
- }
- }
+ defaultValue: "=",
+ menu: "equal",
+ },
+ },
},
- makeLabel('JSON Strings'),
+ makeLabel("JSON Strings"),
{
- opcode: 'json_jlength',
+ opcode: "json_jlength",
blockType: Scratch.BlockType.REPORTER,
- text: 'length of json [json]',
+ text: "length of json [json]",
arguments: {
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '{"key":"value","key2":"value2"}'
- }
- }
+ defaultValue: '{"key":"value","key2":"value2"}',
+ },
+ },
},
{
- opcode: 'json_get',
+ opcode: "json_get",
blockType: Scratch.BlockType.REPORTER,
- text: 'get [item] in [json]',
+ text: "get [item] in [json]",
arguments: {
item: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'key'
+ defaultValue: "key",
},
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '{"key":"value"}'
- }
- }
+ defaultValue: '{"key":"value"}',
+ },
+ },
},
{
- opcode: 'json_set',
+ opcode: "json_set",
blockType: Scratch.BlockType.REPORTER,
- text: 'set [item] to [value] in [json]',
+ text: "set [item] to [value] in [json]",
arguments: {
item: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'key'
+ defaultValue: "key",
},
value: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'new value'
+ defaultValue: "new value",
},
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '{"key":"value"}'
- }
- }
+ defaultValue: '{"key":"value"}',
+ },
+ },
},
{
- opcode: 'json_delete',
+ opcode: "json_delete",
blockType: Scratch.BlockType.REPORTER,
- text: 'delete [item] in [json]',
+ text: "delete [item] in [json]",
arguments: {
item: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'key2'
+ defaultValue: "key2",
},
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '{"key":"value","key2":"value2"}'
- }
- }
+ defaultValue: '{"key":"value","key2":"value2"}',
+ },
+ },
},
- makeLabel('Array'),
+ makeLabel("Array"),
{
- opcode: 'json_length',
+ opcode: "json_length",
blockType: Scratch.BlockType.REPORTER,
- text: 'length of array [json]',
+ text: "length of array [json]",
arguments: {
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '[1,2,3]'
- }
- }
+ defaultValue: "[1,2,3]",
+ },
+ },
},
{
- opcode: 'json_array_get',
+ opcode: "json_array_get",
blockType: Scratch.BlockType.REPORTER,
- text: 'item [item] of array [json]',
+ text: "item [item] of array [json]",
arguments: {
item: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: 1
+ defaultValue: 1,
},
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '["scratch","TurboWarp"]'
- }
- }
+ defaultValue: '["scratch","TurboWarp"]',
+ },
+ },
},
{
- opcode: 'json_array_push',
+ opcode: "json_array_push",
blockType: Scratch.BlockType.REPORTER,
- text: 'add [item] to array [json]',
+ text: "add [item] to array [json]",
arguments: {
item: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'TurboWarp'
+ defaultValue: "TurboWarp",
},
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '["scratch"]'
- }
- }
+ defaultValue: '["scratch"]',
+ },
+ },
},
{
- opcode: 'json_array_set',
+ opcode: "json_array_set",
blockType: Scratch.BlockType.REPORTER,
- text: 'replace item [pos] of [json] to [item]',
+ text: "replace item [pos] of [json] to [item]",
arguments: {
item: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'fav'
+ defaultValue: "fav",
},
pos: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: 2
+ defaultValue: 2,
},
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '["love","heart","follow"]'
- }
- }
+ defaultValue: '["love","heart","follow"]',
+ },
+ },
},
{
- opcode: 'json_array_insert',
+ opcode: "json_array_insert",
blockType: Scratch.BlockType.REPORTER,
- text: 'insert [item] at [pos] of array [json]',
+ text: "insert [item] at [pos] of array [json]",
arguments: {
item: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'fav'
+ defaultValue: "fav",
},
pos: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: 2
+ defaultValue: 2,
},
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '["love","follow"]'
- }
- }
+ defaultValue: '["love","follow"]',
+ },
+ },
},
- '---',
+ "---",
{
- opcode: 'json_array_delete',
+ opcode: "json_array_delete",
blockType: Scratch.BlockType.REPORTER,
- text: 'delete item [item] of array [json]',
+ text: "delete item [item] of array [json]",
arguments: {
item: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: 2
+ defaultValue: 2,
},
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '["scratch","a","TurboWarp"]'
- }
- }
+ defaultValue: '["scratch","a","TurboWarp"]',
+ },
+ },
},
{
- opcode: 'json_array_remove_all',
+ opcode: "json_array_remove_all",
blockType: Scratch.BlockType.REPORTER,
- text: 'delete all [item] in array [json]',
+ text: "delete all [item] in array [json]",
arguments: {
item: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'a'
+ defaultValue: "a",
},
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '["scratch","a","TurboWarp","a","a"]'
- }
- }
+ defaultValue: '["scratch","a","TurboWarp","a","a"]',
+ },
+ },
},
- '---',
+ "---",
{
- opcode: 'json_array_itemH',
+ opcode: "json_array_itemH",
blockType: Scratch.BlockType.REPORTER,
- text: 'item # of [item] in array [json]',
+ text: "item # of [item] in array [json]",
arguments: {
item: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'scratch'
+ defaultValue: "scratch",
},
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '["scratch","TurboWarp"]'
- }
- }
+ defaultValue: '["scratch","TurboWarp"]',
+ },
+ },
},
- makeLabel('Advanced'),
+ makeLabel("Advanced"),
{
- opcode: 'json_array_from',
+ opcode: "json_array_from",
blockType: Scratch.BlockType.REPORTER,
- text: 'array from text [json]',
+ text: "array from text [json]",
arguments: {
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'abcd'
- }
- }
+ defaultValue: "abcd",
+ },
+ },
},
{
- opcode: 'json_array_fromto',
+ opcode: "json_array_fromto",
blockType: Scratch.BlockType.REPORTER,
- text: 'array [json] from item [item] to [item2]',
+ text: "array [json] from item [item] to [item2]",
arguments: {
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '[1,2,3,4]'
+ defaultValue: "[1,2,3,4]",
},
item: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: 2
+ defaultValue: 2,
},
item2: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: 3
- }
- }
+ defaultValue: 3,
+ },
+ },
},
{
- opcode: 'json_array_reverse',
+ opcode: "json_array_reverse",
blockType: Scratch.BlockType.REPORTER,
- text: 'reverse array [json]',
+ text: "reverse array [json]",
arguments: {
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '["a","b","c","d","e","f"]'
- }
- }
+ defaultValue: '["a","b","c","d","e","f"]',
+ },
+ },
},
{
- opcode: 'json_array_flat',
+ opcode: "json_array_flat",
blockType: Scratch.BlockType.REPORTER,
- text: 'flat array [json] by depth [depth]',
+ text: "flat array [json] by depth [depth]",
arguments: {
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '[[1],2,[3,4],[5,[6]]]'
+ defaultValue: "[[1],2,[3,4],[5,[6]]]",
},
depth: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: 2
- }
- }
+ defaultValue: 2,
+ },
+ },
},
{
- opcode: 'json_array_concat',
+ opcode: "json_array_concat",
blockType: Scratch.BlockType.REPORTER,
- text: 'array concat [json] [json2]',
+ text: "array concat [json] [json2]",
arguments: {
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '["a","b"]'
+ defaultValue: '["a","b"]',
},
json2: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '["c","d"]'
- }
- }
+ defaultValue: '["c","d"]',
+ },
+ },
},
{
- opcode: 'json_array_filter',
+ opcode: "json_array_filter",
blockType: Scratch.BlockType.REPORTER,
- text: 'get all value with key [key] in array [json]',
+ text: "get all value with key [key] in array [json]",
arguments: {
key: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'id'
+ defaultValue: "id",
},
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '[{"id":12},{"id":24}]'
- }
- }
+ defaultValue: '[{"id":12},{"id":24}]',
+ },
+ },
},
{
- opcode: 'json_array_setlen',
+ opcode: "json_array_setlen",
blockType: Scratch.BlockType.REPORTER,
- text: 'set length of array [json] to [len]',
+ text: "set length of array [json] to [len]",
arguments: {
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '["a","b","c"]'
+ defaultValue: '["a","b","c"]',
},
len: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: 2
- }
- }
+ defaultValue: 2,
+ },
+ },
},
- '---',
+ "---",
{
- opcode: 'json_array_create',
+ opcode: "json_array_create",
blockType: Scratch.BlockType.REPORTER,
- text: 'create array by [text] with delimiter [d]',
+ text: "create array by [text] with delimiter [d]",
arguments: {
text: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'a,b,c'
+ defaultValue: "a,b,c",
},
d: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ','
- }
- }
+ defaultValue: ",",
+ },
+ },
},
{
- opcode: 'json_array_join',
+ opcode: "json_array_join",
blockType: Scratch.BlockType.REPORTER,
- text: 'join string by array [json] with delimiter [d]',
+ text: "join string by array [json] with delimiter [d]",
arguments: {
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '["a","b","c"]'
+ defaultValue: '["a","b","c"]',
},
d: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ','
- }
- }
+ defaultValue: ",",
+ },
+ },
},
- makeLabel('Lists'),
+ "---",
{
- opcode: 'json_vm_getlist',
+ opcode: "json_array_sort",
blockType: Scratch.BlockType.REPORTER,
- text: 'get list [list] as array',
+ text: "sort array [list] in [order] order",
+ disableMonitor: true,
arguments: {
list: {
type: Scratch.ArgumentType.STRING,
- menu: 'get_list'
+ defaultValue:
+ "[5.23, 214, 522, 61, 5.24, 62.2, 1, 51212, 0, 0]",
},
- }
+ order: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "sort_order",
+ },
+ },
},
+ makeLabel("Lists"),
{
- opcode: 'json_vm_setlist',
+ opcode: "json_vm_getlist",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "get list [list] as array",
+ arguments: {
+ list: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "get_list",
+ },
+ },
+ },
+ {
+ opcode: "json_vm_setlist",
blockType: Scratch.BlockType.COMMAND,
- text: 'set list [list] to content [json]',
+ text: "set list [list] to content [json]",
arguments: {
list: {
type: Scratch.ArgumentType.STRING,
- menu: 'get_list'
+ menu: "get_list",
},
json: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '["apple","banana"]'
+ defaultValue: '["apple","banana"]',
},
- }
+ },
},
],
menus: {
get_all: {
- items: ['keys','values','datas']
+ items: ["keys", "values", "datas"],
},
get_list: {
acceptReporters: true,
- items: 'getLists'
+ items: "getLists",
},
types: {
acceptReporters: true,
- items: ['Object', 'Array']
+ items: ["Object", "Array"],
},
equal: {
acceptReporters: true,
- items: ['=','≠']
- }
- }
+ items: ["=", "≠"],
+ },
+ sort_order: {
+ items: ["ascending", "descending"],
+ acceptReporters: true,
+ },
+ },
};
}
- getLists () {
- const globalLists = Object.values(vm.runtime.getTargetForStage().variables).filter(x => x.type == 'list');
- const localLists = Object.values(vm.editingTarget.variables).filter(x => x.type == 'list');
+ getLists() {
+ const globalLists = Object.values(
+ vm.runtime.getTargetForStage().variables
+ ).filter((x) => x.type == "list");
+ const localLists = Object.values(vm.editingTarget.variables).filter(
+ (x) => x.type == "list"
+ );
const uniqueLists = [...new Set([...globalLists, ...localLists])];
if (uniqueLists.length === 0) {
return [
{
- text: 'select a list',
- value: 'select a list'
- }
+ text: "select a list",
+ value: "select a list",
+ },
];
}
- return uniqueLists.map(i => ({
+ return uniqueLists.map((i) => ({
text: i.name,
- value: i.id
+ value: i.id,
}));
}
lookupList(list, util) {
const byId = util.target.lookupVariableById(list);
- if (byId && byId.type === 'list') {
+ if (byId && byId.type === "list") {
return byId;
}
- const byName = util.target.lookupVariableByNameAndType(list, 'list');
+ const byName = util.target.lookupVariableByNameAndType(list, "list");
if (byName) {
return byName;
}
@@ -531,9 +559,12 @@
}
json_is_valid({ json }) {
- if (typeof json != 'string') {
+ if (typeof json != "string") {
return false;
- } else if ((json.slice(0,1) != '[' || json.slice(-1) != ']') && (json.slice(0,1) != '{' || json.slice(-1) != '}')) {
+ } else if (
+ (json.slice(0, 1) != "[" || json.slice(-1) != "]") &&
+ (json.slice(0, 1) != "{" || json.slice(-1) != "}")
+ ) {
return false;
} else {
try {
@@ -546,10 +577,13 @@
}
// return object if its json else string
- json_valid_return(json){
- if (typeof json != 'string') {
+ json_valid_return(json) {
+ if (typeof json != "string") {
return json;
- } else if ((json.slice(0,1) != '[' || json.slice(-1) != ']') && (json.slice(0,1) != '{' || json.slice(-1) != '}')) {
+ } else if (
+ (json.slice(0, 1) != "[" || json.slice(-1) != "]") &&
+ (json.slice(0, 1) != "{" || json.slice(-1) != "}")
+ ) {
return json;
} else {
try {
@@ -561,13 +595,16 @@
}
json_is({ json, types }) {
- if (!this.json_is_valid({json: json})) return false;
+ if (!this.json_is_valid({ json: json })) return false;
try {
json = JSON.parse(json);
switch (types) {
- case 'Object': return !Array.isArray(json);
- case 'Array': return Array.isArray(json);
- default: return false;
+ case "Object":
+ return !Array.isArray(json);
+ case "Array":
+ return Array.isArray(json);
+ default:
+ return false;
}
} catch {
return false;
@@ -579,21 +616,27 @@
json = JSON.parse(json);
return Object.keys(json).length;
} catch {
- return ' ';
+ return " ";
}
}
json_new({ json }) {
switch (json) {
- case 'Object': return '{}';
- case 'Array': return '[]';
- default: return '';
+ case "Object":
+ return "{}";
+ case "Array":
+ return "[]";
+ default:
+ return "";
}
}
json_has_key({ json, key }) {
try {
- return this._fixInvalidJSONValues(this.json_valid_return(key)) in JSON.parse(json);
+ return (
+ this._fixInvalidJSONValues(this.json_valid_return(key)) in
+ JSON.parse(json)
+ );
} catch {
return false;
}
@@ -616,30 +659,34 @@
const keys1 = Object.keys(json1);
const keys2 = Object.keys(json2);
- const result = keys1.length === keys2.length && Object.keys(json1).every(key=>json1[key] === json2[key]);
- if (equal === '=') return result;
- if (equal === '≠') return !result;
+ const result =
+ keys1.length === keys2.length &&
+ Object.keys(json1).every((key) => json1[key] === json2[key]);
+ if (equal === "=") return result;
+ if (equal === "≠") return !result;
} catch {
// ignore
}
return false;
}
-
- json_get_all({ Stype,json }) {
+ json_get_all({ Stype, json }) {
try {
json = JSON.parse(json);
switch (Stype) {
- case 'keys':
- return JSON.stringify(Object.keys(json).map(key => key));
- case 'values':
- return JSON.stringify(Object.keys(json).map(key => json[key]));
- case 'datas':
- return JSON.stringify(Object.keys(json).map(key => [key, json[key]]));
- default: return '';
+ case "keys":
+ return JSON.stringify(Object.keys(json).map((key) => key));
+ case "values":
+ return JSON.stringify(Object.keys(json).map((key) => json[key]));
+ case "datas":
+ return JSON.stringify(
+ Object.keys(json).map((key) => [key, json[key]])
+ );
+ default:
+ return "";
}
} catch {
- return '';
+ return "";
}
}
@@ -648,7 +695,7 @@
json = JSON.parse(json);
if (hasOwn(json, item)) {
const result = json[item];
- if (typeof result === 'object') {
+ if (typeof result === "object") {
return JSON.stringify(result);
} else {
return result;
@@ -657,14 +704,14 @@
} catch {
// ignore
}
- return '';
+ return "";
}
_fixInvalidJSONValues(value) {
// JSON does not support these values, so convert to string.
- if (Number.isNaN(value)) return 'NaN';
- if (value === Infinity) return 'Infinity';
- if (value === -Infinity) return '-Infinity';
+ if (Number.isNaN(value)) return "NaN";
+ if (value === Infinity) return "Infinity";
+ if (value === -Infinity) return "-Infinity";
return value;
}
@@ -676,7 +723,7 @@
json[item] = value;
return JSON.stringify(json);
} catch {
- return '';
+ return "";
}
}
@@ -686,7 +733,7 @@
delete json[item];
return JSON.stringify(json);
} catch {
- return '';
+ return "";
}
}
@@ -699,7 +746,7 @@
// 1...length : array content, -1...-length : reverse array content, 0 : ERROR
try {
item = Scratch.Cast.toNumber(item);
- if (item == 0) return '';
+ if (item == 0) return "";
if (item > 0) {
item--;
}
@@ -710,13 +757,13 @@
} else {
result = json[json.length + item];
}
- if (typeof result == 'object') {
+ if (typeof result == "object") {
return JSON.stringify(result);
} else {
return result;
}
} catch {
- return '';
+ return "";
}
}
@@ -727,7 +774,7 @@
let result = JSON.stringify(json.indexOf(item) + 1);
return result;
} catch {
- return '';
+ return "";
}
}
@@ -735,7 +782,7 @@
try {
return JSON.stringify(Array.from(String(json)));
} catch {
- return '';
+ return "";
}
}
@@ -745,7 +792,7 @@
json2 = JSON.parse(json2);
return JSON.stringify(json.concat(json2));
} catch {
- return '';
+ return "";
}
}
@@ -756,7 +803,7 @@
json.push(item);
return JSON.stringify(json);
} catch {
- return '';
+ return "";
}
}
@@ -767,17 +814,19 @@
json.splice(pos - 1, 0, item);
return JSON.stringify(json);
} catch {
- return '';
+ return "";
}
}
json_array_set({ item, pos, json }) {
try {
json = JSON.parse(json);
- json[pos - 1] = this._fixInvalidJSONValues(this.json_valid_return(item));
+ json[pos - 1] = this._fixInvalidJSONValues(
+ this.json_valid_return(item)
+ );
return JSON.stringify(json);
} catch {
- return '';
+ return "";
}
}
@@ -787,7 +836,7 @@
json.splice(item - 1, 1);
return JSON.stringify(json);
} catch {
- return '';
+ return "";
}
}
@@ -805,15 +854,15 @@
}
return JSON.stringify(json);
} catch {
- return '';
+ return "";
}
}
json_array_fromto({ json, item, item2 }) {
try {
- return JSON.stringify(JSON.parse(json).slice(item - 1,item2));
+ return JSON.stringify(JSON.parse(json).slice(item - 1, item2));
} catch {
- return '';
+ return "";
}
}
@@ -821,7 +870,7 @@
try {
return JSON.stringify(JSON.parse(json).reverse());
} catch {
- return '';
+ return "";
}
}
@@ -829,7 +878,7 @@
try {
return JSON.stringify(JSON.parse(json).flat(depth));
} catch {
- return '';
+ return "";
}
}
@@ -841,21 +890,23 @@
try {
return JSON.parse(json).join(d);
} catch {
- return '';
+ return "";
}
}
json_array_filter({ key, json }) {
try {
json = JSON.parse(json);
- return JSON.stringify(json.map(x => {
- if (hasOwn(x, key)) {
- return x[key];
- }
- return null;
- }));
+ return JSON.stringify(
+ json.map((x) => {
+ if (hasOwn(x, key)) {
+ return x[key];
+ }
+ return null;
+ })
+ );
} catch (e) {
- return '';
+ return "";
}
}
@@ -865,7 +916,7 @@
json.length = len;
return JSON.stringify(json);
} catch {
- return '';
+ return "";
}
}
@@ -878,7 +929,7 @@
} catch (e) {
// ignore
}
- return '';
+ return "";
}
json_vm_setlist({ list, json }, util) {
try {
@@ -886,8 +937,8 @@
if (listVariable) {
const array = JSON.parse(json);
if (Array.isArray(array)) {
- const safeArray = array.map(i => {
- if (typeof i === 'object') return JSON.stringify(i);
+ const safeArray = array.map((i) => {
+ if (typeof i === "object") return JSON.stringify(i);
return i;
});
listVariable.value = safeArray;
@@ -896,7 +947,22 @@
} catch (e) {
// ignore
}
- return '';
+ return "";
+ }
+
+ json_array_sort(args) {
+ let list;
+ try {
+ list = JSON.parse(args.list);
+ } catch {
+ return "";
+ }
+ if (!Array.isArray(list)) {
+ return "";
+ }
+ list.sort(Scratch.Cast.compare);
+ if (args.order === "descending") list.reverse();
+ return JSON.stringify(list);
}
}
Scratch.extensions.register(new JSONS());
diff --git a/extensions/TheShovel/CanvasEffects.js b/extensions/TheShovel/CanvasEffects.js
index 572483c3e5..e14c905bf5 100644
--- a/extensions/TheShovel/CanvasEffects.js
+++ b/extensions/TheShovel/CanvasEffects.js
@@ -1,235 +1,275 @@
// Name: Canvas Effects
+// ID: theshovelcanvaseffects
// Description: Apply visual effects to the entire stage.
// By: TheShovel
-(function(Scratch) {
- 'use strict';
- if (!Scratch.extensions.unsandboxed) {
- throw new Error('This extension must run unsandboxed');
- }
+(function (Scratch) {
+ "use strict";
+ if (!Scratch.extensions.unsandboxed) {
+ throw new Error("This extension must run unsandboxed");
+ }
- const canvas = Scratch.renderer.canvas;
+ const canvas = Scratch.renderer.canvas;
- const updateStyle = () => {
- // Gotta keep the translation to % because of the stage size, window size and so on
- const transform = `rotate(${rotation}deg) scale(${scale}%) skew(${skewX}deg, ${skewY}deg) translate(${offsetX}%, ${0 - offsetY}%)`;
- if (canvas.style.transform !== transform) {
- canvas.style.transform = transform;
- }
- const filter = `blur(${blur}px) contrast(${contrast / 100}) saturate(${saturation}%) hue-rotate(${color}deg) brightness(${brightness}%) invert(${invert}%) sepia(${sepia}%) opacity(${100 - transparency}%)`;
- if (canvas.style.filter !== filter) {
- canvas.style.filter = filter;
- }
- const cssBorderRadius = borderRadius === 0 ? '' : `${borderRadius}%`;
- if (canvas.style.borderRadius !== cssBorderRadius) {
- canvas.style.borderRadius = cssBorderRadius;
- }
- const imageRendering = resizeMode === 'pixelated' ? 'pixelated' : '';
- if (canvas.style.imageRendering !== imageRendering) {
- canvas.style.imageRendering = imageRendering;
- }
- };
- // scratch-gui may reset canvas styles when resizing the window or going in/out of fullscreen
- new MutationObserver(updateStyle).observe(canvas, {
- attributeFilter: ['style'],
- attributes: true
- });
+ const updateStyle = () => {
+ // Gotta keep the translation to % because of the stage size, window size and so on
+ const transform = `rotate(${rotation}deg) scale(${scale}%) skew(${skewX}deg, ${skewY}deg) translate(${offsetX}%, ${
+ 0 - offsetY
+ }%)`;
+ if (canvas.style.transform !== transform) {
+ canvas.style.transform = transform;
+ }
+ const filter = `blur(${blur}px) contrast(${
+ contrast / 100
+ }) saturate(${saturation}%) hue-rotate(${color}deg) brightness(${brightness}%) invert(${invert}%) sepia(${sepia}%) opacity(${
+ 100 - transparency
+ }%)`;
+ if (canvas.style.filter !== filter) {
+ canvas.style.filter = filter;
+ }
+ const cssBorderRadius = borderRadius === 0 ? "" : `${borderRadius}%`;
+ if (canvas.style.borderRadius !== cssBorderRadius) {
+ canvas.style.borderRadius = cssBorderRadius;
+ }
+ const imageRendering = resizeMode === "pixelated" ? "pixelated" : "";
+ if (canvas.style.imageRendering !== imageRendering) {
+ canvas.style.imageRendering = imageRendering;
+ }
+ };
+ // scratch-gui may reset canvas styles when resizing the window or going in/out of fullscreen
+ new MutationObserver(updateStyle).observe(canvas, {
+ attributeFilter: ["style"],
+ attributes: true,
+ });
- let borderRadius = 0;
- let rotation = 0;
- let offsetY = 0;
- let offsetX = 0;
- let skewY = 0;
- let skewX = 0;
- let scale = 100;
- // Thanks SharkPool for telling me about these
- let transparency = 0;
- let sepia = 0;
- let blur = 0;
- let contrast = 100;
- let saturation = 100;
- let color = 0;
- let brightness = 100;
- let invert = 0;
- let resizeMode = 'default';
+ let borderRadius = 0;
+ let rotation = 0;
+ let offsetY = 0;
+ let offsetX = 0;
+ let skewY = 0;
+ let skewX = 0;
+ let scale = 100;
+ // Thanks SharkPool for telling me about these
+ let transparency = 0;
+ let sepia = 0;
+ let blur = 0;
+ let contrast = 100;
+ let saturation = 100;
+ let color = 0;
+ let brightness = 100;
+ let invert = 0;
+ let resizeMode = "default";
- class CanvasEffects {
- getInfo() {
- return {
- id: 'theshovelcanvaseffects',
- name: 'Canvas Effects',
- blocks: [
- {
- opcode: 'seteffect',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set canvas [EFFECT] to [NUMBER]',
- arguments: {
- EFFECT: {
- type: Scratch.ArgumentType.STRING,
- menu: 'EFFECTMENU'
- },
- NUMBER: {
- type: Scratch.ArgumentType.NUMBER
- }
- },
- },
- {
- opcode: 'geteffect',
- blockType: Scratch.BlockType.REPORTER,
- text: 'get canvas [EFFECT]',
- arguments: {
- EFFECT: {
- type: Scratch.ArgumentType.STRING,
- menu: 'EFFECTGETMENU'
- }
- }
- },
- {
- opcode: 'cleareffects',
- blockType: Scratch.BlockType.COMMAND,
- text: 'clear canvas effects'
- },
- {
- opcode: 'renderscale',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set canvas render size to width:[X] height:[Y]',
- arguments: {
- X: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: 100
- },
- Y: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: 100
- }
- }
- },
- {
- opcode: 'setrendermode',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set canvas resize rendering mode [EFFECT]',
- arguments: {
- EFFECT: {
- type: Scratch.ArgumentType.STRING,
- menu: 'RENDERMODE'
- }
- },
- },
- ],
- menus: {
- EFFECTMENU: {
- acceptReporters: true,
- items: ['blur', 'contrast', 'saturation', 'color shift', 'brightness', 'invert', 'sepia', 'transparency', 'scale', 'skew X', 'skew Y', 'offset X', 'offset Y', 'rotation', 'border radius']
- },
- RENDERMODE: {
- acceptReporters: true,
- items: ['pixelated', 'default']
- },
- EFFECTGETMENU: {
- acceptReporters: true,
- // this contains 'resize rendering mode', EFFECTMENU does not
- items: ['blur', 'contrast', 'saturation', 'color shift', 'brightness', 'invert', 'resize rendering mode', 'sepia', 'transparency', 'scale', 'skew X', 'skew Y', 'offset X', 'offset Y', 'rotation', 'border radius']
- }
- }
- };
- }
- geteffect({EFFECT}) {
- if (EFFECT === 'blur') {
- return blur;
- } else if (EFFECT === 'contrast') {
- return contrast;
- } else if (EFFECT === 'saturation') {
- return saturation;
- } else if (EFFECT === 'color shift') {
- return color;
- } else if (EFFECT === 'brightness') {
- return brightness;
- } else if (EFFECT === 'invert') {
- return invert;
- } else if (EFFECT === 'resize rendering mode') {
- return resizeMode;
- } else if (EFFECT === 'sepia') {
- return sepia;
- } else if (EFFECT === 'transparency') {
- return transparency;
- } else if (EFFECT === 'scale') {
- return scale;
- } else if (EFFECT === 'skew X') {
- return skewX;
- } else if (EFFECT === 'skew Y') {
- return skewY;
- } else if (EFFECT === 'offset X') {
- return offsetX;
- } else if (EFFECT === 'offset Y') {
- return offsetY;
- } else if (EFFECT === 'rotation') {
- return rotation;
- } else if (EFFECT === 'border radius') {
- return borderRadius;
- }
- return '';
- }
- seteffect({EFFECT, NUMBER}) {
- NUMBER = Scratch.Cast.toNumber(NUMBER);
- if (EFFECT === 'blur') {
- blur = NUMBER;
- } else if (EFFECT === 'contrast') {
- contrast = NUMBER;
- } else if (EFFECT === 'saturation') {
- saturation = NUMBER;
- } else if (EFFECT === 'color shift') {
- color = NUMBER;
- } else if (EFFECT === 'brightness') {
- brightness = NUMBER;
- } else if (EFFECT === 'invert') {
- invert = NUMBER;
- } else if (EFFECT === 'sepia') {
- sepia = NUMBER;
- } else if (EFFECT === 'transparency') {
- transparency = NUMBER;
- } else if (EFFECT === 'scale') {
- scale = NUMBER;
- } else if (EFFECT === 'skew X') {
- skewX = NUMBER;
- } else if (EFFECT === 'skew Y') {
- skewY = NUMBER;
- } else if (EFFECT === 'offset X') {
- offsetX = NUMBER;
- } else if (EFFECT === 'offset Y') {
- offsetY = NUMBER;
- } else if (EFFECT === 'rotation') {
- rotation = NUMBER;
- } else if (EFFECT === 'border radius') {
- borderRadius = NUMBER;
- }
- updateStyle();
- }
- cleareffects() {
- borderRadius = 0;
- rotation = 0;
- offsetY = 0;
- offsetX = 0;
- skewY = 0;
- skewX = 0;
- scale = 100;
- transparency = 0;
- sepia = 0;
- blur = 0;
- contrast = 100;
- saturation = 100;
- color = 0;
- brightness = 100;
- invert = 0;
- resizeMode = 'default';
- updateStyle();
- }
- setrendermode({EFFECT}) {
- resizeMode = EFFECT;
- updateStyle();
- }
- renderscale({X, Y}) {
- Scratch.vm.renderer.resize(X, Y);
- }
+ class CanvasEffects {
+ getInfo() {
+ return {
+ id: "theshovelcanvaseffects",
+ name: "Canvas Effects",
+ blocks: [
+ {
+ opcode: "seteffect",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set canvas [EFFECT] to [NUMBER]",
+ arguments: {
+ EFFECT: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "EFFECTMENU",
+ },
+ NUMBER: {
+ type: Scratch.ArgumentType.NUMBER,
+ },
+ },
+ },
+ {
+ opcode: "geteffect",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "get canvas [EFFECT]",
+ arguments: {
+ EFFECT: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "EFFECTGETMENU",
+ },
+ },
+ },
+ {
+ opcode: "cleareffects",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "clear canvas effects",
+ },
+ {
+ opcode: "renderscale",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set canvas render size to width:[X] height:[Y]",
+ arguments: {
+ X: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: 100,
+ },
+ Y: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: 100,
+ },
+ },
+ },
+ {
+ opcode: "setrendermode",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set canvas resize rendering mode [EFFECT]",
+ arguments: {
+ EFFECT: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "RENDERMODE",
+ },
+ },
+ },
+ ],
+ menus: {
+ EFFECTMENU: {
+ acceptReporters: true,
+ items: [
+ "blur",
+ "contrast",
+ "saturation",
+ "color shift",
+ "brightness",
+ "invert",
+ "sepia",
+ "transparency",
+ "scale",
+ "skew X",
+ "skew Y",
+ "offset X",
+ "offset Y",
+ "rotation",
+ "border radius",
+ ],
+ },
+ RENDERMODE: {
+ acceptReporters: true,
+ items: ["pixelated", "default"],
+ },
+ EFFECTGETMENU: {
+ acceptReporters: true,
+ // this contains 'resize rendering mode', EFFECTMENU does not
+ items: [
+ "blur",
+ "contrast",
+ "saturation",
+ "color shift",
+ "brightness",
+ "invert",
+ "resize rendering mode",
+ "sepia",
+ "transparency",
+ "scale",
+ "skew X",
+ "skew Y",
+ "offset X",
+ "offset Y",
+ "rotation",
+ "border radius",
+ ],
+ },
+ },
+ };
+ }
+ geteffect({ EFFECT }) {
+ if (EFFECT === "blur") {
+ return blur;
+ } else if (EFFECT === "contrast") {
+ return contrast;
+ } else if (EFFECT === "saturation") {
+ return saturation;
+ } else if (EFFECT === "color shift") {
+ return color;
+ } else if (EFFECT === "brightness") {
+ return brightness;
+ } else if (EFFECT === "invert") {
+ return invert;
+ } else if (EFFECT === "resize rendering mode") {
+ return resizeMode;
+ } else if (EFFECT === "sepia") {
+ return sepia;
+ } else if (EFFECT === "transparency") {
+ return transparency;
+ } else if (EFFECT === "scale") {
+ return scale;
+ } else if (EFFECT === "skew X") {
+ return skewX;
+ } else if (EFFECT === "skew Y") {
+ return skewY;
+ } else if (EFFECT === "offset X") {
+ return offsetX;
+ } else if (EFFECT === "offset Y") {
+ return offsetY;
+ } else if (EFFECT === "rotation") {
+ return rotation;
+ } else if (EFFECT === "border radius") {
+ return borderRadius;
+ }
+ return "";
+ }
+ seteffect({ EFFECT, NUMBER }) {
+ NUMBER = Scratch.Cast.toNumber(NUMBER);
+ if (EFFECT === "blur") {
+ blur = NUMBER;
+ } else if (EFFECT === "contrast") {
+ contrast = NUMBER;
+ } else if (EFFECT === "saturation") {
+ saturation = NUMBER;
+ } else if (EFFECT === "color shift") {
+ color = NUMBER;
+ } else if (EFFECT === "brightness") {
+ brightness = NUMBER;
+ } else if (EFFECT === "invert") {
+ invert = NUMBER;
+ } else if (EFFECT === "sepia") {
+ sepia = NUMBER;
+ } else if (EFFECT === "transparency") {
+ transparency = NUMBER;
+ } else if (EFFECT === "scale") {
+ scale = NUMBER;
+ } else if (EFFECT === "skew X") {
+ skewX = NUMBER;
+ } else if (EFFECT === "skew Y") {
+ skewY = NUMBER;
+ } else if (EFFECT === "offset X") {
+ offsetX = NUMBER;
+ } else if (EFFECT === "offset Y") {
+ offsetY = NUMBER;
+ } else if (EFFECT === "rotation") {
+ rotation = NUMBER;
+ } else if (EFFECT === "border radius") {
+ borderRadius = NUMBER;
+ }
+ updateStyle();
+ }
+ cleareffects() {
+ borderRadius = 0;
+ rotation = 0;
+ offsetY = 0;
+ offsetX = 0;
+ skewY = 0;
+ skewX = 0;
+ scale = 100;
+ transparency = 0;
+ sepia = 0;
+ blur = 0;
+ contrast = 100;
+ saturation = 100;
+ color = 0;
+ brightness = 100;
+ invert = 0;
+ resizeMode = "default";
+ updateStyle();
+ }
+ setrendermode({ EFFECT }) {
+ resizeMode = EFFECT;
+ updateStyle();
+ }
+ renderscale({ X, Y }) {
+ Scratch.vm.renderer.resize(X, Y);
}
- Scratch.extensions.register(new CanvasEffects());
+ }
+ Scratch.extensions.register(new CanvasEffects());
})(Scratch);
diff --git a/extensions/TheShovel/CustomStyles.js b/extensions/TheShovel/CustomStyles.js
index 979f8c6d7a..fda63171b0 100644
--- a/extensions/TheShovel/CustomStyles.js
+++ b/extensions/TheShovel/CustomStyles.js
@@ -1,739 +1,760 @@
// Name: Custom Styles
+// ID: shovelcss
// Description: Customize the appearance of variable monitors and prompts in your project.
// By: TheShovel
// Thanks LilyMakesThings for the awesome banner!
-(function(Scratch) {
- 'use strict';
-
- // Styles
- let monitorText = '';
- let monitorBorder = '';
- let monitorBackgroundColor = '';
- let variableValueBackground = '';
- let variableValueTextColor = '';
- let listFooterBackground = '';
- let listHeaderBackground = '';
- let listValueText = '';
- let listValueBackground = '';
- let variableValueRoundness = -1;
- let listValueRoundness = -1;
- let monitorBackgroundRoundness = -1;
- let monitorBackgroundBorderWidth = -1;
- let allowScrolling = '';
- let askBackground = '';
- let askBackgroundRoundness = -1;
- let askBackgroundBorderWidth = -1;
- let askButtonBackground = '';
- let askButtonRoundness = -1;
- let askInputBackground = '';
- let askInputRoundness = -1;
- let askInputBorderWidth = -1;
- let askBoxIcon = '';
- let askInputText = '';
- let askButtonImage = '';
- let askInputBorder = '';
-
- // CSS selectors
- let monitorRoot;
- let monitorValue;
- let monitorListHeader;
- let monitorListFooter;
- let monitorRowValueOuter;
- let monitorRowsInner;
- let monitorRowsScroller;
- let monitorRowIndex;
- let monitorValueLarge;
- let askBoxBG;
- let askBoxButton;
- let askBoxInner;
- let askBoxBorderMain;
- let askBoxBorderOuter;
- if (typeof scaffolding !== 'undefined') {
- monitorRoot = '.sc-monitor-root';
- monitorValue = '.sc-monitor-value';
- monitorListHeader = '.sc-monitor-list-label';
- monitorListFooter = '.sc-monitor-list-footer';
- monitorRowValueOuter = '.sc-monitor-row-value-outer';
- monitorRowsInner = '.sc-monitor-rows-inner';
- monitorRowsScroller = monitorRowsInner;
- monitorRowIndex = '.sc-monitor-row-index';
- monitorValueLarge = '.sc-monitor-large-value';
- askBoxBG = '.sc-question-inner';
- askBoxButton = '.sc-question-submit-button';
- askBoxInner = '.sc-question-input';
- askBoxBorderMain = '.sc-question-input:hover';
- askBoxBorderOuter = '.sc-question-input:focus';
- } else {
- monitorRoot = 'div[class^="monitor_monitor-container_"]';
- monitorValue = 'div[class^="monitor_value_"]';
- monitorListHeader = 'div[class^="monitor_list-header_"]';
- monitorListFooter = 'div[class^="monitor_list-footer_"]';
- monitorRowValueOuter = 'div[class^="monitor_list-value_"]';
- monitorRowsInner = 'div[class^="monitor_list-body_"]';
- monitorRowsScroller = 'div[class^="monitor_list-body_"] > .ReactVirtualized__List';
- monitorRowIndex = 'div[class^="monitor_list-index_"]';
- monitorValueLarge = 'div[class^="monitor_large-value_"]';
- askBoxBG = 'div[class^="question_question-container_"]';
- askBoxButton = 'button[class^="question_question-submit-button_"]';
- askBoxInner = '[class^="question_question-container_"] input[class^="input_input-form_"]';
- askBoxIcon = 'img[class^="question_question-submit-button-icon_"]';
- askBoxBorderMain = '[class^="question_question-input_"] input:focus, [class^="question_question-input_"] input:hover';
- askBoxBorderOuter = '[class^="question_question-input_"] > input:focus';
- }
-
- const ColorIcon = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxMTYuNDE5NjQiIGhlaWdodD0iMTE4LjM0OTk0IiB2aWV3Qm94PSIwLDAsMTE2LjQxOTY0LDExOC4zNDk5NCI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOC45MjgwOSw3LjY1MTk0KSI+PGcgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aXNQYWludGluZ0xheWVyJnF1b3Q7OnRydWV9IiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTMxLjc3NzE4LDEwLjU1MDMzYy0xMC4yOTkxNywxLjEwODUyIC0xNi4zNDU2NSw0LjY2NTg3IC0xOS4xNDcxOSw5LjE4MDU3Yy0xLjg5MjIsMy4yNDc5NyAtMi4zNDE1Niw3LjEzODcyIC0xLjIzOTUzLDEwLjczMjVsMC4wOTA3LDAuMzQyNjRsMjAuMjY1NzksLTIwLjI1NTcxek03OC4yODQ2OSwyMS43NTY0OGMtNy4zNDY0NywtNy4zNTY1NSAtMTQuNzczNTcsLTEzLjEwMDcgLTIwLjg5MDU5LC0xNi4yOTUyN2MtNC4zNDMzOSwtMi4yMzcyIC03LjcyOTQyLC0zLjEzNDA5IC05LjM5MjIsLTIuMTg2ODFsLTAuODI2MzYsMC44MjYzNmMtMS4yMjk0NSwyLjAxNTUgLTAuNTEzOTYsNi4zMDg0OSAxLjc5Mzc5LDExLjY2OTdjMy41NjMzNSw3LjczMjE5IDguNTIwMTUsMTQuNzQyMDggMTQuNjIyNCwyMC42Nzg5N2M2Ljk5Mzc3LDYuOTkzNzcgMTQuNTAxNDgsMTIuMjM0MDQgMjAuODkwNTksMTUuMTE2MmM0LjU0NDk0LDIuMDE1NSA4LjI3MzYsMi45MjI0NyAxMC40MzAxOCwyLjMzNzk3bDEuOTI0OCwtMS45MzQ4OGMwLjQ1MzQ4LC0xLjkyNDggLTAuNTAzODgsLTUuMTc5ODIgLTIuNTA5MjksLTkuMjAwNzNjLTMuMTEzOTQsLTYuMjU4MTEgLTguNzg3NTUsLTEzLjc2NTgyIC0xNi4wNDMzMywtMjEuMDExNTJ6TTYwLjI4NjMzLC0wLjE1MTk0YzYuNzExNTksMy40NTY1NyAxNC42OTI5NSw5LjY0NDE0IDIyLjQ4MjgzLDE3LjQzNDAyYzcuNzg5ODksNy43ODk4OSAxMy44NTY1MiwxNS44MjE2MyAxNy4yMjIzOSwyMi43MjQ2OWMzLjc3OTA1LDcuNTY4MTggNC4zODM3LDE0LjAyNzg0IDAuNjk1MzQsMTcuNzE2MTljLTAuNjA1OCwwLjU3NzQ5IC0xLjI5NTUyLDEuMDU5OTYgLTIuMDQ1NzIsMS40MzFsLTM5LjQyMzA2LDM5LjQwMjljLTIuMzY4MiwyLjM4ODM2IC0zLjM1NTgsMy4zOTYxMSAtNy40MzcxNyw0LjMxMzE1Yy0yLjAzMTMyLDAuNDQwMzcgLTQuMTE4NywwLjU2Mjc2IC02LjE4NzU3LDAuMzYyNzhjLTIuMTQxODMsLTAuMjA3OCAtNC4yNTIxNSwtMC42NjQzNyAtNi4yODgzNCwtMS4zNjA0NmMtOC4zOTQ1MywtMi44MjE2OSAtMTcuMTMxNjksLTguNzI3MDkgLTI0LjQwNzYyLC0xNS45ODI4NmMtNy4yNzU5MywtNy4yNTU3NyAtMTMuMzcyOCwtMTYuMDUzNDEgLTE2LjMyNTUsLTI0LjQ2ODFjLTAuNTUwNDYsLTEuNTI1NTIgLTAuOTc4NDMsLTMuMDkyNDggLTEuMjc5ODQsLTQuNjg2MDJjLTAuMjg2MDMsLTEuNDc3NzUgLTAuNDMxMTIsLTIuOTc5MyAtMC40MzMzNCwtNC40ODQ0N2MtMC4xMTgxNywtMi4zNTIzNCAwLjMwNTkyLC00LjcwMDM3IDEuMjM5NTMsLTYuODYyNzVjMC45Njc0LC0xLjg2MTczIDIuMjUzMiwtMy41Mzk3NiAzLjc5OTIxLC00Ljk1ODExbDAuMTYxMjQsLTAuMTcxMzJsNC43NDY0OSwtNC43MzY0MWMtMC40OTc0NywtMS4xMjYwMiAtMC45MDg0OCwtMi4yODgyOCAtMS4yMjk0NSwtMy40NzY3M2MtMS41NTYxLC01LjE5NTg5IC0wLjg1MzgyLC0xMC44MDY3NSAxLjkzNDg4LC0xNS40NTg4NGM0LjMwMzA4LC03LjAzNDA3IDEzLjk1NzMsLTEyLjI2NDI4IDMwLjY2NTczLC0xMi40MzU2bDQuMjAyMzEsLTQuMTkyMjNjMC40NDM2OCwtMC42MDgzMiAwLjk4MTExLC0xLjE0MjM1IDEuNTkyMjQsLTEuNTgyMTZjMC4xOTg1NywtMC4xODczMSAwLjQyNjcsLTAuMzQwNTMgMC42NzUxOSwtMC40NTM0OGMzLjczODc0LC0yLjI1NzM1IDkuMjcxMjcsLTEuMzYwNDYgMTUuNjQwMjMsMS45MjQ4ek04OS4wNDc0Myw1OS43Njg2OWMtMi40NjcyLC0wLjU1OTY2IC00Ljg2ODc0LC0xLjM3NzA3IC03LjE2NTA4LC0yLjQzODc1Yy03LjA1NDIzLC0zLjE4NDQ4IC0xNS4yMjcwNiwtOC44NzgyNSAtMjIuNzY1LC0xNi40MDYxMmMtNi42NjcyNywtNi41MDY3NiAtMTIuMDc4NTEsLTE0LjE4NjM2IC0xNS45NjI3MSwtMjIuNjU0MTVjLTEuMDk4MjYsLTIuNDc4NDUgLTEuOTA2NDYsLTUuMDc1NTEgLTIuNDA4NTIsLTcuNzM5NDlsLTI2LjQxODk4LDI2LjMwMjJjMTMuODgzNTcsMTUuMDM3NTkgNTUuMzcyMzcsMjAuMzU1OTQgNjguMjQxMywxNC44NDM1NmMxLjUyOTk3LC0wLjYyNjEyIDMuMjc5MTUsMC4wOTI3NiAzLjkyNjY5LDEuNjEzOGMwLjY0NzU0LDEuNTIxMDMgLTAuMDQ2NzEsMy4yODAxNCAtMS41NTg0OSwzLjk0ODk3Yy0xNS4zMjc4Myw2LjU2MDQzIC02Mi41NTg3NiwtMS41MjU3NiAtNzQuODY2MzQsLTE2LjA4MzA5bC0zLjY5ODQzLDMuNzM4NzRjLTEuMDE3OSwwLjkxNDA1IC0xLjg4MjE3LDEuOTg1ODcgLTIuNTU5NjgsMy4xNzQ0Yy0wLjUxNTM2LDEuMzI3MzcgLTAuNzM1NDUsMi43NTEwNSAtMC42NDQ5Niw0LjE3MjA3YzAuMDA3NywxLjExNjQyIDAuMTE5MDQsMi4yMjk3MiAwLjMzMjU2LDMuMzI1NTZjMC4yMzkxMywxLjI4MDIzIDAuNTc2MDMsMi41NDAyNCAxLjAwNzc0LDMuNzY4OTdjMi42MzAyMiw3LjQ4NzU2IDguMDYxOTcsMTUuMzg4MjkgMTQuODIzOTYsMjIuMDU5NThjNi43NjE5OSw2LjY3MTI5IDE0LjUyMTYzLDEyLjAyMjQyIDIxLjk3ODk2LDE0LjQ5MTRjMS41NjYyMSwwLjU0IDMuMTg5MjMsMC44OTg0MSA0LjgzNzE5LDEuMDY4MjF2MGMxLjQxNDg2LDAuMTM5NCAyLjg0Mjc3LDAuMDU3ODEgNC4yMzI1MywtMC4yNDE4NmMxLjcwOTMzLC0wLjI5ODk0IDMuMjQ4NTEsLTEuMjE3NDEgNC4zMjMyMywtMi41Nzk4M2MwLjA2MDQ2LC0wLjA3MDU0IDAuNjQ0OTYsLTAuNjQ0OTYgMC42MzQ4OCwtMC42NTUwNHoiIGZpbGw9IiMwMDAwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlLW9wYWNpdHk9IjAuMTI5NDEiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxNSIvPjxwYXRoIGQ9Ik0zLjI5NzAyLDQ5LjQyMTZjMi40NDg2OSwwLjQ4ODgxIDE0LjYwMzczLDI0LjI5Nzc5IDMxLjc5OSwyMS40MjkyM2MxNS42MzU4MywtMi42MDg0MSA5LjA4MDY3LDE0LjMzMjQ1IDQ4LjU0ODY1LC01LjUwNjgybC0yOC41MDkxNiwyOC40Nzg5M2MwLDAgLTAuNTc0NDIsMC41ODQ1IC0wLjYzNDg4LDAuNjU1MDRjLTEuMDc0NzIsMS4zNjI0MSAtMi42MTM5LDIuMjgwOSAtNC4zMjMyMywyLjU3OTgzYy0xLjM4OTc2LDAuMjk5NjYgLTIuODE3NjgsMC4zODEyNiAtNC4yMzI1MywwLjI0MTg2djBjLTEuNjQ3OTYsLTAuMTY5NzkgLTMuMjcwOTcsLTAuNTI4MjEgLTQuODM3MTksLTEuMDY4MjFjLTcuNDU3MzMsLTIuNDk5MjEgLTE1LjI4NzUyLC03Ljg2MDQzIC0yMS45NTg4LC0xNC40ODEzMmMtNi42NzEyOSwtNi42MjA4OSAtMTIuMTYzNSwtMTQuNTcyMDIgLTE0Ljc5MzcyLC0yMi4wNTk1OGMtMC40MzE3MiwtMS4yMjg3NCAtMC43Njg2MiwtMi40ODg3NSAtMS4wMDc3NCwtMy43Njg5N2MtMC4yMTM1MiwtMS4wOTU4NSAtMC4zMjQ4NiwtMi4yMDkxNCAtMC4zMzI1NiwtMy4zMjU1NmMtMC4wMzY5LC0xLjA2NTcxIDAuMDU3ODcsLTIuMTMxOSAwLjI4MjE2LC0zLjE3NDR6IiBmaWxsPSIjZjU0MjQyIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIi8+PHBhdGggZD0iTTMxLjc3NzE4LDEwLjU1MDMzYy0xMC4yOTkxNywxLjEwODUyIC0xNi4zNDU2NSw0LjY2NTg3IC0xOS4xNDcxOSw5LjE4MDU3Yy0xLjg5MjIsMy4yNDc5NyAtMi4zNDE1Niw3LjEzODcyIC0xLjIzOTUzLDEwLjczMjVsMC4wOTA3LDAuMzQyNjRsMjAuMjY1NzksLTIwLjI1NTcxek03OC4yODQ2OSwyMS43NTY0OGMtNy4zNDY0NywtNy4zNTY1NSAtMTQuNzczNTcsLTEzLjEwMDcgLTIwLjg5MDU5LC0xNi4yOTUyN2MtNC4zNDMzOSwtMi4yMzcyIC03LjcyOTQyLC0zLjEzNDA5IC05LjM5MjIsLTIuMTg2ODFsLTAuODI2MzYsMC44MjYzNmMtMS4yMjk0NSwyLjAxNTUgLTAuNTEzOTYsNi4zMDg0OSAxLjc5Mzc5LDExLjY2OTdjMy41NjMzNSw3LjczMjE5IDguNTIwMTUsMTQuNzQyMDggMTQuNjIyNCwyMC42Nzg5N2M2Ljk5Mzc3LDYuOTkzNzcgMTQuNTAxNDgsMTIuMjM0MDQgMjAuODkwNTksMTUuMTE2MmM0LjU0NDk0LDIuMDE1NSA4LjI3MzYsMi45MjI0NyAxMC40MzAxOCwyLjMzNzk3bDEuOTI0OCwtMS45MzQ4OGMwLjQ1MzQ4LC0xLjkyNDggLTAuNTAzODgsLTUuMTc5ODIgLTIuNTA5MjksLTkuMjAwNzNjLTMuMTEzOTQsLTYuMjU4MTEgLTguNzg3NTUsLTEzLjc2NTgyIC0xNi4wNDMzMywtMjEuMDExNTJ6TTYwLjI4NjMzLC0wLjE1MTk0YzYuNzExNTksMy40NTY1NyAxNC42OTI5NSw5LjY0NDE0IDIyLjQ4MjgzLDE3LjQzNDAyYzcuNzg5ODksNy43ODk4OSAxMy44NTY1MiwxNS44MjE2MyAxNy4yMjIzOSwyMi43MjQ2OWMzLjc3OTA1LDcuNTY4MTggNC4zODM3LDE0LjAyNzg0IDAuNjk1MzQsMTcuNzE2MTljLTAuNjA1OCwwLjU3NzQ5IC0xLjI5NTUyLDEuMDU5OTYgLTIuMDQ1NzIsMS40MzFsLTM5LjQyMzA2LDM5LjQwMjljLTIuMzY4MiwyLjM4ODM2IC0zLjM1NTgsMy4zOTYxMSAtNy40MzcxNyw0LjMxMzE1Yy0yLjAzMTMyLDAuNDQwMzcgLTQuMTE4NywwLjU2Mjc2IC02LjE4NzU3LDAuMzYyNzhjLTIuMTQxODMsLTAuMjA3OCAtNC4yNTIxNSwtMC42NjQzNyAtNi4yODgzNCwtMS4zNjA0NmMtOC4zOTQ1MywtMi44MjE2OSAtMTcuMTMxNjksLTguNzI3MDkgLTI0LjQwNzYyLC0xNS45ODI4NmMtNy4yNzU5MywtNy4yNTU3NyAtMTMuMzcyOCwtMTYuMDUzNDEgLTE2LjMyNTUsLTI0LjQ2ODFjLTAuNTUwNDYsLTEuNTI1NTIgLTAuOTc4NDMsLTMuMDkyNDggLTEuMjc5ODQsLTQuNjg2MDJjLTAuMjg2MDMsLTEuNDc3NzUgLTAuNDMxMTIsLTIuOTc5MyAtMC40MzMzNCwtNC40ODQ0N2MtMC4xMTgxNywtMi4zNTIzNCAwLjMwNTkyLC00LjcwMDM3IDEuMjM5NTMsLTYuODYyNzVjMC45Njc0LC0xLjg2MTczIDIuMjUzMiwtMy41Mzk3NiAzLjc5OTIxLC00Ljk1ODExbDAuMTYxMjQsLTAuMTcxMzJsNC43NDY0OSwtNC43MzY0MWMtMC40OTc0NywtMS4xMjYwMiAtMC45MDg0OCwtMi4yODgyOCAtMS4yMjk0NSwtMy40NzY3M2MtMS41NTYxLC01LjE5NTg5IC0wLjg1MzgyLC0xMC44MDY3NSAxLjkzNDg4LC0xNS40NTg4NGM0LjMwMzA4LC03LjAzNDA3IDEzLjk1NzMsLTEyLjI2NDI4IDMwLjY2NTczLC0xMi40MzU2bDQuMjAyMzEsLTQuMTkyMjNjMC40NDM2OCwtMC42MDgzMiAwLjk4MTExLC0xLjE0MjM1IDEuNTkyMjQsLTEuNTgyMTZjMC4xOTg1NywtMC4xODczMSAwLjQyNjcsLTAuMzQwNTMgMC42NzUxOSwtMC40NTM0OGMzLjczODc0LC0yLjI1NzM1IDkuMjcxMjcsLTEuMzYwNDYgMTUuNjQwMjMsMS45MjQ4ek04OS4wNDc0Myw1OS43Njg2OWMtMi40NjcyLC0wLjU1OTY2IC00Ljg2ODc0LC0xLjM3NzA3IC03LjE2NTA4LC0yLjQzODc1Yy03LjA1NDIzLC0zLjE4NDQ4IC0xNS4yMjcwNiwtOC44NzgyNSAtMjIuNzY1LC0xNi40MDYxMmMtNi42NjcyNywtNi41MDY3NiAtMTIuMDc4NTEsLTE0LjE4NjM2IC0xNS45NjI3MSwtMjIuNjU0MTVjLTEuMDk4MjYsLTIuNDc4NDUgLTEuOTA2NDYsLTUuMDc1NTEgLTIuNDA4NTIsLTcuNzM5NDlsLTI2LjQxODk4LDI2LjMwMjJjMTMuODgzNTcsMTUuMDM3NTkgNTUuMzcyMzcsMjAuMzU1OTQgNjguMjQxMywxNC44NDM1NmMxLjUyOTk3LC0wLjYyNjEyIDMuMjc5MTUsMC4wOTI3NiAzLjkyNjY5LDEuNjEzOGMwLjY0NzU0LDEuNTIxMDMgLTAuMDQ2NzEsMy4yODAxNCAtMS41NTg0OSwzLjk0ODk3Yy0xNS4zMjc4Myw2LjU2MDQzIC02Mi41NTg3NiwtMS41MjU3NiAtNzQuODY2MzQsLTE2LjA4MzA5bC0zLjY5ODQzLDMuNzM4NzRjLTEuMDE3OSwwLjkxNDA1IC0xLjg4MjE3LDEuOTg1ODcgLTIuNTU5NjgsMy4xNzQ0Yy0wLjUxNTM2LDEuMzI3MzcgLTAuNzM1NDUsMi43NTEwNSAtMC42NDQ5Niw0LjE3MjA3YzAuMDA3NywxLjExNjQyIDAuMTE5MDQsMi4yMjk3MiAwLjMzMjU2LDMuMzI1NTZjMC4yMzkxMywxLjI4MDIzIDAuNTc2MDMsMi41NDAyNCAxLjAwNzc0LDMuNzY4OTdjMi42MzAyMiw3LjQ4NzU2IDguMDYxOTcsMTUuMzg4MjkgMTQuODIzOTYsMjIuMDU5NThjNi43NjE5OSw2LjY3MTI5IDE0LjUyMTYzLDEyLjAyMjQyIDIxLjk3ODk2LDE0LjQ5MTRjMS41NjYyMSwwLjU0IDMuMTg5MjMsMC44OTg0MSA0LjgzNzE5LDEuMDY4MjF2MGMxLjQxNDg2LDAuMTM5NCAyLjg0Mjc3LDAuMDU3ODEgNC4yMzI1MywtMC4yNDE4NmMxLjcwOTMzLC0wLjI5ODk0IDMuMjQ4NTEsLTEuMjE3NDEgNC4zMjMyMywtMi41Nzk4M2MwLjA2MDQ2LC0wLjA3MDU0IDAuNjQ0OTYsLTAuNjQ0OTYgMC42MzQ4OCwtMC42NTUwNHoiIGZpbGw9IiMwMDAwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEwIi8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6NTguOTI4MDk6NTcuNjUxOTM5OTk5OTk5OTk2LS0+';
- const BorderIcon = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxMjkuMDA4NDIiIGhlaWdodD0iMTI5LjAwODQzIiB2aWV3Qm94PSIwLDAsMTI5LjAwODQyLDEyOS4wMDg0MyI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTQuNTA0MjEsMTQuNTA0MjIpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWRhc2hhcnJheT0iIiBzdHJva2UtZGFzaG9mZnNldD0iMCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0tMi4wMDQyMSw4Ny40NTM4OGMwLC0yMi44MTE2IDAsLTY1LjYwODczIDAsLTc3LjM5MjMxYzAsLTUuOTA0NzkgNy4wNTUxNiwtMTIuMDY1NzkgMTMuMTA3OTYsLTEyLjA2NTc5YzExLjkwNTQsMCA1NC4yNjQ2NSwwIDc2LjcwNzQyLDBjOC41Mzc2NywwIDE0LjE5MzA0LDcuMTQ4NzcgMTQuMTkzMDQsMTcuNTQ0ODljMCwyMy44MjMyNSAwLDY0LjY5OTA0IDAsNzUuNjgwMDljMCw1LjM2NDQ4IC02LjcyOTAyLDEwLjc4MzQ1IC0xNi41OTAxNSwxMC43ODM0NWMtMjIuODg5MjQsMCAtNjIuNjUzNTUsMCAtNzQuMzEwMzEsMGMtNi4zMzM1NSwwIC0xMy4xMDc5NiwtNS44MDcyNCAtMTMuMTA3OTYsLTE0LjU1MDMzeiIgc3Ryb2tlLW9wYWNpdHk9IjAuMTI5NDEiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIyNSIvPjxwYXRoIGQ9Ik0tMi4wMDQyMSw4Ny40NTM4OGMwLC0yMi44MTE2IDAsLTY1LjYwODczIDAsLTc3LjM5MjMxYzAsLTUuOTA0NzkgNy4wNTUxNiwtMTIuMDY1NzkgMTMuMTA3OTYsLTEyLjA2NTc5YzExLjkwNTQsMCA1NC4yNjQ2NSwwIDc2LjcwNzQyLDBjOC41Mzc2NywwIDE0LjE5MzA0LDcuMTQ4NzcgMTQuMTkzMDQsMTcuNTQ0ODljMCwyMy44MjMyNSAwLDY0LjY5OTA0IDAsNzUuNjgwMDljMCw1LjM2NDQ4IC02LjcyOTAyLDEwLjc4MzQ1IC0xNi41OTAxNSwxMC43ODM0NWMtMjIuODg5MjQsMCAtNjIuNjUzNTUsMCAtNzQuMzEwMzEsMGMtNi4zMzM1NSwwIC0xMy4xMDc5NiwtNS44MDcyNCAtMTMuMTA3OTYsLTE0LjU1MDMzeiIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjIwIi8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6NjQuNTA0MjE6NjQuNTA0MjItLT4=';
- const extensionIcon = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciICB2aWV3Qm94PSIwIDAgMjk2Mjk3IDMzMzMzMyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHNoYXBlLXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIiB0ZXh0LXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIiBpbWFnZS1yZW5kZXJpbmc9Im9wdGltaXplUXVhbGl0eSIgZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iaWQ0IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjU0MTI4LjciIHkxPSI3OTM1NS41IiB4Mj0iMjQwMzE4IiB5Mj0iNzkzNTUuNSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZThlN2U1Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjZmZmIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImlkNSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSI2MjAxOS4zIiB5MT0iMjAyODY4IiB4Mj0iMjMzNTE1IiB5Mj0iMjAyODY4Ij48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNlOGU3ZTUiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmZmYiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iaWQ2IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjEwNDk2MyIgeTE9Ijk5NjE2LjkiIHgyPSIxMDQ5NjMiIHkyPSIxNzEwMjEiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2QxZDNkNCIvPjxzdG9wIG9mZnNldD0iLjM4OCIgc3RvcC1jb2xvcj0iI2QxZDNkNCIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI2QxZDNkNCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJpZDciIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4bGluazpocmVmPSIjaWQ2IiB4MT0iMTk0MTc5IiB5MT0iNjExODUuOCIgeDI9IjE5NDE3OSIgeTI9IjEzNTQwNyIvPjxtYXNrIGlkPSJpZDAiPjxsaW5lYXJHcmFkaWVudCBpZD0iaWQxIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjEwNDk2MyIgeTE9Ijk5NjE2LjkiIHgyPSIxMDQ5NjMiIHkyPSIxNzEwMjEiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1vcGFjaXR5PSIwIiBzdG9wLWNvbG9yPSIjZmZmIi8+PHN0b3Agb2Zmc2V0PSIuMzg4IiBzdG9wLWNvbG9yPSIjZmZmIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii44MzEiIHN0b3AtY29sb3I9IiNmZmYiLz48L2xpbmVhckdyYWRpZW50PjxwYXRoIGZpbGw9InVybCgjaWQxKSIgZD0iTTYxNzM3IDk5NDY3aDg2NDUzdjcxNzA0SDYxNzM3eiIvPjwvbWFzaz48bWFzayBpZD0iaWQyIj48bGluZWFyR3JhZGllbnQgaWQ9ImlkMyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIxOTQxNzkiIHkxPSI2MTE4NS44IiB4Mj0iMTk0MTc5IiB5Mj0iMTM1NDA3Ij48c3RvcCBvZmZzZXQ9IjAiIHN0b3Atb3BhY2l0eT0iMCIgc3RvcC1jb2xvcj0iI2ZmZiIvPjxzdG9wIG9mZnNldD0iLjM4OCIgc3RvcC1jb2xvcj0iI2ZmZiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1vcGFjaXR5PSIuODMxIiBzdG9wLWNvbG9yPSIjZmZmIi8+PC9saW5lYXJHcmFkaWVudD48cGF0aCBmaWxsPSJ1cmwoI2lkMykiIGQ9Ik0xNDc4OTAgNjEwMzZoOTI1Nzh2NzQ1MjFoLTkyNTc4eiIvPjwvbWFzaz48c3R5bGU+LmZpbDZ7ZmlsbDojMDAwO2ZpbGwtb3BhY2l0eTouMDUwOTh9PC9zdHlsZT48L2RlZnM+PGcgaWQ9IkxheWVyX3gwMDIwXzEiPjxnIGlkPSJfNTEzMDg1MzA0Ij48cGF0aCBmaWxsPSIjMjA2MmFmIiBkPSJNMjY4NTE3IDMwMDkyMmwtMTIwMzY5IDMyNDExLTEyMDM3MS0zMjQxMUwwIDBoMjk2Mjk3eiIvPjxwYXRoIGZpbGw9IiMzYzljZDciIGQ9Ik0xNDgxNDYgMjQzNzR2MjgzMTA5bDI3MyA3NCA5NzQwOS0yNjIyOSAyMjQ4NS0yNTY5NTR6Ii8+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTE0ODA0MCA5OTYxN2wtODYxNTMgMzU4ODAgMjg1NyAzNTUyNCA4MzI5Ni0zNTYxNCA4ODYwNC0zNzg4MyAzNjc0LTM2MzM5LTkyMjc4IDM4NDMyeiIvPjxwYXRoIG1hc2s9InVybCgjaWQwKSIgZmlsbD0idXJsKCNpZDYpIiBkPSJNNjE4ODcgMTM1NDk3bDI4NTcgMzU1MjQgODMyOTUtMzU2MTRWOTk2MTd6Ii8+PHBhdGggbWFzaz0idXJsKCNpZDIpIiBmaWxsPSJ1cmwoI2lkNykiIGQ9Ik0yNDAzMTggNjExODZsLTkyMjc4IDM4NDMxdjM1NzkwbDg4NjA0LTM3ODgzeiIvPjxwYXRoIGZpbGw9InVybCgjaWQ1KSIgZD0iTTYyMDE5IDEzNTQ5N2wyODU4IDM1NTI0IDEyNzgwNiA0MDctMjg1OSA0NzM2NS00MjA1NSAxMTg0MC00MDQyOC0xMDIwOC0yNDUwLTI5Mzk5SDY3MzI3bDQ5MDAgNTY3NTYgNzU5NTAgMjI0NTcgNzU1MzgtMjIwNTAgOTgwMC0xMTI2OTJ6Ii8+PHBhdGggY2xhc3M9ImZpbDYiIGQ9Ik0xNDgwNDAgMTM1NDk3SDYxODg4bDI4NTcgMzU1MjQgODMyOTUgMjY2di0zNTc5MHptMCA5NTAyMmwtNDA4IDExNC00MDQyMi0xMDIwOC0yNDUwLTI5Mzk5SDY3MTk3bDQ4OTkgNTY3NTYgNzU5NDQgMjI0NTd2LTM5NzIweiIvPjxwYXRoIGZpbGw9InVybCgjaWQ0KSIgZD0iTTU0MTI5IDYxMTg2aDE4NjE4OWwtMzY3NCAzNjMzOUg1ODYyMGwtNDQ5MS0zNjMzOXoiLz48cGF0aCBjbGFzcz0iZmlsNiIgZD0iTTE0ODA0MCA2MTE4Nkg1NDEyOWw0NDkxIDM2MzM5aDg5NDIweiIvPjwvZz48L2c+PC9zdmc+';
- const miscIcon = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxMzUuMzc5IiBoZWlnaHQ9IjEzNS4zNzciIHZpZXdCb3g9IjAsMCwxMzUuMzc5LDEzNS4zNzciPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzIuMzEsLTgyLjMxMSkiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbD0iIzAwMDAwMCIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTM0OC45NjcsMTEyLjA4YzIuMzIxLDIuMzIxIDIuMzIxLDYuMTE4IDAsOC40MzlsLTcuMTAxLDcuMTAxYzEuOTU5LDMuNjU4IDMuNDU0LDcuNjAxIDQuNDA1LDExLjc1Mmg5LjE5OWMzLjI4MywwIDUuOTY5LDIuNjg2IDUuOTY5LDUuOTY4djEyLjQ3MWMwLDMuMjgzIC0yLjY4Niw1Ljk2OSAtNS45NjksNS45NjloLTEwLjAzOWMtMS4yMzEsNC4wNjMgLTIuOTkyLDcuODk2IC01LjIwNCwxMS40MThsNi41MTIsNi41MWMyLjMyMSwyLjMyMyAyLjMyMSw2LjEyIDAsOC40NGwtOC44MTgsOC44MTljLTIuMzIxLDIuMzIgLTYuMTE5LDIuMzIgLTguNDM5LDBsLTcuMTAyLC03LjEwMmMtMy42NTcsMS45NiAtNy42MDEsMy40NTYgLTExLjc1Myw0LjQwNnY5LjE5OWMwLDMuMjgyIC0yLjY4NSw1Ljk2OCAtNS45NjgsNS45NjhoLTEyLjQ3Yy0zLjI4MywwIC01Ljk2OSwtMi42ODYgLTUuOTY5LC01Ljk2OHYtMTAuMDM5Yy00LjA2MywtMS4yMzIgLTcuODk2LC0yLjk5MyAtMTEuNDE3LC01LjIwNWwtNi41MTEsNi41MTJjLTIuMzIzLDIuMzIxIC02LjEyLDIuMzIxIC04LjQ0MSwwbC04LjgxOCwtOC44MThjLTIuMzIxLC0yLjMyMSAtMi4zMjEsLTYuMTE4IDAsLTguNDM5bDcuMTAyLC03LjEwMmMtMS45NiwtMy42NTcgLTMuNDU2LC03LjYgLTQuNDA1LC0xMS43NTFoLTkuMjAyYy0zLjI4MiwwIC01Ljk2OCwtMi42ODUgLTUuOTY4LC01Ljk2OHYtMTIuNDcxYzAsLTMuMjgzIDIuNjg2LC01Ljk2OCA1Ljk2OCwtNS45NjhoMTAuMDM5YzEuMjMyLC00LjA2MyAyLjk5MywtNy44OTYgNS4yMDQsLTExLjQxOGwtNi41MTEsLTYuNTFjLTIuMzIxLC0yLjMyMiAtMi4zMjEsLTYuMTIgMCwtOC40NGw4LjgxOSwtOC44MTljMi4zMjEsLTIuMzIxIDYuMTE4LC0yLjMyMSA4LjQzOSwwbDcuMTAxLDcuMTAxYzMuNjU4LC0xLjk2IDcuNjAxLC0zLjQ1NiAxMS43NTMsLTQuNDA2di05LjE5OWMwLC0zLjI4MyAyLjY4NiwtNS45NjkgNS45NjgsLTUuOTY5aDEyLjQ3MWMzLjI4MiwwIDUuOTY4LDIuNjg2IDUuOTY4LDUuOTY5djEwLjAzNmM0LjA2NCwxLjIzMSA3Ljg5OCwyLjk5MiAxMS40MjIsNS4yMDRsNi41MDcsLTYuNTA5YzIuMzIzLC0yLjMyMSA2LjEyLC0yLjMyMSA4LjQ0MSwwek0zMjQuNTE5LDE1MGMwLDEzLjUzOCAtMTAuOTc5LDI0LjUxOSAtMjQuNTE5LDI0LjUxOWMtMTMuNTM5LDAgLTI0LjUxOSwtMTAuOTggLTI0LjUxOSwtMjQuNTE5YzAsLTEzLjUzOSAxMC45OCwtMjQuNTE5IDI0LjUxOSwtMjQuNTE5YzEzLjU0LDAgMjQuNTE5LDEwLjk4IDI0LjUxOSwyNC41MTl6IiBzdHJva2Utb3BhY2l0eT0iMC4xMjk0MSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEyLjUiLz48cGF0aCBkPSJNMzQ4Ljk2NywxMTIuMDhjMi4zMjEsMi4zMjEgMi4zMjEsNi4xMTggMCw4LjQzOWwtNy4xMDEsNy4xMDFjMS45NTksMy42NTggMy40NTQsNy42MDEgNC40MDUsMTEuNzUyaDkuMTk5YzMuMjgzLDAgNS45NjksMi42ODYgNS45NjksNS45Njh2MTIuNDcxYzAsMy4yODMgLTIuNjg2LDUuOTY5IC01Ljk2OSw1Ljk2OWgtMTAuMDM5Yy0xLjIzMSw0LjA2MyAtMi45OTIsNy44OTYgLTUuMjA0LDExLjQxOGw2LjUxMiw2LjUxYzIuMzIxLDIuMzIzIDIuMzIxLDYuMTIgMCw4LjQ0bC04LjgxOCw4LjgxOWMtMi4zMjEsMi4zMiAtNi4xMTksMi4zMiAtOC40MzksMGwtNy4xMDIsLTcuMTAyYy0zLjY1NywxLjk2IC03LjYwMSwzLjQ1NiAtMTEuNzUzLDQuNDA2djkuMTk5YzAsMy4yODIgLTIuNjg1LDUuOTY4IC01Ljk2OCw1Ljk2OGgtMTIuNDdjLTMuMjgzLDAgLTUuOTY5LC0yLjY4NiAtNS45NjksLTUuOTY4di0xMC4wMzljLTQuMDYzLC0xLjIzMiAtNy44OTYsLTIuOTkzIC0xMS40MTcsLTUuMjA1bC02LjUxMSw2LjUxMmMtMi4zMjMsMi4zMjEgLTYuMTIsMi4zMjEgLTguNDQxLDBsLTguODE4LC04LjgxOGMtMi4zMjEsLTIuMzIxIC0yLjMyMSwtNi4xMTggMCwtOC40MzlsNy4xMDIsLTcuMTAyYy0xLjk2LC0zLjY1NyAtMy40NTYsLTcuNiAtNC40MDUsLTExLjc1MWgtOS4yMDJjLTMuMjgyLDAgLTUuOTY4LC0yLjY4NSAtNS45NjgsLTUuOTY4di0xMi40NzFjMCwtMy4yODMgMi42ODYsLTUuOTY4IDUuOTY4LC01Ljk2OGgxMC4wMzljMS4yMzIsLTQuMDYzIDIuOTkzLC03Ljg5NiA1LjIwNCwtMTEuNDE4bC02LjUxMSwtNi41MWMtMi4zMjEsLTIuMzIyIC0yLjMyMSwtNi4xMiAwLC04LjQ0bDguODE5LC04LjgxOWMyLjMyMSwtMi4zMjEgNi4xMTgsLTIuMzIxIDguNDM5LDBsNy4xMDEsNy4xMDFjMy42NTgsLTEuOTYgNy42MDEsLTMuNDU2IDExLjc1MywtNC40MDZ2LTkuMTk5YzAsLTMuMjgzIDIuNjg2LC01Ljk2OSA1Ljk2OCwtNS45NjloMTIuNDcxYzMuMjgyLDAgNS45NjgsMi42ODYgNS45NjgsNS45Njl2MTAuMDM2YzQuMDY0LDEuMjMxIDcuODk4LDIuOTkyIDExLjQyMiw1LjIwNGw2LjUwNywtNi41MDljMi4zMjMsLTIuMzIxIDYuMTIsLTIuMzIxIDguNDQxLDB6TTMyNC41MTksMTUwYzAsMTMuNTM4IC0xMC45NzksMjQuNTE5IC0yNC41MTksMjQuNTE5Yy0xMy41MzksMCAtMjQuNTE5LC0xMC45OCAtMjQuNTE5LC0yNC41MTljMCwtMTMuNTM5IDEwLjk4LC0yNC41MTkgMjQuNTE5LC0yNC41MTljMTMuNTQsMCAyNC41MTksMTAuOTggMjQuNTE5LDI0LjUxOXoiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSI3LjUiLz48L2c+PC9nPjwvc3ZnPjwhLS1yb3RhdGlvbkNlbnRlcjo2Ny42OTo2Ny42ODktLT4=';
- const TransparentIcon = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxMTYuNDE5NjQiIGhlaWdodD0iMTE4LjM0OTk0IiB2aWV3Qm94PSIwLDAsMTE2LjQxOTY0LDExOC4zNDk5NCI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOC45MjgwOSw3LjY1MTk0KSI+PGcgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aXNQYWludGluZ0xheWVyJnF1b3Q7OnRydWV9IiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTQuNDk0OTYsNDYuMzAwNDRjMi40Mjk4NiwwLjQ4NTA1IDE0LjQ5MTQ3LDI0LjExMSAzMS41NTQ1NSwyMS4yNjQ1YzE1LjUxNTYzLC0yLjU4ODM2IDEuNjU1NjMsMjMuNDU1NDUgNDAuODIwMjEsMy43Njg2OWwtMjAuOTM0NzYsMTkuMDI2ODJjMCwwIC0wLjU3LDAuNTggLTAuNjMsMC42NWMtMS4wNjY0NiwxLjM1MTk0IC0yLjU5MzgxLDIuMjYzMzYgLTQuMjksMi41NmMtMS4zNzkwOCwwLjI5NzM2IC0yLjc5NjAyLDAuMzc4MzMgLTQuMiwwLjI0djBjLTEuNjM1MjksLTAuMTY4NDkgLTMuMjQ1ODMsLTAuNTI0MTUgLTQuOCwtMS4wNmMtNy40LC0yLjQ4IC0xNS4xNywtNy44IC0yMS43OSwtMTQuMzdjLTYuNjIsLTYuNTcgLTEyLjA3LC0xNC40NiAtMTQuNjgsLTIxLjg5Yy0wLjQyODQsLTEuMjE5MjkgLTAuNzYyNzEsLTIuNDY5NjIgLTEsLTMuNzRjLTAuMjExODgsLTEuMDg3NDIgLTAuMzIyMzYsLTIuMTkyMTYgLTAuMzMsLTMuM2MtMC4wMzY2MiwtMS4wNTc1MiAwLjA1NzQyLC0yLjExNTUyIDAuMjgsLTMuMTV6IiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxMCIvPjxwYXRoIGQ9Ik0zMS43NzcxOCwxMC41NTAzM2MtMTAuMjk5MTcsMS4xMDg1MiAtMTYuMzQ1NjUsNC42NjU4NyAtMTkuMTQ3MTksOS4xODA1N2MtMS44OTIyLDMuMjQ3OTcgLTIuMzQxNTYsNy4xMzg3MiAtMS4yMzk1MywxMC43MzI1bDAuMDkwNywwLjM0MjY0bDIwLjI2NTc5LC0yMC4yNTU3MXpNNzguMjg0NjksMjEuNzU2NDhjLTcuMzQ2NDcsLTcuMzU2NTUgLTE0Ljc3MzU3LC0xMy4xMDA3IC0yMC44OTA1OSwtMTYuMjk1MjdjLTQuMzQzMzksLTIuMjM3MiAtNy43Mjk0MiwtMy4xMzQwOSAtOS4zOTIyLC0yLjE4NjgxbC0wLjgyNjM2LDAuODI2MzZjLTEuMjI5NDUsMi4wMTU1IC0wLjUxMzk2LDYuMzA4NDkgMS43OTM3OSwxMS42Njk3YzMuNTYzMzUsNy43MzIxOSA4LjUyMDE1LDE0Ljc0MjA4IDE0LjYyMjQsMjAuNjc4OTdjNi45OTM3Nyw2Ljk5Mzc3IDE0LjUwMTQ4LDEyLjIzNDA0IDIwLjg5MDU5LDE1LjExNjJjNC41NDQ5NCwyLjAxNTUgOC4yNzM2LDIuOTIyNDcgMTAuNDMwMTgsMi4zMzc5N2wxLjkyNDgsLTEuOTM0ODhjMC40NTM0OCwtMS45MjQ4IC0wLjUwMzg4LC01LjE3OTgyIC0yLjUwOTI5LC05LjIwMDczYy0zLjExMzk0LC02LjI1ODExIC04Ljc4NzU1LC0xMy43NjU4MiAtMTYuMDQzMzMsLTIxLjAxMTUyek02MC4yODYzMywtMC4xNTE5NGM2LjcxMTU5LDMuNDU2NTcgMTQuNjkyOTUsOS42NDQxNCAyMi40ODI4MywxNy40MzQwMmM3Ljc4OTg5LDcuNzg5ODkgMTMuODU2NTIsMTUuODIxNjMgMTcuMjIyMzksMjIuNzI0NjljMy43NzkwNSw3LjU2ODE4IDQuMzgzNywxNC4wMjc4NCAwLjY5NTM0LDE3LjcxNjE5Yy0wLjYwNTgsMC41Nzc0OSAtMS4yOTU1MiwxLjA1OTk2IC0yLjA0NTcyLDEuNDMxbC0zOS40MjMwNiwzOS40MDI5Yy0yLjM2ODIsMi4zODgzNiAtMy4zNTU4LDMuMzk2MTEgLTcuNDM3MTcsNC4zMTMxNWMtMi4wMzEzMiwwLjQ0MDM3IC00LjExODcsMC41NjI3NiAtNi4xODc1NywwLjM2Mjc4Yy0yLjE0MTgzLC0wLjIwNzggLTQuMjUyMTUsLTAuNjY0MzcgLTYuMjg4MzQsLTEuMzYwNDZjLTguMzk0NTMsLTIuODIxNjkgLTE3LjEzMTY5LC04LjcyNzA5IC0yNC40MDc2MiwtMTUuOTgyODZjLTcuMjc1OTMsLTcuMjU1NzcgLTEzLjM3MjgsLTE2LjA1MzQxIC0xNi4zMjU1LC0yNC40NjgxYy0wLjU1MDQ2LC0xLjUyNTUyIC0wLjk3ODQzLC0zLjA5MjQ4IC0xLjI3OTg0LC00LjY4NjAyYy0wLjI4NjAzLC0xLjQ3Nzc1IC0wLjQzMTEyLC0yLjk3OTMgLTAuNDMzMzQsLTQuNDg0NDdjLTAuMTE4MTcsLTIuMzUyMzQgMC4zMDU5MiwtNC43MDAzNyAxLjIzOTUzLC02Ljg2Mjc1YzAuOTY3NCwtMS44NjE3MyAyLjI1MzIsLTMuNTM5NzYgMy43OTkyMSwtNC45NTgxMWwwLjE2MTI0LC0wLjE3MTMybDQuNzQ2NDksLTQuNzM2NDFjLTAuNDk3NDcsLTEuMTI2MDIgLTAuOTA4NDgsLTIuMjg4MjggLTEuMjI5NDUsLTMuNDc2NzNjLTEuNTU2MSwtNS4xOTU4OSAtMC44NTM4MiwtMTAuODA2NzUgMS45MzQ4OCwtMTUuNDU4ODRjNC4zMDMwOCwtNy4wMzQwNyAxMy45NTczLC0xMi4yNjQyOCAzMC42NjU3MywtMTIuNDM1Nmw0LjIwMjMxLC00LjE5MjIzYzAuNDQzNjgsLTAuNjA4MzIgMC45ODExMSwtMS4xNDIzNSAxLjU5MjI0LC0xLjU4MjE2YzAuMTk4NTcsLTAuMTg3MzEgMC40MjY3LC0wLjM0MDUzIDAuNjc1MTksLTAuNDUzNDhjMy43Mzg3NCwtMi4yNTczNSA5LjI3MTI3LC0xLjM2MDQ2IDE1LjY0MDIzLDEuOTI0OHpNODkuMDQ3NDMsNTkuNzY4NjljLTIuNDY3MiwtMC41NTk2NiAtNC44Njg3NCwtMS4zNzcwNyAtNy4xNjUwOCwtMi40Mzg3NWMtNy4wNTQyMywtMy4xODQ0OCAtMTUuMjI3MDYsLTguODc4MjUgLTIyLjc2NSwtMTYuNDA2MTJjLTYuNjY3MjcsLTYuNTA2NzYgLTEyLjA3ODUxLC0xNC4xODYzNiAtMTUuOTYyNzEsLTIyLjY1NDE1Yy0xLjA5ODI2LC0yLjQ3ODQ1IC0xLjkwNjQ2LC01LjA3NTUxIC0yLjQwODUyLC03LjczOTQ5bC0yNi40MTg5OCwyNi4zMDIyYzEzLjg4MzU3LDE1LjAzNzU5IDU1LjM3MjM3LDIwLjM1NTk0IDY4LjI0MTMsMTQuODQzNTZjMS41Mjk5NywtMC42MjYxMiAzLjI3OTE1LDAuMDkyNzYgMy45MjY2OSwxLjYxMzhjMC42NDc1NCwxLjUyMTAzIC0wLjA0NjcxLDMuMjgwMTQgLTEuNTU4NDksMy45NDg5N2MtMTUuMzI3ODMsNi41NjA0MyAtNjIuNTU4NzYsLTEuNTI1NzYgLTc0Ljg2NjM0LC0xNi4wODMwOWwtMy42OTg0MywzLjczODc0Yy0xLjAxNzksMC45MTQwNSAtMS44ODIxNywxLjk4NTg3IC0yLjU1OTY4LDMuMTc0NGMtMC41MTUzNiwxLjMyNzM3IC0wLjczNTQ1LDIuNzUxMDUgLTAuNjQ0OTYsNC4xNzIwN2MwLjAwNzcsMS4xMTY0MiAwLjExOTA0LDIuMjI5NzIgMC4zMzI1NiwzLjMyNTU2YzAuMjM5MTMsMS4yODAyMyAwLjU3NjAzLDIuNTQwMjQgMS4wMDc3NCwzLjc2ODk3YzIuNjMwMjIsNy40ODc1NiA4LjA2MTk3LDE1LjM4ODI5IDE0LjgyMzk2LDIyLjA1OTU4YzYuNzYxOTksNi42NzEyOSAxNC41MjE2MywxMi4wMjI0MiAyMS45Nzg5NiwxNC40OTE0YzEuNTY2MjEsMC41NCAzLjE4OTIzLDAuODk4NDEgNC44MzcxOSwxLjA2ODIxdjBjMS40MTQ4NiwwLjEzOTQgMi44NDI3NywwLjA1NzgxIDQuMjMyNTMsLTAuMjQxODZjMS43MDkzMywtMC4yOTg5NCAzLjI0ODUxLC0xLjIxNzQxIDQuMzIzMjMsLTIuNTc5ODNjMC4wNjA0NiwtMC4wNzA1NCAwLjY0NDk2LC0wLjY0NDk2IDAuNjM0ODgsLTAuNjU1MDR6IiBmaWxsPSIjMDAwMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZS1vcGFjaXR5PSIwLjEyOTQxIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMTUiLz48cGF0aCBkPSJNMzEuNzc3MTgsMTAuNTUwMzNjLTEwLjI5OTE3LDEuMTA4NTIgLTE2LjM0NTY1LDQuNjY1ODcgLTE5LjE0NzE5LDkuMTgwNTdjLTEuODkyMiwzLjI0Nzk3IC0yLjM0MTU2LDcuMTM4NzIgLTEuMjM5NTMsMTAuNzMyNWwwLjA5MDcsMC4zNDI2NGwyMC4yNjU3OSwtMjAuMjU1NzF6TTc4LjI4NDY5LDIxLjc1NjQ4Yy03LjM0NjQ3LC03LjM1NjU1IC0xNC43NzM1NywtMTMuMTAwNyAtMjAuODkwNTksLTE2LjI5NTI3Yy00LjM0MzM5LC0yLjIzNzIgLTcuNzI5NDIsLTMuMTM0MDkgLTkuMzkyMiwtMi4xODY4MWwtMC44MjYzNiwwLjgyNjM2Yy0xLjIyOTQ1LDIuMDE1NSAtMC41MTM5Niw2LjMwODQ5IDEuNzkzNzksMTEuNjY5N2MzLjU2MzM1LDcuNzMyMTkgOC41MjAxNSwxNC43NDIwOCAxNC42MjI0LDIwLjY3ODk3YzYuOTkzNzcsNi45OTM3NyAxNC41MDE0OCwxMi4yMzQwNCAyMC44OTA1OSwxNS4xMTYyYzQuNTQ0OTQsMi4wMTU1IDguMjczNiwyLjkyMjQ3IDEwLjQzMDE4LDIuMzM3OTdsMS45MjQ4LC0xLjkzNDg4YzAuNDUzNDgsLTEuOTI0OCAtMC41MDM4OCwtNS4xNzk4MiAtMi41MDkyOSwtOS4yMDA3M2MtMy4xMTM5NCwtNi4yNTgxMSAtOC43ODc1NSwtMTMuNzY1ODIgLTE2LjA0MzMzLC0yMS4wMTE1MnpNNjAuMjg2MzMsLTAuMTUxOTRjNi43MTE1OSwzLjQ1NjU3IDE0LjY5Mjk1LDkuNjQ0MTQgMjIuNDgyODMsMTcuNDM0MDJjNy43ODk4OSw3Ljc4OTg5IDEzLjg1NjUyLDE1LjgyMTYzIDE3LjIyMjM5LDIyLjcyNDY5YzMuNzc5MDUsNy41NjgxOCA0LjM4MzcsMTQuMDI3ODQgMC42OTUzNCwxNy43MTYxOWMtMC42MDU4LDAuNTc3NDkgLTEuMjk1NTIsMS4wNTk5NiAtMi4wNDU3MiwxLjQzMWwtMzkuNDIzMDYsMzkuNDAyOWMtMi4zNjgyLDIuMzg4MzYgLTMuMzU1OCwzLjM5NjExIC03LjQzNzE3LDQuMzEzMTVjLTIuMDMxMzIsMC40NDAzNyAtNC4xMTg3LDAuNTYyNzYgLTYuMTg3NTcsMC4zNjI3OGMtMi4xNDE4MywtMC4yMDc4IC00LjI1MjE1LC0wLjY2NDM3IC02LjI4ODM0LC0xLjM2MDQ2Yy04LjM5NDUzLC0yLjgyMTY5IC0xNy4xMzE2OSwtOC43MjcwOSAtMjQuNDA3NjIsLTE1Ljk4Mjg2Yy03LjI3NTkzLC03LjI1NTc3IC0xMy4zNzI4LC0xNi4wNTM0MSAtMTYuMzI1NSwtMjQuNDY4MWMtMC41NTA0NiwtMS41MjU1MiAtMC45Nzg0MywtMy4wOTI0OCAtMS4yNzk4NCwtNC42ODYwMmMtMC4yODYwMywtMS40Nzc3NSAtMC40MzExMiwtMi45NzkzIC0wLjQzMzM0LC00LjQ4NDQ3Yy0wLjExODE3LC0yLjM1MjM0IDAuMzA1OTIsLTQuNzAwMzcgMS4yMzk1MywtNi44NjI3NWMwLjk2NzQsLTEuODYxNzMgMi4yNTMyLC0zLjUzOTc2IDMuNzk5MjEsLTQuOTU4MTFsMC4xNjEyNCwtMC4xNzEzMmw0Ljc0NjQ5LC00LjczNjQxYy0wLjQ5NzQ3LC0xLjEyNjAyIC0wLjkwODQ4LC0yLjI4ODI4IC0xLjIyOTQ1LC0zLjQ3NjczYy0xLjU1NjEsLTUuMTk1ODkgLTAuODUzODIsLTEwLjgwNjc1IDEuOTM0ODgsLTE1LjQ1ODg0YzQuMzAzMDgsLTcuMDM0MDcgMTMuOTU3MywtMTIuMjY0MjggMzAuNjY1NzMsLTEyLjQzNTZsNC4yMDIzMSwtNC4xOTIyM2MwLjQ0MzY4LC0wLjYwODMyIDAuOTgxMTEsLTEuMTQyMzUgMS41OTIyNCwtMS41ODIxNmMwLjE5ODU3LC0wLjE4NzMxIDAuNDI2NywtMC4zNDA1MyAwLjY3NTE5LC0wLjQ1MzQ4YzMuNzM4NzQsLTIuMjU3MzUgOS4yNzEyNywtMS4zNjA0NiAxNS42NDAyMywxLjkyNDh6TTg5LjA0NzQzLDU5Ljc2ODY5Yy0yLjQ2NzIsLTAuNTU5NjYgLTQuODY4NzQsLTEuMzc3MDcgLTcuMTY1MDgsLTIuNDM4NzVjLTcuMDU0MjMsLTMuMTg0NDggLTE1LjIyNzA2LC04Ljg3ODI1IC0yMi43NjUsLTE2LjQwNjEyYy02LjY2NzI3LC02LjUwNjc2IC0xMi4wNzg1MSwtMTQuMTg2MzYgLTE1Ljk2MjcxLC0yMi42NTQxNWMtMS4wOTgyNiwtMi40Nzg0NSAtMS45MDY0NiwtNS4wNzU1MSAtMi40MDg1MiwtNy43Mzk0OWwtMjYuNDE4OTgsMjYuMzAyMmMxMy44ODM1NywxNS4wMzc1OSA1NS4zNzIzNywyMC4zNTU5NCA2OC4yNDEzLDE0Ljg0MzU2YzEuNTI5OTcsLTAuNjI2MTIgMy4yNzkxNSwwLjA5Mjc2IDMuOTI2NjksMS42MTM4YzAuNjQ3NTQsMS41MjEwMyAtMC4wNDY3MSwzLjI4MDE0IC0xLjU1ODQ5LDMuOTQ4OTdjLTE1LjMyNzgzLDYuNTYwNDMgLTYyLjU1ODc2LC0xLjUyNTc2IC03NC44NjYzNCwtMTYuMDgzMDlsLTMuNjk4NDMsMy43Mzg3NGMtMS4wMTc5LDAuOTE0MDUgLTEuODgyMTcsMS45ODU4NyAtMi41NTk2OCwzLjE3NDRjLTAuNTE1MzYsMS4zMjczNyAtMC43MzU0NSwyLjc1MTA1IC0wLjY0NDk2LDQuMTcyMDdjMC4wMDc3LDEuMTE2NDIgMC4xMTkwNCwyLjIyOTcyIDAuMzMyNTYsMy4zMjU1NmMwLjIzOTEzLDEuMjgwMjMgMC41NzYwMywyLjU0MDI0IDEuMDA3NzQsMy43Njg5N2MyLjYzMDIyLDcuNDg3NTYgOC4wNjE5NywxNS4zODgyOSAxNC44MjM5NiwyMi4wNTk1OGM2Ljc2MTk5LDYuNjcxMjkgMTQuNTIxNjMsMTIuMDIyNDIgMjEuOTc4OTYsMTQuNDkxNGMxLjU2NjIxLDAuNTQgMy4xODkyMywwLjg5ODQxIDQuODM3MTksMS4wNjgyMXYwYzEuNDE0ODYsMC4xMzk0IDIuODQyNzcsMC4wNTc4MSA0LjIzMjUzLC0wLjI0MTg2YzEuNzA5MzMsLTAuMjk4OTQgMy4yNDg1MSwtMS4yMTc0MSA0LjMyMzIzLC0yLjU3OTgzYzAuMDYwNDYsLTAuMDcwNTQgMC42NDQ5NiwtMC42NDQ5NiAwLjYzNDg4LC0wLjY1NTA0eiIgZmlsbD0iIzAwMDAwMCIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMTAiLz48L2c+PC9nPjwvc3ZnPjwhLS1yb3RhdGlvbkNlbnRlcjo1OC45MjgwOTo1Ny42NTE5Mzk5OTk5OTk5OTYtLT4=';
- const GradientIcon = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxMTYuNDE5NjQiIGhlaWdodD0iMTE4LjM0OTk0IiB2aWV3Qm94PSIwLDAsMTE2LjQxOTY0LDExOC4zNDk5NCI+PGRlZnM+PGxpbmVhckdyYWRpZW50IHgxPSIzLjM2ODMzIiB5MT0iNzMuMjEzOCIgeDI9IjgzLjM4NjAzIiB5Mj0iNzMuMjEzOCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGlkPSJjb2xvci0xIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNmNTQyNDIiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiM0Mjk3ZjUiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg4LjkyODA5LDcuNjUxOTQpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIHN0eWxlPSJtaXgtYmxlbmQtbW9kZTogbm9ybWFsIj48cGF0aCBkPSJNMy42NTYwMyw0OS40MjYwM2MyLjQyOTg2LDAuNDg1MDUgMTQuNDkxNDcsMjQuMTExIDMxLjU1NDU1LDIxLjI2NDVjMTUuNTE1NjMsLTIuNTg4MzYgOS4wMTA4NywxNC4yMjIyNyA0OC4xNzU0NSwtNS40NjQ0OWwtMjguMjksMjguMjZjMCwwIC0wLjU3LDAuNTggLTAuNjMsMC42NWMtMS4wNjY0NiwxLjM1MTk0IC0yLjU5MzgxLDIuMjYzMzYgLTQuMjksMi41NmMtMS4zNzkwOCwwLjI5NzM2IC0yLjc5NjAyLDAuMzc4MzMgLTQuMiwwLjI0djBjLTEuNjM1MjksLTAuMTY4NDkgLTMuMjQ1ODMsLTAuNTI0MTUgLTQuOCwtMS4wNmMtNy40LC0yLjQ4IC0xNS4xNywtNy44IC0yMS43OSwtMTQuMzdjLTYuNjIsLTYuNTcgLTEyLjA3LC0xNC40NiAtMTQuNjgsLTIxLjg5Yy0wLjQyODQsLTEuMjE5MjkgLTAuNzYyNzEsLTIuNDY5NjIgLTEsLTMuNzRjLTAuMjExODgsLTEuMDg3NDIgLTAuMzIyMzYsLTIuMTkyMTYgLTAuMzMsLTMuM2MtMC4wMzY2MiwtMS4wNTc1MiAwLjA1NzQyLC0yLjExNTUyIDAuMjgsLTMuMTV6IiBmaWxsPSJ1cmwoI2NvbG9yLTEpIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIi8+PHBhdGggZD0iTTMxLjc3NzE4LDEwLjU1MDMzYy0xMC4yOTkxNywxLjEwODUyIC0xNi4zNDU2NSw0LjY2NTg3IC0xOS4xNDcxOSw5LjE4MDU3Yy0xLjg5MjIsMy4yNDc5NyAtMi4zNDE1Niw3LjEzODcyIC0xLjIzOTUzLDEwLjczMjVsMC4wOTA3LDAuMzQyNjRsMjAuMjY1NzksLTIwLjI1NTcxek03OC4yODQ2OSwyMS43NTY0OGMtNy4zNDY0NywtNy4zNTY1NSAtMTQuNzczNTcsLTEzLjEwMDcgLTIwLjg5MDU5LC0xNi4yOTUyN2MtNC4zNDMzOSwtMi4yMzcyIC03LjcyOTQyLC0zLjEzNDA5IC05LjM5MjIsLTIuMTg2ODFsLTAuODI2MzYsMC44MjYzNmMtMS4yMjk0NSwyLjAxNTUgLTAuNTEzOTYsNi4zMDg0OSAxLjc5Mzc5LDExLjY2OTdjMy41NjMzNSw3LjczMjE5IDguNTIwMTUsMTQuNzQyMDggMTQuNjIyNCwyMC42Nzg5N2M2Ljk5Mzc3LDYuOTkzNzcgMTQuNTAxNDgsMTIuMjM0MDQgMjAuODkwNTksMTUuMTE2MmM0LjU0NDk0LDIuMDE1NSA4LjI3MzYsMi45MjI0NyAxMC40MzAxOCwyLjMzNzk3bDEuOTI0OCwtMS45MzQ4OGMwLjQ1MzQ4LC0xLjkyNDggLTAuNTAzODgsLTUuMTc5ODIgLTIuNTA5MjksLTkuMjAwNzNjLTMuMTEzOTQsLTYuMjU4MTEgLTguNzg3NTUsLTEzLjc2NTgyIC0xNi4wNDMzMywtMjEuMDExNTJ6TTYwLjI4NjMzLC0wLjE1MTk0YzYuNzExNTksMy40NTY1NyAxNC42OTI5NSw5LjY0NDE0IDIyLjQ4MjgzLDE3LjQzNDAyYzcuNzg5ODksNy43ODk4OSAxMy44NTY1MiwxNS44MjE2MyAxNy4yMjIzOSwyMi43MjQ2OWMzLjc3OTA1LDcuNTY4MTggNC4zODM3LDE0LjAyNzg0IDAuNjk1MzQsMTcuNzE2MTljLTAuNjA1OCwwLjU3NzQ5IC0xLjI5NTUyLDEuMDU5OTYgLTIuMDQ1NzIsMS40MzFsLTM5LjQyMzA2LDM5LjQwMjljLTIuMzY4MiwyLjM4ODM2IC0zLjM1NTgsMy4zOTYxMSAtNy40MzcxNyw0LjMxMzE1Yy0yLjAzMTMyLDAuNDQwMzcgLTQuMTE4NywwLjU2Mjc2IC02LjE4NzU3LDAuMzYyNzhjLTIuMTQxODMsLTAuMjA3OCAtNC4yNTIxNSwtMC42NjQzNyAtNi4yODgzNCwtMS4zNjA0NmMtOC4zOTQ1MywtMi44MjE2OSAtMTcuMTMxNjksLTguNzI3MDkgLTI0LjQwNzYyLC0xNS45ODI4NmMtNy4yNzU5MywtNy4yNTU3NyAtMTMuMzcyOCwtMTYuMDUzNDEgLTE2LjMyNTUsLTI0LjQ2ODFjLTAuNTUwNDYsLTEuNTI1NTIgLTAuOTc4NDMsLTMuMDkyNDggLTEuMjc5ODQsLTQuNjg2MDJjLTAuMjg2MDMsLTEuNDc3NzUgLTAuNDMxMTIsLTIuOTc5MyAtMC40MzMzNCwtNC40ODQ0N2MtMC4xMTgxNywtMi4zNTIzNCAwLjMwNTkyLC00LjcwMDM3IDEuMjM5NTMsLTYuODYyNzVjMC45Njc0LC0xLjg2MTczIDIuMjUzMiwtMy41Mzk3NiAzLjc5OTIxLC00Ljk1ODExbDAuMTYxMjQsLTAuMTcxMzJsNC43NDY0OSwtNC43MzY0MWMtMC40OTc0NywtMS4xMjYwMiAtMC45MDg0OCwtMi4yODgyOCAtMS4yMjk0NSwtMy40NzY3M2MtMS41NTYxLC01LjE5NTg5IC0wLjg1MzgyLC0xMC44MDY3NSAxLjkzNDg4LC0xNS40NTg4NGM0LjMwMzA4LC03LjAzNDA3IDEzLjk1NzMsLTEyLjI2NDI4IDMwLjY2NTczLC0xMi40MzU2bDQuMjAyMzEsLTQuMTkyMjNjMC40NDM2OCwtMC42MDgzMiAwLjk4MTExLC0xLjE0MjM1IDEuNTkyMjQsLTEuNTgyMTZjMC4xOTg1NywtMC4xODczMSAwLjQyNjcsLTAuMzQwNTMgMC42NzUxOSwtMC40NTM0OGMzLjczODc0LC0yLjI1NzM1IDkuMjcxMjcsLTEuMzYwNDYgMTUuNjQwMjMsMS45MjQ4ek04OS4wNDc0Myw1OS43Njg2OWMtMi40NjcyLC0wLjU1OTY2IC00Ljg2ODc0LC0xLjM3NzA3IC03LjE2NTA4LC0yLjQzODc1Yy03LjA1NDIzLC0zLjE4NDQ4IC0xNS4yMjcwNiwtOC44NzgyNSAtMjIuNzY1LC0xNi40MDYxMmMtNi42NjcyNywtNi41MDY3NiAtMTIuMDc4NTEsLTE0LjE4NjM2IC0xNS45NjI3MSwtMjIuNjU0MTVjLTEuMDk4MjYsLTIuNDc4NDUgLTEuOTA2NDYsLTUuMDc1NTEgLTIuNDA4NTIsLTcuNzM5NDlsLTI2LjQxODk4LDI2LjMwMjJjMTMuODgzNTcsMTUuMDM3NTkgNTUuMzcyMzcsMjAuMzU1OTQgNjguMjQxMywxNC44NDM1NmMxLjUyOTk3LC0wLjYyNjEyIDMuMjc5MTUsMC4wOTI3NiAzLjkyNjY5LDEuNjEzOGMwLjY0NzU0LDEuNTIxMDMgLTAuMDQ2NzEsMy4yODAxNCAtMS41NTg0OSwzLjk0ODk3Yy0xNS4zMjc4Myw2LjU2MDQzIC02Mi41NTg3NiwtMS41MjU3NiAtNzQuODY2MzQsLTE2LjA4MzA5bC0zLjY5ODQzLDMuNzM4NzRjLTEuMDE3OSwwLjkxNDA1IC0xLjg4MjE3LDEuOTg1ODcgLTIuNTU5NjgsMy4xNzQ0Yy0wLjUxNTM2LDEuMzI3MzcgLTAuNzM1NDUsMi43NTEwNSAtMC42NDQ5Niw0LjE3MjA3YzAuMDA3NywxLjExNjQyIDAuMTE5MDQsMi4yMjk3MiAwLjMzMjU2LDMuMzI1NTZjMC4yMzkxMywxLjI4MDIzIDAuNTc2MDMsMi41NDAyNCAxLjAwNzc0LDMuNzY4OTdjMi42MzAyMiw3LjQ4NzU2IDguMDYxOTcsMTUuMzg4MjkgMTQuODIzOTYsMjIuMDU5NThjNi43NjE5OSw2LjY3MTI5IDE0LjUyMTYzLDEyLjAyMjQyIDIxLjk3ODk2LDE0LjQ5MTRjMS41NjYyMSwwLjU0IDMuMTg5MjMsMC44OTg0MSA0LjgzNzE5LDEuMDY4MjF2MGMxLjQxNDg2LDAuMTM5NCAyLjg0Mjc3LDAuMDU3ODEgNC4yMzI1MywtMC4yNDE4NmMxLjcwOTMzLC0wLjI5ODk0IDMuMjQ4NTEsLTEuMjE3NDEgNC4zMjMyMywtMi41Nzk4M2MwLjA2MDQ2LC0wLjA3MDU0IDAuNjQ0OTYsLTAuNjQ0OTYgMC42MzQ4OCwtMC42NTUwNHoiIGZpbGw9IiMwMDAwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlLW9wYWNpdHk9IjAuMTI5NDEiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxNSIvPjxwYXRoIGQ9Ik0zMS43NzcxOCwxMC41NTAzM2MtMTAuMjk5MTcsMS4xMDg1MiAtMTYuMzQ1NjUsNC42NjU4NyAtMTkuMTQ3MTksOS4xODA1N2MtMS44OTIyLDMuMjQ3OTcgLTIuMzQxNTYsNy4xMzg3MiAtMS4yMzk1MywxMC43MzI1bDAuMDkwNywwLjM0MjY0bDIwLjI2NTc5LC0yMC4yNTU3MXpNNzguMjg0NjksMjEuNzU2NDhjLTcuMzQ2NDcsLTcuMzU2NTUgLTE0Ljc3MzU3LC0xMy4xMDA3IC0yMC44OTA1OSwtMTYuMjk1MjdjLTQuMzQzMzksLTIuMjM3MiAtNy43Mjk0MiwtMy4xMzQwOSAtOS4zOTIyLC0yLjE4NjgxbC0wLjgyNjM2LDAuODI2MzZjLTEuMjI5NDUsMi4wMTU1IC0wLjUxMzk2LDYuMzA4NDkgMS43OTM3OSwxMS42Njk3YzMuNTYzMzUsNy43MzIxOSA4LjUyMDE1LDE0Ljc0MjA4IDE0LjYyMjQsMjAuNjc4OTdjNi45OTM3Nyw2Ljk5Mzc3IDE0LjUwMTQ4LDEyLjIzNDA0IDIwLjg5MDU5LDE1LjExNjJjNC41NDQ5NCwyLjAxNTUgOC4yNzM2LDIuOTIyNDcgMTAuNDMwMTgsMi4zMzc5N2wxLjkyNDgsLTEuOTM0ODhjMC40NTM0OCwtMS45MjQ4IC0wLjUwMzg4LC01LjE3OTgyIC0yLjUwOTI5LC05LjIwMDczYy0zLjExMzk0LC02LjI1ODExIC04Ljc4NzU1LC0xMy43NjU4MiAtMTYuMDQzMzMsLTIxLjAxMTUyek02MC4yODYzMywtMC4xNTE5NGM2LjcxMTU5LDMuNDU2NTcgMTQuNjkyOTUsOS42NDQxNCAyMi40ODI4MywxNy40MzQwMmM3Ljc4OTg5LDcuNzg5ODkgMTMuODU2NTIsMTUuODIxNjMgMTcuMjIyMzksMjIuNzI0NjljMy43NzkwNSw3LjU2ODE4IDQuMzgzNywxNC4wMjc4NCAwLjY5NTM0LDE3LjcxNjE5Yy0wLjYwNTgsMC41Nzc0OSAtMS4yOTU1MiwxLjA1OTk2IC0yLjA0NTcyLDEuNDMxbC0zOS40MjMwNiwzOS40MDI5Yy0yLjM2ODIsMi4zODgzNiAtMy4zNTU4LDMuMzk2MTEgLTcuNDM3MTcsNC4zMTMxNWMtMi4wMzEzMiwwLjQ0MDM3IC00LjExODcsMC41NjI3NiAtNi4xODc1NywwLjM2Mjc4Yy0yLjE0MTgzLC0wLjIwNzggLTQuMjUyMTUsLTAuNjY0MzcgLTYuMjg4MzQsLTEuMzYwNDZjLTguMzk0NTMsLTIuODIxNjkgLTE3LjEzMTY5LC04LjcyNzA5IC0yNC40MDc2MiwtMTUuOTgyODZjLTcuMjc1OTMsLTcuMjU1NzcgLTEzLjM3MjgsLTE2LjA1MzQxIC0xNi4zMjU1LC0yNC40NjgxYy0wLjU1MDQ2LC0xLjUyNTUyIC0wLjk3ODQzLC0zLjA5MjQ4IC0xLjI3OTg0LC00LjY4NjAyYy0wLjI4NjAzLC0xLjQ3Nzc1IC0wLjQzMTEyLC0yLjk3OTMgLTAuNDMzMzQsLTQuNDg0NDdjLTAuMTE4MTcsLTIuMzUyMzQgMC4zMDU5MiwtNC43MDAzNyAxLjIzOTUzLC02Ljg2Mjc1YzAuOTY3NCwtMS44NjE3MyAyLjI1MzIsLTMuNTM5NzYgMy43OTkyMSwtNC45NTgxMWwwLjE2MTI0LC0wLjE3MTMybDQuNzQ2NDksLTQuNzM2NDFjLTAuNDk3NDcsLTEuMTI2MDIgLTAuOTA4NDgsLTIuMjg4MjggLTEuMjI5NDUsLTMuNDc2NzNjLTEuNTU2MSwtNS4xOTU4OSAtMC44NTM4MiwtMTAuODA2NzUgMS45MzQ4OCwtMTUuNDU4ODRjNC4zMDMwOCwtNy4wMzQwNyAxMy45NTczLC0xMi4yNjQyOCAzMC42NjU3MywtMTIuNDM1Nmw0LjIwMjMxLC00LjE5MjIzYzAuNDQzNjgsLTAuNjA4MzIgMC45ODExMSwtMS4xNDIzNSAxLjU5MjI0LC0xLjU4MjE2YzAuMTk4NTcsLTAuMTg3MzEgMC40MjY3LC0wLjM0MDUzIDAuNjc1MTksLTAuNDUzNDhjMy43Mzg3NCwtMi4yNTczNSA5LjI3MTI3LC0xLjM2MDQ2IDE1LjY0MDIzLDEuOTI0OHpNODkuMDQ3NDMsNTkuNzY4NjljLTIuNDY3MiwtMC41NTk2NiAtNC44Njg3NCwtMS4zNzcwNyAtNy4xNjUwOCwtMi40Mzg3NWMtNy4wNTQyMywtMy4xODQ0OCAtMTUuMjI3MDYsLTguODc4MjUgLTIyLjc2NSwtMTYuNDA2MTJjLTYuNjY3MjcsLTYuNTA2NzYgLTEyLjA3ODUxLC0xNC4xODYzNiAtMTUuOTYyNzEsLTIyLjY1NDE1Yy0xLjA5ODI2LC0yLjQ3ODQ1IC0xLjkwNjQ2LC01LjA3NTUxIC0yLjQwODUyLC03LjczOTQ5bC0yNi40MTg5OCwyNi4zMDIyYzEzLjg4MzU3LDE1LjAzNzU5IDU1LjM3MjM3LDIwLjM1NTk0IDY4LjI0MTMsMTQuODQzNTZjMS41Mjk5NywtMC42MjYxMiAzLjI3OTE1LDAuMDkyNzYgMy45MjY2OSwxLjYxMzhjMC42NDc1NCwxLjUyMTAzIC0wLjA0NjcxLDMuMjgwMTQgLTEuNTU4NDksMy45NDg5N2MtMTUuMzI3ODMsNi41NjA0MyAtNjIuNTU4NzYsLTEuNTI1NzYgLTc0Ljg2NjM0LC0xNi4wODMwOWwtMy42OTg0MywzLjczODc0Yy0xLjAxNzksMC45MTQwNSAtMS44ODIxNywxLjk4NTg3IC0yLjU1OTY4LDMuMTc0NGMtMC41MTUzNiwxLjMyNzM3IC0wLjczNTQ1LDIuNzUxMDUgLTAuNjQ0OTYsNC4xNzIwN2MwLjAwNzcsMS4xMTY0MiAwLjExOTA0LDIuMjI5NzIgMC4zMzI1NiwzLjMyNTU2YzAuMjM5MTMsMS4yODAyMyAwLjU3NjAzLDIuNTQwMjQgMS4wMDc3NCwzLjc2ODk3YzIuNjMwMjIsNy40ODc1NiA4LjA2MTk3LDE1LjM4ODI5IDE0LjgyMzk2LDIyLjA1OTU4YzYuNzYxOTksNi42NzEyOSAxNC41MjE2MywxMi4wMjI0MiAyMS45Nzg5NiwxNC40OTE0YzEuNTY2MjEsMC41NCAzLjE4OTIzLDAuODk4NDEgNC44MzcxOSwxLjA2ODIxdjBjMS40MTQ4NiwwLjEzOTQgMi44NDI3NywwLjA1NzgxIDQuMjMyNTMsLTAuMjQxODZjMS43MDkzMywtMC4yOTg5NCAzLjI0ODUxLC0xLjIxNzQxIDQuMzIzMjMsLTIuNTc5ODNjMC4wNjA0NiwtMC4wNzA1NCAwLjY0NDk2LC0wLjY0NDk2IDAuNjM0ODgsLTAuNjU1MDR6IiBmaWxsPSIjMDAwMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxMCIvPjwvZz48L2c+PC9zdmc+PCEtLXJvdGF0aW9uQ2VudGVyOjU4LjkyODA4ODk1NTAxODk4OjU3LjY1MTk0MTAyMjI0MTgzLS0+';
- const PictureIcon = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxMzguNjA3MDUiIGhlaWdodD0iMTE0LjIxMjI3IiB2aWV3Qm94PSIwLDAsMTM4LjYwNzA1LDExNC4yMTIyNyI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTkuMzAzNTIsNy4xMDYxNCkiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbD0iIzAwMDAwMCIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2Utb3BhY2l0eT0iMC4xMjk0MSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjcuNSIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWRhc2hhcnJheT0iIiBzdHJva2UtZGFzaG9mZnNldD0iMCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0xMTUuNTUzNTIsNS43OTA3NnY4OC40MTg0OGMwLDUuMDUxMzcgLTQuMDk1NTIsOS4xNDY5IC05LjE0NjksOS4xNDY5aC0xMTIuODEzMjVjLTUuMDUxMzcsMCAtOS4xNDY5LC00LjA5NTUyIC05LjE0NjksLTkuMTQ2OXYtODguNDE4NDhjMCwtNS4wNTEzNyA0LjA5NTUyLC05LjE0NjkgOS4xNDY5LC05LjE0NjloMTEyLjgxMzI1YzUuMDUxMzcsMCA5LjE0NjksNC4wOTU1MiA5LjE0NjksOS4xNDY5ek0yLjc0MDI3LDU3LjU4OTY0bDE1LjE2MDk4LC0xMS4xNDc3OGMzLjgxODgzLC0yLjgxMjY3IDkuMTYwNjIsLTIuMjM0MTMgMTIuMjkzNDMsMS4zMzU0NWwxMC4yMjM5NCwxMS42NDg1N2wyNC41NDU3LC0xMi4yNzA1NmMzLjAxNjE5LC0xLjUxMTUyIDYuNjI0NjQsLTEuMjM3MTIgOS4zODAxNCwwLjcxNTc0bDIyLjkxNTI2LDE2LjIzMzQ2di00OS4xNjY4NmgtOTQuNTE5NDZ6TTM4LjU3NzgxLDg1LjA2MjM0bC0xNi41OTcwNCwtMTguOTA4OTJsLTE5LjE1NTg5LDE0LjA4MTY1Yy0wLjAyNzQ0LDAuMDIwNTggLTAuMDU3MTcsMC4wMjk3MyAtMC4wODQ2MSwwLjA1MDMxdjQuNzc2OTd6TTk1LjE5NzEsODUuMDYyMzRsLTI2Ljk1NTkxLC0xOS4wOTE4NmwtMTUuMzQ2MjEsNy42NzE5NmwxMC4wMjI3MSwxMS40MTk5ek0zMC4xODA5NiwyOS44MDEzNmMwLC01LjQ3MjEzIC00LjQzNjI1LC05LjkwODM4IC05LjkwODM4LC05LjkwODM4Yy01LjQ3MjEzLDAgLTkuOTA4MzgsNC40MzYyNSAtOS45MDgzOCw5LjkwODM4YzAsNS40NzIxMyA0LjQzNjI1LDkuOTA4MzggOS45MDgzOCw5LjkwODM4YzUuNDcyMTMsMCA5LjkwODM4LC00LjQzNjI1IDkuOTA4MzgsLTkuOTA4Mzh6Ii8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6NjkuMzAzNTIzOTQ2MzExODo1Ny4xMDYxMzY4MjA3MDk5Ni0tPg==';
- const ResetIcon = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIzNDkuMzIzMjQiIGhlaWdodD0iMzI3LjM0OTEyIiB2aWV3Qm94PSIwLDAsMzQ5LjMyMzI0LDMyNy4zNDkxMiI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTY1LjMzODM4LC0xNi4zMjU0NCkiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIHN0eWxlPSJtaXgtYmxlbmQtbW9kZTogbm9ybWFsIj48cGF0aCBkPSJNMTM2LjQxNTc0LDk3LjkwMTk0Yy0zOC4yNDgwNiw4Mi43NzIyNSAtMzAuNTkzMDYsMjMyLjc5NzI4IDEzOS43MzA3NywyMjAuMDA0MTZjMTQyLjQzOSwtMTAuNjk4NjggMTQzLjc0MTk3LC0xOTIuNTE4ODQgNDMuMzc5NjksLTI0OS41MzgxIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iNTAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjxwYXRoIGQ9Ik05NS42NDc3MSw0OS43ODA2N2w5OS44NTIyMiwtMC40MDI2MyIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PHBhdGggZD0iTTkxLjQ1MzI4LDY1LjUxNDIxbDEwOS42ODM0NywtMjQuMTg4NzciIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSI1MCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PHBhdGggZD0iTTIwNC4wMjE1Miw0My4yOTQzNWwtOS44MjI0LDEwNi4wMTExMyIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjUwIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48cGF0aCBkPSJNOTAuMzM4MzgsNjcuNDI2MDhsMTAzLjUyMzY2LDgyLjIwODIiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSI1MCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PHBhdGggZD0iTTE1My45MTUxOCwxMDMuODQ5ODV2LTMyLjA5NTloMTQuNjk0NTF2MzIuMDk1OXoiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSI1MCIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiLz48cGF0aCBkPSJNMTk1LjQ5OTkyLDQ5LjM3ODA0IiBzdHJva2U9IiNmZjAwMDAiIHN0cm9rZS13aWR0aD0iNTAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjwvZz48L2c+PC9zdmc+PCEtLXJvdGF0aW9uQ2VudGVyOjE3NC42NjE2MjoxNjMuNjc0NTU5OTk5OTk5OTktLT4=';
-
- const stylesheet = document.createElement('style');
- stylesheet.className = 'shovelcss-style';
- // end of for higher precedence than other sheets
- document.body.appendChild(stylesheet);
-
- const applyCSS = () => {
- let css = '';
-
- // We assume all values are sanitized when they are set, so then we can just use them as-is here.
-
- if (monitorText) {
- css += `${monitorRoot}, ${monitorListFooter}, ${monitorListHeader}, ${monitorRowIndex} { color: ${monitorText}; }`;
- }
- if (monitorBackgroundColor) {
- css += `${monitorRoot}, ${monitorRowsInner} { background: ${monitorBackgroundColor}; }`;
- }
- if (monitorBorder) {
- css += `${monitorRoot} { border-color: ${monitorBorder}; }`;
- }
- if (monitorBackgroundRoundness >= 0) {
- css += `${monitorRoot} { border-radius: ${monitorBackgroundRoundness}px; }`;
- }
- if (monitorBackgroundBorderWidth >= 0) {
- css += `${monitorRoot} { border-width: ${monitorBackgroundBorderWidth}px; }`;
- }
- if (variableValueBackground) {
- css += `${monitorValue}, ${monitorValueLarge} { background: ${variableValueBackground} !important; }`;
- }
- if (variableValueTextColor) {
- css += `${monitorValue}, ${monitorValueLarge} { color: ${variableValueTextColor}; }`;
- }
- if (variableValueRoundness >= 0) {
- css += `${monitorValue} { border-radius: ${variableValueRoundness}px; }`;
- }
- if (listHeaderBackground) {
- css += `${monitorListHeader} { background: ${listHeaderBackground}; }`;
- }
- if (listFooterBackground) {
- css += `${monitorListFooter} { background: ${listHeaderBackground}; }`;
- }
- if (listValueBackground) {
- css += `${monitorRowValueOuter} { background: ${listValueBackground} !important; }`;
- }
- if (listValueText) {
- css += `${monitorRowValueOuter} { color: ${listValueText}; }`;
- }
- if (listValueRoundness >= 0) {
- css += `${monitorRowValueOuter} { border-radius: ${listValueRoundness}px; }`;
- }
- if (allowScrolling) {
- css += `${monitorRowsScroller} { overflow: ${allowScrolling} !important; }`;
- }
- if (askBackground) {
- css += `${askBoxBG} { background: ${askBackground} !important; border: none !important; }`;
- }
- if (askBackgroundRoundness >= 0) {
- css += `${askBoxBG} { border-radius: ${askBackgroundRoundness}px !important; }`;
- }
- if (askBackgroundBorderWidth >= 0) {
- css += `${askBoxBG} { border-width: ${askBackgroundBorderWidth}px !important; }`;
- }
- if (askButtonBackground) {
- css += `${askBoxButton} { background-color: ${askButtonBackground}; }`;
- }
- if (askButtonRoundness >= 0) {
- css += `${askBoxButton} { border-radius: ${askButtonRoundness}px !important; }`;
- }
- if (askInputBackground) {
- css += `${askBoxInner} { background: ${askInputBackground} !important; }`;
- css += `${askBoxInner} { border: none !important; }`;
- }
- if (askInputText) {
- css += `${askBoxInner} { color: ${askInputText} !important; }`;
- }
- if (askInputRoundness >= 0) {
- css += `${askBoxInner} { border-radius: ${askInputRoundness}px !important; }`;
- }
- if (askInputBorderWidth >= 0) {
- css += `${askBoxInner} { border-width: ${askInputBorderWidth}px !important; }`;
- }
- if (askButtonImage) {
- css += `${askBoxButton} { background-image: url("${encodeURI(askButtonImage)}") !important; background-repeat: no-repeat; background-size: contain; }`;
- css += `${askBoxIcon} { visibility: hidden; }`;
- }
- if (askInputBorder) {
- css += `${askBoxBorderMain}, ${askBoxBorderOuter} { border-color: ${askInputBorder} !important; }`;
- css += `${askBoxBorderOuter} { box-shadow: none !important; }`;
- }
+(function (Scratch) {
+ "use strict";
+
+ // Styles
+ let monitorText = "";
+ let monitorBorder = "";
+ let monitorBackgroundColor = "";
+ let variableValueBackground = "";
+ let variableValueTextColor = "";
+ let listFooterBackground = "";
+ let listHeaderBackground = "";
+ let listValueText = "";
+ let listValueBackground = "";
+ let variableValueRoundness = -1;
+ let listValueRoundness = -1;
+ let monitorBackgroundRoundness = -1;
+ let monitorBackgroundBorderWidth = -1;
+ let allowScrolling = "";
+ let askBackground = "";
+ let askBackgroundRoundness = -1;
+ let askBackgroundBorderWidth = -1;
+ let askButtonBackground = "";
+ let askButtonRoundness = -1;
+ let askInputBackground = "";
+ let askInputRoundness = -1;
+ let askInputBorderWidth = -1;
+ let askBoxIcon = "";
+ let askInputText = "";
+ let askButtonImage = "";
+ let askInputBorder = "";
+
+ // CSS selectors
+ let monitorRoot;
+ let monitorValue;
+ let monitorListHeader;
+ let monitorListFooter;
+ let monitorRowValueOuter;
+ let monitorRowsInner;
+ let monitorRowsScroller;
+ let monitorRowIndex;
+ let monitorValueLarge;
+ let askBoxBG;
+ let askBoxButton;
+ let askBoxInner;
+ let askBoxBorderMain;
+ let askBoxBorderOuter;
+ if (typeof scaffolding !== "undefined") {
+ monitorRoot = ".sc-monitor-root";
+ monitorValue = ".sc-monitor-value";
+ monitorListHeader = ".sc-monitor-list-label";
+ monitorListFooter = ".sc-monitor-list-footer";
+ monitorRowValueOuter = ".sc-monitor-row-value-outer";
+ monitorRowsInner = ".sc-monitor-rows-inner";
+ monitorRowsScroller = monitorRowsInner;
+ monitorRowIndex = ".sc-monitor-row-index";
+ monitorValueLarge = ".sc-monitor-large-value";
+ askBoxBG = ".sc-question-inner";
+ askBoxButton = ".sc-question-submit-button";
+ askBoxInner = ".sc-question-input";
+ askBoxBorderMain = ".sc-question-input:hover";
+ askBoxBorderOuter = ".sc-question-input:focus";
+ } else {
+ monitorRoot = 'div[class^="monitor_monitor-container_"]';
+ monitorValue = 'div[class^="monitor_value_"]';
+ monitorListHeader = 'div[class^="monitor_list-header_"]';
+ monitorListFooter = 'div[class^="monitor_list-footer_"]';
+ monitorRowValueOuter = 'div[class^="monitor_list-value_"]';
+ monitorRowsInner = 'div[class^="monitor_list-body_"]';
+ monitorRowsScroller =
+ 'div[class^="monitor_list-body_"] > .ReactVirtualized__List';
+ monitorRowIndex = 'div[class^="monitor_list-index_"]';
+ monitorValueLarge = 'div[class^="monitor_large-value_"]';
+ askBoxBG = 'div[class^="question_question-container_"]';
+ askBoxButton = 'button[class^="question_question-submit-button_"]';
+ askBoxInner =
+ '[class^="question_question-container_"] input[class^="input_input-form_"]';
+ askBoxIcon = 'img[class^="question_question-submit-button-icon_"]';
+ askBoxBorderMain =
+ '[class^="question_question-input_"] input:focus, [class^="question_question-input_"] input:hover';
+ askBoxBorderOuter = '[class^="question_question-input_"] > input:focus';
+ }
+
+ const ColorIcon =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxMTYuNDE5NjQiIGhlaWdodD0iMTE4LjM0OTk0IiB2aWV3Qm94PSIwLDAsMTE2LjQxOTY0LDExOC4zNDk5NCI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOC45MjgwOSw3LjY1MTk0KSI+PGcgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aXNQYWludGluZ0xheWVyJnF1b3Q7OnRydWV9IiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTMxLjc3NzE4LDEwLjU1MDMzYy0xMC4yOTkxNywxLjEwODUyIC0xNi4zNDU2NSw0LjY2NTg3IC0xOS4xNDcxOSw5LjE4MDU3Yy0xLjg5MjIsMy4yNDc5NyAtMi4zNDE1Niw3LjEzODcyIC0xLjIzOTUzLDEwLjczMjVsMC4wOTA3LDAuMzQyNjRsMjAuMjY1NzksLTIwLjI1NTcxek03OC4yODQ2OSwyMS43NTY0OGMtNy4zNDY0NywtNy4zNTY1NSAtMTQuNzczNTcsLTEzLjEwMDcgLTIwLjg5MDU5LC0xNi4yOTUyN2MtNC4zNDMzOSwtMi4yMzcyIC03LjcyOTQyLC0zLjEzNDA5IC05LjM5MjIsLTIuMTg2ODFsLTAuODI2MzYsMC44MjYzNmMtMS4yMjk0NSwyLjAxNTUgLTAuNTEzOTYsNi4zMDg0OSAxLjc5Mzc5LDExLjY2OTdjMy41NjMzNSw3LjczMjE5IDguNTIwMTUsMTQuNzQyMDggMTQuNjIyNCwyMC42Nzg5N2M2Ljk5Mzc3LDYuOTkzNzcgMTQuNTAxNDgsMTIuMjM0MDQgMjAuODkwNTksMTUuMTE2MmM0LjU0NDk0LDIuMDE1NSA4LjI3MzYsMi45MjI0NyAxMC40MzAxOCwyLjMzNzk3bDEuOTI0OCwtMS45MzQ4OGMwLjQ1MzQ4LC0xLjkyNDggLTAuNTAzODgsLTUuMTc5ODIgLTIuNTA5MjksLTkuMjAwNzNjLTMuMTEzOTQsLTYuMjU4MTEgLTguNzg3NTUsLTEzLjc2NTgyIC0xNi4wNDMzMywtMjEuMDExNTJ6TTYwLjI4NjMzLC0wLjE1MTk0YzYuNzExNTksMy40NTY1NyAxNC42OTI5NSw5LjY0NDE0IDIyLjQ4MjgzLDE3LjQzNDAyYzcuNzg5ODksNy43ODk4OSAxMy44NTY1MiwxNS44MjE2MyAxNy4yMjIzOSwyMi43MjQ2OWMzLjc3OTA1LDcuNTY4MTggNC4zODM3LDE0LjAyNzg0IDAuNjk1MzQsMTcuNzE2MTljLTAuNjA1OCwwLjU3NzQ5IC0xLjI5NTUyLDEuMDU5OTYgLTIuMDQ1NzIsMS40MzFsLTM5LjQyMzA2LDM5LjQwMjljLTIuMzY4MiwyLjM4ODM2IC0zLjM1NTgsMy4zOTYxMSAtNy40MzcxNyw0LjMxMzE1Yy0yLjAzMTMyLDAuNDQwMzcgLTQuMTE4NywwLjU2Mjc2IC02LjE4NzU3LDAuMzYyNzhjLTIuMTQxODMsLTAuMjA3OCAtNC4yNTIxNSwtMC42NjQzNyAtNi4yODgzNCwtMS4zNjA0NmMtOC4zOTQ1MywtMi44MjE2OSAtMTcuMTMxNjksLTguNzI3MDkgLTI0LjQwNzYyLC0xNS45ODI4NmMtNy4yNzU5MywtNy4yNTU3NyAtMTMuMzcyOCwtMTYuMDUzNDEgLTE2LjMyNTUsLTI0LjQ2ODFjLTAuNTUwNDYsLTEuNTI1NTIgLTAuOTc4NDMsLTMuMDkyNDggLTEuMjc5ODQsLTQuNjg2MDJjLTAuMjg2MDMsLTEuNDc3NzUgLTAuNDMxMTIsLTIuOTc5MyAtMC40MzMzNCwtNC40ODQ0N2MtMC4xMTgxNywtMi4zNTIzNCAwLjMwNTkyLC00LjcwMDM3IDEuMjM5NTMsLTYuODYyNzVjMC45Njc0LC0xLjg2MTczIDIuMjUzMiwtMy41Mzk3NiAzLjc5OTIxLC00Ljk1ODExbDAuMTYxMjQsLTAuMTcxMzJsNC43NDY0OSwtNC43MzY0MWMtMC40OTc0NywtMS4xMjYwMiAtMC45MDg0OCwtMi4yODgyOCAtMS4yMjk0NSwtMy40NzY3M2MtMS41NTYxLC01LjE5NTg5IC0wLjg1MzgyLC0xMC44MDY3NSAxLjkzNDg4LC0xNS40NTg4NGM0LjMwMzA4LC03LjAzNDA3IDEzLjk1NzMsLTEyLjI2NDI4IDMwLjY2NTczLC0xMi40MzU2bDQuMjAyMzEsLTQuMTkyMjNjMC40NDM2OCwtMC42MDgzMiAwLjk4MTExLC0xLjE0MjM1IDEuNTkyMjQsLTEuNTgyMTZjMC4xOTg1NywtMC4xODczMSAwLjQyNjcsLTAuMzQwNTMgMC42NzUxOSwtMC40NTM0OGMzLjczODc0LC0yLjI1NzM1IDkuMjcxMjcsLTEuMzYwNDYgMTUuNjQwMjMsMS45MjQ4ek04OS4wNDc0Myw1OS43Njg2OWMtMi40NjcyLC0wLjU1OTY2IC00Ljg2ODc0LC0xLjM3NzA3IC03LjE2NTA4LC0yLjQzODc1Yy03LjA1NDIzLC0zLjE4NDQ4IC0xNS4yMjcwNiwtOC44NzgyNSAtMjIuNzY1LC0xNi40MDYxMmMtNi42NjcyNywtNi41MDY3NiAtMTIuMDc4NTEsLTE0LjE4NjM2IC0xNS45NjI3MSwtMjIuNjU0MTVjLTEuMDk4MjYsLTIuNDc4NDUgLTEuOTA2NDYsLTUuMDc1NTEgLTIuNDA4NTIsLTcuNzM5NDlsLTI2LjQxODk4LDI2LjMwMjJjMTMuODgzNTcsMTUuMDM3NTkgNTUuMzcyMzcsMjAuMzU1OTQgNjguMjQxMywxNC44NDM1NmMxLjUyOTk3LC0wLjYyNjEyIDMuMjc5MTUsMC4wOTI3NiAzLjkyNjY5LDEuNjEzOGMwLjY0NzU0LDEuNTIxMDMgLTAuMDQ2NzEsMy4yODAxNCAtMS41NTg0OSwzLjk0ODk3Yy0xNS4zMjc4Myw2LjU2MDQzIC02Mi41NTg3NiwtMS41MjU3NiAtNzQuODY2MzQsLTE2LjA4MzA5bC0zLjY5ODQzLDMuNzM4NzRjLTEuMDE3OSwwLjkxNDA1IC0xLjg4MjE3LDEuOTg1ODcgLTIuNTU5NjgsMy4xNzQ0Yy0wLjUxNTM2LDEuMzI3MzcgLTAuNzM1NDUsMi43NTEwNSAtMC42NDQ5Niw0LjE3MjA3YzAuMDA3NywxLjExNjQyIDAuMTE5MDQsMi4yMjk3MiAwLjMzMjU2LDMuMzI1NTZjMC4yMzkxMywxLjI4MDIzIDAuNTc2MDMsMi41NDAyNCAxLjAwNzc0LDMuNzY4OTdjMi42MzAyMiw3LjQ4NzU2IDguMDYxOTcsMTUuMzg4MjkgMTQuODIzOTYsMjIuMDU5NThjNi43NjE5OSw2LjY3MTI5IDE0LjUyMTYzLDEyLjAyMjQyIDIxLjk3ODk2LDE0LjQ5MTRjMS41NjYyMSwwLjU0IDMuMTg5MjMsMC44OTg0MSA0LjgzNzE5LDEuMDY4MjF2MGMxLjQxNDg2LDAuMTM5NCAyLjg0Mjc3LDAuMDU3ODEgNC4yMzI1MywtMC4yNDE4NmMxLjcwOTMzLC0wLjI5ODk0IDMuMjQ4NTEsLTEuMjE3NDEgNC4zMjMyMywtMi41Nzk4M2MwLjA2MDQ2LC0wLjA3MDU0IDAuNjQ0OTYsLTAuNjQ0OTYgMC42MzQ4OCwtMC42NTUwNHoiIGZpbGw9IiMwMDAwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlLW9wYWNpdHk9IjAuMTI5NDEiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxNSIvPjxwYXRoIGQ9Ik0zLjI5NzAyLDQ5LjQyMTZjMi40NDg2OSwwLjQ4ODgxIDE0LjYwMzczLDI0LjI5Nzc5IDMxLjc5OSwyMS40MjkyM2MxNS42MzU4MywtMi42MDg0MSA5LjA4MDY3LDE0LjMzMjQ1IDQ4LjU0ODY1LC01LjUwNjgybC0yOC41MDkxNiwyOC40Nzg5M2MwLDAgLTAuNTc0NDIsMC41ODQ1IC0wLjYzNDg4LDAuNjU1MDRjLTEuMDc0NzIsMS4zNjI0MSAtMi42MTM5LDIuMjgwOSAtNC4zMjMyMywyLjU3OTgzYy0xLjM4OTc2LDAuMjk5NjYgLTIuODE3NjgsMC4zODEyNiAtNC4yMzI1MywwLjI0MTg2djBjLTEuNjQ3OTYsLTAuMTY5NzkgLTMuMjcwOTcsLTAuNTI4MjEgLTQuODM3MTksLTEuMDY4MjFjLTcuNDU3MzMsLTIuNDk5MjEgLTE1LjI4NzUyLC03Ljg2MDQzIC0yMS45NTg4LC0xNC40ODEzMmMtNi42NzEyOSwtNi42MjA4OSAtMTIuMTYzNSwtMTQuNTcyMDIgLTE0Ljc5MzcyLC0yMi4wNTk1OGMtMC40MzE3MiwtMS4yMjg3NCAtMC43Njg2MiwtMi40ODg3NSAtMS4wMDc3NCwtMy43Njg5N2MtMC4yMTM1MiwtMS4wOTU4NSAtMC4zMjQ4NiwtMi4yMDkxNCAtMC4zMzI1NiwtMy4zMjU1NmMtMC4wMzY5LC0xLjA2NTcxIDAuMDU3ODcsLTIuMTMxOSAwLjI4MjE2LC0zLjE3NDR6IiBmaWxsPSIjZjU0MjQyIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIi8+PHBhdGggZD0iTTMxLjc3NzE4LDEwLjU1MDMzYy0xMC4yOTkxNywxLjEwODUyIC0xNi4zNDU2NSw0LjY2NTg3IC0xOS4xNDcxOSw5LjE4MDU3Yy0xLjg5MjIsMy4yNDc5NyAtMi4zNDE1Niw3LjEzODcyIC0xLjIzOTUzLDEwLjczMjVsMC4wOTA3LDAuMzQyNjRsMjAuMjY1NzksLTIwLjI1NTcxek03OC4yODQ2OSwyMS43NTY0OGMtNy4zNDY0NywtNy4zNTY1NSAtMTQuNzczNTcsLTEzLjEwMDcgLTIwLjg5MDU5LC0xNi4yOTUyN2MtNC4zNDMzOSwtMi4yMzcyIC03LjcyOTQyLC0zLjEzNDA5IC05LjM5MjIsLTIuMTg2ODFsLTAuODI2MzYsMC44MjYzNmMtMS4yMjk0NSwyLjAxNTUgLTAuNTEzOTYsNi4zMDg0OSAxLjc5Mzc5LDExLjY2OTdjMy41NjMzNSw3LjczMjE5IDguNTIwMTUsMTQuNzQyMDggMTQuNjIyNCwyMC42Nzg5N2M2Ljk5Mzc3LDYuOTkzNzcgMTQuNTAxNDgsMTIuMjM0MDQgMjAuODkwNTksMTUuMTE2MmM0LjU0NDk0LDIuMDE1NSA4LjI3MzYsMi45MjI0NyAxMC40MzAxOCwyLjMzNzk3bDEuOTI0OCwtMS45MzQ4OGMwLjQ1MzQ4LC0xLjkyNDggLTAuNTAzODgsLTUuMTc5ODIgLTIuNTA5MjksLTkuMjAwNzNjLTMuMTEzOTQsLTYuMjU4MTEgLTguNzg3NTUsLTEzLjc2NTgyIC0xNi4wNDMzMywtMjEuMDExNTJ6TTYwLjI4NjMzLC0wLjE1MTk0YzYuNzExNTksMy40NTY1NyAxNC42OTI5NSw5LjY0NDE0IDIyLjQ4MjgzLDE3LjQzNDAyYzcuNzg5ODksNy43ODk4OSAxMy44NTY1MiwxNS44MjE2MyAxNy4yMjIzOSwyMi43MjQ2OWMzLjc3OTA1LDcuNTY4MTggNC4zODM3LDE0LjAyNzg0IDAuNjk1MzQsMTcuNzE2MTljLTAuNjA1OCwwLjU3NzQ5IC0xLjI5NTUyLDEuMDU5OTYgLTIuMDQ1NzIsMS40MzFsLTM5LjQyMzA2LDM5LjQwMjljLTIuMzY4MiwyLjM4ODM2IC0zLjM1NTgsMy4zOTYxMSAtNy40MzcxNyw0LjMxMzE1Yy0yLjAzMTMyLDAuNDQwMzcgLTQuMTE4NywwLjU2Mjc2IC02LjE4NzU3LDAuMzYyNzhjLTIuMTQxODMsLTAuMjA3OCAtNC4yNTIxNSwtMC42NjQzNyAtNi4yODgzNCwtMS4zNjA0NmMtOC4zOTQ1MywtMi44MjE2OSAtMTcuMTMxNjksLTguNzI3MDkgLTI0LjQwNzYyLC0xNS45ODI4NmMtNy4yNzU5MywtNy4yNTU3NyAtMTMuMzcyOCwtMTYuMDUzNDEgLTE2LjMyNTUsLTI0LjQ2ODFjLTAuNTUwNDYsLTEuNTI1NTIgLTAuOTc4NDMsLTMuMDkyNDggLTEuMjc5ODQsLTQuNjg2MDJjLTAuMjg2MDMsLTEuNDc3NzUgLTAuNDMxMTIsLTIuOTc5MyAtMC40MzMzNCwtNC40ODQ0N2MtMC4xMTgxNywtMi4zNTIzNCAwLjMwNTkyLC00LjcwMDM3IDEuMjM5NTMsLTYuODYyNzVjMC45Njc0LC0xLjg2MTczIDIuMjUzMiwtMy41Mzk3NiAzLjc5OTIxLC00Ljk1ODExbDAuMTYxMjQsLTAuMTcxMzJsNC43NDY0OSwtNC43MzY0MWMtMC40OTc0NywtMS4xMjYwMiAtMC45MDg0OCwtMi4yODgyOCAtMS4yMjk0NSwtMy40NzY3M2MtMS41NTYxLC01LjE5NTg5IC0wLjg1MzgyLC0xMC44MDY3NSAxLjkzNDg4LC0xNS40NTg4NGM0LjMwMzA4LC03LjAzNDA3IDEzLjk1NzMsLTEyLjI2NDI4IDMwLjY2NTczLC0xMi40MzU2bDQuMjAyMzEsLTQuMTkyMjNjMC40NDM2OCwtMC42MDgzMiAwLjk4MTExLC0xLjE0MjM1IDEuNTkyMjQsLTEuNTgyMTZjMC4xOTg1NywtMC4xODczMSAwLjQyNjcsLTAuMzQwNTMgMC42NzUxOSwtMC40NTM0OGMzLjczODc0LC0yLjI1NzM1IDkuMjcxMjcsLTEuMzYwNDYgMTUuNjQwMjMsMS45MjQ4ek04OS4wNDc0Myw1OS43Njg2OWMtMi40NjcyLC0wLjU1OTY2IC00Ljg2ODc0LC0xLjM3NzA3IC03LjE2NTA4LC0yLjQzODc1Yy03LjA1NDIzLC0zLjE4NDQ4IC0xNS4yMjcwNiwtOC44NzgyNSAtMjIuNzY1LC0xNi40MDYxMmMtNi42NjcyNywtNi41MDY3NiAtMTIuMDc4NTEsLTE0LjE4NjM2IC0xNS45NjI3MSwtMjIuNjU0MTVjLTEuMDk4MjYsLTIuNDc4NDUgLTEuOTA2NDYsLTUuMDc1NTEgLTIuNDA4NTIsLTcuNzM5NDlsLTI2LjQxODk4LDI2LjMwMjJjMTMuODgzNTcsMTUuMDM3NTkgNTUuMzcyMzcsMjAuMzU1OTQgNjguMjQxMywxNC44NDM1NmMxLjUyOTk3LC0wLjYyNjEyIDMuMjc5MTUsMC4wOTI3NiAzLjkyNjY5LDEuNjEzOGMwLjY0NzU0LDEuNTIxMDMgLTAuMDQ2NzEsMy4yODAxNCAtMS41NTg0OSwzLjk0ODk3Yy0xNS4zMjc4Myw2LjU2MDQzIC02Mi41NTg3NiwtMS41MjU3NiAtNzQuODY2MzQsLTE2LjA4MzA5bC0zLjY5ODQzLDMuNzM4NzRjLTEuMDE3OSwwLjkxNDA1IC0xLjg4MjE3LDEuOTg1ODcgLTIuNTU5NjgsMy4xNzQ0Yy0wLjUxNTM2LDEuMzI3MzcgLTAuNzM1NDUsMi43NTEwNSAtMC42NDQ5Niw0LjE3MjA3YzAuMDA3NywxLjExNjQyIDAuMTE5MDQsMi4yMjk3MiAwLjMzMjU2LDMuMzI1NTZjMC4yMzkxMywxLjI4MDIzIDAuNTc2MDMsMi41NDAyNCAxLjAwNzc0LDMuNzY4OTdjMi42MzAyMiw3LjQ4NzU2IDguMDYxOTcsMTUuMzg4MjkgMTQuODIzOTYsMjIuMDU5NThjNi43NjE5OSw2LjY3MTI5IDE0LjUyMTYzLDEyLjAyMjQyIDIxLjk3ODk2LDE0LjQ5MTRjMS41NjYyMSwwLjU0IDMuMTg5MjMsMC44OTg0MSA0LjgzNzE5LDEuMDY4MjF2MGMxLjQxNDg2LDAuMTM5NCAyLjg0Mjc3LDAuMDU3ODEgNC4yMzI1MywtMC4yNDE4NmMxLjcwOTMzLC0wLjI5ODk0IDMuMjQ4NTEsLTEuMjE3NDEgNC4zMjMyMywtMi41Nzk4M2MwLjA2MDQ2LC0wLjA3MDU0IDAuNjQ0OTYsLTAuNjQ0OTYgMC42MzQ4OCwtMC42NTUwNHoiIGZpbGw9IiMwMDAwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEwIi8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6NTguOTI4MDk6NTcuNjUxOTM5OTk5OTk5OTk2LS0+";
+ const BorderIcon =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxMjkuMDA4NDIiIGhlaWdodD0iMTI5LjAwODQzIiB2aWV3Qm94PSIwLDAsMTI5LjAwODQyLDEyOS4wMDg0MyI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTQuNTA0MjEsMTQuNTA0MjIpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWRhc2hhcnJheT0iIiBzdHJva2UtZGFzaG9mZnNldD0iMCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0tMi4wMDQyMSw4Ny40NTM4OGMwLC0yMi44MTE2IDAsLTY1LjYwODczIDAsLTc3LjM5MjMxYzAsLTUuOTA0NzkgNy4wNTUxNiwtMTIuMDY1NzkgMTMuMTA3OTYsLTEyLjA2NTc5YzExLjkwNTQsMCA1NC4yNjQ2NSwwIDc2LjcwNzQyLDBjOC41Mzc2NywwIDE0LjE5MzA0LDcuMTQ4NzcgMTQuMTkzMDQsMTcuNTQ0ODljMCwyMy44MjMyNSAwLDY0LjY5OTA0IDAsNzUuNjgwMDljMCw1LjM2NDQ4IC02LjcyOTAyLDEwLjc4MzQ1IC0xNi41OTAxNSwxMC43ODM0NWMtMjIuODg5MjQsMCAtNjIuNjUzNTUsMCAtNzQuMzEwMzEsMGMtNi4zMzM1NSwwIC0xMy4xMDc5NiwtNS44MDcyNCAtMTMuMTA3OTYsLTE0LjU1MDMzeiIgc3Ryb2tlLW9wYWNpdHk9IjAuMTI5NDEiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIyNSIvPjxwYXRoIGQ9Ik0tMi4wMDQyMSw4Ny40NTM4OGMwLC0yMi44MTE2IDAsLTY1LjYwODczIDAsLTc3LjM5MjMxYzAsLTUuOTA0NzkgNy4wNTUxNiwtMTIuMDY1NzkgMTMuMTA3OTYsLTEyLjA2NTc5YzExLjkwNTQsMCA1NC4yNjQ2NSwwIDc2LjcwNzQyLDBjOC41Mzc2NywwIDE0LjE5MzA0LDcuMTQ4NzcgMTQuMTkzMDQsMTcuNTQ0ODljMCwyMy44MjMyNSAwLDY0LjY5OTA0IDAsNzUuNjgwMDljMCw1LjM2NDQ4IC02LjcyOTAyLDEwLjc4MzQ1IC0xNi41OTAxNSwxMC43ODM0NWMtMjIuODg5MjQsMCAtNjIuNjUzNTUsMCAtNzQuMzEwMzEsMGMtNi4zMzM1NSwwIC0xMy4xMDc5NiwtNS44MDcyNCAtMTMuMTA3OTYsLTE0LjU1MDMzeiIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjIwIi8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6NjQuNTA0MjE6NjQuNTA0MjItLT4=";
+ const extensionIcon =
+ "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciICB2aWV3Qm94PSIwIDAgMjk2Mjk3IDMzMzMzMyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHNoYXBlLXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIiB0ZXh0LXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIiBpbWFnZS1yZW5kZXJpbmc9Im9wdGltaXplUXVhbGl0eSIgZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iaWQ0IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjU0MTI4LjciIHkxPSI3OTM1NS41IiB4Mj0iMjQwMzE4IiB5Mj0iNzkzNTUuNSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZThlN2U1Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjZmZmIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImlkNSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSI2MjAxOS4zIiB5MT0iMjAyODY4IiB4Mj0iMjMzNTE1IiB5Mj0iMjAyODY4Ij48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNlOGU3ZTUiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNmZmYiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iaWQ2IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjEwNDk2MyIgeTE9Ijk5NjE2LjkiIHgyPSIxMDQ5NjMiIHkyPSIxNzEwMjEiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2QxZDNkNCIvPjxzdG9wIG9mZnNldD0iLjM4OCIgc3RvcC1jb2xvcj0iI2QxZDNkNCIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI2QxZDNkNCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJpZDciIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4bGluazpocmVmPSIjaWQ2IiB4MT0iMTk0MTc5IiB5MT0iNjExODUuOCIgeDI9IjE5NDE3OSIgeTI9IjEzNTQwNyIvPjxtYXNrIGlkPSJpZDAiPjxsaW5lYXJHcmFkaWVudCBpZD0iaWQxIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjEwNDk2MyIgeTE9Ijk5NjE2LjkiIHgyPSIxMDQ5NjMiIHkyPSIxNzEwMjEiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1vcGFjaXR5PSIwIiBzdG9wLWNvbG9yPSIjZmZmIi8+PHN0b3Agb2Zmc2V0PSIuMzg4IiBzdG9wLWNvbG9yPSIjZmZmIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii44MzEiIHN0b3AtY29sb3I9IiNmZmYiLz48L2xpbmVhckdyYWRpZW50PjxwYXRoIGZpbGw9InVybCgjaWQxKSIgZD0iTTYxNzM3IDk5NDY3aDg2NDUzdjcxNzA0SDYxNzM3eiIvPjwvbWFzaz48bWFzayBpZD0iaWQyIj48bGluZWFyR3JhZGllbnQgaWQ9ImlkMyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIxOTQxNzkiIHkxPSI2MTE4NS44IiB4Mj0iMTk0MTc5IiB5Mj0iMTM1NDA3Ij48c3RvcCBvZmZzZXQ9IjAiIHN0b3Atb3BhY2l0eT0iMCIgc3RvcC1jb2xvcj0iI2ZmZiIvPjxzdG9wIG9mZnNldD0iLjM4OCIgc3RvcC1jb2xvcj0iI2ZmZiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1vcGFjaXR5PSIuODMxIiBzdG9wLWNvbG9yPSIjZmZmIi8+PC9saW5lYXJHcmFkaWVudD48cGF0aCBmaWxsPSJ1cmwoI2lkMykiIGQ9Ik0xNDc4OTAgNjEwMzZoOTI1Nzh2NzQ1MjFoLTkyNTc4eiIvPjwvbWFzaz48c3R5bGU+LmZpbDZ7ZmlsbDojMDAwO2ZpbGwtb3BhY2l0eTouMDUwOTh9PC9zdHlsZT48L2RlZnM+PGcgaWQ9IkxheWVyX3gwMDIwXzEiPjxnIGlkPSJfNTEzMDg1MzA0Ij48cGF0aCBmaWxsPSIjMjA2MmFmIiBkPSJNMjY4NTE3IDMwMDkyMmwtMTIwMzY5IDMyNDExLTEyMDM3MS0zMjQxMUwwIDBoMjk2Mjk3eiIvPjxwYXRoIGZpbGw9IiMzYzljZDciIGQ9Ik0xNDgxNDYgMjQzNzR2MjgzMTA5bDI3MyA3NCA5NzQwOS0yNjIyOSAyMjQ4NS0yNTY5NTR6Ii8+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTE0ODA0MCA5OTYxN2wtODYxNTMgMzU4ODAgMjg1NyAzNTUyNCA4MzI5Ni0zNTYxNCA4ODYwNC0zNzg4MyAzNjc0LTM2MzM5LTkyMjc4IDM4NDMyeiIvPjxwYXRoIG1hc2s9InVybCgjaWQwKSIgZmlsbD0idXJsKCNpZDYpIiBkPSJNNjE4ODcgMTM1NDk3bDI4NTcgMzU1MjQgODMyOTUtMzU2MTRWOTk2MTd6Ii8+PHBhdGggbWFzaz0idXJsKCNpZDIpIiBmaWxsPSJ1cmwoI2lkNykiIGQ9Ik0yNDAzMTggNjExODZsLTkyMjc4IDM4NDMxdjM1NzkwbDg4NjA0LTM3ODgzeiIvPjxwYXRoIGZpbGw9InVybCgjaWQ1KSIgZD0iTTYyMDE5IDEzNTQ5N2wyODU4IDM1NTI0IDEyNzgwNiA0MDctMjg1OSA0NzM2NS00MjA1NSAxMTg0MC00MDQyOC0xMDIwOC0yNDUwLTI5Mzk5SDY3MzI3bDQ5MDAgNTY3NTYgNzU5NTAgMjI0NTcgNzU1MzgtMjIwNTAgOTgwMC0xMTI2OTJ6Ii8+PHBhdGggY2xhc3M9ImZpbDYiIGQ9Ik0xNDgwNDAgMTM1NDk3SDYxODg4bDI4NTcgMzU1MjQgODMyOTUgMjY2di0zNTc5MHptMCA5NTAyMmwtNDA4IDExNC00MDQyMi0xMDIwOC0yNDUwLTI5Mzk5SDY3MTk3bDQ4OTkgNTY3NTYgNzU5NDQgMjI0NTd2LTM5NzIweiIvPjxwYXRoIGZpbGw9InVybCgjaWQ0KSIgZD0iTTU0MTI5IDYxMTg2aDE4NjE4OWwtMzY3NCAzNjMzOUg1ODYyMGwtNDQ5MS0zNjMzOXoiLz48cGF0aCBjbGFzcz0iZmlsNiIgZD0iTTE0ODA0MCA2MTE4Nkg1NDEyOWw0NDkxIDM2MzM5aDg5NDIweiIvPjwvZz48L2c+PC9zdmc+";
+ const miscIcon =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxMzUuMzc5IiBoZWlnaHQ9IjEzNS4zNzciIHZpZXdCb3g9IjAsMCwxMzUuMzc5LDEzNS4zNzciPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzIuMzEsLTgyLjMxMSkiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbD0iIzAwMDAwMCIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTM0OC45NjcsMTEyLjA4YzIuMzIxLDIuMzIxIDIuMzIxLDYuMTE4IDAsOC40MzlsLTcuMTAxLDcuMTAxYzEuOTU5LDMuNjU4IDMuNDU0LDcuNjAxIDQuNDA1LDExLjc1Mmg5LjE5OWMzLjI4MywwIDUuOTY5LDIuNjg2IDUuOTY5LDUuOTY4djEyLjQ3MWMwLDMuMjgzIC0yLjY4Niw1Ljk2OSAtNS45NjksNS45NjloLTEwLjAzOWMtMS4yMzEsNC4wNjMgLTIuOTkyLDcuODk2IC01LjIwNCwxMS40MThsNi41MTIsNi41MWMyLjMyMSwyLjMyMyAyLjMyMSw2LjEyIDAsOC40NGwtOC44MTgsOC44MTljLTIuMzIxLDIuMzIgLTYuMTE5LDIuMzIgLTguNDM5LDBsLTcuMTAyLC03LjEwMmMtMy42NTcsMS45NiAtNy42MDEsMy40NTYgLTExLjc1Myw0LjQwNnY5LjE5OWMwLDMuMjgyIC0yLjY4NSw1Ljk2OCAtNS45NjgsNS45NjhoLTEyLjQ3Yy0zLjI4MywwIC01Ljk2OSwtMi42ODYgLTUuOTY5LC01Ljk2OHYtMTAuMDM5Yy00LjA2MywtMS4yMzIgLTcuODk2LC0yLjk5MyAtMTEuNDE3LC01LjIwNWwtNi41MTEsNi41MTJjLTIuMzIzLDIuMzIxIC02LjEyLDIuMzIxIC04LjQ0MSwwbC04LjgxOCwtOC44MThjLTIuMzIxLC0yLjMyMSAtMi4zMjEsLTYuMTE4IDAsLTguNDM5bDcuMTAyLC03LjEwMmMtMS45NiwtMy42NTcgLTMuNDU2LC03LjYgLTQuNDA1LC0xMS43NTFoLTkuMjAyYy0zLjI4MiwwIC01Ljk2OCwtMi42ODUgLTUuOTY4LC01Ljk2OHYtMTIuNDcxYzAsLTMuMjgzIDIuNjg2LC01Ljk2OCA1Ljk2OCwtNS45NjhoMTAuMDM5YzEuMjMyLC00LjA2MyAyLjk5MywtNy44OTYgNS4yMDQsLTExLjQxOGwtNi41MTEsLTYuNTFjLTIuMzIxLC0yLjMyMiAtMi4zMjEsLTYuMTIgMCwtOC40NGw4LjgxOSwtOC44MTljMi4zMjEsLTIuMzIxIDYuMTE4LC0yLjMyMSA4LjQzOSwwbDcuMTAxLDcuMTAxYzMuNjU4LC0xLjk2IDcuNjAxLC0zLjQ1NiAxMS43NTMsLTQuNDA2di05LjE5OWMwLC0zLjI4MyAyLjY4NiwtNS45NjkgNS45NjgsLTUuOTY5aDEyLjQ3MWMzLjI4MiwwIDUuOTY4LDIuNjg2IDUuOTY4LDUuOTY5djEwLjAzNmM0LjA2NCwxLjIzMSA3Ljg5OCwyLjk5MiAxMS40MjIsNS4yMDRsNi41MDcsLTYuNTA5YzIuMzIzLC0yLjMyMSA2LjEyLC0yLjMyMSA4LjQ0MSwwek0zMjQuNTE5LDE1MGMwLDEzLjUzOCAtMTAuOTc5LDI0LjUxOSAtMjQuNTE5LDI0LjUxOWMtMTMuNTM5LDAgLTI0LjUxOSwtMTAuOTggLTI0LjUxOSwtMjQuNTE5YzAsLTEzLjUzOSAxMC45OCwtMjQuNTE5IDI0LjUxOSwtMjQuNTE5YzEzLjU0LDAgMjQuNTE5LDEwLjk4IDI0LjUxOSwyNC41MTl6IiBzdHJva2Utb3BhY2l0eT0iMC4xMjk0MSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjEyLjUiLz48cGF0aCBkPSJNMzQ4Ljk2NywxMTIuMDhjMi4zMjEsMi4zMjEgMi4zMjEsNi4xMTggMCw4LjQzOWwtNy4xMDEsNy4xMDFjMS45NTksMy42NTggMy40NTQsNy42MDEgNC40MDUsMTEuNzUyaDkuMTk5YzMuMjgzLDAgNS45NjksMi42ODYgNS45NjksNS45Njh2MTIuNDcxYzAsMy4yODMgLTIuNjg2LDUuOTY5IC01Ljk2OSw1Ljk2OWgtMTAuMDM5Yy0xLjIzMSw0LjA2MyAtMi45OTIsNy44OTYgLTUuMjA0LDExLjQxOGw2LjUxMiw2LjUxYzIuMzIxLDIuMzIzIDIuMzIxLDYuMTIgMCw4LjQ0bC04LjgxOCw4LjgxOWMtMi4zMjEsMi4zMiAtNi4xMTksMi4zMiAtOC40MzksMGwtNy4xMDIsLTcuMTAyYy0zLjY1NywxLjk2IC03LjYwMSwzLjQ1NiAtMTEuNzUzLDQuNDA2djkuMTk5YzAsMy4yODIgLTIuNjg1LDUuOTY4IC01Ljk2OCw1Ljk2OGgtMTIuNDdjLTMuMjgzLDAgLTUuOTY5LC0yLjY4NiAtNS45NjksLTUuOTY4di0xMC4wMzljLTQuMDYzLC0xLjIzMiAtNy44OTYsLTIuOTkzIC0xMS40MTcsLTUuMjA1bC02LjUxMSw2LjUxMmMtMi4zMjMsMi4zMjEgLTYuMTIsMi4zMjEgLTguNDQxLDBsLTguODE4LC04LjgxOGMtMi4zMjEsLTIuMzIxIC0yLjMyMSwtNi4xMTggMCwtOC40MzlsNy4xMDIsLTcuMTAyYy0xLjk2LC0zLjY1NyAtMy40NTYsLTcuNiAtNC40MDUsLTExLjc1MWgtOS4yMDJjLTMuMjgyLDAgLTUuOTY4LC0yLjY4NSAtNS45NjgsLTUuOTY4di0xMi40NzFjMCwtMy4yODMgMi42ODYsLTUuOTY4IDUuOTY4LC01Ljk2OGgxMC4wMzljMS4yMzIsLTQuMDYzIDIuOTkzLC03Ljg5NiA1LjIwNCwtMTEuNDE4bC02LjUxMSwtNi41MWMtMi4zMjEsLTIuMzIyIC0yLjMyMSwtNi4xMiAwLC04LjQ0bDguODE5LC04LjgxOWMyLjMyMSwtMi4zMjEgNi4xMTgsLTIuMzIxIDguNDM5LDBsNy4xMDEsNy4xMDFjMy42NTgsLTEuOTYgNy42MDEsLTMuNDU2IDExLjc1MywtNC40MDZ2LTkuMTk5YzAsLTMuMjgzIDIuNjg2LC01Ljk2OSA1Ljk2OCwtNS45NjloMTIuNDcxYzMuMjgyLDAgNS45NjgsMi42ODYgNS45NjgsNS45Njl2MTAuMDM2YzQuMDY0LDEuMjMxIDcuODk4LDIuOTkyIDExLjQyMiw1LjIwNGw2LjUwNywtNi41MDljMi4zMjMsLTIuMzIxIDYuMTIsLTIuMzIxIDguNDQxLDB6TTMyNC41MTksMTUwYzAsMTMuNTM4IC0xMC45NzksMjQuNTE5IC0yNC41MTksMjQuNTE5Yy0xMy41MzksMCAtMjQuNTE5LC0xMC45OCAtMjQuNTE5LC0yNC41MTljMCwtMTMuNTM5IDEwLjk4LC0yNC41MTkgMjQuNTE5LC0yNC41MTljMTMuNTQsMCAyNC41MTksMTAuOTggMjQuNTE5LDI0LjUxOXoiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSI3LjUiLz48L2c+PC9nPjwvc3ZnPjwhLS1yb3RhdGlvbkNlbnRlcjo2Ny42OTo2Ny42ODktLT4=";
+ const TransparentIcon =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxMTYuNDE5NjQiIGhlaWdodD0iMTE4LjM0OTk0IiB2aWV3Qm94PSIwLDAsMTE2LjQxOTY0LDExOC4zNDk5NCI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOC45MjgwOSw3LjY1MTk0KSI+PGcgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aXNQYWludGluZ0xheWVyJnF1b3Q7OnRydWV9IiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTQuNDk0OTYsNDYuMzAwNDRjMi40Mjk4NiwwLjQ4NTA1IDE0LjQ5MTQ3LDI0LjExMSAzMS41NTQ1NSwyMS4yNjQ1YzE1LjUxNTYzLC0yLjU4ODM2IDEuNjU1NjMsMjMuNDU1NDUgNDAuODIwMjEsMy43Njg2OWwtMjAuOTM0NzYsMTkuMDI2ODJjMCwwIC0wLjU3LDAuNTggLTAuNjMsMC42NWMtMS4wNjY0NiwxLjM1MTk0IC0yLjU5MzgxLDIuMjYzMzYgLTQuMjksMi41NmMtMS4zNzkwOCwwLjI5NzM2IC0yLjc5NjAyLDAuMzc4MzMgLTQuMiwwLjI0djBjLTEuNjM1MjksLTAuMTY4NDkgLTMuMjQ1ODMsLTAuNTI0MTUgLTQuOCwtMS4wNmMtNy40LC0yLjQ4IC0xNS4xNywtNy44IC0yMS43OSwtMTQuMzdjLTYuNjIsLTYuNTcgLTEyLjA3LC0xNC40NiAtMTQuNjgsLTIxLjg5Yy0wLjQyODQsLTEuMjE5MjkgLTAuNzYyNzEsLTIuNDY5NjIgLTEsLTMuNzRjLTAuMjExODgsLTEuMDg3NDIgLTAuMzIyMzYsLTIuMTkyMTYgLTAuMzMsLTMuM2MtMC4wMzY2MiwtMS4wNTc1MiAwLjA1NzQyLC0yLjExNTUyIDAuMjgsLTMuMTV6IiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxMCIvPjxwYXRoIGQ9Ik0zMS43NzcxOCwxMC41NTAzM2MtMTAuMjk5MTcsMS4xMDg1MiAtMTYuMzQ1NjUsNC42NjU4NyAtMTkuMTQ3MTksOS4xODA1N2MtMS44OTIyLDMuMjQ3OTcgLTIuMzQxNTYsNy4xMzg3MiAtMS4yMzk1MywxMC43MzI1bDAuMDkwNywwLjM0MjY0bDIwLjI2NTc5LC0yMC4yNTU3MXpNNzguMjg0NjksMjEuNzU2NDhjLTcuMzQ2NDcsLTcuMzU2NTUgLTE0Ljc3MzU3LC0xMy4xMDA3IC0yMC44OTA1OSwtMTYuMjk1MjdjLTQuMzQzMzksLTIuMjM3MiAtNy43Mjk0MiwtMy4xMzQwOSAtOS4zOTIyLC0yLjE4NjgxbC0wLjgyNjM2LDAuODI2MzZjLTEuMjI5NDUsMi4wMTU1IC0wLjUxMzk2LDYuMzA4NDkgMS43OTM3OSwxMS42Njk3YzMuNTYzMzUsNy43MzIxOSA4LjUyMDE1LDE0Ljc0MjA4IDE0LjYyMjQsMjAuNjc4OTdjNi45OTM3Nyw2Ljk5Mzc3IDE0LjUwMTQ4LDEyLjIzNDA0IDIwLjg5MDU5LDE1LjExNjJjNC41NDQ5NCwyLjAxNTUgOC4yNzM2LDIuOTIyNDcgMTAuNDMwMTgsMi4zMzc5N2wxLjkyNDgsLTEuOTM0ODhjMC40NTM0OCwtMS45MjQ4IC0wLjUwMzg4LC01LjE3OTgyIC0yLjUwOTI5LC05LjIwMDczYy0zLjExMzk0LC02LjI1ODExIC04Ljc4NzU1LC0xMy43NjU4MiAtMTYuMDQzMzMsLTIxLjAxMTUyek02MC4yODYzMywtMC4xNTE5NGM2LjcxMTU5LDMuNDU2NTcgMTQuNjkyOTUsOS42NDQxNCAyMi40ODI4MywxNy40MzQwMmM3Ljc4OTg5LDcuNzg5ODkgMTMuODU2NTIsMTUuODIxNjMgMTcuMjIyMzksMjIuNzI0NjljMy43NzkwNSw3LjU2ODE4IDQuMzgzNywxNC4wMjc4NCAwLjY5NTM0LDE3LjcxNjE5Yy0wLjYwNTgsMC41Nzc0OSAtMS4yOTU1MiwxLjA1OTk2IC0yLjA0NTcyLDEuNDMxbC0zOS40MjMwNiwzOS40MDI5Yy0yLjM2ODIsMi4zODgzNiAtMy4zNTU4LDMuMzk2MTEgLTcuNDM3MTcsNC4zMTMxNWMtMi4wMzEzMiwwLjQ0MDM3IC00LjExODcsMC41NjI3NiAtNi4xODc1NywwLjM2Mjc4Yy0yLjE0MTgzLC0wLjIwNzggLTQuMjUyMTUsLTAuNjY0MzcgLTYuMjg4MzQsLTEuMzYwNDZjLTguMzk0NTMsLTIuODIxNjkgLTE3LjEzMTY5LC04LjcyNzA5IC0yNC40MDc2MiwtMTUuOTgyODZjLTcuMjc1OTMsLTcuMjU1NzcgLTEzLjM3MjgsLTE2LjA1MzQxIC0xNi4zMjU1LC0yNC40NjgxYy0wLjU1MDQ2LC0xLjUyNTUyIC0wLjk3ODQzLC0zLjA5MjQ4IC0xLjI3OTg0LC00LjY4NjAyYy0wLjI4NjAzLC0xLjQ3Nzc1IC0wLjQzMTEyLC0yLjk3OTMgLTAuNDMzMzQsLTQuNDg0NDdjLTAuMTE4MTcsLTIuMzUyMzQgMC4zMDU5MiwtNC43MDAzNyAxLjIzOTUzLC02Ljg2Mjc1YzAuOTY3NCwtMS44NjE3MyAyLjI1MzIsLTMuNTM5NzYgMy43OTkyMSwtNC45NTgxMWwwLjE2MTI0LC0wLjE3MTMybDQuNzQ2NDksLTQuNzM2NDFjLTAuNDk3NDcsLTEuMTI2MDIgLTAuOTA4NDgsLTIuMjg4MjggLTEuMjI5NDUsLTMuNDc2NzNjLTEuNTU2MSwtNS4xOTU4OSAtMC44NTM4MiwtMTAuODA2NzUgMS45MzQ4OCwtMTUuNDU4ODRjNC4zMDMwOCwtNy4wMzQwNyAxMy45NTczLC0xMi4yNjQyOCAzMC42NjU3MywtMTIuNDM1Nmw0LjIwMjMxLC00LjE5MjIzYzAuNDQzNjgsLTAuNjA4MzIgMC45ODExMSwtMS4xNDIzNSAxLjU5MjI0LC0xLjU4MjE2YzAuMTk4NTcsLTAuMTg3MzEgMC40MjY3LC0wLjM0MDUzIDAuNjc1MTksLTAuNDUzNDhjMy43Mzg3NCwtMi4yNTczNSA5LjI3MTI3LC0xLjM2MDQ2IDE1LjY0MDIzLDEuOTI0OHpNODkuMDQ3NDMsNTkuNzY4NjljLTIuNDY3MiwtMC41NTk2NiAtNC44Njg3NCwtMS4zNzcwNyAtNy4xNjUwOCwtMi40Mzg3NWMtNy4wNTQyMywtMy4xODQ0OCAtMTUuMjI3MDYsLTguODc4MjUgLTIyLjc2NSwtMTYuNDA2MTJjLTYuNjY3MjcsLTYuNTA2NzYgLTEyLjA3ODUxLC0xNC4xODYzNiAtMTUuOTYyNzEsLTIyLjY1NDE1Yy0xLjA5ODI2LC0yLjQ3ODQ1IC0xLjkwNjQ2LC01LjA3NTUxIC0yLjQwODUyLC03LjczOTQ5bC0yNi40MTg5OCwyNi4zMDIyYzEzLjg4MzU3LDE1LjAzNzU5IDU1LjM3MjM3LDIwLjM1NTk0IDY4LjI0MTMsMTQuODQzNTZjMS41Mjk5NywtMC42MjYxMiAzLjI3OTE1LDAuMDkyNzYgMy45MjY2OSwxLjYxMzhjMC42NDc1NCwxLjUyMTAzIC0wLjA0NjcxLDMuMjgwMTQgLTEuNTU4NDksMy45NDg5N2MtMTUuMzI3ODMsNi41NjA0MyAtNjIuNTU4NzYsLTEuNTI1NzYgLTc0Ljg2NjM0LC0xNi4wODMwOWwtMy42OTg0MywzLjczODc0Yy0xLjAxNzksMC45MTQwNSAtMS44ODIxNywxLjk4NTg3IC0yLjU1OTY4LDMuMTc0NGMtMC41MTUzNiwxLjMyNzM3IC0wLjczNTQ1LDIuNzUxMDUgLTAuNjQ0OTYsNC4xNzIwN2MwLjAwNzcsMS4xMTY0MiAwLjExOTA0LDIuMjI5NzIgMC4zMzI1NiwzLjMyNTU2YzAuMjM5MTMsMS4yODAyMyAwLjU3NjAzLDIuNTQwMjQgMS4wMDc3NCwzLjc2ODk3YzIuNjMwMjIsNy40ODc1NiA4LjA2MTk3LDE1LjM4ODI5IDE0LjgyMzk2LDIyLjA1OTU4YzYuNzYxOTksNi42NzEyOSAxNC41MjE2MywxMi4wMjI0MiAyMS45Nzg5NiwxNC40OTE0YzEuNTY2MjEsMC41NCAzLjE4OTIzLDAuODk4NDEgNC44MzcxOSwxLjA2ODIxdjBjMS40MTQ4NiwwLjEzOTQgMi44NDI3NywwLjA1NzgxIDQuMjMyNTMsLTAuMjQxODZjMS43MDkzMywtMC4yOTg5NCAzLjI0ODUxLC0xLjIxNzQxIDQuMzIzMjMsLTIuNTc5ODNjMC4wNjA0NiwtMC4wNzA1NCAwLjY0NDk2LC0wLjY0NDk2IDAuNjM0ODgsLTAuNjU1MDR6IiBmaWxsPSIjMDAwMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZS1vcGFjaXR5PSIwLjEyOTQxIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMTUiLz48cGF0aCBkPSJNMzEuNzc3MTgsMTAuNTUwMzNjLTEwLjI5OTE3LDEuMTA4NTIgLTE2LjM0NTY1LDQuNjY1ODcgLTE5LjE0NzE5LDkuMTgwNTdjLTEuODkyMiwzLjI0Nzk3IC0yLjM0MTU2LDcuMTM4NzIgLTEuMjM5NTMsMTAuNzMyNWwwLjA5MDcsMC4zNDI2NGwyMC4yNjU3OSwtMjAuMjU1NzF6TTc4LjI4NDY5LDIxLjc1NjQ4Yy03LjM0NjQ3LC03LjM1NjU1IC0xNC43NzM1NywtMTMuMTAwNyAtMjAuODkwNTksLTE2LjI5NTI3Yy00LjM0MzM5LC0yLjIzNzIgLTcuNzI5NDIsLTMuMTM0MDkgLTkuMzkyMiwtMi4xODY4MWwtMC44MjYzNiwwLjgyNjM2Yy0xLjIyOTQ1LDIuMDE1NSAtMC41MTM5Niw2LjMwODQ5IDEuNzkzNzksMTEuNjY5N2MzLjU2MzM1LDcuNzMyMTkgOC41MjAxNSwxNC43NDIwOCAxNC42MjI0LDIwLjY3ODk3YzYuOTkzNzcsNi45OTM3NyAxNC41MDE0OCwxMi4yMzQwNCAyMC44OTA1OSwxNS4xMTYyYzQuNTQ0OTQsMi4wMTU1IDguMjczNiwyLjkyMjQ3IDEwLjQzMDE4LDIuMzM3OTdsMS45MjQ4LC0xLjkzNDg4YzAuNDUzNDgsLTEuOTI0OCAtMC41MDM4OCwtNS4xNzk4MiAtMi41MDkyOSwtOS4yMDA3M2MtMy4xMTM5NCwtNi4yNTgxMSAtOC43ODc1NSwtMTMuNzY1ODIgLTE2LjA0MzMzLC0yMS4wMTE1MnpNNjAuMjg2MzMsLTAuMTUxOTRjNi43MTE1OSwzLjQ1NjU3IDE0LjY5Mjk1LDkuNjQ0MTQgMjIuNDgyODMsMTcuNDM0MDJjNy43ODk4OSw3Ljc4OTg5IDEzLjg1NjUyLDE1LjgyMTYzIDE3LjIyMjM5LDIyLjcyNDY5YzMuNzc5MDUsNy41NjgxOCA0LjM4MzcsMTQuMDI3ODQgMC42OTUzNCwxNy43MTYxOWMtMC42MDU4LDAuNTc3NDkgLTEuMjk1NTIsMS4wNTk5NiAtMi4wNDU3MiwxLjQzMWwtMzkuNDIzMDYsMzkuNDAyOWMtMi4zNjgyLDIuMzg4MzYgLTMuMzU1OCwzLjM5NjExIC03LjQzNzE3LDQuMzEzMTVjLTIuMDMxMzIsMC40NDAzNyAtNC4xMTg3LDAuNTYyNzYgLTYuMTg3NTcsMC4zNjI3OGMtMi4xNDE4MywtMC4yMDc4IC00LjI1MjE1LC0wLjY2NDM3IC02LjI4ODM0LC0xLjM2MDQ2Yy04LjM5NDUzLC0yLjgyMTY5IC0xNy4xMzE2OSwtOC43MjcwOSAtMjQuNDA3NjIsLTE1Ljk4Mjg2Yy03LjI3NTkzLC03LjI1NTc3IC0xMy4zNzI4LC0xNi4wNTM0MSAtMTYuMzI1NSwtMjQuNDY4MWMtMC41NTA0NiwtMS41MjU1MiAtMC45Nzg0MywtMy4wOTI0OCAtMS4yNzk4NCwtNC42ODYwMmMtMC4yODYwMywtMS40Nzc3NSAtMC40MzExMiwtMi45NzkzIC0wLjQzMzM0LC00LjQ4NDQ3Yy0wLjExODE3LC0yLjM1MjM0IDAuMzA1OTIsLTQuNzAwMzcgMS4yMzk1MywtNi44NjI3NWMwLjk2NzQsLTEuODYxNzMgMi4yNTMyLC0zLjUzOTc2IDMuNzk5MjEsLTQuOTU4MTFsMC4xNjEyNCwtMC4xNzEzMmw0Ljc0NjQ5LC00LjczNjQxYy0wLjQ5NzQ3LC0xLjEyNjAyIC0wLjkwODQ4LC0yLjI4ODI4IC0xLjIyOTQ1LC0zLjQ3NjczYy0xLjU1NjEsLTUuMTk1ODkgLTAuODUzODIsLTEwLjgwNjc1IDEuOTM0ODgsLTE1LjQ1ODg0YzQuMzAzMDgsLTcuMDM0MDcgMTMuOTU3MywtMTIuMjY0MjggMzAuNjY1NzMsLTEyLjQzNTZsNC4yMDIzMSwtNC4xOTIyM2MwLjQ0MzY4LC0wLjYwODMyIDAuOTgxMTEsLTEuMTQyMzUgMS41OTIyNCwtMS41ODIxNmMwLjE5ODU3LC0wLjE4NzMxIDAuNDI2NywtMC4zNDA1MyAwLjY3NTE5LC0wLjQ1MzQ4YzMuNzM4NzQsLTIuMjU3MzUgOS4yNzEyNywtMS4zNjA0NiAxNS42NDAyMywxLjkyNDh6TTg5LjA0NzQzLDU5Ljc2ODY5Yy0yLjQ2NzIsLTAuNTU5NjYgLTQuODY4NzQsLTEuMzc3MDcgLTcuMTY1MDgsLTIuNDM4NzVjLTcuMDU0MjMsLTMuMTg0NDggLTE1LjIyNzA2LC04Ljg3ODI1IC0yMi43NjUsLTE2LjQwNjEyYy02LjY2NzI3LC02LjUwNjc2IC0xMi4wNzg1MSwtMTQuMTg2MzYgLTE1Ljk2MjcxLC0yMi42NTQxNWMtMS4wOTgyNiwtMi40Nzg0NSAtMS45MDY0NiwtNS4wNzU1MSAtMi40MDg1MiwtNy43Mzk0OWwtMjYuNDE4OTgsMjYuMzAyMmMxMy44ODM1NywxNS4wMzc1OSA1NS4zNzIzNywyMC4zNTU5NCA2OC4yNDEzLDE0Ljg0MzU2YzEuNTI5OTcsLTAuNjI2MTIgMy4yNzkxNSwwLjA5Mjc2IDMuOTI2NjksMS42MTM4YzAuNjQ3NTQsMS41MjEwMyAtMC4wNDY3MSwzLjI4MDE0IC0xLjU1ODQ5LDMuOTQ4OTdjLTE1LjMyNzgzLDYuNTYwNDMgLTYyLjU1ODc2LC0xLjUyNTc2IC03NC44NjYzNCwtMTYuMDgzMDlsLTMuNjk4NDMsMy43Mzg3NGMtMS4wMTc5LDAuOTE0MDUgLTEuODgyMTcsMS45ODU4NyAtMi41NTk2OCwzLjE3NDRjLTAuNTE1MzYsMS4zMjczNyAtMC43MzU0NSwyLjc1MTA1IC0wLjY0NDk2LDQuMTcyMDdjMC4wMDc3LDEuMTE2NDIgMC4xMTkwNCwyLjIyOTcyIDAuMzMyNTYsMy4zMjU1NmMwLjIzOTEzLDEuMjgwMjMgMC41NzYwMywyLjU0MDI0IDEuMDA3NzQsMy43Njg5N2MyLjYzMDIyLDcuNDg3NTYgOC4wNjE5NywxNS4zODgyOSAxNC44MjM5NiwyMi4wNTk1OGM2Ljc2MTk5LDYuNjcxMjkgMTQuNTIxNjMsMTIuMDIyNDIgMjEuOTc4OTYsMTQuNDkxNGMxLjU2NjIxLDAuNTQgMy4xODkyMywwLjg5ODQxIDQuODM3MTksMS4wNjgyMXYwYzEuNDE0ODYsMC4xMzk0IDIuODQyNzcsMC4wNTc4MSA0LjIzMjUzLC0wLjI0MTg2YzEuNzA5MzMsLTAuMjk4OTQgMy4yNDg1MSwtMS4yMTc0MSA0LjMyMzIzLC0yLjU3OTgzYzAuMDYwNDYsLTAuMDcwNTQgMC42NDQ5NiwtMC42NDQ5NiAwLjYzNDg4LC0wLjY1NTA0eiIgZmlsbD0iIzAwMDAwMCIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMTAiLz48L2c+PC9nPjwvc3ZnPjwhLS1yb3RhdGlvbkNlbnRlcjo1OC45MjgwOTo1Ny42NTE5Mzk5OTk5OTk5OTYtLT4=";
+ const GradientIcon =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxMTYuNDE5NjQiIGhlaWdodD0iMTE4LjM0OTk0IiB2aWV3Qm94PSIwLDAsMTE2LjQxOTY0LDExOC4zNDk5NCI+PGRlZnM+PGxpbmVhckdyYWRpZW50IHgxPSIzLjM2ODMzIiB5MT0iNzMuMjEzOCIgeDI9IjgzLjM4NjAzIiB5Mj0iNzMuMjEzOCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGlkPSJjb2xvci0xIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNmNTQyNDIiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiM0Mjk3ZjUiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg4LjkyODA5LDcuNjUxOTQpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIHN0eWxlPSJtaXgtYmxlbmQtbW9kZTogbm9ybWFsIj48cGF0aCBkPSJNMy42NTYwMyw0OS40MjYwM2MyLjQyOTg2LDAuNDg1MDUgMTQuNDkxNDcsMjQuMTExIDMxLjU1NDU1LDIxLjI2NDVjMTUuNTE1NjMsLTIuNTg4MzYgOS4wMTA4NywxNC4yMjIyNyA0OC4xNzU0NSwtNS40NjQ0OWwtMjguMjksMjguMjZjMCwwIC0wLjU3LDAuNTggLTAuNjMsMC42NWMtMS4wNjY0NiwxLjM1MTk0IC0yLjU5MzgxLDIuMjYzMzYgLTQuMjksMi41NmMtMS4zNzkwOCwwLjI5NzM2IC0yLjc5NjAyLDAuMzc4MzMgLTQuMiwwLjI0djBjLTEuNjM1MjksLTAuMTY4NDkgLTMuMjQ1ODMsLTAuNTI0MTUgLTQuOCwtMS4wNmMtNy40LC0yLjQ4IC0xNS4xNywtNy44IC0yMS43OSwtMTQuMzdjLTYuNjIsLTYuNTcgLTEyLjA3LC0xNC40NiAtMTQuNjgsLTIxLjg5Yy0wLjQyODQsLTEuMjE5MjkgLTAuNzYyNzEsLTIuNDY5NjIgLTEsLTMuNzRjLTAuMjExODgsLTEuMDg3NDIgLTAuMzIyMzYsLTIuMTkyMTYgLTAuMzMsLTMuM2MtMC4wMzY2MiwtMS4wNTc1MiAwLjA1NzQyLC0yLjExNTUyIDAuMjgsLTMuMTV6IiBmaWxsPSJ1cmwoI2NvbG9yLTEpIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIi8+PHBhdGggZD0iTTMxLjc3NzE4LDEwLjU1MDMzYy0xMC4yOTkxNywxLjEwODUyIC0xNi4zNDU2NSw0LjY2NTg3IC0xOS4xNDcxOSw5LjE4MDU3Yy0xLjg5MjIsMy4yNDc5NyAtMi4zNDE1Niw3LjEzODcyIC0xLjIzOTUzLDEwLjczMjVsMC4wOTA3LDAuMzQyNjRsMjAuMjY1NzksLTIwLjI1NTcxek03OC4yODQ2OSwyMS43NTY0OGMtNy4zNDY0NywtNy4zNTY1NSAtMTQuNzczNTcsLTEzLjEwMDcgLTIwLjg5MDU5LC0xNi4yOTUyN2MtNC4zNDMzOSwtMi4yMzcyIC03LjcyOTQyLC0zLjEzNDA5IC05LjM5MjIsLTIuMTg2ODFsLTAuODI2MzYsMC44MjYzNmMtMS4yMjk0NSwyLjAxNTUgLTAuNTEzOTYsNi4zMDg0OSAxLjc5Mzc5LDExLjY2OTdjMy41NjMzNSw3LjczMjE5IDguNTIwMTUsMTQuNzQyMDggMTQuNjIyNCwyMC42Nzg5N2M2Ljk5Mzc3LDYuOTkzNzcgMTQuNTAxNDgsMTIuMjM0MDQgMjAuODkwNTksMTUuMTE2MmM0LjU0NDk0LDIuMDE1NSA4LjI3MzYsMi45MjI0NyAxMC40MzAxOCwyLjMzNzk3bDEuOTI0OCwtMS45MzQ4OGMwLjQ1MzQ4LC0xLjkyNDggLTAuNTAzODgsLTUuMTc5ODIgLTIuNTA5MjksLTkuMjAwNzNjLTMuMTEzOTQsLTYuMjU4MTEgLTguNzg3NTUsLTEzLjc2NTgyIC0xNi4wNDMzMywtMjEuMDExNTJ6TTYwLjI4NjMzLC0wLjE1MTk0YzYuNzExNTksMy40NTY1NyAxNC42OTI5NSw5LjY0NDE0IDIyLjQ4MjgzLDE3LjQzNDAyYzcuNzg5ODksNy43ODk4OSAxMy44NTY1MiwxNS44MjE2MyAxNy4yMjIzOSwyMi43MjQ2OWMzLjc3OTA1LDcuNTY4MTggNC4zODM3LDE0LjAyNzg0IDAuNjk1MzQsMTcuNzE2MTljLTAuNjA1OCwwLjU3NzQ5IC0xLjI5NTUyLDEuMDU5OTYgLTIuMDQ1NzIsMS40MzFsLTM5LjQyMzA2LDM5LjQwMjljLTIuMzY4MiwyLjM4ODM2IC0zLjM1NTgsMy4zOTYxMSAtNy40MzcxNyw0LjMxMzE1Yy0yLjAzMTMyLDAuNDQwMzcgLTQuMTE4NywwLjU2Mjc2IC02LjE4NzU3LDAuMzYyNzhjLTIuMTQxODMsLTAuMjA3OCAtNC4yNTIxNSwtMC42NjQzNyAtNi4yODgzNCwtMS4zNjA0NmMtOC4zOTQ1MywtMi44MjE2OSAtMTcuMTMxNjksLTguNzI3MDkgLTI0LjQwNzYyLC0xNS45ODI4NmMtNy4yNzU5MywtNy4yNTU3NyAtMTMuMzcyOCwtMTYuMDUzNDEgLTE2LjMyNTUsLTI0LjQ2ODFjLTAuNTUwNDYsLTEuNTI1NTIgLTAuOTc4NDMsLTMuMDkyNDggLTEuMjc5ODQsLTQuNjg2MDJjLTAuMjg2MDMsLTEuNDc3NzUgLTAuNDMxMTIsLTIuOTc5MyAtMC40MzMzNCwtNC40ODQ0N2MtMC4xMTgxNywtMi4zNTIzNCAwLjMwNTkyLC00LjcwMDM3IDEuMjM5NTMsLTYuODYyNzVjMC45Njc0LC0xLjg2MTczIDIuMjUzMiwtMy41Mzk3NiAzLjc5OTIxLC00Ljk1ODExbDAuMTYxMjQsLTAuMTcxMzJsNC43NDY0OSwtNC43MzY0MWMtMC40OTc0NywtMS4xMjYwMiAtMC45MDg0OCwtMi4yODgyOCAtMS4yMjk0NSwtMy40NzY3M2MtMS41NTYxLC01LjE5NTg5IC0wLjg1MzgyLC0xMC44MDY3NSAxLjkzNDg4LC0xNS40NTg4NGM0LjMwMzA4LC03LjAzNDA3IDEzLjk1NzMsLTEyLjI2NDI4IDMwLjY2NTczLC0xMi40MzU2bDQuMjAyMzEsLTQuMTkyMjNjMC40NDM2OCwtMC42MDgzMiAwLjk4MTExLC0xLjE0MjM1IDEuNTkyMjQsLTEuNTgyMTZjMC4xOTg1NywtMC4xODczMSAwLjQyNjcsLTAuMzQwNTMgMC42NzUxOSwtMC40NTM0OGMzLjczODc0LC0yLjI1NzM1IDkuMjcxMjcsLTEuMzYwNDYgMTUuNjQwMjMsMS45MjQ4ek04OS4wNDc0Myw1OS43Njg2OWMtMi40NjcyLC0wLjU1OTY2IC00Ljg2ODc0LC0xLjM3NzA3IC03LjE2NTA4LC0yLjQzODc1Yy03LjA1NDIzLC0zLjE4NDQ4IC0xNS4yMjcwNiwtOC44NzgyNSAtMjIuNzY1LC0xNi40MDYxMmMtNi42NjcyNywtNi41MDY3NiAtMTIuMDc4NTEsLTE0LjE4NjM2IC0xNS45NjI3MSwtMjIuNjU0MTVjLTEuMDk4MjYsLTIuNDc4NDUgLTEuOTA2NDYsLTUuMDc1NTEgLTIuNDA4NTIsLTcuNzM5NDlsLTI2LjQxODk4LDI2LjMwMjJjMTMuODgzNTcsMTUuMDM3NTkgNTUuMzcyMzcsMjAuMzU1OTQgNjguMjQxMywxNC44NDM1NmMxLjUyOTk3LC0wLjYyNjEyIDMuMjc5MTUsMC4wOTI3NiAzLjkyNjY5LDEuNjEzOGMwLjY0NzU0LDEuNTIxMDMgLTAuMDQ2NzEsMy4yODAxNCAtMS41NTg0OSwzLjk0ODk3Yy0xNS4zMjc4Myw2LjU2MDQzIC02Mi41NTg3NiwtMS41MjU3NiAtNzQuODY2MzQsLTE2LjA4MzA5bC0zLjY5ODQzLDMuNzM4NzRjLTEuMDE3OSwwLjkxNDA1IC0xLjg4MjE3LDEuOTg1ODcgLTIuNTU5NjgsMy4xNzQ0Yy0wLjUxNTM2LDEuMzI3MzcgLTAuNzM1NDUsMi43NTEwNSAtMC42NDQ5Niw0LjE3MjA3YzAuMDA3NywxLjExNjQyIDAuMTE5MDQsMi4yMjk3MiAwLjMzMjU2LDMuMzI1NTZjMC4yMzkxMywxLjI4MDIzIDAuNTc2MDMsMi41NDAyNCAxLjAwNzc0LDMuNzY4OTdjMi42MzAyMiw3LjQ4NzU2IDguMDYxOTcsMTUuMzg4MjkgMTQuODIzOTYsMjIuMDU5NThjNi43NjE5OSw2LjY3MTI5IDE0LjUyMTYzLDEyLjAyMjQyIDIxLjk3ODk2LDE0LjQ5MTRjMS41NjYyMSwwLjU0IDMuMTg5MjMsMC44OTg0MSA0LjgzNzE5LDEuMDY4MjF2MGMxLjQxNDg2LDAuMTM5NCAyLjg0Mjc3LDAuMDU3ODEgNC4yMzI1MywtMC4yNDE4NmMxLjcwOTMzLC0wLjI5ODk0IDMuMjQ4NTEsLTEuMjE3NDEgNC4zMjMyMywtMi41Nzk4M2MwLjA2MDQ2LC0wLjA3MDU0IDAuNjQ0OTYsLTAuNjQ0OTYgMC42MzQ4OCwtMC42NTUwNHoiIGZpbGw9IiMwMDAwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlLW9wYWNpdHk9IjAuMTI5NDEiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxNSIvPjxwYXRoIGQ9Ik0zMS43NzcxOCwxMC41NTAzM2MtMTAuMjk5MTcsMS4xMDg1MiAtMTYuMzQ1NjUsNC42NjU4NyAtMTkuMTQ3MTksOS4xODA1N2MtMS44OTIyLDMuMjQ3OTcgLTIuMzQxNTYsNy4xMzg3MiAtMS4yMzk1MywxMC43MzI1bDAuMDkwNywwLjM0MjY0bDIwLjI2NTc5LC0yMC4yNTU3MXpNNzguMjg0NjksMjEuNzU2NDhjLTcuMzQ2NDcsLTcuMzU2NTUgLTE0Ljc3MzU3LC0xMy4xMDA3IC0yMC44OTA1OSwtMTYuMjk1MjdjLTQuMzQzMzksLTIuMjM3MiAtNy43Mjk0MiwtMy4xMzQwOSAtOS4zOTIyLC0yLjE4NjgxbC0wLjgyNjM2LDAuODI2MzZjLTEuMjI5NDUsMi4wMTU1IC0wLjUxMzk2LDYuMzA4NDkgMS43OTM3OSwxMS42Njk3YzMuNTYzMzUsNy43MzIxOSA4LjUyMDE1LDE0Ljc0MjA4IDE0LjYyMjQsMjAuNjc4OTdjNi45OTM3Nyw2Ljk5Mzc3IDE0LjUwMTQ4LDEyLjIzNDA0IDIwLjg5MDU5LDE1LjExNjJjNC41NDQ5NCwyLjAxNTUgOC4yNzM2LDIuOTIyNDcgMTAuNDMwMTgsMi4zMzc5N2wxLjkyNDgsLTEuOTM0ODhjMC40NTM0OCwtMS45MjQ4IC0wLjUwMzg4LC01LjE3OTgyIC0yLjUwOTI5LC05LjIwMDczYy0zLjExMzk0LC02LjI1ODExIC04Ljc4NzU1LC0xMy43NjU4MiAtMTYuMDQzMzMsLTIxLjAxMTUyek02MC4yODYzMywtMC4xNTE5NGM2LjcxMTU5LDMuNDU2NTcgMTQuNjkyOTUsOS42NDQxNCAyMi40ODI4MywxNy40MzQwMmM3Ljc4OTg5LDcuNzg5ODkgMTMuODU2NTIsMTUuODIxNjMgMTcuMjIyMzksMjIuNzI0NjljMy43NzkwNSw3LjU2ODE4IDQuMzgzNywxNC4wMjc4NCAwLjY5NTM0LDE3LjcxNjE5Yy0wLjYwNTgsMC41Nzc0OSAtMS4yOTU1MiwxLjA1OTk2IC0yLjA0NTcyLDEuNDMxbC0zOS40MjMwNiwzOS40MDI5Yy0yLjM2ODIsMi4zODgzNiAtMy4zNTU4LDMuMzk2MTEgLTcuNDM3MTcsNC4zMTMxNWMtMi4wMzEzMiwwLjQ0MDM3IC00LjExODcsMC41NjI3NiAtNi4xODc1NywwLjM2Mjc4Yy0yLjE0MTgzLC0wLjIwNzggLTQuMjUyMTUsLTAuNjY0MzcgLTYuMjg4MzQsLTEuMzYwNDZjLTguMzk0NTMsLTIuODIxNjkgLTE3LjEzMTY5LC04LjcyNzA5IC0yNC40MDc2MiwtMTUuOTgyODZjLTcuMjc1OTMsLTcuMjU1NzcgLTEzLjM3MjgsLTE2LjA1MzQxIC0xNi4zMjU1LC0yNC40NjgxYy0wLjU1MDQ2LC0xLjUyNTUyIC0wLjk3ODQzLC0zLjA5MjQ4IC0xLjI3OTg0LC00LjY4NjAyYy0wLjI4NjAzLC0xLjQ3Nzc1IC0wLjQzMTEyLC0yLjk3OTMgLTAuNDMzMzQsLTQuNDg0NDdjLTAuMTE4MTcsLTIuMzUyMzQgMC4zMDU5MiwtNC43MDAzNyAxLjIzOTUzLC02Ljg2Mjc1YzAuOTY3NCwtMS44NjE3MyAyLjI1MzIsLTMuNTM5NzYgMy43OTkyMSwtNC45NTgxMWwwLjE2MTI0LC0wLjE3MTMybDQuNzQ2NDksLTQuNzM2NDFjLTAuNDk3NDcsLTEuMTI2MDIgLTAuOTA4NDgsLTIuMjg4MjggLTEuMjI5NDUsLTMuNDc2NzNjLTEuNTU2MSwtNS4xOTU4OSAtMC44NTM4MiwtMTAuODA2NzUgMS45MzQ4OCwtMTUuNDU4ODRjNC4zMDMwOCwtNy4wMzQwNyAxMy45NTczLC0xMi4yNjQyOCAzMC42NjU3MywtMTIuNDM1Nmw0LjIwMjMxLC00LjE5MjIzYzAuNDQzNjgsLTAuNjA4MzIgMC45ODExMSwtMS4xNDIzNSAxLjU5MjI0LC0xLjU4MjE2YzAuMTk4NTcsLTAuMTg3MzEgMC40MjY3LC0wLjM0MDUzIDAuNjc1MTksLTAuNDUzNDhjMy43Mzg3NCwtMi4yNTczNSA5LjI3MTI3LC0xLjM2MDQ2IDE1LjY0MDIzLDEuOTI0OHpNODkuMDQ3NDMsNTkuNzY4NjljLTIuNDY3MiwtMC41NTk2NiAtNC44Njg3NCwtMS4zNzcwNyAtNy4xNjUwOCwtMi40Mzg3NWMtNy4wNTQyMywtMy4xODQ0OCAtMTUuMjI3MDYsLTguODc4MjUgLTIyLjc2NSwtMTYuNDA2MTJjLTYuNjY3MjcsLTYuNTA2NzYgLTEyLjA3ODUxLC0xNC4xODYzNiAtMTUuOTYyNzEsLTIyLjY1NDE1Yy0xLjA5ODI2LC0yLjQ3ODQ1IC0xLjkwNjQ2LC01LjA3NTUxIC0yLjQwODUyLC03LjczOTQ5bC0yNi40MTg5OCwyNi4zMDIyYzEzLjg4MzU3LDE1LjAzNzU5IDU1LjM3MjM3LDIwLjM1NTk0IDY4LjI0MTMsMTQuODQzNTZjMS41Mjk5NywtMC42MjYxMiAzLjI3OTE1LDAuMDkyNzYgMy45MjY2OSwxLjYxMzhjMC42NDc1NCwxLjUyMTAzIC0wLjA0NjcxLDMuMjgwMTQgLTEuNTU4NDksMy45NDg5N2MtMTUuMzI3ODMsNi41NjA0MyAtNjIuNTU4NzYsLTEuNTI1NzYgLTc0Ljg2NjM0LC0xNi4wODMwOWwtMy42OTg0MywzLjczODc0Yy0xLjAxNzksMC45MTQwNSAtMS44ODIxNywxLjk4NTg3IC0yLjU1OTY4LDMuMTc0NGMtMC41MTUzNiwxLjMyNzM3IC0wLjczNTQ1LDIuNzUxMDUgLTAuNjQ0OTYsNC4xNzIwN2MwLjAwNzcsMS4xMTY0MiAwLjExOTA0LDIuMjI5NzIgMC4zMzI1NiwzLjMyNTU2YzAuMjM5MTMsMS4yODAyMyAwLjU3NjAzLDIuNTQwMjQgMS4wMDc3NCwzLjc2ODk3YzIuNjMwMjIsNy40ODc1NiA4LjA2MTk3LDE1LjM4ODI5IDE0LjgyMzk2LDIyLjA1OTU4YzYuNzYxOTksNi42NzEyOSAxNC41MjE2MywxMi4wMjI0MiAyMS45Nzg5NiwxNC40OTE0YzEuNTY2MjEsMC41NCAzLjE4OTIzLDAuODk4NDEgNC44MzcxOSwxLjA2ODIxdjBjMS40MTQ4NiwwLjEzOTQgMi44NDI3NywwLjA1NzgxIDQuMjMyNTMsLTAuMjQxODZjMS43MDkzMywtMC4yOTg5NCAzLjI0ODUxLC0xLjIxNzQxIDQuMzIzMjMsLTIuNTc5ODNjMC4wNjA0NiwtMC4wNzA1NCAwLjY0NDk2LC0wLjY0NDk2IDAuNjM0ODgsLTAuNjU1MDR6IiBmaWxsPSIjMDAwMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSIxMCIvPjwvZz48L2c+PC9zdmc+PCEtLXJvdGF0aW9uQ2VudGVyOjU4LjkyODA4ODk1NTAxODk4OjU3LjY1MTk0MTAyMjI0MTgzLS0+";
+ const PictureIcon =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxMzguNjA3MDUiIGhlaWdodD0iMTE0LjIxMjI3IiB2aWV3Qm94PSIwLDAsMTM4LjYwNzA1LDExNC4yMTIyNyI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTkuMzAzNTIsNy4xMDYxNCkiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbD0iIzAwMDAwMCIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2Utb3BhY2l0eT0iMC4xMjk0MSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjcuNSIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWRhc2hhcnJheT0iIiBzdHJva2UtZGFzaG9mZnNldD0iMCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0xMTUuNTUzNTIsNS43OTA3NnY4OC40MTg0OGMwLDUuMDUxMzcgLTQuMDk1NTIsOS4xNDY5IC05LjE0NjksOS4xNDY5aC0xMTIuODEzMjVjLTUuMDUxMzcsMCAtOS4xNDY5LC00LjA5NTUyIC05LjE0NjksLTkuMTQ2OXYtODguNDE4NDhjMCwtNS4wNTEzNyA0LjA5NTUyLC05LjE0NjkgOS4xNDY5LC05LjE0NjloMTEyLjgxMzI1YzUuMDUxMzcsMCA5LjE0NjksNC4wOTU1MiA5LjE0NjksOS4xNDY5ek0yLjc0MDI3LDU3LjU4OTY0bDE1LjE2MDk4LC0xMS4xNDc3OGMzLjgxODgzLC0yLjgxMjY3IDkuMTYwNjIsLTIuMjM0MTMgMTIuMjkzNDMsMS4zMzU0NWwxMC4yMjM5NCwxMS42NDg1N2wyNC41NDU3LC0xMi4yNzA1NmMzLjAxNjE5LC0xLjUxMTUyIDYuNjI0NjQsLTEuMjM3MTIgOS4zODAxNCwwLjcxNTc0bDIyLjkxNTI2LDE2LjIzMzQ2di00OS4xNjY4NmgtOTQuNTE5NDZ6TTM4LjU3NzgxLDg1LjA2MjM0bC0xNi41OTcwNCwtMTguOTA4OTJsLTE5LjE1NTg5LDE0LjA4MTY1Yy0wLjAyNzQ0LDAuMDIwNTggLTAuMDU3MTcsMC4wMjk3MyAtMC4wODQ2MSwwLjA1MDMxdjQuNzc2OTd6TTk1LjE5NzEsODUuMDYyMzRsLTI2Ljk1NTkxLC0xOS4wOTE4NmwtMTUuMzQ2MjEsNy42NzE5NmwxMC4wMjI3MSwxMS40MTk5ek0zMC4xODA5NiwyOS44MDEzNmMwLC01LjQ3MjEzIC00LjQzNjI1LC05LjkwODM4IC05LjkwODM4LC05LjkwODM4Yy01LjQ3MjEzLDAgLTkuOTA4MzgsNC40MzYyNSAtOS45MDgzOCw5LjkwODM4YzAsNS40NzIxMyA0LjQzNjI1LDkuOTA4MzggOS45MDgzOCw5LjkwODM4YzUuNDcyMTMsMCA5LjkwODM4LC00LjQzNjI1IDkuOTA4MzgsLTkuOTA4Mzh6Ii8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6NjkuMzAzNTIzOTQ2MzExODo1Ny4xMDYxMzY4MjA3MDk5Ni0tPg==";
+ const ResetIcon =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIzNDkuMzIzMjQiIGhlaWdodD0iMzI3LjM0OTEyIiB2aWV3Qm94PSIwLDAsMzQ5LjMyMzI0LDMyNy4zNDkxMiI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTY1LjMzODM4LC0xNi4zMjU0NCkiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIHN0eWxlPSJtaXgtYmxlbmQtbW9kZTogbm9ybWFsIj48cGF0aCBkPSJNMTM2LjQxNTc0LDk3LjkwMTk0Yy0zOC4yNDgwNiw4Mi43NzIyNSAtMzAuNTkzMDYsMjMyLjc5NzI4IDEzOS43MzA3NywyMjAuMDA0MTZjMTQyLjQzOSwtMTAuNjk4NjggMTQzLjc0MTk3LC0xOTIuNTE4ODQgNDMuMzc5NjksLTI0OS41MzgxIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iNTAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjxwYXRoIGQ9Ik05NS42NDc3MSw0OS43ODA2N2w5OS44NTIyMiwtMC40MDI2MyIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PHBhdGggZD0iTTkxLjQ1MzI4LDY1LjUxNDIxbDEwOS42ODM0NywtMjQuMTg4NzciIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSI1MCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PHBhdGggZD0iTTIwNC4wMjE1Miw0My4yOTQzNWwtOS44MjI0LDEwNi4wMTExMyIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjUwIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48cGF0aCBkPSJNOTAuMzM4MzgsNjcuNDI2MDhsMTAzLjUyMzY2LDgyLjIwODIiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSI1MCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PHBhdGggZD0iTTE1My45MTUxOCwxMDMuODQ5ODV2LTMyLjA5NTloMTQuNjk0NTF2MzIuMDk1OXoiIHN0cm9rZT0iIzAwMDAwMCIgc3Ryb2tlLXdpZHRoPSI1MCIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiLz48cGF0aCBkPSJNMTk1LjQ5OTkyLDQ5LjM3ODA0IiBzdHJva2U9IiNmZjAwMDAiIHN0cm9rZS13aWR0aD0iNTAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjwvZz48L2c+PC9zdmc+PCEtLXJvdGF0aW9uQ2VudGVyOjE3NC42NjE2MjoxNjMuNjc0NTU5OTk5OTk5OTktLT4=";
+
+ const stylesheet = document.createElement("style");
+ stylesheet.className = "shovelcss-style";
+ // end of for higher precedence than other sheets
+ document.body.appendChild(stylesheet);
+
+ const applyCSS = () => {
+ let css = "";
+
+ // We assume all values are sanitized when they are set, so then we can just use them as-is here.
+
+ if (monitorText) {
+ css += `${monitorRoot}, ${monitorListFooter}, ${monitorListHeader}, ${monitorRowIndex} { color: ${monitorText}; }`;
+ }
+ if (monitorBackgroundColor) {
+ css += `${monitorRoot}, ${monitorRowsInner} { background: ${monitorBackgroundColor}; }`;
+ }
+ if (monitorBorder) {
+ css += `${monitorRoot} { border-color: ${monitorBorder}; }`;
+ }
+ if (monitorBackgroundRoundness >= 0) {
+ css += `${monitorRoot} { border-radius: ${monitorBackgroundRoundness}px; }`;
+ }
+ if (monitorBackgroundBorderWidth >= 0) {
+ css += `${monitorRoot} { border-width: ${monitorBackgroundBorderWidth}px; }`;
+ }
+ if (variableValueBackground) {
+ css += `${monitorValue}, ${monitorValueLarge} { background: ${variableValueBackground} !important; }`;
+ }
+ if (variableValueTextColor) {
+ css += `${monitorValue}, ${monitorValueLarge} { color: ${variableValueTextColor}; }`;
+ }
+ if (variableValueRoundness >= 0) {
+ css += `${monitorValue} { border-radius: ${variableValueRoundness}px; }`;
+ }
+ if (listHeaderBackground) {
+ css += `${monitorListHeader} { background: ${listHeaderBackground}; }`;
+ }
+ if (listFooterBackground) {
+ css += `${monitorListFooter} { background: ${listHeaderBackground}; }`;
+ }
+ if (listValueBackground) {
+ css += `${monitorRowValueOuter} { background: ${listValueBackground} !important; }`;
+ }
+ if (listValueText) {
+ css += `${monitorRowValueOuter} { color: ${listValueText}; }`;
+ }
+ if (listValueRoundness >= 0) {
+ css += `${monitorRowValueOuter} { border-radius: ${listValueRoundness}px; }`;
+ }
+ if (allowScrolling) {
+ css += `${monitorRowsScroller} { overflow: ${allowScrolling} !important; }`;
+ }
+ if (askBackground) {
+ css += `${askBoxBG} { background: ${askBackground} !important; border: none !important; }`;
+ }
+ if (askBackgroundRoundness >= 0) {
+ css += `${askBoxBG} { border-radius: ${askBackgroundRoundness}px !important; }`;
+ }
+ if (askBackgroundBorderWidth >= 0) {
+ css += `${askBoxBG} { border-width: ${askBackgroundBorderWidth}px !important; }`;
+ }
+ if (askButtonBackground) {
+ css += `${askBoxButton} { background-color: ${askButtonBackground}; }`;
+ }
+ if (askButtonRoundness >= 0) {
+ css += `${askBoxButton} { border-radius: ${askButtonRoundness}px !important; }`;
+ }
+ if (askInputBackground) {
+ css += `${askBoxInner} { background: ${askInputBackground} !important; }`;
+ css += `${askBoxInner} { border: none !important; }`;
+ }
+ if (askInputText) {
+ css += `${askBoxInner} { color: ${askInputText} !important; }`;
+ }
+ if (askInputRoundness >= 0) {
+ css += `${askBoxInner} { border-radius: ${askInputRoundness}px !important; }`;
+ }
+ if (askInputBorderWidth >= 0) {
+ css += `${askBoxInner} { border-width: ${askInputBorderWidth}px !important; }`;
+ }
+ if (askButtonImage) {
+ css += `${askBoxButton} { background-image: url("${encodeURI(
+ askButtonImage
+ )}") !important; background-repeat: no-repeat; background-size: contain; }`;
+ css += `${askBoxIcon} { visibility: hidden; }`;
+ }
+ if (askInputBorder) {
+ css += `${askBoxBorderMain}, ${askBoxBorderOuter} { border-color: ${askInputBorder} !important; }`;
+ css += `${askBoxBorderOuter} { box-shadow: none !important; }`;
+ }
- stylesheet.textContent = css;
- };
+ stylesheet.textContent = css;
+ };
- const getMonitorRoot = (id) => {
- const allMonitors = document.querySelectorAll(monitorRoot);
- for (const monitor of allMonitors) {
- if (monitor.dataset.id === id) {
- return monitor;
- }
- }
- return null;
- };
-
- /**
- * @param {string} id
- * @param {number} x
- * @param {number} y
- */
- const setMonitorPosition = (id, x, y) => {
- const root = getMonitorRoot(id);
- if (root) {
- root.style.transform = `translate(${x}px, ${y}px)`;
- root.style.left = '0px';
- root.style.top = '0px';
- }
- };
-
- /**
- * @param {VM.Target} target
- * @param {string} name
- * @param {VM.VariableType} type
- * @param {number} x
- * @param {number} y
- */
- const setVariableMonitorPosition = (target, name, type, x, y) => {
- // @ts-expect-error
- const variable = target.lookupVariableByNameAndType(name, type);
- if (variable) {
- // @ts-expect-error
- setMonitorPosition(variable.id, x, y);
- }
- };
+ const getMonitorRoot = (id) => {
+ const allMonitors = document.querySelectorAll(monitorRoot);
+ for (const monitor of allMonitors) {
+ if (monitor.dataset.id === id) {
+ return monitor;
+ }
+ }
+ return null;
+ };
+
+ /**
+ * @param {string} id
+ * @param {number} x
+ * @param {number} y
+ */
+ const setMonitorPosition = (id, x, y) => {
+ const root = getMonitorRoot(id);
+ if (root) {
+ root.style.transform = `translate(${x}px, ${y}px)`;
+ root.style.left = "0px";
+ root.style.top = "0px";
+ }
+ };
+
+ /**
+ * @param {VM.Target} target
+ * @param {string} name
+ * @param {VM.VariableType} type
+ * @param {number} x
+ * @param {number} y
+ */
+ const setVariableMonitorPosition = (target, name, type, x, y) => {
+ // @ts-expect-error
+ const variable = target.lookupVariableByNameAndType(name, type);
+ if (variable) {
+ // @ts-expect-error
+ setMonitorPosition(variable.id, x, y);
+ }
+ };
- const parseColor = (color, callback) => {
- color = Scratch.Cast.toString(color);
+ const parseColor = (color, callback) => {
+ color = Scratch.Cast.toString(color);
- // These might have some exponential backtracking/ReDoS, but that's not really a concern here.
- // If a project wanted to get stuck in an infinite loop, there are so many other ways to do that.
+ // These might have some exponential backtracking/ReDoS, but that's not really a concern here.
+ // If a project wanted to get stuck in an infinite loop, there are so many other ways to do that.
- // Simple color code or name
- if (/^#?[a-z0-9]+$/.test(color)) {
- callback(color);
- return;
- }
+ // Simple color code or name
+ if (/^#?[a-z0-9]+$/.test(color)) {
+ callback(color);
+ return;
+ }
- // Simple linear gradient
- if (/^linear-gradient\(\d+deg,#?[a-z0-9]+,#?[a-z0-9]+\)$/.test(color)) {
- callback(color);
- return;
- }
+ // Simple linear gradient
+ if (/^linear-gradient\(\d+deg,#?[a-z0-9]+,#?[a-z0-9]+\)$/.test(color)) {
+ callback(color);
+ return;
+ }
- // URL
- // see list of non-escaped characters:
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI#description
- const match = color.match(/^url\("([A-Za-z0-9\-_.!~*'();/?:@&=+$,#]+)"\)$/);
- if (match) {
- const url = match[1];
- return Scratch.canFetch(url).then(allowed => {
- if (allowed) {
- callback(color);
- }
- });
- }
+ // URL
+ // see list of non-escaped characters:
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI#description
+ const match = color.match(/^url\("([A-Za-z0-9\-_.!~*'();/?:@&=+$,#]+)"\)$/);
+ if (match) {
+ const url = match[1];
+ return Scratch.canFetch(url).then((allowed) => {
+ if (allowed) {
+ callback(color);
+ }
+ });
+ }
- console.error('Invalid color', color);
- };
-
- class MonitorStyles {
- getInfo() {
- return {
- id: 'shovelcss',
- name: 'Custom Styles',
- menuIconURI: extensionIcon,
- color1: '#0072d6',
- color2: '#0064bc',
- color3: '#01539b',
- blocks: [
- {
- blockIconURI: ColorIcon,
- opcode: 'changecss',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set [COLORABLE] to [COLOR]',
- arguments: {
- COLORABLE: {
- type: Scratch.ArgumentType.STRING,
- menu: 'COLORABLE_MENU'
- },
- COLOR: {
- type: Scratch.ArgumentType.COLOR,
- defaultValue: '#ff0000'
- }
- }
- },
- {
- blockIconURI: GradientIcon,
- opcode: 'gradientAngle',
- blockType: Scratch.BlockType.REPORTER,
- text: 'make a gradient with [COLOR1] and [COLOR2] at angle [ANGLE]',
- arguments: {
- COLOR1: {
- type: Scratch.ArgumentType.COLOR,
- defaultValue: '#ff0000'
- },
- COLOR2: {
- type: Scratch.ArgumentType.COLOR,
- defaultValue: '#6ed02d'
- },
- ANGLE: {
- type: Scratch.ArgumentType.ANGLE,
- defaultValue: '90'
- }
- }
- },
- {
- blockIconURI: TransparentIcon,
- disableMonitor: true,
- opcode: 'transparentinput',
- blockType: Scratch.BlockType.REPORTER,
- text: 'transparent',
- },
- {
- blockIconURI: PictureIcon,
- disableMonitor: true,
- opcode: 'pictureinput',
- blockType: Scratch.BlockType.REPORTER,
- text: 'image [URL]',
- arguments: {
- URL: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'https://extensions.turbowarp.org/dango.png'
- }
- }
- },
- '---',
- {
- blockIconURI: PictureIcon,
- disableMonitor: true,
- opcode: 'setAskURI',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set ask prompt button image to [URL]',
- arguments: {
- URL: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'https://extensions.turbowarp.org/dango.png'
- }
- }
- },
- '---',
- {
- blockIconURI: BorderIcon,
- opcode: 'setbordersize',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set border width of [BORDER] to [SIZE]',
- arguments: {
- BORDER: {
- type: Scratch.ArgumentType.STRING,
- menu: 'BORDER_WIDTH_MENU'
- },
- SIZE: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '2'
- }
- }
- },
- {
- blockIconURI: BorderIcon,
- opcode: 'setborderradius',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set roundness of [CORNER] to [SIZE]',
- arguments: {
- SIZE: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '4'
- },
- CORNER: {
- type: Scratch.ArgumentType.STRING,
- menu: 'BORDER_ROUNDNESS_MENU'
- }
- }
- },
- '---',
- {
- blockIconURI: ResetIcon,
- opcode: 'clearCSS',
- blockType: Scratch.BlockType.COMMAND,
- text: 'reset styles'
- },
- '---',
- {
- blockIconURI: miscIcon,
- opcode: 'allowscrollrule',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set list scrolling to [SCROLLRULE]',
- arguments: {
- SCROLLRULE: {
- type: Scratch.ArgumentType.STRING,
- menu: 'SCROLL_MENU'
- }
- }
- },
- {
- blockIconURI: miscIcon,
- opcode: 'getValue',
- blockType: Scratch.BlockType.REPORTER,
- text: 'get [ITEM]',
- arguments: {
- ITEM: {
- type: Scratch.ArgumentType.STRING,
- menu: 'VALUEGET_LIST'
- }
- }
- },
- '---',
- {
- blockIconURI: miscIcon,
- opcode: 'setvarpos',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set position of variable [NAME] to x: [X] y: [Y]',
- arguments: {
- X: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- Y: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- NAME: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'my variable'
- }
- }
- },
- {
- blockIconURI: miscIcon,
- opcode: 'setlistpos',
- blockType: Scratch.BlockType.COMMAND,
- text: 'set position of list [NAME] to x: [X] y: [Y]',
- arguments: {
- X: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- Y: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
- },
- NAME: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'my variable'
- }
- }
- },
- ],
- // Accepting reporters because there can't be errors in case the value is not correct
- menus: {
- COLORABLE_MENU: {
- acceptReporters: true,
- items: [
- 'monitor text',
- 'monitor background',
- 'monitor border',
- 'variable value background',
- 'variable value text',
- 'list header background',
- 'list footer background',
- 'list value background',
- 'list value text',
- 'ask prompt background',
- 'ask prompt button background',
- 'ask prompt input background',
- 'ask prompt input text',
- 'ask prompt input border'
- ]
- },
- BORDER_WIDTH_MENU: {
- acceptReporters: true,
- items: [
- 'monitor background',
- 'ask prompt background',
- 'ask prompt input'
- ]
- },
- BORDER_ROUNDNESS_MENU: {
- acceptReporters: true,
- items: [
- 'monitor background',
- 'variable value',
- 'list value',
- 'ask prompt background',
- 'ask prompt button',
- 'ask prompt input'
- ]
- },
- SCROLL_MENU: {
- acceptReporters: true,
- items: [
- 'enabled',
- 'disabled'
- ]
- },
- VALUEGET_LIST: {
- acceptReporters: true,
- items: [
- 'monitor text',
- 'monitor background',
- 'monitor border color',
- 'variable value background',
- 'variable value text',
- 'list header background',
- 'list footer background',
- 'list value background',
- 'list value text',
- 'ask prompt background',
- 'ask prompt button background',
- 'ask prompt input background',
- 'ask prompt input text',
- 'ask prompt input border',
- 'monitor background border width',
- 'ask prompt background border width',
- 'ask prompt input border width',
- 'monitor background roundness',
- 'variable value roundness',
- 'list value roundness',
- 'ask prompt background roundness',
- 'ask prompt button roundness',
- 'ask prompt input roundness',
- 'ask prompt button image',
- 'list scroll rule'
- ]
- }
- }
- };
- }
+ console.error("Invalid color", color);
+ };
+
+ class MonitorStyles {
+ getInfo() {
+ return {
+ id: "shovelcss",
+ name: "Custom Styles",
+ menuIconURI: extensionIcon,
+ color1: "#0072d6",
+ color2: "#0064bc",
+ color3: "#01539b",
+ blocks: [
+ {
+ blockIconURI: ColorIcon,
+ opcode: "changecss",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set [COLORABLE] to [COLOR]",
+ arguments: {
+ COLORABLE: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "COLORABLE_MENU",
+ },
+ COLOR: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: "#ff0000",
+ },
+ },
+ },
+ {
+ blockIconURI: GradientIcon,
+ opcode: "gradientAngle",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "make a gradient with [COLOR1] and [COLOR2] at angle [ANGLE]",
+ arguments: {
+ COLOR1: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: "#ff0000",
+ },
+ COLOR2: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: "#6ed02d",
+ },
+ ANGLE: {
+ type: Scratch.ArgumentType.ANGLE,
+ defaultValue: "90",
+ },
+ },
+ },
+ {
+ blockIconURI: TransparentIcon,
+ disableMonitor: true,
+ opcode: "transparentinput",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "transparent",
+ },
+ {
+ blockIconURI: PictureIcon,
+ disableMonitor: true,
+ opcode: "pictureinput",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "image [URL]",
+ arguments: {
+ URL: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "https://extensions.turbowarp.org/dango.png",
+ },
+ },
+ },
+ "---",
+ {
+ blockIconURI: PictureIcon,
+ disableMonitor: true,
+ opcode: "setAskURI",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set ask prompt button image to [URL]",
+ arguments: {
+ URL: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "https://extensions.turbowarp.org/dango.png",
+ },
+ },
+ },
+ "---",
+ {
+ blockIconURI: BorderIcon,
+ opcode: "setbordersize",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set border width of [BORDER] to [SIZE]",
+ arguments: {
+ BORDER: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "BORDER_WIDTH_MENU",
+ },
+ SIZE: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "2",
+ },
+ },
+ },
+ {
+ blockIconURI: BorderIcon,
+ opcode: "setborderradius",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set roundness of [CORNER] to [SIZE]",
+ arguments: {
+ SIZE: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "4",
+ },
+ CORNER: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "BORDER_ROUNDNESS_MENU",
+ },
+ },
+ },
+ "---",
+ {
+ blockIconURI: ResetIcon,
+ opcode: "clearCSS",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "reset styles",
+ },
+ "---",
+ {
+ blockIconURI: miscIcon,
+ opcode: "allowscrollrule",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set list scrolling to [SCROLLRULE]",
+ arguments: {
+ SCROLLRULE: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "SCROLL_MENU",
+ },
+ },
+ },
+ {
+ blockIconURI: miscIcon,
+ opcode: "getValue",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "get [ITEM]",
+ arguments: {
+ ITEM: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "VALUEGET_LIST",
+ },
+ },
+ },
+ "---",
+ {
+ blockIconURI: miscIcon,
+ opcode: "setvarpos",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set position of variable [NAME] to x: [X] y: [Y]",
+ arguments: {
+ X: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ Y: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ NAME: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my variable",
+ },
+ },
+ },
+ {
+ blockIconURI: miscIcon,
+ opcode: "setlistpos",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set position of list [NAME] to x: [X] y: [Y]",
+ arguments: {
+ X: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ Y: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "0",
+ },
+ NAME: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my variable",
+ },
+ },
+ },
+ ],
+ // Accepting reporters because there can't be errors in case the value is not correct
+ menus: {
+ COLORABLE_MENU: {
+ acceptReporters: true,
+ items: [
+ "monitor text",
+ "monitor background",
+ "monitor border",
+ "variable value background",
+ "variable value text",
+ "list header background",
+ "list footer background",
+ "list value background",
+ "list value text",
+ "ask prompt background",
+ "ask prompt button background",
+ "ask prompt input background",
+ "ask prompt input text",
+ "ask prompt input border",
+ ],
+ },
+ BORDER_WIDTH_MENU: {
+ acceptReporters: true,
+ items: [
+ "monitor background",
+ "ask prompt background",
+ "ask prompt input",
+ ],
+ },
+ BORDER_ROUNDNESS_MENU: {
+ acceptReporters: true,
+ items: [
+ "monitor background",
+ "variable value",
+ "list value",
+ "ask prompt background",
+ "ask prompt button",
+ "ask prompt input",
+ ],
+ },
+ SCROLL_MENU: {
+ acceptReporters: true,
+ items: ["enabled", "disabled"],
+ },
+ VALUEGET_LIST: {
+ acceptReporters: true,
+ items: [
+ "monitor text",
+ "monitor background",
+ "monitor border color",
+ "variable value background",
+ "variable value text",
+ "list header background",
+ "list footer background",
+ "list value background",
+ "list value text",
+ "ask prompt background",
+ "ask prompt button background",
+ "ask prompt input background",
+ "ask prompt input text",
+ "ask prompt input border",
+ "monitor background border width",
+ "ask prompt background border width",
+ "ask prompt input border width",
+ "monitor background roundness",
+ "variable value roundness",
+ "list value roundness",
+ "ask prompt background roundness",
+ "ask prompt button roundness",
+ "ask prompt input roundness",
+ "ask prompt button image",
+ "list scroll rule",
+ ],
+ },
+ },
+ };
+ }
- changecss(args) {
- return parseColor(args.COLOR, color => {
- if (args.COLORABLE === 'monitor text') {
- monitorText = color;
- } else if (args.COLORABLE === 'monitor background') {
- monitorBackgroundColor = color;
- } else if (args.COLORABLE === 'monitor border') {
- monitorBorder = color;
- } else if (args.COLORABLE === 'variable value background') {
- variableValueBackground = color;
- } else if (args.COLORABLE === 'variable value text') {
- variableValueTextColor = color;
- } else if (args.COLORABLE === 'list header background') {
- listHeaderBackground = color;
- } else if (args.COLORABLE === 'list footer background') {
- listFooterBackground = color;
- } else if (args.COLORABLE === 'list value background') {
- listValueBackground = color;
- } else if (args.COLORABLE === 'list value text') {
- listValueText = color;
- } else if (args.COLORABLE === 'ask prompt background') {
- askBackground = color;
- } else if (args.COLORABLE === 'ask prompt button background') {
- askButtonBackground = color;
- } else if (args.COLORABLE === 'ask prompt input background') {
- askInputBackground = color;
- } else if (args.COLORABLE === 'ask prompt input text') {
- askInputText = color;
- } else if (args.COLORABLE === 'ask prompt input border') {
- askInputBorder = color;
- }
-
- applyCSS();
- });
- }
+ changecss(args) {
+ return parseColor(args.COLOR, (color) => {
+ if (args.COLORABLE === "monitor text") {
+ monitorText = color;
+ } else if (args.COLORABLE === "monitor background") {
+ monitorBackgroundColor = color;
+ } else if (args.COLORABLE === "monitor border") {
+ monitorBorder = color;
+ } else if (args.COLORABLE === "variable value background") {
+ variableValueBackground = color;
+ } else if (args.COLORABLE === "variable value text") {
+ variableValueTextColor = color;
+ } else if (args.COLORABLE === "list header background") {
+ listHeaderBackground = color;
+ } else if (args.COLORABLE === "list footer background") {
+ listFooterBackground = color;
+ } else if (args.COLORABLE === "list value background") {
+ listValueBackground = color;
+ } else if (args.COLORABLE === "list value text") {
+ listValueText = color;
+ } else if (args.COLORABLE === "ask prompt background") {
+ askBackground = color;
+ } else if (args.COLORABLE === "ask prompt button background") {
+ askButtonBackground = color;
+ } else if (args.COLORABLE === "ask prompt input background") {
+ askInputBackground = color;
+ } else if (args.COLORABLE === "ask prompt input text") {
+ askInputText = color;
+ } else if (args.COLORABLE === "ask prompt input border") {
+ askInputBorder = color;
+ }
+
+ applyCSS();
+ });
+ }
- gradientAngle(args) {
- return 'linear-gradient(' + args.ANGLE + 'deg,' + args.COLOR1 + ',' + args.COLOR2 + ')';
- }
+ gradientAngle(args) {
+ return (
+ "linear-gradient(" +
+ args.ANGLE +
+ "deg," +
+ args.COLOR1 +
+ "," +
+ args.COLOR2 +
+ ")"
+ );
+ }
- setbordersize(args) {
- const size = Scratch.Cast.toNumber(args.SIZE);
- if (args.BORDER === 'monitor background') {
- monitorBackgroundBorderWidth = size;
- } else if (args.BORDER === 'ask prompt background') {
- askBackgroundBorderWidth = size;
- } else if (args.BORDER === 'ask prompt input') {
- askInputBorderWidth = size;
- }
- applyCSS();
- }
+ setbordersize(args) {
+ const size = Scratch.Cast.toNumber(args.SIZE);
+ if (args.BORDER === "monitor background") {
+ monitorBackgroundBorderWidth = size;
+ } else if (args.BORDER === "ask prompt background") {
+ askBackgroundBorderWidth = size;
+ } else if (args.BORDER === "ask prompt input") {
+ askInputBorderWidth = size;
+ }
+ applyCSS();
+ }
- setborderradius(args) {
- const size = Scratch.Cast.toNumber(args.SIZE);
- if (args.CORNER === 'monitor background') {
- monitorBackgroundRoundness = size;
- } else if (args.CORNER === 'variable value') {
- variableValueRoundness = size;
- } else if (args.CORNER === 'list value') {
- listValueRoundness = size;
- } else if (args.CORNER === 'ask prompt background') {
- askBackgroundRoundness = size;
- } else if (args.CORNER === 'ask prompt button') {
- askButtonRoundness = size;
- } else if (args.CORNER === 'ask prompt input') {
- askInputRoundness = size;
- }
- applyCSS();
- }
+ setborderradius(args) {
+ const size = Scratch.Cast.toNumber(args.SIZE);
+ if (args.CORNER === "monitor background") {
+ monitorBackgroundRoundness = size;
+ } else if (args.CORNER === "variable value") {
+ variableValueRoundness = size;
+ } else if (args.CORNER === "list value") {
+ listValueRoundness = size;
+ } else if (args.CORNER === "ask prompt background") {
+ askBackgroundRoundness = size;
+ } else if (args.CORNER === "ask prompt button") {
+ askButtonRoundness = size;
+ } else if (args.CORNER === "ask prompt input") {
+ askInputRoundness = size;
+ }
+ applyCSS();
+ }
- allowscrollrule(args) {
- if (args.SCROLLRULE === 'enabled'){
- allowScrolling = 'auto';
- } else {
- allowScrolling = 'hidden';
- }
- applyCSS();
- }
+ allowscrollrule(args) {
+ if (args.SCROLLRULE === "enabled") {
+ allowScrolling = "auto";
+ } else {
+ allowScrolling = "hidden";
+ }
+ applyCSS();
+ }
- setvarpos(args, util) {
- setVariableMonitorPosition(
- util.target,
- args.NAME,
- '',
- Scratch.Cast.toNumber(args.X) + Scratch.vm.runtime.stageWidth / 2,
- Scratch.vm.runtime.stageHeight / 2 - Scratch.Cast.toNumber(args.Y)
- );
- }
+ setvarpos(args, util) {
+ setVariableMonitorPosition(
+ util.target,
+ args.NAME,
+ "",
+ Scratch.Cast.toNumber(args.X) + Scratch.vm.runtime.stageWidth / 2,
+ Scratch.vm.runtime.stageHeight / 2 - Scratch.Cast.toNumber(args.Y)
+ );
+ }
- setlistpos(args, util) {
- setVariableMonitorPosition(
- util.target,
- args.NAME,
- 'list',
- Scratch.Cast.toNumber(args.X) + Scratch.vm.runtime.stageWidth / 2,
- Scratch.vm.runtime.stageHeight / 2 - Scratch.Cast.toNumber(args.Y)
- );
- }
+ setlistpos(args, util) {
+ setVariableMonitorPosition(
+ util.target,
+ args.NAME,
+ "list",
+ Scratch.Cast.toNumber(args.X) + Scratch.vm.runtime.stageWidth / 2,
+ Scratch.vm.runtime.stageHeight / 2 - Scratch.Cast.toNumber(args.Y)
+ );
+ }
- help() {
- alert("\nThis is a short introduction to how to use the Monitor Styles extension!\n\n𝗟𝗼𝗼𝗸𝘀 𝗯𝗹𝗼𝗰𝗸𝘀\nThese blocks change the appearance of the variable and list didsplays. You can use the drop-down menu to select what component you want to modify. 𝙏𝙝𝙚 𝙘𝙤𝙡𝙤𝙧 𝙗𝙡𝙤𝙘𝙠 modifieas the color of a component. You can use the 𝙜𝙧𝙖𝙙𝙞𝙚𝙣𝙩 block inside the color input, to create gradients or the 𝙄𝙢𝙖𝙜𝙚 block to use a image instead of solid colors. 𝙏𝙝𝙚𝙨𝙚 𝙩𝙬𝙤 𝙤𝙣𝙡𝙮 𝙬𝙤𝙧𝙠 𝙤𝙣 𝙘𝙚𝙧𝙩𝙖𝙞𝙣 𝙘𝙤𝙢𝙥𝙤𝙣𝙚𝙣𝙩𝙨! You can also use the 𝙩𝙧𝙖𝙣𝙨𝙥𝙖𝙧𝙚𝙣𝙩 𝙗𝙡𝙤𝙘𝙠 as a color input, to make components invisible. The 𝙗𝙤𝙧𝙙𝙚𝙧 𝙗𝙡𝙤𝙘𝙠𝙨 modify the borders of components.\n\n𝗦𝗲𝗻𝘀𝗶𝗻𝗴 𝗯𝗹𝗼𝗰𝗸𝘀\nThese blocks can change the behaviour of certain components. The 𝙨𝙘𝙧𝙤𝙡𝙡 𝙧𝙪𝙡𝙚 block change the behaviour for lists. On 'auto' they will show the scroll bar, and allow you to school, but on 'hidden', they won't let you do that, and the scroll bar will be hidden.\n\n𝗠𝗼𝘁𝗶𝗼𝗻 𝗯𝗹𝗼𝗰𝗸𝘀\nThese blocks allow you to move variable and list displays around. You need to use their 𝙡𝙖𝙗𝙚𝙡 𝙣𝙖𝙢𝙚. The label name is the text that displays on the monitor. For example, a 'for this sprite only' variable will be like 'Sprite1: my variable'.");
- }
+ help() {
+ alert(
+ "\nThis is a short introduction to how to use the Monitor Styles extension!\n\n𝗟𝗼𝗼𝗸𝘀 𝗯𝗹𝗼𝗰𝗸𝘀\nThese blocks change the appearance of the variable and list didsplays. You can use the drop-down menu to select what component you want to modify. 𝙏𝙝𝙚 𝙘𝙤𝙡𝙤𝙧 𝙗𝙡𝙤𝙘𝙠 modifieas the color of a component. You can use the 𝙜𝙧𝙖𝙙𝙞𝙚𝙣𝙩 block inside the color input, to create gradients or the 𝙄𝙢𝙖𝙜𝙚 block to use a image instead of solid colors. 𝙏𝙝𝙚𝙨𝙚 𝙩𝙬𝙤 𝙤𝙣𝙡𝙮 𝙬𝙤𝙧𝙠 𝙤𝙣 𝙘𝙚𝙧𝙩𝙖𝙞𝙣 𝙘𝙤𝙢𝙥𝙤𝙣𝙚𝙣𝙩𝙨! You can also use the 𝙩𝙧𝙖𝙣𝙨𝙥𝙖𝙧𝙚𝙣𝙩 𝙗𝙡𝙤𝙘𝙠 as a color input, to make components invisible. The 𝙗𝙤𝙧𝙙𝙚𝙧 𝙗𝙡𝙤𝙘𝙠𝙨 modify the borders of components.\n\n𝗦𝗲𝗻𝘀𝗶𝗻𝗴 𝗯𝗹𝗼𝗰𝗸𝘀\nThese blocks can change the behaviour of certain components. The 𝙨𝙘𝙧𝙤𝙡𝙡 𝙧𝙪𝙡𝙚 block change the behaviour for lists. On 'auto' they will show the scroll bar, and allow you to school, but on 'hidden', they won't let you do that, and the scroll bar will be hidden.\n\n𝗠𝗼𝘁𝗶𝗼𝗻 𝗯𝗹𝗼𝗰𝗸𝘀\nThese blocks allow you to move variable and list displays around. You need to use their 𝙡𝙖𝙗𝙚𝙡 𝙣𝙖𝙢𝙚. The label name is the text that displays on the monitor. For example, a 'for this sprite only' variable will be like 'Sprite1: my variable'."
+ );
+ }
- transparentinput() {
- return 'transparent';
- }
+ transparentinput() {
+ return "transparent";
+ }
- pictureinput(args) {
- return `url("${encodeURI(args.URL)}")`;
- }
+ pictureinput(args) {
+ return `url("${encodeURI(args.URL)}")`;
+ }
- clearCSS() {
- monitorText = '';
- monitorBorder = '';
- monitorBackgroundColor = '';
- variableValueBackground = '';
- variableValueTextColor = '';
- listFooterBackground = '';
- listHeaderBackground = '';
- listValueText = '';
- listValueBackground = '';
- variableValueRoundness = -1;
- listValueRoundness = -1;
- monitorBackgroundRoundness = -1;
- monitorBackgroundBorderWidth = -1;
- allowScrolling = '';
- askBackground = '';
- askBackgroundRoundness = -1;
- askBackgroundBorderWidth = -1;
- askButtonBackground = '';
- askButtonRoundness = -1;
- askInputBackground = '';
- askInputRoundness = -1;
- askInputBorderWidth = -1;
- askBoxIcon = '';
- askInputText = '';
- askButtonImage = '';
- askInputBorder = '';
- applyCSS();
- }
+ clearCSS() {
+ monitorText = "";
+ monitorBorder = "";
+ monitorBackgroundColor = "";
+ variableValueBackground = "";
+ variableValueTextColor = "";
+ listFooterBackground = "";
+ listHeaderBackground = "";
+ listValueText = "";
+ listValueBackground = "";
+ variableValueRoundness = -1;
+ listValueRoundness = -1;
+ monitorBackgroundRoundness = -1;
+ monitorBackgroundBorderWidth = -1;
+ allowScrolling = "";
+ askBackground = "";
+ askBackgroundRoundness = -1;
+ askBackgroundBorderWidth = -1;
+ askButtonBackground = "";
+ askButtonRoundness = -1;
+ askInputBackground = "";
+ askInputRoundness = -1;
+ askInputBorderWidth = -1;
+ askBoxIcon = "";
+ askInputText = "";
+ askButtonImage = "";
+ askInputBorder = "";
+ applyCSS();
+ }
- getValue(args) {
- if (args.ITEM === 'monitor text') {
- return monitorText;
- } else if (args.ITEM === 'monitor background') {
- return monitorBackgroundColor;
- } else if (args.ITEM === 'monitor border color') {
- return monitorBorder;
- } else if (args.ITEM === 'variable value background') {
- return variableValueBackground;
- } else if (args.ITEM === 'variable value text') {
- return variableValueTextColor;
- } else if (args.ITEM === 'list header background') {
- return listHeaderBackground;
- } else if (args.ITEM === 'list footer background') {
- return listFooterBackground;
- } else if (args.ITEM === 'list value background') {
- return listValueBackground;
- } else if (args.ITEM === 'list value text') {
- return listValueText;
- } else if (args.ITEM === 'ask prompt background') {
- return askBackground;
- } else if (args.ITEM === 'ask prompt button background') {
- return askButtonBackground;
- } else if (args.ITEM === 'ask prompt input background') {
- return askInputBackground;
- } else if (args.ITEM === 'ask prompt input text') {
- return askInputText;
- } else if (args.ITEM === 'ask prompt input border') {
- return askInputBorder;
- } else if (args.ITEM === 'monitor background border width') {
- return monitorBackgroundBorderWidth;
- } else if (args.ITEM === 'ask prompt background border width') {
- return askBackgroundBorderWidth;
- } else if (args.ITEM === 'ask prompt input border width') {
- return askInputBorderWidth;
- } else if (args.ITEM === 'monitor background roundness') {
- return monitorBackgroundRoundness;
- } else if (args.ITEM === 'variable value roundness') {
- return variableValueRoundness;
- } else if (args.ITEM === 'list value roundness') {
- return listValueRoundness;
- } else if (args.ITEM === 'ask prompt background roundness') {
- return askBackgroundRoundness;
- } else if (args.ITEM === 'ask prompt button roundness') {
- return askButtonRoundness;
- } else if (args.ITEM === 'ask prompt input roundness') {
- return askInputRoundness;
- } else if (args.ITEM === 'ask prompt button image') {
- return askButtonImage;
- } else if (args.ITEM === 'list scrolling') {
- if (allowScrolling === 'auto') {
- return 'enabled';
- } else {
- return 'disabled';
- }
- }
- return '';
- }
+ getValue(args) {
+ if (args.ITEM === "monitor text") {
+ return monitorText;
+ } else if (args.ITEM === "monitor background") {
+ return monitorBackgroundColor;
+ } else if (args.ITEM === "monitor border color") {
+ return monitorBorder;
+ } else if (args.ITEM === "variable value background") {
+ return variableValueBackground;
+ } else if (args.ITEM === "variable value text") {
+ return variableValueTextColor;
+ } else if (args.ITEM === "list header background") {
+ return listHeaderBackground;
+ } else if (args.ITEM === "list footer background") {
+ return listFooterBackground;
+ } else if (args.ITEM === "list value background") {
+ return listValueBackground;
+ } else if (args.ITEM === "list value text") {
+ return listValueText;
+ } else if (args.ITEM === "ask prompt background") {
+ return askBackground;
+ } else if (args.ITEM === "ask prompt button background") {
+ return askButtonBackground;
+ } else if (args.ITEM === "ask prompt input background") {
+ return askInputBackground;
+ } else if (args.ITEM === "ask prompt input text") {
+ return askInputText;
+ } else if (args.ITEM === "ask prompt input border") {
+ return askInputBorder;
+ } else if (args.ITEM === "monitor background border width") {
+ return monitorBackgroundBorderWidth;
+ } else if (args.ITEM === "ask prompt background border width") {
+ return askBackgroundBorderWidth;
+ } else if (args.ITEM === "ask prompt input border width") {
+ return askInputBorderWidth;
+ } else if (args.ITEM === "monitor background roundness") {
+ return monitorBackgroundRoundness;
+ } else if (args.ITEM === "variable value roundness") {
+ return variableValueRoundness;
+ } else if (args.ITEM === "list value roundness") {
+ return listValueRoundness;
+ } else if (args.ITEM === "ask prompt background roundness") {
+ return askBackgroundRoundness;
+ } else if (args.ITEM === "ask prompt button roundness") {
+ return askButtonRoundness;
+ } else if (args.ITEM === "ask prompt input roundness") {
+ return askInputRoundness;
+ } else if (args.ITEM === "ask prompt button image") {
+ return askButtonImage;
+ } else if (args.ITEM === "list scrolling") {
+ if (allowScrolling === "auto") {
+ return "enabled";
+ } else {
+ return "disabled";
+ }
+ }
+ return "";
+ }
- setAskURI(args) {
- return Scratch.canFetch(args.URL).then(allowed => {
- if (allowed) {
- askButtonImage = args.URL;
- applyCSS();
- }
- });
+ setAskURI(args) {
+ return Scratch.canFetch(args.URL).then((allowed) => {
+ if (allowed) {
+ askButtonImage = args.URL;
+ applyCSS();
}
+ });
}
+ }
- Scratch.extensions.register(new MonitorStyles());
+ Scratch.extensions.register(new MonitorStyles());
})(Scratch);
diff --git a/extensions/TheShovel/LZ-String.js b/extensions/TheShovel/LZ-String.js
index 73095e6709..635ed972ef 100644
--- a/extensions/TheShovel/LZ-String.js
+++ b/extensions/TheShovel/LZ-String.js
@@ -1,305 +1,435 @@
// Name: LZ Compress
+// ID: shovellzcompress
// Description: Compress and decompress text using lz-string.
-(function(Scratch) {
- 'use strict';
+(function (Scratch) {
+ "use strict";
- /* eslint-disable */
- // Code from https://github.com/pieroxy/lz-string/
- // MIT License
+ /* eslint-disable */
+ // Code from https://github.com/pieroxy/lz-string/
+ // MIT License
- // Copyright (c) 2013 pieroxy
+ // Copyright (c) 2013 pieroxy
- // 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:
+ // 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 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.
- var LZString = function() {
- var r = String.fromCharCode,
- o = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
- n = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$",
- e = {};
+ // 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.
+ var LZString = (function () {
+ var r = String.fromCharCode,
+ o = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
+ n = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$",
+ e = {};
- function t(r, o) {
- if (!e[r]) {
- e[r] = {};
- for (var n = 0; n < r.length; n++) e[r][r.charAt(n)] = n
- }
- return e[r][o]
+ function t(r, o) {
+ if (!e[r]) {
+ e[r] = {};
+ for (var n = 0; n < r.length; n++) e[r][r.charAt(n)] = n;
+ }
+ return e[r][o];
+ }
+ var i = {
+ compressToBase64: function (r) {
+ if (null == r) return "";
+ var n = i._compress(r, 6, function (r) {
+ return o.charAt(r);
+ });
+ switch (n.length % 4) {
+ default:
+ case 0:
+ return n;
+ case 1:
+ return n + "===";
+ case 2:
+ return n + "==";
+ case 3:
+ return n + "=";
}
- var i = {
- compressToBase64: function(r) {
- if (null == r) return "";
- var n = i._compress(r, 6, function(r) {
- return o.charAt(r)
- });
- switch (n.length % 4) {
- default:
- case 0:
- return n;
- case 1:
- return n + "===";
- case 2:
- return n + "==";
- case 3:
- return n + "="
- }
- },
- decompressFromBase64: function(r) {
- return null == r ? "" : "" == r ? null : i._decompress(r.length, 32, function(n) {
- return t(o, r.charAt(n))
- })
- },
- compressToUTF16: function(o) {
- return null == o ? "" : i._compress(o, 15, function(o) {
- return r(o + 32)
- }) + " "
- },
- decompressFromUTF16: function(r) {
- return null == r ? "" : "" == r ? null : i._decompress(r.length, 16384, function(o) {
- return r.charCodeAt(o) - 32
- })
- },
- compressToUint8Array: function(r) {
- for (var o = i.compress(r), n = new Uint8Array(2 * o.length), e = 0, t = o.length; e < t; e++) {
- var s = o.charCodeAt(e);
- n[2 * e] = s >>> 8, n[2 * e + 1] = s % 256
- }
- return n
- },
- decompressFromUint8Array: function(o) {
- if (null == o) return i.decompress(o);
- for (var n = new Array(o.length / 2), e = 0, t = n.length; e < t; e++) n[e] = 256 * o[2 * e] + o[2 * e + 1];
- var s = [];
- return n.forEach(function(o) {
- s.push(r(o))
- }), i.decompress(s.join(""))
- },
- compressToEncodedURIComponent: function(r) {
- return null == r ? "" : i._compress(r, 6, function(r) {
- return n.charAt(r)
- })
- },
- decompressFromEncodedURIComponent: function(r) {
- return null == r ? "" : "" == r ? null : (r = r.replace(/ /g, "+"), i._decompress(r.length, 32, function(o) {
- return t(n, r.charAt(o))
- }))
- },
- compress: function(o) {
- return i._compress(o, 16, function(o) {
- return r(o)
- })
- },
- _compress: function(r, o, n) {
- if (null == r) return "";
- var e, t, i, s = {},
- u = {},
- a = "",
- p = "",
- c = "",
- l = 2,
- f = 3,
- h = 2,
- d = [],
- m = 0,
- v = 0;
- for (i = 0; i < r.length; i += 1)
- if (a = r.charAt(i), Object.prototype.hasOwnProperty.call(s, a) || (s[a] = f++, u[a] = !0), p = c + a, Object.prototype.hasOwnProperty.call(s, p)) c = p;
- else {
- if (Object.prototype.hasOwnProperty.call(u, c)) {
- if (c.charCodeAt(0) < 256) {
- for (e = 0; e < h; e++) m <<= 1, v == o - 1 ? (v = 0, d.push(n(m)), m = 0) : v++;
- for (t = c.charCodeAt(0), e = 0; e < 8; e++) m = m << 1 | 1 & t, v == o - 1 ? (v = 0, d.push(n(m)), m = 0) : v++, t >>= 1
- } else {
- for (t = 1, e = 0; e < h; e++) m = m << 1 | t, v == o - 1 ? (v = 0, d.push(n(m)), m = 0) : v++, t = 0;
- for (t = c.charCodeAt(0), e = 0; e < 16; e++) m = m << 1 | 1 & t, v == o - 1 ? (v = 0, d.push(n(m)), m = 0) : v++, t >>= 1
- }
- 0 == --l && (l = Math.pow(2, h), h++), delete u[c]
- } else
- for (t = s[c], e = 0; e < h; e++) m = m << 1 | 1 & t, v == o - 1 ? (v = 0, d.push(n(m)), m = 0) : v++, t >>= 1;
- 0 == --l && (l = Math.pow(2, h), h++), s[p] = f++, c = String(a)
- } if ("" !== c) {
- if (Object.prototype.hasOwnProperty.call(u, c)) {
- if (c.charCodeAt(0) < 256) {
- for (e = 0; e < h; e++) m <<= 1, v == o - 1 ? (v = 0, d.push(n(m)), m = 0) : v++;
- for (t = c.charCodeAt(0), e = 0; e < 8; e++) m = m << 1 | 1 & t, v == o - 1 ? (v = 0, d.push(n(m)), m = 0) : v++, t >>= 1
- } else {
- for (t = 1, e = 0; e < h; e++) m = m << 1 | t, v == o - 1 ? (v = 0, d.push(n(m)), m = 0) : v++, t = 0;
- for (t = c.charCodeAt(0), e = 0; e < 16; e++) m = m << 1 | 1 & t, v == o - 1 ? (v = 0, d.push(n(m)), m = 0) : v++, t >>= 1
- }
- 0 == --l && (l = Math.pow(2, h), h++), delete u[c]
- } else
- for (t = s[c], e = 0; e < h; e++) m = m << 1 | 1 & t, v == o - 1 ? (v = 0, d.push(n(m)), m = 0) : v++, t >>= 1;
- 0 == --l && (l = Math.pow(2, h), h++)
- }
- for (t = 2, e = 0; e < h; e++) m = m << 1 | 1 & t, v == o - 1 ? (v = 0, d.push(n(m)), m = 0) : v++, t >>= 1;
- for (;;) {
- if (m <<= 1, v == o - 1) {
- d.push(n(m));
- break
- }
- v++
- }
- return d.join("")
- },
- decompress: function(r) {
- return null == r ? "" : "" == r ? null : i._decompress(r.length, 32768, function(o) {
- return r.charCodeAt(o)
- })
- },
- _decompress: function(o, n, e) {
- var t, i, s, u, a, p, c, l = [],
- f = 4,
- h = 4,
- d = 3,
- m = "",
- v = [],
- g = {
- val: e(0),
- position: n,
- index: 1
- };
- for (t = 0; t < 3; t += 1) l[t] = t;
- for (s = 0, a = Math.pow(2, 2), p = 1; p != a;) u = g.val & g.position, g.position >>= 1, 0 == g.position && (g.position = n, g.val = e(g.index++)), s |= (u > 0 ? 1 : 0) * p, p <<= 1;
- switch (s) {
- case 0:
- for (s = 0, a = Math.pow(2, 8), p = 1; p != a;) u = g.val & g.position, g.position >>= 1, 0 == g.position && (g.position = n, g.val = e(g.index++)), s |= (u > 0 ? 1 : 0) * p, p <<= 1;
- c = r(s);
- break;
- case 1:
- for (s = 0, a = Math.pow(2, 16), p = 1; p != a;) u = g.val & g.position, g.position >>= 1, 0 == g.position && (g.position = n, g.val = e(g.index++)), s |= (u > 0 ? 1 : 0) * p, p <<= 1;
- c = r(s);
- break;
- case 2:
- return ""
- }
- for (l[3] = c, i = c, v.push(c);;) {
- if (g.index > o) return "";
- for (s = 0, a = Math.pow(2, d), p = 1; p != a;) u = g.val & g.position, g.position >>= 1, 0 == g.position && (g.position = n, g.val = e(g.index++)), s |= (u > 0 ? 1 : 0) * p, p <<= 1;
- switch (c = s) {
- case 0:
- for (s = 0, a = Math.pow(2, 8), p = 1; p != a;) u = g.val & g.position, g.position >>= 1, 0 == g.position && (g.position = n, g.val = e(g.index++)), s |= (u > 0 ? 1 : 0) * p, p <<= 1;
- l[h++] = r(s), c = h - 1, f--;
- break;
- case 1:
- for (s = 0, a = Math.pow(2, 16), p = 1; p != a;) u = g.val & g.position, g.position >>= 1, 0 == g.position && (g.position = n, g.val = e(g.index++)), s |= (u > 0 ? 1 : 0) * p, p <<= 1;
- l[h++] = r(s), c = h - 1, f--;
- break;
- case 2:
- return v.join("")
- }
- // @ts-ignore
- if (0 == f && (f = Math.pow(2, d), d++), l[c]) m = l[c];
- else {
- if (c !== h) return null;
- m = i + i.charAt(0)
- }
- v.push(m), l[h++] = i + m.charAt(0), i = m, 0 == --f && (f = Math.pow(2, d), d++)
- }
+ },
+ decompressFromBase64: function (r) {
+ return null == r
+ ? ""
+ : "" == r
+ ? null
+ : i._decompress(r.length, 32, function (n) {
+ return t(o, r.charAt(n));
+ });
+ },
+ compressToUTF16: function (o) {
+ return null == o
+ ? ""
+ : i._compress(o, 15, function (o) {
+ return r(o + 32);
+ }) + " ";
+ },
+ decompressFromUTF16: function (r) {
+ return null == r
+ ? ""
+ : "" == r
+ ? null
+ : i._decompress(r.length, 16384, function (o) {
+ return r.charCodeAt(o) - 32;
+ });
+ },
+ compressToUint8Array: function (r) {
+ for (
+ var o = i.compress(r),
+ n = new Uint8Array(2 * o.length),
+ e = 0,
+ t = o.length;
+ e < t;
+ e++
+ ) {
+ var s = o.charCodeAt(e);
+ (n[2 * e] = s >>> 8), (n[2 * e + 1] = s % 256);
+ }
+ return n;
+ },
+ decompressFromUint8Array: function (o) {
+ if (null == o) return i.decompress(o);
+ for (var n = new Array(o.length / 2), e = 0, t = n.length; e < t; e++)
+ n[e] = 256 * o[2 * e] + o[2 * e + 1];
+ var s = [];
+ return (
+ n.forEach(function (o) {
+ s.push(r(o));
+ }),
+ i.decompress(s.join(""))
+ );
+ },
+ compressToEncodedURIComponent: function (r) {
+ return null == r
+ ? ""
+ : i._compress(r, 6, function (r) {
+ return n.charAt(r);
+ });
+ },
+ decompressFromEncodedURIComponent: function (r) {
+ return null == r
+ ? ""
+ : "" == r
+ ? null
+ : ((r = r.replace(/ /g, "+")),
+ i._decompress(r.length, 32, function (o) {
+ return t(n, r.charAt(o));
+ }));
+ },
+ compress: function (o) {
+ return i._compress(o, 16, function (o) {
+ return r(o);
+ });
+ },
+ _compress: function (r, o, n) {
+ if (null == r) return "";
+ var e,
+ t,
+ i,
+ s = {},
+ u = {},
+ a = "",
+ p = "",
+ c = "",
+ l = 2,
+ f = 3,
+ h = 2,
+ d = [],
+ m = 0,
+ v = 0;
+ for (i = 0; i < r.length; i += 1)
+ if (
+ ((a = r.charAt(i)),
+ Object.prototype.hasOwnProperty.call(s, a) ||
+ ((s[a] = f++), (u[a] = !0)),
+ (p = c + a),
+ Object.prototype.hasOwnProperty.call(s, p))
+ )
+ c = p;
+ else {
+ if (Object.prototype.hasOwnProperty.call(u, c)) {
+ if (c.charCodeAt(0) < 256) {
+ for (e = 0; e < h; e++)
+ (m <<= 1),
+ v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++;
+ for (t = c.charCodeAt(0), e = 0; e < 8; e++)
+ (m = (m << 1) | (1 & t)),
+ v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++,
+ (t >>= 1);
+ } else {
+ for (t = 1, e = 0; e < h; e++)
+ (m = (m << 1) | t),
+ v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++,
+ (t = 0);
+ for (t = c.charCodeAt(0), e = 0; e < 16; e++)
+ (m = (m << 1) | (1 & t)),
+ v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++,
+ (t >>= 1);
+ }
+ 0 == --l && ((l = Math.pow(2, h)), h++), delete u[c];
+ } else
+ for (t = s[c], e = 0; e < h; e++)
+ (m = (m << 1) | (1 & t)),
+ v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++,
+ (t >>= 1);
+ 0 == --l && ((l = Math.pow(2, h)), h++),
+ (s[p] = f++),
+ (c = String(a));
+ }
+ if ("" !== c) {
+ if (Object.prototype.hasOwnProperty.call(u, c)) {
+ if (c.charCodeAt(0) < 256) {
+ for (e = 0; e < h; e++)
+ (m <<= 1), v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++;
+ for (t = c.charCodeAt(0), e = 0; e < 8; e++)
+ (m = (m << 1) | (1 & t)),
+ v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++,
+ (t >>= 1);
+ } else {
+ for (t = 1, e = 0; e < h; e++)
+ (m = (m << 1) | t),
+ v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++,
+ (t = 0);
+ for (t = c.charCodeAt(0), e = 0; e < 16; e++)
+ (m = (m << 1) | (1 & t)),
+ v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++,
+ (t >>= 1);
}
- };
- return i
- }();
- // @ts-ignore
- "function" == typeof define && define.amd ? define(function() {
- return LZString
- // @ts-ignore
- }) : "undefined" != typeof module && null != module ? module.exports = LZString : "undefined" != typeof angular && null != angular && angular.module("LZString", []).factory("LZString", function() {
- return LZString
- });
- /* eslint-enable */
-
- class lzcompress {
- getInfo() {
- return {
- id: 'shovellzcompress',
- name: 'LZ Compress',
- blocks: [{
- opcode: 'compress',
- blockType: Scratch.BlockType.REPORTER,
- text: 'compress [TEXT] to [TYPE]',
- arguments: {
- TEXT: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Hello world!'
- },
- TYPE: {
- type: Scratch.ArgumentType.STRING,
- menu: 'COMPRESSIONTYPES'
- }
- }
- },
- {
- opcode: 'decompress',
- blockType: Scratch.BlockType.REPORTER,
- text: 'decompress [TEXT] from [TYPE]',
- arguments: {
- TEXT: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: '҅〶惶@✰Ӏ葀'
- },
- TYPE: {
- type: Scratch.ArgumentType.STRING,
- menu: 'COMPRESSIONTYPES'
- }
- }
- }
- ],
- menus: {
- COMPRESSIONTYPES: {
- acceptReporters: true,
- items: ['Raw', 'Base64', 'EncodedURIComponent', 'Uint8Array', 'UTF16']
- }
- }
- };
+ 0 == --l && ((l = Math.pow(2, h)), h++), delete u[c];
+ } else
+ for (t = s[c], e = 0; e < h; e++)
+ (m = (m << 1) | (1 & t)),
+ v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++,
+ (t >>= 1);
+ 0 == --l && ((l = Math.pow(2, h)), h++);
}
- compress(args) {
- const text = Scratch.Cast.toString(args.TEXT);
- if (args.TYPE == 'Raw') {
- return LZString.compress(text);
- } else if (args.TYPE == 'Base64') {
- return LZString.compressToBase64(text);
- } else if (args.TYPE == 'EncodedURIComponent') {
- return LZString.compressToEncodedURIComponent(text);
- } else if (args.TYPE == 'Uint8Array') {
- return LZString.compressToUint8Array(text);
- } else if (args.TYPE == 'UTF16') {
- return LZString.compressToUTF16(text);
- } return '';
+ for (t = 2, e = 0; e < h; e++)
+ (m = (m << 1) | (1 & t)),
+ v == o - 1 ? ((v = 0), d.push(n(m)), (m = 0)) : v++,
+ (t >>= 1);
+ for (;;) {
+ if (((m <<= 1), v == o - 1)) {
+ d.push(n(m));
+ break;
+ }
+ v++;
}
+ return d.join("");
+ },
+ decompress: function (r) {
+ return null == r
+ ? ""
+ : "" == r
+ ? null
+ : i._decompress(r.length, 32768, function (o) {
+ return r.charCodeAt(o);
+ });
+ },
+ _decompress: function (o, n, e) {
+ var t,
+ i,
+ s,
+ u,
+ a,
+ p,
+ c,
+ l = [],
+ f = 4,
+ h = 4,
+ d = 3,
+ m = "",
+ v = [],
+ g = {
+ val: e(0),
+ position: n,
+ index: 1,
+ };
+ for (t = 0; t < 3; t += 1) l[t] = t;
+ for (s = 0, a = Math.pow(2, 2), p = 1; p != a; )
+ (u = g.val & g.position),
+ (g.position >>= 1),
+ 0 == g.position && ((g.position = n), (g.val = e(g.index++))),
+ (s |= (u > 0 ? 1 : 0) * p),
+ (p <<= 1);
+ switch (s) {
+ case 0:
+ for (s = 0, a = Math.pow(2, 8), p = 1; p != a; )
+ (u = g.val & g.position),
+ (g.position >>= 1),
+ 0 == g.position && ((g.position = n), (g.val = e(g.index++))),
+ (s |= (u > 0 ? 1 : 0) * p),
+ (p <<= 1);
+ c = r(s);
+ break;
+ case 1:
+ for (s = 0, a = Math.pow(2, 16), p = 1; p != a; )
+ (u = g.val & g.position),
+ (g.position >>= 1),
+ 0 == g.position && ((g.position = n), (g.val = e(g.index++))),
+ (s |= (u > 0 ? 1 : 0) * p),
+ (p <<= 1);
+ c = r(s);
+ break;
+ case 2:
+ return "";
+ }
+ for (l[3] = c, i = c, v.push(c); ; ) {
+ if (g.index > o) return "";
+ for (s = 0, a = Math.pow(2, d), p = 1; p != a; )
+ (u = g.val & g.position),
+ (g.position >>= 1),
+ 0 == g.position && ((g.position = n), (g.val = e(g.index++))),
+ (s |= (u > 0 ? 1 : 0) * p),
+ (p <<= 1);
+ switch ((c = s)) {
+ case 0:
+ for (s = 0, a = Math.pow(2, 8), p = 1; p != a; )
+ (u = g.val & g.position),
+ (g.position >>= 1),
+ 0 == g.position && ((g.position = n), (g.val = e(g.index++))),
+ (s |= (u > 0 ? 1 : 0) * p),
+ (p <<= 1);
+ (l[h++] = r(s)), (c = h - 1), f--;
+ break;
+ case 1:
+ for (s = 0, a = Math.pow(2, 16), p = 1; p != a; )
+ (u = g.val & g.position),
+ (g.position >>= 1),
+ 0 == g.position && ((g.position = n), (g.val = e(g.index++))),
+ (s |= (u > 0 ? 1 : 0) * p),
+ (p <<= 1);
+ (l[h++] = r(s)), (c = h - 1), f--;
+ break;
+ case 2:
+ return v.join("");
+ }
+ // @ts-ignore
+ if ((0 == f && ((f = Math.pow(2, d)), d++), l[c])) m = l[c];
+ else {
+ if (c !== h) return null;
+ m = i + i.charAt(0);
+ }
+ v.push(m),
+ (l[h++] = i + m.charAt(0)),
+ (i = m),
+ 0 == --f && ((f = Math.pow(2, d)), d++);
+ }
+ },
+ };
+ return i;
+ })();
+ // @ts-ignore
+ "function" == typeof define && define.amd
+ ? define(function () {
+ return LZString;
+ // @ts-ignore
+ })
+ : "undefined" != typeof module && null != module
+ ? (module.exports = LZString)
+ : "undefined" != typeof angular &&
+ null != angular &&
+ angular.module("LZString", []).factory("LZString", function () {
+ return LZString;
+ });
+ /* eslint-enable */
- decompress(args) {
- try {
- const text = Scratch.Cast.toString(args.TEXT);
- if (args.TYPE == 'Raw') {
- return LZString.decompress(text) || '';
- } else if (args.TYPE == 'Base64') {
- return LZString.decompressFromBase64(text) || '';
- } else if (args.TYPE == 'EncodedURIComponent') {
- return LZString.decompressFromEncodedURIComponent(text) || '';
- } else if (args.TYPE == 'Uint8Array') {
- return LZString.decompressFromUint8Array(text) || '';
- } else if (args.TYPE == 'UTF16') {
- return LZString.decompressFromUTF16(text) || '';
- }
- } catch (e) {
- console.error('decompress error', e);
- }
- return '';
+ class lzcompress {
+ getInfo() {
+ return {
+ id: "shovellzcompress",
+ name: "LZ Compress",
+ blocks: [
+ {
+ opcode: "compress",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "compress [TEXT] to [TYPE]",
+ arguments: {
+ TEXT: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Hello world!",
+ },
+ TYPE: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "COMPRESSIONTYPES",
+ },
+ },
+ },
+ {
+ opcode: "decompress",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "decompress [TEXT] from [TYPE]",
+ arguments: {
+ TEXT: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "҅〶惶@✰Ӏ葀",
+ },
+ TYPE: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "COMPRESSIONTYPES",
+ },
+ },
+ },
+ ],
+ menus: {
+ COMPRESSIONTYPES: {
+ acceptReporters: true,
+ items: [
+ "Raw",
+ "Base64",
+ "EncodedURIComponent",
+ "Uint8Array",
+ "UTF16",
+ ],
+ },
+ },
+ };
+ }
+ compress(args) {
+ const text = Scratch.Cast.toString(args.TEXT);
+ if (args.TYPE == "Raw") {
+ return LZString.compress(text);
+ } else if (args.TYPE == "Base64") {
+ return LZString.compressToBase64(text);
+ } else if (args.TYPE == "EncodedURIComponent") {
+ return LZString.compressToEncodedURIComponent(text);
+ } else if (args.TYPE == "Uint8Array") {
+ return LZString.compressToUint8Array(text);
+ } else if (args.TYPE == "UTF16") {
+ return LZString.compressToUTF16(text);
+ }
+ return "";
+ }
+
+ decompress(args) {
+ try {
+ const text = Scratch.Cast.toString(args.TEXT);
+ if (args.TYPE == "Raw") {
+ return LZString.decompress(text) || "";
+ } else if (args.TYPE == "Base64") {
+ return LZString.decompressFromBase64(text) || "";
+ } else if (args.TYPE == "EncodedURIComponent") {
+ return LZString.decompressFromEncodedURIComponent(text) || "";
+ } else if (args.TYPE == "Uint8Array") {
+ return LZString.decompressFromUint8Array(text) || "";
+ } else if (args.TYPE == "UTF16") {
+ return LZString.decompressFromUTF16(text) || "";
}
+ } catch (e) {
+ console.error("decompress error", e);
+ }
+ return "";
}
- Scratch.extensions.register(new lzcompress());
+ }
+ Scratch.extensions.register(new lzcompress());
})(Scratch);
diff --git a/extensions/TheShovel/ShovelUtils.js b/extensions/TheShovel/ShovelUtils.js
index f667a302c9..fff74dc116 100644
--- a/extensions/TheShovel/ShovelUtils.js
+++ b/extensions/TheShovel/ShovelUtils.js
@@ -1,11 +1,12 @@
// Name: ShovelUtils
+// ID: ShovelUtils
// Description: A bunch of miscellaneous blocks.
// By: TheShovel
(function (Scratch) {
- 'use strict';
+ "use strict";
if (!Scratch.extensions.unsandboxed) {
- throw new Error('ShovelUtils must be run unsandboxed');
+ throw new Error("ShovelUtils must be run unsandboxed");
}
console.log("ShovelUtils v1.4");
const vm = Scratch.vm;
@@ -27,174 +28,176 @@
class ShovelUtils {
getInfo() {
return {
- id: 'ShovelUtils',
- name: 'ShovelUtils',
- color1: '#f54242',
- color2: '#f54242',
- color3: '#f54242',
+ id: "ShovelUtils",
+ name: "ShovelUtils",
+ color1: "#f54242",
+ color2: "#f54242",
+ color3: "#f54242",
+ docsURI: "https://extensions.turbowarp.org/TheShovel/ShovelUtils",
blocks: [
{
- opcode: 'importImage',
+ opcode: "importImage",
blockType: Scratch.BlockType.COMMAND,
- text: 'Import image from [TEXT] name [NAME]',
+ text: "Import image from [TEXT] name [NAME]",
arguments: {
TEXT: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'https://extensions.turbowarp.org/dango.png',
+ defaultValue: "https://extensions.turbowarp.org/dango.png",
},
NAME: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'Dango',
- }
- }
+ defaultValue: "Dango",
+ },
+ },
},
{
- opcode: 'getlist',
+ opcode: "getlist",
blockType: Scratch.BlockType.REPORTER,
- text: 'Get list [TEXT]',
+ text: "Get list [TEXT]",
arguments: {
TEXT: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'MyList',
- }
- }
+ defaultValue: "MyList",
+ },
+ },
},
{
- opcode: 'setlist',
+ opcode: "setlist",
blockType: Scratch.BlockType.COMMAND,
- text: 'Set list [NAME] to [TEXT]',
+ text: "Set list [NAME] to [TEXT]",
arguments: {
TEXT: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '[1,2]',
+ defaultValue: "[1,2]",
},
NAME: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'MyList',
- }
- }
+ defaultValue: "MyList",
+ },
+ },
},
{
- opcode: 'importSprite',
+ opcode: "importSprite",
blockType: Scratch.BlockType.COMMAND,
- text: 'Import sprite from [TEXT]',
+ text: "Import sprite from [TEXT]",
arguments: {
TEXT: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'Link or data uri here',
- }
- }
+ defaultValue: "Link or data uri here",
+ },
+ },
},
{
- opcode: 'importSound',
+ opcode: "importSound",
blockType: Scratch.BlockType.COMMAND,
- text: 'Import sound from [TEXT] name [NAME]',
+ text: "Import sound from [TEXT] name [NAME]",
arguments: {
TEXT: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'https://extensions.turbowarp.org/meow.mp3',
+ defaultValue: "https://extensions.turbowarp.org/meow.mp3",
},
NAME: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'Meow',
- }
- }
+ defaultValue: "Meow",
+ },
+ },
},
{
- opcode: 'importProject',
+ opcode: "importProject",
blockType: Scratch.BlockType.COMMAND,
- text: 'Import project from [TEXT]',
+ text: "Import project from [TEXT]",
arguments: {
TEXT: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'https://theshovel.github.io/Bullet-Hell/Bullet%20Hell',
- }
- }
+ defaultValue:
+ "https://theshovel.github.io/Bullet-Hell/Bullet%20Hell",
+ },
+ },
},
{
- opcode: 'loadExtension',
+ opcode: "loadExtension",
blockType: Scratch.BlockType.COMMAND,
- text: 'Load extension from [TEXT]',
+ text: "Load extension from [TEXT]",
arguments: {
TEXT: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'https://extensions.turbowarp.org/utilities.js',
- }
- }
+ defaultValue: "https://extensions.turbowarp.org/utilities.js",
+ },
+ },
},
{
- opcode: 'restartProject',
+ opcode: "restartProject",
blockType: Scratch.BlockType.COMMAND,
- text: 'Restart project',
+ text: "Restart project",
arguments: {
TEXT: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '0',
- }
- }
+ defaultValue: "0",
+ },
+ },
},
{
- opcode: 'deleteSprite',
+ opcode: "deleteSprite",
blockType: Scratch.BlockType.COMMAND,
- text: 'Delete sprite [SPRITE]',
+ text: "Delete sprite [SPRITE]",
arguments: {
SPRITE: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'Sprite1',
- }
- }
+ defaultValue: "Sprite1",
+ },
+ },
},
{
- opcode: 'deleteImage',
+ opcode: "deleteImage",
blockType: Scratch.BlockType.COMMAND,
- text: 'Delete costume [COSNAME] in [SPRITE]',
+ text: "Delete costume [COSNAME] in [SPRITE]",
arguments: {
COSNAME: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'costume1'
+ defaultValue: "costume1",
},
SPRITE: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'Sprite1'
- }
- }
+ defaultValue: "Sprite1",
+ },
+ },
},
{
- opcode: 'setedtarget',
+ opcode: "setedtarget",
blockType: Scratch.BlockType.COMMAND,
- text: 'Set editing target to [NAME]',
+ text: "Set editing target to [NAME]",
arguments: {
NAME: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'Sprite1',
- }
- }
+ defaultValue: "Sprite1",
+ },
+ },
},
{
- opcode: 'brightnessByColor',
+ opcode: "brightnessByColor",
blockType: Scratch.BlockType.REPORTER,
- text: 'Get brightness of [color]',
+ text: "Get brightness of [color]",
arguments: {
color: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '#ffffff',
- }
- }
+ defaultValue: "#ffffff",
+ },
+ },
},
{
- opcode: 'getAllSprites',
+ opcode: "getAllSprites",
blockType: Scratch.BlockType.REPORTER,
- text: 'get all sprites'
+ text: "get all sprites",
},
{
- opcode: 'getfps',
+ opcode: "getfps",
blockType: Scratch.BlockType.REPORTER,
- text: 'Fps'
+ text: "Fps",
},
- ]
+ ],
};
}
@@ -203,23 +206,23 @@
.then((r) => r.arrayBuffer())
.then((arrayBuffer) => {
const storage = vm.runtime.storage;
- vm.addCostume(NAME + '.PNG', {
- name: NAME + '',
+ vm.addCostume(NAME + ".PNG", {
+ name: NAME + "",
asset: new storage.Asset(
storage.AssetType.ImageBitmap,
null, // asset id, doesn't need to be set here because of `true` at the end will make Scratch generate it for you
storage.DataFormat.PNG,
new Uint8Array(arrayBuffer),
true
- )
+ ),
});
});
}
importSprite({ TEXT }) {
Scratch.fetch(TEXT)
- .then(r => r.arrayBuffer())
- .then(buffer => vm.addSprite(buffer))
+ .then((r) => r.arrayBuffer())
+ .then((buffer) => vm.addSprite(buffer))
.then(() => {
console.log("Done");
})
@@ -234,8 +237,12 @@
return;
}
// @ts-expect-error
- if (typeof ScratchBlocks !== 'undefined') {
- if (!confirm(`Do you want to delete the sprite "${SPRITE}"? This cannot be undone.`)) {
+ if (typeof ScratchBlocks !== "undefined") {
+ if (
+ !confirm(
+ `Do you want to delete the sprite "${SPRITE}"? This cannot be undone.`
+ )
+ ) {
return;
}
}
@@ -255,24 +262,28 @@
true
);
vm.addSound({
- md5: asset.assetId + '.' + asset.dataFormat,
+ md5: asset.assetId + "." + asset.dataFormat,
asset: asset,
- name: NAME + ''
+ name: NAME + "",
});
});
}
importProject({ TEXT }) {
// @ts-ignore
- if (typeof ScratchBlocks !== 'undefined') {
+ if (typeof ScratchBlocks !== "undefined") {
// We are in the editor. Ask before loading a new project to avoid unrecoverable data loss.
- if (!confirm(`Do you want to import a project from "${TEXT}"? Everything in the current project will be permanently deleted.`)) {
+ if (
+ !confirm(
+ `Do you want to import a project from "${TEXT}"? Everything in the current project will be permanently deleted.`
+ )
+ ) {
return;
}
}
Scratch.fetch(TEXT)
- .then(r => r.arrayBuffer())
- .then(buffer => vm.loadProject(buffer))
+ .then((r) => r.arrayBuffer())
+ .then((buffer) => vm.loadProject(buffer))
.then(() => {
console.log("Done");
vm.greenFlag();
@@ -293,7 +304,9 @@
}
getlist({ TEXT }) {
- const list = vm.runtime.getTargetForStage().lookupVariableByNameAndType(TEXT, 'list');
+ const list = vm.runtime
+ .getTargetForStage()
+ .lookupVariableByNameAndType(TEXT, "list");
if (list) {
return JSON.stringify(list.value);
} else {
@@ -319,7 +332,9 @@
}
}
- const list = vm.runtime.getTargetForStage().lookupVariableByNameAndType(NAME, 'list');
+ const list = vm.runtime
+ .getTargetForStage()
+ .lookupVariableByNameAndType(NAME, "list");
if (!list) {
return; // List was not found
}
@@ -348,15 +363,15 @@
*/
brightnessByColor({ color }) {
// https://www.w3.org/TR/AERT/#color-contrast
- const {r, g, b} = Scratch.Cast.toRgbColorObject(color);
- return ((r * 299) + (g * 587) + (b * 114)) / 1000;
+ const { r, g, b } = Scratch.Cast.toRgbColorObject(color);
+ return (r * 299 + g * 587 + b * 114) / 1000;
}
- getfps(){
+ getfps() {
return fps;
}
- deleteImage({ SPRITE, COSNAME }){
+ deleteImage({ SPRITE, COSNAME }) {
// 0znzw, since shovel did not add it yet.
const target = vm.runtime.getSpriteTargetByName(SPRITE);
if (!target) {
@@ -365,7 +380,7 @@
target.deleteCostume(target.getCostumeIndexByName(COSNAME));
}
- getAllSprites(){
+ getAllSprites() {
// 0znzw, since shovel did not add it yet.
let sprites = [];
for (const target of vm.runtime.targets) {
@@ -375,5 +390,5 @@
}
}
Scratch.extensions.register(new ShovelUtils());
-// @ts-ignore
+ // @ts-ignore
})(Scratch);
diff --git a/extensions/TheShovel/profanity.js b/extensions/TheShovel/profanity.js
index 028023e4db..337dc397dc 100644
--- a/extensions/TheShovel/profanity.js
+++ b/extensions/TheShovel/profanity.js
@@ -1,8 +1,18 @@
-(function(Scratch) {
- 'use strict';
+(function (Scratch) {
+ "use strict";
- const encode = (str) => btoa(str).split('').map((i) => String.fromCharCode(i.charCodeAt(0) + 1)).join('');
- const decode = (str) => atob(str.split('').map((i) => String.fromCharCode(i.charCodeAt(0) - 1)).join(''));
+ const encode = (str) =>
+ btoa(str)
+ .split("")
+ .map((i) => String.fromCharCode(i.charCodeAt(0) + 1))
+ .join("");
+ const decode = (str) =>
+ atob(
+ str
+ .split("")
+ .map((i) => String.fromCharCode(i.charCodeAt(0) - 1))
+ .join("")
+ );
// A forewarning for the reader:
// This list contains some very bad naughty words, so we've encoded it in a way that
@@ -214,44 +224,44 @@
"e3iwdnV>",
"e3m{[XG{dx>>",
"e3m{[XG{d3W{",
- "e3:x"
+ "e3:x",
].map(decode);
// Put the longest words first so that if "test" and "tests" are in the word list in
// that order, redacting "tests" will give "***" instead of "***s"
NAUGHTY_WORDS.sort((a, b) => b.length - a.length);
- const regex = new RegExp(NAUGHTY_WORDS.join('|'), 'gi');
+ const regex = new RegExp(NAUGHTY_WORDS.join("|"), "gi");
class Profanity {
- getInfo () {
+ getInfo() {
return {
- id: 'theshovelprofanity',
- name: 'Bad Word Remover',
- color1: '#cf6a3c',
- color2: '#cf6a3c',
- color3: '#cf6a3c',
+ id: "theshovelprofanity",
+ name: "Bad Word Remover",
+ color1: "#cf6a3c",
+ color2: "#cf6a3c",
+ color3: "#cf6a3c",
blocks: [
{
- opcode: 'checkProfanity',
+ opcode: "checkProfanity",
blockType: Scratch.BlockType.REPORTER,
- text: 'replace bad words in [TEXT] with [REPLACEMENT]',
+ text: "replace bad words in [TEXT] with [REPLACEMENT]",
arguments: {
REPLACEMENT: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '***',
+ defaultValue: "***",
},
TEXT: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'Hello!',
- }
- }
+ defaultValue: "Hello!",
+ },
+ },
},
- ]
+ ],
};
}
- checkProfanity({TEXT, REPLACEMENT}) {
+ checkProfanity({ TEXT, REPLACEMENT }) {
// Use a function as the second argument so that replacing with "$&" does not allow
// bypass.
return String(TEXT).replace(regex, () => REPLACEMENT);
diff --git a/extensions/TheShovel/profanityAPI.js b/extensions/TheShovel/profanityAPI.js
index ce2cef49fc..773158a22b 100644
--- a/extensions/TheShovel/profanityAPI.js
+++ b/extensions/TheShovel/profanityAPI.js
@@ -1,33 +1,35 @@
-(function(Scratch) {
- 'use strict';
+(function (Scratch) {
+ "use strict";
class profanityAPI {
- getInfo () {
+ getInfo() {
return {
- id: 'profanityAPI',
- name: 'profanityAPI',
- color1: '#cf6a3c',
- color2: '#cf6a3c',
- color3: '#cf6a3c',
+ id: "profanityAPI",
+ name: "profanityAPI",
+ color1: "#cf6a3c",
+ color2: "#cf6a3c",
+ color3: "#cf6a3c",
blocks: [
{
- opcode: 'checkProfanity',
+ opcode: "checkProfanity",
blockType: Scratch.BlockType.REPORTER,
text: "Remove profanity from [TEXT]",
arguments: {
TEXT: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'Hello, I love pizza!',
- }
- }
+ defaultValue: "Hello, I love pizza!",
+ },
+ },
},
- ]
+ ],
};
}
- checkProfanity({TEXT}) {
- return Scratch.fetch("https://www.purgomalum.com/service/plain?text=" + TEXT)
- .then(r => r.text())
- .catch(() => '');
+ checkProfanity({ TEXT }) {
+ return Scratch.fetch(
+ "https://www.purgomalum.com/service/plain?text=" + TEXT
+ )
+ .then((r) => r.text())
+ .catch(() => "");
}
}
diff --git a/extensions/WP-Studio01/text2speech.js b/extensions/WP-Studio01/text2speech.js
index 74b72c3c86..a51c7cb015 100644
--- a/extensions/WP-Studio01/text2speech.js
+++ b/extensions/WP-Studio01/text2speech.js
@@ -1,40 +1,40 @@
-(function(Scratch) {
- 'use strict';
-
- class Tools {
- getInfo () {
- return {
- id: 'wpstudio01tts',
- name: 'System Text To Speech',
- blocks: [
- {
- opcode: 'speak',
- blockType: Scratch.BlockType.COMMAND,
- text: 'speak [TEXT]',
- arguments: {
- TEXT: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Hello'
- }
- }
- }
- ]
- };
- }
-
- speak (args) {
- return new Promise((resolve, reject) => {
- const utterance = new SpeechSynthesisUtterance(args.TEXT);
- utterance.onend = () => {
- resolve();
- };
- utterance.onerror = () => {
- reject(new Error('Utterance error'));
- };
- speechSynthesis.speak(utterance);
- });
- }
- }
-
- Scratch.extensions.register(new Tools());
-})(Scratch);
\ No newline at end of file
+(function (Scratch) {
+ "use strict";
+
+ class Tools {
+ getInfo() {
+ return {
+ id: "wpstudio01tts",
+ name: "System Text To Speech",
+ blocks: [
+ {
+ opcode: "speak",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "speak [TEXT]",
+ arguments: {
+ TEXT: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Hello",
+ },
+ },
+ },
+ ],
+ };
+ }
+
+ speak(args) {
+ return new Promise((resolve, reject) => {
+ const utterance = new SpeechSynthesisUtterance(args.TEXT);
+ utterance.onend = () => {
+ resolve();
+ };
+ utterance.onerror = () => {
+ reject(new Error("Utterance error"));
+ };
+ speechSynthesis.speak(utterance);
+ });
+ }
+ }
+
+ Scratch.extensions.register(new Tools());
+})(Scratch);
diff --git a/extensions/Xeltalliv/clippingblending.js b/extensions/Xeltalliv/clippingblending.js
index e307bbc77a..3b79908c01 100644
--- a/extensions/Xeltalliv/clippingblending.js
+++ b/extensions/Xeltalliv/clippingblending.js
@@ -1,17 +1,19 @@
// Name: Clipping & Blending
+// ID: xeltallivclipblend
// Description: Clipping outside of a specified rectangular area and additive color blending.
// By: Vadik1
-(function(Scratch) {
- 'use strict';
+(function (Scratch) {
+ "use strict";
if (!Scratch.extensions.unsandboxed) {
throw new Error("Effects extension must be run unsandboxed");
}
-
// Simplified remake of an icon by True-Fantom
- const icon = 'data:image/svg+xml,' + encodeURIComponent(`
+ const icon =
+ "data:image/svg+xml," +
+ encodeURIComponent(`
`);
-
let toCorrectThing = null;
let active = false;
let flipY = false;
@@ -38,7 +39,6 @@
let scratchUnitHeight = 360;
let penDirty = false;
-
renderer._drawThese = function (drawables, drawMode, projection, opts) {
active = true;
[scratchUnitWidth, scratchUnitHeight] = renderer.getNativeSize();
@@ -74,40 +74,40 @@
};
// Getting Drawable
- const dr = renderer.createDrawable('background');
+ const dr = renderer.createDrawable("background");
const DrawableProto = renderer._allDrawables[dr].__proto__;
- renderer.destroyDrawable(dr, 'background');
+ renderer.destroyDrawable(dr, "background");
function setupModes(clipbox, blendMode, flipY) {
if (clipbox) {
gl.enable(gl.SCISSOR_TEST);
- let x = (clipbox.x_min / scratchUnitWidth + 0.5) * width | 0;
- let y = (clipbox.y_min / scratchUnitHeight + 0.5) * height | 0;
- let x2 = (clipbox.x_max / scratchUnitWidth + 0.5) * width | 0;
- let y2 = (clipbox.y_max / scratchUnitHeight + 0.5) * height | 0;
+ let x = ((clipbox.x_min / scratchUnitWidth + 0.5) * width) | 0;
+ let y = ((clipbox.y_min / scratchUnitHeight + 0.5) * height) | 0;
+ let x2 = ((clipbox.x_max / scratchUnitWidth + 0.5) * width) | 0;
+ let y2 = ((clipbox.y_max / scratchUnitHeight + 0.5) * height) | 0;
let w = x2 - x;
let h = y2 - y;
if (flipY) {
- y = (-(clipbox.y_max) / scratchUnitHeight + 0.5) * height | 0;
+ y = ((-clipbox.y_max / scratchUnitHeight + 0.5) * height) | 0;
}
gl.scissor(x, y, w, h);
} else {
gl.disable(gl.SCISSOR_TEST);
}
switch (blendMode) {
- case 'additive':
+ case "additive":
gl.blendEquation(gl.FUNC_ADD);
gl.blendFunc(gl.ONE, gl.ONE);
break;
- case 'subtract':
+ case "subtract":
gl.blendEquation(gl.FUNC_REVERSE_SUBTRACT);
gl.blendFunc(gl.ONE, gl.ONE);
break;
- case 'multiply':
+ case "multiply":
gl.blendEquation(gl.FUNC_ADD);
gl.blendFunc(gl.DST_COLOR, gl.ONE_MINUS_SRC_ALPHA);
break;
- case 'invert':
+ case "invert":
gl.blendEquation(gl.FUNC_ADD);
gl.blendFunc(gl.ONE_MINUS_DST_COLOR, gl.ONE_MINUS_SRC_COLOR);
break;
@@ -132,7 +132,6 @@
this.blendMode = blendMode;
};
-
// Expanding renderer
renderer.updateDrawableClipBox = function (drawableID, clipbox) {
const drawable = this._allDrawables[drawableID];
@@ -145,16 +144,23 @@
drawable.updateBlendMode(blendMode);
};
-
// Reset on stop & clones inherit effects
const regTargetStuff = function (args) {
if (args.editingTarget) {
- vm.removeListener('targetsUpdate', regTargetStuff);
+ vm.removeListener("targetsUpdate", regTargetStuff);
const proto = vm.runtime.targets[0].__proto__;
const osa = proto.onStopAll;
proto.onStopAll = function () {
- this.renderer.updateDrawableClipBox.call(renderer, this.drawableID, null);
- this.renderer.updateDrawableBlendMode.call(renderer, this.drawableID, null);
+ this.renderer.updateDrawableClipBox.call(
+ renderer,
+ this.drawableID,
+ null
+ );
+ this.renderer.updateDrawableBlendMode.call(
+ renderer,
+ this.drawableID,
+ null
+ );
osa.call(this);
};
const mc = proto.makeClone;
@@ -163,15 +169,22 @@
if (this.clipbox || this.blendMode) {
newTarget.clipbox = Object.assign({}, this.clipbox);
newTarget.blendMode = this.blendMode;
- renderer.updateDrawableClipBox.call(renderer, newTarget.drawableID, this.clipbox);
- renderer.updateDrawableBlendMode.call(renderer, newTarget.drawableID, this.blendMode);
+ renderer.updateDrawableClipBox.call(
+ renderer,
+ newTarget.drawableID,
+ this.clipbox
+ );
+ renderer.updateDrawableBlendMode.call(
+ renderer,
+ newTarget.drawableID,
+ this.blendMode
+ );
}
return newTarget;
};
}
};
- vm.on('targetsUpdate', regTargetStuff);
-
+ vm.on("targetsUpdate", regTargetStuff);
// Pen lines support
let emptyObject = {};
@@ -188,14 +201,20 @@
lastClipbox = null;
lastBlendMode = "default";
};
- const willDrawPenWithTarget = function(target) {
+ const willDrawPenWithTarget = function (target) {
if (!penDirty && target == lastTarget) return;
penDirty = false;
const clipbox = target.clipbox;
- if ((!lastClipbox ^ !clipbox) ||
- (lastBlendMode != target.blendMode) ||
- (clipbox && (clipbox.x_min != lastClipbox.x_min || clipbox.y_min != lastClipbox.y_min || clipbox.x_max != lastClipbox.x_max || clipbox.y_max != lastClipbox.y_max))) {
+ if (
+ !lastClipbox ^ !clipbox ||
+ lastBlendMode != target.blendMode ||
+ (clipbox &&
+ (clipbox.x_min != lastClipbox.x_min ||
+ clipbox.y_min != lastClipbox.y_min ||
+ clipbox.x_max != lastClipbox.x_max ||
+ clipbox.y_max != lastClipbox.y_max))
+ ) {
if (skin.a_lineColorIndex) {
skin._flushLines();
}
@@ -205,7 +224,7 @@
x_min: clipbox.x_min,
y_min: clipbox.y_min,
x_max: clipbox.x_max,
- y_max: clipbox.y_max
+ y_max: clipbox.y_max,
};
} else {
lastClipbox = null;
@@ -217,7 +236,7 @@
// When drawing a line it is important to know the target.
// This saves target.
const onTargetMoved = ext_pen._onTargetMoved;
- ext_pen._onTargetMoved = function(target, oldX, oldY, isForce) {
+ ext_pen._onTargetMoved = function (target, oldX, oldY, isForce) {
willDrawPenWithTarget(target);
onTargetMoved.call(this, target, oldX, oldY, isForce);
};
@@ -230,13 +249,13 @@
// When drawing a dot it is important to know the target.
// This saves target.
const penDown = ext_pen._penDown;
- ext_pen._penDown = function(target) {
+ ext_pen._penDown = function (target) {
willDrawPenWithTarget(target);
penDown.call(this, target);
};
// Set up correct clipping/blending before drawing
const flushLines = skin.__proto__._flushLines;
- skin.__proto__._flushLines = function() {
+ skin.__proto__._flushLines = function () {
setupModes(lastClipbox, lastBlendMode, true);
flushLines.call(this);
};
@@ -248,7 +267,7 @@
// If pen skin does not exist, wait until it will,
// trigger code once, and return everything as it was
const createPenSkin = renderer.createPenSkin;
- renderer.createPenSkin = function() {
+ renderer.createPenSkin = function () {
let skinId = createPenSkin.call(this);
patchPen(renderer._allSkins[skinId]);
renderer.createPenSkin = createPenSkin;
@@ -259,128 +278,132 @@
class Extension {
getInfo() {
return {
- id: 'xeltallivclipblend',
- name: 'Clipping & Blending',
- color1: '#9966FF',
- color2: '#855CD6',
- color3: '#774DCB',
+ id: "xeltallivclipblend",
+ name: "Clipping & Blending",
+ color1: "#9966FF",
+ color2: "#855CD6",
+ color3: "#774DCB",
menuIconURI: icon,
blocks: [
{
- opcode: 'setClipbox',
+ opcode: "setClipbox",
blockType: Scratch.BlockType.COMMAND,
- text: 'set clipping box x1:[X1] y1:[Y1] x2:[X2] y2:[Y2]',
+ text: "set clipping box x1:[X1] y1:[Y1] x2:[X2] y2:[Y2]",
arguments: {
X1: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
+ defaultValue: "0",
},
Y1: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0'
+ defaultValue: "0",
},
X2: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '100'
+ defaultValue: "100",
},
Y2: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '100'
- }
+ defaultValue: "100",
+ },
},
- filter: [Scratch.TargetType.SPRITE]
+ filter: [Scratch.TargetType.SPRITE],
},
{
- opcode: 'clearClipbox',
+ opcode: "clearClipbox",
blockType: Scratch.BlockType.COMMAND,
- text: 'clear clipping box',
- filter: [Scratch.TargetType.SPRITE]
+ text: "clear clipping box",
+ filter: [Scratch.TargetType.SPRITE],
},
{
- opcode: 'getClipbox',
+ opcode: "getClipbox",
blockType: Scratch.BlockType.REPORTER,
- text: 'clipping box [PROP]',
+ text: "clipping box [PROP]",
arguments: {
PROP: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'width',
- menu: 'props'
- }
+ defaultValue: "width",
+ menu: "props",
+ },
},
- filter: [Scratch.TargetType.SPRITE]
+ filter: [Scratch.TargetType.SPRITE],
},
- '---',
+ "---",
{
- opcode: 'setBlend',
+ opcode: "setBlend",
blockType: Scratch.BlockType.COMMAND,
- text: 'use [BLENDMODE] blending ',
+ text: "use [BLENDMODE] blending ",
arguments: {
BLENDMODE: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'default',
- menu: 'blends'
- }
+ defaultValue: "default",
+ menu: "blends",
+ },
},
- filter: [Scratch.TargetType.SPRITE]
+ filter: [Scratch.TargetType.SPRITE],
},
{
- opcode: 'getBlend',
+ opcode: "getBlend",
blockType: Scratch.BlockType.REPORTER,
- text: 'blending',
+ text: "blending",
filter: [Scratch.TargetType.SPRITE],
- disableMonitor: true
+ disableMonitor: true,
},
- '---',
+ "---",
{
- opcode: 'setAdditiveBlend',
+ opcode: "setAdditiveBlend",
blockType: Scratch.BlockType.COMMAND,
- text: 'turn additive blending [STATE]',
+ text: "turn additive blending [STATE]",
arguments: {
STATE: {
type: Scratch.ArgumentType.STRING,
- defaultValue: 'on',
- menu: 'states'
- }
+ defaultValue: "on",
+ menu: "states",
+ },
},
filter: [Scratch.TargetType.SPRITE],
- hideFromPalette: true
+ hideFromPalette: true,
},
{
- opcode: 'getAdditiveBlend',
+ opcode: "getAdditiveBlend",
blockType: Scratch.BlockType.BOOLEAN,
- text: 'is additive blending on?',
+ text: "is additive blending on?",
filter: [Scratch.TargetType.SPRITE],
- hideFromPalette: true
+ hideFromPalette: true,
},
],
menus: {
states: {
acceptReporters: true,
- items: ['on', 'off']
+ items: ["on", "off"],
},
blends: {
acceptReporters: true,
- items: ['default', 'additive', 'subtract', 'multiply', 'invert']
+ items: ["default", "additive", "subtract", "multiply", "invert"],
},
props: {
acceptReporters: true,
- items: ['width', 'height', 'min x', 'min y', 'max x', 'max y']
+ items: ["width", "height", "min x", "min y", "max x", "max y"],
},
- }
+ },
};
}
- setClipbox ({X1, Y1, X2, Y2}, {target}) {
+ setClipbox({ X1, Y1, X2, Y2 }, { target }) {
if (target.isStage) return;
const newClipbox = {
x_min: Math.min(X1, X2),
y_min: Math.min(Y1, Y2),
x_max: Math.max(X1, X2),
- y_max: Math.max(Y1, Y2)
+ y_max: Math.max(Y1, Y2),
};
penDirty = true;
target.clipbox = newClipbox;
- renderer.updateDrawableClipBox.call(renderer, target.drawableID, newClipbox);
+ renderer.updateDrawableClipBox.call(
+ renderer,
+ target.drawableID,
+ newClipbox
+ );
if (target.visible) {
renderer.dirty = true;
target.emitVisualChange();
@@ -389,7 +412,7 @@
}
}
- clearClipbox (args, {target}) {
+ clearClipbox(args, { target }) {
if (target.isStage) return;
target.clipbox = null;
penDirty = true;
@@ -402,28 +425,35 @@
}
}
- getClipbox ({PROP}, {target}) {
+ getClipbox({ PROP }, { target }) {
const clipbox = target.clipbox;
- if (!clipbox) return '';
+ if (!clipbox) return "";
switch (PROP) {
- case 'width': return clipbox.x_max - clipbox.x_min;
- case 'height': return clipbox.y_max - clipbox.y_min;
- case 'min x': return clipbox.x_min;
- case 'min y': return clipbox.y_min;
- case 'max x': return clipbox.x_max;
- case 'max y': return clipbox.y_max;
- default: return '';
+ case "width":
+ return clipbox.x_max - clipbox.x_min;
+ case "height":
+ return clipbox.y_max - clipbox.y_min;
+ case "min x":
+ return clipbox.x_min;
+ case "min y":
+ return clipbox.y_min;
+ case "max x":
+ return clipbox.x_max;
+ case "max y":
+ return clipbox.y_max;
+ default:
+ return "";
}
}
- setBlend ({BLENDMODE}, {target}) {
+ setBlend({ BLENDMODE }, { target }) {
let newValue = null;
switch (BLENDMODE) {
- case 'default':
- case 'additive':
- case 'subtract':
- case 'multiply':
- case 'invert':
+ case "default":
+ case "additive":
+ case "subtract":
+ case "multiply":
+ case "invert":
newValue = BLENDMODE;
break;
default:
@@ -432,7 +462,11 @@
if (target.isStage) return;
penDirty = true;
target.blendMode = newValue;
- renderer.updateDrawableBlendMode.call(renderer, target.drawableID, newValue);
+ renderer.updateDrawableBlendMode.call(
+ renderer,
+ target.drawableID,
+ newValue
+ );
if (target.visible) {
renderer.dirty = true;
target.emitVisualChange();
@@ -441,19 +475,19 @@
}
}
- getBlend (args, {target}) {
- return target.blendMode ?? 'default';
+ getBlend(args, { target }) {
+ return target.blendMode ?? "default";
}
- setAdditiveBlend ({STATE}, util) {
- if (STATE === 'on') this.setBlend ({BLENDMODE: 'additive'}, util);
- if (STATE === 'off') this.setBlend ({BLENDMODE: 'default'}, util);
+ setAdditiveBlend({ STATE }, util) {
+ if (STATE === "on") this.setBlend({ BLENDMODE: "additive" }, util);
+ if (STATE === "off") this.setBlend({ BLENDMODE: "default" }, util);
}
- getAdditiveBlend (args, {target}) {
- return target.blendMode === 'additive';
+ getAdditiveBlend(args, { target }) {
+ return target.blendMode === "additive";
}
}
Scratch.extensions.register(new Extension());
-})(Scratch);
\ No newline at end of file
+})(Scratch);
diff --git a/extensions/XeroName/Deltatime.js b/extensions/XeroName/Deltatime.js
index 3e01ec6473..0d6f3a7de0 100644
--- a/extensions/XeroName/Deltatime.js
+++ b/extensions/XeroName/Deltatime.js
@@ -1,14 +1,16 @@
// Name: Deltatime
+// ID: dtbyxeroname
// Description: Precise delta timing blocks.
// By: XeroName
(function (Scratch) {
- 'use strict';
+ "use strict";
- const icon = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAwIiBoZWlnaHQ9IjYwMCIgdmlld0JveD0iMCAwIDYwMCA2MDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxjaXJjbGUgY3g9IjMwMCIgY3k9IjMwMCIgcj0iMzAwIiBmaWxsPSIjMjAyMDIwIi8+CjxwYXRoIGQ9Ik04Ny44NjggNTEyLjEzMkM2MC4wMTA0IDQ4NC4yNzQgMzcuOTEyNSA0NTEuMjAzIDIyLjgzNjEgNDE0LjgwNUM3Ljc1OTcyIDM3OC40MDcgLTMuNDQ0MTZlLTA2IDMzOS4zOTcgMCAzMDBDMy40NDQxNmUtMDYgMjYwLjYwMyA3Ljc1OTc0IDIyMS41OTMgMjIuODM2MiAxODUuMTk1QzM3LjkxMjYgMTQ4Ljc5NyA2MC4wMTA0IDExNS43MjYgODcuODY4IDg3Ljg2NzlDMTE1LjcyNiA2MC4wMTA0IDE0OC43OTcgMzcuOTEyNSAxODUuMTk1IDIyLjgzNjFDMjIxLjU5MyA3Ljc1OTcxIDI2MC42MDQgLTkuODYyNjZlLTA2IDMwMCAwQzMzOS4zOTcgOS44NjI2OGUtMDYgMzc4LjQwNyA3Ljc1OTc1IDQxNC44MDUgMjIuODM2MkM0NTEuMjAzIDM3LjkxMjYgNDg0LjI3NSA2MC4wMTA0IDUxMi4xMzIgODcuODY4TDMwMCAzMDBMODcuODY4IDUxMi4xMzJaIiBmaWxsPSIjMzAzMDMwIi8+CjxwYXRoIGQ9Ik0zMzAgNDM1TDIzMCAxODUiIHN0cm9rZT0iIzYxMjM2MSIgc3Ryb2tlLXdpZHRoPSIzMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxwYXRoIGQ9Ik0zMjAgMTg1SDQyME01MjAgMTg1SDQyME00MjAgMTg1VjQzNU0yOTkuNDUxIDQzMy42MjlMMjAwLjkyOCAxODcuMzIxQzIwMC41OTMgMTg2LjQ4MyAxOTkuNDA3IDE4Ni40ODMgMTk5LjA3MiAxODcuMzIxTDEwMC41NDkgNDMzLjYyOUMxMDAuMjg2IDQzNC4yODUgMTAwLjc3IDQzNSAxMDEuNDc3IDQzNUgyOTguNTIzQzI5OS4yMyA0MzUgMjk5LjcxNCA0MzQuMjg1IDI5OS40NTEgNDMzLjYyOVoiIHN0cm9rZT0iIzYwNjA2MCIgc3Ryb2tlLXdpZHRoPSIzMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxwYXRoIGQ9Ik0zMTAgNDE1TDIxMCAxNjUiIHN0cm9rZT0iI0ZGNUNGRiIgc3Ryb2tlLXdpZHRoPSIzMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxwYXRoIGQ9Ik0zMDAgMTY1SDQwME01MDAgMTY1SDQwME00MDAgMTY1VjQxNU0yNzkuNDUxIDQxMy42MjlMMTgwLjkyOCAxNjcuMzIxQzE4MC41OTMgMTY2LjQ4MyAxNzkuNDA3IDE2Ni40ODMgMTc5LjA3MiAxNjcuMzIxTDgwLjU0ODYgNDEzLjYyOUM4MC4yODU4IDQxNC4yODUgODAuNzY5NiA0MTUgODEuNDc3IDQxNUgyNzguNTIzQzI3OS4yMyA0MTUgMjc5LjcxNCA0MTQuMjg1IDI3OS40NTEgNDEzLjYyOVoiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMzIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K';
+ const icon =
+ "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAwIiBoZWlnaHQ9IjYwMCIgdmlld0JveD0iMCAwIDYwMCA2MDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxjaXJjbGUgY3g9IjMwMCIgY3k9IjMwMCIgcj0iMzAwIiBmaWxsPSIjMjAyMDIwIi8+CjxwYXRoIGQ9Ik04Ny44NjggNTEyLjEzMkM2MC4wMTA0IDQ4NC4yNzQgMzcuOTEyNSA0NTEuMjAzIDIyLjgzNjEgNDE0LjgwNUM3Ljc1OTcyIDM3OC40MDcgLTMuNDQ0MTZlLTA2IDMzOS4zOTcgMCAzMDBDMy40NDQxNmUtMDYgMjYwLjYwMyA3Ljc1OTc0IDIyMS41OTMgMjIuODM2MiAxODUuMTk1QzM3LjkxMjYgMTQ4Ljc5NyA2MC4wMTA0IDExNS43MjYgODcuODY4IDg3Ljg2NzlDMTE1LjcyNiA2MC4wMTA0IDE0OC43OTcgMzcuOTEyNSAxODUuMTk1IDIyLjgzNjFDMjIxLjU5MyA3Ljc1OTcxIDI2MC42MDQgLTkuODYyNjZlLTA2IDMwMCAwQzMzOS4zOTcgOS44NjI2OGUtMDYgMzc4LjQwNyA3Ljc1OTc1IDQxNC44MDUgMjIuODM2MkM0NTEuMjAzIDM3LjkxMjYgNDg0LjI3NSA2MC4wMTA0IDUxMi4xMzIgODcuODY4TDMwMCAzMDBMODcuODY4IDUxMi4xMzJaIiBmaWxsPSIjMzAzMDMwIi8+CjxwYXRoIGQ9Ik0zMzAgNDM1TDIzMCAxODUiIHN0cm9rZT0iIzYxMjM2MSIgc3Ryb2tlLXdpZHRoPSIzMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxwYXRoIGQ9Ik0zMjAgMTg1SDQyME01MjAgMTg1SDQyME00MjAgMTg1VjQzNU0yOTkuNDUxIDQzMy42MjlMMjAwLjkyOCAxODcuMzIxQzIwMC41OTMgMTg2LjQ4MyAxOTkuNDA3IDE4Ni40ODMgMTk5LjA3MiAxODcuMzIxTDEwMC41NDkgNDMzLjYyOUMxMDAuMjg2IDQzNC4yODUgMTAwLjc3IDQzNSAxMDEuNDc3IDQzNUgyOTguNTIzQzI5OS4yMyA0MzUgMjk5LjcxNCA0MzQuMjg1IDI5OS40NTEgNDMzLjYyOVoiIHN0cm9rZT0iIzYwNjA2MCIgc3Ryb2tlLXdpZHRoPSIzMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxwYXRoIGQ9Ik0zMTAgNDE1TDIxMCAxNjUiIHN0cm9rZT0iI0ZGNUNGRiIgc3Ryb2tlLXdpZHRoPSIzMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxwYXRoIGQ9Ik0zMDAgMTY1SDQwME01MDAgMTY1SDQwME00MDAgMTY1VjQxNU0yNzkuNDUxIDQxMy42MjlMMTgwLjkyOCAxNjcuMzIxQzE4MC41OTMgMTY2LjQ4MyAxNzkuNDA3IDE2Ni40ODMgMTc5LjA3MiAxNjcuMzIxTDgwLjU0ODYgNDEzLjYyOUM4MC4yODU4IDQxNC4yODUgODAuNzY5NiA0MTUgODEuNDc3IDQxNUgyNzguNTIzQzI3OS4yMyA0MTUgMjc5LjcxNCA0MTQuMjg1IDI3OS40NTEgNDEzLjYyOVoiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMzIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K";
if (!Scratch.extensions.unsandboxed) {
- throw new Error('DeltaTime must be run unsandboxed');
+ throw new Error("DeltaTime must be run unsandboxed");
}
const vm = Scratch.vm;
@@ -16,7 +18,7 @@
let deltaTime = 0;
let previousTime = 0;
- vm.runtime.on('BEFORE_EXECUTE', () => {
+ vm.runtime.on("BEFORE_EXECUTE", () => {
const now = performance.now();
deltaTime = previousTime === 0 ? 0 : (now - previousTime) / 1000;
previousTime = now;
@@ -25,24 +27,24 @@
class Dt {
getInfo() {
return {
- id: 'dtbyxeroname',
- name: 'Deltatime',
- color1: '#333333',
- color2: '#444444',
- color3: '#ffffff',
+ id: "dtbyxeroname",
+ name: "Deltatime",
+ color1: "#333333",
+ color2: "#444444",
+ color3: "#ffffff",
menuIconURI: icon,
blocks: [
{
- opcode: 'dt',
+ opcode: "dt",
blockType: Scratch.BlockType.REPORTER,
- text: 'ΔT'
+ text: "ΔT",
},
{
- opcode: 'fps',
+ opcode: "fps",
blockType: Scratch.BlockType.REPORTER,
- text: 'fps'
- }
- ]
+ text: "fps",
+ },
+ ],
};
}
diff --git a/extensions/ZXMushroom63/searchApi.js b/extensions/ZXMushroom63/searchApi.js
index cd4274710f..6d25fe08b5 100644
--- a/extensions/ZXMushroom63/searchApi.js
+++ b/extensions/ZXMushroom63/searchApi.js
@@ -1,214 +1,203 @@
-// Name: Search Params
-// Description: Interact with URL search parameters: the part of the URL after a question mark.
-// By: ZXMushroom63
-
-(function (Scratch) {
- "use strict";
- if (!Scratch.extensions.unsandboxed) {
- throw new Error("SearchParams must be run unsandboxed.");
- }
-
- // Disable in desktop app editor for security reasons.
- // @ts-ignore
- const disabled = typeof TWD !== 'undefined';
-
- class SearchApi {
- getInfo() {
- return {
- id: "zxmushroom63searchparams",
- name: "Search Params",
- color1: "#b4b4b4",
- color2: "#9c9c9c",
- color3: "#646464",
- blocks: [
- {
- blockType: Scratch.BlockType.LABEL,
- text: 'Disabled in desktop editor for security',
- hideFromPalette: !disabled
- },
- {
- opcode: "searchparam",
- blockType: Scratch.BlockType.REPORTER,
- text: "value of search parameter [ID]",
- arguments: {
- ID: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: "x",
- },
- },
- },
- {
- opcode: "occurencesofsearchparam",
- blockType: Scratch.BlockType.REPORTER,
- text: "occurences of search parameter [ID]",
- arguments: {
- ID: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: "x",
- },
- },
- },
- {
- opcode: "indexedsearchparam",
- blockType: Scratch.BlockType.REPORTER,
- text: "index [I] of search parameters [ID]",
- arguments: {
- ID: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: "x",
- },
- I: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: 1,
- },
- },
- },
- {
- opcode: "setsearchparam",
- blockType: Scratch.BlockType.COMMAND,
- text: "set search parameter [ID] to [VAL]",
- arguments: {
- ID: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: "x",
- },
- VAL: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: "15",
- },
- },
- },
- {
- opcode: "deletesearchparam",
- blockType: Scratch.BlockType.COMMAND,
- text: "delete search parameter [ID]",
- arguments: {
- ID: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: "x",
- },
- },
- },
- {
- opcode: "appendsearchparam",
- blockType: Scratch.BlockType.COMMAND,
- text: "append search parameter [ID] with value [VAL]",
- arguments: {
- ID: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: "x",
- },
- VAL: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: "15",
- },
- },
- },
- {
- opcode: "hassearchparam",
- blockType: Scratch.BlockType.BOOLEAN,
- text: "has search parameter [ID]",
- arguments: {
- ID: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: "x",
- },
- },
- },
- {
- opcode: "searchparamslength",
- blockType: Scratch.BlockType.REPORTER,
- text: "length of search parameters",
- },
- {
- opcode: "searchparamatindex",
- blockType: Scratch.BlockType.REPORTER,
- text: "search parameter [PARAM] at index [I]",
- arguments: {
- PARAM: {
- type: Scratch.ArgumentType.STRING,
- menu: "PARAM",
- },
- I: {
- type: Scratch.ArgumentType.NUMBER,
- defaultValue: 1,
- },
- },
- },
- ],
- menus: {
- PARAM: {
- acceptReporters: true,
- items: ["value", "name"],
- },
- },
- };
- }
-
- searchparam({ ID }) {
- if (disabled) return "";
- return new URLSearchParams(location.search).get(ID.toString()) || "";
- }
-
- occurencesofsearchparam({ ID }) {
- if (disabled) return 0;
- return new URLSearchParams(location.search).getAll(ID.toString()).length || 0;
- }
-
- indexedsearchparam({ ID, I }) {
- if (disabled) return "";
- return new URLSearchParams(location.search).getAll(ID.toString())[parseInt(I) - 1] || "";
- }
-
- setsearchparam({ ID, VAL }) {
- if (disabled) return;
- var s = new URLSearchParams(location.search);
- s.set(ID.toString(), VAL.toString());
- history.replaceState("", "", "?" + s.toString());
- }
-
- searchparamslength() {
- if (disabled) return 0;
- var s = new URLSearchParams(location.search);
- // @ts-ignore
- return typeof s.size !== "object" ? s.size : 0;
- }
-
- deletesearchparam({ ID }) {
- if (disabled) return;
- var s = new URLSearchParams(location.search);
- s.delete(ID.toString());
- history.replaceState("", "", "?" + s.toString());
- }
-
- appendsearchparam({ ID, VAL }) {
- if (disabled) return;
- var s = new URLSearchParams(location.search);
- s.append(ID.toString(), VAL.toString());
- history.replaceState("", "", "?" + s.toString());
- }
-
- hassearchparam({ ID }) {
- if (disabled) return false;
- var s = new URLSearchParams(location.search);
- return s.has(ID.toString()) || false;
- }
-
- searchparamatindex({ PARAM, I }) {
- if (disabled) return "";
- var index = parseInt(I) - 1 || 0;
- index = Math.max(0, index);
- var s = new URLSearchParams(location.search);
- var values = PARAM.toString() === "value" ? s.values() : s.keys();
- var i = 0;
- for (const value of values) {
- if (i === index) {
- return value;
- }
- i++;
- }
- return "";
- }
- }
- Scratch.extensions.register(new SearchApi());
-})(Scratch);
+// Name: Search Params
+// ID: zxmushroom63searchparams
+// Description: Interact with URL search parameters: the part of the URL after a question mark.
+// By: ZXMushroom63
+
+(function (Scratch) {
+ "use strict";
+ if (!Scratch.extensions.unsandboxed) {
+ throw new Error("SearchParams must be run unsandboxed.");
+ }
+
+ class SearchApi {
+ getInfo() {
+ return {
+ id: "zxmushroom63searchparams",
+ name: "Search Params",
+ color1: "#b4b4b4",
+ color2: "#9c9c9c",
+ color3: "#646464",
+ blocks: [
+ {
+ opcode: "searchparam",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "value of search parameter [ID]",
+ arguments: {
+ ID: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "x",
+ },
+ },
+ },
+ {
+ opcode: "occurencesofsearchparam",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "occurences of search parameter [ID]",
+ arguments: {
+ ID: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "x",
+ },
+ },
+ },
+ {
+ opcode: "indexedsearchparam",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "index [I] of search parameters [ID]",
+ arguments: {
+ ID: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "x",
+ },
+ I: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: 1,
+ },
+ },
+ },
+ {
+ opcode: "setsearchparam",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "set search parameter [ID] to [VAL]",
+ arguments: {
+ ID: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "x",
+ },
+ VAL: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "15",
+ },
+ },
+ },
+ {
+ opcode: "deletesearchparam",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "delete search parameter [ID]",
+ arguments: {
+ ID: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "x",
+ },
+ },
+ },
+ {
+ opcode: "appendsearchparam",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "append search parameter [ID] with value [VAL]",
+ arguments: {
+ ID: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "x",
+ },
+ VAL: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "15",
+ },
+ },
+ },
+ {
+ opcode: "hassearchparam",
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: "has search parameter [ID]",
+ arguments: {
+ ID: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "x",
+ },
+ },
+ },
+ {
+ opcode: "searchparamslength",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "length of search parameters",
+ },
+ {
+ opcode: "searchparamatindex",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "search parameter [PARAM] at index [I]",
+ arguments: {
+ PARAM: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "PARAM",
+ },
+ I: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: 1,
+ },
+ },
+ },
+ ],
+ menus: {
+ PARAM: {
+ acceptReporters: true,
+ items: ["value", "name"],
+ },
+ },
+ };
+ }
+
+ searchparam({ ID }) {
+ return new URLSearchParams(location.search).get(ID.toString()) || "";
+ }
+
+ occurencesofsearchparam({ ID }) {
+ return (
+ new URLSearchParams(location.search).getAll(ID.toString()).length || 0
+ );
+ }
+
+ indexedsearchparam({ ID, I }) {
+ return (
+ new URLSearchParams(location.search).getAll(ID.toString())[
+ parseInt(I) - 1
+ ] || ""
+ );
+ }
+
+ setsearchparam({ ID, VAL }) {
+ var s = new URLSearchParams(location.search);
+ s.set(ID.toString(), VAL.toString());
+ history.replaceState("", "", "?" + s.toString());
+ }
+
+ searchparamslength() {
+ var s = new URLSearchParams(location.search);
+ // @ts-ignore
+ return typeof s.size !== "object" ? s.size : 0;
+ }
+
+ deletesearchparam({ ID }) {
+ var s = new URLSearchParams(location.search);
+ s.delete(ID.toString());
+ history.replaceState("", "", "?" + s.toString());
+ }
+
+ appendsearchparam({ ID, VAL }) {
+ var s = new URLSearchParams(location.search);
+ s.append(ID.toString(), VAL.toString());
+ history.replaceState("", "", "?" + s.toString());
+ }
+
+ hassearchparam({ ID }) {
+ var s = new URLSearchParams(location.search);
+ return s.has(ID.toString()) || false;
+ }
+
+ searchparamatindex({ PARAM, I }) {
+ var index = parseInt(I) - 1 || 0;
+ index = Math.max(0, index);
+ var s = new URLSearchParams(location.search);
+ var values = PARAM.toString() === "value" ? s.values() : s.keys();
+ var i = 0;
+ for (const value of values) {
+ if (i === index) {
+ return value;
+ }
+ i++;
+ }
+ return "";
+ }
+ }
+ Scratch.extensions.register(new SearchApi());
+})(Scratch);
diff --git a/extensions/ar.js b/extensions/ar.js
index 597df3757c..0d9ad60a71 100644
--- a/extensions/ar.js
+++ b/extensions/ar.js
@@ -1,809 +1,845 @@
// Name: Augmented Reality
+// ID: AR
// Description: Shows image from camera and performs motion tracking, allowing 3D projects to correctly overlay virtual objects on real world.
// By: Vadik1
-(function(Scratch) {
- "use strict";
-
- /* globals XRWebGLLayer, XRRigidTransform, XRWebGLLayer */
-
- if (!Scratch.extensions.unsandboxed) {
- throw new Error("AR extension must be run unsandboxed");
+(function (Scratch) {
+ "use strict";
+
+ /* globals XRWebGLLayer, XRRigidTransform, XRWebGLLayer */
+
+ if (!Scratch.extensions.unsandboxed) {
+ throw new Error("AR extension must be run unsandboxed");
+ }
+
+ const ArgumentType = Scratch.ArgumentType;
+ const BlockType = Scratch.BlockType;
+
+ const vm = Scratch.vm;
+ const renderer = vm.renderer;
+ const runtime = vm.runtime;
+ const frameLoop = runtime.frameLoop;
+ const mouse = runtime.ioDevices.mouse;
+ const video = runtime.ioDevices.video;
+
+ let arResolution = 1;
+ let isPackaged = false;
+
+ let arFail = "uninitialized";
+ let xrSession = null;
+ let xrState = false;
+ let xrRefSpace;
+ let xrViewSpace;
+ let xrProjectionMatrix;
+ let xrTransform;
+ let xrCombinedMatrix;
+ let xrHitTestSource;
+ let hitPosition;
+ let hitPositionAvailible;
+ let oldWidth = 0;
+ let oldHeight = 0;
+ let xrNeedsResize = false;
+ let poseAvailible = false;
+ let enterARDone = [];
+
+ let stageWrapper;
+ let stageWrapperParent;
+ let scLayers;
+ let scControlsBar;
+ const div = document.createElement("div");
+ document.body.append(div);
+ const canvas = Scratch.vm.renderer.canvas;
+ const gl = Scratch.vm.renderer.gl;
+ const enableVideoOriginal = video.enableVideo;
+
+ // Checking whether AR is supported.
+ // If not, extension should still load, to let people
+ // develop AR projects on non-AR-capable devices and then
+ // test them on AR-capable mobile devices
+ if (!window.isSecureContext) {
+ console.error(
+ (arFail =
+ "Window is not secure context. WebXR only works in secure context")
+ );
+ } else if (!navigator.xr) {
+ console.error(
+ (arFail = "navigator.xr is not defined in the browser you are using")
+ );
+ } else {
+ gl.makeXRCompatible().catch((error) => {
+ console.error((arFail = "gl.makeXRCompatible rejected with: " + error));
+ });
+ navigator.xr.isSessionSupported("immersive-ar").then((supported) => {
+ if (!supported) {
+ console.error(
+ (arFail =
+ "WebXR exists in the browser you are using, but 'immersive-ar' session type is not supported")
+ );
+ } else {
+ arFail = null;
+ }
+ });
+ }
+
+ const onSuccess = function (session) {
+ xrSession = session;
+ xrRefSpace = null;
+ xrViewSpace = null;
+ xrHitTestSource = null;
+ hitPosition = null;
+ hitPositionAvailible = false;
+ poseAvailible = false;
+
+ session.updateRenderState({
+ baseLayer: new XRWebGLLayer(session, gl, {
+ framebufferScaleFactor: arResolution,
+ }),
+ });
+ session.addEventListener("end", () => {
+ xrSession = null;
+ updateState();
+ });
+ session.requestReferenceSpace("local").then((refSpace) => {
+ xrRefSpace = refSpace;
+ });
+ session
+ .requestReferenceSpace("viewer")
+ .then((viewSpace) => {
+ xrViewSpace = viewSpace;
+ return session.requestHitTestSource({ space: viewSpace });
+ })
+ .then((hts) => {
+ xrHitTestSource = hts;
+ });
+ updateState();
+
+ // [enter AR] blocks should continue after success
+ enterARDone.forEach((fn) => fn());
+ enterARDone = [];
+ };
+ const onError = function (error) {
+ // This shouldn't set arFail, because arFail is for cases when it permanently failed.
+ // This might fail once, but work on the next attempt.
+ console.error(
+ "Even though 'immersive-ar' is supported in your browser, requesting it failed"
+ );
+ console.error(error);
+
+ // [enter AR] blocks should continue after failure
+ enterARDone.forEach((fn) => fn());
+ enterARDone = [];
+ };
+ const onErrorTryTap = function (error) {
+ canvas.removeEventListener("pointerup", enterAR);
+ canvas.addEventListener("pointerup", enterAR, { once: true });
+ };
+
+ const updateState = function () {
+ const state = !!xrSession;
+ if (state === xrState) return;
+
+ xrState = state;
+ renderer.draw = state ? drawXR : drawOrig;
+ renderer.xr = xrSession;
+ frameLoop.inXR = state;
+ if (frameLoop.running) {
+ frameLoop.stop();
+ frameLoop.start();
}
-
-
- const ArgumentType = Scratch.ArgumentType;
- const BlockType = Scratch.BlockType;
-
- const vm = Scratch.vm;
- const renderer = vm.renderer;
- const runtime = vm.runtime;
- const frameLoop = runtime.frameLoop;
- const mouse = runtime.ioDevices.mouse;
- const video = runtime.ioDevices.video;
-
- let arResolution = 1;
- let isPackaged = false;
-
- let arFail = "uninitialized";
- let xrSession = null;
- let xrState = false;
- let xrRefSpace;
- let xrViewSpace;
- let xrProjectionMatrix;
- let xrTransform;
- let xrCombinedMatrix;
- let xrHitTestSource;
- let hitPosition;
- let hitPositionAvailible;
- let oldWidth = 0;
- let oldHeight = 0;
- let xrNeedsResize = false;
- let poseAvailible = false;
- let enterARDone = [];
-
- let stageWrapper;
- let stageWrapperParent;
- let scLayers;
- let scControlsBar;
- const div = document.createElement("div");
- document.body.append(div);
- const canvas = Scratch.vm.renderer.canvas;
- const gl = Scratch.vm.renderer.gl;
- const enableVideoOriginal = video.enableVideo;
-
- // Checking whether AR is supported.
- // If not, extension should still load, to let people
- // develop AR projects on non-AR-capable devices and then
- // test them on AR-capable mobile devices
- if (!window.isSecureContext) {
- console.error(arFail = "Window is not secure context. WebXR only works in secure context");
- } else if (!navigator.xr) {
- console.error(arFail = "navigator.xr is not defined in the browser you are using");
+ canvas.removeEventListener("pointerup", enterAR);
+ if (state) {
+ video.disableVideo(); // Hiding it, since it freezes anyways
+ video.enableVideo = () => null;
+
+ // css "transform" doesn't work directly on domOverlay element,
+ // but works on it's children. stageWrapper needs to have "transform: scale"
+ // on it, so that is why it is placed into another div
+ div.append(stageWrapper);
+
+ xrNeedsResize = true;
+ oldWidth = runtime.stageWidth;
+ oldHeight = runtime.stageHeight;
} else {
- gl.makeXRCompatible().catch(
- (error) => {
- console.error(arFail = "gl.makeXRCompatible rejected with: " + error);
- }
- );
- navigator.xr.isSessionSupported("immersive-ar").then(
- (supported) => {
- if (!supported) {
- console.error(arFail = "WebXR exists in the browser you are using, but 'immersive-ar' session type is not supported");
- } else {
- arFail = null;
- }
- }
- );
+ video.enableVideo = enableVideoOriginal; // After exiting AR, video sensing can be used again
+
+ if (!isPackaged) {
+ const borderThing = stageWrapper.children[0].children[0].style;
+ borderThing["border"] = "";
+ borderThing["border-radius"] = "";
+ } else {
+ scControlsBar.style["display"] = null;
+ scLayers.style["transform"] = null;
+ stageWrapper.style["align-items"] = null;
+ stageWrapper.style["justify-content"] = null;
+ runtime.setStageSize(oldWidth, oldHeight);
+ }
+ stageWrapper.style = "";
+ stageWrapperParent.append(stageWrapper);
+
+ canvas.style.opacity = "";
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}
-
-
- const onSuccess = function(session) {
- xrSession = session;
- xrRefSpace = null;
- xrViewSpace = null;
- xrHitTestSource = null;
- hitPosition = null;
- hitPositionAvailible = false;
- poseAvailible = false;
-
- session.updateRenderState({
- baseLayer: new XRWebGLLayer(session, gl, { framebufferScaleFactor: arResolution })
- });
- session.addEventListener("end", () => {
- xrSession = null;
- updateState();
- });
- session.requestReferenceSpace("local").then((refSpace) => {
- xrRefSpace = refSpace;
- });
- session.requestReferenceSpace("viewer").then((viewSpace) => {
- xrViewSpace = viewSpace;
- return session.requestHitTestSource({ space: viewSpace });
- }).then((hts) => {
- xrHitTestSource = hts;
- });
- updateState();
-
- // [enter AR] blocks should continue after success
- enterARDone.forEach(fn => fn());
- enterARDone = [];
+ };
+
+ // Code copied from tw-frame-loop.js because existing code can't be accesed
+ const _requestAnimationFrame =
+ typeof requestAnimationFrame === "function"
+ ? requestAnimationFrame
+ : (f) => setTimeout(f, 1000 / 60);
+ const _cancelAnimationFrame =
+ typeof requestAnimationFrame === "function"
+ ? cancelAnimationFrame
+ : clearTimeout;
+
+ const animationFrameWrapper = (callback) => {
+ let id;
+ const handle = () => {
+ id = _requestAnimationFrame(handle);
+ callback();
};
- const onError = function(error) {
- // This shouldn't set arFail, because arFail is for cases when it permanently failed.
- // This might fail once, but work on the next attempt.
- console.error("Even though 'immersive-ar' is supported in your browser, requesting it failed");
- console.error(error);
-
- // [enter AR] blocks should continue after failure
- enterARDone.forEach(fn => fn());
- enterARDone = [];
- };
- const onErrorTryTap = function(error) {
- canvas.removeEventListener("pointerup", enterAR);
- canvas.addEventListener("pointerup", enterAR, {once: true});
- };
-
- const updateState = function() {
- const state = !!xrSession;
- if (state === xrState) return;
-
- xrState = state;
- renderer.draw = state ? drawXR : drawOrig;
- renderer.xr = xrSession;
- frameLoop.inXR = state;
- if (frameLoop.running) {
- frameLoop.stop();
- frameLoop.start();
- }
- canvas.removeEventListener("pointerup", enterAR);
- if (state) {
- video.disableVideo(); // Hiding it, since it freezes anyways
- video.enableVideo = () => null;
-
- // css "transform" doesn't work directly on domOverlay element,
- // but works on it's children. stageWrapper needs to have "transform: scale"
- // on it, so that is why it is placed into another div
- div.append(stageWrapper);
-
- xrNeedsResize = true;
- oldWidth = runtime.stageWidth;
- oldHeight = runtime.stageHeight;
- } else {
- video.enableVideo = enableVideoOriginal; // After exiting AR, video sensing can be used again
-
- if (!isPackaged) {
- const borderThing = stageWrapper.children[0].children[0].style;
- borderThing["border"] = "";
- borderThing["border-radius"] = "";
- } else {
- scControlsBar.style["display"] = null;
- scLayers.style["transform"] = null;
- stageWrapper.style["align-items"] = null;
- stageWrapper.style["justify-content"] = null;
- runtime.setStageSize(oldWidth, oldHeight);
- }
- stageWrapper.style = "";
- stageWrapperParent.append(stageWrapper);
-
- canvas.style.opacity = "";
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- }
+ const cancel = () => _cancelAnimationFrame(id);
+ id = _requestAnimationFrame(handle);
+ return {
+ cancel,
};
-
-
-
- // Code copied from tw-frame-loop.js because existing code can't be accesed
- const _requestAnimationFrame = typeof requestAnimationFrame === 'function' ?
- requestAnimationFrame :
- (f => setTimeout(f, 1000 / 60));
- const _cancelAnimationFrame = typeof requestAnimationFrame === 'function' ?
- cancelAnimationFrame :
- clearTimeout;
-
- const animationFrameWrapper = callback => {
- let id;
- const handle = () => {
- id = _requestAnimationFrame(handle);
- callback();
- };
- const cancel = () => _cancelAnimationFrame(id);
- id = _requestAnimationFrame(handle);
- return {
- cancel
- };
- };
-
-
-
- // Patching frameLoop to use xrSession.requestAnimationFrame when in AR mode
- const xrAnimationFrameWrapper = (callback, fps = 30) => {
- const xrSessionBackup = xrSession;
- let shouldTriggerAgain = false;
- let id;
- let idIsXR;
- let interval;
- const handle = (t, frame) => {
- // If fps = 0, then run at screen's refresh rate
- // and always use xr animation frame.
- // In other cases keep using normal animation frame
- // and waiting until shouldTriggerAgain gets set
- // to true, and only then use xr animation frame
- // once and resume waiting. shouldTriggerAgain is
- // set to true by the interval located below.
- if (fps === 0 || shouldTriggerAgain) {
- shouldTriggerAgain = false;
- id = xrSession.requestAnimationFrame(handle);
- idIsXR = true;
- } else {
- id = window.requestAnimationFrame(handle);
- idIsXR = false;
- }
- // Normal animation frames are just for waiting and
- // shouldn't trigger callback()
- if (!frame) return;
-
- if (xrNeedsResize) {
- xrNeedsResize = false;
-
- // This needs to run before setStageSize
- if (isPackaged) {
- scControlsBar.style["display"] = "none";
- scLayers.style["transform"] = "translate(0px, 0px)";
- stageWrapper.style["align-items"] = "normal";
- stageWrapper.style["justify-content"] = "flex-start";
- }
-
- const bl = xrSession.renderState.baseLayer;
- const newWidth = Math.round(bl.framebufferWidth / bl.framebufferHeight * oldHeight);
- if (runtime.stageWidth !== newWidth) {
- runtime.setStageSize(newWidth, oldHeight);
- }
-
- const scale = div.clientHeight / canvas.clientHeight;
- stageWrapper.style = "transform-origin: top left; transform: scale(" + scale + "," + scale + ")";
- canvas.style.opacity = "0";
-
- if (!isPackaged) {
- const borderThing = stageWrapper.children[0].children[0].style;
- borderThing["border"] = "none";
- borderThing["border-radius"] = "0";
- borderThing["transform"] = ""; // Removes translateX
- }
- }
- poseAvailible = false;
- if (xrRefSpace) {
- const pose = frame.getViewerPose(xrRefSpace);
- if (pose) {
- poseAvailible = true;
- xrProjectionMatrix = pose.views[0].projectionMatrix;
- xrTransform = pose.views[0].transform;
- const inverseTransformMatrix = xrTransform.inverse.matrix;
- const a00 = xrProjectionMatrix[0];
- const a01 = xrProjectionMatrix[1];
- const a02 = xrProjectionMatrix[2];
- const a03 = xrProjectionMatrix[3];
- const a10 = xrProjectionMatrix[4];
- const a11 = xrProjectionMatrix[5];
- const a12 = xrProjectionMatrix[6];
- const a13 = xrProjectionMatrix[7];
- const a20 = xrProjectionMatrix[8];
- const a21 = xrProjectionMatrix[9];
- const a22 = xrProjectionMatrix[10];
- const a23 = xrProjectionMatrix[11];
- const a30 = xrProjectionMatrix[12];
- const a31 = xrProjectionMatrix[13];
- const a32 = xrProjectionMatrix[14];
- const a33 = xrProjectionMatrix[15];
- const b00 = inverseTransformMatrix[0];
- const b01 = inverseTransformMatrix[1];
- const b02 = inverseTransformMatrix[2];
- const b03 = inverseTransformMatrix[3];
- const b10 = inverseTransformMatrix[4];
- const b11 = inverseTransformMatrix[5];
- const b12 = inverseTransformMatrix[6];
- const b13 = inverseTransformMatrix[7];
- const b20 = inverseTransformMatrix[8];
- const b21 = inverseTransformMatrix[9];
- const b22 = inverseTransformMatrix[10];
- const b23 = inverseTransformMatrix[11];
- const b30 = inverseTransformMatrix[12];
- const b31 = inverseTransformMatrix[13];
- const b32 = inverseTransformMatrix[14];
- const b33 = inverseTransformMatrix[15];
- xrCombinedMatrix = [
- b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30,
- b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31,
- b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32,
- b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33,
- b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30,
- b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31,
- b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32,
- b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33,
- b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30,
- b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31,
- b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32,
- b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33,
- b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30,
- b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31,
- b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32,
- b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33
- ];
- }
- }
- hitPositionAvailible = false;
- if (xrHitTestSource) {
- const hitTestResults = frame.getHitTestResults(xrHitTestSource);
- if (hitTestResults.length > 0) {
- hitPositionAvailible = true;
- hitPosition = hitTestResults[0].getPose(xrRefSpace).transform.position;
- }
- }
- callback();
- };
- const cancel = () => {
- if (idIsXR) {
- xrSessionBackup.cancelAnimationFrame(id);
- } else {
- cancelAnimationFrame(id);
- }
- if (interval) {
- clearInterval(interval);
- }
- };
+ };
+
+ // Patching frameLoop to use xrSession.requestAnimationFrame when in AR mode
+ const xrAnimationFrameWrapper = (callback, fps = 30) => {
+ const xrSessionBackup = xrSession;
+ let shouldTriggerAgain = false;
+ let id;
+ let idIsXR;
+ let interval;
+ const handle = (t, frame) => {
+ // If fps = 0, then run at screen's refresh rate
+ // and always use xr animation frame.
+ // In other cases keep using normal animation frame
+ // and waiting until shouldTriggerAgain gets set
+ // to true, and only then use xr animation frame
+ // once and resume waiting. shouldTriggerAgain is
+ // set to true by the interval located below.
+ if (fps === 0 || shouldTriggerAgain) {
+ shouldTriggerAgain = false;
id = xrSession.requestAnimationFrame(handle);
- if (fps > 0) {
- interval = setInterval(() => {
- shouldTriggerAgain = true;
- }, 1000 / fps);
+ idIsXR = true;
+ } else {
+ id = window.requestAnimationFrame(handle);
+ idIsXR = false;
+ }
+ // Normal animation frames are just for waiting and
+ // shouldn't trigger callback()
+ if (!frame) return;
+
+ if (xrNeedsResize) {
+ xrNeedsResize = false;
+
+ // This needs to run before setStageSize
+ if (isPackaged) {
+ scControlsBar.style["display"] = "none";
+ scLayers.style["transform"] = "translate(0px, 0px)";
+ stageWrapper.style["align-items"] = "normal";
+ stageWrapper.style["justify-content"] = "flex-start";
}
- return {
- cancel
- };
- };
- const start = function() {
- this.running = true;
- if (this.inXR) {
- if (this.framerate === 0) {
- this._stepAnimation = this.xrAnimationFrameWrapper(this.stepCallback, 0);
- this.runtime.currentStepTime = 1000 / 60;
- } else {
- // Interpolation should never be enabled when framerate === 0 as that's just redundant
- if (this.interpolation) {
- this._interpolationAnimation = animationFrameWrapper(this.interpolationCallback);
- }
- this._stepAnimation = this.xrAnimationFrameWrapper(this.stepCallback, this.framerate);
- this.runtime.currentStepTime = 1000 / this.framerate;
- }
- } else {
- if (this.framerate === 0) {
- this._stepAnimation = animationFrameWrapper(this.stepCallback);
- this.runtime.currentStepTime = 1000 / 60;
- } else {
- // Interpolation should never be enabled when framerate === 0 as that's just redundant
- if (this.interpolation) {
- this._interpolationAnimation = animationFrameWrapper(this.interpolationCallback);
- }
- this._stepInterval = setInterval(this.stepCallback, 1000 / this.framerate);
- this.runtime.currentStepTime = 1000 / this.framerate;
- }
- }
- };
- frameLoop.xrAnimationFrameWrapper = xrAnimationFrameWrapper.bind(frameLoop);
- frameLoop.start = start.bind(frameLoop);
- frameLoop.inXR = false;
-
-
- // Patching renderer.draw() to draw to xr framebuffer instead of canvas
- const drawOrig = renderer.draw.bind(renderer);
- const drawXR = (function() {
- const bl = this.xr.renderState.baseLayer; // ADDED
- if (!bl) return; // Should fix very rare crash during exiting // ADDED
-
- this._doExitDrawRegion();
-
- const gl = this._gl;
-
- gl.bindFramebuffer(gl.FRAMEBUFFER, bl.framebuffer); // CHANGED
- gl.viewport(0, 0, bl.framebufferWidth, bl.framebufferHeight); // CHANGED
- gl.clearColor(0, 0, 0, 0);
- gl.clear(gl.COLOR_BUFFER_BIT);
-
- this._drawThese(this._drawList, "default" /*ShaderManager.DRAW_MODE.default*/, this._projection, {
- framebufferWidth: bl.framebufferWidth, // CHANGED
- framebufferHeight: bl.framebufferHeight // CHANGED
- });
- if (this._snapshotCallbacks.length > 0) {
- const snapshot = gl.canvas.toDataURL();
- this._snapshotCallbacks.forEach(cb => cb(snapshot));
- this._snapshotCallbacks = [];
- }
- }).bind(renderer);
- renderer.draw = drawOrig;
-
-
-
- // Patching _pickTarget incorrect position bug:
- // When the canvas is scaled using transform:scale,
- // canvas.getBoundingClientRect is affected by it, but
- // canvas.clientWidth and canvas.clientHeight are not.
- //
- // postData receives data.x and data.y, which are mouse position in
- // screen units. To be able to rescale it to usable scratch units
- // it also receives data.canvasWidth and data.canvasHeight
- // which are based on getBoundingClientRect. Based of that it
- // calculates this._scratchX and this._scratchY.
- // Even when canvas is scaled, those are calculated correctly and
- // as a result, blocks (mouse x) and (mouse y) report correct values.
- //
- // Later, postData calls _pickTarget, while only passing data.x and data.y
- // without data.canvasWidth and data.canvasHeight. That method calls
- // runtime renderer.pick, which calls clientSpaceToScratchBounds, which
- // uses canvas.clientWidth and canvas.clientHeight to rescale mouse
- // position from screen units to scratch units. This ignores
- // transform:scale and as a result, sprites can't be clicked or dragged.
- //
- // WARNING: Makes _pickTarget only work correctly when called from postData.
- // If something else calls it directly, it may cause problems.
- const postDataOriginal = mouse.postData.bind(mouse);
- mouse.postData = function(data) {
- this._canvasWidth = data.canvasWidth;
- this._canvasHeight = data.canvasHeight;
- postDataOriginal(data);
- }.bind(mouse);
-
- const _pickTargetOriginal = mouse._pickTarget.bind(mouse);
- mouse._pickTarget = function (x, y) {
- return _pickTargetOriginal(
- x / this._canvasWidth * canvas.clientWidth,
- y / this._canvasHeight * canvas.clientHeight
+ const bl = xrSession.renderState.baseLayer;
+ const newWidth = Math.round(
+ (bl.framebufferWidth / bl.framebufferHeight) * oldHeight
);
- }.bind(mouse);
-
- // This is used by .
- // It was also broken in a similar way.
- mouse.getClientX = function() {
- return this._clientX / this._canvasWidth * canvas.clientWidth;
- }.bind(mouse);
-
- mouse.getClientY = function() {
- return this._clientY / this._canvasHeight * canvas.clientHeight;
- }.bind(mouse);
- // END OF WARNING
-
-
- const enterAR = function(event) {
- if (!xrSession) {
- // Entering and exiting editor recreates this element
- stageWrapper = document.querySelector("[class*='stage-wrapper_stage-canvas-wrapper']");
- if (!stageWrapper) {
- stageWrapper = document.querySelector("[class='sc-root']");
- scControlsBar = document.querySelector("[class='sc-controls-bar']");
- scLayers = document.querySelector("[class='sc-layers']");
- if (!stageWrapper) {
- console.error(arFail = "Failed to get the div element of the stage");
- return;
- }
- isPackaged = true;
- }
- stageWrapperParent = stageWrapper.parentElement;
-
- const noop = () => {};
- navigator.xr.requestSession("immersive-ar", {
- requiredFeatures: ["hit-test", "dom-overlay"],
- domOverlay: {root: div}
- }).then(onSuccess, event ? onError : onErrorTryTap);
- // If (event) is defined, it was from click, so something went wrong.
- // If (event) is null, it was called directly, and might've been rejected due to lack of user interaction.
+ if (runtime.stageWidth !== newWidth) {
+ runtime.setStageSize(newWidth, oldHeight);
}
- };
-
-
- class ARExtension {
- getInfo() {
- return {
- id: "AR",
- color1: "#d10000",
- color2: "#bd0000",
- color3: "#af0100",
- docsURI: "https://extensions.turbowarp.org/ar",
- blocks: [
- {
- opcode: "enterAR",
- blockType: BlockType.COMMAND,
- text: "enter AR mode",
- arguments: {}
- },
- {
- opcode: "exitAR",
- blockType: BlockType.COMMAND,
- text: "exit AR mode",
- arguments: {}
- },
- {
- opcode: "isInAR",
- blockType: BlockType.BOOLEAN,
- text: "is in AR?",
- arguments: {}
- },
- {
- opcode: "isFeatureAvailible",
- blockType: BlockType.BOOLEAN,
- text: "is [FEATURE] availible?",
- arguments: {
- FEATURE: {
- type: ArgumentType.STRING,
- menu: "xrFeature",
- defaultValue: "ar"
- }
- }
- },
- "---",
- {
- opcode: "getStageWidth",
- blockType: BlockType.REPORTER,
- text: "stage width",
- arguments: {}
- },
- {
- opcode: "getStageHeight",
- blockType: BlockType.REPORTER,
- text: "stage height",
- arguments: {}
- },
- "---",
- {
- opcode: "getMatrixItem",
- blockType: BlockType.REPORTER,
- text: "item [ITEM] of [MATRIX] matrix",
- arguments: {
- MATRIX: {
- type: ArgumentType.STRING,
- menu: "xrMatrix",
- defaultValue: "combined"
- },
- ITEM: {
- type: ArgumentType.NUMBER,
- defaultValue: 1
- }
- }
- },
- {
- opcode: "getPosition",
- blockType: BlockType.REPORTER,
- text: "position [POSITION_COMPONENT]",
- arguments: {
- POSITION_COMPONENT: {
- type: ArgumentType.STRING,
- menu: "positionComponent",
- defaultValue: "x"
- }
- }
- },
- {
- opcode: "getOrientation",
- blockType: BlockType.REPORTER,
- text: "orientation [ORIENTATION_COMPONENT]",
- arguments: {
- ORIENTATION_COMPONENT: {
- type: ArgumentType.STRING,
- menu: "orientationComponent",
- defaultValue: "w"
- }
- }
- },
- "---",
- {
- opcode: "getHitPosition",
- blockType: BlockType.REPORTER,
- text: "hit position [POSITION_COMPONENT]",
- arguments: {
- POSITION_COMPONENT: {
- type: ArgumentType.STRING,
- menu: "positionComponent",
- defaultValue: "x"
- }
- }
- },
- "---",
- {
- opcode: "moveSpaceBy",
- blockType: BlockType.COMMAND,
- text: "move everything by x:[X] y:[Y] z:[Z]",
- arguments: {
- X: {
- type: ArgumentType.NUMBER,
- defaultValue: 0
- },
- Y: {
- type: ArgumentType.NUMBER,
- defaultValue: 0
- },
- Z: {
- type: ArgumentType.NUMBER,
- defaultValue: 0
- }
- }
- },
- {
- opcode: "turnSpaceBy",
- blockType: BlockType.COMMAND,
- text: "turn everything by r:[R] i:[I] j:[J] k:[K]",
- arguments: {
- R: {
- type: ArgumentType.NUMBER,
- defaultValue: 1
- },
- I: {
- type: ArgumentType.NUMBER,
- defaultValue: 0
- },
- J: {
- type: ArgumentType.NUMBER,
- defaultValue: 0
- },
- K: {
- type: ArgumentType.NUMBER,
- defaultValue: 0
- }
- }
- },
- "---",
- {
- opcode: "setResolution",
- blockType: BlockType.COMMAND,
- text: "set resolution [RESOLUTION]",
- arguments: {
- RESOLUTION: {
- type: ArgumentType.NUMBER,
- defaultValue: 1
- }
- }
- },
- ],
- menus: {
- positionComponent: {
- acceptReporters: false,
- items: [
- {
- text: "x",
- value: "x"
- },
- {
- text: "y",
- value: "y"
- },
- {
- text: "z",
- value: "z"
- }
- ]
- },
- orientationComponent: {
- acceptReporters: false,
- items: [
- {
- text: "r",
- value: "w"
- },
- {
- text: "i",
- value: "x"
- },
- {
- text: "j",
- value: "y"
- },
- {
- text: "k",
- value: "z"
- }
- ]
- },
- xrMatrix: {
- acceptReporters: false,
- items: [
- "combined",
- "projection",
- "view",
- "inverse view"
- ]
- },
- xrFeature: {
- acceptReporters: false,
- items: [
- "ar",
- "pose",
- "hit position"
- ]
- }
- }
- };
- }
- enterAR() {
- if (arFail) {
- if (arFail !== "shown") {
- // AR is used on mobile, where accessing browser console to see what's wrong can be an issue
- alert("Project attempted to start AR even though it's not avalible. The reason: " + arFail + ". This message will only be shown once.");
- arFail = "shown";
- }
- } else {
- if (!xrSession) {
- if (enterARDone.length === 0) enterAR(null);
- return new Promise(resolve => enterARDone.push(resolve));
- }
- }
- }
- exitAR() {
- if (xrSession) {
- xrSession.end();
- }
- }
- isInAR() {
- return !!xrSession;
+ const scale = div.clientHeight / canvas.clientHeight;
+ stageWrapper.style =
+ "transform-origin: top left; transform: scale(" +
+ scale +
+ "," +
+ scale +
+ ")";
+ canvas.style.opacity = "0";
+
+ if (!isPackaged) {
+ const borderThing = stageWrapper.children[0].children[0].style;
+ borderThing["border"] = "none";
+ borderThing["border-radius"] = "0";
+ borderThing["transform"] = ""; // Removes translateX
}
- getStageWidth() {
- return runtime.stageWidth;
+ }
+ poseAvailible = false;
+ if (xrRefSpace) {
+ const pose = frame.getViewerPose(xrRefSpace);
+ if (pose) {
+ poseAvailible = true;
+ xrProjectionMatrix = pose.views[0].projectionMatrix;
+ xrTransform = pose.views[0].transform;
+ const inverseTransformMatrix = xrTransform.inverse.matrix;
+ const a00 = xrProjectionMatrix[0];
+ const a01 = xrProjectionMatrix[1];
+ const a02 = xrProjectionMatrix[2];
+ const a03 = xrProjectionMatrix[3];
+ const a10 = xrProjectionMatrix[4];
+ const a11 = xrProjectionMatrix[5];
+ const a12 = xrProjectionMatrix[6];
+ const a13 = xrProjectionMatrix[7];
+ const a20 = xrProjectionMatrix[8];
+ const a21 = xrProjectionMatrix[9];
+ const a22 = xrProjectionMatrix[10];
+ const a23 = xrProjectionMatrix[11];
+ const a30 = xrProjectionMatrix[12];
+ const a31 = xrProjectionMatrix[13];
+ const a32 = xrProjectionMatrix[14];
+ const a33 = xrProjectionMatrix[15];
+ const b00 = inverseTransformMatrix[0];
+ const b01 = inverseTransformMatrix[1];
+ const b02 = inverseTransformMatrix[2];
+ const b03 = inverseTransformMatrix[3];
+ const b10 = inverseTransformMatrix[4];
+ const b11 = inverseTransformMatrix[5];
+ const b12 = inverseTransformMatrix[6];
+ const b13 = inverseTransformMatrix[7];
+ const b20 = inverseTransformMatrix[8];
+ const b21 = inverseTransformMatrix[9];
+ const b22 = inverseTransformMatrix[10];
+ const b23 = inverseTransformMatrix[11];
+ const b30 = inverseTransformMatrix[12];
+ const b31 = inverseTransformMatrix[13];
+ const b32 = inverseTransformMatrix[14];
+ const b33 = inverseTransformMatrix[15];
+ xrCombinedMatrix = [
+ b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30,
+ b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31,
+ b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32,
+ b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33,
+ b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30,
+ b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31,
+ b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32,
+ b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33,
+ b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30,
+ b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31,
+ b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32,
+ b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33,
+ b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30,
+ b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31,
+ b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32,
+ b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33,
+ ];
}
- getStageHeight() {
- return runtime.stageHeight;
+ }
+ hitPositionAvailible = false;
+ if (xrHitTestSource) {
+ const hitTestResults = frame.getHitTestResults(xrHitTestSource);
+ if (hitTestResults.length > 0) {
+ hitPositionAvailible = true;
+ hitPosition =
+ hitTestResults[0].getPose(xrRefSpace).transform.position;
}
- getMatrixItem(args) {
- let item = args.ITEM | 0;
- if (item < 1 && item > 16) return "";
- let matrix = null;
- switch (args.MATRIX) {
- case "combined":
- matrix = xrCombinedMatrix;
- break;
- case "projection":
- matrix = xrProjectionMatrix;
- break;
- case "view":
- matrix = xrTransform?.matrix;
- break;
- case "inverse view":
- matrix = xrTransform?.inverse?.matrix;
- break;
- }
- if (!matrix) return 0;
- return matrix[item - 1] || 0;
- }
- moveSpaceBy(args) {
- if (!xrRefSpace) return;
- const x = +args.X || 0;
- const y = +args.Y || 0;
- const z = +args.Z || 0;
- if (!isFinite(x + y + z)) return;
- const offsetTransform = new XRRigidTransform({x: x, y: y, z: z}, {x: 0, y: 0, z: 0, w: 1});
- xrRefSpace = xrRefSpace.getOffsetReferenceSpace(offsetTransform);
- }
- turnSpaceBy(args) {
- if (!xrRefSpace) return;
- const r = +args.R || 0;
- const i = +args.I || 0;
- const j = +args.J || 0;
- const k = +args.K || 0;
- const len = Math.sqrt(r * r + i * i + j * j + k * k);
- if (!isFinite(len) || len === 0) return;
- const offsetTransform = new XRRigidTransform({x: 0, y: 0, z: 0}, {x: i / len, y: j / len, z: k / len, w: r / len});
- xrRefSpace = xrRefSpace.getOffsetReferenceSpace(offsetTransform);
- }
- getPosition(args) {
- if (!xrTransform) return 0;
- return xrTransform.position[args.POSITION_COMPONENT] || 0;
+ }
+ callback();
+ };
+ const cancel = () => {
+ if (idIsXR) {
+ xrSessionBackup.cancelAnimationFrame(id);
+ } else {
+ cancelAnimationFrame(id);
+ }
+ if (interval) {
+ clearInterval(interval);
+ }
+ };
+ id = xrSession.requestAnimationFrame(handle);
+ if (fps > 0) {
+ interval = setInterval(() => {
+ shouldTriggerAgain = true;
+ }, 1000 / fps);
+ }
+ return {
+ cancel,
+ };
+ };
+ const start = function () {
+ this.running = true;
+ if (this.inXR) {
+ if (this.framerate === 0) {
+ this._stepAnimation = this.xrAnimationFrameWrapper(
+ this.stepCallback,
+ 0
+ );
+ this.runtime.currentStepTime = 1000 / 60;
+ } else {
+ // Interpolation should never be enabled when framerate === 0 as that's just redundant
+ if (this.interpolation) {
+ this._interpolationAnimation = animationFrameWrapper(
+ this.interpolationCallback
+ );
}
- getOrientation(args) {
- if (!xrTransform) return 0;
- return xrTransform.orientation[args.ORIENTATION_COMPONENT] || 0;
+ this._stepAnimation = this.xrAnimationFrameWrapper(
+ this.stepCallback,
+ this.framerate
+ );
+ this.runtime.currentStepTime = 1000 / this.framerate;
+ }
+ } else {
+ if (this.framerate === 0) {
+ this._stepAnimation = animationFrameWrapper(this.stepCallback);
+ this.runtime.currentStepTime = 1000 / 60;
+ } else {
+ // Interpolation should never be enabled when framerate === 0 as that's just redundant
+ if (this.interpolation) {
+ this._interpolationAnimation = animationFrameWrapper(
+ this.interpolationCallback
+ );
}
- getHitPosition(args) {
- if (!hitPosition) return 0;
- return hitPosition[args.POSITION_COMPONENT] || 0;
+ this._stepInterval = setInterval(
+ this.stepCallback,
+ 1000 / this.framerate
+ );
+ this.runtime.currentStepTime = 1000 / this.framerate;
+ }
+ }
+ };
+ frameLoop.xrAnimationFrameWrapper = xrAnimationFrameWrapper.bind(frameLoop);
+ frameLoop.start = start.bind(frameLoop);
+ frameLoop.inXR = false;
+
+ // Patching renderer.draw() to draw to xr framebuffer instead of canvas
+ const drawOrig = renderer.draw.bind(renderer);
+ const drawXR = function () {
+ const bl = this.xr.renderState.baseLayer; // ADDED
+ if (!bl) return; // Should fix very rare crash during exiting // ADDED
+
+ this._doExitDrawRegion();
+
+ const gl = this._gl;
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, bl.framebuffer); // CHANGED
+ gl.viewport(0, 0, bl.framebufferWidth, bl.framebufferHeight); // CHANGED
+ gl.clearColor(0, 0, 0, 0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ this._drawThese(
+ this._drawList,
+ "default" /*ShaderManager.DRAW_MODE.default*/,
+ this._projection,
+ {
+ framebufferWidth: bl.framebufferWidth, // CHANGED
+ framebufferHeight: bl.framebufferHeight, // CHANGED
+ }
+ );
+ if (this._snapshotCallbacks.length > 0) {
+ const snapshot = gl.canvas.toDataURL();
+ this._snapshotCallbacks.forEach((cb) => cb(snapshot));
+ this._snapshotCallbacks = [];
+ }
+ }.bind(renderer);
+ renderer.draw = drawOrig;
+
+ // Patching _pickTarget incorrect position bug:
+ // When the canvas is scaled using transform:scale,
+ // canvas.getBoundingClientRect is affected by it, but
+ // canvas.clientWidth and canvas.clientHeight are not.
+ //
+ // postData receives data.x and data.y, which are mouse position in
+ // screen units. To be able to rescale it to usable scratch units
+ // it also receives data.canvasWidth and data.canvasHeight
+ // which are based on getBoundingClientRect. Based of that it
+ // calculates this._scratchX and this._scratchY.
+ // Even when canvas is scaled, those are calculated correctly and
+ // as a result, blocks (mouse x) and (mouse y) report correct values.
+ //
+ // Later, postData calls _pickTarget, while only passing data.x and data.y
+ // without data.canvasWidth and data.canvasHeight. That method calls
+ // runtime renderer.pick, which calls clientSpaceToScratchBounds, which
+ // uses canvas.clientWidth and canvas.clientHeight to rescale mouse
+ // position from screen units to scratch units. This ignores
+ // transform:scale and as a result, sprites can't be clicked or dragged.
+ //
+ // WARNING: Makes _pickTarget only work correctly when called from postData.
+ // If something else calls it directly, it may cause problems.
+ const postDataOriginal = mouse.postData.bind(mouse);
+ mouse.postData = function (data) {
+ this._canvasWidth = data.canvasWidth;
+ this._canvasHeight = data.canvasHeight;
+ postDataOriginal(data);
+ }.bind(mouse);
+
+ const _pickTargetOriginal = mouse._pickTarget.bind(mouse);
+ mouse._pickTarget = function (x, y) {
+ return _pickTargetOriginal(
+ (x / this._canvasWidth) * canvas.clientWidth,
+ (y / this._canvasHeight) * canvas.clientHeight
+ );
+ }.bind(mouse);
+
+ // This is used by .
+ // It was also broken in a similar way.
+ mouse.getClientX = function () {
+ return (this._clientX / this._canvasWidth) * canvas.clientWidth;
+ }.bind(mouse);
+
+ mouse.getClientY = function () {
+ return (this._clientY / this._canvasHeight) * canvas.clientHeight;
+ }.bind(mouse);
+ // END OF WARNING
+
+ const enterAR = function (event) {
+ if (!xrSession) {
+ // Entering and exiting editor recreates this element
+ stageWrapper = document.querySelector(
+ "[class*='stage-wrapper_stage-canvas-wrapper']"
+ );
+ if (!stageWrapper) {
+ stageWrapper = document.querySelector("[class='sc-root']");
+ scControlsBar = document.querySelector("[class='sc-controls-bar']");
+ scLayers = document.querySelector("[class='sc-layers']");
+ if (!stageWrapper) {
+ console.error(
+ (arFail = "Failed to get the div element of the stage")
+ );
+ return;
}
- isFeatureAvailible(args) {
- switch (args.FEATURE) {
- case "ar":
- return !arFail;
- case "pose":
- return poseAvailible;
- case "hit position":
- return hitPositionAvailible;
- default:
- return false;
- }
+ isPackaged = true;
+ }
+ stageWrapperParent = stageWrapper.parentElement;
+
+ const noop = () => {};
+ navigator.xr
+ .requestSession("immersive-ar", {
+ requiredFeatures: ["hit-test", "dom-overlay"],
+ domOverlay: { root: div },
+ })
+ .then(onSuccess, event ? onError : onErrorTryTap);
+ // If (event) is defined, it was from click, so something went wrong.
+ // If (event) is null, it was called directly, and might've been rejected due to lack of user interaction.
+ }
+ };
+
+ class ARExtension {
+ getInfo() {
+ return {
+ id: "AR",
+ color1: "#d10000",
+ color2: "#bd0000",
+ color3: "#af0100",
+ docsURI: "https://extensions.turbowarp.org/ar",
+ blocks: [
+ {
+ opcode: "enterAR",
+ blockType: BlockType.COMMAND,
+ text: "enter AR mode",
+ arguments: {},
+ },
+ {
+ opcode: "exitAR",
+ blockType: BlockType.COMMAND,
+ text: "exit AR mode",
+ arguments: {},
+ },
+ {
+ opcode: "isInAR",
+ blockType: BlockType.BOOLEAN,
+ text: "is in AR?",
+ arguments: {},
+ },
+ {
+ opcode: "isFeatureAvailible",
+ blockType: BlockType.BOOLEAN,
+ text: "is [FEATURE] availible?",
+ arguments: {
+ FEATURE: {
+ type: ArgumentType.STRING,
+ menu: "xrFeature",
+ defaultValue: "ar",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "getStageWidth",
+ blockType: BlockType.REPORTER,
+ text: "stage width",
+ arguments: {},
+ },
+ {
+ opcode: "getStageHeight",
+ blockType: BlockType.REPORTER,
+ text: "stage height",
+ arguments: {},
+ },
+ "---",
+ {
+ opcode: "getMatrixItem",
+ blockType: BlockType.REPORTER,
+ text: "item [ITEM] of [MATRIX] matrix",
+ arguments: {
+ MATRIX: {
+ type: ArgumentType.STRING,
+ menu: "xrMatrix",
+ defaultValue: "combined",
+ },
+ ITEM: {
+ type: ArgumentType.NUMBER,
+ defaultValue: 1,
+ },
+ },
+ },
+ {
+ opcode: "getPosition",
+ blockType: BlockType.REPORTER,
+ text: "position [POSITION_COMPONENT]",
+ arguments: {
+ POSITION_COMPONENT: {
+ type: ArgumentType.STRING,
+ menu: "positionComponent",
+ defaultValue: "x",
+ },
+ },
+ },
+ {
+ opcode: "getOrientation",
+ blockType: BlockType.REPORTER,
+ text: "orientation [ORIENTATION_COMPONENT]",
+ arguments: {
+ ORIENTATION_COMPONENT: {
+ type: ArgumentType.STRING,
+ menu: "orientationComponent",
+ defaultValue: "w",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "getHitPosition",
+ blockType: BlockType.REPORTER,
+ text: "hit position [POSITION_COMPONENT]",
+ arguments: {
+ POSITION_COMPONENT: {
+ type: ArgumentType.STRING,
+ menu: "positionComponent",
+ defaultValue: "x",
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "moveSpaceBy",
+ blockType: BlockType.COMMAND,
+ text: "move everything by x:[X] y:[Y] z:[Z]",
+ arguments: {
+ X: {
+ type: ArgumentType.NUMBER,
+ defaultValue: 0,
+ },
+ Y: {
+ type: ArgumentType.NUMBER,
+ defaultValue: 0,
+ },
+ Z: {
+ type: ArgumentType.NUMBER,
+ defaultValue: 0,
+ },
+ },
+ },
+ {
+ opcode: "turnSpaceBy",
+ blockType: BlockType.COMMAND,
+ text: "turn everything by r:[R] i:[I] j:[J] k:[K]",
+ arguments: {
+ R: {
+ type: ArgumentType.NUMBER,
+ defaultValue: 1,
+ },
+ I: {
+ type: ArgumentType.NUMBER,
+ defaultValue: 0,
+ },
+ J: {
+ type: ArgumentType.NUMBER,
+ defaultValue: 0,
+ },
+ K: {
+ type: ArgumentType.NUMBER,
+ defaultValue: 0,
+ },
+ },
+ },
+ "---",
+ {
+ opcode: "setResolution",
+ blockType: BlockType.COMMAND,
+ text: "set resolution [RESOLUTION]",
+ arguments: {
+ RESOLUTION: {
+ type: ArgumentType.NUMBER,
+ defaultValue: 1,
+ },
+ },
+ },
+ ],
+ menus: {
+ positionComponent: {
+ acceptReporters: false,
+ items: [
+ {
+ text: "x",
+ value: "x",
+ },
+ {
+ text: "y",
+ value: "y",
+ },
+ {
+ text: "z",
+ value: "z",
+ },
+ ],
+ },
+ orientationComponent: {
+ acceptReporters: false,
+ items: [
+ {
+ text: "r",
+ value: "w",
+ },
+ {
+ text: "i",
+ value: "x",
+ },
+ {
+ text: "j",
+ value: "y",
+ },
+ {
+ text: "k",
+ value: "z",
+ },
+ ],
+ },
+ xrMatrix: {
+ acceptReporters: false,
+ items: ["combined", "projection", "view", "inverse view"],
+ },
+ xrFeature: {
+ acceptReporters: false,
+ items: ["ar", "pose", "hit position"],
+ },
+ },
+ };
+ }
+ enterAR() {
+ if (arFail) {
+ if (arFail !== "shown") {
+ // AR is used on mobile, where accessing browser console to see what's wrong can be an issue
+ alert(
+ "Project attempted to start AR even though it's not avalible. The reason: " +
+ arFail +
+ ". This message will only be shown once."
+ );
+ arFail = "shown";
}
- setResolution(args) {
- arResolution = Math.max(0.1, Math.min(1, +args.RESOLUTION || 0));
- if (xrSession) {
- xrSession.updateRenderState({
- baseLayer: new XRWebGLLayer(xrSession, gl, { framebufferScaleFactor: arResolution })
- });
- }
+ } else {
+ if (!xrSession) {
+ if (enterARDone.length === 0) enterAR(null);
+ return new Promise((resolve) => enterARDone.push(resolve));
}
+ }
+ }
+ exitAR() {
+ if (xrSession) {
+ xrSession.end();
+ }
+ }
+ isInAR() {
+ return !!xrSession;
+ }
+ getStageWidth() {
+ return runtime.stageWidth;
+ }
+ getStageHeight() {
+ return runtime.stageHeight;
+ }
+ getMatrixItem(args) {
+ let item = args.ITEM | 0;
+ if (item < 1 && item > 16) return "";
+ let matrix = null;
+ switch (args.MATRIX) {
+ case "combined":
+ matrix = xrCombinedMatrix;
+ break;
+ case "projection":
+ matrix = xrProjectionMatrix;
+ break;
+ case "view":
+ matrix = xrTransform?.matrix;
+ break;
+ case "inverse view":
+ matrix = xrTransform?.inverse?.matrix;
+ break;
+ }
+ if (!matrix) return 0;
+ return matrix[item - 1] || 0;
+ }
+ moveSpaceBy(args) {
+ if (!xrRefSpace) return;
+ const x = +args.X || 0;
+ const y = +args.Y || 0;
+ const z = +args.Z || 0;
+ if (!isFinite(x + y + z)) return;
+ const offsetTransform = new XRRigidTransform(
+ { x: x, y: y, z: z },
+ { x: 0, y: 0, z: 0, w: 1 }
+ );
+ xrRefSpace = xrRefSpace.getOffsetReferenceSpace(offsetTransform);
+ }
+ turnSpaceBy(args) {
+ if (!xrRefSpace) return;
+ const r = +args.R || 0;
+ const i = +args.I || 0;
+ const j = +args.J || 0;
+ const k = +args.K || 0;
+ const len = Math.sqrt(r * r + i * i + j * j + k * k);
+ if (!isFinite(len) || len === 0) return;
+ const offsetTransform = new XRRigidTransform(
+ { x: 0, y: 0, z: 0 },
+ { x: i / len, y: j / len, z: k / len, w: r / len }
+ );
+ xrRefSpace = xrRefSpace.getOffsetReferenceSpace(offsetTransform);
+ }
+ getPosition(args) {
+ if (!xrTransform) return 0;
+ return xrTransform.position[args.POSITION_COMPONENT] || 0;
+ }
+ getOrientation(args) {
+ if (!xrTransform) return 0;
+ return xrTransform.orientation[args.ORIENTATION_COMPONENT] || 0;
+ }
+ getHitPosition(args) {
+ if (!hitPosition) return 0;
+ return hitPosition[args.POSITION_COMPONENT] || 0;
+ }
+ isFeatureAvailible(args) {
+ switch (args.FEATURE) {
+ case "ar":
+ return !arFail;
+ case "pose":
+ return poseAvailible;
+ case "hit position":
+ return hitPositionAvailible;
+ default:
+ return false;
+ }
+ }
+ setResolution(args) {
+ arResolution = Math.max(0.1, Math.min(1, +args.RESOLUTION || 0));
+ if (xrSession) {
+ xrSession.updateRenderState({
+ baseLayer: new XRWebGLLayer(xrSession, gl, {
+ framebufferScaleFactor: arResolution,
+ }),
+ });
+ }
}
+ }
- Scratch.extensions.register(new ARExtension());
-})(Scratch);
\ No newline at end of file
+ Scratch.extensions.register(new ARExtension());
+})(Scratch);
diff --git a/extensions/battery.js b/extensions/battery.js
index 7e491ea656..fcb432b803 100644
--- a/extensions/battery.js
+++ b/extensions/battery.js
@@ -1,8 +1,9 @@
// Name: Battery
+// ID: battery
// Description: Access information about the battery of phones or laptops. May not work on all devices and browsers.
(function (Scratch) {
- 'use strict';
+ "use strict";
/** @type {Promise|null} */
let getBatteryPromise = null;
@@ -21,34 +22,35 @@
return callback(cachedBattery);
}
if (!getBatteryPromise) {
- getBatteryPromise = navigator.getBattery()
- .then(battery => {
+ getBatteryPromise = navigator
+ .getBattery()
+ .then((battery) => {
getBatteryPromise = null;
cachedBattery = battery;
- cachedBattery.addEventListener('chargingchange', () => {
- Scratch.vm.runtime.startHats('battery_chargingChanged');
+ cachedBattery.addEventListener("chargingchange", () => {
+ Scratch.vm.runtime.startHats("battery_chargingChanged");
});
- cachedBattery.addEventListener('levelchange', () => {
- Scratch.vm.runtime.startHats('battery_levelChanged');
+ cachedBattery.addEventListener("levelchange", () => {
+ Scratch.vm.runtime.startHats("battery_levelChanged");
});
- cachedBattery.addEventListener('chargingtimechange', () => {
- Scratch.vm.runtime.startHats('battery_chargeTimeChanged');
+ cachedBattery.addEventListener("chargingtimechange", () => {
+ Scratch.vm.runtime.startHats("battery_chargeTimeChanged");
});
- cachedBattery.addEventListener('dischargingtimechange', () => {
- Scratch.vm.runtime.startHats('battery_dischargeTimeChanged');
+ cachedBattery.addEventListener("dischargingtimechange", () => {
+ Scratch.vm.runtime.startHats("battery_dischargeTimeChanged");
});
return cachedBattery;
})
- .catch(error => {
+ .catch((error) => {
getBatteryPromise = null;
- console.error('Could not get battery', error);
+ console.error("Could not get battery", error);
batteryError = true;
return null;
});
}
- return getBatteryPromise.then(battery => {
+ return getBatteryPromise.then((battery) => {
return callback(battery);
});
};
@@ -57,78 +59,78 @@
withBattery(() => {});
class BatteryExtension {
- getInfo () {
+ getInfo() {
return {
- name: 'Battery',
- id: 'battery',
+ name: "Battery",
+ id: "battery",
blocks: [
{
- opcode: 'charging',
+ opcode: "charging",
blockType: Scratch.BlockType.BOOLEAN,
- text: 'charging?'
+ text: "charging?",
},
{
- opcode: 'level',
+ opcode: "level",
blockType: Scratch.BlockType.REPORTER,
- text: 'battery level'
+ text: "battery level",
},
{
- opcode: 'chargeTime',
+ opcode: "chargeTime",
blockType: Scratch.BlockType.REPORTER,
- text: 'seconds until charged'
+ text: "seconds until charged",
},
{
- opcode: 'dischargeTime',
+ opcode: "dischargeTime",
blockType: Scratch.BlockType.REPORTER,
- text: 'seconds until empty'
+ text: "seconds until empty",
},
{
- opcode: 'chargingChanged',
+ opcode: "chargingChanged",
blockType: Scratch.BlockType.EVENT,
- text: 'when charging changed',
- isEdgeActivated: false
+ text: "when charging changed",
+ isEdgeActivated: false,
},
{
- opcode: 'levelChanged',
+ opcode: "levelChanged",
blockType: Scratch.BlockType.EVENT,
- text: 'when battery level changed',
- isEdgeActivated: false
+ text: "when battery level changed",
+ isEdgeActivated: false,
},
{
- opcode: 'chargeTimeChanged',
+ opcode: "chargeTimeChanged",
blockType: Scratch.BlockType.EVENT,
- text: 'when time until charged changed',
- isEdgeActivated: false
+ text: "when time until charged changed",
+ isEdgeActivated: false,
},
{
- opcode: 'dischargeTimeChanged',
+ opcode: "dischargeTimeChanged",
blockType: Scratch.BlockType.EVENT,
- text: 'when time until empty changed',
- isEdgeActivated: false
+ text: "when time until empty changed",
+ isEdgeActivated: false,
},
- ]
+ ],
};
}
- charging () {
- return withBattery(battery => {
+ charging() {
+ return withBattery((battery) => {
if (!battery) return true;
return battery.charging;
});
}
- level () {
- return withBattery(battery => {
+ level() {
+ return withBattery((battery) => {
if (!battery) return 100;
return battery.level * 100;
});
}
- chargeTime () {
- return withBattery(battery => {
+ chargeTime() {
+ return withBattery((battery) => {
if (!battery) return 0;
return battery.chargingTime;
});
}
- dischargeTime () {
- return withBattery(battery => {
+ dischargeTime() {
+ return withBattery((battery) => {
if (!battery) return Infinity;
return battery.dischargingTime;
});
diff --git a/extensions/bitwise.js b/extensions/bitwise.js
index b9208ed3c0..d78d514037 100644
--- a/extensions/bitwise.js
+++ b/extensions/bitwise.js
@@ -1,20 +1,22 @@
// Name: Bitwise
+// ID: Bitwise
// Description: Blocks that operate on the binary representation of numbers in computers.
// By: TrueFantom
-(Scratch => {
- 'use strict';
+((Scratch) => {
+ "use strict";
- const icon = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIyMjUuMzU0ODEiIGhlaWdodD0iMjI1LjM1NDgiIHZpZXdCb3g9IjAsMCwyMjUuMzU0ODEsMjI1LjM1NDgiPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNjIuMzIyODcsLTM3LjMyMjY1KSI+PGcgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aXNQYWludGluZ0xheWVyJnF1b3Q7OnRydWV9IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIHN0eWxlPSJtaXgtYmxlbmQtbW9kZTogbm9ybWFsIj48cGF0aCBkPSJNMTYyLjMyMjg4LDE1MC4wMDAwNWMwLC02Mi4yMzAwMSA1MC40NDczOSwtMTEyLjY3NzQgMTEyLjY3NzQsLTExMi42Nzc0YzYyLjIzMDAxLDAgMTEyLjY3NzQsNTAuNDQ3MzkgMTEyLjY3NzQsMTEyLjY3NzRjMCw2Mi4yMzAwMSAtNTAuNDQ3MzksMTEyLjY3NzQgLTExMi42Nzc0LDExMi42Nzc0Yy02Mi4yMzAwMSwwIC0xMTIuNjc3NCwtNTAuNDQ3MzkgLTExMi42Nzc0LC0xMTIuNjc3NHoiIGZpbGw9IiNlNjI4MmEiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjAiIHN0cm9rZS1saW5lY2FwPSJidXR0Ii8+PHBhdGggZD0iTTE2Mi4zMjI4NywxNTAuMDAwMDVjMCwtNjIuMjMwMDEgNTAuNDQ3MzksLTExMi42Nzc0IDExMi42Nzc0LC0xMTIuNjc3NGM2Mi4yMzAwMSwwIDExMi42Nzc0LDUwLjQ0NzM5IDExMi42Nzc0LDExMi42Nzc0YzAsNjIuMjMwMDEgLTUwLjQ0NzM5LDExMi42Nzc0IC0xMTIuNjc3NCwxMTIuNjc3NGMtNjIuMjMwMDEsMCAtMTEyLjY3NzQsLTUwLjQ0NzM5IC0xMTIuNjc3NCwtMTEyLjY3NzR6IiBmaWxsPSIjMTdjZGU2IiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIwIiBzdHJva2UtbGluZWNhcD0iYnV0dCIvPjxwYXRoIGQ9Ik0yNDEuNjc3NzgsMTQ3LjA4OTUzdjAuNDU5NjJjMi42OTA2NCwwLjI5NzU3IDUuMzUzMTYsMC44MDk5MSA3Ljk2MjEsMS41MzIwNmMyLjc5MzQ0LDAuNzkxNTUgNS4yODQ1NywxLjgxNjUxIDcuNDczMzcsMy4wNzQ4NGMxLjY0NiwwLjkzNzAxIDMuMTc1ODMsMi4wNjQ3MiA0LjU1Nzg3LDMuMzU5OGMzLjUyOTU0LDMuMzQzOTcgNS44MTE1Miw3Ljc5MjgzIDYuNDY4MzQsMTIuNjEwMzZjMC4yMzgsMS41NTU5IDAuMzU0NzksMy4xMjc5MSAwLjM0OTMxLDQuNzAxODhjMC4wMzMzNCw0LjAxNjE1IC0wLjY2NDkzLDguMDA0ODEgLTIuMDYwNjIsMTEuNzcwNzljLTAuNjk4NjIsMS44NTAxMSAtMS41NzMzNSwzLjYyODgzIC0yLjYxMjE2LDUuMzExNjRjLTIuMTA2MjIsMy4zNjc4NyAtNC44OTQ3Miw2LjI1NzEgLTguMTg1NzgsOC40ODE0NmMtMS45NjE1NywxLjM0MzYgLTQuMDQyMTIsMi41MDQ2NSAtNi4yMTU1NSwzLjQ2ODU4Yy01LjA1ODg1LDIuMjY5NSAtMTEuMjUxNDIsMy42NTQ0NiAtMTguNTc3NzEsNC4xNTQ5NGMtMi4xNTQxMiwwLjE0MzgxIC00LjMxMjUxLDAuMjE0MzMgLTYuNDcxNDEsMC4yMTE0MmMtMTEuODQ3OTEsMCAtMjIuMzY4MDIsLTEuOTkxNjcgLTMxLjU2MDM2LC01Ljk3NTAydi0xOS42MTAzMmM0LjY5ODMxLDIuMzQ5MTUgOS42MjY0Miw0LjEzNjU1IDE0Ljc4NDM0LDUuMzYyMmM1LjE1NzkzLDEuMjI1NjUgOS45MzI4MywxLjgzODQ3IDE0LjMyNDczLDEuODM4NDdjOC4yNzMxLDAgMTQuMDY5MzksLTEuNDI5OTEgMTcuMzg4ODQsLTQuMjg5NzZjMi40NzQyNCwtMi4xNDA1NyA0LjExOTE2LC01LjA4MTI2IDQuNjQ4MjYsLTguMzA5ODdjMC4yMjYzMywtMS4yNTE2NCAwLjMzNzEsLTIuNTIxNDQgMC4zMzA5MiwtMy43OTMzN2MwLC0zLjA2NDExIC0wLjc2NjAzLC01LjY0MzA3IC0yLjI5ODA4LC03LjczNjg4Yy0wLjY4ODk0LC0wLjkxOSAtMS41MzI5OSwtMS43MTA3MSAtMi40OTQxOSwtMi4zMzk0NWMtMC44MTcwOSwtMC41NDk1IC0xLjc1ODgsLTEuMDQzODQgLTIuODI1MTEsLTEuNDgzMDNjLTAuODg5MDUsLTAuMzYyMDcgLTEuNzk4NjIsLTAuNjcxNTggLTIuNzI0LC0wLjkyNjg5Yy0xLjMyNjEsLTAuMzYyNjUgLTIuNjcyNiwtMC42NDYwOCAtNC4wMzIzNywtMC44NDg3NmMtMi44MTg5OCwtMC40MzYxMiAtNi4xNjM5OCwtMC42ODQ4MyAtMTAuMDM0OTcsLTAuNzQ2MTFjLTAuNjQ3NTEsLTAuMDA5NzMgLTEuMjk1MDYsLTAuMDE0MzIgLTEuOTQyNjUsLTAuMDEzNzloLTguMjczMXYtMTcuNzcxODVoOC40MjYzMWM2Ljc0MTA1LDAgMTEuODczNDQsLTAuNjM4MzUgMTUuMzk3MTcsLTEuOTE1MDdjMS4wMTMxOSwtMC4zNjEwNCAxLjk5NjY4LC0wLjgwMDU1IDIuOTQxNTUsLTEuMzE0NWMxLjk1Njk0LC0xLjA3ODU3IDMuMzc2NjUsLTIuMzc2NzIgNC4yNTkxMiwtMy44OTQ0OWMxLjI3NjcyLC0yLjE5NTk0IDEuOTE1MDcsLTQuNzIzODQgMS45MTUwNywtNy41ODM2OGMwLC0zLjg4MTIgLTEuMjAwMTEsLTYuOTE5NzkgLTMuNjAwMzMsLTkuMTE1NzNjLTAuOTc3NDIsLTAuODY3NDIgLTIuMTA5NDEsLTEuNTQzMDcgLTMuMzM2ODIsLTEuOTkxNjdjLTIuMjYwMywtMC44NjgxNyAtNS4xNTY5LC0xLjMwMjI1IC04LjY4OTgyLC0xLjMwMjI1Yy01LjE2MzAzLDAgLTkuNjQ4ODksMC44Nzc4NyAtMTMuNDU3NTgsMi42MzM2Yy0wLjAzMzc1LDAuMDE1NzUgLTAuMDY3NDYsMC4wMzE1OSAtMC4xMDExMiwwLjA0NzQ5Yy0zLjgzMDE0LDEuNzg3NCAtNy4wNzMsMy41NDkyNyAtOS43Mjg1Niw1LjI4NTU5bC0xMC43MjQzOSwtMTUuOTMzMzljMi44MDQ5MywtMS45ODcyIDUuNzg3OCwtMy43MTA0NiA4LjkxMDQ0LC01LjE0NzcxYzIuMDE5MSwtMC45MzUwNiA0LjA4MTU0LC0xLjc3MzUyIDYuMTgwMzEsLTIuNTEyNTdjMi44NzMzMSwtMC45OTk5IDUuODMxNjUsLTEuNzM2MjggOC44Mzg0MywtMi4yMDAwM2MyLjkyODI4LC0wLjQ2Njc2IDYuMDYwMzEsLTAuNzQ0NTggOS4zOTYxLC0wLjgzMzQ0YzAuNzkwMzcsLTAuMDIwNyAxLjU4MDk5LC0wLjAzMDkyIDIuMzcxNjIsLTAuMDMwNjRjMy41NjQ5OSwtMC4wMTkwMyA3LjEyNTksMC4yNDQyOSAxMC42NDkzMiwwLjc4NzQ4YzMuNzcxOTIsMC42MDI2IDcuMTg5NDMsMS41NTI0OCAxMC4yNTI1MiwyLjg0OTYyYzIuMDQ3MjEsMC44NTkyMSA0LjAwMTUsMS45MjQ5MSA1LjgzMjU0LDMuMTgwNTVjMy4wMjEwMywyLjAxNTE3IDUuNTA5ODksNC43MzEwOCA3LjI1NDI5LDcuOTE2MTNjMS43NTE2NSwzLjI3NDUxIDIuNjI3NDgsNy4wNDQ5IDIuNjI3NDgsMTEuMzExMTdjMC4wMjI4MywyLjY0OTMyIC0wLjI5OTU4LDUuMjkwMzYgLTAuOTU5MDcsNy44NTYzOGMtMC45OTAyNiwzLjg3NTMyIC0zLjAxNTMyLDcuNDA4NTggLTUuODU4NTgsMTAuMjIxODhjLTQuMjg3OSw0LjIxNDEgLTkuNTg4MjgsNy4yNTM2NiAtMTUuMzkxMDQsOC44MjYxOGMtMC40NTk1NCwwLjEyOTkzIC0wLjkyMTI3LDAuMjUyMDEgLTEuMzg0OTgsMC4zNjYxNnoiIGZpbGw9IiNmZmZmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjZmZmZmZmIiBzdHJva2Utd2lkdGg9IjIuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PHBhdGggZD0iTTM1Ny4xOTQ4MiwxODUuMjM3NzN2MTkuNDU3MTFoLTc2LjQ0OTYxdi0xNi4wODY1OWwyNy40MjM4MSwtMjcuNzMwMjJjNS41MTU0LC01LjcxOTY5IDkuOTU4MzcsLTEwLjQ5NDU5IDEzLjMyODg5LC0xNC4zMjQ3M2MxLjE0MDMxLC0xLjI5MDI0IDIuMjMzMzcsLTIuNjIxNDQgMy4yNzcwNywtMy45OTEwMWMxLjczNjMzLC0yLjI4ODg5IDMuMDk1MjYsLTQuNDMxMjMgNC4wNzY4LC02LjQyNjk4YzEuNTMyMDYsLTMuMTE1MTggMi4yOTgwOCwtNi40NjAxOCAyLjI5ODA4LC0xMC4wMzQ5N2MwLC00LjM5MTkgLTEuMjAwMTEsLTcuNjYwMjggLTMuNjAwMzMsLTkuODA1MTZjLTEuNDk5NjUsLTEuMzE5NjEgLTMuMzAxMzQsLTIuMjQ5NCAtNS4yNDU3NiwtMi43MDcxNGMtMS4yMzI3OCwtMC4zMDg0NiAtMi41NzUzOSwtMC40NzY5OSAtNC4wMjc3OCwtMC41MDU1OGMtMC4xNTE2NiwtMC4wMDMwNSAtMC4zMDMzMywtMC4wMDQ1NyAtMC40NTUwMiwtMC4wMDQ2Yy00LjExNSwwLjAxMzE5IC04LjE3NDk2LDAuOTQ2NTQgLTExLjg4MjYzLDIuNzMxNjZjLTAuMTI1LDAuMDU4OTQgLTAuMjQ5NiwwLjExODY5IC0wLjM3MzgyLDAuMTc5MjVjLTEuOTc2NDgsMC45NzE0NSAtMy44OTIwMiwyLjA2MjI1IC01LjczNjAyLDMuMjY2MzRjLTEuODcxMTYsMS4yMTMzOSAtMy43ODM2NywyLjU5MTIzIC01LjczNzU1LDQuMTMzNDljLTAuMzY1MjksMC4yODgzNSAtMC43Mjg0LDAuNTc5NDQgLTEuMDg5MjksMC44NzMyN2wtMTIuNTYyODYsLTE0Ljg2MDk1YzMuMTY2MjUsLTIuNzU3NyA2LjUxMTI0LC01LjMxMTEzIDEwLjAzNDk3LC03LjY2MDI4YzIuMjk2ODQsLTEuNTE5MDggNC43Mjg4LC0yLjgyMzI3IDcuMjY1MDEsLTMuODk2MDJjMS42MzM0MywtMC42OTU0OCAzLjI5OTE1LC0xLjMxMjU3IDQuOTkxNDQsLTEuODQ5MTljNC42NDcyNSwtMS40ODA5OSAxMC4yMzkyNSwtMi4yMjE0OCAxNi43NzYwMiwtMi4yMjE0OGMyLjk1MzkxLC0wLjAxNTA2IDUuOTAzMjMsMC4yMzUxNiA4LjgxMjM5LDAuNzQ3NjRjMy41MDMzMSwwLjYzNTI4IDYuNjk3MTIsMS42NzY1OSA5LjU4MTQ4LDMuMTIzODZjMC4wMjI0OSwwLjAxMTcyIDAuMDQ0OTcsMC4wMjM0NyAwLjA2NzQxLDAuMDM1MjRjMi44MTA3NCwxLjM5Mzg2IDUuMzk4NTgsMy4xOTc5MiA3LjY3ODY3LDUuMzUzYzEuNjMxNSwxLjU1ODQ4IDMuMDY1OTcsMy4zMTA5MyA0LjI3MTM3LDUuMjE4MThjMi44MDg3Niw0LjQ0Mjk2IDQuMjEzMTUsOS40NzMyMiA0LjIxMzE1LDE1LjA5MDc1YzAuMDIzMDQsMy43ODk2NiAtMC40OTkxMSw3LjU2MjkzIC0xLjU1MDQ0LDExLjIwMzkzYy0wLjUzODM4LDEuODMzMDMgLTEuMjIzOSwzLjYxOTYxIC0yLjA0OTg5LDUuMzQyMjhjLTEuNDI5ODcsMi45NDcyMiAtMy4xMjYyOCw1Ljc1NzQ3IC01LjA2ODA0LDguMzk1NjdjLTEuNjU1MDMsMi4yNTgxIC0zLjQ0MTQ0LDQuNDE2OTIgLTUuMzQ5OTQsNi40NjUyOGMtMi4xMjQ1NiwyLjI4NTM1IC00LjI5NDM4LDQuNTI4MjIgLTYuNTA4MTgsNi43MjcyNmMtMi4yNjY0MiwyLjI1NzIyIC00LjcwMDg1LDQuNjA0ODYgLTcuMzAzMzEsNy4wNDI4NmMtMC44ODI0MiwwLjgyNzExIC0xLjc2ODQ3LDEuNjUwMzUgLTIuNjU4MTIsMi40Njk2N2wtMTQuMDk0OTIsMTMuMTc1Njh2MS4wNzI0NHoiIGZpbGw9IiNmZmZmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjZmZmZmZmIiBzdHJva2Utd2lkdGg9IjIuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6MTEyLjY3NzEzOjExMi42NzczNDUwMDAwMDAwMy0tPg==';
+ const icon =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIyMjUuMzU0ODEiIGhlaWdodD0iMjI1LjM1NDgiIHZpZXdCb3g9IjAsMCwyMjUuMzU0ODEsMjI1LjM1NDgiPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNjIuMzIyODcsLTM3LjMyMjY1KSI+PGcgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aXNQYWludGluZ0xheWVyJnF1b3Q7OnRydWV9IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIHN0eWxlPSJtaXgtYmxlbmQtbW9kZTogbm9ybWFsIj48cGF0aCBkPSJNMTYyLjMyMjg4LDE1MC4wMDAwNWMwLC02Mi4yMzAwMSA1MC40NDczOSwtMTEyLjY3NzQgMTEyLjY3NzQsLTExMi42Nzc0YzYyLjIzMDAxLDAgMTEyLjY3NzQsNTAuNDQ3MzkgMTEyLjY3NzQsMTEyLjY3NzRjMCw2Mi4yMzAwMSAtNTAuNDQ3MzksMTEyLjY3NzQgLTExMi42Nzc0LDExMi42Nzc0Yy02Mi4yMzAwMSwwIC0xMTIuNjc3NCwtNTAuNDQ3MzkgLTExMi42Nzc0LC0xMTIuNjc3NHoiIGZpbGw9IiNlNjI4MmEiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjAiIHN0cm9rZS1saW5lY2FwPSJidXR0Ii8+PHBhdGggZD0iTTE2Mi4zMjI4NywxNTAuMDAwMDVjMCwtNjIuMjMwMDEgNTAuNDQ3MzksLTExMi42Nzc0IDExMi42Nzc0LC0xMTIuNjc3NGM2Mi4yMzAwMSwwIDExMi42Nzc0LDUwLjQ0NzM5IDExMi42Nzc0LDExMi42Nzc0YzAsNjIuMjMwMDEgLTUwLjQ0NzM5LDExMi42Nzc0IC0xMTIuNjc3NCwxMTIuNjc3NGMtNjIuMjMwMDEsMCAtMTEyLjY3NzQsLTUwLjQ0NzM5IC0xMTIuNjc3NCwtMTEyLjY3NzR6IiBmaWxsPSIjMTdjZGU2IiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIwIiBzdHJva2UtbGluZWNhcD0iYnV0dCIvPjxwYXRoIGQ9Ik0yNDEuNjc3NzgsMTQ3LjA4OTUzdjAuNDU5NjJjMi42OTA2NCwwLjI5NzU3IDUuMzUzMTYsMC44MDk5MSA3Ljk2MjEsMS41MzIwNmMyLjc5MzQ0LDAuNzkxNTUgNS4yODQ1NywxLjgxNjUxIDcuNDczMzcsMy4wNzQ4NGMxLjY0NiwwLjkzNzAxIDMuMTc1ODMsMi4wNjQ3MiA0LjU1Nzg3LDMuMzU5OGMzLjUyOTU0LDMuMzQzOTcgNS44MTE1Miw3Ljc5MjgzIDYuNDY4MzQsMTIuNjEwMzZjMC4yMzgsMS41NTU5IDAuMzU0NzksMy4xMjc5MSAwLjM0OTMxLDQuNzAxODhjMC4wMzMzNCw0LjAxNjE1IC0wLjY2NDkzLDguMDA0ODEgLTIuMDYwNjIsMTEuNzcwNzljLTAuNjk4NjIsMS44NTAxMSAtMS41NzMzNSwzLjYyODgzIC0yLjYxMjE2LDUuMzExNjRjLTIuMTA2MjIsMy4zNjc4NyAtNC44OTQ3Miw2LjI1NzEgLTguMTg1NzgsOC40ODE0NmMtMS45NjE1NywxLjM0MzYgLTQuMDQyMTIsMi41MDQ2NSAtNi4yMTU1NSwzLjQ2ODU4Yy01LjA1ODg1LDIuMjY5NSAtMTEuMjUxNDIsMy42NTQ0NiAtMTguNTc3NzEsNC4xNTQ5NGMtMi4xNTQxMiwwLjE0MzgxIC00LjMxMjUxLDAuMjE0MzMgLTYuNDcxNDEsMC4yMTE0MmMtMTEuODQ3OTEsMCAtMjIuMzY4MDIsLTEuOTkxNjcgLTMxLjU2MDM2LC01Ljk3NTAydi0xOS42MTAzMmM0LjY5ODMxLDIuMzQ5MTUgOS42MjY0Miw0LjEzNjU1IDE0Ljc4NDM0LDUuMzYyMmM1LjE1NzkzLDEuMjI1NjUgOS45MzI4MywxLjgzODQ3IDE0LjMyNDczLDEuODM4NDdjOC4yNzMxLDAgMTQuMDY5MzksLTEuNDI5OTEgMTcuMzg4ODQsLTQuMjg5NzZjMi40NzQyNCwtMi4xNDA1NyA0LjExOTE2LC01LjA4MTI2IDQuNjQ4MjYsLTguMzA5ODdjMC4yMjYzMywtMS4yNTE2NCAwLjMzNzEsLTIuNTIxNDQgMC4zMzA5MiwtMy43OTMzN2MwLC0zLjA2NDExIC0wLjc2NjAzLC01LjY0MzA3IC0yLjI5ODA4LC03LjczNjg4Yy0wLjY4ODk0LC0wLjkxOSAtMS41MzI5OSwtMS43MTA3MSAtMi40OTQxOSwtMi4zMzk0NWMtMC44MTcwOSwtMC41NDk1IC0xLjc1ODgsLTEuMDQzODQgLTIuODI1MTEsLTEuNDgzMDNjLTAuODg5MDUsLTAuMzYyMDcgLTEuNzk4NjIsLTAuNjcxNTggLTIuNzI0LC0wLjkyNjg5Yy0xLjMyNjEsLTAuMzYyNjUgLTIuNjcyNiwtMC42NDYwOCAtNC4wMzIzNywtMC44NDg3NmMtMi44MTg5OCwtMC40MzYxMiAtNi4xNjM5OCwtMC42ODQ4MyAtMTAuMDM0OTcsLTAuNzQ2MTFjLTAuNjQ3NTEsLTAuMDA5NzMgLTEuMjk1MDYsLTAuMDE0MzIgLTEuOTQyNjUsLTAuMDEzNzloLTguMjczMXYtMTcuNzcxODVoOC40MjYzMWM2Ljc0MTA1LDAgMTEuODczNDQsLTAuNjM4MzUgMTUuMzk3MTcsLTEuOTE1MDdjMS4wMTMxOSwtMC4zNjEwNCAxLjk5NjY4LC0wLjgwMDU1IDIuOTQxNTUsLTEuMzE0NWMxLjk1Njk0LC0xLjA3ODU3IDMuMzc2NjUsLTIuMzc2NzIgNC4yNTkxMiwtMy44OTQ0OWMxLjI3NjcyLC0yLjE5NTk0IDEuOTE1MDcsLTQuNzIzODQgMS45MTUwNywtNy41ODM2OGMwLC0zLjg4MTIgLTEuMjAwMTEsLTYuOTE5NzkgLTMuNjAwMzMsLTkuMTE1NzNjLTAuOTc3NDIsLTAuODY3NDIgLTIuMTA5NDEsLTEuNTQzMDcgLTMuMzM2ODIsLTEuOTkxNjdjLTIuMjYwMywtMC44NjgxNyAtNS4xNTY5LC0xLjMwMjI1IC04LjY4OTgyLC0xLjMwMjI1Yy01LjE2MzAzLDAgLTkuNjQ4ODksMC44Nzc4NyAtMTMuNDU3NTgsMi42MzM2Yy0wLjAzMzc1LDAuMDE1NzUgLTAuMDY3NDYsMC4wMzE1OSAtMC4xMDExMiwwLjA0NzQ5Yy0zLjgzMDE0LDEuNzg3NCAtNy4wNzMsMy41NDkyNyAtOS43Mjg1Niw1LjI4NTU5bC0xMC43MjQzOSwtMTUuOTMzMzljMi44MDQ5MywtMS45ODcyIDUuNzg3OCwtMy43MTA0NiA4LjkxMDQ0LC01LjE0NzcxYzIuMDE5MSwtMC45MzUwNiA0LjA4MTU0LC0xLjc3MzUyIDYuMTgwMzEsLTIuNTEyNTdjMi44NzMzMSwtMC45OTk5IDUuODMxNjUsLTEuNzM2MjggOC44Mzg0MywtMi4yMDAwM2MyLjkyODI4LC0wLjQ2Njc2IDYuMDYwMzEsLTAuNzQ0NTggOS4zOTYxLC0wLjgzMzQ0YzAuNzkwMzcsLTAuMDIwNyAxLjU4MDk5LC0wLjAzMDkyIDIuMzcxNjIsLTAuMDMwNjRjMy41NjQ5OSwtMC4wMTkwMyA3LjEyNTksMC4yNDQyOSAxMC42NDkzMiwwLjc4NzQ4YzMuNzcxOTIsMC42MDI2IDcuMTg5NDMsMS41NTI0OCAxMC4yNTI1MiwyLjg0OTYyYzIuMDQ3MjEsMC44NTkyMSA0LjAwMTUsMS45MjQ5MSA1LjgzMjU0LDMuMTgwNTVjMy4wMjEwMywyLjAxNTE3IDUuNTA5ODksNC43MzEwOCA3LjI1NDI5LDcuOTE2MTNjMS43NTE2NSwzLjI3NDUxIDIuNjI3NDgsNy4wNDQ5IDIuNjI3NDgsMTEuMzExMTdjMC4wMjI4MywyLjY0OTMyIC0wLjI5OTU4LDUuMjkwMzYgLTAuOTU5MDcsNy44NTYzOGMtMC45OTAyNiwzLjg3NTMyIC0zLjAxNTMyLDcuNDA4NTggLTUuODU4NTgsMTAuMjIxODhjLTQuMjg3OSw0LjIxNDEgLTkuNTg4MjgsNy4yNTM2NiAtMTUuMzkxMDQsOC44MjYxOGMtMC40NTk1NCwwLjEyOTkzIC0wLjkyMTI3LDAuMjUyMDEgLTEuMzg0OTgsMC4zNjYxNnoiIGZpbGw9IiNmZmZmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjZmZmZmZmIiBzdHJva2Utd2lkdGg9IjIuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PHBhdGggZD0iTTM1Ny4xOTQ4MiwxODUuMjM3NzN2MTkuNDU3MTFoLTc2LjQ0OTYxdi0xNi4wODY1OWwyNy40MjM4MSwtMjcuNzMwMjJjNS41MTU0LC01LjcxOTY5IDkuOTU4MzcsLTEwLjQ5NDU5IDEzLjMyODg5LC0xNC4zMjQ3M2MxLjE0MDMxLC0xLjI5MDI0IDIuMjMzMzcsLTIuNjIxNDQgMy4yNzcwNywtMy45OTEwMWMxLjczNjMzLC0yLjI4ODg5IDMuMDk1MjYsLTQuNDMxMjMgNC4wNzY4LC02LjQyNjk4YzEuNTMyMDYsLTMuMTE1MTggMi4yOTgwOCwtNi40NjAxOCAyLjI5ODA4LC0xMC4wMzQ5N2MwLC00LjM5MTkgLTEuMjAwMTEsLTcuNjYwMjggLTMuNjAwMzMsLTkuODA1MTZjLTEuNDk5NjUsLTEuMzE5NjEgLTMuMzAxMzQsLTIuMjQ5NCAtNS4yNDU3NiwtMi43MDcxNGMtMS4yMzI3OCwtMC4zMDg0NiAtMi41NzUzOSwtMC40NzY5OSAtNC4wMjc3OCwtMC41MDU1OGMtMC4xNTE2NiwtMC4wMDMwNSAtMC4zMDMzMywtMC4wMDQ1NyAtMC40NTUwMiwtMC4wMDQ2Yy00LjExNSwwLjAxMzE5IC04LjE3NDk2LDAuOTQ2NTQgLTExLjg4MjYzLDIuNzMxNjZjLTAuMTI1LDAuMDU4OTQgLTAuMjQ5NiwwLjExODY5IC0wLjM3MzgyLDAuMTc5MjVjLTEuOTc2NDgsMC45NzE0NSAtMy44OTIwMiwyLjA2MjI1IC01LjczNjAyLDMuMjY2MzRjLTEuODcxMTYsMS4yMTMzOSAtMy43ODM2NywyLjU5MTIzIC01LjczNzU1LDQuMTMzNDljLTAuMzY1MjksMC4yODgzNSAtMC43Mjg0LDAuNTc5NDQgLTEuMDg5MjksMC44NzMyN2wtMTIuNTYyODYsLTE0Ljg2MDk1YzMuMTY2MjUsLTIuNzU3NyA2LjUxMTI0LC01LjMxMTEzIDEwLjAzNDk3LC03LjY2MDI4YzIuMjk2ODQsLTEuNTE5MDggNC43Mjg4LC0yLjgyMzI3IDcuMjY1MDEsLTMuODk2MDJjMS42MzM0MywtMC42OTU0OCAzLjI5OTE1LC0xLjMxMjU3IDQuOTkxNDQsLTEuODQ5MTljNC42NDcyNSwtMS40ODA5OSAxMC4yMzkyNSwtMi4yMjE0OCAxNi43NzYwMiwtMi4yMjE0OGMyLjk1MzkxLC0wLjAxNTA2IDUuOTAzMjMsMC4yMzUxNiA4LjgxMjM5LDAuNzQ3NjRjMy41MDMzMSwwLjYzNTI4IDYuNjk3MTIsMS42NzY1OSA5LjU4MTQ4LDMuMTIzODZjMC4wMjI0OSwwLjAxMTcyIDAuMDQ0OTcsMC4wMjM0NyAwLjA2NzQxLDAuMDM1MjRjMi44MTA3NCwxLjM5Mzg2IDUuMzk4NTgsMy4xOTc5MiA3LjY3ODY3LDUuMzUzYzEuNjMxNSwxLjU1ODQ4IDMuMDY1OTcsMy4zMTA5MyA0LjI3MTM3LDUuMjE4MThjMi44MDg3Niw0LjQ0Mjk2IDQuMjEzMTUsOS40NzMyMiA0LjIxMzE1LDE1LjA5MDc1YzAuMDIzMDQsMy43ODk2NiAtMC40OTkxMSw3LjU2MjkzIC0xLjU1MDQ0LDExLjIwMzkzYy0wLjUzODM4LDEuODMzMDMgLTEuMjIzOSwzLjYxOTYxIC0yLjA0OTg5LDUuMzQyMjhjLTEuNDI5ODcsMi45NDcyMiAtMy4xMjYyOCw1Ljc1NzQ3IC01LjA2ODA0LDguMzk1NjdjLTEuNjU1MDMsMi4yNTgxIC0zLjQ0MTQ0LDQuNDE2OTIgLTUuMzQ5OTQsNi40NjUyOGMtMi4xMjQ1NiwyLjI4NTM1IC00LjI5NDM4LDQuNTI4MjIgLTYuNTA4MTgsNi43MjcyNmMtMi4yNjY0MiwyLjI1NzIyIC00LjcwMDg1LDQuNjA0ODYgLTcuMzAzMzEsNy4wNDI4NmMtMC44ODI0MiwwLjgyNzExIC0xLjc2ODQ3LDEuNjUwMzUgLTIuNjU4MTIsMi40Njk2N2wtMTQuMDk0OTIsMTMuMTc1Njh2MS4wNzI0NHoiIGZpbGw9IiNmZmZmZmYiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlPSIjZmZmZmZmIiBzdHJva2Utd2lkdGg9IjIuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6MTEyLjY3NzEzOjExMi42NzczNDUwMDAwMDAwMy0tPg==";
- const isNumberBits = bits => {
+ const isNumberBits = (bits) => {
return /^-?[01]+$/.test(bits);
};
- const number2bits = number => {
+ const number2bits = (number) => {
return Scratch.Cast.toNumber(number).toString(2);
};
- const bits2number = bits => {
- return /^-?[01]+$/.test(bits) ? (parseInt(bits, 2) || 0) : 0;
+ const bits2number = (bits) => {
+ return /^-?[01]+$/.test(bits) ? parseInt(bits, 2) || 0 : 0;
};
const circularRightShift = (number, k) => {
@@ -25,224 +27,222 @@
};
class Bitwise {
-
getInfo() {
return {
+ id: "Bitwise",
+ name: "Bitwise",
- id: 'Bitwise',
- name: 'Bitwise',
-
- color1: '#17cde6',
+ color1: "#17cde6",
docsURI: "https://extensions.turbowarp.org/bitwise",
menuIconURI: icon,
blocks: [
{
- opcode: 'isNumberBits',
+ opcode: "isNumberBits",
blockType: Scratch.BlockType.BOOLEAN,
- text: 'is [CENTRAL] binary?',
+ text: "is [CENTRAL] binary?",
arguments: {
CENTRAL: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0000000000100000'
- }
- }
+ defaultValue: "0000000000100000",
+ },
+ },
},
- '---',
+ "---",
{
- opcode: 'toNumberBits',
+ opcode: "toNumberBits",
blockType: Scratch.BlockType.REPORTER,
- text: '[CENTRAL] to binary',
+ text: "[CENTRAL] to binary",
arguments: {
CENTRAL: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '32'
- }
- }
+ defaultValue: "32",
+ },
+ },
},
{
- opcode: 'ofNumberBits',
+ opcode: "ofNumberBits",
blockType: Scratch.BlockType.REPORTER,
- text: '[CENTRAL] to number',
+ text: "[CENTRAL] to number",
arguments: {
CENTRAL: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0000000000100000'
- }
- }
+ defaultValue: "0000000000100000",
+ },
+ },
},
- '---',
+ "---",
{
- opcode: 'bitwiseRightShift',
+ opcode: "bitwiseRightShift",
blockType: Scratch.BlockType.REPORTER,
- text: '[LEFT] >> [RIGHT]',
+ text: "[LEFT] >> [RIGHT]",
arguments: {
LEFT: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
+ defaultValue: "",
},
RIGHT: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'bitwiseLeftShift',
+ opcode: "bitwiseLeftShift",
blockType: Scratch.BlockType.REPORTER,
- text: '[LEFT] << [RIGHT]',
+ text: "[LEFT] << [RIGHT]",
arguments: {
LEFT: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
+ defaultValue: "",
},
RIGHT: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'bitwiseLogicalRightShift',
+ opcode: "bitwiseLogicalRightShift",
blockType: Scratch.BlockType.REPORTER,
- text: '[LEFT] >>> [RIGHT]',
+ text: "[LEFT] >>> [RIGHT]",
arguments: {
LEFT: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
+ defaultValue: "",
},
RIGHT: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
- {
- opcode: 'bitwiseCircularRightShift',
+ {
+ opcode: "bitwiseCircularRightShift",
blockType: Scratch.BlockType.REPORTER,
- text: '[LEFT] ↻ [RIGHT]',
+ text: "[LEFT] ↻ [RIGHT]",
arguments: {
LEFT: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
+ defaultValue: "",
},
RIGHT: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'bitwiseCircularLeftShift',
+ opcode: "bitwiseCircularLeftShift",
blockType: Scratch.BlockType.REPORTER,
- text: '[LEFT] ↺ [RIGHT]',
+ text: "[LEFT] ↺ [RIGHT]",
arguments: {
LEFT: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
+ defaultValue: "",
},
RIGHT: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
- '---',
+ "---",
{
- opcode: 'bitwiseAnd',
+ opcode: "bitwiseAnd",
blockType: Scratch.BlockType.REPORTER,
- text: '[LEFT] and [RIGHT]',
+ text: "[LEFT] and [RIGHT]",
arguments: {
LEFT: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
+ defaultValue: "",
},
RIGHT: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'bitwiseOr',
+ opcode: "bitwiseOr",
blockType: Scratch.BlockType.REPORTER,
- text: '[LEFT] or [RIGHT]',
+ text: "[LEFT] or [RIGHT]",
arguments: {
LEFT: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
+ defaultValue: "",
},
RIGHT: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'bitwiseXor',
+ opcode: "bitwiseXor",
blockType: Scratch.BlockType.REPORTER,
- text: '[LEFT] xor [RIGHT]',
+ text: "[LEFT] xor [RIGHT]",
arguments: {
LEFT: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
+ defaultValue: "",
},
RIGHT: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
- opcode: 'bitwiseNot',
+ opcode: "bitwiseNot",
blockType: Scratch.BlockType.REPORTER,
- text: 'not [CENTRAL]',
+ text: "not [CENTRAL]",
arguments: {
CENTRAL: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: ''
- }
- }
- }
- ]
+ defaultValue: "",
+ },
+ },
+ },
+ ],
};
}
- isNumberBits({CENTRAL}) {
+ isNumberBits({ CENTRAL }) {
return isNumberBits(CENTRAL);
}
- toNumberBits({CENTRAL}) {
+ toNumberBits({ CENTRAL }) {
return number2bits(CENTRAL);
}
- ofNumberBits({CENTRAL}) {
+ ofNumberBits({ CENTRAL }) {
return bits2number(CENTRAL);
}
- bitwiseRightShift({LEFT, RIGHT}) {
+ bitwiseRightShift({ LEFT, RIGHT }) {
return LEFT >> RIGHT;
}
- bitwiseLeftShift({LEFT, RIGHT}) {
+ bitwiseLeftShift({ LEFT, RIGHT }) {
return LEFT << RIGHT;
}
- bitwiseLogicalRightShift({LEFT, RIGHT}) {
+ bitwiseLogicalRightShift({ LEFT, RIGHT }) {
return LEFT >>> RIGHT;
}
- bitwiseCircularRightShift({LEFT, RIGHT}) {
+ bitwiseCircularRightShift({ LEFT, RIGHT }) {
return circularRightShift(LEFT, RIGHT);
}
- bitwiseCircularLeftShift({LEFT, RIGHT}) {
+ bitwiseCircularLeftShift({ LEFT, RIGHT }) {
return circularLeftShift(LEFT, RIGHT);
}
- bitwiseAnd({LEFT, RIGHT}) {
+ bitwiseAnd({ LEFT, RIGHT }) {
return LEFT & RIGHT;
}
- bitwiseOr({LEFT, RIGHT}) {
+ bitwiseOr({ LEFT, RIGHT }) {
return LEFT | RIGHT;
}
- bitwiseXor({LEFT, RIGHT}) {
+ bitwiseXor({ LEFT, RIGHT }) {
return LEFT ^ RIGHT;
}
- bitwiseNot({CENTRAL}) {
+ bitwiseNot({ CENTRAL }) {
return ~CENTRAL;
}
}
diff --git a/extensions/box2d.js b/extensions/box2d.js
index d554534600..42df1ab034 100644
--- a/extensions/box2d.js
+++ b/extensions/box2d.js
@@ -1,4 +1,5 @@
// Name: Box2D Physics
+// ID: griffpatch
// Description: Two dimensional physics.
// Original: griffpatch
@@ -8,11 +9,11 @@
/* eslint-disable */
-(function(Scratch) {
- 'use strict';
+(function (Scratch) {
+ "use strict";
if (!Scratch.extensions.unsandboxed) {
- throw new Error('Box2D must be run unsandboxed');
+ throw new Error("Box2D must be run unsandboxed");
}
// First we need to load the Box2D physics library that this extension uses.
@@ -20,22 +21,22 @@
// the source code below. Yes, this is really ugly.
/*!
- * Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com
- *
- * This software is provided 'as-is', without any express or implied
- * warranty. In no event will the authors be held liable for any damages
- * arising from the use of this software.
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- * 1. The origin of this software must not be misrepresented; you must not
- * claim that you wrote the original software. If you use this software
- * in a product, an acknowledgment in the product documentation would be
- * appreciated but is not required.
- * 2. Altered source versions must be plainly marked as such, and must not be
- * misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- */
+ * Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
var Box2D = {};
(function (a2j, undefined) {
@@ -82,7 +83,8 @@
//package structure
if (typeof Box2D === "undefined") Box2D = {};
if (typeof Box2D.Collision === "undefined") Box2D.Collision = {};
- if (typeof Box2D.Collision.Shapes === "undefined") Box2D.Collision.Shapes = {};
+ if (typeof Box2D.Collision.Shapes === "undefined")
+ Box2D.Collision.Shapes = {};
if (typeof Box2D.Common === "undefined") Box2D.Common = {};
if (typeof Box2D.Common.Math === "undefined") Box2D.Common.Math = {};
if (typeof Box2D.Dynamics === "undefined") Box2D.Dynamics = {};
@@ -173,7 +175,8 @@
function b2Manifold() {
b2Manifold.b2Manifold.apply(this, arguments);
- if (this.constructor === b2Manifold) this.b2Manifold.apply(this, arguments);
+ if (this.constructor === b2Manifold)
+ this.b2Manifold.apply(this, arguments);
}
Box2D.Collision.b2Manifold = b2Manifold;
@@ -519,13 +522,19 @@
Box2D.Dynamics.Controllers.b2BuoyancyController = b2BuoyancyController;
function b2ConstantAccelController() {
- b2ConstantAccelController.b2ConstantAccelController.apply(this, arguments);
+ b2ConstantAccelController.b2ConstantAccelController.apply(
+ this,
+ arguments
+ );
}
Box2D.Dynamics.Controllers.b2ConstantAccelController =
b2ConstantAccelController;
function b2ConstantForceController() {
- b2ConstantForceController.b2ConstantForceController.apply(this, arguments);
+ b2ConstantForceController.b2ConstantForceController.apply(
+ this,
+ arguments
+ );
}
Box2D.Dynamics.Controllers.b2ConstantForceController =
b2ConstantForceController;
@@ -546,7 +555,10 @@
Box2D.Dynamics.Controllers.b2GravityController = b2GravityController;
function b2TensorDampingController() {
- b2TensorDampingController.b2TensorDampingController.apply(this, arguments);
+ b2TensorDampingController.b2TensorDampingController.apply(
+ this,
+ arguments
+ );
}
Box2D.Dynamics.Controllers.b2TensorDampingController =
b2TensorDampingController;
@@ -606,7 +618,8 @@
function b2JointDef() {
b2JointDef.b2JointDef.apply(this, arguments);
- if (this.constructor === b2JointDef) this.b2JointDef.apply(this, arguments);
+ if (this.constructor === b2JointDef)
+ this.b2JointDef.apply(this, arguments);
}
Box2D.Dynamics.Joints.b2JointDef = b2JointDef;
@@ -961,7 +974,13 @@
var separation = v2X * normal1WorldX + v2Y * normal1WorldY;
return separation;
};
- b2Collision.FindMaxSeparation = function (edgeIndex, poly1, xf1, poly2, xf2) {
+ b2Collision.FindMaxSeparation = function (
+ edgeIndex,
+ poly1,
+ xf1,
+ poly2,
+ xf2
+ ) {
var count1 = parseInt(poly1.m_vertexCount);
var normals1 = poly1.m_normals;
var tVec;
@@ -1007,7 +1026,8 @@
return s;
}
while (true) {
- if (increment == -1) edge = bestEdge - 1 >= 0 ? bestEdge - 1 : count1 - 1;
+ if (increment == -1)
+ edge = bestEdge - 1 >= 0 ? bestEdge - 1 : count1 - 1;
else edge = bestEdge + 1 < count1 ? bestEdge + 1 : 0;
s = b2Collision.EdgeSeparation(poly1, xf1, edge, poly2, xf2);
if (s > bestSeparation) {
@@ -1053,16 +1073,20 @@
tClip = c[0];
tVec = vertices2[i1];
tMat = xf2.R;
- tClip.v.x = xf2.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y);
- tClip.v.y = xf2.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y);
+ tClip.v.x =
+ xf2.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y);
+ tClip.v.y =
+ xf2.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y);
tClip.id.features.referenceEdge = edge1;
tClip.id.features.incidentEdge = i1;
tClip.id.features.incidentVertex = 0;
tClip = c[1];
tVec = vertices2[i2];
tMat = xf2.R;
- tClip.v.x = xf2.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y);
- tClip.v.y = xf2.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y);
+ tClip.v.x =
+ xf2.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y);
+ tClip.v.y =
+ xf2.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y);
tClip.id.features.referenceEdge = edge1;
tClip.id.features.incidentEdge = i2;
tClip.id.features.incidentVertex = 1;
@@ -1160,13 +1184,17 @@
var v11 = b2Collision.s_v11;
var v12 = b2Collision.s_v12;
v11.x =
- xf1.position.x + (tMat.col1.x * local_v11.x + tMat.col2.x * local_v11.y);
+ xf1.position.x +
+ (tMat.col1.x * local_v11.x + tMat.col2.x * local_v11.y);
v11.y =
- xf1.position.y + (tMat.col1.y * local_v11.x + tMat.col2.y * local_v11.y);
+ xf1.position.y +
+ (tMat.col1.y * local_v11.x + tMat.col2.y * local_v11.y);
v12.x =
- xf1.position.x + (tMat.col1.x * local_v12.x + tMat.col2.x * local_v12.y);
+ xf1.position.x +
+ (tMat.col1.x * local_v12.x + tMat.col2.x * local_v12.y);
v12.y =
- xf1.position.y + (tMat.col1.y * local_v12.x + tMat.col2.y * local_v12.y);
+ xf1.position.y +
+ (tMat.col1.y * local_v12.x + tMat.col2.y * local_v12.y);
var frontOffset = normal.x * v11.x + normal.y * v11.y;
var sideOffset1 = -tangent.x * v11.x - tangent.y * v11.y + totalRadius;
var sideOffset2 = tangent.x * v12.x + tangent.y * v12.y + totalRadius;
@@ -1207,7 +1235,13 @@
}
manifold.m_pointCount = pointCount;
};
- b2Collision.CollideCircles = function (manifold, circle1, xf1, circle2, xf2) {
+ b2Collision.CollideCircles = function (
+ manifold,
+ circle1,
+ xf1,
+ circle2,
+ xf2
+ ) {
manifold.m_pointCount = 0;
var tMat;
var tVec;
@@ -1493,7 +1527,9 @@
}
b2Distance.b2_gjkMaxIters = b2Math.Max(b2Distance.b2_gjkMaxIters, iter);
simplex.GetWitnessPoints(output.pointA, output.pointB);
- output.distance = b2Math.SubtractVV(output.pointA, output.pointB).Length();
+ output.distance = b2Math
+ .SubtractVV(output.pointA, output.pointB)
+ .Length();
output.iterations = iter;
simplex.WriteCache(cache);
if (input.useRadii) {
@@ -1752,17 +1788,21 @@
var child2 = sibling.child2;
var norm1 =
Math.abs(
- (child1.aabb.lowerBound.x + child1.aabb.upperBound.x) / 2 - center.x
+ (child1.aabb.lowerBound.x + child1.aabb.upperBound.x) / 2 -
+ center.x
) +
Math.abs(
- (child1.aabb.lowerBound.y + child1.aabb.upperBound.y) / 2 - center.y
+ (child1.aabb.lowerBound.y + child1.aabb.upperBound.y) / 2 -
+ center.y
);
var norm2 =
Math.abs(
- (child2.aabb.lowerBound.x + child2.aabb.upperBound.x) / 2 - center.x
+ (child2.aabb.lowerBound.x + child2.aabb.upperBound.x) / 2 -
+ center.x
) +
Math.abs(
- (child2.aabb.lowerBound.y + child2.aabb.upperBound.y) / 2 - center.y
+ (child2.aabb.lowerBound.y + child2.aabb.upperBound.y) / 2 -
+ center.y
);
if (norm1 < norm2) {
sibling = child1;
@@ -2279,15 +2319,19 @@
tVec = this.m_localPoint;
tMat = transformB.R;
pointBX =
- transformB.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y);
+ transformB.position.x +
+ (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y);
pointBY =
- transformB.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y);
+ transformB.position.y +
+ (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y);
tVec = localPointA;
tMat = transformA.R;
pointAX =
- transformA.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y);
+ transformA.position.x +
+ (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y);
pointAY =
- transformA.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y);
+ transformA.position.y +
+ (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y);
sgn = (pointAX - pointBX) * normalX + (pointAY - pointBY) * normalY;
if (s < 0.0) {
this.m_axis.NegativeSelf();
@@ -2306,15 +2350,19 @@
tVec = this.m_localPoint;
tMat = transformA.R;
pointAX =
- transformA.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y);
+ transformA.position.x +
+ (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y);
pointAY =
- transformA.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y);
+ transformA.position.y +
+ (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y);
tVec = localPointB;
tMat = transformB.R;
pointBX =
- transformB.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y);
+ transformB.position.x +
+ (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y);
pointBY =
- transformB.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y);
+ transformB.position.y +
+ (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y);
sgn = (pointBX - pointAX) * normalX + (pointBY - pointAY) * normalY;
if (s < 0.0) {
this.m_axis.NegativeSelf();
@@ -2322,7 +2370,10 @@
}
}
};
- b2SeparationFunction.prototype.Evaluate = function (transformA, transformB) {
+ b2SeparationFunction.prototype.Evaluate = function (
+ transformA,
+ transformB
+ ) {
var axisA;
var axisB;
var localPointA;
@@ -3137,7 +3188,8 @@
};
b2CircleShape.prototype.ComputeMass = function (massData, density) {
if (density === undefined) density = 0;
- massData.mass = density * b2Settings.b2_pi * this.m_radius * this.m_radius;
+ massData.mass =
+ density * b2Settings.b2_pi * this.m_radius * this.m_radius;
massData.center.SetV(this.m_p);
massData.I =
massData.mass *
@@ -3399,7 +3451,10 @@
this.m_nextEdge = null;
this.m_v1 = v1;
this.m_v2 = v2;
- this.m_direction.Set(this.m_v2.x - this.m_v1.x, this.m_v2.y - this.m_v1.y);
+ this.m_direction.Set(
+ this.m_v2.x - this.m_v1.x,
+ this.m_v2.y - this.m_v1.y
+ );
this.m_length = this.m_direction.Normalize();
this.m_normal.Set(this.m_direction.y, -this.m_direction.x);
this.m_coreV1.Set(
@@ -3417,13 +3472,23 @@
this.m_cornerDir1 = this.m_normal;
this.m_cornerDir2.Set(-this.m_normal.x, -this.m_normal.y);
};
- b2EdgeShape.prototype.SetPrevEdge = function (edge, core, cornerDir, convex) {
+ b2EdgeShape.prototype.SetPrevEdge = function (
+ edge,
+ core,
+ cornerDir,
+ convex
+ ) {
this.m_prevEdge = edge;
this.m_coreV1 = core;
this.m_cornerDir1 = cornerDir;
this.m_cornerConvex1 = convex;
};
- b2EdgeShape.prototype.SetNextEdge = function (edge, core, cornerDir, convex) {
+ b2EdgeShape.prototype.SetNextEdge = function (
+ edge,
+ core,
+ cornerDir,
+ convex
+ ) {
this.m_nextEdge = edge;
this.m_coreV2 = core;
this.m_cornerDir2 = cornerDir;
@@ -3525,7 +3590,12 @@
polygonShape.SetAsBox(hx, hy);
return polygonShape;
};
- b2PolygonShape.prototype.SetAsOrientedBox = function (hx, hy, center, angle) {
+ b2PolygonShape.prototype.SetAsOrientedBox = function (
+ hx,
+ hy,
+ center,
+ angle
+ ) {
if (hx === undefined) hx = 0;
if (hy === undefined) hy = 0;
if (center === undefined) center = null;
@@ -3650,8 +3720,10 @@
b2PolygonShape.prototype.ComputeAABB = function (aabb, xf) {
var tMat = xf.R;
var tVec = this.m_vertices[0];
- var lowerX = xf.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y);
- var lowerY = xf.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y);
+ var lowerX =
+ xf.position.x + (tMat.col1.x * tVec.x + tMat.col2.x * tVec.y);
+ var lowerY =
+ xf.position.y + (tMat.col1.y * tVec.x + tMat.col2.y * tVec.y);
var upperX = lowerX;
var upperY = lowerY;
for (var i = 1; i < this.m_vertexCount; ++i) {
@@ -3707,11 +3779,13 @@
var ey2 = e2Y;
var intx2 =
k_inv3 *
- (0.25 * (ex1 * ex1 + ex2 * ex1 + ex2 * ex2) + (px * ex1 + px * ex2)) +
+ (0.25 * (ex1 * ex1 + ex2 * ex1 + ex2 * ex2) +
+ (px * ex1 + px * ex2)) +
0.5 * px * px;
var inty2 =
k_inv3 *
- (0.25 * (ey1 * ey1 + ey2 * ey1 + ey2 * ey2) + (py * ey1 + py * ey2)) +
+ (0.25 * (ey1 * ey1 + ey2 * ey1 + ey2 * ey2) +
+ (py * ey1 + py * ey2)) +
0.5 * py * py;
I += D * (intx2 + inty2);
}
@@ -3929,8 +4003,10 @@
var centerX = 0.5 * (lowerX + upperX);
var centerY = 0.5 * (lowerY + upperY);
var tMat = obb.R;
- obb.center.x = root.x + (tMat.col1.x * centerX + tMat.col2.x * centerY);
- obb.center.y = root.y + (tMat.col1.y * centerX + tMat.col2.y * centerY);
+ obb.center.x =
+ root.x + (tMat.col1.x * centerX + tMat.col2.x * centerY);
+ obb.center.y =
+ root.y + (tMat.col1.y * centerX + tMat.col2.y * centerY);
obb.extents.x = 0.5 * (upperX - lowerX);
obb.extents.y = 0.5 * (upperY - lowerY);
}
@@ -4408,8 +4484,14 @@
return C;
};
b2Math.MulTMM = function (A, B) {
- var c1 = new b2Vec2(b2Math.Dot(A.col1, B.col1), b2Math.Dot(A.col2, B.col1));
- var c2 = new b2Vec2(b2Math.Dot(A.col1, B.col2), b2Math.Dot(A.col2, B.col2));
+ var c1 = new b2Vec2(
+ b2Math.Dot(A.col1, B.col1),
+ b2Math.Dot(A.col2, B.col1)
+ );
+ var c2 = new b2Vec2(
+ b2Math.Dot(A.col1, B.col2),
+ b2Math.Dot(A.col2, B.col2)
+ );
var C = b2Mat22.FromVV(c1, c2);
return C;
};
@@ -4784,7 +4866,8 @@
b2CircleContact = Box2D.Dynamics.Contacts.b2CircleContact,
b2Contact = Box2D.Dynamics.Contacts.b2Contact,
b2ContactConstraint = Box2D.Dynamics.Contacts.b2ContactConstraint,
- b2ContactConstraintPoint = Box2D.Dynamics.Contacts.b2ContactConstraintPoint,
+ b2ContactConstraintPoint =
+ Box2D.Dynamics.Contacts.b2ContactConstraintPoint,
b2ContactEdge = Box2D.Dynamics.Contacts.b2ContactEdge,
b2ContactFactory = Box2D.Dynamics.Contacts.b2ContactFactory,
b2ContactRegister = Box2D.Dynamics.Contacts.b2ContactRegister,
@@ -4795,7 +4878,8 @@
b2PolyAndCircleContact = Box2D.Dynamics.Contacts.b2PolyAndCircleContact,
b2PolyAndEdgeContact = Box2D.Dynamics.Contacts.b2PolyAndEdgeContact,
b2PolygonContact = Box2D.Dynamics.Contacts.b2PolygonContact,
- b2PositionSolverManifold = Box2D.Dynamics.Contacts.b2PositionSolverManifold,
+ b2PositionSolverManifold =
+ Box2D.Dynamics.Contacts.b2PositionSolverManifold,
b2Controller = Box2D.Dynamics.Controllers.b2Controller,
b2DistanceJoint = Box2D.Dynamics.Joints.b2DistanceJoint,
b2DistanceJointDef = Box2D.Dynamics.Joints.b2DistanceJointDef,
@@ -4988,7 +5072,8 @@
bd.angularDamping = this.m_angularDamping;
bd.angularVelocity = this.m_angularVelocity;
bd.fixedRotation =
- (this.m_flags & b2Body.e_fixedRotationFlag) == b2Body.e_fixedRotationFlag;
+ (this.m_flags & b2Body.e_fixedRotationFlag) ==
+ b2Body.e_fixedRotationFlag;
bd.bullet = (this.m_flags & b2Body.e_bulletFlag) == b2Body.e_bulletFlag;
bd.awake = (this.m_flags & b2Body.e_awakeFlag) == b2Body.e_awakeFlag;
bd.linearDamping = this.m_linearDamping;
@@ -5130,7 +5215,10 @@
this.m_mass = 1.0;
}
this.m_invMass = 1.0 / this.m_mass;
- if (massData.I > 0.0 && (this.m_flags & b2Body.e_fixedRotationFlag) == 0) {
+ if (
+ massData.I > 0.0 &&
+ (this.m_flags & b2Body.e_fixedRotationFlag) == 0
+ ) {
this.m_I =
massData.I -
this.m_mass *
@@ -5317,7 +5405,8 @@
};
b2Body.prototype.IsFixedRotation = function () {
return (
- (this.m_flags & b2Body.e_fixedRotationFlag) == b2Body.e_fixedRotationFlag
+ (this.m_flags & b2Body.e_fixedRotationFlag) ==
+ b2Body.e_fixedRotationFlag
);
};
b2Body.prototype.SetActive = function (flag) {
@@ -5351,7 +5440,9 @@
return (this.m_flags & b2Body.e_activeFlag) == b2Body.e_activeFlag;
};
b2Body.prototype.IsSleepingAllowed = function () {
- return (this.m_flags & b2Body.e_allowSleepFlag) == b2Body.e_allowSleepFlag;
+ return (
+ (this.m_flags & b2Body.e_allowSleepFlag) == b2Body.e_allowSleepFlag
+ );
};
b2Body.prototype.GetFixtureList = function () {
return this.m_fixtureList;
@@ -5565,8 +5656,10 @@
proxyUserDataA,
proxyUserDataB
) {
- var fixtureA = proxyUserDataA instanceof b2Fixture ? proxyUserDataA : null;
- var fixtureB = proxyUserDataB instanceof b2Fixture ? proxyUserDataB : null;
+ var fixtureA =
+ proxyUserDataA instanceof b2Fixture ? proxyUserDataA : null;
+ var fixtureB =
+ proxyUserDataB instanceof b2Fixture ? proxyUserDataB : null;
var bodyA = fixtureA.GetBody();
var bodyB = fixtureB.GetBody();
if (bodyA == bodyB) return;
@@ -5733,7 +5826,11 @@
if (xformScale === undefined) xformScale = 0;
};
b2DebugDraw.prototype.GetXFormScale = function () {};
- b2DebugDraw.prototype.DrawPolygon = function (vertices, vertexCount, color) {
+ b2DebugDraw.prototype.DrawPolygon = function (
+ vertices,
+ vertexCount,
+ color
+ ) {
if (vertexCount === undefined) vertexCount = 0;
};
b2DebugDraw.prototype.DrawSolidPolygon = function (
@@ -5983,8 +6080,10 @@
for (i = 0; i < this.m_bodyCount; ++i) {
b = this.m_bodies[i];
if (b.GetType() != b2Body.b2_dynamicBody) continue;
- b.m_linearVelocity.x += step.dt * (gravity.x + b.m_invMass * b.m_force.x);
- b.m_linearVelocity.y += step.dt * (gravity.y + b.m_invMass * b.m_force.y);
+ b.m_linearVelocity.x +=
+ step.dt * (gravity.x + b.m_invMass * b.m_force.x);
+ b.m_linearVelocity.y +=
+ step.dt * (gravity.y + b.m_invMass * b.m_force.y);
b.m_angularVelocity += step.dt * b.m_invI * b.m_torque;
b.m_linearVelocity.Multiply(
b2Math.Clamp(1.0 - step.dt * b.m_linearDamping, 0.0, 1.0)
@@ -6067,7 +6166,8 @@
if (allowSleep) {
var minSleepTime = Number.MAX_VALUE;
var linTolSqr =
- b2Settings.b2_linearSleepTolerance * b2Settings.b2_linearSleepTolerance;
+ b2Settings.b2_linearSleepTolerance *
+ b2Settings.b2_linearSleepTolerance;
var angTolSqr =
b2Settings.b2_angularSleepTolerance *
b2Settings.b2_angularSleepTolerance;
@@ -6149,7 +6249,8 @@
}
var k_toiBaumgarte = 0.75;
for (i = 0; i < subStep.positionIterations; ++i) {
- var contactsOkay = contactSolver.SolvePositionConstraints(k_toiBaumgarte);
+ var contactsOkay =
+ contactSolver.SolvePositionConstraints(k_toiBaumgarte);
var jointsOkay = true;
for (j = 0; j < this.m_jointCount; ++j) {
var jointOkay = this.m_joints[j].SolvePositionConstraints(
@@ -6874,7 +6975,8 @@
bA = fA.m_body;
bB = fB.m_body;
if (
- (bA.GetType() != b2Body.b2_dynamicBody || bA.IsAwake() == false) &&
+ (bA.GetType() != b2Body.b2_dynamicBody ||
+ bA.IsAwake() == false) &&
(bB.GetType() != b2Body.b2_dynamicBody || bB.IsAwake() == false)
) {
continue;
@@ -7117,7 +7219,8 @@
b2CircleContact = Box2D.Dynamics.Contacts.b2CircleContact,
b2Contact = Box2D.Dynamics.Contacts.b2Contact,
b2ContactConstraint = Box2D.Dynamics.Contacts.b2ContactConstraint,
- b2ContactConstraintPoint = Box2D.Dynamics.Contacts.b2ContactConstraintPoint,
+ b2ContactConstraintPoint =
+ Box2D.Dynamics.Contacts.b2ContactConstraintPoint,
b2ContactEdge = Box2D.Dynamics.Contacts.b2ContactEdge,
b2ContactFactory = Box2D.Dynamics.Contacts.b2ContactFactory,
b2ContactRegister = Box2D.Dynamics.Contacts.b2ContactRegister,
@@ -7128,7 +7231,8 @@
b2PolyAndCircleContact = Box2D.Dynamics.Contacts.b2PolyAndCircleContact,
b2PolyAndEdgeContact = Box2D.Dynamics.Contacts.b2PolyAndEdgeContact,
b2PolygonContact = Box2D.Dynamics.Contacts.b2PolygonContact,
- b2PositionSolverManifold = Box2D.Dynamics.Contacts.b2PositionSolverManifold,
+ b2PositionSolverManifold =
+ Box2D.Dynamics.Contacts.b2PositionSolverManifold,
b2Body = Box2D.Dynamics.b2Body,
b2BodyDef = Box2D.Dynamics.b2BodyDef,
b2ContactFilter = Box2D.Dynamics.b2ContactFilter,
@@ -7241,7 +7345,8 @@
};
b2Contact.prototype.IsContinuous = function () {
return (
- (this.m_flags & b2Contact.e_continuousFlag) == b2Contact.e_continuousFlag
+ (this.m_flags & b2Contact.e_continuousFlag) ==
+ b2Contact.e_continuousFlag
);
};
b2Contact.prototype.SetSensor = function (sensor) {
@@ -7262,7 +7367,9 @@
}
};
b2Contact.prototype.IsEnabled = function () {
- return (this.m_flags & b2Contact.e_enabledFlag) == b2Contact.e_enabledFlag;
+ return (
+ (this.m_flags & b2Contact.e_enabledFlag) == b2Contact.e_enabledFlag
+ );
};
b2Contact.prototype.GetNext = function () {
return this.m_next;
@@ -7554,7 +7661,8 @@
var tMat;
this.m_constraintCount = contactCount;
while (this.m_constraints.length < this.m_constraintCount) {
- this.m_constraints[this.m_constraints.length] = new b2ContactConstraint();
+ this.m_constraints[this.m_constraints.length] =
+ new b2ContactConstraint();
}
for (i = 0; i < contactCount; ++i) {
contact = contacts[i];
@@ -7633,7 +7741,8 @@
var kEqualized =
bodyA.m_mass * bodyA.m_invMass + bodyB.m_mass * bodyB.m_invMass;
kEqualized +=
- bodyA.m_mass * bodyA.m_invI * rnA + bodyB.m_mass * bodyB.m_invI * rnB;
+ bodyA.m_mass * bodyA.m_invI * rnA +
+ bodyB.m_mass * bodyB.m_invI * rnB;
ccp.equalizedMass = 1.0 / kEqualized;
var tangentX = normalY;
var tangentY = -normalX;
@@ -7708,8 +7817,10 @@
var ccp = c.points[j];
ccp.normalImpulse *= step.dtRatio;
ccp.tangentImpulse *= step.dtRatio;
- var PX = ccp.normalImpulse * normalX + ccp.tangentImpulse * tangentX;
- var PY = ccp.normalImpulse * normalY + ccp.tangentImpulse * tangentY;
+ var PX =
+ ccp.normalImpulse * normalX + ccp.tangentImpulse * tangentX;
+ var PY =
+ ccp.normalImpulse * normalY + ccp.tangentImpulse * tangentY;
bodyA.m_angularVelocity -= invIA * (ccp.rA.x * PY - ccp.rA.y * PX);
bodyA.m_linearVelocity.x -= invMassA * PX;
bodyA.m_linearVelocity.y -= invMassA * PY;
@@ -7992,7 +8103,8 @@
var rAY = point.y - bodyA.m_sweep.c.y;
var rBX = point.x - bodyB.m_sweep.c.x;
var rBY = point.y - bodyB.m_sweep.c.y;
- minSeparation = minSeparation < separation ? minSeparation : separation;
+ minSeparation =
+ minSeparation < separation ? minSeparation : separation;
var C = b2Math.Clamp(
baumgarte * (separation + b2Settings.b2_linearSlop),
-b2Settings.b2_maxLinearCorrection,
@@ -8055,7 +8167,8 @@
xf2
) {};
Box2D.inherit(b2NullContact, Box2D.Dynamics.Contacts.b2Contact);
- b2NullContact.prototype.__super = Box2D.Dynamics.Contacts.b2Contact.prototype;
+ b2NullContact.prototype.__super =
+ Box2D.Dynamics.Contacts.b2Contact.prototype;
b2NullContact.b2NullContact = function () {
Box2D.Dynamics.Contacts.b2Contact.b2Contact.apply(this, arguments);
};
@@ -8161,7 +8274,9 @@
b2PositionSolverManifold.b2PositionSolverManifold = function () {};
b2PositionSolverManifold.prototype.b2PositionSolverManifold = function () {
this.m_normal = new b2Vec2();
- this.m_separations = new Vector_a2j_Number(b2Settings.b2_maxManifoldPoints);
+ this.m_separations = new Vector_a2j_Number(
+ b2Settings.b2_maxManifoldPoints
+ );
this.m_points = new Vector(b2Settings.b2_maxManifoldPoints);
for (var i = 0; i < b2Settings.b2_maxManifoldPoints; i++) {
this.m_points[i] = new b2Vec2();
@@ -8328,11 +8443,17 @@
b2TensorDampingController =
Box2D.Dynamics.Controllers.b2TensorDampingController;
- Box2D.inherit(b2BuoyancyController, Box2D.Dynamics.Controllers.b2Controller);
+ Box2D.inherit(
+ b2BuoyancyController,
+ Box2D.Dynamics.Controllers.b2Controller
+ );
b2BuoyancyController.prototype.__super =
Box2D.Dynamics.Controllers.b2Controller.prototype;
b2BuoyancyController.b2BuoyancyController = function () {
- Box2D.Dynamics.Controllers.b2Controller.b2Controller.apply(this, arguments);
+ Box2D.Dynamics.Controllers.b2Controller.b2Controller.apply(
+ this,
+ arguments
+ );
this.normal = new b2Vec2(0, -1);
this.offset = 0;
this.density = 0;
@@ -8422,7 +8543,10 @@
b2ConstantAccelController.prototype.__super =
Box2D.Dynamics.Controllers.b2Controller.prototype;
b2ConstantAccelController.b2ConstantAccelController = function () {
- Box2D.Dynamics.Controllers.b2Controller.b2Controller.apply(this, arguments);
+ Box2D.Dynamics.Controllers.b2Controller.b2Controller.apply(
+ this,
+ arguments
+ );
this.A = new b2Vec2(0, 0);
};
b2ConstantAccelController.prototype.Step = function (step) {
@@ -8445,7 +8569,10 @@
b2ConstantForceController.prototype.__super =
Box2D.Dynamics.Controllers.b2Controller.prototype;
b2ConstantForceController.b2ConstantForceController = function () {
- Box2D.Dynamics.Controllers.b2Controller.b2Controller.apply(this, arguments);
+ Box2D.Dynamics.Controllers.b2Controller.b2Controller.apply(
+ this,
+ arguments
+ );
this.F = new b2Vec2(0, 0);
};
b2ConstantForceController.prototype.Step = function (step) {
@@ -8505,7 +8632,10 @@
b2GravityController.prototype.__super =
Box2D.Dynamics.Controllers.b2Controller.prototype;
b2GravityController.b2GravityController = function () {
- Box2D.Dynamics.Controllers.b2Controller.b2Controller.apply(this, arguments);
+ Box2D.Dynamics.Controllers.b2Controller.b2Controller.apply(
+ this,
+ arguments
+ );
this.G = 1;
this.invSqr = true;
};
@@ -8568,7 +8698,10 @@
b2TensorDampingController.prototype.__super =
Box2D.Dynamics.Controllers.b2Controller.prototype;
b2TensorDampingController.b2TensorDampingController = function () {
- Box2D.Dynamics.Controllers.b2Controller.b2Controller.apply(this, arguments);
+ Box2D.Dynamics.Controllers.b2Controller.b2Controller.apply(
+ this,
+ arguments
+ );
this.T = new b2Mat22();
this.maxTimestep = 0;
};
@@ -9220,7 +9353,9 @@
this.m_J.linearB.Set(-this.m_ratio * ugX, -this.m_ratio * ugY);
this.m_J.angularB = -this.m_ratio * crug;
K +=
- this.m_ratio * this.m_ratio * (bB.m_invMass + bB.m_invI * crug * crug);
+ this.m_ratio *
+ this.m_ratio *
+ (bB.m_invMass + bB.m_invI * crug * crug);
}
this.m_mass = K > 0.0 ? 1.0 / K : 0.0;
if (step.warmStarting) {
@@ -9286,7 +9421,8 @@
return linearError < b2Settings.b2_linearSlop;
};
Box2D.inherit(b2GearJointDef, Box2D.Dynamics.Joints.b2JointDef);
- b2GearJointDef.prototype.__super = Box2D.Dynamics.Joints.b2JointDef.prototype;
+ b2GearJointDef.prototype.__super =
+ Box2D.Dynamics.Joints.b2JointDef.prototype;
b2GearJointDef.b2GearJointDef = function () {
Box2D.Dynamics.Joints.b2JointDef.b2JointDef.apply(this, arguments);
};
@@ -9379,7 +9515,9 @@
break;
case b2Joint.e_mouseJoint:
{
- joint = new b2MouseJoint(def instanceof b2MouseJointDef ? def : null);
+ joint = new b2MouseJoint(
+ def instanceof b2MouseJointDef ? def : null
+ );
}
break;
case b2Joint.e_prismaticJoint:
@@ -9677,7 +9815,8 @@
var i2 = this.m_invIB;
this.m_K.col1.x =
m1 + m2 + i1 * this.m_s1 * this.m_s1 + i2 * this.m_s2 * this.m_s2;
- this.m_K.col1.y = i1 * this.m_s1 * this.m_a1 + i2 * this.m_s2 * this.m_a2;
+ this.m_K.col1.y =
+ i1 * this.m_s1 * this.m_a1 + i2 * this.m_s2 * this.m_a2;
this.m_K.col2.x = this.m_K.col1.y;
this.m_K.col2.y =
m1 + m2 + i1 * this.m_a1 * this.m_a1 + i2 * this.m_a2 * this.m_a2;
@@ -9920,7 +10059,8 @@
i2 = this.m_invIB;
this.m_K.col1.x =
m1 + m2 + i1 * this.m_s1 * this.m_s1 + i2 * this.m_s2 * this.m_s2;
- this.m_K.col1.y = i1 * this.m_s1 * this.m_a1 + i2 * this.m_s2 * this.m_a2;
+ this.m_K.col1.y =
+ i1 * this.m_s1 * this.m_a1 + i2 * this.m_s2 * this.m_a2;
this.m_K.col2.x = this.m_K.col1.y;
this.m_K.col2.y =
m1 + m2 + i1 * this.m_a1 * this.m_a1 + i2 * this.m_a2 * this.m_a2;
@@ -9961,7 +10101,8 @@
);
};
Box2D.inherit(b2LineJointDef, Box2D.Dynamics.Joints.b2JointDef);
- b2LineJointDef.prototype.__super = Box2D.Dynamics.Joints.b2JointDef.prototype;
+ b2LineJointDef.prototype.__super =
+ Box2D.Dynamics.Joints.b2JointDef.prototype;
b2LineJointDef.b2LineJointDef = function () {
Box2D.Dynamics.Joints.b2JointDef.b2JointDef.apply(this, arguments);
this.localAnchorA = new b2Vec2();
@@ -10150,7 +10291,8 @@
this.dampingRatio = 0.7;
};
Box2D.inherit(b2PrismaticJoint, Box2D.Dynamics.Joints.b2Joint);
- b2PrismaticJoint.prototype.__super = Box2D.Dynamics.Joints.b2Joint.prototype;
+ b2PrismaticJoint.prototype.__super =
+ Box2D.Dynamics.Joints.b2Joint.prototype;
b2PrismaticJoint.b2PrismaticJoint = function () {
Box2D.Dynamics.Joints.b2Joint.b2Joint.apply(this, arguments);
this.m_localAnchor1 = new b2Vec2();
@@ -10351,7 +10493,8 @@
this.m_K.col1.x =
m1 + m2 + i1 * this.m_s1 * this.m_s1 + i2 * this.m_s2 * this.m_s2;
this.m_K.col1.y = i1 * this.m_s1 + i2 * this.m_s2;
- this.m_K.col1.z = i1 * this.m_s1 * this.m_a1 + i2 * this.m_s2 * this.m_a2;
+ this.m_K.col1.z =
+ i1 * this.m_s1 * this.m_a1 + i2 * this.m_s2 * this.m_a2;
this.m_K.col2.x = this.m_K.col1.y;
this.m_K.col2.y = i1 + i2;
this.m_K.col2.z = i1 * this.m_a1 + i2 * this.m_a2;
@@ -10599,7 +10742,8 @@
this.m_K.col1.x =
m1 + m2 + i1 * this.m_s1 * this.m_s1 + i2 * this.m_s2 * this.m_s2;
this.m_K.col1.y = i1 * this.m_s1 + i2 * this.m_s2;
- this.m_K.col1.z = i1 * this.m_s1 * this.m_a1 + i2 * this.m_s2 * this.m_a2;
+ this.m_K.col1.z =
+ i1 * this.m_s1 * this.m_a1 + i2 * this.m_s2 * this.m_a2;
this.m_K.col2.x = this.m_K.col1.y;
this.m_K.col2.y = i1 + i2;
this.m_K.col2.z = i1 * this.m_a1 + i2 * this.m_a2;
@@ -11374,11 +11518,13 @@
v1.x -= m1 * this.impulse3.x;
v1.y -= m1 * this.impulse3.y;
w1 -=
- i1 * (r1X * this.impulse3.y - r1Y * this.impulse3.x + this.impulse3.z);
+ i1 *
+ (r1X * this.impulse3.y - r1Y * this.impulse3.x + this.impulse3.z);
v2.x += m2 * this.impulse3.x;
v2.y += m2 * this.impulse3.y;
w2 +=
- i2 * (r2X * this.impulse3.y - r2Y * this.impulse3.x + this.impulse3.z);
+ i2 *
+ (r2X * this.impulse3.y - r2Y * this.impulse3.x + this.impulse3.z);
} else {
tMat = bA.m_xf.R;
r1X = this.m_localAnchor1.x - bA.m_sweep.localCenter.x;
@@ -11733,7 +11879,8 @@
);
};
Box2D.inherit(b2WeldJointDef, Box2D.Dynamics.Joints.b2JointDef);
- b2WeldJointDef.prototype.__super = Box2D.Dynamics.Joints.b2JointDef.prototype;
+ b2WeldJointDef.prototype.__super =
+ Box2D.Dynamics.Joints.b2JointDef.prototype;
b2WeldJointDef.b2WeldJointDef = function () {
Box2D.Dynamics.Joints.b2JointDef.b2JointDef.apply(this, arguments);
this.localAnchorA = new b2Vec2();
@@ -11848,7 +11995,11 @@
b2DebugDraw.prototype.GetXFormScale = function () {
return this.m_xformScale;
};
- b2DebugDraw.prototype.DrawPolygon = function (vertices, vertexCount, color) {
+ b2DebugDraw.prototype.DrawPolygon = function (
+ vertices,
+ vertexCount,
+ color
+ ) {
if (!vertexCount) return;
var s = this.m_ctx;
var drawScale = this.m_drawScale;
@@ -12007,16 +12158,16 @@
const prevPos = {};
/**
- * Active b2Body/s in the world.
- * @type {Object.}
- */
+ * Active b2Body/s in the world.
+ * @type {Object.}
+ */
const bodies = {};
// const joints = {};
const pinned = {}; // Map of IDs to pinned joints
/**
- * The runtime instantiating this block package.
- * @type {Array}
- */
+ * The runtime instantiating this block package.
+ * @type {Array}
+ */
const stageBodies = [];
// const categorySeq = 1;
@@ -12212,12 +12363,12 @@
};
/**
- * Set the X and Y coordinates (No Fencing)
- * @param {!RenderedTarget} rt the renderedTarget.
- * @param {!number} x New X coordinate, in Scratch coordinates.
- * @param {!number} y New Y coordinate, in Scratch coordinates.
- * @param {?boolean} force Force setting X/Y, in case of dragging
- */
+ * Set the X and Y coordinates (No Fencing)
+ * @param {!RenderedTarget} rt the renderedTarget.
+ * @param {!number} x New X coordinate, in Scratch coordinates.
+ * @param {!number} y New Y coordinate, in Scratch coordinates.
+ * @param {?boolean} force Force setting X/Y, in case of dragging
+ */
const _setXY = function (rt, x, y, force) {
if (rt.isStage) return;
if (rt.dragging && !force) return;
@@ -12295,8 +12446,10 @@
}
};
- const blockIconURI = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiDQoJIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbG5zOmE9Imh0dHA6Ly9ucy5hZG9iZS5jb20vQWRvYmVTVkdWaWV3ZXJFeHRlbnNpb25zLzMuMC8iDQoJIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSItMy43IC0zLjcgNDAgNDAiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgLTMuNyAtMy43IDQwIDQwIg0KCSB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxkZWZzPg0KPC9kZWZzPg0KPHJlY3QgeD0iOC45IiB5PSIxLjUiIGZpbGw9IiNGRkZGRkYiIHN0cm9rZT0iIzE2OUZCMCIgc3Ryb2tlLXdpZHRoPSIzIiB3aWR0aD0iMTQuOCIgaGVpZ2h0PSIxNC44Ii8+DQo8cmVjdCB4PSIxLjUiIHk9IjE2LjMiIGZpbGw9IiNGRkZGRkYiIHN0cm9rZT0iIzE2OUZCMCIgc3Ryb2tlLXdpZHRoPSIzIiB3aWR0aD0iMTQuOCIgaGVpZ2h0PSIxNC44Ii8+DQo8cmVjdCB4PSIxNi4zIiB5PSIxNi4zIiBmaWxsPSIjRkZGRkZGIiBzdHJva2U9IiMxNjlGQjAiIHN0cm9rZS13aWR0aD0iMyIgd2lkdGg9IjE0LjgiIGhlaWdodD0iMTQuOCIvPg0KPC9zdmc+";
- const menuIconURI = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiDQoJIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbG5zOmE9Imh0dHA6Ly9ucy5hZG9iZS5jb20vQWRvYmVTVkdWaWV3ZXJFeHRlbnNpb25zLzMuMC8iDQoJIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSItMy43IC0zLjcgNDAgNDAiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgLTMuNyAtMy43IDQwIDQwIg0KCSB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxkZWZzPg0KPC9kZWZzPg0KPHJlY3QgeD0iOC45IiB5PSIxLjUiIGZpbGw9IiNGRkZGRkYiIHN0cm9rZT0iIzE2OUZCMCIgc3Ryb2tlLXdpZHRoPSIzIiB3aWR0aD0iMTQuOCIgaGVpZ2h0PSIxNC44Ii8+DQo8cmVjdCB4PSIxLjUiIHk9IjE2LjMiIGZpbGw9IiNGRkZGRkYiIHN0cm9rZT0iIzE2OUZCMCIgc3Ryb2tlLXdpZHRoPSIzIiB3aWR0aD0iMTQuOCIgaGVpZ2h0PSIxNC44Ii8+DQo8cmVjdCB4PSIxNi4zIiB5PSIxNi4zIiBmaWxsPSIjRkZGRkZGIiBzdHJva2U9IiMxNjlGQjAiIHN0cm9rZS13aWR0aD0iMyIgd2lkdGg9IjE0LjgiIGhlaWdodD0iMTQuOCIvPg0KPC9zdmc+";
+ const blockIconURI =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiDQoJIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbG5zOmE9Imh0dHA6Ly9ucy5hZG9iZS5jb20vQWRvYmVTVkdWaWV3ZXJFeHRlbnNpb25zLzMuMC8iDQoJIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSItMy43IC0zLjcgNDAgNDAiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgLTMuNyAtMy43IDQwIDQwIg0KCSB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxkZWZzPg0KPC9kZWZzPg0KPHJlY3QgeD0iOC45IiB5PSIxLjUiIGZpbGw9IiNGRkZGRkYiIHN0cm9rZT0iIzE2OUZCMCIgc3Ryb2tlLXdpZHRoPSIzIiB3aWR0aD0iMTQuOCIgaGVpZ2h0PSIxNC44Ii8+DQo8cmVjdCB4PSIxLjUiIHk9IjE2LjMiIGZpbGw9IiNGRkZGRkYiIHN0cm9rZT0iIzE2OUZCMCIgc3Ryb2tlLXdpZHRoPSIzIiB3aWR0aD0iMTQuOCIgaGVpZ2h0PSIxNC44Ii8+DQo8cmVjdCB4PSIxNi4zIiB5PSIxNi4zIiBmaWxsPSIjRkZGRkZGIiBzdHJva2U9IiMxNjlGQjAiIHN0cm9rZS13aWR0aD0iMyIgd2lkdGg9IjE0LjgiIGhlaWdodD0iMTQuOCIvPg0KPC9zdmc+";
+ const menuIconURI =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiDQoJIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbG5zOmE9Imh0dHA6Ly9ucy5hZG9iZS5jb20vQWRvYmVTVkdWaWV3ZXJFeHRlbnNpb25zLzMuMC8iDQoJIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSItMy43IC0zLjcgNDAgNDAiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgLTMuNyAtMy43IDQwIDQwIg0KCSB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxkZWZzPg0KPC9kZWZzPg0KPHJlY3QgeD0iOC45IiB5PSIxLjUiIGZpbGw9IiNGRkZGRkYiIHN0cm9rZT0iIzE2OUZCMCIgc3Ryb2tlLXdpZHRoPSIzIiB3aWR0aD0iMTQuOCIgaGVpZ2h0PSIxNC44Ii8+DQo8cmVjdCB4PSIxLjUiIHk9IjE2LjMiIGZpbGw9IiNGRkZGRkYiIHN0cm9rZT0iIzE2OUZCMCIgc3Ryb2tlLXdpZHRoPSIzIiB3aWR0aD0iMTQuOCIgaGVpZ2h0PSIxNC44Ii8+DQo8cmVjdCB4PSIxNi4zIiB5PSIxNi4zIiBmaWxsPSIjRkZGRkZGIiBzdHJva2U9IiMxNjlGQjAiIHN0cm9rZS13aWR0aD0iMyIgd2lkdGg9IjE0LjgiIGhlaWdodD0iMTQuOCIvPg0KPC9zdmc+";
const vm = Scratch.vm;
class Scratch3Griffpatch {
@@ -12613,7 +12766,7 @@
filter: [Scratch.TargetType.SPRITE],
},
- '---',
+ "---",
{
opcode: "setAngVelocity",
@@ -12641,7 +12794,7 @@
}),
filter: [Scratch.TargetType.SPRITE],
},
-
+
"---",
{
@@ -12666,10 +12819,10 @@
text: formatMessage({
id: "griffpatch.getStatic",
default: "fixed?",
- description: "get whether this sprite is fixed"
+ description: "get whether this sprite is fixed",
}),
blockType: BlockType.BOOLEAN,
- filter: [Scratch.TargetType.SPRITE]
+ filter: [Scratch.TargetType.SPRITE],
},
"---",
@@ -12680,7 +12833,7 @@
text: formatMessage({
id: "griffpatch.setDensity",
default: "set density [density]",
- description: "Set the density of the object"
+ description: "Set the density of the object",
}),
arguments: {
density: {
@@ -12698,13 +12851,13 @@
text: formatMessage({
id: "griffpatch.setDensityValue",
default: "set density to [density]",
- description: "Set the density of the object"
+ description: "Set the density of the object",
}),
arguments: {
density: {
type: ArgumentType.NUMBER,
- defaultValue: 100
- }
+ defaultValue: 100,
+ },
},
filter: [Scratch.TargetType.SPRITE],
},
@@ -12820,7 +12973,8 @@
blockType: BlockType.COMMAND,
text: formatMessage({
id: "griffpatch.setProperties",
- default: "set density [density] roughness [friction] bounce [restitution]",
+ default:
+ "set density [density] roughness [friction] bounce [restitution]",
description: "Set the density of the object",
}),
arguments: {
@@ -12942,7 +13096,7 @@
description: "get the y scroll",
}),
blockType: BlockType.REPORTER,
- }
+ },
],
menus: {
@@ -13108,7 +13262,8 @@
}
const prev = prevPos[targetID];
- const fixedRotation = target.rotationStyle !== ROTATION_STYLE_ALL_AROUND;
+ const fixedRotation =
+ target.rotationStyle !== ROTATION_STYLE_ALL_AROUND;
if (prev && (prev.x !== target.x || prev.y !== target.y)) {
const pos = new b2Vec2(
@@ -13354,7 +13509,7 @@
body.GetFixtureList().SetDensity(Cast.toNumber(args.density) / 100.0);
body.ResetMassData();
}
-
+
getDensity(args, util) {
let body = bodies[util.target.id];
if (!body) {
@@ -13389,7 +13544,9 @@
body = this.setPhysicsFor(util.target);
}
- body.GetFixtureList().SetRestitution(Cast.toNumber(args.restitution) / 100.0);
+ body
+ .GetFixtureList()
+ .SetRestitution(Cast.toNumber(args.restitution) / 100.0);
body.ResetMassData();
}
@@ -13410,7 +13567,9 @@
body.GetFixtureList().SetDensity(Cast.toNumber(args.density) / 100.0);
body.GetFixtureList().SetFriction(Cast.toNumber(args.friction) / 100.0);
- body.GetFixtureList().SetRestitution(Cast.toNumber(args.restitution) / 100.0);
+ body
+ .GetFixtureList()
+ .SetRestitution(Cast.toNumber(args.restitution) / 100.0);
body.ResetMassData();
}
@@ -13699,7 +13858,11 @@
position.x * zoom - _scroll.x,
position.y * zoom - _scroll.y
);
- prevPos[targetID] = { x: target.x, y: target.y, dir: target.direction };
+ prevPos[targetID] = {
+ x: target.x,
+ y: target.y,
+ dir: target.direction,
+ };
}
}
}
diff --git a/extensions/clipboard.js b/extensions/clipboard.js
index 7169483623..07f1752857 100644
--- a/extensions/clipboard.js
+++ b/extensions/clipboard.js
@@ -1,121 +1,123 @@
-// Name: Clipboard
-// Description: Read and write from the system clipboard.
-
-/*!
- * Copyright 2023 tomyo-code + AdamMady
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-(function(Scratch) {
- 'use strict';
-
- if (!Scratch.extensions.unsandboxed) {
- throw new Error('Clipboard must run unsandboxed');
- }
-
- const extensionicon = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSI4MC40NTQ1NCIgaGVpZ2h0PSI4MC40NTQ1NCIgdmlld0JveD0iMCwwLDgwLjQ1NDU0LDgwLjQ1NDU0Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTk5Ljc3MjcyLC0xMzkuNzcyNzIpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTE5OS43NzI3MywxODBjMCwtMjIuMjE2OSAxOC4wMTAzNywtNDAuMjI3MjcgNDAuMjI3MjcsLTQwLjIyNzI3YzIyLjIxNjksMCA0MC4yMjcyNywxOC4wMTAzNyA0MC4yMjcyNyw0MC4yMjcyN2MwLDIyLjIxNjkgLTE4LjAxMDM3LDQwLjIyNzI3IC00MC4yMjcyNyw0MC4yMjcyN2MtMjIuMjE2OSwwIC00MC4yMjcyNywtMTguMDEwMzcgLTQwLjIyNzI3LC00MC4yMjcyN3oiIGZpbGw9IiMwMDgwODAiIHN0cm9rZS13aWR0aD0iMCIvPjxpbWFnZSB4PSI0MzQiIHk9IjMwMCIgdHJhbnNmb3JtPSJzY2FsZSgwLjUsMC41KSIgd2lkdGg9Ijk0IiBoZWlnaHQ9IjExOCIgeGxpbms6aHJlZj0iZGF0YTppbWFnZS9wbmc7YmFzZTY0LGlWQk9SdzBLR2dvQUFBQU5TVWhFVWdBQUFGNEFBQUIyQ0FZQUFBQkJMU1ExQUFBQUFYTlNSMElBcnM0YzZRQUFCckpKUkVGVWVGN3RuVnZJYmtNWXgyZTJuTUlPeVNHbDVCQjJTWExNc1lRTFo5cHNaMEl1aEhKQmlkeTZJaUVrNTFPT0VYZkVqZHk0c0pPemN0Z1hLRGtrNStTdmI3Ny9zMzNmM3U5YTYxbnZtdlhNdk85KzNwdXA5VDd6UERPLythOVpzMmF0bVJWRDVUOEFlN0tJMXpNOWx1bTJUTDlqK2dyVGV4ZlNHT09mTlZjdDFseTRoYkk1ZU9NV0FuQXpROTdLZEN0bEVlUU1XRTNsdjYzTVoycFdyZUlkdktrT1V0ZHlHME5LT3EwNHBJOVAxNFFZNDd2R1ZXa05OMjJsUnF1RGd4OE43V1RIQVBibFB4OHczVHhURVQ2bm4vMm8vSDh6K1Iza3BockZiN0xnQVd6QkpyeUo2UWxNdHh6VXRQOW5mcHFLdTN1U1B3Q1A4dmlsSGZIKzR2OC9NZDJaNllxR2ZPRHhreG4vOVliNGovRzRuSGxEcXkzbGU0SnhuMW5xY0wzaUhUeHN3UVBZbmkwaFYvMjloeloxUS80MzJQSW5OaWp1TXg3ZnB5SC9iengrTVAwa2V3RFg4TGljU1UzZFovby94bmhkUS94ZmVIeTd6UFdYTSs0cHhyODRwUTUrRVRNQWMvRFM5NnpKM05JYnVudVZMWDU2ZytLKzVmRmRHOHFSenNnWTQyRU4rZi9nOGFZNzNPZVkvN3lHL0QvdytJNGpjemhWRk8vZ0Z4VnZEcjVMS2JrRWNCa1ZKeGV4Wlg0QnlCekxMZzBCcFp4SDBjOTc3Q0p1cFAzdFRKdjYrT2VaNzl3R3hiL0c0NmZrcW5DRG4zV2llQWUvcUhoejhITFY3V3JvZjJnZzQrZ3VlMm5RQjZpMFc5b3lBT2pxNHlXN2xFTXVoanQwS0YzeWRmWHhLMm1ZN0VJSVJ6TGRyS09pY29adHJTeEhNbHNZMVRqNFJjVlhCLzVIdHVReFREL3Bram9WM210T0JNRDM5THVUeHY4VU51a0pWWXp4ekQ1NUFUVGRFWXNiK2Y4U0huaVFhV3MramVJZGZIdExqUVkrRFRkampCZjBVWXJXRnNCcHRIMVpveFN0M3dsMklxQzlXSitmQi9ocXpLbzljeldLZC9BOVdpZ24rTWVwa0s1WlExWHhBSnhFdzZ1WW5zRTAxL3g3Vnpua1J1bGhHcVpaMFJqalIxMFpOZjhyN2tlU0c0M2lIYnlHT0cxeWduK1Npa2l6YW4xL0FPUk84SDdtM1oxcExROWg1TDVBWm1mUFlYM2x2cUpYbFIyOEh0ZDhnUWR3Tk92K3BuRWZya2MrMlRMTnBZUVFWbEg1di9aeFdGenhEajQwVGZhcEw2NVQ5ZkVBWkpTd2Z4L0ZWR1I3SnhWL1E1OHkxYUI0QjkvU1lwcmhaQy9GQXppQThkNW4yalc3MTBkUWxyYmZVUEV5Q2xQRkxxWjRCOS81UUdlY1BuN0pVLzk3VkJLcDEwaUdtWHYwR2RlWFZMeThidUhnQy9YeHZlYTlLeFMrUEVGN2lJcVhKMTZ0UlMycGVMbTRPbmhMeFZlb1hOTWlGVk84YVMwckRPYmdDeldLZzNmd2hRZ1VDdXVLbjNmd0FPVFo2ZVhHZGIyTDQrdG5qZVBXTVk1MzhNdmJ3YXlyQVpEZWV3OGhwUGUrRFgvcHJlTVlZM29MdVphZmd5L1VFcGJnWlUyUjlQVldWYjZEaW4vRUtxQW1qb1BYVUJyQnhnejhDR1dmYVpjT3ZsRHo1UVF2NnpNdktsU1htUXJyNEFzMWw0TjM4SVVJRkFwcnBuZ0FCN0tPRTFkY0Y2cC9qckN5dzlOTHZGLzRYZVBVd1dzb3RkdFVELzVGbHYvczRYV3R5b01zUTVYZFF0WnFTcWRkcjZ0NWhhOTFPQW5Bd1M5cEVVdndhZVYyQ01GNlBsNGp3Q0UyZnkrOWRtblhTRG40SWNnWDgxWVBYdmI0emIyejBYQjB3enpJeXZTMDRqekdxRnFwYnFsNEIxK2lqeDhtcXZuTGJhYjQrVU0zckVZT2ZoaS9xWE03K0tuUkRjdG9CbjdKUnFHeVUrdXdrbytYTzYzd0dQdExDZzUrNHdhY08vQ3l3K21WNDRrMWkrY1BxZmhEc25ocmNHS3BlQWRmWWh3UElNMVhoeERPR2xOSkdYeW5mUzFqakx0bDhOWG93bEx4RHI2UTRnOW4zT1BHVkZJRzMxOVE4UzlrOEZXRjRoMThDY1dQcVo1WjlHM1d4ODhpbkRITDdPREhwTnZpMjhIUEFmaFJOL3dzeEdlMHNEa1Y3K0I3TkpNWmVBQlhzMXhWclVWU3NQcUs0L3J6RmJacUV3ZmZqV3Jtd1pkYTlkZU50dDBpdmFJWFk1UXZIUXoxbC9KYkt0N0JqM1RuMm5weEJYQXQ0MTZSUlRKMlRyNms0clBPcWxvcTNzR1hVTHlkUUdjamtwbmlad09IWFNrZHZCM3JaWkVjdklNdlJLQlFXRmY4dklNSGNCRHJlRVNodWtyWVR6a3VmNnRrT2N3VTcrQ1hON01sK0ZvV24zMU14Y3NXdTBXRTcrQ0xZTGVkSkx1dmtybWFkNmo0NHdzeE41K2RkUEFsNW1vQXlGZmhaUkZhS2NISi9IcXY3emJsTHF4bEgrL2dTeWcrdDJKbTNaK1o0bWNkVk83eU8vamNSSlgrSEx3U1ZHNHpCNSticU5LZmcxZUN5bTJXRTN6YW56M0d1Q1ozSWVmUlg4NDl5Ung4RDRYa0JKLzJhd2toSEVybGY5MmpISnVNS1lEVnJHeDZEeW1FMFBwVlQ4MmVaQTVlSVo4eHdFdFkyU3BLdm9HbktFNVZKbkdrMG9qZmJlaC9oU2FPUnZFT3ZwM2sxT0JGd1RMWnBXa3d0NW1ld0xwMHJRVGc0S2VIT0UzTzllRFRON2xEQ0JkTzQ4WHpxQW5JenEzcDYwRUxpbmZ3YW5hREREY0N2NUx1MGpQTEVNS3FRZTQ5ODRZRUJMaDhDVGw5UFdoQjhRNStYTEZNQlA4ZjVqR04yQ3N0cTkwQUFBQUFTVVZPUks1Q1lJST0iIGZpbGw9Im5vbmUiIHN0cm9rZS13aWR0aD0iMC41Ii8+PC9nPjwvZz48L3N2Zz4=";
-
- let lastPastedText = '';
-
- window.addEventListener('copy', (event) => {
- Scratch.vm.runtime.startHats('clipboard_whenCopied') ;
- });
- window.addEventListener('paste', (event) => {
- Scratch.vm.runtime.startHats('clipboard_whenPasted');
- const clipboardData = event.clipboardData || window.clipboardData;
- const pastedText = clipboardData.getData('Text');
- lastPastedText = pastedText;
- });
-
- class Clipboard {
- getInfo() {
- return {
- id: 'clipboard',
- name: 'Clipboard',
- blockIconURI: extensionicon,
- color1: '#008080',
- color2: '#006666',
- blocks: [
- {
- opcode: 'whenCopied',
- blockType: Scratch.BlockType.EVENT,
- text: 'when something is copied',
- isEdgeActivated: false
- },
- {
- opcode: 'whenPasted',
- blockType: Scratch.BlockType.EVENT,
- text: 'when something is pasted',
- isEdgeActivated: false
- },
- '---',
- {
- opcode: 'setClipboard',
- blockType: Scratch.BlockType.COMMAND,
- text: 'copy to clipboard: [TEXT]',
- arguments: {
- TEXT: {
- type: Scratch.ArgumentType.STRING
- }
- }
- },
- {
- opcode: 'resetClipboard',
- blockType: Scratch.BlockType.COMMAND,
- text: 'reset clipboard'
- },
- '---',
- {
- opcode: 'clipboard',
- blockType: Scratch.BlockType.REPORTER,
- text: 'clipboard',
- disableMonitor: true
- },
- {
- opcode: 'getLastPastedText',
- blockType: Scratch.BlockType.REPORTER,
- text: 'last pasted text',
- disableMonitor: true
- }
- ],
- };
- }
-
- setClipboard(args) {
- navigator.clipboard.writeText(args.TEXT);
- }
-
- resetClipboard() {
- navigator.clipboard.writeText('');
- }
-
- clipboard() {
- if (navigator.clipboard && navigator.clipboard.readText) {
- return Scratch.canReadClipboard().then(allowed => {
- if (allowed) {
- return navigator.clipboard.readText();
- }
- return '';
- });
- }
- return '';
- }
-
- getLastPastedText() {
- return lastPastedText;
- }
- }
-
- Scratch.extensions.register(new Clipboard());
-})(Scratch);
+// Name: Clipboard
+// ID: clipboard
+// Description: Read and write from the system clipboard.
+
+/*!
+ * Copyright 2023 tomyo-code + AdamMady
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function (Scratch) {
+ "use strict";
+
+ if (!Scratch.extensions.unsandboxed) {
+ throw new Error("Clipboard must run unsandboxed");
+ }
+
+ const extensionicon =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSI4MC40NTQ1NCIgaGVpZ2h0PSI4MC40NTQ1NCIgdmlld0JveD0iMCwwLDgwLjQ1NDU0LDgwLjQ1NDU0Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTk5Ljc3MjcyLC0xMzkuNzcyNzIpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTE5OS43NzI3MywxODBjMCwtMjIuMjE2OSAxOC4wMTAzNywtNDAuMjI3MjcgNDAuMjI3MjcsLTQwLjIyNzI3YzIyLjIxNjksMCA0MC4yMjcyNywxOC4wMTAzNyA0MC4yMjcyNyw0MC4yMjcyN2MwLDIyLjIxNjkgLTE4LjAxMDM3LDQwLjIyNzI3IC00MC4yMjcyNyw0MC4yMjcyN2MtMjIuMjE2OSwwIC00MC4yMjcyNywtMTguMDEwMzcgLTQwLjIyNzI3LC00MC4yMjcyN3oiIGZpbGw9IiMwMDgwODAiIHN0cm9rZS13aWR0aD0iMCIvPjxpbWFnZSB4PSI0MzQiIHk9IjMwMCIgdHJhbnNmb3JtPSJzY2FsZSgwLjUsMC41KSIgd2lkdGg9Ijk0IiBoZWlnaHQ9IjExOCIgeGxpbms6aHJlZj0iZGF0YTppbWFnZS9wbmc7YmFzZTY0LGlWQk9SdzBLR2dvQUFBQU5TVWhFVWdBQUFGNEFBQUIyQ0FZQUFBQkJMU1ExQUFBQUFYTlNSMElBcnM0YzZRQUFCckpKUkVGVWVGN3RuVnZJYmtNWXgyZTJuTUlPeVNHbDVCQjJTWExNc1lRTFo5cHNaMEl1aEhKQmlkeTZJaUVrNTFPT0VYZkVqZHk0c0pPemN0Z1hLRGtrNStTdmI3Ny9zMzNmM3U5YTYxbnZtdlhNdk85KzNwdXA5VDd6UERPLythOVpzMmF0bVJWRDVUOEFlN0tJMXpNOWx1bTJUTDlqK2dyVGV4ZlNHT09mTlZjdDFseTRoYkk1ZU9NV0FuQXpROTdLZEN0bEVlUU1XRTNsdjYzTVoycFdyZUlkdktrT1V0ZHlHME5LT3EwNHBJOVAxNFFZNDd2R1ZXa05OMjJsUnF1RGd4OE43V1RIQVBibFB4OHczVHhURVQ2bm4vMm8vSDh6K1Iza3BockZiN0xnQVd6QkpyeUo2UWxNdHh6VXRQOW5mcHFLdTN1U1B3Q1A4dmlsSGZIKzR2OC9NZDJaNllxR2ZPRHhreG4vOVliNGovRzRuSGxEcXkzbGU0SnhuMW5xY0wzaUhUeHN3UVBZbmkwaFYvMjloeloxUS80MzJQSW5OaWp1TXg3ZnB5SC9iengrTVAwa2V3RFg4TGljU1UzZFovby94bmhkUS94ZmVIeTd6UFdYTSs0cHhyODRwUTUrRVRNQWMvRFM5NnpKM05JYnVudVZMWDU2ZytLKzVmRmRHOHFSenNnWTQyRU4rZi9nOGFZNzNPZVkvN3lHL0QvdytJNGpjemhWRk8vZ0Z4VnZEcjVMS2JrRWNCa1ZKeGV4Wlg0QnlCekxMZzBCcFp4SDBjOTc3Q0p1cFAzdFRKdjYrT2VaNzl3R3hiL0c0NmZrcW5DRG4zV2llQWUvcUhoejhITFY3V3JvZjJnZzQrZ3VlMm5RQjZpMFc5b3lBT2pxNHlXN2xFTXVoanQwS0YzeWRmWHhLMm1ZN0VJSVJ6TGRyS09pY29adHJTeEhNbHNZMVRqNFJjVlhCLzVIdHVReFREL3Bram9WM210T0JNRDM5THVUeHY4VU51a0pWWXp4ekQ1NUFUVGRFWXNiK2Y4U0huaVFhV3MramVJZGZIdExqUVkrRFRkampCZjBVWXJXRnNCcHRIMVpveFN0M3dsMklxQzlXSitmQi9ocXpLbzljeldLZC9BOVdpZ24rTWVwa0s1WlExWHhBSnhFdzZ1WW5zRTAxL3g3Vnpua1J1bGhHcVpaMFJqalIxMFpOZjhyN2tlU0c0M2lIYnlHT0cxeWduK1Npa2l6YW4xL0FPUk84SDdtM1oxcExROWg1TDVBWm1mUFlYM2x2cUpYbFIyOEh0ZDhnUWR3Tk92K3BuRWZya2MrMlRMTnBZUVFWbEg1di9aeFdGenhEajQwVGZhcEw2NVQ5ZkVBWkpTd2Z4L0ZWR1I3SnhWL1E1OHkxYUI0QjkvU1lwcmhaQy9GQXppQThkNW4yalc3MTBkUWxyYmZVUEV5Q2xQRkxxWjRCOS81UUdlY1BuN0pVLzk3VkJLcDEwaUdtWHYwR2RlWFZMeThidUhnQy9YeHZlYTlLeFMrUEVGN2lJcVhKMTZ0UlMycGVMbTRPbmhMeFZlb1hOTWlGVk84YVMwckRPYmdDeldLZzNmd2hRZ1VDdXVLbjNmd0FPVFo2ZVhHZGIyTDQrdG5qZVBXTVk1MzhNdmJ3YXlyQVpEZWV3OGhwUGUrRFgvcHJlTVlZM29MdVphZmd5L1VFcGJnWlUyUjlQVldWYjZEaW4vRUtxQW1qb1BYVUJyQnhnejhDR1dmYVpjT3ZsRHo1UVF2NnpNdktsU1htUXJyNEFzMWw0TjM4SVVJRkFwcnBuZ0FCN0tPRTFkY0Y2cC9qckN5dzlOTHZGLzRYZVBVd1dzb3RkdFVELzVGbHYvczRYV3R5b01zUTVYZFF0WnFTcWRkcjZ0NWhhOTFPQW5Bd1M5cEVVdndhZVYyQ01GNlBsNGp3Q0UyZnkrOWRtblhTRG40SWNnWDgxWVBYdmI0emIyejBYQjB3enpJeXZTMDRqekdxRnFwYnFsNEIxK2lqeDhtcXZuTGJhYjQrVU0zckVZT2ZoaS9xWE03K0tuUkRjdG9CbjdKUnFHeVUrdXdrbytYTzYzd0dQdExDZzUrNHdhY08vQ3l3K21WNDRrMWkrY1BxZmhEc25ocmNHS3BlQWRmWWh3UElNMVhoeERPR2xOSkdYeW5mUzFqakx0bDhOWG93bEx4RHI2UTRnOW4zT1BHVkZJRzMxOVE4UzlrOEZXRjRoMThDY1dQcVo1WjlHM1d4ODhpbkRITDdPREhwTnZpMjhIUEFmaFJOL3dzeEdlMHNEa1Y3K0I3TkpNWmVBQlhzMXhWclVWU3NQcUs0L3J6RmJacUV3ZmZqV3Jtd1pkYTlkZU50dDBpdmFJWFk1UXZIUXoxbC9KYkt0N0JqM1RuMm5weEJYQXQ0MTZSUlRKMlRyNms0clBPcWxvcTNzR1hVTHlkUUdjamtwbmlad09IWFNrZHZCM3JaWkVjdklNdlJLQlFXRmY4dklNSGNCRHJlRVNodWtyWVR6a3VmNnRrT2N3VTcrQ1hON01sK0ZvV24zMU14Y3NXdTBXRTcrQ0xZTGVkSkx1dmtybWFkNmo0NHdzeE41K2RkUEFsNW1vQXlGZmhaUkZhS2NISi9IcXY3emJsTHF4bEgrL2dTeWcrdDJKbTNaK1o0bWNkVk83eU8vamNSSlgrSEx3U1ZHNHpCNSticU5LZmcxZUN5bTJXRTN6YW56M0d1Q1ozSWVmUlg4NDl5Ung4RDRYa0JKLzJhd2toSEVybGY5MmpISnVNS1lEVnJHeDZEeW1FMFBwVlQ4MmVaQTVlSVo4eHdFdFkyU3BLdm9HbktFNVZKbkdrMG9qZmJlaC9oU2FPUnZFT3ZwM2sxT0JGd1RMWnBXa3d0NW1ld0xwMHJRVGc0S2VIT0UzTzllRFRON2xEQ0JkTzQ4WHpxQW5JenEzcDYwRUxpbmZ3YW5hREREY0N2NUx1MGpQTEVNS3FRZTQ5ODRZRUJMaDhDVGw5UFdoQjhRNStYTEZNQlA4ZjVqR04yQ3N0cTkwQUFBQUFTVVZPUks1Q1lJST0iIGZpbGw9Im5vbmUiIHN0cm9rZS13aWR0aD0iMC41Ii8+PC9nPjwvZz48L3N2Zz4=";
+
+ let lastPastedText = "";
+
+ window.addEventListener("copy", (event) => {
+ Scratch.vm.runtime.startHats("clipboard_whenCopied");
+ });
+ window.addEventListener("paste", (event) => {
+ Scratch.vm.runtime.startHats("clipboard_whenPasted");
+ const clipboardData = event.clipboardData || window.clipboardData;
+ const pastedText = clipboardData.getData("Text");
+ lastPastedText = pastedText;
+ });
+
+ class Clipboard {
+ getInfo() {
+ return {
+ id: "clipboard",
+ name: "Clipboard",
+ blockIconURI: extensionicon,
+ color1: "#008080",
+ color2: "#006666",
+ blocks: [
+ {
+ opcode: "whenCopied",
+ blockType: Scratch.BlockType.EVENT,
+ text: "when something is copied",
+ isEdgeActivated: false,
+ },
+ {
+ opcode: "whenPasted",
+ blockType: Scratch.BlockType.EVENT,
+ text: "when something is pasted",
+ isEdgeActivated: false,
+ },
+ "---",
+ {
+ opcode: "setClipboard",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "copy to clipboard: [TEXT]",
+ arguments: {
+ TEXT: {
+ type: Scratch.ArgumentType.STRING,
+ },
+ },
+ },
+ {
+ opcode: "resetClipboard",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "reset clipboard",
+ },
+ "---",
+ {
+ opcode: "clipboard",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "clipboard",
+ disableMonitor: true,
+ },
+ {
+ opcode: "getLastPastedText",
+ blockType: Scratch.BlockType.REPORTER,
+ text: "last pasted text",
+ disableMonitor: true,
+ },
+ ],
+ };
+ }
+
+ setClipboard(args) {
+ navigator.clipboard.writeText(args.TEXT);
+ }
+
+ resetClipboard() {
+ navigator.clipboard.writeText("");
+ }
+
+ clipboard() {
+ if (navigator.clipboard && navigator.clipboard.readText) {
+ return Scratch.canReadClipboard().then((allowed) => {
+ if (allowed) {
+ return navigator.clipboard.readText();
+ }
+ return "";
+ });
+ }
+ return "";
+ }
+
+ getLastPastedText() {
+ return lastPastedText;
+ }
+ }
+
+ Scratch.extensions.register(new Clipboard());
+})(Scratch);
diff --git a/extensions/clouddata-ping.js b/extensions/clouddata-ping.js
index 9defe28429..93a130de1e 100644
--- a/extensions/clouddata-ping.js
+++ b/extensions/clouddata-ping.js
@@ -1,4 +1,5 @@
// Name: Ping Cloud Data
+// ID: clouddataping
// Description: Determine whether a cloud variable server is probably up.
// Original: TheShovel
@@ -21,10 +22,10 @@
* @returns {Promise}
*/
const pingWebSocket = async (uri) => {
- if (!await Scratch.canFetch(uri)) {
+ if (!(await Scratch.canFetch(uri))) {
return {
expires: 0,
- value: false
+ value: false,
};
}
@@ -37,7 +38,7 @@
} catch (e) {
return {
expires: 0,
- value: false
+ value: false,
};
}
@@ -64,7 +65,7 @@
return {
expires: Date.now() + 60000,
- value: isUp
+ value: isUp,
};
};
diff --git a/extensions/cloudlink.js b/extensions/cloudlink.js
index 5db7711f5e..1583cc1e23 100644
--- a/extensions/cloudlink.js
+++ b/extensions/cloudlink.js
@@ -1,1717 +1,1838 @@
// Name: Cloudlink
+// ID: cloudlink
// Description: Powerful WebSocket extension for Scratch 3.
// By: MikeDEV
// Copy of S4-0_nosuite.js as of 10/31/2022
/* eslint-disable */
-(function(Scratch) {
-
-var servers = {}; ; // Server list
-let mWS = null;
-
-// Get the server URL list
-try {
- Scratch.fetch('https://mikedev101.github.io/cloudlink/serverlist.json').then(response => {
- return response.text();
- }).then(data => {
- servers = JSON.parse(data);
- }).catch(err => {
- console.log(err);
- servers = {};
- });
-} catch(err) {
- console.log(err);
- servers = {};
-};
-
-function find_id(ID, ulist) {
- // Thanks StackOverflow!
- if (jsonCheck(ID) && (!intCheck(ID))) {
- return ulist.some(o => ((o.username === JSON.parse(ID).username) && (o.id == JSON.parse(ID).id)));
- } else {
- return ulist.some(o => ((o.username === String(ID)) || (o.id == ID)));
- };
-}
-
-function jsonCheck(JSON_STRING) {
- try {
- JSON.parse(JSON_STRING);
- return true;
- } catch (err) {
- return false;
- }
-}
-
-function intCheck(value) {
- return !isNaN(value);
-}
-
-function autoConvert(value) {
- // Check if the value is JSON / Dict first
- try {
- JSON.parse(value);
- return JSON.parse(value);
- } catch (err) {};
-
- // Check if the value is an array
- try {
- tmp = value;
- tmp = tmp.replace(/'/g, '"');
- JSON.parse(tmp);
- return JSON.parse(tmp);
- } catch (err) {};
-
- // Check if an int/float
- if (!isNaN(value)) {
- return Number(value);
- };
-
- // Leave as the original value if none of the above work
- return value;
-}
-
-class CloudLink {
- constructor (runtime, extensionId) {
- // Extension stuff
- this.runtime = runtime;
- this.cl_icon = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIyMjUuMzU0OCIgaGVpZ2h0PSIyMjUuMzU0OCIgdmlld0JveD0iMCwwLDIyNS4zNTQ4LDIyNS4zNTQ4Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTI3LjMyMjYsLTY3LjMyMjYpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWRhc2hhcnJheT0iIiBzdHJva2UtZGFzaG9mZnNldD0iMCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0xMjcuMzIyNiwxODBjMCwtNjIuMjMwMDEgNTAuNDQ3MzksLTExMi42Nzc0IDExMi42Nzc0LC0xMTIuNjc3NGM2Mi4yMzAwMSwwIDExMi42Nzc0LDUwLjQ0NzM5IDExMi42Nzc0LDExMi42Nzc0YzAsNjIuMjMwMDEgLTUwLjQ0NzM5LDExMi42Nzc0IC0xMTIuNjc3NCwxMTIuNjc3NGMtNjIuMjMwMDEsMCAtMTEyLjY3NzQsLTUwLjQ0NzM5IC0xMTIuNjc3NCwtMTEyLjY3NzR6IiBmaWxsPSIjMDBjMjhjIiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZS13aWR0aD0iMCIvPjxnIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlLXdpZHRoPSIxIj48cGF0aCBkPSJNMjg2LjEyMDM3LDE1MC41NTc5NWMyMy4yNDA4NiwwIDQyLjA3ODksMTguODM5NDYgNDIuMDc4OSw0Mi4wNzg5YzAsMjMuMjM5NDQgLTE4LjgzODAzLDQyLjA3ODkgLTQyLjA3ODksNDIuMDc4OWgtOTIuMjQwNzRjLTIzLjI0MDg2LDAgLTQyLjA3ODksLTE4LjgzOTQ2IC00Mi4wNzg5LC00Mi4wNzg5YzAsLTIzLjIzOTQ0IDE4LjgzODAzLC00Mi4wNzg5IDQyLjA3ODksLTQyLjA3ODloNC4xODg4N2MxLjgxMTUzLC0yMS41NzA1NSAxOS44OTM1NywtMzguNTEyODkgNDEuOTMxNSwtMzguNTEyODljMjIuMDM3OTMsMCA0MC4xMTk5NywxNi45NDIzNCA0MS45MzE1LDM4LjUxMjg5eiIgZmlsbD0iI2ZmZmZmZiIvPjxwYXRoIGQ9Ik0yODkuMDg2NTUsMjEwLjM0MTE0djkuMDQ2NjdoLTI2LjkxNjYzaC05LjA0NjY3di05LjA0NjY3di01NC41MDMzOWg5LjA0NjY3djU0LjUwMzM5eiIgZmlsbD0iIzAwYzI4YyIvPjxwYXRoIGQ9Ik0yMjIuNDA5MjUsMjE5LjM4NzgxYy04LjM1MzIsMCAtMTYuMzY0MzEsLTMuMzE4MzQgLTIyLjI3MDksLTkuMjI0OTJjLTUuOTA2NjEsLTUuOTA2NTggLTkuMjI0OTEsLTEzLjkxNzY4IC05LjIyNDkxLC0yMi4yNzA4OWMwLC04LjM1MzIgMy4zMTgyOSwtMTYuMzY0MzEgOS4yMjQ5MSwtMjIuMjcwOWM1LjkwNjU5LC01LjkwNjYxIDEzLjkxNzcsLTkuMjI0OTEgMjIuMjcwOSwtOS4yMjQ5MWgyMS4xMDg5djguOTM0OThoLTIxLjEwODl2MC4xMDI1N2MtNS45NTYyOCwwIC0xMS42Njg2NCwyLjM2NjE2IC0xNS44ODAzNyw2LjU3Nzg5Yy00LjIxMTczLDQuMjExNzMgLTYuNTc3ODksOS45MjQwOCAtNi41Nzc4OSwxNS44ODAzN2MwLDUuOTU2MjggMi4zNjYxNiwxMS42Njg2NCA2LjU3Nzg5LDE1Ljg4MDM3YzQuMjExNzMsNC4yMTE3MyA5LjkyNDA4LDYuNTc3OTMgMTUuODgwMzcsNi41Nzc5M3YwLjEwMjUzaDIxLjEwODl2OC45MzQ5OHoiIGZpbGw9IiMwMGMyOGMiLz48L2c+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6MTEyLjY3NzQwNDA4NDA4MzkyOjExMi42Nzc0MDQwODQwODQwMy0tPg==';
- this.cl_block = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxNzYuMzk4NTQiIGhlaWdodD0iMTIyLjY3MDY5IiB2aWV3Qm94PSIwLDAsMTc2LjM5ODU0LDEyMi42NzA2OSI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTE1MS44MDA3MywtMTE4LjY2NDY2KSI+PGcgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aXNQYWludGluZ0xheWVyJnF1b3Q7OnRydWV9IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PGc+PHBhdGggZD0iTTI4Ni4xMjAzNywxNTcuMTc3NTVjMjMuMjQwODYsMCA0Mi4wNzg5LDE4LjgzOTQ2IDQyLjA3ODksNDIuMDc4OWMwLDIzLjIzOTQ0IC0xOC44MzgwMyw0Mi4wNzg5IC00Mi4wNzg5LDQyLjA3ODloLTkyLjI0MDc0Yy0yMy4yNDA4NiwwIC00Mi4wNzg5LC0xOC44Mzk0NiAtNDIuMDc4OSwtNDIuMDc4OWMwLC0yMy4yMzk0NCAxOC44MzgwMywtNDIuMDc4OSA0Mi4wNzg5LC00Mi4wNzg5aDQuMTg4ODdjMS44MTE1MywtMjEuNTcwNTUgMTkuODkzNTcsLTM4LjUxMjg5IDQxLjkzMTUsLTM4LjUxMjg5YzIyLjAzNzkzLDAgNDAuMTE5OTcsMTYuOTQyMzQgNDEuOTMxNSwzOC41MTI4OXoiIGZpbGw9IiNmZmZmZmYiLz48cGF0aCBkPSJNMjg5LjA4NjU1LDIxNi45NjA3NHY5LjA0NjY3aC0yNi45MTY2M2gtOS4wNDY2N3YtOS4wNDY2N3YtNTQuNTAzMzloOS4wNDY2N3Y1NC41MDMzOXoiIGZpbGw9IiMwMGMyOGMiLz48cGF0aCBkPSJNMjIyLjQwOTI1LDIyNi4wMDc0MWMtOC4zNTMyLDAgLTE2LjM2NDMxLC0zLjMxODM0IC0yMi4yNzA5LC05LjIyNDkyYy01LjkwNjYxLC01LjkwNjU4IC05LjIyNDkxLC0xMy45MTc2OCAtOS4yMjQ5MSwtMjIuMjcwODljMCwtOC4zNTMyIDMuMzE4MjksLTE2LjM2NDMxIDkuMjI0OTEsLTIyLjI3MDljNS45MDY1OSwtNS45MDY2MSAxMy45MTc3LC05LjIyNDkxIDIyLjI3MDksLTkuMjI0OTFoMjEuMTA4OXY4LjkzNDk4aC0yMS4xMDg5djAuMTAyNTdjLTUuOTU2MjgsMCAtMTEuNjY4NjQsMi4zNjYxNiAtMTUuODgwMzcsNi41Nzc4OWMtNC4yMTE3Myw0LjIxMTczIC02LjU3Nzg5LDkuOTI0MDggLTYuNTc3ODksMTUuODgwMzdjMCw1Ljk1NjI4IDIuMzY2MTYsMTEuNjY4NjQgNi41Nzc4OSwxNS44ODAzN2M0LjIxMTczLDQuMjExNzMgOS45MjQwOCw2LjU3NzkzIDE1Ljg4MDM3LDYuNTc3OTN2MC4xMDI1M2gyMS4xMDg5djguOTM0OTh6IiBmaWxsPSIjMDBjMjhjIi8+PC9nPjwvZz48L2c+PC9zdmc+PCEtLXJvdGF0aW9uQ2VudGVyOjg4LjE5OTI2OTk5OTk5OTk4OjYxLjMzNTM0NDk5OTk5OTk5LS0+';
-
- // Socket data
- this.socketData = {
- "gmsg": [],
- "pmsg": [],
- "direct": [],
- "statuscode": [],
- "gvar": [],
- "pvar": [],
- "motd": "",
- "client_ip": "",
- "ulist": [],
- "server_version": ""
- };
- this.varData = {
- "gvar": {},
- "pvar": {}
- };
-
- this.queueableCmds = ["gmsg", "pmsg", "gvar", "pvar", "direct", "statuscode"];
- this.varCmds = ["gvar", "pvar"];
-
- // Listeners
- this.socketListeners = {};
- this.socketListenersData = {};
- this.newSocketData = {
- "gmsg": false,
- "pmsg": false,
- "direct": false,
- "statuscode": false,
- "gvar": false,
- "pvar": false
- };
-
- // Edge-triggered hat blocks
- this.connect_hat = 0;
- this.packet_hat = 0;
- this.close_hat = 0;
-
- // Status stuff
- this.isRunning = false;
- this.isLinked = false;
- this.version = "S4.0";
- this.link_status = 0;
- this.username = "";
- this.tmp_username = "";
- this.isUsernameSyncing = false;
- this.isUsernameSet = false;
- this.disconnectWasClean = false;
- this.wasConnectionDropped = false;
- this.didConnectionFail = false;
- this.protocolOk = false;
-
- // Listeners stuff
- this.enableListener = false;
- this.setListener = "";
-
- // Rooms stuff
- this.enableRoom = false;
- this.isRoomSetting = false;
- this.selectRoom = "";
-
- // Remapping stuff
- this.menuRemap = {
- "Global data": "gmsg",
- "Private data": "pmsg",
- "Global variables": "gvar",
- "Private variables": "pvar",
- "Direct data": "direct",
- "Status code": "statuscode",
- "All data": "all"
- };
- }
-
- getInfo () {
- return {
- "id": 'cloudlink',
- "name": 'CloudLink',
- "blockIconURI": this.cl_block,
- "menuIconURI": this.cl_icon,
- "docsURI": "https://hackmd.io/@MikeDEV/HJiNYwOfo",
- "blocks": [
- {
- "opcode": 'returnGlobalData',
- "blockType": "reporter",
- "text": "Global data"
- },
- {
- "opcode": 'returnPrivateData',
- "blockType": "reporter",
- "text": "Private data"
- },
- {
- "opcode": 'returnDirectData',
- "blockType": "reporter",
- "text": "Direct Data"
- },
- {
- "opcode": 'returnLinkData',
- "blockType": "reporter",
- "text": "Link status"
- },
- {
- "opcode": 'returnStatusCode',
- "blockType": "reporter",
- "text": "Status code"
- },
- {
- "opcode": 'returnUserListData',
- "blockType": "reporter",
- "text": "Usernames"
- },
- {
- "opcode": "returnUsernameData",
- "blockType": "reporter",
- "text": "My username"
- },
- {
- "opcode": "returnVersionData",
- "blockType": "reporter",
- "text": "Extension version"
- },
- {
- "opcode": "returnServerVersion",
- "blockType": "reporter",
- "text": "Server version"
- },
- {
- "opcode": "returnServerList",
- "blockType": "reporter",
- "text": "Server list"
- },
- {
- "opcode": "returnMOTD",
- "blockType": "reporter",
- "text": "Server MOTD"
- },
- {
- "opcode": "returnClientIP",
- "blockType": "reporter",
- "text": "My IP address"
- },
- {
- "opcode": 'returnListenerData',
- "blockType": "reporter",
- "text": "Response for listener [ID]",
- "arguments": {
- "ID": {
- "type": "string",
- "defaultValue": "example-listener",
- },
- },
- },
- {
- "opcode": "readQueueSize",
- "blockType": "reporter",
- "text": "Size of queue for [TYPE]",
- "arguments": {
- "TYPE": {
- "type": "string",
- "menu": "allmenu",
- "defaultValue": "All data",
- },
- },
- },
- {
- "opcode": "readQueueData",
- "blockType": "reporter",
- "text": "Packet queue for [TYPE]",
- "arguments": {
- "TYPE": {
- "type": "string",
- "menu": "allmenu",
- "defaultValue": "All data",
- },
- },
- },
- {
- "opcode": 'returnVarData',
- "blockType": "reporter",
- "text": "[TYPE] [VAR] data",
- "arguments": {
- "VAR": {
- "type": "string",
- "defaultValue": "Apple",
- },
- "TYPE": {
- "type": "string",
- "menu": "varmenu",
- "defaultValue": "Global variables",
- },
- },
- },
- {
- "opcode": 'parseJSON',
- "blockType": "reporter",
- "text": '[PATH] of [JSON_STRING]',
- "arguments": {
- "PATH": {
- "type": "string",
- "defaultValue": 'fruit/apples',
- },
- "JSON_STRING": {
- "type": "string",
- "defaultValue": '{"fruit": {"apples": 2, "bananas": 3}, "total_fruit": 5}',
- },
- },
- },
- {
- "opcode": 'getFromJSONArray',
- "blockType": "reporter",
- "text": 'Get [NUM] from JSON array [ARRAY]',
- "arguments": {
- "NUM": {
- "type": "number",
- "defaultValue": 0,
- },
- "ARRAY": {
- "type": "string",
- "defaultValue": '["foo","bar"]',
- }
- }
- },
- {
- "opcode": 'fetchURL',
- "blockType": "reporter",
- "blockAllThreads": "true",
- "text": "Fetch data from URL [url]",
- "arguments": {
- "url": {
- "type": "string",
- "defaultValue": "https://mikedev101.github.io/cloudlink/fetch_test",
- },
- },
- },
- {
- "opcode": 'requestURL',
- "blockType": "reporter",
- "blockAllThreads": "true",
- "text": 'Send request with method [method] for URL [url] with data [data] and headers [headers]',
- "arguments": {
- "method": {
- "type": "string",
- "defaultValue": 'GET',
- },
- "url": {
- "type": "string",
- "defaultValue": 'https://mikedev101.github.io/cloudlink/fetch_test',
- },
- "data": {
- "type": "string",
- "defaultValue": '{}'
- },
- "headers": {
- "type": "string",
- "defaultValue": '{}'
- },
- }
- },
- {
- "opcode": 'makeJSON',
- "blockType": "reporter",
- "text": 'Convert [toBeJSONified] to JSON',
- "arguments": {
- "toBeJSONified": {
- "type": "string",
- "defaultValue": '{"test": true}',
- },
- }
- },
- {
- "opcode": 'onConnect',
- "blockType": "hat",
- "text": 'When connected',
- "blockAllThreads": "true"
- },
- {
- "opcode": 'onClose',
- "blockType": "hat",
- "text": 'When disconnected',
- "blockAllThreads": "true"
- },
- {
- "opcode": 'onListener',
- "blockType": "hat",
- "text": 'When I receive new packet with listener [ID]',
- "blockAllThreads": "true",
- "arguments": {
- "ID": {
- "type": "string",
- "defaultValue": "example-listener",
- },
- },
- },
- {
- "opcode": 'onNewPacket',
- "blockType": "hat",
- "text": 'When I receive new [TYPE] packet',
- "blockAllThreads": "true",
- "arguments": {
- "TYPE": {
- "type": "string",
- "menu": "almostallmenu",
- "defaultValue": 'Global data'
- },
- },
- },
- {
- "opcode": 'onNewVar',
- "blockType": "hat",
- "text": 'When I receive new [TYPE] data for [VAR]',
- "blockAllThreads": "true",
- "arguments": {
- "TYPE": {
- "type": "string",
- "menu": "varmenu",
- "defaultValue": 'Global variables',
- },
- "VAR": {
- "type": "string",
- "defaultValue": 'Apple',
- },
- },
- },
- {
- "opcode": 'getComState',
- "blockType": "Boolean",
- "text": 'Connected?',
- },
- {
- "opcode": 'getRoomState',
- "blockType": "Boolean",
- "text": 'Linked to rooms?',
- },
- {
- "opcode": 'getComLostConnectionState',
- "blockType": "Boolean",
- "text": 'Lost connection?',
- },
- {
- "opcode": 'getComFailedConnectionState',
- "blockType": "Boolean",
- "text": 'Failed to connnect?',
- },
- {
- "opcode": 'getUsernameState',
- "blockType": "Boolean",
- "text": 'Username synced?',
- },
- {
- "opcode": 'returnIsNewData',
- "blockType": "Boolean",
- "text": 'Got New [TYPE]?',
- "arguments": {
- "TYPE": {
- "type": "string",
- "menu": "datamenu",
- "defaultValue": 'Global data',
- },
- },
- },
- {
- "opcode": 'returnIsNewVarData',
- "blockType": "Boolean",
- "text": 'Got New [TYPE] data for variable [VAR]?',
- "arguments": {
- "TYPE": {
- "type": "string",
- "menu": "varmenu",
- "defaultValue": 'Global variables',
- },
- "VAR": {
- "type": "string",
- "defaultValue": 'Apple',
- },
- },
- },
- {
- "opcode": 'returnIsNewListener',
- "blockType": "Boolean",
- "text": 'Got new packet with listener [ID]?',
- "blockAllThreads": "true",
- "arguments": {
- "ID": {
- "type": "string",
- "defaultValue": "example-listener",
- },
- },
- },
- {
- "opcode": 'checkForID',
- "blockType": "Boolean",
- "text": 'ID [ID] connected?',
- "arguments": {
- "ID": {
- "type": "string",
- "defaultValue": 'Another name',
- },
- },
- },
- {
- "opcode": 'isValidJSON',
- "blockType": "Boolean",
- "text": 'Is [JSON_STRING] valid JSON?',
- "arguments": {
- "JSON_STRING": {
- "type": "string",
- "defaultValue": '{"fruit": {"apples": 2, "bananas": 3}, "total_fruit": 5}',
- },
- },
- },
- {
- "opcode": 'openSocket',
- "blockType": "command",
- "text": 'Connect to [IP]',
- "blockAllThreads": "true",
- "arguments": {
- "IP": {
- "type": "string",
- "defaultValue": 'ws://127.0.0.1:3000/',
- },
- },
- },
- {
- "opcode": 'openSocketPublicServers',
- "blockType": "command",
- "text": 'Connect to server [ID]',
- "blockAllThreads": "true",
- "arguments": {
- "ID": {
- "type": "number",
- "defaultValue": '',
- },
- },
- },
- {
- "opcode": 'closeSocket',
- "blockType": "command",
- "blockAllThreads": "true",
- "text": 'Disconnect',
- },
- {
- "opcode": 'setMyName',
- "blockType": "command",
- "text": 'Set [NAME] as username',
- "blockAllThreads": "true",
- "arguments": {
- "NAME": {
- "type": "string",
- "defaultValue": "A name",
- },
- },
- },
- {
- "opcode": 'createListener',
- "blockType": "command",
- "text": 'Attach listener [ID] to next packet',
- "blockAllThreads": "true",
- "arguments": {
- "ID": {
- "type": "string",
- "defaultValue": "example-listener",
- },
- },
- },
- {
- "opcode": 'linkToRooms',
- "blockType": "command",
- "text": 'Link to room(s) [ROOMS]',
- "blockAllThreads": "true",
- "arguments": {
- "ROOMS": {
- "type": "string",
- "defaultValue": '["test"]',
- },
- }
- },
- {
- "opcode": 'selectRoomsInNextPacket',
- "blockType": "command",
- "text": 'Select room(s) [ROOMS] for next packet',
- "blockAllThreads": "true",
- "arguments": {
- "ROOMS": {
- "type": "string",
- "defaultValue": '["test"]',
- },
- },
- },
- {
- "opcode": 'unlinkFromRooms',
- "blockType": "command",
- "text": 'Unlink from all rooms',
- "blockAllThreads": "true"
- },
- {
- "opcode": 'sendGData',
- "blockType": "command",
- "text": 'Send [DATA]',
- "blockAllThreads": "true",
- "arguments": {
- "DATA": {
- "type": "string",
- "defaultValue": 'Apple'
- }
- }
- },
- {
- "opcode": 'sendPData',
- "blockType": "command",
- "text": 'Send [DATA] to [ID]',
- "blockAllThreads": "true",
- "arguments": {
- "DATA": {
- "type": "string",
- "defaultValue": 'Apple'
- },
- "ID": {
- "type": "string",
- "defaultValue": 'Another name'
- }
- }
- },
- {
- "opcode": 'sendGDataAsVar',
- "blockType": "command",
- "text": 'Send variable [VAR] with data [DATA]',
- "blockAllThreads": "true",
- "arguments": {
- "DATA": {
- "type": "string",
- "defaultValue": 'Banana'
- },
- "VAR": {
- "type": "string",
- "defaultValue": 'Apple'
- }
- }
- },
- {
- "opcode": 'sendPDataAsVar',
- "blockType": "command",
- "text": 'Send variable [VAR] to [ID] with data [DATA]',
- "blockAllThreads": "true",
- "arguments": {
- "DATA": {
- "type": "string",
- "defaultValue": 'Banana'
- },
- "ID": {
- "type": "string",
- "defaultValue": 'Another name'
- },
- "VAR": {
- "type": "string",
- "defaultValue": 'Apple'
- }
- }
- },
- {
- "opcode": 'runCMDnoID',
- "blockType": "command",
- "text": 'Send command without ID [CMD] [DATA]',
- "blockAllThreads": "true",
- "arguments": {
- "CMD": {
- "type": "string",
- "defaultValue": 'direct'
- },
- "DATA": {
- "type": "string",
- "defaultValue": 'val'
- }
- }
- },
- {
- "opcode": 'runCMD',
- "blockType": "command",
- "text": 'Send command [CMD] [ID] [DATA]',
- "blockAllThreads": "true",
- "arguments": {
- "CMD": {
- "type": "string",
- "defaultValue": 'direct'
- },
- "ID": {
- "type": "string",
- "defaultValue": 'id'
- },
- "DATA": {
- "type": "string",
- "defaultValue": 'val'
- }
- }
- },
- {
- "opcode": 'resetNewData',
- "blockType": "command",
- "text": 'Reset got new [TYPE] status',
- "blockAllThreads": "true",
- "arguments": {
- "TYPE": {
- "type": "string",
- "menu": "datamenu",
- "defaultValue": 'Global data'
- }
- }
- },
- {
- "opcode": 'resetNewVarData',
- "blockType": "command",
- "text": 'Reset got new [TYPE] [VAR] status',
- "blockAllThreads": "true",
- "arguments": {
- "TYPE": {
- "type": "string",
- "menu": "varmenu",
- "defaultValue": 'Global variables'
- },
- "VAR": {
- "type": "string",
- "defaultValue": 'Apple'
- }
- }
- },
- {
- "opcode": 'resetNewListener',
- "blockType": "command",
- "text": 'Reset got new [ID] listener status',
- "blockAllThreads": "true",
- "arguments": {
- "ID": {
- "type": "string",
- "defaultValue": 'example-listener'
- }
- }
- },
- {
- "opcode": 'clearAllPackets',
- "blockType": "command",
- "text": "Clear all packets for [TYPE]",
- "arguments": {
- "TYPE": {
- "type": "string",
- "menu": "allmenu",
- "defaultValue": "All data"
- },
- },
- }
- ],
- "menus": {
- "coms": {
- "items": ["Connected", "Username synced"]
- },
- "datamenu": {
- "items": ['Global data', 'Private data', 'Direct data', 'Status code']
- },
- "varmenu": {
- "items": ['Global variables', 'Private variables']
- },
- "allmenu": {
- "items": ['Global data', 'Private data', 'Direct data', 'Status code', "Global variables", "Private variables", "All data"]
- },
- "almostallmenu": {
- "items": ['Global data', 'Private data', 'Direct data', 'Status code', "Global variables", "Private variables"]
- },
- },
- };
- };
-
- // Code for blocks go here
-
- returnGlobalData() {
- if (this.socketData.gmsg.length != 0) {
-
- let data = (this.socketData.gmsg[this.socketData.gmsg.length - 1].val);
-
- if (typeof(data) == "object") {
- data = JSON.stringify(data); // Make the JSON safe for Scratch
- }
-
- return data;
- } else {
- return "";
- };
- };
-
- returnPrivateData() {
- if (this.socketData.pmsg.length != 0) {
- let data = (this.socketData.pmsg[this.socketData.pmsg.length - 1].val);
-
- if (typeof (data) == "object") {
- data = JSON.stringify(data); // Make the JSON safe for Scratch
- }
-
- return data;
- } else {
- return "";
- };
- };
-
- returnDirectData() {
- if (this.socketData.direct.length != 0) {
- let data = (this.socketData.direct[this.socketData.direct.length - 1].val);
-
- if (typeof (data) == "object") {
- data = JSON.stringify(data); // Make the JSON safe for Scratch
- }
-
- return data;
- } else {
- return "";
- };
- };
-
- returnLinkData() {
- return String(this.link_status);
- };
-
- returnStatusCode() {
- if (this.socketData.statuscode.length != 0) {
- let data = (this.socketData.statuscode[this.socketData.statuscode.length - 1].code);
-
- if (typeof (data) == "object") {
- data = JSON.stringify(data); // Make the JSON safe for Scratch
- }
-
- return data;
- } else {
- return "";
- };
- };
-
- returnUserListData() {
- return JSON.stringify(this.socketData.ulist);
- };
-
- returnUsernameData() {
- let data = this.username;
-
- if (typeof (data) == "object") {
- data = JSON.stringify(data); // Make the JSON safe for Scratch
- }
-
- return data;
- };
-
- returnVersionData() {
- return String(this.version);
- };
-
- returnServerVersion() {
- return String(this.socketData.server_version);
- };
-
- returnServerList() {
- return JSON.stringify(servers);
- };
-
- returnMOTD() {
- return String(this.socketData.motd);
- };
-
- returnClientIP() {
- return String(this.socketData.client_ip);
- };
-
- returnListenerData({ID}) {
- const self = this;
- if ((this.isRunning) && (this.socketListeners.hasOwnProperty(String(ID)))) {
- return JSON.stringify(this.socketListenersData[ID]);
- } else {
- return "{}";
- };
- };
-
- readQueueSize({TYPE}) {
- if (this.menuRemap[String(TYPE)] == "all") {
- let tmp_size = 0;
- tmp_size = tmp_size + this.socketData.gmsg.length;
- tmp_size = tmp_size + this.socketData.pmsg.length;
- tmp_size = tmp_size + this.socketData.direct.length;
- tmp_size = tmp_size + this.socketData.statuscode.length;
- tmp_size = tmp_size + this.socketData.gvar.length;
- tmp_size = tmp_size + this.socketData.pvar.length;
- return tmp_size;
- } else {
- return this.socketData[this.menuRemap[String(TYPE)]].length;
- };
- };
-
- readQueueData({TYPE}) {
- if (this.menuRemap[String(TYPE)] == "all") {
- let tmp_socketData = JSON.parse(JSON.stringify(this.socketData)); // Deep copy
-
- delete tmp_socketData.motd;
- delete tmp_socketData.client_ip;
- delete tmp_socketData.ulist;
- delete tmp_socketData.server_version;
-
- return JSON.stringify(tmp_socketData);
- } else {
- return JSON.stringify(this.socketData[this.menuRemap[String(TYPE)]]);
- };
- };
-
- returnVarData({ TYPE, VAR }) {
- if (this.isRunning) {
- if (this.varData.hasOwnProperty(this.menuRemap[TYPE])) {
- if (this.varData[this.menuRemap[TYPE]].hasOwnProperty(VAR)) {
- return this.varData[this.menuRemap[TYPE]][VAR].value;
- } else {
- return "";
- };
- } else {
- return "";
- };
- } else {
- return "";
- };
- };
-
- parseJSON({PATH, JSON_STRING}) {
- try {
- const path = PATH.toString().split('/').map(prop => decodeURIComponent(prop));
- if (path[0] === '') path.splice(0, 1);
- if (path[path.length - 1] === '') path.splice(-1, 1);
- let json;
- try {
- json = JSON.parse(' ' + JSON_STRING);
- } catch (e) {
- return e.message;
- };
- path.forEach(prop => json = json[prop]);
- if (json === null) return 'null';
- else if (json === undefined) return '';
- else if (typeof json === 'object') return JSON.stringify(json);
- else return json.toString();
- } catch (err) {
- return '';
- };
- };
-
- getFromJSONArray({NUM, ARRAY}) {
- var json_array = JSON.parse(ARRAY);
- if (json_array[NUM] == "undefined") {
- return "";
- } else {
- let data = json_array[NUM];
-
- if (typeof (data) == "object") {
- data = JSON.stringify(data); // Make the JSON safe for Scratch
- }
-
- return data;
- }
- };
-
- fetchURL(args) {
- return Scratch.fetch(args.url, {
- method: "GET"
- }).then(response => response.text());
- };
-
- requestURL(args) {
- if (args.method == "GET" || args.method == "HEAD") {
- return Scratch.fetch(args.url, {
- method: args.method,
- headers: JSON.parse(args.headers)
- }).then(response => response.text());
- } else {
- return Scratch.fetch(args.url, {
- method: args.method,
- headers: JSON.parse(args.headers),
- body: JSON.parse(args.data)
- }).then(response => response.text());
- }
- };
-
- isValidJSON({JSON_STRING}) {
- return jsonCheck(JSON_STRING);
- };
-
- makeJSON({toBeJSONified}) {
- if (typeof(toBeJSONified) == "string") {
- try {
- JSON.parse(toBeJSONified);
- return String(toBeJSONified);
- } catch(err) {
- return "Not JSON!";
- }
- } else if (typeof(toBeJSONified) == "object") {
- return JSON.stringify(toBeJSONified);
- } else {
- return "Not JSON!";
- };
- };
-
- onConnect() {
- const self = this;
- if (self.connect_hat == 0 && self.isRunning && self.protocolOk) {
- self.connect_hat = 1;
- return true;
- } else {
- return false;
- };
- };
-
- onClose() {
- const self = this;
- if (self.close_hat == 0 && !self.isRunning) {
- self.close_hat = 1;
- return true;
- } else {
- return false;
- };
- };
-
- onListener({ ID }) {
- const self = this;
- if ((this.isRunning) && (this.socketListeners.hasOwnProperty(String(ID)))) {
- if (self.socketListeners[String(ID)]) {
- self.socketListeners[String(ID)] = false;
- return true;
- } else {
- return false;
- };
- } else {
- return false;
- };
- };
-
- onNewPacket({ TYPE }) {
- const self = this;
- if ((this.isRunning) && (this.newSocketData[this.menuRemap[String(TYPE)]])) {
- self.newSocketData[this.menuRemap[String(TYPE)]] = false;
- return true;
- } else {
- return false;
- };
- };
-
- onNewVar({ TYPE, VAR }) {
- const self = this;
- if (this.isRunning) {
- if (this.varData.hasOwnProperty(this.menuRemap[TYPE])) {
- if (this.varData[this.menuRemap[TYPE]].hasOwnProperty(VAR)) {
- if (this.varData[this.menuRemap[TYPE]][VAR].isNew) {
- self.varData[this.menuRemap[TYPE]][VAR].isNew = false;
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- };
- } else {
- return false;
- };
- } else {
- return false;
- };
- };
-
- getComState(){
- return String((this.link_status == 2) || this.protocolOk);
- };
-
- getRoomState() {
- return this.isLinked;
- };
-
- getComLostConnectionState() {
- return this.wasConnectionDropped;
- };
-
- getComFailedConnectionState() {
- return this.didConnectionFail;
- };
-
- getUsernameState(){
- return this.isUsernameSet;
- };
-
- returnIsNewData({TYPE}){
- if (this.isRunning) {
- return this.newSocketData[this.menuRemap[String(TYPE)]];
- } else {
- return false;
- };
- };
-
- returnIsNewVarData({ TYPE, VAR }) {
- if (this.isRunning) {
- if (this.varData.hasOwnProperty(this.menuRemap[TYPE])) {
- if (this.varData[this.menuRemap[TYPE]].hasOwnProperty(VAR)) {
- return this.varData[this.menuRemap[TYPE]][VAR].isNew;
- } else {
- return false;
- };
- } else {
- return false;
- };
- } else {
- return false;
- };
- };
-
- returnIsNewListener({ ID }) {
- if (this.isRunning) {
- if (this.socketListeners.hasOwnProperty(String(ID))) {
- return this.socketListeners[ID];
- } else {
- return false;
- };
- } else {
- return false;
- };
- };
-
- checkForID({ ID }) {
- return find_id(ID, this.socketData.ulist);
- };
-
- async openSocket({IP}) {
- const self = this;
- if (!self.isRunning) {
- if (!await Scratch.canFetch(IP)) {
- return;
- }
-
- console.log("Starting socket.");
- self.link_status = 1;
-
- self.disconnectWasClean = false;
- self.wasConnectionDropped = false;
- self.didConnectionFail = false;
-
- mWS = new WebSocket(String(IP));
-
- mWS.onerror = function(){
- self.isRunning = false;
- };
-
- mWS.onopen = function(){
- self.isRunning = true;
- self.packet_queue = {};
- self.link_status = 2;
-
- // Send the handshake request to get server to detect client protocol
- mWS.send(JSON.stringify({"cmd": "handshake", "listener": "setprotocol"}))
-
- console.log("Successfully opened socket.");
- };
-
- mWS.onmessage = function(event){
- let tmp_socketData = JSON.parse(event.data);
- console.log("RX:", tmp_socketData);
-
- if (self.queueableCmds.includes(tmp_socketData.cmd)) {
- self.socketData[tmp_socketData.cmd].push(tmp_socketData);
- } else {
- if (tmp_socketData.cmd == "ulist") {
- // ulist functionality has been changed in server 0.1.9
- if (tmp_socketData.hasOwnProperty("mode")) {
- if (tmp_socketData.mode == "set") {
- self.socketData["ulist"] = tmp_socketData.val;
- } else if (tmp_socketData.mode == "add") {
- if (!self.socketData.ulist.some(o => ((o.username === tmp_socketData.val.username) && (o.id == tmp_socketData.val.id)))) {
- self.socketData["ulist"].push(tmp_socketData.val);
- } else {
- console.log("Could not perform ulist method add, client", tmp_socketData.val, "already exists");
- };
- } else if (tmp_socketData.mode == "remove") {
- if (self.socketData.ulist.some(o => ((o.username === tmp_socketData.val.username) && (o.id == tmp_socketData.val.id)))) {
- // This is by far the fugliest thing I have ever written in JS, or in any programming language... thanks I hate it
- self.socketData["ulist"] = self.socketData["ulist"].filter(user => ((!(user.username === tmp_socketData.val.username)) && (!(user.id == tmp_socketData.val.id))));
- } else {
- console.log("Could not perform ulist method remove, client", tmp_socketData.val, "was not found");
- };
- } else {
- console.log("Could not understand ulist method:", tmp_socketData.mode);
- };
- } else {
- // Retain compatibility wtih existing servers
- self.socketData["ulist"] = tmp_socketData.val;
- };
- } else {
- self.socketData[tmp_socketData.cmd] = tmp_socketData.val;
- };
- };
-
- if (self.newSocketData.hasOwnProperty(tmp_socketData.cmd)) {
- self.newSocketData[tmp_socketData.cmd] = true;
- };
-
- if (self.varCmds.includes(tmp_socketData.cmd)) {
- self.varData[tmp_socketData.cmd][tmp_socketData.name] = {
- "value": tmp_socketData.val,
- "isNew": true
- };
- };
- if (tmp_socketData.hasOwnProperty("listener")) {
- if (tmp_socketData.listener == "setusername") {
- self.socketListeners["setusername"] = true;
- if (tmp_socketData.code == "I:100 | OK") {
- self.username = tmp_socketData.val;
- self.isUsernameSyncing = false;
- self.isUsernameSet = true;
- console.log("Username was accepted by the server, and has been set to:", self.username);
- } else {
- console.warn("Username was rejected by the server. Error code:", String(tmp_socketData.code));
- self.isUsernameSyncing = false;
- };
- } else if (tmp_socketData.listener == "roomLink") {
- self.isRoomSetting = false;
- self.socketListeners["roomLink"] = true;
- if (tmp_socketData.code == "I:100 | OK") {
- console.log("Linking to room(s) was accepted by the server!");
- self.isLinked = true;
- } else {
- console.warn("Linking to room(s) was rejected by the server. Error code:", String(tmp_socketData.code));
- self.enableRoom = false;
- self.isLinked = false;
- self.selectRoom = "";
- };
- } else if ((tmp_socketData.listener == "setprotocol") && (!this.protocolOk)) {
- console.log("Server successfully set client protocol to cloudlink!");
- self.socketData.statuscode = [];
- self.protocolOk = true;
- self.socketListeners["setprotocol"] = true;
- } else {
- if (self.socketListeners.hasOwnProperty(tmp_socketData.listener)) {
- self.socketListeners[tmp_socketData.listener] = true;
- };
- };
- self.socketListenersData[tmp_socketData.listener] = tmp_socketData;
- };
- self.packet_hat = 0;
- };
-
- mWS.onclose = function() {
- self.isRunning = false;
- self.connect_hat = 0;
- self.packet_hat = 0;
- self.protocolOk = false;
- if (self.close_hat == 1) {
- self.close_hat = 0;
- };
- self.socketData = {
- "gmsg": [],
- "pmsg": [],
- "direct": [],
- "statuscode": [],
- "gvar": [],
- "pvar": [],
- "motd": "",
- "client_ip": "",
- "ulist": [],
- "server_version": ""
- };
- self.newSocketData = {
- "gmsg": false,
- "pmsg": false,
- "direct": false,
- "statuscode": false,
- "gvar": false,
- "pvar": false
- };
- self.socketListeners = {};
- self.username = "";
- self.tmp_username = "";
- self.isUsernameSyncing = false;
- self.isUsernameSet = false;
- self.enableListener = false;
- self.setListener = "";
- self.enableRoom = false;
- self.selectRoom = "";
- self.isLinked = false;
- self.isRoomSetting = false;
-
- if (self.link_status != 1) {
- if (self.disconnectWasClean) {
- self.link_status = 3;
- console.log("Socket closed.");
- self.wasConnectionDropped = false;
- self.didConnectionFail = false;
- } else {
- self.link_status = 4;
- console.error("Lost connection to the server.");
- self.wasConnectionDropped = true;
- self.didConnectionFail = false;
- };
- } else {
- self.link_status = 4;
- console.error("Failed to connect to server.");
- self.wasConnectionDropped = false;
- self.didConnectionFail = true;
- };
- };
- } else {
- console.warn("Socket is already open.");
- };
- }
-
- openSocketPublicServers({ ID }){
- if (servers.hasOwnProperty(ID)) {
- console.log("Connecting to:", servers[ID].url)
- this.openSocket({"IP": servers[ID].url});
- };
- };
-
- closeSocket(){
- const self = this;
- if (this.isRunning) {
- console.log("Closing socket...");
- mWS.close(1000,'script closure');
- self.disconnectWasClean = true;
- } else {
- console.warn("Socket is not open.");
- };
- }
-
- setMyName({NAME}) {
- const self = this;
- if (this.isRunning) {
- if (!this.isUsernameSyncing) {
- if (!this.isUsernameSet){
- if (String(NAME) != "") {
- if ((!(String(NAME).length > 20))) {
- if (!(String(NAME) == "%CA%" || String(NAME) == "%CC%" || String(NAME) == "%CD%" || String(NAME) == "%MS%")){
- let tmp_msg = {
- cmd: "setid",
- val: String(NAME),
- listener: "setusername"
- };
-
- console.log("TX:", tmp_msg);
- mWS.send(JSON.stringify(tmp_msg));
-
- self.tmp_username = String(NAME);
- self.isUsernameSyncing = true;
-
- } else {
- console.log("Blocking attempt to use reserved usernames");
- };
- } else {
- console.log("Blocking attempt to use username larger than 20 characters, username is " + String(NAME).length + " characters long");
- };
- } else {
- console.log("Blocking attempt to use blank username");
- };
- } else {
- console.warn("Username already has been set!");
- };
- } else {
- console.warn("Username is still syncing!");
- };
- };
- };
-
- createListener({ ID }) {
- self = this;
- if (this.isRunning) {
- if (!this.enableListener) {
- self.enableListener = true;
- self.setListener = String(ID);
- } else {
- console.warn("Listeners were already created!");
- };
- } else {
- console.log("Cannot assign a listener to a packet while disconnected");
- };
- };
-
- linkToRooms({ ROOMS }) {
- const self = this;
-
- if (this.isRunning) {
- if (!this.isRoomSetting) {
- if (!(String(ROOMS).length > 1000)) {
- let tmp_msg = {
- cmd: "link",
- val: autoConvert(ROOMS),
- listener: "roomLink"
- };
-
- console.log("TX:", tmp_msg);
- mWS.send(JSON.stringify(tmp_msg));
-
- self.isRoomSetting = true;
-
- } else {
- console.warn("Blocking attempt to send a room ID / room list larger than 1000 bytes (1 KB), room ID / room list is " + String(ROOMS).length + " bytes");
- };
- } else {
- console.warn("Still linking to rooms!");
- };
- } else {
- console.warn("Socket is not open.");
- };
- };
-
- selectRoomsInNextPacket({ROOMS}) {
- const self = this;
- if (this.isRunning) {
- if (this.isLinked) {
- if (!this.enableRoom) {
- if (!(String(ROOMS).length > 1000)) {
- self.enableRoom = true;
- self.selectRoom = ROOMS;
- } else {
- console.warn("Blocking attempt to select a room ID / room list larger than 1000 bytes (1 KB), room ID / room list is " + String(ROOMS).length + " bytes");
- };
- } else {
- console.warn("Rooms were already selected!");
- };
- } else {
- console.warn("Not linked to any room(s)!");
- };
- } else {
- console.warn("Socket is not open.");
- };
- };
-
- unlinkFromRooms() {
- const self = this;
- if (this.isRunning) {
- if (this.isLinked) {
- let tmp_msg = {
- cmd: "unlink",
- val: ""
- };
-
- if (this.enableListener) {
- tmp_msg["listener"] = autoConvert(this.setListener);
- };
-
- console.log("TX:", tmp_msg);
- mWS.send(JSON.stringify(tmp_msg));
-
- if (this.enableListener) {
- if (!self.socketListeners.hasOwnProperty(this.setListener)) {
- self.socketListeners[this.setListener] = false;
- };
- self.enableListener = false;
- };
-
- self.isLinked = false;
- } else {
- console.warn("Not linked to any rooms!");
- };
- } else {
- console.warn("Socket is not open.");
- };
- };
-
- sendGData({DATA}){
- const self = this;
- if (this.isRunning) {
- if (!(String(DATA).length > 1000)) {
- let tmp_msg = {
- cmd: "gmsg",
- val: autoConvert(DATA)
- };
-
- if (this.enableListener) {
- tmp_msg["listener"] = String(this.setListener);
- };
-
- if (this.enableRoom) {
- tmp_msg["rooms"] = autoConvert(this.selectRoom);
- };
-
- console.log("TX:", tmp_msg);
- mWS.send(JSON.stringify(tmp_msg));
-
- if (this.enableListener) {
- if (!self.socketListeners.hasOwnProperty(this.setListener)) {
- self.socketListeners[this.setListener] = false;
- };
- self.enableListener = false;
- };
- if (this.enableRoom) {
- self.enableRoom = false;
- self.selectRoom = "";
- };
-
- } else {
- console.warn("Blocking attempt to send packet larger than 1000 bytes (1 KB), packet is " + String(DATA).length + " bytes");
- };
- } else {
- console.warn("Socket is not open.");
- };
- };
-
- sendPData({DATA, ID}) {
- const self = this;
- if (this.isRunning) {
- if (!(String(DATA).length > 1000)) {
- let tmp_msg = {
- cmd: "pmsg",
- val: autoConvert(DATA),
- id: autoConvert(ID)
- }
-
- if (this.enableListener) {
- tmp_msg["listener"] = String(this.setListener);
- };
- if (this.enableRoom) {
- tmp_msg["rooms"] = autoConvert(this.selectRoom);
- };
-
- console.log("TX:", tmp_msg);
- mWS.send(JSON.stringify(tmp_msg));
-
- if (this.enableListener) {
- if (!self.socketListeners.hasOwnProperty(this.setListener)) {
- self.socketListeners[this.setListener] = false;
- };
- self.enableListener = false;
- };
- if (this.enableRoom) {
- self.enableRoom = false;
- self.selectRoom = "";
- };
-
- } else {
- console.warn("Blocking attempt to send packet larger than 1000 bytes (1 KB), packet is " + String(DATA).length + " bytes");
- };
- } else {
- console.warn("Socket is not open.");
- };
- };
-
- sendGDataAsVar({VAR, DATA }) {
- const self = this;
- if (this.isRunning) {
- if (!(String(DATA).length > 1000)) {
- let tmp_msg = {
- cmd: "gvar",
- name: VAR,
- val: autoConvert(DATA)
- }
-
- if (this.enableListener) {
- tmp_msg["listener"] = String(this.setListener);
- };
- if (this.enableRoom) {
- tmp_msg["rooms"] = autoConvert(this.selectRoom);
- };
-
- console.log("TX:", tmp_msg);
- mWS.send(JSON.stringify(tmp_msg));
-
- if (this.enableListener) {
- if (!self.socketListeners.hasOwnProperty(this.setListener)) {
- self.socketListeners[this.setListener] = false;
- };
- self.enableListener = false;
- };
- if (this.enableRoom) {
- self.enableRoom = false;
- self.selectRoom = "";
- };
-
- } else {
- console.warn("Blocking attempt to send packet larger than 1000 bytes (1 KB), packet is " + String(DATA).length + " bytes");
- };
- } else {
- console.warn("Socket is not open.");
- };
- };
-
- sendPDataAsVar({VAR, ID, DATA}) {
- const self = this;
- if (this.isRunning) {
- if (!(String(DATA).length > 1000)) {
- let tmp_msg = {
- cmd: "pvar",
- name: VAR,
- val: autoConvert(DATA),
- id: autoConvert(ID)
- }
-
- if (this.enableListener) {
- tmp_msg["listener"] = String(this.setListener);
- };
- if (this.enableRoom) {
- tmp_msg["rooms"] = autoConvert(this.selectRoom);
- };
-
- console.log("TX:", tmp_msg);
- mWS.send(JSON.stringify(tmp_msg));
-
- if (this.enableListener) {
- if (!self.socketListeners.hasOwnProperty(this.setListener)) {
- self.socketListeners[this.setListener] = false;
- };
- self.enableListener = false;
- };
- if (this.enableRoom) {
- self.enableRoom = false;
- self.selectRoom = "";
- };
-
- } else {
- console.warn("Blocking attempt to send packet larger than 1000 bytes (1 KB), packet is " + String(DATA).length + " bytes");
- };
- } else {
- console.warn("Socket is not open.");
- };
- };
-
- runCMDnoID({CMD, DATA}) {
- const self = this;
- if (this.isRunning) {
- if (!(String(CMD).length > 100) || !(String(DATA).length > 1000)) {
- let tmp_msg = {
- cmd: String(CMD),
- val: autoConvert(DATA)
- }
-
- if (this.enableListener) {
- tmp_msg["listener"] = String(this.setListener);
- };
- if (this.enableRoom) {
- tmp_msg["rooms"] = String(this.selectRoom);
- };
-
- console.log("TX:", tmp_msg);
- mWS.send(JSON.stringify(tmp_msg));
-
- if (this.enableListener) {
- if (!self.socketListeners.hasOwnProperty(this.setListener)) {
- self.socketListeners[this.setListener] = false;
- };
- self.enableListener = false;
- };
- if (this.enableRoom) {
- self.enableRoom = false;
- self.selectRoom = "";
- };
-
- } else {
- console.warn("Blocking attempt to send packet with questionably long arguments");
- };
- } else {
- console.warn("Socket is not open.");
- };
- };
-
- runCMD({CMD, ID, DATA}) {
- const self = this;
- if (this.isRunning) {
- if (!(String(CMD).length > 100) || !(String(ID).length > 20) || !(String(DATA).length > 1000)) {
- let tmp_msg = {
- cmd: String(CMD),
- id: autoConvert(ID),
- val: autoConvert(DATA)
- }
-
- if (this.enableListener) {
- tmp_msg["listener"] = String(this.setListener);
- };
- if (this.enableRoom) {
- tmp_msg["rooms"] = String(this.selectRoom);
- };
-
- console.log("TX:", tmp_msg);
- mWS.send(JSON.stringify(tmp_msg));
-
- if (this.enableListener) {
- if (!self.socketListeners.hasOwnProperty(this.setListener)) {
- self.socketListeners[this.setListener] = false;
- };
- self.enableListener = false;
- };
- if (this.enableRoom) {
- self.enableRoom = false;
- self.selectRoom = "";
- };
-
- } else {
- console.warn("Blocking attempt to send packet with questionably long arguments");
- };
- } else {
- console.warn("Socket is not open.");
- };
- };
-
- resetNewData({TYPE}){
- const self = this;
- if (this.isRunning) {
- self.newSocketData[this.menuRemap[String(TYPE)]] = false;
- };
- };
-
- resetNewVarData({ TYPE, VAR }) {
- const self = this;
- if (this.isRunning) {
- if (this.varData.hasOwnProperty(this.menuRemap[TYPE])) {
- if (this.varData[this.menuRemap[TYPE]].hasOwnProperty(VAR)) {
- self.varData[this.menuRemap[TYPE]][VAR].isNew = false;
- };
- };
- };
- };
-
- resetNewListener({ ID }) {
- const self = this;
- if (this.isRunning) {
- if (this.socketListeners.hasOwnProperty(String(ID))) {
- self.socketListeners[String(ID)] = false;
- };
- };
- };
-
- clearAllPackets({TYPE}){
- const self = this;
- if (this.menuRemap[String(TYPE)] == "all") {
- self.socketData.gmsg = [];
- self.socketData.pmsg = [];
- self.socketData.direct = [];
- self.socketData.statuscode = [];
- self.socketData.gvar = [];
- self.socketData.pvar = [];
- } else {
- self.socketData[this.menuRemap[String(TYPE)]] = [];
- };
- };
-};
-
-console.log("CloudLink 4.0 loaded. Detecting unsandboxed mode.");
-Scratch.extensions.register(new CloudLink(Scratch.vm.runtime));
-
+(function (Scratch) {
+ var servers = {}; // Server list
+ let mWS = null;
+
+ // Get the server URL list
+ try {
+ Scratch.fetch("https://mikedev101.github.io/cloudlink/serverlist.json")
+ .then((response) => {
+ return response.text();
+ })
+ .then((data) => {
+ servers = JSON.parse(data);
+ })
+ .catch((err) => {
+ console.log(err);
+ servers = {};
+ });
+ } catch (err) {
+ console.log(err);
+ servers = {};
+ }
+
+ function find_id(ID, ulist) {
+ // Thanks StackOverflow!
+ if (jsonCheck(ID) && !intCheck(ID)) {
+ return ulist.some(
+ (o) =>
+ o.username === JSON.parse(ID).username && o.id == JSON.parse(ID).id
+ );
+ } else {
+ return ulist.some((o) => o.username === String(ID) || o.id == ID);
+ }
+ }
+
+ function jsonCheck(JSON_STRING) {
+ try {
+ JSON.parse(JSON_STRING);
+ return true;
+ } catch (err) {
+ return false;
+ }
+ }
+
+ function intCheck(value) {
+ return !isNaN(value);
+ }
+
+ function autoConvert(value) {
+ // Check if the value is JSON / Dict first
+ try {
+ JSON.parse(value);
+ return JSON.parse(value);
+ } catch (err) {}
+
+ // Check if the value is an array
+ try {
+ tmp = value;
+ tmp = tmp.replace(/'/g, '"');
+ JSON.parse(tmp);
+ return JSON.parse(tmp);
+ } catch (err) {}
+
+ // Check if an int/float
+ if (!isNaN(value)) {
+ return Number(value);
+ }
+
+ // Leave as the original value if none of the above work
+ return value;
+ }
+
+ class CloudLink {
+ constructor(runtime, extensionId) {
+ // Extension stuff
+ this.runtime = runtime;
+ this.cl_icon =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIyMjUuMzU0OCIgaGVpZ2h0PSIyMjUuMzU0OCIgdmlld0JveD0iMCwwLDIyNS4zNTQ4LDIyNS4zNTQ4Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTI3LjMyMjYsLTY3LjMyMjYpIj48ZyBkYXRhLXBhcGVyLWRhdGE9InsmcXVvdDtpc1BhaW50aW5nTGF5ZXImcXVvdDs6dHJ1ZX0iIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWRhc2hhcnJheT0iIiBzdHJva2UtZGFzaG9mZnNldD0iMCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0xMjcuMzIyNiwxODBjMCwtNjIuMjMwMDEgNTAuNDQ3MzksLTExMi42Nzc0IDExMi42Nzc0LC0xMTIuNjc3NGM2Mi4yMzAwMSwwIDExMi42Nzc0LDUwLjQ0NzM5IDExMi42Nzc0LDExMi42Nzc0YzAsNjIuMjMwMDEgLTUwLjQ0NzM5LDExMi42Nzc0IC0xMTIuNjc3NCwxMTIuNjc3NGMtNjIuMjMwMDEsMCAtMTEyLjY3NzQsLTUwLjQ0NzM5IC0xMTIuNjc3NCwtMTEyLjY3NzR6IiBmaWxsPSIjMDBjMjhjIiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZS13aWR0aD0iMCIvPjxnIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3Ryb2tlLXdpZHRoPSIxIj48cGF0aCBkPSJNMjg2LjEyMDM3LDE1MC41NTc5NWMyMy4yNDA4NiwwIDQyLjA3ODksMTguODM5NDYgNDIuMDc4OSw0Mi4wNzg5YzAsMjMuMjM5NDQgLTE4LjgzODAzLDQyLjA3ODkgLTQyLjA3ODksNDIuMDc4OWgtOTIuMjQwNzRjLTIzLjI0MDg2LDAgLTQyLjA3ODksLTE4LjgzOTQ2IC00Mi4wNzg5LC00Mi4wNzg5YzAsLTIzLjIzOTQ0IDE4LjgzODAzLC00Mi4wNzg5IDQyLjA3ODksLTQyLjA3ODloNC4xODg4N2MxLjgxMTUzLC0yMS41NzA1NSAxOS44OTM1NywtMzguNTEyODkgNDEuOTMxNSwtMzguNTEyODljMjIuMDM3OTMsMCA0MC4xMTk5NywxNi45NDIzNCA0MS45MzE1LDM4LjUxMjg5eiIgZmlsbD0iI2ZmZmZmZiIvPjxwYXRoIGQ9Ik0yODkuMDg2NTUsMjEwLjM0MTE0djkuMDQ2NjdoLTI2LjkxNjYzaC05LjA0NjY3di05LjA0NjY3di01NC41MDMzOWg5LjA0NjY3djU0LjUwMzM5eiIgZmlsbD0iIzAwYzI4YyIvPjxwYXRoIGQ9Ik0yMjIuNDA5MjUsMjE5LjM4NzgxYy04LjM1MzIsMCAtMTYuMzY0MzEsLTMuMzE4MzQgLTIyLjI3MDksLTkuMjI0OTJjLTUuOTA2NjEsLTUuOTA2NTggLTkuMjI0OTEsLTEzLjkxNzY4IC05LjIyNDkxLC0yMi4yNzA4OWMwLC04LjM1MzIgMy4zMTgyOSwtMTYuMzY0MzEgOS4yMjQ5MSwtMjIuMjcwOWM1LjkwNjU5LC01LjkwNjYxIDEzLjkxNzcsLTkuMjI0OTEgMjIuMjcwOSwtOS4yMjQ5MWgyMS4xMDg5djguOTM0OThoLTIxLjEwODl2MC4xMDI1N2MtNS45NTYyOCwwIC0xMS42Njg2NCwyLjM2NjE2IC0xNS44ODAzNyw2LjU3Nzg5Yy00LjIxMTczLDQuMjExNzMgLTYuNTc3ODksOS45MjQwOCAtNi41Nzc4OSwxNS44ODAzN2MwLDUuOTU2MjggMi4zNjYxNiwxMS42Njg2NCA2LjU3Nzg5LDE1Ljg4MDM3YzQuMjExNzMsNC4yMTE3MyA5LjkyNDA4LDYuNTc3OTMgMTUuODgwMzcsNi41Nzc5M3YwLjEwMjUzaDIxLjEwODl2OC45MzQ5OHoiIGZpbGw9IiMwMGMyOGMiLz48L2c+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6MTEyLjY3NzQwNDA4NDA4MzkyOjExMi42Nzc0MDQwODQwODQwMy0tPg==";
+ this.cl_block =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxNzYuMzk4NTQiIGhlaWdodD0iMTIyLjY3MDY5IiB2aWV3Qm94PSIwLDAsMTc2LjM5ODU0LDEyMi42NzA2OSI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTE1MS44MDA3MywtMTE4LjY2NDY2KSI+PGcgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aXNQYWludGluZ0xheWVyJnF1b3Q7OnRydWV9IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PGc+PHBhdGggZD0iTTI4Ni4xMjAzNywxNTcuMTc3NTVjMjMuMjQwODYsMCA0Mi4wNzg5LDE4LjgzOTQ2IDQyLjA3ODksNDIuMDc4OWMwLDIzLjIzOTQ0IC0xOC44MzgwMyw0Mi4wNzg5IC00Mi4wNzg5LDQyLjA3ODloLTkyLjI0MDc0Yy0yMy4yNDA4NiwwIC00Mi4wNzg5LC0xOC44Mzk0NiAtNDIuMDc4OSwtNDIuMDc4OWMwLC0yMy4yMzk0NCAxOC44MzgwMywtNDIuMDc4OSA0Mi4wNzg5LC00Mi4wNzg5aDQuMTg4ODdjMS44MTE1MywtMjEuNTcwNTUgMTkuODkzNTcsLTM4LjUxMjg5IDQxLjkzMTUsLTM4LjUxMjg5YzIyLjAzNzkzLDAgNDAuMTE5OTcsMTYuOTQyMzQgNDEuOTMxNSwzOC41MTI4OXoiIGZpbGw9IiNmZmZmZmYiLz48cGF0aCBkPSJNMjg5LjA4NjU1LDIxNi45NjA3NHY5LjA0NjY3aC0yNi45MTY2M2gtOS4wNDY2N3YtOS4wNDY2N3YtNTQuNTAzMzloOS4wNDY2N3Y1NC41MDMzOXoiIGZpbGw9IiMwMGMyOGMiLz48cGF0aCBkPSJNMjIyLjQwOTI1LDIyNi4wMDc0MWMtOC4zNTMyLDAgLTE2LjM2NDMxLC0zLjMxODM0IC0yMi4yNzA5LC05LjIyNDkyYy01LjkwNjYxLC01LjkwNjU4IC05LjIyNDkxLC0xMy45MTc2OCAtOS4yMjQ5MSwtMjIuMjcwODljMCwtOC4zNTMyIDMuMzE4MjksLTE2LjM2NDMxIDkuMjI0OTEsLTIyLjI3MDljNS45MDY1OSwtNS45MDY2MSAxMy45MTc3LC05LjIyNDkxIDIyLjI3MDksLTkuMjI0OTFoMjEuMTA4OXY4LjkzNDk4aC0yMS4xMDg5djAuMTAyNTdjLTUuOTU2MjgsMCAtMTEuNjY4NjQsMi4zNjYxNiAtMTUuODgwMzcsNi41Nzc4OWMtNC4yMTE3Myw0LjIxMTczIC02LjU3Nzg5LDkuOTI0MDggLTYuNTc3ODksMTUuODgwMzdjMCw1Ljk1NjI4IDIuMzY2MTYsMTEuNjY4NjQgNi41Nzc4OSwxNS44ODAzN2M0LjIxMTczLDQuMjExNzMgOS45MjQwOCw2LjU3NzkzIDE1Ljg4MDM3LDYuNTc3OTN2MC4xMDI1M2gyMS4xMDg5djguOTM0OTh6IiBmaWxsPSIjMDBjMjhjIi8+PC9nPjwvZz48L2c+PC9zdmc+PCEtLXJvdGF0aW9uQ2VudGVyOjg4LjE5OTI2OTk5OTk5OTk4OjYxLjMzNTM0NDk5OTk5OTk5LS0+";
+
+ // Socket data
+ this.socketData = {
+ gmsg: [],
+ pmsg: [],
+ direct: [],
+ statuscode: [],
+ gvar: [],
+ pvar: [],
+ motd: "",
+ client_ip: "",
+ ulist: [],
+ server_version: "",
+ };
+ this.varData = {
+ gvar: {},
+ pvar: {},
+ };
+
+ this.queueableCmds = [
+ "gmsg",
+ "pmsg",
+ "gvar",
+ "pvar",
+ "direct",
+ "statuscode",
+ ];
+ this.varCmds = ["gvar", "pvar"];
+
+ // Listeners
+ this.socketListeners = {};
+ this.socketListenersData = {};
+ this.newSocketData = {
+ gmsg: false,
+ pmsg: false,
+ direct: false,
+ statuscode: false,
+ gvar: false,
+ pvar: false,
+ };
+
+ // Edge-triggered hat blocks
+ this.connect_hat = 0;
+ this.packet_hat = 0;
+ this.close_hat = 0;
+
+ // Status stuff
+ this.isRunning = false;
+ this.isLinked = false;
+ this.version = "S4.0";
+ this.link_status = 0;
+ this.username = "";
+ this.tmp_username = "";
+ this.isUsernameSyncing = false;
+ this.isUsernameSet = false;
+ this.disconnectWasClean = false;
+ this.wasConnectionDropped = false;
+ this.didConnectionFail = false;
+ this.protocolOk = false;
+
+ // Listeners stuff
+ this.enableListener = false;
+ this.setListener = "";
+
+ // Rooms stuff
+ this.enableRoom = false;
+ this.isRoomSetting = false;
+ this.selectRoom = "";
+
+ // Remapping stuff
+ this.menuRemap = {
+ "Global data": "gmsg",
+ "Private data": "pmsg",
+ "Global variables": "gvar",
+ "Private variables": "pvar",
+ "Direct data": "direct",
+ "Status code": "statuscode",
+ "All data": "all",
+ };
+ }
+
+ getInfo() {
+ return {
+ id: "cloudlink",
+ name: "CloudLink",
+ blockIconURI: this.cl_block,
+ menuIconURI: this.cl_icon,
+ docsURI: "https://hackmd.io/@MikeDEV/HJiNYwOfo",
+ blocks: [
+ {
+ opcode: "returnGlobalData",
+ blockType: "reporter",
+ text: "Global data",
+ },
+ {
+ opcode: "returnPrivateData",
+ blockType: "reporter",
+ text: "Private data",
+ },
+ {
+ opcode: "returnDirectData",
+ blockType: "reporter",
+ text: "Direct Data",
+ },
+ {
+ opcode: "returnLinkData",
+ blockType: "reporter",
+ text: "Link status",
+ },
+ {
+ opcode: "returnStatusCode",
+ blockType: "reporter",
+ text: "Status code",
+ },
+ {
+ opcode: "returnUserListData",
+ blockType: "reporter",
+ text: "Usernames",
+ },
+ {
+ opcode: "returnUsernameData",
+ blockType: "reporter",
+ text: "My username",
+ },
+ {
+ opcode: "returnVersionData",
+ blockType: "reporter",
+ text: "Extension version",
+ },
+ {
+ opcode: "returnServerVersion",
+ blockType: "reporter",
+ text: "Server version",
+ },
+ {
+ opcode: "returnServerList",
+ blockType: "reporter",
+ text: "Server list",
+ },
+ {
+ opcode: "returnMOTD",
+ blockType: "reporter",
+ text: "Server MOTD",
+ },
+ {
+ opcode: "returnClientIP",
+ blockType: "reporter",
+ text: "My IP address",
+ },
+ {
+ opcode: "returnListenerData",
+ blockType: "reporter",
+ text: "Response for listener [ID]",
+ arguments: {
+ ID: {
+ type: "string",
+ defaultValue: "example-listener",
+ },
+ },
+ },
+ {
+ opcode: "readQueueSize",
+ blockType: "reporter",
+ text: "Size of queue for [TYPE]",
+ arguments: {
+ TYPE: {
+ type: "string",
+ menu: "allmenu",
+ defaultValue: "All data",
+ },
+ },
+ },
+ {
+ opcode: "readQueueData",
+ blockType: "reporter",
+ text: "Packet queue for [TYPE]",
+ arguments: {
+ TYPE: {
+ type: "string",
+ menu: "allmenu",
+ defaultValue: "All data",
+ },
+ },
+ },
+ {
+ opcode: "returnVarData",
+ blockType: "reporter",
+ text: "[TYPE] [VAR] data",
+ arguments: {
+ VAR: {
+ type: "string",
+ defaultValue: "Apple",
+ },
+ TYPE: {
+ type: "string",
+ menu: "varmenu",
+ defaultValue: "Global variables",
+ },
+ },
+ },
+ {
+ opcode: "parseJSON",
+ blockType: "reporter",
+ text: "[PATH] of [JSON_STRING]",
+ arguments: {
+ PATH: {
+ type: "string",
+ defaultValue: "fruit/apples",
+ },
+ JSON_STRING: {
+ type: "string",
+ defaultValue:
+ '{"fruit": {"apples": 2, "bananas": 3}, "total_fruit": 5}',
+ },
+ },
+ },
+ {
+ opcode: "getFromJSONArray",
+ blockType: "reporter",
+ text: "Get [NUM] from JSON array [ARRAY]",
+ arguments: {
+ NUM: {
+ type: "number",
+ defaultValue: 0,
+ },
+ ARRAY: {
+ type: "string",
+ defaultValue: '["foo","bar"]',
+ },
+ },
+ },
+ {
+ opcode: "fetchURL",
+ blockType: "reporter",
+ blockAllThreads: "true",
+ text: "Fetch data from URL [url]",
+ arguments: {
+ url: {
+ type: "string",
+ defaultValue:
+ "https://mikedev101.github.io/cloudlink/fetch_test",
+ },
+ },
+ },
+ {
+ opcode: "requestURL",
+ blockType: "reporter",
+ blockAllThreads: "true",
+ text: "Send request with method [method] for URL [url] with data [data] and headers [headers]",
+ arguments: {
+ method: {
+ type: "string",
+ defaultValue: "GET",
+ },
+ url: {
+ type: "string",
+ defaultValue:
+ "https://mikedev101.github.io/cloudlink/fetch_test",
+ },
+ data: {
+ type: "string",
+ defaultValue: "{}",
+ },
+ headers: {
+ type: "string",
+ defaultValue: "{}",
+ },
+ },
+ },
+ {
+ opcode: "makeJSON",
+ blockType: "reporter",
+ text: "Convert [toBeJSONified] to JSON",
+ arguments: {
+ toBeJSONified: {
+ type: "string",
+ defaultValue: '{"test": true}',
+ },
+ },
+ },
+ {
+ opcode: "onConnect",
+ blockType: "hat",
+ text: "When connected",
+ blockAllThreads: "true",
+ },
+ {
+ opcode: "onClose",
+ blockType: "hat",
+ text: "When disconnected",
+ blockAllThreads: "true",
+ },
+ {
+ opcode: "onListener",
+ blockType: "hat",
+ text: "When I receive new packet with listener [ID]",
+ blockAllThreads: "true",
+ arguments: {
+ ID: {
+ type: "string",
+ defaultValue: "example-listener",
+ },
+ },
+ },
+ {
+ opcode: "onNewPacket",
+ blockType: "hat",
+ text: "When I receive new [TYPE] packet",
+ blockAllThreads: "true",
+ arguments: {
+ TYPE: {
+ type: "string",
+ menu: "almostallmenu",
+ defaultValue: "Global data",
+ },
+ },
+ },
+ {
+ opcode: "onNewVar",
+ blockType: "hat",
+ text: "When I receive new [TYPE] data for [VAR]",
+ blockAllThreads: "true",
+ arguments: {
+ TYPE: {
+ type: "string",
+ menu: "varmenu",
+ defaultValue: "Global variables",
+ },
+ VAR: {
+ type: "string",
+ defaultValue: "Apple",
+ },
+ },
+ },
+ {
+ opcode: "getComState",
+ blockType: "Boolean",
+ text: "Connected?",
+ },
+ {
+ opcode: "getRoomState",
+ blockType: "Boolean",
+ text: "Linked to rooms?",
+ },
+ {
+ opcode: "getComLostConnectionState",
+ blockType: "Boolean",
+ text: "Lost connection?",
+ },
+ {
+ opcode: "getComFailedConnectionState",
+ blockType: "Boolean",
+ text: "Failed to connnect?",
+ },
+ {
+ opcode: "getUsernameState",
+ blockType: "Boolean",
+ text: "Username synced?",
+ },
+ {
+ opcode: "returnIsNewData",
+ blockType: "Boolean",
+ text: "Got New [TYPE]?",
+ arguments: {
+ TYPE: {
+ type: "string",
+ menu: "datamenu",
+ defaultValue: "Global data",
+ },
+ },
+ },
+ {
+ opcode: "returnIsNewVarData",
+ blockType: "Boolean",
+ text: "Got New [TYPE] data for variable [VAR]?",
+ arguments: {
+ TYPE: {
+ type: "string",
+ menu: "varmenu",
+ defaultValue: "Global variables",
+ },
+ VAR: {
+ type: "string",
+ defaultValue: "Apple",
+ },
+ },
+ },
+ {
+ opcode: "returnIsNewListener",
+ blockType: "Boolean",
+ text: "Got new packet with listener [ID]?",
+ blockAllThreads: "true",
+ arguments: {
+ ID: {
+ type: "string",
+ defaultValue: "example-listener",
+ },
+ },
+ },
+ {
+ opcode: "checkForID",
+ blockType: "Boolean",
+ text: "ID [ID] connected?",
+ arguments: {
+ ID: {
+ type: "string",
+ defaultValue: "Another name",
+ },
+ },
+ },
+ {
+ opcode: "isValidJSON",
+ blockType: "Boolean",
+ text: "Is [JSON_STRING] valid JSON?",
+ arguments: {
+ JSON_STRING: {
+ type: "string",
+ defaultValue:
+ '{"fruit": {"apples": 2, "bananas": 3}, "total_fruit": 5}',
+ },
+ },
+ },
+ {
+ opcode: "openSocket",
+ blockType: "command",
+ text: "Connect to [IP]",
+ blockAllThreads: "true",
+ arguments: {
+ IP: {
+ type: "string",
+ defaultValue: "ws://127.0.0.1:3000/",
+ },
+ },
+ },
+ {
+ opcode: "openSocketPublicServers",
+ blockType: "command",
+ text: "Connect to server [ID]",
+ blockAllThreads: "true",
+ arguments: {
+ ID: {
+ type: "number",
+ defaultValue: "",
+ },
+ },
+ },
+ {
+ opcode: "closeSocket",
+ blockType: "command",
+ blockAllThreads: "true",
+ text: "Disconnect",
+ },
+ {
+ opcode: "setMyName",
+ blockType: "command",
+ text: "Set [NAME] as username",
+ blockAllThreads: "true",
+ arguments: {
+ NAME: {
+ type: "string",
+ defaultValue: "A name",
+ },
+ },
+ },
+ {
+ opcode: "createListener",
+ blockType: "command",
+ text: "Attach listener [ID] to next packet",
+ blockAllThreads: "true",
+ arguments: {
+ ID: {
+ type: "string",
+ defaultValue: "example-listener",
+ },
+ },
+ },
+ {
+ opcode: "linkToRooms",
+ blockType: "command",
+ text: "Link to room(s) [ROOMS]",
+ blockAllThreads: "true",
+ arguments: {
+ ROOMS: {
+ type: "string",
+ defaultValue: '["test"]',
+ },
+ },
+ },
+ {
+ opcode: "selectRoomsInNextPacket",
+ blockType: "command",
+ text: "Select room(s) [ROOMS] for next packet",
+ blockAllThreads: "true",
+ arguments: {
+ ROOMS: {
+ type: "string",
+ defaultValue: '["test"]',
+ },
+ },
+ },
+ {
+ opcode: "unlinkFromRooms",
+ blockType: "command",
+ text: "Unlink from all rooms",
+ blockAllThreads: "true",
+ },
+ {
+ opcode: "sendGData",
+ blockType: "command",
+ text: "Send [DATA]",
+ blockAllThreads: "true",
+ arguments: {
+ DATA: {
+ type: "string",
+ defaultValue: "Apple",
+ },
+ },
+ },
+ {
+ opcode: "sendPData",
+ blockType: "command",
+ text: "Send [DATA] to [ID]",
+ blockAllThreads: "true",
+ arguments: {
+ DATA: {
+ type: "string",
+ defaultValue: "Apple",
+ },
+ ID: {
+ type: "string",
+ defaultValue: "Another name",
+ },
+ },
+ },
+ {
+ opcode: "sendGDataAsVar",
+ blockType: "command",
+ text: "Send variable [VAR] with data [DATA]",
+ blockAllThreads: "true",
+ arguments: {
+ DATA: {
+ type: "string",
+ defaultValue: "Banana",
+ },
+ VAR: {
+ type: "string",
+ defaultValue: "Apple",
+ },
+ },
+ },
+ {
+ opcode: "sendPDataAsVar",
+ blockType: "command",
+ text: "Send variable [VAR] to [ID] with data [DATA]",
+ blockAllThreads: "true",
+ arguments: {
+ DATA: {
+ type: "string",
+ defaultValue: "Banana",
+ },
+ ID: {
+ type: "string",
+ defaultValue: "Another name",
+ },
+ VAR: {
+ type: "string",
+ defaultValue: "Apple",
+ },
+ },
+ },
+ {
+ opcode: "runCMDnoID",
+ blockType: "command",
+ text: "Send command without ID [CMD] [DATA]",
+ blockAllThreads: "true",
+ arguments: {
+ CMD: {
+ type: "string",
+ defaultValue: "direct",
+ },
+ DATA: {
+ type: "string",
+ defaultValue: "val",
+ },
+ },
+ },
+ {
+ opcode: "runCMD",
+ blockType: "command",
+ text: "Send command [CMD] [ID] [DATA]",
+ blockAllThreads: "true",
+ arguments: {
+ CMD: {
+ type: "string",
+ defaultValue: "direct",
+ },
+ ID: {
+ type: "string",
+ defaultValue: "id",
+ },
+ DATA: {
+ type: "string",
+ defaultValue: "val",
+ },
+ },
+ },
+ {
+ opcode: "resetNewData",
+ blockType: "command",
+ text: "Reset got new [TYPE] status",
+ blockAllThreads: "true",
+ arguments: {
+ TYPE: {
+ type: "string",
+ menu: "datamenu",
+ defaultValue: "Global data",
+ },
+ },
+ },
+ {
+ opcode: "resetNewVarData",
+ blockType: "command",
+ text: "Reset got new [TYPE] [VAR] status",
+ blockAllThreads: "true",
+ arguments: {
+ TYPE: {
+ type: "string",
+ menu: "varmenu",
+ defaultValue: "Global variables",
+ },
+ VAR: {
+ type: "string",
+ defaultValue: "Apple",
+ },
+ },
+ },
+ {
+ opcode: "resetNewListener",
+ blockType: "command",
+ text: "Reset got new [ID] listener status",
+ blockAllThreads: "true",
+ arguments: {
+ ID: {
+ type: "string",
+ defaultValue: "example-listener",
+ },
+ },
+ },
+ {
+ opcode: "clearAllPackets",
+ blockType: "command",
+ text: "Clear all packets for [TYPE]",
+ arguments: {
+ TYPE: {
+ type: "string",
+ menu: "allmenu",
+ defaultValue: "All data",
+ },
+ },
+ },
+ ],
+ menus: {
+ coms: {
+ items: ["Connected", "Username synced"],
+ },
+ datamenu: {
+ items: [
+ "Global data",
+ "Private data",
+ "Direct data",
+ "Status code",
+ ],
+ },
+ varmenu: {
+ items: ["Global variables", "Private variables"],
+ },
+ allmenu: {
+ items: [
+ "Global data",
+ "Private data",
+ "Direct data",
+ "Status code",
+ "Global variables",
+ "Private variables",
+ "All data",
+ ],
+ },
+ almostallmenu: {
+ items: [
+ "Global data",
+ "Private data",
+ "Direct data",
+ "Status code",
+ "Global variables",
+ "Private variables",
+ ],
+ },
+ },
+ };
+ }
+
+ // Code for blocks go here
+
+ returnGlobalData() {
+ if (this.socketData.gmsg.length != 0) {
+ let data = this.socketData.gmsg[this.socketData.gmsg.length - 1].val;
+
+ if (typeof data == "object") {
+ data = JSON.stringify(data); // Make the JSON safe for Scratch
+ }
+
+ return data;
+ } else {
+ return "";
+ }
+ }
+
+ returnPrivateData() {
+ if (this.socketData.pmsg.length != 0) {
+ let data = this.socketData.pmsg[this.socketData.pmsg.length - 1].val;
+
+ if (typeof data == "object") {
+ data = JSON.stringify(data); // Make the JSON safe for Scratch
+ }
+
+ return data;
+ } else {
+ return "";
+ }
+ }
+
+ returnDirectData() {
+ if (this.socketData.direct.length != 0) {
+ let data =
+ this.socketData.direct[this.socketData.direct.length - 1].val;
+
+ if (typeof data == "object") {
+ data = JSON.stringify(data); // Make the JSON safe for Scratch
+ }
+
+ return data;
+ } else {
+ return "";
+ }
+ }
+
+ returnLinkData() {
+ return String(this.link_status);
+ }
+
+ returnStatusCode() {
+ if (this.socketData.statuscode.length != 0) {
+ let data =
+ this.socketData.statuscode[this.socketData.statuscode.length - 1]
+ .code;
+
+ if (typeof data == "object") {
+ data = JSON.stringify(data); // Make the JSON safe for Scratch
+ }
+
+ return data;
+ } else {
+ return "";
+ }
+ }
+
+ returnUserListData() {
+ return JSON.stringify(this.socketData.ulist);
+ }
+
+ returnUsernameData() {
+ let data = this.username;
+
+ if (typeof data == "object") {
+ data = JSON.stringify(data); // Make the JSON safe for Scratch
+ }
+
+ return data;
+ }
+
+ returnVersionData() {
+ return String(this.version);
+ }
+
+ returnServerVersion() {
+ return String(this.socketData.server_version);
+ }
+
+ returnServerList() {
+ return JSON.stringify(servers);
+ }
+
+ returnMOTD() {
+ return String(this.socketData.motd);
+ }
+
+ returnClientIP() {
+ return String(this.socketData.client_ip);
+ }
+
+ returnListenerData({ ID }) {
+ const self = this;
+ if (this.isRunning && this.socketListeners.hasOwnProperty(String(ID))) {
+ return JSON.stringify(this.socketListenersData[ID]);
+ } else {
+ return "{}";
+ }
+ }
+
+ readQueueSize({ TYPE }) {
+ if (this.menuRemap[String(TYPE)] == "all") {
+ let tmp_size = 0;
+ tmp_size = tmp_size + this.socketData.gmsg.length;
+ tmp_size = tmp_size + this.socketData.pmsg.length;
+ tmp_size = tmp_size + this.socketData.direct.length;
+ tmp_size = tmp_size + this.socketData.statuscode.length;
+ tmp_size = tmp_size + this.socketData.gvar.length;
+ tmp_size = tmp_size + this.socketData.pvar.length;
+ return tmp_size;
+ } else {
+ return this.socketData[this.menuRemap[String(TYPE)]].length;
+ }
+ }
+
+ readQueueData({ TYPE }) {
+ if (this.menuRemap[String(TYPE)] == "all") {
+ let tmp_socketData = JSON.parse(JSON.stringify(this.socketData)); // Deep copy
+
+ delete tmp_socketData.motd;
+ delete tmp_socketData.client_ip;
+ delete tmp_socketData.ulist;
+ delete tmp_socketData.server_version;
+
+ return JSON.stringify(tmp_socketData);
+ } else {
+ return JSON.stringify(this.socketData[this.menuRemap[String(TYPE)]]);
+ }
+ }
+
+ returnVarData({ TYPE, VAR }) {
+ if (this.isRunning) {
+ if (this.varData.hasOwnProperty(this.menuRemap[TYPE])) {
+ if (this.varData[this.menuRemap[TYPE]].hasOwnProperty(VAR)) {
+ return this.varData[this.menuRemap[TYPE]][VAR].value;
+ } else {
+ return "";
+ }
+ } else {
+ return "";
+ }
+ } else {
+ return "";
+ }
+ }
+
+ parseJSON({ PATH, JSON_STRING }) {
+ try {
+ const path = PATH.toString()
+ .split("/")
+ .map((prop) => decodeURIComponent(prop));
+ if (path[0] === "") path.splice(0, 1);
+ if (path[path.length - 1] === "") path.splice(-1, 1);
+ let json;
+ try {
+ json = JSON.parse(" " + JSON_STRING);
+ } catch (e) {
+ return e.message;
+ }
+ path.forEach((prop) => (json = json[prop]));
+ if (json === null) return "null";
+ else if (json === undefined) return "";
+ else if (typeof json === "object") return JSON.stringify(json);
+ else return json.toString();
+ } catch (err) {
+ return "";
+ }
+ }
+
+ getFromJSONArray({ NUM, ARRAY }) {
+ var json_array = JSON.parse(ARRAY);
+ if (json_array[NUM] == "undefined") {
+ return "";
+ } else {
+ let data = json_array[NUM];
+
+ if (typeof data == "object") {
+ data = JSON.stringify(data); // Make the JSON safe for Scratch
+ }
+
+ return data;
+ }
+ }
+
+ fetchURL(args) {
+ return Scratch.fetch(args.url, {
+ method: "GET",
+ }).then((response) => response.text());
+ }
+
+ requestURL(args) {
+ if (args.method == "GET" || args.method == "HEAD") {
+ return Scratch.fetch(args.url, {
+ method: args.method,
+ headers: JSON.parse(args.headers),
+ }).then((response) => response.text());
+ } else {
+ return Scratch.fetch(args.url, {
+ method: args.method,
+ headers: JSON.parse(args.headers),
+ body: JSON.parse(args.data),
+ }).then((response) => response.text());
+ }
+ }
+
+ isValidJSON({ JSON_STRING }) {
+ return jsonCheck(JSON_STRING);
+ }
+
+ makeJSON({ toBeJSONified }) {
+ if (typeof toBeJSONified == "string") {
+ try {
+ JSON.parse(toBeJSONified);
+ return String(toBeJSONified);
+ } catch (err) {
+ return "Not JSON!";
+ }
+ } else if (typeof toBeJSONified == "object") {
+ return JSON.stringify(toBeJSONified);
+ } else {
+ return "Not JSON!";
+ }
+ }
+
+ onConnect() {
+ const self = this;
+ if (self.connect_hat == 0 && self.isRunning && self.protocolOk) {
+ self.connect_hat = 1;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ onClose() {
+ const self = this;
+ if (self.close_hat == 0 && !self.isRunning) {
+ self.close_hat = 1;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ onListener({ ID }) {
+ const self = this;
+ if (this.isRunning && this.socketListeners.hasOwnProperty(String(ID))) {
+ if (self.socketListeners[String(ID)]) {
+ self.socketListeners[String(ID)] = false;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ onNewPacket({ TYPE }) {
+ const self = this;
+ if (this.isRunning && this.newSocketData[this.menuRemap[String(TYPE)]]) {
+ self.newSocketData[this.menuRemap[String(TYPE)]] = false;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ onNewVar({ TYPE, VAR }) {
+ const self = this;
+ if (this.isRunning) {
+ if (this.varData.hasOwnProperty(this.menuRemap[TYPE])) {
+ if (this.varData[this.menuRemap[TYPE]].hasOwnProperty(VAR)) {
+ if (this.varData[this.menuRemap[TYPE]][VAR].isNew) {
+ self.varData[this.menuRemap[TYPE]][VAR].isNew = false;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ getComState() {
+ return String(this.link_status == 2 || this.protocolOk);
+ }
+
+ getRoomState() {
+ return this.isLinked;
+ }
+
+ getComLostConnectionState() {
+ return this.wasConnectionDropped;
+ }
+
+ getComFailedConnectionState() {
+ return this.didConnectionFail;
+ }
+
+ getUsernameState() {
+ return this.isUsernameSet;
+ }
+
+ returnIsNewData({ TYPE }) {
+ if (this.isRunning) {
+ return this.newSocketData[this.menuRemap[String(TYPE)]];
+ } else {
+ return false;
+ }
+ }
+
+ returnIsNewVarData({ TYPE, VAR }) {
+ if (this.isRunning) {
+ if (this.varData.hasOwnProperty(this.menuRemap[TYPE])) {
+ if (this.varData[this.menuRemap[TYPE]].hasOwnProperty(VAR)) {
+ return this.varData[this.menuRemap[TYPE]][VAR].isNew;
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ returnIsNewListener({ ID }) {
+ if (this.isRunning) {
+ if (this.socketListeners.hasOwnProperty(String(ID))) {
+ return this.socketListeners[ID];
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ checkForID({ ID }) {
+ return find_id(ID, this.socketData.ulist);
+ }
+
+ async openSocket({ IP }) {
+ const self = this;
+ if (!self.isRunning) {
+ if (!(await Scratch.canFetch(IP))) {
+ return;
+ }
+
+ console.log("Starting socket.");
+ self.link_status = 1;
+
+ self.disconnectWasClean = false;
+ self.wasConnectionDropped = false;
+ self.didConnectionFail = false;
+
+ mWS = new WebSocket(String(IP));
+
+ mWS.onerror = function () {
+ self.isRunning = false;
+ };
+
+ mWS.onopen = function () {
+ self.isRunning = true;
+ self.packet_queue = {};
+ self.link_status = 2;
+
+ // Send the handshake request to get server to detect client protocol
+ mWS.send(
+ JSON.stringify({ cmd: "handshake", listener: "setprotocol" })
+ );
+
+ console.log("Successfully opened socket.");
+ };
+
+ mWS.onmessage = function (event) {
+ let tmp_socketData = JSON.parse(event.data);
+ console.log("RX:", tmp_socketData);
+
+ if (self.queueableCmds.includes(tmp_socketData.cmd)) {
+ self.socketData[tmp_socketData.cmd].push(tmp_socketData);
+ } else {
+ if (tmp_socketData.cmd == "ulist") {
+ // ulist functionality has been changed in server 0.1.9
+ if (tmp_socketData.hasOwnProperty("mode")) {
+ if (tmp_socketData.mode == "set") {
+ self.socketData["ulist"] = tmp_socketData.val;
+ } else if (tmp_socketData.mode == "add") {
+ if (
+ !self.socketData.ulist.some(
+ (o) =>
+ o.username === tmp_socketData.val.username &&
+ o.id == tmp_socketData.val.id
+ )
+ ) {
+ self.socketData["ulist"].push(tmp_socketData.val);
+ } else {
+ console.log(
+ "Could not perform ulist method add, client",
+ tmp_socketData.val,
+ "already exists"
+ );
+ }
+ } else if (tmp_socketData.mode == "remove") {
+ if (
+ self.socketData.ulist.some(
+ (o) =>
+ o.username === tmp_socketData.val.username &&
+ o.id == tmp_socketData.val.id
+ )
+ ) {
+ // This is by far the fugliest thing I have ever written in JS, or in any programming language... thanks I hate it
+ self.socketData["ulist"] = self.socketData["ulist"].filter(
+ (user) =>
+ !(user.username === tmp_socketData.val.username) &&
+ !(user.id == tmp_socketData.val.id)
+ );
+ } else {
+ console.log(
+ "Could not perform ulist method remove, client",
+ tmp_socketData.val,
+ "was not found"
+ );
+ }
+ } else {
+ console.log(
+ "Could not understand ulist method:",
+ tmp_socketData.mode
+ );
+ }
+ } else {
+ // Retain compatibility wtih existing servers
+ self.socketData["ulist"] = tmp_socketData.val;
+ }
+ } else {
+ self.socketData[tmp_socketData.cmd] = tmp_socketData.val;
+ }
+ }
+
+ if (self.newSocketData.hasOwnProperty(tmp_socketData.cmd)) {
+ self.newSocketData[tmp_socketData.cmd] = true;
+ }
+
+ if (self.varCmds.includes(tmp_socketData.cmd)) {
+ self.varData[tmp_socketData.cmd][tmp_socketData.name] = {
+ value: tmp_socketData.val,
+ isNew: true,
+ };
+ }
+ if (tmp_socketData.hasOwnProperty("listener")) {
+ if (tmp_socketData.listener == "setusername") {
+ self.socketListeners["setusername"] = true;
+ if (tmp_socketData.code == "I:100 | OK") {
+ self.username = tmp_socketData.val;
+ self.isUsernameSyncing = false;
+ self.isUsernameSet = true;
+ console.log(
+ "Username was accepted by the server, and has been set to:",
+ self.username
+ );
+ } else {
+ console.warn(
+ "Username was rejected by the server. Error code:",
+ String(tmp_socketData.code)
+ );
+ self.isUsernameSyncing = false;
+ }
+ } else if (tmp_socketData.listener == "roomLink") {
+ self.isRoomSetting = false;
+ self.socketListeners["roomLink"] = true;
+ if (tmp_socketData.code == "I:100 | OK") {
+ console.log("Linking to room(s) was accepted by the server!");
+ self.isLinked = true;
+ } else {
+ console.warn(
+ "Linking to room(s) was rejected by the server. Error code:",
+ String(tmp_socketData.code)
+ );
+ self.enableRoom = false;
+ self.isLinked = false;
+ self.selectRoom = "";
+ }
+ } else if (
+ tmp_socketData.listener == "setprotocol" &&
+ !this.protocolOk
+ ) {
+ console.log(
+ "Server successfully set client protocol to cloudlink!"
+ );
+ self.socketData.statuscode = [];
+ self.protocolOk = true;
+ self.socketListeners["setprotocol"] = true;
+ } else {
+ if (
+ self.socketListeners.hasOwnProperty(tmp_socketData.listener)
+ ) {
+ self.socketListeners[tmp_socketData.listener] = true;
+ }
+ }
+ self.socketListenersData[tmp_socketData.listener] = tmp_socketData;
+ }
+ self.packet_hat = 0;
+ };
+
+ mWS.onclose = function () {
+ self.isRunning = false;
+ self.connect_hat = 0;
+ self.packet_hat = 0;
+ self.protocolOk = false;
+ if (self.close_hat == 1) {
+ self.close_hat = 0;
+ }
+ self.socketData = {
+ gmsg: [],
+ pmsg: [],
+ direct: [],
+ statuscode: [],
+ gvar: [],
+ pvar: [],
+ motd: "",
+ client_ip: "",
+ ulist: [],
+ server_version: "",
+ };
+ self.newSocketData = {
+ gmsg: false,
+ pmsg: false,
+ direct: false,
+ statuscode: false,
+ gvar: false,
+ pvar: false,
+ };
+ self.socketListeners = {};
+ self.username = "";
+ self.tmp_username = "";
+ self.isUsernameSyncing = false;
+ self.isUsernameSet = false;
+ self.enableListener = false;
+ self.setListener = "";
+ self.enableRoom = false;
+ self.selectRoom = "";
+ self.isLinked = false;
+ self.isRoomSetting = false;
+
+ if (self.link_status != 1) {
+ if (self.disconnectWasClean) {
+ self.link_status = 3;
+ console.log("Socket closed.");
+ self.wasConnectionDropped = false;
+ self.didConnectionFail = false;
+ } else {
+ self.link_status = 4;
+ console.error("Lost connection to the server.");
+ self.wasConnectionDropped = true;
+ self.didConnectionFail = false;
+ }
+ } else {
+ self.link_status = 4;
+ console.error("Failed to connect to server.");
+ self.wasConnectionDropped = false;
+ self.didConnectionFail = true;
+ }
+ };
+ } else {
+ console.warn("Socket is already open.");
+ }
+ }
+
+ openSocketPublicServers({ ID }) {
+ if (servers.hasOwnProperty(ID)) {
+ console.log("Connecting to:", servers[ID].url);
+ this.openSocket({ IP: servers[ID].url });
+ }
+ }
+
+ closeSocket() {
+ const self = this;
+ if (this.isRunning) {
+ console.log("Closing socket...");
+ mWS.close(1000, "script closure");
+ self.disconnectWasClean = true;
+ } else {
+ console.warn("Socket is not open.");
+ }
+ }
+
+ setMyName({ NAME }) {
+ const self = this;
+ if (this.isRunning) {
+ if (!this.isUsernameSyncing) {
+ if (!this.isUsernameSet) {
+ if (String(NAME) != "") {
+ if (!(String(NAME).length > 20)) {
+ if (
+ !(
+ String(NAME) == "%CA%" ||
+ String(NAME) == "%CC%" ||
+ String(NAME) == "%CD%" ||
+ String(NAME) == "%MS%"
+ )
+ ) {
+ let tmp_msg = {
+ cmd: "setid",
+ val: String(NAME),
+ listener: "setusername",
+ };
+
+ console.log("TX:", tmp_msg);
+ mWS.send(JSON.stringify(tmp_msg));
+
+ self.tmp_username = String(NAME);
+ self.isUsernameSyncing = true;
+ } else {
+ console.log("Blocking attempt to use reserved usernames");
+ }
+ } else {
+ console.log(
+ "Blocking attempt to use username larger than 20 characters, username is " +
+ String(NAME).length +
+ " characters long"
+ );
+ }
+ } else {
+ console.log("Blocking attempt to use blank username");
+ }
+ } else {
+ console.warn("Username already has been set!");
+ }
+ } else {
+ console.warn("Username is still syncing!");
+ }
+ }
+ }
+
+ createListener({ ID }) {
+ self = this;
+ if (this.isRunning) {
+ if (!this.enableListener) {
+ self.enableListener = true;
+ self.setListener = String(ID);
+ } else {
+ console.warn("Listeners were already created!");
+ }
+ } else {
+ console.log("Cannot assign a listener to a packet while disconnected");
+ }
+ }
+
+ linkToRooms({ ROOMS }) {
+ const self = this;
+
+ if (this.isRunning) {
+ if (!this.isRoomSetting) {
+ if (!(String(ROOMS).length > 1000)) {
+ let tmp_msg = {
+ cmd: "link",
+ val: autoConvert(ROOMS),
+ listener: "roomLink",
+ };
+
+ console.log("TX:", tmp_msg);
+ mWS.send(JSON.stringify(tmp_msg));
+
+ self.isRoomSetting = true;
+ } else {
+ console.warn(
+ "Blocking attempt to send a room ID / room list larger than 1000 bytes (1 KB), room ID / room list is " +
+ String(ROOMS).length +
+ " bytes"
+ );
+ }
+ } else {
+ console.warn("Still linking to rooms!");
+ }
+ } else {
+ console.warn("Socket is not open.");
+ }
+ }
+
+ selectRoomsInNextPacket({ ROOMS }) {
+ const self = this;
+ if (this.isRunning) {
+ if (this.isLinked) {
+ if (!this.enableRoom) {
+ if (!(String(ROOMS).length > 1000)) {
+ self.enableRoom = true;
+ self.selectRoom = ROOMS;
+ } else {
+ console.warn(
+ "Blocking attempt to select a room ID / room list larger than 1000 bytes (1 KB), room ID / room list is " +
+ String(ROOMS).length +
+ " bytes"
+ );
+ }
+ } else {
+ console.warn("Rooms were already selected!");
+ }
+ } else {
+ console.warn("Not linked to any room(s)!");
+ }
+ } else {
+ console.warn("Socket is not open.");
+ }
+ }
+
+ unlinkFromRooms() {
+ const self = this;
+ if (this.isRunning) {
+ if (this.isLinked) {
+ let tmp_msg = {
+ cmd: "unlink",
+ val: "",
+ };
+
+ if (this.enableListener) {
+ tmp_msg["listener"] = autoConvert(this.setListener);
+ }
+
+ console.log("TX:", tmp_msg);
+ mWS.send(JSON.stringify(tmp_msg));
+
+ if (this.enableListener) {
+ if (!self.socketListeners.hasOwnProperty(this.setListener)) {
+ self.socketListeners[this.setListener] = false;
+ }
+ self.enableListener = false;
+ }
+
+ self.isLinked = false;
+ } else {
+ console.warn("Not linked to any rooms!");
+ }
+ } else {
+ console.warn("Socket is not open.");
+ }
+ }
+
+ sendGData({ DATA }) {
+ const self = this;
+ if (this.isRunning) {
+ if (!(String(DATA).length > 1000)) {
+ let tmp_msg = {
+ cmd: "gmsg",
+ val: autoConvert(DATA),
+ };
+
+ if (this.enableListener) {
+ tmp_msg["listener"] = String(this.setListener);
+ }
+
+ if (this.enableRoom) {
+ tmp_msg["rooms"] = autoConvert(this.selectRoom);
+ }
+
+ console.log("TX:", tmp_msg);
+ mWS.send(JSON.stringify(tmp_msg));
+
+ if (this.enableListener) {
+ if (!self.socketListeners.hasOwnProperty(this.setListener)) {
+ self.socketListeners[this.setListener] = false;
+ }
+ self.enableListener = false;
+ }
+ if (this.enableRoom) {
+ self.enableRoom = false;
+ self.selectRoom = "";
+ }
+ } else {
+ console.warn(
+ "Blocking attempt to send packet larger than 1000 bytes (1 KB), packet is " +
+ String(DATA).length +
+ " bytes"
+ );
+ }
+ } else {
+ console.warn("Socket is not open.");
+ }
+ }
+
+ sendPData({ DATA, ID }) {
+ const self = this;
+ if (this.isRunning) {
+ if (!(String(DATA).length > 1000)) {
+ let tmp_msg = {
+ cmd: "pmsg",
+ val: autoConvert(DATA),
+ id: autoConvert(ID),
+ };
+
+ if (this.enableListener) {
+ tmp_msg["listener"] = String(this.setListener);
+ }
+ if (this.enableRoom) {
+ tmp_msg["rooms"] = autoConvert(this.selectRoom);
+ }
+
+ console.log("TX:", tmp_msg);
+ mWS.send(JSON.stringify(tmp_msg));
+
+ if (this.enableListener) {
+ if (!self.socketListeners.hasOwnProperty(this.setListener)) {
+ self.socketListeners[this.setListener] = false;
+ }
+ self.enableListener = false;
+ }
+ if (this.enableRoom) {
+ self.enableRoom = false;
+ self.selectRoom = "";
+ }
+ } else {
+ console.warn(
+ "Blocking attempt to send packet larger than 1000 bytes (1 KB), packet is " +
+ String(DATA).length +
+ " bytes"
+ );
+ }
+ } else {
+ console.warn("Socket is not open.");
+ }
+ }
+
+ sendGDataAsVar({ VAR, DATA }) {
+ const self = this;
+ if (this.isRunning) {
+ if (!(String(DATA).length > 1000)) {
+ let tmp_msg = {
+ cmd: "gvar",
+ name: VAR,
+ val: autoConvert(DATA),
+ };
+
+ if (this.enableListener) {
+ tmp_msg["listener"] = String(this.setListener);
+ }
+ if (this.enableRoom) {
+ tmp_msg["rooms"] = autoConvert(this.selectRoom);
+ }
+
+ console.log("TX:", tmp_msg);
+ mWS.send(JSON.stringify(tmp_msg));
+
+ if (this.enableListener) {
+ if (!self.socketListeners.hasOwnProperty(this.setListener)) {
+ self.socketListeners[this.setListener] = false;
+ }
+ self.enableListener = false;
+ }
+ if (this.enableRoom) {
+ self.enableRoom = false;
+ self.selectRoom = "";
+ }
+ } else {
+ console.warn(
+ "Blocking attempt to send packet larger than 1000 bytes (1 KB), packet is " +
+ String(DATA).length +
+ " bytes"
+ );
+ }
+ } else {
+ console.warn("Socket is not open.");
+ }
+ }
+
+ sendPDataAsVar({ VAR, ID, DATA }) {
+ const self = this;
+ if (this.isRunning) {
+ if (!(String(DATA).length > 1000)) {
+ let tmp_msg = {
+ cmd: "pvar",
+ name: VAR,
+ val: autoConvert(DATA),
+ id: autoConvert(ID),
+ };
+
+ if (this.enableListener) {
+ tmp_msg["listener"] = String(this.setListener);
+ }
+ if (this.enableRoom) {
+ tmp_msg["rooms"] = autoConvert(this.selectRoom);
+ }
+
+ console.log("TX:", tmp_msg);
+ mWS.send(JSON.stringify(tmp_msg));
+
+ if (this.enableListener) {
+ if (!self.socketListeners.hasOwnProperty(this.setListener)) {
+ self.socketListeners[this.setListener] = false;
+ }
+ self.enableListener = false;
+ }
+ if (this.enableRoom) {
+ self.enableRoom = false;
+ self.selectRoom = "";
+ }
+ } else {
+ console.warn(
+ "Blocking attempt to send packet larger than 1000 bytes (1 KB), packet is " +
+ String(DATA).length +
+ " bytes"
+ );
+ }
+ } else {
+ console.warn("Socket is not open.");
+ }
+ }
+
+ runCMDnoID({ CMD, DATA }) {
+ const self = this;
+ if (this.isRunning) {
+ if (!(String(CMD).length > 100) || !(String(DATA).length > 1000)) {
+ let tmp_msg = {
+ cmd: String(CMD),
+ val: autoConvert(DATA),
+ };
+
+ if (this.enableListener) {
+ tmp_msg["listener"] = String(this.setListener);
+ }
+ if (this.enableRoom) {
+ tmp_msg["rooms"] = String(this.selectRoom);
+ }
+
+ console.log("TX:", tmp_msg);
+ mWS.send(JSON.stringify(tmp_msg));
+
+ if (this.enableListener) {
+ if (!self.socketListeners.hasOwnProperty(this.setListener)) {
+ self.socketListeners[this.setListener] = false;
+ }
+ self.enableListener = false;
+ }
+ if (this.enableRoom) {
+ self.enableRoom = false;
+ self.selectRoom = "";
+ }
+ } else {
+ console.warn(
+ "Blocking attempt to send packet with questionably long arguments"
+ );
+ }
+ } else {
+ console.warn("Socket is not open.");
+ }
+ }
+
+ runCMD({ CMD, ID, DATA }) {
+ const self = this;
+ if (this.isRunning) {
+ if (
+ !(String(CMD).length > 100) ||
+ !(String(ID).length > 20) ||
+ !(String(DATA).length > 1000)
+ ) {
+ let tmp_msg = {
+ cmd: String(CMD),
+ id: autoConvert(ID),
+ val: autoConvert(DATA),
+ };
+
+ if (this.enableListener) {
+ tmp_msg["listener"] = String(this.setListener);
+ }
+ if (this.enableRoom) {
+ tmp_msg["rooms"] = String(this.selectRoom);
+ }
+
+ console.log("TX:", tmp_msg);
+ mWS.send(JSON.stringify(tmp_msg));
+
+ if (this.enableListener) {
+ if (!self.socketListeners.hasOwnProperty(this.setListener)) {
+ self.socketListeners[this.setListener] = false;
+ }
+ self.enableListener = false;
+ }
+ if (this.enableRoom) {
+ self.enableRoom = false;
+ self.selectRoom = "";
+ }
+ } else {
+ console.warn(
+ "Blocking attempt to send packet with questionably long arguments"
+ );
+ }
+ } else {
+ console.warn("Socket is not open.");
+ }
+ }
+
+ resetNewData({ TYPE }) {
+ const self = this;
+ if (this.isRunning) {
+ self.newSocketData[this.menuRemap[String(TYPE)]] = false;
+ }
+ }
+
+ resetNewVarData({ TYPE, VAR }) {
+ const self = this;
+ if (this.isRunning) {
+ if (this.varData.hasOwnProperty(this.menuRemap[TYPE])) {
+ if (this.varData[this.menuRemap[TYPE]].hasOwnProperty(VAR)) {
+ self.varData[this.menuRemap[TYPE]][VAR].isNew = false;
+ }
+ }
+ }
+ }
+
+ resetNewListener({ ID }) {
+ const self = this;
+ if (this.isRunning) {
+ if (this.socketListeners.hasOwnProperty(String(ID))) {
+ self.socketListeners[String(ID)] = false;
+ }
+ }
+ }
+
+ clearAllPackets({ TYPE }) {
+ const self = this;
+ if (this.menuRemap[String(TYPE)] == "all") {
+ self.socketData.gmsg = [];
+ self.socketData.pmsg = [];
+ self.socketData.direct = [];
+ self.socketData.statuscode = [];
+ self.socketData.gvar = [];
+ self.socketData.pvar = [];
+ } else {
+ self.socketData[this.menuRemap[String(TYPE)]] = [];
+ }
+ }
+ }
+
+ console.log("CloudLink 4.0 loaded. Detecting unsandboxed mode.");
+ Scratch.extensions.register(new CloudLink(Scratch.vm.runtime));
})(Scratch);
-
diff --git a/extensions/codeGIO/ExtraUtilities.js b/extensions/codeGIO/ExtraUtilities.js
index 3c7414dde4..a88fee3591 100644
--- a/extensions/codeGIO/ExtraUtilities.js
+++ b/extensions/codeGIO/ExtraUtilities.js
@@ -1,7 +1,7 @@
-(function(Scratch) {
+(function (Scratch) {
"use strict";
class codegioExtension {
- getInfo () {
+ getInfo() {
return {
id: "utilitiesCodegio",
name: "Utilities",
@@ -11,7 +11,7 @@
{
opcode: "newline",
blockType: Scratch.BlockType.REPORTER,
- text: "New Line"
+ text: "New Line",
},
{
@@ -21,25 +21,25 @@
arguments: {
one: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ""
+ defaultValue: "",
},
two: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ""
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
opcode: "returntrue",
blockType: Scratch.BlockType.BOOLEAN,
- text: "true"
+ text: "true",
},
{
opcode: "returnfalse",
blockType: Scratch.BlockType.BOOLEAN,
- text: "false"
+ text: "false",
},
{
@@ -49,13 +49,13 @@
arguments: {
one: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: ""
+ defaultValue: "",
},
two: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: ""
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
@@ -65,33 +65,33 @@
arguments: {
color: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: "#96ccff"
- }
- }
+ defaultValue: "#96ccff",
+ },
+ },
},
{
opcode: "monitor_width",
blockType: Scratch.BlockType.REPORTER,
- text: "Screen | Width"
+ text: "Screen | Width",
},
{
opcode: "monitor_height",
blockType: Scratch.BlockType.REPORTER,
- text: "Screen | Height"
+ text: "Screen | Height",
},
{
opcode: "window_width",
blockType: Scratch.BlockType.REPORTER,
- text: "Window | Width"
+ text: "Window | Width",
},
{
opcode: "window_height",
blockType: Scratch.BlockType.REPORTER,
- text: "Window | Height"
+ text: "Window | Height",
},
{
@@ -101,9 +101,9 @@
arguments: {
one: {
type: Scratch.ArgumentType.STRING,
- defaultValue: "Alert..."
- }
- }
+ defaultValue: "Alert...",
+ },
+ },
},
{
@@ -113,9 +113,9 @@
arguments: {
one: {
type: Scratch.ArgumentType.STRING,
- defaultValue: "Confirm..."
- }
- }
+ defaultValue: "Confirm...",
+ },
+ },
},
{
@@ -125,13 +125,13 @@
arguments: {
one: {
type: Scratch.ArgumentType.STRING,
- defaultValue: "Enter Username:"
+ defaultValue: "Enter Username:",
},
two: {
type: Scratch.ArgumentType.STRING,
- defaultValue: "griffpatch"
+ defaultValue: "griffpatch",
},
- }
+ },
},
{
@@ -141,9 +141,9 @@
arguments: {
one: {
type: Scratch.ArgumentType.STRING,
- defaultValue: "https://turbowarp.org/"
- }
- }
+ defaultValue: "https://turbowarp.org/",
+ },
+ },
},
{
@@ -153,21 +153,21 @@
arguments: {
one: {
type: Scratch.ArgumentType.STRING,
- defaultValue: "https://turbowarp.org/"
- }
- }
+ defaultValue: "https://turbowarp.org/",
+ },
+ },
},
{
opcode: "get_current_url",
blockType: Scratch.BlockType.REPORTER,
- text: "Current URL"
+ text: "Current URL",
},
{
opcode: "get_current_url_hash",
blockType: Scratch.BlockType.REPORTER,
- text: "Current URL hash (#)"
+ text: "Current URL hash (#)",
},
{
@@ -177,27 +177,27 @@
arguments: {
one: {
type: Scratch.ArgumentType.STRING,
- defaultValue: ""
- }
- }
+ defaultValue: "",
+ },
+ },
},
{
opcode: "get_clipboard",
blockType: Scratch.BlockType.REPORTER,
- text: "Clipboard"
+ text: "Clipboard",
},
{
opcode: "get_browser",
blockType: Scratch.BlockType.REPORTER,
- text: "Browser"
+ text: "Browser",
},
{
opcode: "get_os",
blockType: Scratch.BlockType.REPORTER,
- text: "Operating System"
+ text: "Operating System",
},
{
@@ -212,7 +212,7 @@
font: {
type: Scratch.ArgumentType.STRING,
defaultValue: "Monospace",
- menu: "consoleFonts"
+ menu: "consoleFonts",
},
size: {
type: Scratch.ArgumentType.NUMBER,
@@ -222,25 +222,25 @@
type: Scratch.ArgumentType.COLOR,
defaultValue: "#000000",
},
- }
+ },
},
{
opcode: "consoleClear",
blockType: Scratch.BlockType.COMMAND,
- text: "Console | Clear"
+ text: "Console | Clear",
},
],
menus: {
consoleFonts: {
acceptReporters: true,
items: [
- {text: "Serif (default)", value: "serif"},
- {text: "Monospace", value: "monospace"},
- {text: "Sans-serif", value: "sans-serif"}
- ]
- }
- }
+ { text: "Serif (default)", value: "serif" },
+ { text: "Monospace", value: "monospace" },
+ { text: "Sans-serif", value: "sans-serif" },
+ ],
+ },
+ },
};
}
@@ -257,7 +257,7 @@
}
strict_equality(args) {
- return (args.one == args.two);
+ return args.one == args.two;
}
exponent(args) {
@@ -333,28 +333,28 @@
get_clipboard() {
if (navigator.clipboard && navigator.clipboard.readText) {
- return Scratch.canReadClipboard().then(allowed => {
+ return Scratch.canReadClipboard().then((allowed) => {
if (allowed) {
return navigator.clipboard.readText();
}
- return '';
+ return "";
});
}
- return '';
+ return "";
}
get_browser() {
let userAgent = navigator.userAgent;
- if (userAgent.match(/chrome|chromium|crios/i)){
+ if (userAgent.match(/chrome|chromium|crios/i)) {
return "Chrome";
- } else if (userAgent.match(/firefox|fxios/i)){
+ } else if (userAgent.match(/firefox|fxios/i)) {
return "Firefox";
- } else if (userAgent.match(/safari/i)){
+ } else if (userAgent.match(/safari/i)) {
return "Safari";
- } else if (userAgent.match(/opr\//i)){
+ } else if (userAgent.match(/opr\//i)) {
return "Opera";
- } else if (userAgent.match(/edg/i)){
+ } else if (userAgent.match(/edg/i)) {
return "Edge";
} else {
return "No browser detection";
@@ -366,7 +366,10 @@
}
consoleLog(args) {
- console.log(`%c${args.input}`, `color:${args.color}; font-family:${args.font}; font-size: ${args.size}px;`);
+ console.log(
+ `%c${args.input}`,
+ `color:${args.color}; font-family:${args.font}; font-size: ${args.size}px;`
+ );
}
consoleClear() {
diff --git a/extensions/cs2627883/numericalencoding.js b/extensions/cs2627883/numericalencoding.js
index c3204b2bb8..5212283722 100644
--- a/extensions/cs2627883/numericalencoding.js
+++ b/extensions/cs2627883/numericalencoding.js
@@ -1,52 +1,54 @@
// Name: Numerical Encoding
+// ID: cs2627883NumericalEncoding
// Description: Encode strings as numbers for cloud variables.
// By: cs2627883
// https://github.com/CS2627883/Turbowarp-Encoding-Extension/blob/main/Encoding.js
-(function(Scratch) {
- 'use strict';
+(function (Scratch) {
+ "use strict";
class NumericalEncodingExtension {
maxcharlength = 6; // There are 149,186 unicode characters, so the maximum character code length is 6
encoded = 0;
decoded = 0;
getInfo() {
return {
- id: 'cs2627883NumericalEncoding',
- name: 'Numerical Encoding',
- blocks: [{
- opcode: 'NumericalEncode',
- blockType: Scratch.BlockType.COMMAND,
- text: 'Encode [DATA] to numbers',
- arguments: {
- DATA: {
- type: Scratch.ArgumentType.STRING,
- defaultValue: 'Hello!'
- }
- }
- },
+ id: "cs2627883NumericalEncoding",
+ name: "Numerical Encoding",
+ blocks: [
{
- opcode: 'NumericalDecode',
+ opcode: "NumericalEncode",
blockType: Scratch.BlockType.COMMAND,
- text: 'Decode [ENCODED] back to text',
+ text: "Encode [DATA] to numbers",
+ arguments: {
+ DATA: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Hello!",
+ },
+ },
+ },
+ {
+ opcode: "NumericalDecode",
+ blockType: Scratch.BlockType.COMMAND,
+ text: "Decode [ENCODED] back to text",
arguments: {
ENCODED: {
type: Scratch.ArgumentType.STRING,
- defaultValue: '000072000101000108000108000111000033' //Encoded "Hello!"
- }
- }
+ defaultValue: "000072000101000108000108000111000033", //Encoded "Hello!"
+ },
+ },
},
{
- opcode: 'GetNumericalEncoded',
+ opcode: "GetNumericalEncoded",
blockType: Scratch.BlockType.REPORTER,
- text: 'encoded',
+ text: "encoded",
},
{
- opcode: 'GetNumericalDecoded',
+ opcode: "GetNumericalDecoded",
blockType: Scratch.BlockType.REPORTER,
- text: 'decoded',
- }
- ]
+ text: "decoded",
+ },
+ ],
};
}
NumericalEncode(args) {
@@ -56,7 +58,8 @@
// Get char code of character
var encodedchar = String(toencode.charCodeAt(i));
// Pad encodedchar with 0s to ensure all encodedchars are the same length
- encodedchar = "0".repeat(this.maxcharlength - encodedchar.length) + encodedchar;
+ encodedchar =
+ "0".repeat(this.maxcharlength - encodedchar.length) + encodedchar;
encoded += encodedchar;
}
this.encoded = encoded;
@@ -69,7 +72,7 @@
}
var decoded = "";
// Create regex to split by char length
- const regex = new RegExp('.{1,' + this.maxcharlength + '}', 'g');
+ const regex = new RegExp(".{1," + this.maxcharlength + "}", "g");
// Split into array of characters
var encodedchars = todecode.match(regex);
for (let i = 0; i < encodedchars.length; i++) {
diff --git a/extensions/cursor.js b/extensions/cursor.js
index 9c011998b3..3fdf7c47ca 100644
--- a/extensions/cursor.js
+++ b/extensions/cursor.js
@@ -1,289 +1,323 @@
-// Name: Mouse Cursor
-// Description: Use custom cursors or hide the cursor. Also allows replacing the cursor with any costume image.
-
-(function (Scratch) {
- 'use strict';
-
- if (!Scratch.extensions.unsandboxed) {
- throw new Error('MouseCursor extension must be run unsandboxed');
- }
-
- const lazilyCreatedCanvas = () => {
- /** @type {HTMLCanvasElement} */
- let canvas = null;
- /** @type {CanvasRenderingContext2D} */
- let ctx = null;
- /**
- * @param {number} width
- * @param {number} height
- * @returns {[HTMLCanvasElement, CanvasRenderingContext2D]}
- */
- return (width, height) => {
- if (!canvas) {
- canvas = document.createElement('canvas');
- ctx = canvas.getContext('2d');
- if (!ctx) {
- throw new Error('Could not get 2d rendering context');
- }
- }
- // Setting canvas size also clears it
- canvas.width = width;
- canvas.height = height;
- return [canvas, ctx];
- };
- };
- const getRawSkinCanvas = lazilyCreatedCanvas();
-
- /**
- * @param {RenderWebGL.Skin} skin
- * @returns {string} A data: URI for the skin.
- */
- const encodeSkinToURL = (skin) => {
- const svgSkin = /** @type {RenderWebGL.SVGSkin} */ (skin);
- if (svgSkin._svgImage) {
- // This is an SVG skin
- return svgSkin._svgImage.src;
- }
-
- // It's probably a bitmap skin.
- // The most reliable way to get the bitmap in every runtime is through the silhouette.
- // This is very slow and could involve reading the texture from the GPU.
- const silhouette = skin._silhouette;
- // unlazy() only exists in TW
- if (silhouette.unlazy) {
- silhouette.unlazy();
- }
- const colorData = silhouette._colorData;
- const width = silhouette._width;
- const height = silhouette._height;
- const imageData = new ImageData(colorData, silhouette._width, silhouette._height);
- const [canvas, ctx] = getRawSkinCanvas(width, height);
- ctx.putImageData(imageData, 0, 0);
- return canvas.toDataURL();
- };
-
- /**
- * @param {VM.Costume} costume
- * @param {number} maxWidth
- * @param {number} maxHeight
- * @returns {{uri: string, width: number, height: number}}
- */
- const costumeToCursor = (costume, maxWidth, maxHeight) => {
- const skin = Scratch.vm.renderer._allSkins[costume.skinId];
- const imageURI = encodeSkinToURL(skin);
-
- let width = skin.size[0];
- let height = skin.size[1];
- if (width > maxWidth) {
- height = height * (maxWidth / width);
- width = maxWidth;
- }
- if (height > maxHeight) {
- width = width * (maxHeight / height);
- height = maxHeight;
- }
- width = Math.round(width);
- height = Math.round(height);
-
- // We wrap the encoded image in an