diff --git a/.eslintrc.js b/.eslintrc.js index 7e9fd14fee..db32ceab95 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -17,24 +17,6 @@ module.exports = { scaffolding: 'readonly' }, rules: { - // Here is where we enforce rules to have somewhat consistent code style without being overbearing - 'semi': [ - 'warn', - 'always' - ], - 'brace-style': 'warn', - 'key-spacing': 'warn', - 'keyword-spacing': 'warn', - 'new-parens': 'warn', - 'no-trailing-spaces': [ - 'warn', - { - ignoreComments: true - } - ], - 'space-infix-ops': 'warn', - 'no-tabs': 'warn', - 'no-unused-vars': 'off', // Allow while (true) { } 'no-constant-condition': [ diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index bd20acc560..2db07a1930 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -4,7 +4,7 @@ on: pull_request: jobs: - build: + validate: runs-on: ubuntu-latest steps: - name: Checkout @@ -12,10 +12,39 @@ jobs: - name: Install Node.js uses: actions/setup-node@v3 with: - node-version: 16 + node-version: '14.x' + cache: 'npm' - name: Install dependencies run: npm ci - name: Validate run: npm run validate - - name: Lint + + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Node.js + uses: actions/setup-node@v3 + with: + node-version: '14.x' + cache: 'npm' + - name: Install dependencies + run: npm ci + - name: Validate run: npm run lint + + format: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Node.js + uses: actions/setup-node@v3 + with: + node-version: '14.x' + cache: 'npm' + - name: Install dependencies + run: npm ci + - name: Validate + run: npm run check-format diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..0a39a3b587 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,9 @@ +build +images +licenses +website +.eslintrc.js +*.json +*.yml +*.md +extensions/docs-examples diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000000..37cdf9afd4 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,4 @@ +{ + "trailingComma": "es5", + "endOfLine": "auto" +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9af7a91309..16779c139d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,6 +29,8 @@ Every merged extension is more code that we will be expected to maintain indefin We're all volunteers who all have lives outside of Scratch extensions. Many have full time jobs or are full time students. We'll get to you as soon as we can, so please be patient. +Every extension is also covered under [our bug bounty](https://github.com/TurboWarp/extensions/security/policy), so mindlessly merging things will have a direct impact on my wallet. + ## Writing extensions Extension source code goes in the [`extensions`](extensions) folder. For example, an extension placed at `extensions/hello-world.js` would be accessible at [http://localhost:8000/hello-world.js](http://localhost:8000/hello-world.js) using our development server. @@ -45,12 +47,13 @@ The header comments look like this: ```js // Name: Example Extension +// ID: extensionid // Description: Does a very cool thing. This must have punctuation at the end! // By: GarboMuffin // Original: TestMuffin ``` -Remember, this has to be the *very first* thing in the JS file. `Name` and `Description` are required. You can have zero or more `By` and `Original`. Put credit links in `` if you have one. It must point to a Scratch user profile. The parser is pretty loose, but try not to deviate too far from this format. +Remember, this has to be the *very first* thing in the JS file. `Name`, `Description`, and `ID` are required. Make sure that `ID` exactly matches what you return in `getInfo()`. You can have zero or more `By` and `Original`. Put credit links in `` if you have one. It must point to a Scratch user profile. This metadata is parsed by a script to generate the website and extension library. It tries to be pretty loose, but don't deviate too far. You must use `//`, not `/* */`. New extensions do not *need* images, but they are highly encouraged. Save the image in the `images` folder with the same folder name and file name (but different file extension) as the extension's source code. For example, if your extension is located in `extensions/TestMuffin/fetch.js`, save the image as `images/TestMuffin/fetch.svg` or `images/TestMuffin/fetch.png`. The homepage generator will detect it automatically. Images are displayed in a 2:1 aspect ratio. SVG (preferred), PNG, or JPG are accepted. PNG or JPG should be 600x300 in resolution. Please add proper attribution to `images/README.md` for *any* resources that were not made by you. @@ -112,41 +115,40 @@ You **MUST** avoid using any code or images under these licenses as we believe t We take licenses very seriously. License violations are one of the few things that can force us to break project compatibility. -## Code style - -Our preferred code style is: - - - Indent with 2 spaces - - Use semicolons - - Use whitespace liberally - - Comment liberally, but don't just re-explain what the code literally says (`var x = 3; // set x to 3` is a bad comment) - - Pick one type of quotes and be consistent (if unsure, we like single quotes) - ## Type checking If you use our development server, TypeScript aware editors such as Visual Studio Code will give you smart autocomplete suggestions for most Scratch and extension APIs based on [@turbowarp/types](https://github.com/TurboWarp/types) and [@turbowarp/types-tw](https://github.com/TurboWarp/types-tw). Note that these types are not perfect; some methods are missing or incorrect. Please report any issues you find. If you encounter a TypeScript error, as long as you understand the error, feel free to add `// @ts-ignore`, `// @ts-expect-error`, or just ignore the error entirely. We currently do not require extensions to pass type checking. -## Linting +## Linting, validation, and formatting -We use ESLint to automatically common problems in pull requests. To run our linting rules on your computer, run: +All pull requests are automatically checked by a combination of custom validation scripts, [ESLint](https://eslint.org/), and [Prettier](https://prettier.io/). Don't worry about passing these checks on the first attempt -- most don't. That's why we have these checks. + +Our custom validation scripts do things like making sure you have the correct headers at the start of your extension and that the images are the right size. **Your extension must pass validation.** You can run them locally with: ```bash -npm run lint +npm run validate ``` -ESLint can automatically fix certain issues. You can run this with: +ESLint detects common JavaScript errors such as referencing non-existant variables. **Your extension must pass linting.** You can run it locally with: ```bash -npm run fix +npm run lint ``` -Our ESLint configuration separates between *warnings* and *errors*. They both cause big red error messages to appear on pull requests, but they are different: +You are allowed to [disable ESLint warnings and errors](https://eslint.org/docs/latest/use/configure/rules#disabling-rules) as needed, but please only do so if actually required. - - Warnings are typically minor style issues. If you ignore these we will fix it ourselves. This usually takes 15 seconds to address. - - Errors are actually problems. Please read and address all of them. Extensions with errors may take longer to review as your extension is unfinished. +When including third-party code, especially minified code, you may use `/* eslint-disable*/` and `/* eslint-enable */` markers to disable linting for that entire section. -You are allowed to [disable warnings or errors](https://eslint.org/docs/latest/use/configure/rules#disabling-rules) as needed, but only if actually required. +We use Prettier to ensure consistent code formatting. **Your extension does not need to pass format; we will fix it for you if linting and validation pass.** You can format your code automatically with: -When including third-party code, especially minified code, you may use `/* eslint-disable*/` and `/* eslint-enable */` markers to disable linting for that entire section. +```bash +npm run format +``` + +To just check formatting, use: + +```bash +npm run check-format +``` diff --git a/development/build-production.js b/development/build-production.js index c4664ac017..39f50abffe 100644 --- a/development/build-production.js +++ b/development/build-production.js @@ -1,9 +1,9 @@ -const pathUtil = require('path'); -const Builder = require('./builder'); +const pathUtil = require("path"); +const Builder = require("./builder"); -const outputDirectory = pathUtil.join(__dirname, '..', 'build'); +const outputDirectory = pathUtil.join(__dirname, "..", "build"); -const builder = new Builder('production'); +const builder = new Builder("production"); const build = builder.build(); build.export(outputDirectory); diff --git a/development/builder.js b/development/builder.js index a9c735b62c..292758e484 100644 --- a/development/builder.js +++ b/development/builder.js @@ -1,11 +1,9 @@ -const fs = require('fs'); -const pathUtil = require('path'); -const sizeOfImage = require('image-size'); -const renderTemplate = require('./render-template'); -const renderDocs = require('./render-docs'); -const compatibilityAliases = require('./compatibility-aliases'); -const parseMetadata = require('./parse-extension-metadata'); -const featuredExtensionsIDs = require('../extensions/extensions.json'); +const fs = require("fs"); +const AdmZip = require("adm-zip"); +const pathUtil = require("path"); +const compatibilityAliases = require("./compatibility-aliases"); +const parseMetadata = require("./parse-extension-metadata"); +const featuredExtensionSlugs = require("../extensions/extensions.json"); /** * @typedef {'development'|'production'|'desktop'} Mode @@ -20,14 +18,17 @@ const featuredExtensionsIDs = require('../extensions/extensions.json'); const recursiveReadDirectory = (directory) => { const result = []; for (const name of fs.readdirSync(directory)) { - if (name.startsWith('.')) { + if (name.startsWith(".")) { // Ignore .eslintrc.js, .DS_Store, etc. continue; } const absolutePath = pathUtil.join(directory, name); const stat = fs.statSync(absolutePath); if (stat.isDirectory()) { - for (const [relativeToChildName, childAbsolutePath] of recursiveReadDirectory(absolutePath)) { + for (const [ + relativeToChildName, + childAbsolutePath, + ] of recursiveReadDirectory(absolutePath)) { // This always needs to use / on all systems result.push([`${name}/${relativeToChildName}`, childAbsolutePath]); } @@ -39,72 +40,87 @@ const recursiveReadDirectory = (directory) => { }; class BuildFile { - constructor (source) { + constructor(source) { this.sourcePath = source; } - getType () { + getType() { return pathUtil.extname(this.sourcePath); } - getLastModified () { + getLastModified() { return fs.statSync(this.sourcePath).mtimeMs; } - read () { + read() { return fs.readFileSync(this.sourcePath); } - validate () { + validate() { // no-op by default } } class ExtensionFile extends BuildFile { - constructor (absolutePath, featured) { + constructor(absolutePath, featured) { super(absolutePath); this.featured = featured; } - getMetadata () { - const data = fs.readFileSync(this.sourcePath, 'utf-8'); + getMetadata() { + const data = fs.readFileSync(this.sourcePath, "utf-8"); return parseMetadata(data); } - validate () { + validate() { if (!this.featured) { return; } const metadata = this.getMetadata(); + if (!metadata.id) { + throw new Error("Missing // ID:"); + } + if (!metadata.name) { - throw new Error('Missing // Name:'); + throw new Error("Missing // Name:"); } if (!metadata.description) { - throw new Error('Missing // Description:'); + throw new Error("Missing // Description:"); } - const PUNCTUATION = ['.', '!', '?']; - if (!PUNCTUATION.some((punctuation) => metadata.description.endsWith(punctuation))) { - throw new Error(`Description is missing punctuation: ${metadata.description}`); + const PUNCTUATION = [".", "!", "?"]; + if ( + !PUNCTUATION.some((punctuation) => + metadata.description.endsWith(punctuation) + ) + ) { + throw new Error( + `Description is missing punctuation: ${metadata.description}` + ); } for (const person of [...metadata.by, ...metadata.original]) { if (!person.name) { - throw new Error('Person is missing name'); + throw new Error("Person is missing name"); } - if (person.link && !person.link.startsWith('https://scratch.mit.edu/users/')) { - throw new Error(`Link for ${person.name} does not point to a Scratch user`); + if ( + person.link && + !person.link.startsWith("https://scratch.mit.edu/users/") + ) { + throw new Error( + `Link for ${person.name} does not point to a Scratch user` + ); } } } } class HomepageFile extends BuildFile { - constructor (extensionFiles, extensionImages, mode) { - super(pathUtil.join(__dirname, 'homepage-template.ejs')); + constructor(extensionFiles, extensionImages, withDocs, samples, mode) { + super(pathUtil.join(__dirname, "homepage-template.ejs")); /** @type {Record} */ this.extensionFiles = extensionFiles; @@ -112,34 +128,66 @@ class HomepageFile extends BuildFile { /** @type {Record} */ this.extensionImages = extensionImages; + /** @type {Map} */ + this.withDocs = withDocs; + + /** @type {SampleFile[]} */ + this.samples = samples; + /** @type {Mode} */ this.mode = mode; - this.host = mode === 'development' ? 'http://localhost:8000/' : 'https://extensions.turbowarp.org/'; + this.host = + mode === "development" + ? "http://localhost:8000/" + : "https://extensions.turbowarp.org/"; } - getType () { - return '.html'; + getType() { + return ".html"; } - getFullExtensionURL (extensionID) { - return `${this.host}${extensionID}.js`; + getFullExtensionURL(extensionSlug) { + return `${this.host}${extensionSlug}.js`; } - getRunExtensionURL (extensionID) { - return `https://turbowarp.org/editor?extension=${this.getFullExtensionURL(extensionID)}`; + getDocumentationURL(extensionSlug) { + return `${this.host}${extensionSlug}`; } - read () { + getRunExtensionURL(extensionSlug) { + return `https://turbowarp.org/editor?extension=${this.getFullExtensionURL( + extensionSlug + )}`; + } + + /** + * @param {SampleFile} sampleFile + * @returns {string} + */ + getRunSampleURL(sampleFile) { + const path = encodeURIComponent(`samples/${sampleFile.getSlug()}`); + return `https://turbowarp.org/editor?project_url=${this.host}${path}`; + } + + read() { + const renderTemplate = require("./render-template"); + const mostRecentExtensions = Object.entries(this.extensionFiles) .sort((a, b) => b[1].getLastModified() - a[1].getLastModified()) .slice(0, 5) .map((i) => i[0]); - const extensionMetadata = Object.fromEntries(featuredExtensionsIDs.map((id) => [ - id, - this.extensionFiles[id].getMetadata() - ])); + const extensionMetadata = Object.fromEntries( + featuredExtensionSlugs.map((slug) => [ + slug, + { + ...this.extensionFiles[slug].getMetadata(), + hasDocumentation: this.withDocs.has(slug), + samples: this.samples.get(slug) || [], + }, + ]) + ); return renderTemplate(this.sourcePath, { mode: this.mode, @@ -147,13 +195,15 @@ class HomepageFile extends BuildFile { extensionImages: this.extensionImages, extensionMetadata, getFullExtensionURL: this.getFullExtensionURL.bind(this), - getRunExtensionURL: this.getRunExtensionURL.bind(this) + getRunExtensionURL: this.getRunExtensionURL.bind(this), + getDocumentationURL: this.getDocumentationURL.bind(this), + getRunSampleURL: this.getRunSampleURL.bind(this), }); } } class JSONMetadataFile extends BuildFile { - constructor (extensionFiles, extensionImages) { + constructor(extensionFiles, extensionImages, withDocs, samples) { super(null); /** @type {Record} */ @@ -161,142 +211,207 @@ class JSONMetadataFile extends BuildFile { /** @type {Record} */ this.extensionImages = extensionImages; + + /** @type {Set} */ + this.withDocs = withDocs; + + /** @type {Map} */ + this.samples = samples; } - getType () { - return '.json'; + getType() { + return ".json"; } - read () { + read() { const extensions = []; - for (const extensionID of featuredExtensionsIDs) { + for (const extensionSlug of featuredExtensionSlugs) { const extension = {}; - const file = this.extensionFiles[extensionID]; + const file = this.extensionFiles[extensionSlug]; const metadata = file.getMetadata(); - const image = this.extensionImages[extensionID]; + const image = this.extensionImages[extensionSlug]; - extension.id = extensionID; + extension.slug = extensionSlug; + extension.id = metadata.id; extension.name = metadata.name; extension.description = metadata.description; if (image) { extension.image = image; } + if (metadata.by.length) { + extension.by = metadata.by; + } + if (metadata.original.length) { + extension.original = metadata.original; + } + if (this.withDocs.has(extensionSlug)) { + extension.docs = true; + } + const samples = this.samples.get(extensionSlug); + if (samples) { + extension.samples = samples.map((i) => i.getTitle()); + } + extensions.push(extension); } const data = { - extensions + extensions, }; return JSON.stringify(data); } } class ImageFile extends BuildFile { - validate () { + validate() { + const sizeOfImage = require("image-size"); const contents = this.read(); - const {width, height} = sizeOfImage(contents); + const { width, height } = sizeOfImage(contents); const aspectRatio = width / height; if (aspectRatio !== 2) { - throw new Error(`Aspect ratio must be exactly 2, but found ${aspectRatio.toFixed(4)} (${width}x${height})`); + throw new Error( + `Aspect ratio must be exactly 2, but found ${aspectRatio.toFixed( + 4 + )} (${width}x${height})` + ); } } } class SVGFile extends ImageFile { - validate () { + validate() { const contents = this.read(); - if (contents.includes(' elements -- please convert the text to a path. This ensures it will display correctly on all devices.'); + if (contents.includes(" elements -- please convert the text to a path. This ensures it will display correctly on all devices." + ); } super.validate(); } } +const IMAGE_FORMATS = new Map(); +IMAGE_FORMATS.set(".png", ImageFile); +IMAGE_FORMATS.set(".jpg", ImageFile); +IMAGE_FORMATS.set(".svg", SVGFile); + class SitemapFile extends BuildFile { - constructor (build) { + constructor(build) { super(null); this.build = build; } - getType () { - return '.xml'; + getType() { + return ".xml"; } - read () { - let xml = ''; + read() { + let xml = ""; xml += '\n'; xml += '\n'; xml += Object.keys(this.build.files) - .filter(file => file.endsWith('.html')) - .map(file => file.replace('index.html', '').replace('.html', '')) + .filter((file) => file.endsWith(".html")) + .map((file) => file.replace("index.html", "").replace(".html", "")) .sort((a, b) => { if (a.length < b.length) return -1; if (a.length > b.length) return 1; return a - b; }) - .map(path => `https://extensions.turbowarp.org${path}`) - .map(absoluteURL => `${absoluteURL}`) - .join('\n'); + .map((path) => `https://extensions.turbowarp.org${path}`) + .map((absoluteURL) => `${absoluteURL}`) + .join("\n"); - xml += '\n'; + xml += "\n"; return xml; } } -const IMAGE_FORMATS = new Map(); -IMAGE_FORMATS.set('.png', ImageFile); -IMAGE_FORMATS.set('.jpg', ImageFile); -IMAGE_FORMATS.set('.svg', SVGFile); - class DocsFile extends BuildFile { - constructor (absolutePath, extensionId) { + constructor(absolutePath, extensionSlug) { super(absolutePath); - this.extensionId = extensionId; + this.extensionSlug = extensionSlug; } - read () { - const markdown = super.read().toString('utf-8'); - return renderDocs(markdown, this.extensionId); + read() { + const renderDocs = require("./render-docs"); + const markdown = super.read().toString("utf-8"); + return renderDocs(markdown, this.extensionSlug); } - getType () { - return '.html'; + getType() { + return ".html"; } } -class Build { - constructor (mode) { - /** @type {Mode} */ - this.mode = mode; +class SampleFile extends BuildFile { + getSlug() { + return pathUtil.basename(this.sourcePath); + } + + getTitle() { + return this.getSlug().replace(".sb3", ""); + } + + /** @returns {string[]} list of full URLs */ + getExtensionURLs() { + const zip = new AdmZip(this.sourcePath); + const entry = zip.getEntry("project.json"); + if (!entry) { + throw new Error("package.json missing"); + } + const data = JSON.parse(entry.getData().toString("utf-8")); + return data.extensionURLs ? Object.values(data.extensionURLs) : []; + } + + validate() { + const urls = this.getExtensionURLs(); + + if (urls.length === 0) { + throw new Error("Has no extensions"); + } + for (const url of urls) { + if ( + !url.startsWith("https://extensions.turbowarp.org/") || + !url.endsWith(".js") + ) { + throw new Error(`Invalid extension URL for sample: ${url}`); + } + } + } +} + +class Build { + constructor() { this.files = {}; } - getFile (path) { - return this.files[path] || this.files[`${path}.html`] || this.files[`${path}index.html`] || null; + getFile(path) { + return ( + this.files[path] || + this.files[`${path}.html`] || + this.files[`${path}index.html`] || + null + ); } - export (root) { + export(root) { try { fs.rmSync(root, { - recursive: true + recursive: true, }); } catch (e) { - if (e.code !== 'ENOENT') { + if (e.code !== "ENOENT") { throw e; } } for (const [relativePath, file] of Object.entries(this.files)) { - if (this.mode === 'desktop' && relativePath.endsWith('.html')) { - continue; - } - const directoryName = pathUtil.dirname(relativePath); fs.mkdirSync(pathUtil.join(root, directoryName), { - recursive: true + recursive: true, }); fs.writeFileSync(pathUtil.join(root, relativePath), file.read()); } @@ -307,82 +422,137 @@ class Builder { /** * @param {Mode} mode */ - constructor (mode) { - if (process.argv.includes('--production')) { - this.mode = 'production'; - } else if (process.argv.includes('--development')) { - this.mode = 'development'; - } else if (process.argv.includes('--desktop')) { - this.mode = 'desktop'; + constructor(mode) { + if (process.argv.includes("--production")) { + this.mode = "production"; + } else if (process.argv.includes("--development")) { + this.mode = "development"; + } else if (process.argv.includes("--desktop")) { + this.mode = "desktop"; } else { /** @type {Mode} */ this.mode = mode; } - this.extensionsRoot = pathUtil.join(__dirname, '../extensions'); - this.websiteRoot = pathUtil.join(__dirname, '../website'); - this.imagesRoot = pathUtil.join(__dirname, '../images'); - this.docsRoot = pathUtil.join(__dirname, '../docs'); + this.extensionsRoot = pathUtil.join(__dirname, "../extensions"); + this.websiteRoot = pathUtil.join(__dirname, "../website"); + this.imagesRoot = pathUtil.join(__dirname, "../images"); + this.docsRoot = pathUtil.join(__dirname, "../docs"); + this.samplesRoot = pathUtil.join(__dirname, "../samples"); } - build () { + build() { const build = new Build(this.mode); + /** @type {Record} */ + const extensionFiles = {}; + for (const [filename, absolutePath] of recursiveReadDirectory( + this.extensionsRoot + )) { + if (!filename.endsWith(".js")) { + continue; + } + const extensionSlug = filename.split(".")[0]; + const featured = featuredExtensionSlugs.includes(extensionSlug); + const file = new ExtensionFile(absolutePath, featured); + extensionFiles[extensionSlug] = file; + build.files[`/${filename}`] = file; + } + /** @type {Record} */ const extensionImages = {}; - for (const [filename, absolutePath] of recursiveReadDirectory(this.imagesRoot)) { + for (const [filename, absolutePath] of recursiveReadDirectory( + this.imagesRoot + )) { const extension = pathUtil.extname(filename); const ImageFileClass = IMAGE_FORMATS.get(extension); if (!ImageFileClass) { continue; } - const extensionId = filename.split('.')[0]; - if (extensionId !== 'unknown') { - extensionImages[extensionId] = `images/${filename}`; + const extensionSlug = filename.split(".")[0]; + if (extensionSlug !== "unknown") { + extensionImages[extensionSlug] = `images/${filename}`; } build.files[`/images/${filename}`] = new ImageFileClass(absolutePath); } - for (const [filename, absolutePath] of recursiveReadDirectory(this.docsRoot)) { - if (!filename.endsWith('.md')) { + /** @type {Set} */ + const extensionsWithDocs = new Set(); + + /** @type {Map} */ + const samples = new Map(); + for (const [filename, absolutePath] of recursiveReadDirectory( + this.samplesRoot + )) { + if (!filename.endsWith(".sb3")) { continue; } - const extensionId = filename.split('.')[0]; - build.files[`/${extensionId}.html`] = new DocsFile(absolutePath, extensionId); + + const file = new SampleFile(absolutePath); + for (const url of file.getExtensionURLs()) { + const slug = new URL(url).pathname.substring(1).replace(".js", ""); + if (samples.has(slug)) { + samples.get(slug).push(file); + } else { + samples.set(slug, [file]); + } + } + build.files[`/samples/${filename}`] = file; } - const scratchblocksPath = pathUtil.join(__dirname, '../node_modules/scratchblocks/build/scratchblocks.min.js'); - build.files['/docs-internal/scratchblocks.js'] = new BuildFile(scratchblocksPath); + if (this.mode !== "desktop") { + for (const [filename, absolutePath] of recursiveReadDirectory( + this.websiteRoot + )) { + build.files[`/${filename}`] = new BuildFile(absolutePath); + } - /** @type {Record} */ - const extensionFiles = {}; - for (const [filename, absolutePath] of recursiveReadDirectory(this.extensionsRoot)) { - if (!filename.endsWith('.js')) { - continue; + for (const [filename, absolutePath] of recursiveReadDirectory( + this.docsRoot + )) { + if (!filename.endsWith(".md")) { + continue; + } + const extensionSlug = filename.split(".")[0]; + const file = new DocsFile(absolutePath, extensionSlug); + extensionsWithDocs.add(extensionSlug); + build.files[`/${extensionSlug}.html`] = file; } - const extensionId = filename.split('.')[0]; - const featured = featuredExtensionsIDs.includes(extensionId); - const file = new ExtensionFile(absolutePath, featured); - extensionFiles[extensionId] = file; - build.files[`/${filename}`] = file; + + const scratchblocksPath = pathUtil.join( + __dirname, + "../node_modules/scratchblocks/build/scratchblocks.min.js" + ); + build.files["/docs-internal/scratchblocks.js"] = new BuildFile( + scratchblocksPath + ); + + build.files["/index.html"] = new HomepageFile( + extensionFiles, + extensionImages, + extensionsWithDocs, + samples, + this.mode + ); + build.files["/sitemap.xml"] = new SitemapFile(build); } + build.files["/generated-metadata/extensions-v0.json"] = + new JSONMetadataFile( + extensionFiles, + extensionImages, + extensionsWithDocs, + samples + ); + for (const [oldPath, newPath] of Object.entries(compatibilityAliases)) { build.files[oldPath] = build.files[newPath]; } - for (const [filename, absolutePath] of recursiveReadDirectory(this.websiteRoot)) { - build.files[`/${filename}`] = new BuildFile(absolutePath); - } - - build.files['/index.html'] = new HomepageFile(extensionFiles, extensionImages, this.mode); - build.files['/generated-metadata/extensions-v0.json'] = new JSONMetadataFile(extensionFiles, extensionImages); - build.files['/sitemap.xml'] = new SitemapFile(build); - return build; } - tryBuild (...args) { + tryBuild(...args) { const start = new Date(); process.stdout.write(`[${start.toLocaleTimeString()}] Building... `); @@ -392,30 +562,36 @@ class Builder { console.log(`done in ${time}ms`); return build; } catch (error) { - console.log('error'); + console.log("error"); console.error(error); } return null; } - startWatcher (callback) { + startWatcher(callback) { // Load chokidar lazily. - const chokidar = require('chokidar'); + const chokidar = require("chokidar"); callback(this.tryBuild()); - chokidar.watch([ - `${this.extensionsRoot}/**/*`, - `${this.imagesRoot}/**/*`, - `${this.websiteRoot}/**/*`, - `${this.docsRoot}/**/*`, - ], { - ignoreInitial: true - }).on('all', () => { - callback(this.tryBuild()); - }); + chokidar + .watch( + [ + `${this.extensionsRoot}/**/*`, + `${this.imagesRoot}/**/*`, + `${this.websiteRoot}/**/*`, + `${this.docsRoot}/**/*`, + `${this.samplesRoot}/**/*`, + ], + { + ignoreInitial: true, + } + ) + .on("all", () => { + callback(this.tryBuild()); + }); } - validate () { + validate() { const errors = []; const build = this.build(); for (const [fileName, file] of Object.entries(build.files)) { @@ -424,7 +600,7 @@ class Builder { } catch (e) { errors.push({ fileName, - error: e + error: e, }); } } diff --git a/development/colors.js b/development/colors.js index 6dc21194d1..7e6fa1eaa8 100644 --- a/development/colors.js +++ b/development/colors.js @@ -1,9 +1,9 @@ const enableColor = !process.env.NO_COLOR; -const color = (i) => enableColor ? i : ''; +const color = (i) => (enableColor ? i : ""); module.exports = { - RESET: color('\x1b[0m'), - BOLD: color('\x1b[1m'), - RED: color('\x1b[31m'), - GREEN: color('\x1b[32m') + RESET: color("\x1b[0m"), + BOLD: color("\x1b[1m"), + RED: color("\x1b[31m"), + GREEN: color("\x1b[32m"), }; diff --git a/development/compatibility-aliases.js b/development/compatibility-aliases.js index a3302ea292..e1722787fa 100644 --- a/development/compatibility-aliases.js +++ b/development/compatibility-aliases.js @@ -1,13 +1,13 @@ const extensions = { // maps old path to new path - '/LukeManiaStudios/ClonesPlus.js': '/Lily/ClonesPlus.js', - '/LukeManiaStudios/CommentBlocks.js': '/Lily/CommentBlocks.js', - '/LukeManiaStudios/lmsutils.js': '/Lily/lmsutils.js', - '/LukeManiaStudios/LooksPlus.js': '/Lily/LooksPlus.js', - '/LukeManiaStudios/McUtils.js': '/Lily/McUtils.js', - '/LukeManiaStudios/MoreTimers.js': '/Lily/MoreTimers.js', - '/LukeManiaStudios/TempVariables.js': '/Lily/TempVariables.js', - '/LukeManiaStudios/TempVariables2.js': '/Lily/TempVariables2.js' + "/LukeManiaStudios/ClonesPlus.js": "/Lily/ClonesPlus.js", + "/LukeManiaStudios/CommentBlocks.js": "/Lily/CommentBlocks.js", + "/LukeManiaStudios/lmsutils.js": "/Lily/lmsutils.js", + "/LukeManiaStudios/LooksPlus.js": "/Lily/LooksPlus.js", + "/LukeManiaStudios/McUtils.js": "/Lily/McUtils.js", + "/LukeManiaStudios/MoreTimers.js": "/Lily/MoreTimers.js", + "/LukeManiaStudios/TempVariables.js": "/Lily/TempVariables.js", + "/LukeManiaStudios/TempVariables2.js": "/Lily/TempVariables2.js", }; module.exports = extensions; diff --git a/development/homepage-template.ejs b/development/homepage-template.ejs index ad693af7a2..95b3c5fbf3 100644 --- a/development/homepage-template.ejs +++ b/development/homepage-template.ejs @@ -107,6 +107,7 @@ } .extension { + position: relative; border: 2px solid #ccc; border-radius: 8px; margin: 4px; @@ -121,6 +122,9 @@ .extension h3 { font-size: 1.5em; } + .extension > :last-child { + margin-bottom: 0; + } .extension-banner { position: relative; margin: -8px -8px 0 -8px; @@ -137,10 +141,13 @@ top: 0; left: 0; display: flex; + flex-wrap: wrap; width: 100%; height: 100%; align-items: center; + align-content: center; justify-content: center; + gap: 0.5rem; opacity: 0; transition: opacity .15s; background: rgba(0, 0, 0, 0.5); @@ -148,14 +155,12 @@ } .extension:hover .extension-buttons, .extension:focus-within .extension-buttons { opacity: 1; - } - .extension:hover .extension-buttons { backdrop-filter: blur(0.6px); } .extension-buttons > * { - padding: 8px; + padding: 0.5rem; background-color: #4c97ff; - border-radius: 8px; + border-radius: 0.5rem; border: none; font: inherit; cursor: pointer; @@ -175,17 +180,52 @@ .extension-buttons *:disabled { opacity: 0.5; } - .extension-buttons .copy { - margin: 0 8px 0 0; - } .extension-buttons .open { background-color: #ff4c4c; color: white; } + .extension-buttons .docs { + background-color: #FFAB19; + color: white; + } + .extension-buttons .sample { + background-color: #40BF4A; + color: white; + } .extension-buttons :disabled { opacity: 0.5; } + .sample-list { + display: none; + position: absolute; + left: 0.5rem; + right: 0.5rem; + width: calc(100% - 1rem); + margin-top: -1.5rem; + padding: 0.5rem; + box-sizing: border-box; + background-color: white; + border-radius: 0.5rem; + box-shadow: 0px 0px 8px 1px rgba(0, 0, 0, .3); + border: 1px solid rgba(0, 0, 0, 0.15); + flex-direction: column; + gap: 0.5rem; + } + .sample-list h3 { + font-size: 1rem; + margin: 0; + } + .extension:hover[data-samples-open="true"] .sample-list { + display: flex; + } + @media (prefers-color-scheme: dark) { + .sample-list { + border: 1px solid rgba(255, 255, 255, 0.15); + background-color: #333; + } + } + footer { opacity: 0.8; width: 100%; @@ -230,6 +270,7 @@
+
Some extensions may not work in TurboWarp Desktop.
For compatibility, security, and offline support, each TurboWarp Desktop update contains an offline copy of these extensions from its release date, so some extensions may be outdated or missing. Use the latest update for best results.
@@ -241,8 +282,8 @@

Development Server Tools

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)) { %>
- <% const image = extensionImages[extensionID] || 'images/unknown.svg'; %> + <% const image = extensionImages[extensionSlug] || 'images/unknown.svg'; %>
- - Open Extension + + Open Extension + + <% if (metadata.hasDocumentation) { %> + Documentation + <% } %> + + <% if (metadata.samples.length === 1) { %> + Sample Project + <% } else if (metadata.samples.length > 1) { %> + + <% } %>
+ + <% if (metadata.samples.length > 1) { %> +
+

<%= metadata.name %> Sample Projects:

+ <% for (const sample of metadata.samples) { %> + <%= sample.getTitle() %> + <% } %> +
+ <% } %>

<%= metadata.name %>

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 `

${md.utils.escapeHtml(token.content)}
`; + return `
${md.utils.escapeHtml( + token.content + )}
`; } // By default markdown-it will use a strange combination of 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,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'; - 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,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 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,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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: "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,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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); 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(` @@ -22,7 +24,6 @@ `); - 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 . This lets us do some clever things: - // - We can resize the image without a canvas. - // - We can give the browser an image with more raw pixels than its DPI independent size. - // The latter is important so that cursors won't look horrible on high DPI displays. For - // example, if the cursor will display at 32x32 in DPI independent units on a 2x high DPI - // display, we actually need to send a 64x64 image for it to look good. This lets us do - // that automatically. - let svg = ``; - svg += ``; - svg += ''; - // URI encoding usually results in smaller string than base 64 for the types of data we get here. - const svgURI = `data:image/svg+xml;,${encodeURIComponent(svg)}`; - - return { - uri: svgURI, - width, - height - }; - }; - - /** @type {string} */ - let nativeCursor = 'default'; - /** @type {null|string} */ - let customCursorImageName = null; - - const canvas = Scratch.renderer.canvas; - /** @type {string} */ - let currentCanvasCursor = nativeCursor; - const updateCanvasCursor = () => { - if (canvas.style.cursor !== currentCanvasCursor) { - canvas.style.cursor = currentCanvasCursor; - } - }; - - // scratch-gui will sometimes reset the cursor when resizing the window or going in/out of fullscreen - new MutationObserver(updateCanvasCursor).observe(canvas, { - attributeFilter: ['style'], - attributes: true - }); - - /** - * Parse strings like "60x12" or "77,1" - * @param {string} string - * @returns {[number, number]} - */ - const parseTuple = (string) => { - const [a, b] = ('' + string).split(/[ ,x]/); - return [ - +a || 0, - +b || 0 - ]; - }; - - const cursors = [ - 'default', 'pointer', 'move', 'grab', 'grabbing', 'text', - 'vertical-text', 'wait', 'progress', 'help', 'context-menu', - 'zoom-in', 'zoom-out', 'crosshair', 'cell', 'not-allowed', - 'copy', 'alias', 'no-drop', 'all-scroll', 'col-resize', - 'row-resize', 'n-resize', 'e-resize', 's-resize', 'w-resize', - 'ne-resize', 'nw-resize', 'se-resize', 'sw-resize', - 'ew-resize', 'ns-resize', 'nesw-resize', 'nwse-resize' - ]; - - class MouseCursor { - getInfo() { - return { - id: 'MouseCursor', - name: 'Mouse Cursor', - blocks: [ - { - opcode: 'setCur', - blockType: Scratch.BlockType.COMMAND, - text: 'set cursor to [cur]', - arguments: { - cur: { - type: Scratch.ArgumentType.STRING, - defaultValue: 'pointer', - menu: 'cursors', - }, - }, - }, - { - opcode: 'setCursorImage', - blockType: Scratch.BlockType.COMMAND, - text: "set cursor to current costume center: [position] max size: [size]", - arguments: { - position: { - type: Scratch.ArgumentType.STRING, - defaultValue: '0,0', - menu: 'imagePositions' - }, - size: { - type: Scratch.ArgumentType.STRING, - defaultValue: '32x32', - menu: 'imageSizes' - } - } - }, - { - opcode: 'hideCur', - blockType: Scratch.BlockType.COMMAND, - text: 'hide cursor', - }, - { - opcode: 'getCur', - blockType: Scratch.BlockType.REPORTER, - text: 'cursor', - }, - ], - menus: { - cursors: { - acceptReporters: true, - items: cursors, - }, - imagePositions: { - acceptReporters: true, - items: [ - // [x, y] where x is [0=left, 100=right] and y is [0=top, 100=bottom] - { text: 'top left', value: '0,0' }, - { text: 'top right', value: '100,0' }, - { text: 'bottom left', value: '0,100' }, - { text: 'bottom right', value: '100,100' }, - { text: 'center', value: '50,50' }, - ] - }, - imageSizes: { - acceptReporters: true, - items: [ - // Some important numbers to keep in mind: - // Browsers ignore cursor images >128 in any dimension (https://searchfox.org/mozilla-central/rev/43ee5e789b079e94837a21336e9ce2420658fd19/widget/gtk/nsWindow.cpp#3393-3402) - // Browsers may refuse to display a cursor near window borders for images >32 in any dimension - { text: '4x4', value: '4x4' }, - { text: '8x8', value: '8x4' }, - { text: '12x12', value: '12x12' }, - { text: '16x16', value: '16x16' }, - { text: '32x32', value: '32x32' }, - { text: '48x48 (unreliable)', value: '48x48' }, - { text: '64x64 (unreliable)', value: '64x64' }, - { text: '128x128 (unreliable)', value: '128x128' }, - ] - } - }, - }; - } - - setCur(args) { - const newCursor = Scratch.Cast.toString(args.cur); - // Prevent setting cursor to "url(...), default" from causing fetch. - if (cursors.includes(newCursor) || newCursor === 'none') { - nativeCursor = newCursor; - customCursorImageName = null; - currentCanvasCursor = newCursor; - updateCanvasCursor(); - } - } - - setCursorImage(args, util) { - const [maxWidth, maxHeight] = parseTuple(args.size).map(i => Math.max(0, i)); - - const currentCostume = util.target.getCostumes()[util.target.currentCostume]; - const costumeName = currentCostume.name; - - let encodedCostume; - try { - encodedCostume = costumeToCursor(currentCostume, maxWidth, maxHeight); - } catch (e) { - // This could happen for a variety of reasons. - console.error(e); - } - - if (encodedCostume) { - const [percentX, percentY] = parseTuple(args.position).map(i => Math.max(0, Math.min(100, i)) / 100); - const x = percentX * encodedCostume.width; - const y = percentY * encodedCostume.height; - - currentCanvasCursor = `url("${encodedCostume.uri}") ${x} ${y}, ${nativeCursor}`; - updateCanvasCursor(); - } else { - // If for some reason the costume couldn't be encoded, we'll leave the cursor unchanged. - // This is the same behavior that would happen if we successfully encode a cursor but the browser - // is unable to parse it for some reason. - } - - customCursorImageName = costumeName; - } - - hideCur() { - this.setCur({ - cur: 'none' - }); - } - - getCur() { - if (customCursorImageName !== null) { - return customCursorImageName; - } - return nativeCursor; - } - } - - Scratch.extensions.register(new MouseCursor()); -})(Scratch); +// Name: Mouse Cursor +// ID: MouseCursor +// 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 . This lets us do some clever things: + // - We can resize the image without a canvas. + // - We can give the browser an image with more raw pixels than its DPI independent size. + // The latter is important so that cursors won't look horrible on high DPI displays. For + // example, if the cursor will display at 32x32 in DPI independent units on a 2x high DPI + // display, we actually need to send a 64x64 image for it to look good. This lets us do + // that automatically. + let svg = ``; + svg += ``; + svg += ""; + // URI encoding usually results in smaller string than base 64 for the types of data we get here. + const svgURI = `data:image/svg+xml;,${encodeURIComponent(svg)}`; + + return { + uri: svgURI, + width, + height, + }; + }; + + /** @type {string} */ + let nativeCursor = "default"; + /** @type {null|string} */ + let customCursorImageName = null; + + const canvas = Scratch.renderer.canvas; + /** @type {string} */ + let currentCanvasCursor = nativeCursor; + const updateCanvasCursor = () => { + if (canvas.style.cursor !== currentCanvasCursor) { + canvas.style.cursor = currentCanvasCursor; + } + }; + + // scratch-gui will sometimes reset the cursor when resizing the window or going in/out of fullscreen + new MutationObserver(updateCanvasCursor).observe(canvas, { + attributeFilter: ["style"], + attributes: true, + }); + + /** + * Parse strings like "60x12" or "77,1" + * @param {string} string + * @returns {[number, number]} + */ + const parseTuple = (string) => { + const [a, b] = ("" + string).split(/[ ,x]/); + return [+a || 0, +b || 0]; + }; + + const cursors = [ + "default", + "pointer", + "move", + "grab", + "grabbing", + "text", + "vertical-text", + "wait", + "progress", + "help", + "context-menu", + "zoom-in", + "zoom-out", + "crosshair", + "cell", + "not-allowed", + "copy", + "alias", + "no-drop", + "all-scroll", + "col-resize", + "row-resize", + "n-resize", + "e-resize", + "s-resize", + "w-resize", + "ne-resize", + "nw-resize", + "se-resize", + "sw-resize", + "ew-resize", + "ns-resize", + "nesw-resize", + "nwse-resize", + ]; + + class MouseCursor { + getInfo() { + return { + id: "MouseCursor", + name: "Mouse Cursor", + blocks: [ + { + opcode: "setCur", + blockType: Scratch.BlockType.COMMAND, + text: "set cursor to [cur]", + arguments: { + cur: { + type: Scratch.ArgumentType.STRING, + defaultValue: "pointer", + menu: "cursors", + }, + }, + }, + { + opcode: "setCursorImage", + blockType: Scratch.BlockType.COMMAND, + text: "set cursor to current costume center: [position] max size: [size]", + arguments: { + position: { + type: Scratch.ArgumentType.STRING, + defaultValue: "0,0", + menu: "imagePositions", + }, + size: { + type: Scratch.ArgumentType.STRING, + defaultValue: "32x32", + menu: "imageSizes", + }, + }, + }, + { + opcode: "hideCur", + blockType: Scratch.BlockType.COMMAND, + text: "hide cursor", + }, + { + opcode: "getCur", + blockType: Scratch.BlockType.REPORTER, + text: "cursor", + }, + ], + menus: { + cursors: { + acceptReporters: true, + items: cursors, + }, + imagePositions: { + acceptReporters: true, + items: [ + // [x, y] where x is [0=left, 100=right] and y is [0=top, 100=bottom] + { text: "top left", value: "0,0" }, + { text: "top right", value: "100,0" }, + { text: "bottom left", value: "0,100" }, + { text: "bottom right", value: "100,100" }, + { text: "center", value: "50,50" }, + ], + }, + imageSizes: { + acceptReporters: true, + items: [ + // Some important numbers to keep in mind: + // Browsers ignore cursor images >128 in any dimension (https://searchfox.org/mozilla-central/rev/43ee5e789b079e94837a21336e9ce2420658fd19/widget/gtk/nsWindow.cpp#3393-3402) + // Browsers may refuse to display a cursor near window borders for images >32 in any dimension + { text: "4x4", value: "4x4" }, + { text: "8x8", value: "8x4" }, + { text: "12x12", value: "12x12" }, + { text: "16x16", value: "16x16" }, + { text: "32x32", value: "32x32" }, + { text: "48x48 (unreliable)", value: "48x48" }, + { text: "64x64 (unreliable)", value: "64x64" }, + { text: "128x128 (unreliable)", value: "128x128" }, + ], + }, + }, + }; + } + + setCur(args) { + const newCursor = Scratch.Cast.toString(args.cur); + // Prevent setting cursor to "url(...), default" from causing fetch. + if (cursors.includes(newCursor) || newCursor === "none") { + nativeCursor = newCursor; + customCursorImageName = null; + currentCanvasCursor = newCursor; + updateCanvasCursor(); + } + } + + setCursorImage(args, util) { + const [maxWidth, maxHeight] = parseTuple(args.size).map((i) => + Math.max(0, i) + ); + + const currentCostume = + util.target.getCostumes()[util.target.currentCostume]; + const costumeName = currentCostume.name; + + let encodedCostume; + try { + encodedCostume = costumeToCursor(currentCostume, maxWidth, maxHeight); + } catch (e) { + // This could happen for a variety of reasons. + console.error(e); + } + + if (encodedCostume) { + const [percentX, percentY] = parseTuple(args.position).map( + (i) => Math.max(0, Math.min(100, i)) / 100 + ); + const x = percentX * encodedCostume.width; + const y = percentY * encodedCostume.height; + + currentCanvasCursor = `url("${encodedCostume.uri}") ${x} ${y}, ${nativeCursor}`; + updateCanvasCursor(); + } else { + // If for some reason the costume couldn't be encoded, we'll leave the cursor unchanged. + // This is the same behavior that would happen if we successfully encode a cursor but the browser + // is unable to parse it for some reason. + } + + customCursorImageName = costumeName; + } + + hideCur() { + this.setCur({ + cur: "none", + }); + } + + getCur() { + if (customCursorImageName !== null) { + return customCursorImageName; + } + return nativeCursor; + } + } + + Scratch.extensions.register(new MouseCursor()); +})(Scratch); diff --git a/extensions/docs-examples/unsandboxed/broadcast-5.js b/extensions/docs-examples/unsandboxed/broadcast-5.js index 0b9df60f93..1e08bff12b 100644 --- a/extensions/docs-examples/unsandboxed/broadcast-5.js +++ b/extensions/docs-examples/unsandboxed/broadcast-5.js @@ -8,7 +8,7 @@ blocks: [ { opcode: 'whenReceived', - blockType: Scratch.BlockType.HAT, + blockType: Scratch.BlockType.EVENT, text: 'when I receive [EVENT_OPTION]', isEdgeActivated: false, arguments: { diff --git a/extensions/encoding.js b/extensions/encoding.js index d2ad7bc1a1..f506508052 100644 --- a/extensions/encoding.js +++ b/extensions/encoding.js @@ -1,622 +1,634 @@ -// Name: Encoding -// Description: Encode and decode strings into their unicode numbers, base 64, or URLs. -// By: -SIPC- - -(function (Scratch) { - 'use strict'; - const icon = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxMzcuNzk1MDYiIGhlaWdodD0iMTM0LjIzNzA3IiB2aWV3Qm94PSIwLDAsMTM3Ljc5NTA2LDEzNC4yMzcwNyI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTE1Mi44OTU4NiwtMTMwLjM3OTg5KSI+PGcgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aXNQYWludGluZ0xheWVyJnF1b3Q7OnRydWV9IiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0iI2ZmZmZmZiIgc3Ryb2tlLXdpZHRoPSIyMCIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWRhc2hhcnJheT0iIiBzdHJva2UtZGFzaG9mZnNldD0iMCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0xOTkuMzA5MDgsMjE5LjYyMDExdi03OS4yNDAyMmg4MS4zODE4NHY3OS4yNDAyMnoiLz48cGF0aCBkPSJNMTYyLjg5NTg2LDI1NC42MTY5NnYtNzkuMjQwMjJoODEuMzgxODR2NzkuMjQwMjJ6Ii8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6ODcuMTA0MTQwMTg0NTE2NDQ6NDkuNjIwMTA4MzQwNzA3OTYtLT4='; - const icon2 = 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSI4MS44ODUzOSIgaGVpZ2h0PSI4MC42MDMwNyIgdmlld0JveD0iMCwwLDgxLjg4NTM5LDgwLjYwMzA3Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTk5LjA1NzMsLTEzOS42OTg0NikiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTI4MC45NDI3LDE4MGMwLDIyLjI1NzkyIC0xOC4zMzA2Nyw0MC4zMDE1NCAtNDAuOTQyNyw0MC4zMDE1NGMtMjIuNjEyMDMsMCAtNDAuOTQyNywtMTguMDQzNjEgLTQwLjk0MjcsLTQwLjMwMTU0YzAsLTIyLjI1NzkyIDE4LjMzMDY3LC00MC4zMDE1NCA0MC45NDI3LC00MC4zMDE1NGMyMi42MTIwMywwIDQwLjk0MjcsMTguMDQzNjEgNDAuOTQyNyw0MC4zMDE1NHoiIGZpbGw9IiM2NDk1ZWQiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIwIi8+PHBhdGggZD0iTTIzMS44MTg3NiwxODcuOTc2MDh2LTI4Ljc2NzE1aDI5LjczNDExdjI4Ljc2NzE1eiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmZmZmIiBzdHJva2Utd2lkdGg9IjYiLz48cGF0aCBkPSJNMjE4LjQ0NzEzLDIwMC43OTEwN3YtMjguNzY3MTVoMjkuNzM0MTF2MjguNzY3MTV6IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iNiIvPjwvZz48L2c+PC9zdmc+PCEtLXJvdGF0aW9uQ2VudGVyOjQwLjk0MjY5NjA1MzgwMTE0OjQwLjMwMTUzNTI2NTQ4NjcwNi0tPg=='; - - /*! - This md5 function is based on https://github.com/blueimp/JavaScript-MD5/blob/master/js/md5.js - which is licensed under: - - MIT License - - Copyright © 2011 Sebastian Tschan, https://blueimp.net - - Permission is hereby granted, free of charge, to any person obtaining a copy of - this software and associated documentation files (the "Software"), to deal in - the Software without restriction, including without limitation the rights to - use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - the Software, and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - /* eslint-disable */ - const md5 = (function () { - /** - * Add integers, wrapping at 2^32. - * This uses 16-bit operations internally to work around bugs in interpreters. - * - * @param {number} x First integer - * @param {number} y Second integer - * @returns {number} Sum - */ - function safeAdd(x, y) { - var lsw = (x & 0xffff) + (y & 0xffff) - var msw = (x >> 16) + (y >> 16) + (lsw >> 16) - return (msw << 16) | (lsw & 0xffff) - } - - /** - * Bitwise rotate a 32-bit number to the left. - * - * @param {number} num 32-bit number - * @param {number} cnt Rotation count - * @returns {number} Rotated number - */ - function bitRotateLeft(num, cnt) { - return (num << cnt) | (num >>> (32 - cnt)) - } - - /** - * Basic operation the algorithm uses. - * - * @param {number} q q - * @param {number} a a - * @param {number} b b - * @param {number} x x - * @param {number} s s - * @param {number} t t - * @returns {number} Result - */ - function md5cmn(q, a, b, x, s, t) { - return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b) - } - /** - * Basic operation the algorithm uses. - * - * @param {number} a a - * @param {number} b b - * @param {number} c c - * @param {number} d d - * @param {number} x x - * @param {number} s s - * @param {number} t t - * @returns {number} Result - */ - function md5ff(a, b, c, d, x, s, t) { - return md5cmn((b & c) | (~b & d), a, b, x, s, t) - } - /** - * Basic operation the algorithm uses. - * - * @param {number} a a - * @param {number} b b - * @param {number} c c - * @param {number} d d - * @param {number} x x - * @param {number} s s - * @param {number} t t - * @returns {number} Result - */ - function md5gg(a, b, c, d, x, s, t) { - return md5cmn((b & d) | (c & ~d), a, b, x, s, t) - } - /** - * Basic operation the algorithm uses. - * - * @param {number} a a - * @param {number} b b - * @param {number} c c - * @param {number} d d - * @param {number} x x - * @param {number} s s - * @param {number} t t - * @returns {number} Result - */ - function md5hh(a, b, c, d, x, s, t) { - return md5cmn(b ^ c ^ d, a, b, x, s, t) - } - /** - * Basic operation the algorithm uses. - * - * @param {number} a a - * @param {number} b b - * @param {number} c c - * @param {number} d d - * @param {number} x x - * @param {number} s s - * @param {number} t t - * @returns {number} Result - */ - function md5ii(a, b, c, d, x, s, t) { - return md5cmn(c ^ (b | ~d), a, b, x, s, t) - } - - /** - * Calculate the MD5 of an array of little-endian words, and a bit length. - * - * @param {Array} x Array of little-endian words - * @param {number} len Bit length - * @returns {Array} MD5 Array - */ - function binlMD5(x, len) { - /* append padding */ - x[len >> 5] |= 0x80 << len % 32 - x[(((len + 64) >>> 9) << 4) + 14] = len - - var i - var olda - var oldb - var oldc - var oldd - var a = 1732584193 - var b = -271733879 - var c = -1732584194 - var d = 271733878 - - for (i = 0; i < x.length; i += 16) { - olda = a - oldb = b - oldc = c - oldd = d - - a = md5ff(a, b, c, d, x[i], 7, -680876936) - d = md5ff(d, a, b, c, x[i + 1], 12, -389564586) - c = md5ff(c, d, a, b, x[i + 2], 17, 606105819) - b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330) - a = md5ff(a, b, c, d, x[i + 4], 7, -176418897) - d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426) - c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341) - b = md5ff(b, c, d, a, x[i + 7], 22, -45705983) - a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416) - d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417) - c = md5ff(c, d, a, b, x[i + 10], 17, -42063) - b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162) - a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682) - d = md5ff(d, a, b, c, x[i + 13], 12, -40341101) - c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290) - b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329) - - a = md5gg(a, b, c, d, x[i + 1], 5, -165796510) - d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632) - c = md5gg(c, d, a, b, x[i + 11], 14, 643717713) - b = md5gg(b, c, d, a, x[i], 20, -373897302) - a = md5gg(a, b, c, d, x[i + 5], 5, -701558691) - d = md5gg(d, a, b, c, x[i + 10], 9, 38016083) - c = md5gg(c, d, a, b, x[i + 15], 14, -660478335) - b = md5gg(b, c, d, a, x[i + 4], 20, -405537848) - a = md5gg(a, b, c, d, x[i + 9], 5, 568446438) - d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690) - c = md5gg(c, d, a, b, x[i + 3], 14, -187363961) - b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501) - a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467) - d = md5gg(d, a, b, c, x[i + 2], 9, -51403784) - c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473) - b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734) - - a = md5hh(a, b, c, d, x[i + 5], 4, -378558) - d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463) - c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562) - b = md5hh(b, c, d, a, x[i + 14], 23, -35309556) - a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060) - d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353) - c = md5hh(c, d, a, b, x[i + 7], 16, -155497632) - b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640) - a = md5hh(a, b, c, d, x[i + 13], 4, 681279174) - d = md5hh(d, a, b, c, x[i], 11, -358537222) - c = md5hh(c, d, a, b, x[i + 3], 16, -722521979) - b = md5hh(b, c, d, a, x[i + 6], 23, 76029189) - a = md5hh(a, b, c, d, x[i + 9], 4, -640364487) - d = md5hh(d, a, b, c, x[i + 12], 11, -421815835) - c = md5hh(c, d, a, b, x[i + 15], 16, 530742520) - b = md5hh(b, c, d, a, x[i + 2], 23, -995338651) - - a = md5ii(a, b, c, d, x[i], 6, -198630844) - d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415) - c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905) - b = md5ii(b, c, d, a, x[i + 5], 21, -57434055) - a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571) - d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606) - c = md5ii(c, d, a, b, x[i + 10], 15, -1051523) - b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799) - a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359) - d = md5ii(d, a, b, c, x[i + 15], 10, -30611744) - c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380) - b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649) - a = md5ii(a, b, c, d, x[i + 4], 6, -145523070) - d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379) - c = md5ii(c, d, a, b, x[i + 2], 15, 718787259) - b = md5ii(b, c, d, a, x[i + 9], 21, -343485551) - - a = safeAdd(a, olda) - b = safeAdd(b, oldb) - c = safeAdd(c, oldc) - d = safeAdd(d, oldd) - } - return [a, b, c, d] - } - - /** - * Convert an array of little-endian words to a string - * - * @param {Array} input MD5 Array - * @returns {string} MD5 string - */ - function binl2rstr(input) { - var i - var output = '' - var length32 = input.length * 32 - for (i = 0; i < length32; i += 8) { - output += String.fromCharCode((input[i >> 5] >>> i % 32) & 0xff) - } - return output - } - - /** - * Convert a raw string to an array of little-endian words - * Characters >255 have their high-byte silently ignored. - * - * @param {string} input Raw input string - * @returns {Array} Array of little-endian words - */ - function rstr2binl(input) { - var i - var output = [] - output[(input.length >> 2) - 1] = undefined - for (i = 0; i < output.length; i += 1) { - output[i] = 0 - } - var length8 = input.length * 8 - for (i = 0; i < length8; i += 8) { - output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << i % 32 - } - return output - } - - /** - * Calculate the MD5 of a raw string - * - * @param {string} s Input string - * @returns {string} Raw MD5 string - */ - function rstrMD5(s) { - return binl2rstr(binlMD5(rstr2binl(s), s.length * 8)) - } - - /** - * Calculates the HMAC-MD5 of a key and some data (raw strings) - * - * @param {string} key HMAC key - * @param {string} data Raw input string - * @returns {string} Raw MD5 string - */ - function rstrHMACMD5(key, data) { - var i - var bkey = rstr2binl(key) - var ipad = [] - var opad = [] - var hash - ipad[15] = opad[15] = undefined - if (bkey.length > 16) { - bkey = binlMD5(bkey, key.length * 8) - } - for (i = 0; i < 16; i += 1) { - ipad[i] = bkey[i] ^ 0x36363636 - opad[i] = bkey[i] ^ 0x5c5c5c5c - } - hash = binlMD5(ipad.concat(rstr2binl(data)), 512 + data.length * 8) - return binl2rstr(binlMD5(opad.concat(hash), 512 + 128)) - } - - /** - * Convert a raw string to a hex string - * - * @param {string} input Raw input string - * @returns {string} Hex encoded string - */ - function rstr2hex(input) { - var hexTab = '0123456789abcdef' - var output = '' - var x - var i - for (i = 0; i < input.length; i += 1) { - x = input.charCodeAt(i) - output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f) - } - return output - } - - /** - * Encode a string as UTF-8 - * - * @param {string} input Input string - * @returns {string} UTF8 string - */ - function str2rstrUTF8(input) { - return unescape(encodeURIComponent(input)) - } - - /** - * Encodes input string as raw MD5 string - * - * @param {string} s Input string - * @returns {string} Raw MD5 string - */ - function rawMD5(s) { - return rstrMD5(str2rstrUTF8(s)) - } - /** - * Encodes input string as Hex encoded string - * - * @param {string} s Input string - * @returns {string} Hex encoded string - */ - function hexMD5(s) { - return rstr2hex(rawMD5(s)) - } - /** - * Calculates the raw HMAC-MD5 for the given key and data - * - * @param {string} k HMAC key - * @param {string} d Input string - * @returns {string} Raw MD5 string - */ - function rawHMACMD5(k, d) { - return rstrHMACMD5(str2rstrUTF8(k), str2rstrUTF8(d)) - } - /** - * Calculates the Hex encoded HMAC-MD5 for the given key and data - * - * @param {string} k HMAC key - * @param {string} d Input string - * @returns {string} Raw MD5 string - */ - function hexHMACMD5(k, d) { - return rstr2hex(rawHMACMD5(k, d)) - } - - /** - * Calculates MD5 value for a given string. - * If a key is provided, calculates the HMAC-MD5 value. - * Returns a Hex encoded string unless the raw argument is given. - * - * @param {string} string Input string - * @param {string} [key] HMAC key - * @param {boolean} [raw] Raw output switch - * @returns {string} MD5 output - */ - function md5(string, key, raw) { - if (!key) { - if (!raw) { - return hexMD5(string) - } - return rawMD5(string) - } - if (!raw) { - return hexHMACMD5(key, string) - } - return rawHMACMD5(key, string) - } - - return md5; - })(); - /* eslint-enable */ - - class Encoding { - getInfo() { - return { - id: 'Encoding', - name: 'Encoding', - color1: '#6495ed', - color2: '#739fee', - color3: '#83aaf0', - menuIconURI: icon2, - blockIconURI: icon, - blocks: [ - { - opcode: 'encode', - blockType: Scratch.BlockType.REPORTER, - text: 'Encode [string] in [code]', - arguments: { - string: { - type: Scratch.ArgumentType.STRING, - defaultValue: 'apple' - }, - code: { - type: Scratch.ArgumentType.STRING, - menu: 'encode', - defaultValue: 'Base64' - } - } - }, - { - opcode: 'decode', - blockType: Scratch.BlockType.REPORTER, - text: 'Decode [string] with [code]', - arguments: { - string: { - type: Scratch.ArgumentType.STRING, - defaultValue: 'VHVyYm9XYXJw' - }, - code: { - type: Scratch.ArgumentType.STRING, - menu: 'encode', - defaultValue: 'Base64' - } - } - }, - { - opcode: 'hash', - blockType: Scratch.BlockType.REPORTER, - text: 'Hash [string] with [hash]', - arguments: { - string: { - type: Scratch.ArgumentType.STRING, - defaultValue: 'apple' - }, - hash: { - type: Scratch.ArgumentType.STRING, - menu: 'hash', - defaultValue: 'MD5' - } - } - }, - - '---', - - { - opcode: 'Conversioncodes', - blockType: Scratch.BlockType.REPORTER, - text: 'Convert the character [string] to [CodeList]', - arguments: { - string: { - type: Scratch.ArgumentType.STRING, - defaultValue: 'A' - }, - CodeList: { - type: Scratch.ArgumentType.STRING, - menu: 'Code', - defaultValue: 'UNICODE' - } - } - }, - { - opcode: 'Restorecode', - blockType: Scratch.BlockType.REPORTER, - text: '[string] corresponding to the [CodeList] character', - arguments: { - string: { - type: Scratch.ArgumentType.STRING, - defaultValue: '65' - }, - CodeList: { - type: Scratch.ArgumentType.STRING, - menu: 'Code', - defaultValue: 'UNICODE' - } - } - }, - - '---', - - { - opcode: 'Randomstrings', - blockType: Scratch.BlockType.REPORTER, - text: 'Randomly generated [position] character string', - arguments: { - position: { - type: Scratch.ArgumentType.NUMBER, - defaultValue: '8' - } - } - }, - { - opcode: 'Fontgenerationstring', - blockType: Scratch.BlockType.REPORTER, - text: 'Use [wordbank] to generate a random [position] character string', - arguments: { - wordbank: { - type: Scratch.ArgumentType.STRING, - defaultValue: '1234567890' - }, - position: { - type: Scratch.ArgumentType.NUMBER, - defaultValue: '8' - } - } - }, - ], - menus: { - Code: { - acceptReporters: true, - items: [ - { - text: 'Unicode', - value: 'UNICODE' - } - ] - }, - encode: { - acceptReporters: true, - items: [ - { - text: 'Base 64', - value: 'Base64' - }, - 'URL' - ] - }, - hash: { - acceptReporters: true, - items: ['MD5'] - } - } - }; - } - encode({ string, code }) { - string = Scratch.Cast.toString(string); - switch (code) { - case 'Base64': return btoa(string); - case 'URL': return encodeURIComponent(string); - } - return ''; - } - decode({ string, code }) { - string = Scratch.Cast.toString(string); - switch (code) { - case 'Base64': - try { - return atob(string); - } catch (error) { - console.error('invalid base 64', error); - return ''; - } - case 'URL': return decodeURIComponent(string); - } - return ''; - } - hash({ string, hash }) { - string = Scratch.Cast.toString(string); - switch (hash) { - case 'MD5': return md5(string); - } - return ''; - } - Conversioncodes({ string, CodeList }) { - string = Scratch.Cast.toString(string); - switch (CodeList) { - case 'UNICODE': return string.charCodeAt(0); - } - return 0; - } - Restorecode({ string, CodeList}) { - switch (CodeList) { - case 'UNICODE': return String.fromCharCode(string); - } - return ''; - } - Randomstrings({ position }) { - position = Scratch.Cast.toNumber(position) || 32; - let t = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; - let a = t.length; - let string = ''; - for (let i = 0; i < position; i++) { - string += t.charAt(Math.floor(Math.random() * a)); - } - return string; - } - Fontgenerationstring({ wordbank, position }) { - position = Scratch.Cast.toNumber(position) || 32; - let t = String(wordbank); - let a = t.length; - let string = ''; - for (let i = 0; i < position; i++) { - string += t.charAt(Math.floor(Math.random() * a)); - } - return string; - } - } - Scratch.extensions.register(new Encoding()); -})(Scratch); +// Name: Encoding +// ID: Encoding +// Description: Encode and decode strings into their unicode numbers, base 64, or URLs. +// By: -SIPC- + +(function (Scratch) { + "use strict"; + const icon = + "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxMzcuNzk1MDYiIGhlaWdodD0iMTM0LjIzNzA3IiB2aWV3Qm94PSIwLDAsMTM3Ljc5NTA2LDEzNC4yMzcwNyI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTE1Mi44OTU4NiwtMTMwLjM3OTg5KSI+PGcgZGF0YS1wYXBlci1kYXRhPSJ7JnF1b3Q7aXNQYWludGluZ0xheWVyJnF1b3Q7OnRydWV9IiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0iI2ZmZmZmZiIgc3Ryb2tlLXdpZHRoPSIyMCIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiIHN0cm9rZS1saW5lam9pbj0ibWl0ZXIiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWRhc2hhcnJheT0iIiBzdHJva2UtZGFzaG9mZnNldD0iMCIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0xOTkuMzA5MDgsMjE5LjYyMDExdi03OS4yNDAyMmg4MS4zODE4NHY3OS4yNDAyMnoiLz48cGF0aCBkPSJNMTYyLjg5NTg2LDI1NC42MTY5NnYtNzkuMjQwMjJoODEuMzgxODR2NzkuMjQwMjJ6Ii8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6ODcuMTA0MTQwMTg0NTE2NDQ6NDkuNjIwMTA4MzQwNzA3OTYtLT4="; + const icon2 = + "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSI4MS44ODUzOSIgaGVpZ2h0PSI4MC42MDMwNyIgdmlld0JveD0iMCwwLDgxLjg4NTM5LDgwLjYwMzA3Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTk5LjA1NzMsLTEzOS42OTg0NikiPjxnIGRhdGEtcGFwZXItZGF0YT0ieyZxdW90O2lzUGFpbnRpbmdMYXllciZxdW90Ozp0cnVlfSIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2UtbGluZWNhcD0iYnV0dCIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgc3Ryb2tlLW1pdGVybGltaXQ9IjEwIiBzdHJva2UtZGFzaGFycmF5PSIiIHN0cm9rZS1kYXNob2Zmc2V0PSIwIiBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6IG5vcm1hbCI+PHBhdGggZD0iTTI4MC45NDI3LDE4MGMwLDIyLjI1NzkyIC0xOC4zMzA2Nyw0MC4zMDE1NCAtNDAuOTQyNyw0MC4zMDE1NGMtMjIuNjEyMDMsMCAtNDAuOTQyNywtMTguMDQzNjEgLTQwLjk0MjcsLTQwLjMwMTU0YzAsLTIyLjI1NzkyIDE4LjMzMDY3LC00MC4zMDE1NCA0MC45NDI3LC00MC4zMDE1NGMyMi42MTIwMywwIDQwLjk0MjcsMTguMDQzNjEgNDAuOTQyNyw0MC4zMDE1NHoiIGZpbGw9IiM2NDk1ZWQiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIwIi8+PHBhdGggZD0iTTIzMS44MTg3NiwxODcuOTc2MDh2LTI4Ljc2NzE1aDI5LjczNDExdjI4Ljc2NzE1eiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmZmZmIiBzdHJva2Utd2lkdGg9IjYiLz48cGF0aCBkPSJNMjE4LjQ0NzEzLDIwMC43OTEwN3YtMjguNzY3MTVoMjkuNzM0MTF2MjguNzY3MTV6IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iNiIvPjwvZz48L2c+PC9zdmc+PCEtLXJvdGF0aW9uQ2VudGVyOjQwLjk0MjY5NjA1MzgwMTE0OjQwLjMwMTUzNTI2NTQ4NjcwNi0tPg=="; + + /*! + This md5 function is based on https://github.com/blueimp/JavaScript-MD5/blob/master/js/md5.js + which is licensed under: + + MIT License + + Copyright © 2011 Sebastian Tschan, https://blueimp.net + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + /* eslint-disable */ + const md5 = (function () { + /** + * Add integers, wrapping at 2^32. + * This uses 16-bit operations internally to work around bugs in interpreters. + * + * @param {number} x First integer + * @param {number} y Second integer + * @returns {number} Sum + */ + function safeAdd(x, y) { + var lsw = (x & 0xffff) + (y & 0xffff); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xffff); + } + + /** + * Bitwise rotate a 32-bit number to the left. + * + * @param {number} num 32-bit number + * @param {number} cnt Rotation count + * @returns {number} Rotated number + */ + function bitRotateLeft(num, cnt) { + return (num << cnt) | (num >>> (32 - cnt)); + } + + /** + * Basic operation the algorithm uses. + * + * @param {number} q q + * @param {number} a a + * @param {number} b b + * @param {number} x x + * @param {number} s s + * @param {number} t t + * @returns {number} Result + */ + function md5cmn(q, a, b, x, s, t) { + return safeAdd( + bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), + b + ); + } + /** + * Basic operation the algorithm uses. + * + * @param {number} a a + * @param {number} b b + * @param {number} c c + * @param {number} d d + * @param {number} x x + * @param {number} s s + * @param {number} t t + * @returns {number} Result + */ + function md5ff(a, b, c, d, x, s, t) { + return md5cmn((b & c) | (~b & d), a, b, x, s, t); + } + /** + * Basic operation the algorithm uses. + * + * @param {number} a a + * @param {number} b b + * @param {number} c c + * @param {number} d d + * @param {number} x x + * @param {number} s s + * @param {number} t t + * @returns {number} Result + */ + function md5gg(a, b, c, d, x, s, t) { + return md5cmn((b & d) | (c & ~d), a, b, x, s, t); + } + /** + * Basic operation the algorithm uses. + * + * @param {number} a a + * @param {number} b b + * @param {number} c c + * @param {number} d d + * @param {number} x x + * @param {number} s s + * @param {number} t t + * @returns {number} Result + */ + function md5hh(a, b, c, d, x, s, t) { + return md5cmn(b ^ c ^ d, a, b, x, s, t); + } + /** + * Basic operation the algorithm uses. + * + * @param {number} a a + * @param {number} b b + * @param {number} c c + * @param {number} d d + * @param {number} x x + * @param {number} s s + * @param {number} t t + * @returns {number} Result + */ + function md5ii(a, b, c, d, x, s, t) { + return md5cmn(c ^ (b | ~d), a, b, x, s, t); + } + + /** + * Calculate the MD5 of an array of little-endian words, and a bit length. + * + * @param {Array} x Array of little-endian words + * @param {number} len Bit length + * @returns {Array} MD5 Array + */ + function binlMD5(x, len) { + /* append padding */ + x[len >> 5] |= 0x80 << len % 32; + x[(((len + 64) >>> 9) << 4) + 14] = len; + + var i; + var olda; + var oldb; + var oldc; + var oldd; + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + + for (i = 0; i < x.length; i += 16) { + olda = a; + oldb = b; + oldc = c; + oldd = d; + + a = md5ff(a, b, c, d, x[i], 7, -680876936); + d = md5ff(d, a, b, c, x[i + 1], 12, -389564586); + c = md5ff(c, d, a, b, x[i + 2], 17, 606105819); + b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330); + a = md5ff(a, b, c, d, x[i + 4], 7, -176418897); + d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426); + c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341); + b = md5ff(b, c, d, a, x[i + 7], 22, -45705983); + a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416); + d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417); + c = md5ff(c, d, a, b, x[i + 10], 17, -42063); + b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162); + a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682); + d = md5ff(d, a, b, c, x[i + 13], 12, -40341101); + c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290); + b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329); + + a = md5gg(a, b, c, d, x[i + 1], 5, -165796510); + d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632); + c = md5gg(c, d, a, b, x[i + 11], 14, 643717713); + b = md5gg(b, c, d, a, x[i], 20, -373897302); + a = md5gg(a, b, c, d, x[i + 5], 5, -701558691); + d = md5gg(d, a, b, c, x[i + 10], 9, 38016083); + c = md5gg(c, d, a, b, x[i + 15], 14, -660478335); + b = md5gg(b, c, d, a, x[i + 4], 20, -405537848); + a = md5gg(a, b, c, d, x[i + 9], 5, 568446438); + d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690); + c = md5gg(c, d, a, b, x[i + 3], 14, -187363961); + b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501); + a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467); + d = md5gg(d, a, b, c, x[i + 2], 9, -51403784); + c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473); + b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734); + + a = md5hh(a, b, c, d, x[i + 5], 4, -378558); + d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463); + c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562); + b = md5hh(b, c, d, a, x[i + 14], 23, -35309556); + a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060); + d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353); + c = md5hh(c, d, a, b, x[i + 7], 16, -155497632); + b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640); + a = md5hh(a, b, c, d, x[i + 13], 4, 681279174); + d = md5hh(d, a, b, c, x[i], 11, -358537222); + c = md5hh(c, d, a, b, x[i + 3], 16, -722521979); + b = md5hh(b, c, d, a, x[i + 6], 23, 76029189); + a = md5hh(a, b, c, d, x[i + 9], 4, -640364487); + d = md5hh(d, a, b, c, x[i + 12], 11, -421815835); + c = md5hh(c, d, a, b, x[i + 15], 16, 530742520); + b = md5hh(b, c, d, a, x[i + 2], 23, -995338651); + + a = md5ii(a, b, c, d, x[i], 6, -198630844); + d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415); + c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905); + b = md5ii(b, c, d, a, x[i + 5], 21, -57434055); + a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571); + d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606); + c = md5ii(c, d, a, b, x[i + 10], 15, -1051523); + b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799); + a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359); + d = md5ii(d, a, b, c, x[i + 15], 10, -30611744); + c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380); + b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649); + a = md5ii(a, b, c, d, x[i + 4], 6, -145523070); + d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379); + c = md5ii(c, d, a, b, x[i + 2], 15, 718787259); + b = md5ii(b, c, d, a, x[i + 9], 21, -343485551); + + a = safeAdd(a, olda); + b = safeAdd(b, oldb); + c = safeAdd(c, oldc); + d = safeAdd(d, oldd); + } + return [a, b, c, d]; + } + + /** + * Convert an array of little-endian words to a string + * + * @param {Array} input MD5 Array + * @returns {string} MD5 string + */ + function binl2rstr(input) { + var i; + var output = ""; + var length32 = input.length * 32; + for (i = 0; i < length32; i += 8) { + output += String.fromCharCode((input[i >> 5] >>> i % 32) & 0xff); + } + return output; + } + + /** + * Convert a raw string to an array of little-endian words + * Characters >255 have their high-byte silently ignored. + * + * @param {string} input Raw input string + * @returns {Array} Array of little-endian words + */ + function rstr2binl(input) { + var i; + var output = []; + output[(input.length >> 2) - 1] = undefined; + for (i = 0; i < output.length; i += 1) { + output[i] = 0; + } + var length8 = input.length * 8; + for (i = 0; i < length8; i += 8) { + output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << i % 32; + } + return output; + } + + /** + * Calculate the MD5 of a raw string + * + * @param {string} s Input string + * @returns {string} Raw MD5 string + */ + function rstrMD5(s) { + return binl2rstr(binlMD5(rstr2binl(s), s.length * 8)); + } + + /** + * Calculates the HMAC-MD5 of a key and some data (raw strings) + * + * @param {string} key HMAC key + * @param {string} data Raw input string + * @returns {string} Raw MD5 string + */ + function rstrHMACMD5(key, data) { + var i; + var bkey = rstr2binl(key); + var ipad = []; + var opad = []; + var hash; + ipad[15] = opad[15] = undefined; + if (bkey.length > 16) { + bkey = binlMD5(bkey, key.length * 8); + } + for (i = 0; i < 16; i += 1) { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5c5c5c5c; + } + hash = binlMD5(ipad.concat(rstr2binl(data)), 512 + data.length * 8); + return binl2rstr(binlMD5(opad.concat(hash), 512 + 128)); + } + + /** + * Convert a raw string to a hex string + * + * @param {string} input Raw input string + * @returns {string} Hex encoded string + */ + function rstr2hex(input) { + var hexTab = "0123456789abcdef"; + var output = ""; + var x; + var i; + for (i = 0; i < input.length; i += 1) { + x = input.charCodeAt(i); + output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f); + } + return output; + } + + /** + * Encode a string as UTF-8 + * + * @param {string} input Input string + * @returns {string} UTF8 string + */ + function str2rstrUTF8(input) { + return unescape(encodeURIComponent(input)); + } + + /** + * Encodes input string as raw MD5 string + * + * @param {string} s Input string + * @returns {string} Raw MD5 string + */ + function rawMD5(s) { + return rstrMD5(str2rstrUTF8(s)); + } + /** + * Encodes input string as Hex encoded string + * + * @param {string} s Input string + * @returns {string} Hex encoded string + */ + function hexMD5(s) { + return rstr2hex(rawMD5(s)); + } + /** + * Calculates the raw HMAC-MD5 for the given key and data + * + * @param {string} k HMAC key + * @param {string} d Input string + * @returns {string} Raw MD5 string + */ + function rawHMACMD5(k, d) { + return rstrHMACMD5(str2rstrUTF8(k), str2rstrUTF8(d)); + } + /** + * Calculates the Hex encoded HMAC-MD5 for the given key and data + * + * @param {string} k HMAC key + * @param {string} d Input string + * @returns {string} Raw MD5 string + */ + function hexHMACMD5(k, d) { + return rstr2hex(rawHMACMD5(k, d)); + } + + /** + * Calculates MD5 value for a given string. + * If a key is provided, calculates the HMAC-MD5 value. + * Returns a Hex encoded string unless the raw argument is given. + * + * @param {string} string Input string + * @param {string} [key] HMAC key + * @param {boolean} [raw] Raw output switch + * @returns {string} MD5 output + */ + function md5(string, key, raw) { + if (!key) { + if (!raw) { + return hexMD5(string); + } + return rawMD5(string); + } + if (!raw) { + return hexHMACMD5(key, string); + } + return rawHMACMD5(key, string); + } + + return md5; + })(); + /* eslint-enable */ + + class Encoding { + getInfo() { + return { + id: "Encoding", + name: "Encoding", + color1: "#6495ed", + color2: "#739fee", + color3: "#83aaf0", + menuIconURI: icon2, + blockIconURI: icon, + blocks: [ + { + opcode: "encode", + blockType: Scratch.BlockType.REPORTER, + text: "Encode [string] in [code]", + arguments: { + string: { + type: Scratch.ArgumentType.STRING, + defaultValue: "apple", + }, + code: { + type: Scratch.ArgumentType.STRING, + menu: "encode", + defaultValue: "Base64", + }, + }, + }, + { + opcode: "decode", + blockType: Scratch.BlockType.REPORTER, + text: "Decode [string] with [code]", + arguments: { + string: { + type: Scratch.ArgumentType.STRING, + defaultValue: "VHVyYm9XYXJw", + }, + code: { + type: Scratch.ArgumentType.STRING, + menu: "encode", + defaultValue: "Base64", + }, + }, + }, + { + opcode: "hash", + blockType: Scratch.BlockType.REPORTER, + text: "Hash [string] with [hash]", + arguments: { + string: { + type: Scratch.ArgumentType.STRING, + defaultValue: "apple", + }, + hash: { + type: Scratch.ArgumentType.STRING, + menu: "hash", + defaultValue: "MD5", + }, + }, + }, + + "---", + + { + opcode: "Conversioncodes", + blockType: Scratch.BlockType.REPORTER, + text: "Convert the character [string] to [CodeList]", + arguments: { + string: { + type: Scratch.ArgumentType.STRING, + defaultValue: "A", + }, + CodeList: { + type: Scratch.ArgumentType.STRING, + menu: "Code", + defaultValue: "UNICODE", + }, + }, + }, + { + opcode: "Restorecode", + blockType: Scratch.BlockType.REPORTER, + text: "[string] corresponding to the [CodeList] character", + arguments: { + string: { + type: Scratch.ArgumentType.STRING, + defaultValue: "65", + }, + CodeList: { + type: Scratch.ArgumentType.STRING, + menu: "Code", + defaultValue: "UNICODE", + }, + }, + }, + + "---", + + { + opcode: "Randomstrings", + blockType: Scratch.BlockType.REPORTER, + text: "Randomly generated [position] character string", + arguments: { + position: { + type: Scratch.ArgumentType.NUMBER, + defaultValue: "8", + }, + }, + }, + { + opcode: "Fontgenerationstring", + blockType: Scratch.BlockType.REPORTER, + text: "Use [wordbank] to generate a random [position] character string", + arguments: { + wordbank: { + type: Scratch.ArgumentType.STRING, + defaultValue: "1234567890", + }, + position: { + type: Scratch.ArgumentType.NUMBER, + defaultValue: "8", + }, + }, + }, + ], + menus: { + Code: { + acceptReporters: true, + items: [ + { + text: "Unicode", + value: "UNICODE", + }, + ], + }, + encode: { + acceptReporters: true, + items: [ + { + text: "Base 64", + value: "Base64", + }, + "URL", + ], + }, + hash: { + acceptReporters: true, + items: ["MD5"], + }, + }, + }; + } + encode({ string, code }) { + string = Scratch.Cast.toString(string); + switch (code) { + case "Base64": + return btoa(string); + case "URL": + return encodeURIComponent(string); + } + return ""; + } + decode({ string, code }) { + string = Scratch.Cast.toString(string); + switch (code) { + case "Base64": + try { + return atob(string); + } catch (error) { + console.error("invalid base 64", error); + return ""; + } + case "URL": + return decodeURIComponent(string); + } + return ""; + } + hash({ string, hash }) { + string = Scratch.Cast.toString(string); + switch (hash) { + case "MD5": + return md5(string); + } + return ""; + } + Conversioncodes({ string, CodeList }) { + string = Scratch.Cast.toString(string); + switch (CodeList) { + case "UNICODE": + return string.charCodeAt(0); + } + return 0; + } + Restorecode({ string, CodeList }) { + switch (CodeList) { + case "UNICODE": + return String.fromCharCode(string); + } + return ""; + } + Randomstrings({ position }) { + position = Scratch.Cast.toNumber(position) || 32; + let t = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678"; + let a = t.length; + let string = ""; + for (let i = 0; i < position; i++) { + string += t.charAt(Math.floor(Math.random() * a)); + } + return string; + } + Fontgenerationstring({ wordbank, position }) { + position = Scratch.Cast.toNumber(position) || 32; + let t = String(wordbank); + let a = t.length; + let string = ""; + for (let i = 0; i < position; i++) { + string += t.charAt(Math.floor(Math.random() * a)); + } + return string; + } + } + Scratch.extensions.register(new Encoding()); +})(Scratch); diff --git a/extensions/extensions.json b/extensions/extensions.json index 7ec0ffa91d..12abcf6abc 100644 --- a/extensions/extensions.json +++ b/extensions/extensions.json @@ -22,14 +22,18 @@ "obviousAlexC/SensingPlus", "Lily/ClonesPlus", "Lily/LooksPlus", + "Lily/MoreEvents", "NexusKitten/moremotion", + "CubesterYT/WindowControls", "navigator", "battery", "TheShovel/CustomStyles", + "NexusKitten/controlcontrols", "mdwalters/notifications", "XeroName/Deltatime", "ar", "encoding", + "Lily/SoundExpanded", "Lily/TempVariables2", "Lily/MoreTimers", "clouddata-ping", @@ -44,6 +48,7 @@ "-SIPC-/consoles", "ZXMushroom63/searchApi", "TheShovel/ShovelUtils", + "DNin/wake-lock", "Skyhigh173/json", "cs2627883/numericalencoding", "DT/cameracontrols", @@ -57,6 +62,7 @@ "NOname-awa/more-comparisons", "JeremyGamer13/tween", "rixxyx", + "Lily/lmsutils", "qxsck/data-analysis", "qxsck/var-and-list", "vercte/dictionaries", @@ -69,4 +75,4 @@ "gamejolt", "obviousAlexC/newgroundsIO", "Lily/McUtils" -] \ No newline at end of file +] diff --git a/extensions/fetch.js b/extensions/fetch.js index 04646a942e..10ff8e13e4 100644 --- a/extensions/fetch.js +++ b/extensions/fetch.js @@ -1,34 +1,35 @@ // Name: Fetch +// ID: fetch // Description: Make requests to the broader internet. -(function(Scratch) { - 'use strict'; +(function (Scratch) { + "use strict"; class Fetch { - getInfo () { + getInfo() { return { - id: 'fetch', - name: 'Fetch', + id: "fetch", + name: "Fetch", blocks: [ { - opcode: 'get', + opcode: "get", blockType: Scratch.BlockType.REPORTER, - text: 'GET [URL]', + text: "GET [URL]", arguments: { URL: { type: Scratch.ArgumentType.STRING, - defaultValue: 'https://extensions.turbowarp.org/hello.txt' - } - } - } - ] + defaultValue: "https://extensions.turbowarp.org/hello.txt", + }, + }, + }, + ], }; } - get (args) { + get(args) { return Scratch.fetch(args.URL) - .then(r => r.text()) - .catch(() => ''); + .then((r) => r.text()) + .catch(() => ""); } } diff --git a/extensions/files.js b/extensions/files.js index e381f6637e..c4212b34c8 100644 --- a/extensions/files.js +++ b/extensions/files.js @@ -1,178 +1,187 @@ // Name: Files +// ID: files // Description: Read and download files. -(function(Scratch) { - 'use strict'; +(function (Scratch) { + "use strict"; if (!Scratch.extensions.unsandboxed) { - throw new Error('files extension must be run unsandboxed'); + throw new Error("files extension must be run unsandboxed"); } - const MODE_MODAL = 'modal'; - const MODE_IMMEDIATELY_SHOW_SELECTOR = 'selector'; - const MODE_ONLY_SELECTOR = 'only-selector'; - const ALL_MODES = [MODE_MODAL, MODE_IMMEDIATELY_SHOW_SELECTOR, MODE_ONLY_SELECTOR]; + const MODE_MODAL = "modal"; + const MODE_IMMEDIATELY_SHOW_SELECTOR = "selector"; + const MODE_ONLY_SELECTOR = "only-selector"; + const ALL_MODES = [ + MODE_MODAL, + MODE_IMMEDIATELY_SHOW_SELECTOR, + MODE_ONLY_SELECTOR, + ]; let openFileSelectorMode = MODE_MODAL; - const AS_TEXT = 'text'; - const AS_DATA_URL = 'url'; + const AS_TEXT = "text"; + const AS_DATA_URL = "url"; /** * @param {string} accept See MODE_ constants above * @param {string} as See AS_ constants above * @returns {Promise} format given by as parameter */ - const showFilePrompt = (accept, as) => new Promise((_resolve) => { - // We can't reliably show an