diff --git a/src/lib/Documentation/HtmlDocuments.md b/src/lib/Documentation/HtmlDocuments.md
new file mode 100644
index 000000000..c35d0a330
--- /dev/null
+++ b/src/lib/Documentation/HtmlDocuments.md
@@ -0,0 +1,258 @@
+# HTML Documents
+Create HTML webpages and display them on the stage!
+
+## Blocks
+
+```scratch
+create new page named [my-page] :: #e34f26
+```
+
+Create a new blank page
+(Clicking this after you have already made a page with that name will clear all content and reset all display settings)
+
+---
+
+```scratch
+clear content of [my-page] :: #e34f26
+```
+
+Clear HTML content of the page, but keep display settings
+
+---
+
+```scratch
+all pages :: #e34f26 reporter
+```
+
+Returns an array of all created pages
+
+---
+
+```scratch
+[my-page] html [code v] :: #e34f26 reporter
+```
+
+Based on the dropdown:
+* **code:** Returns the html code of the page
+* **preview:** Returns a visual preview of the page
+Both options return the same value, just in different visual forms
+
+---
+
+```scratch
+display page [my-page] :: #e34f26
+```
+
+Display the page on the stage
+
+---
+
+```scratch
+hide page [my-page] :: #e34f26
+```
+
+Hide the page
+
+---
+
+```scratch
+hide all pages :: #e34f26
+```
+
+Hide all pages
+
+---
+
+```scratch
+current pages displayed:: #e34f26 reporter
+```
+
+Returns an array of all pages currently displayed on the stage
+
+---
+
+```scratch
+add [div v] if [new-el] to [my-page] {
+}:: #e34f26
+```
+
+Add an element to the page. These elements can have children, like ```div``` and ```h1```
+
+---
+
+```scratch
+add [input v] if [new-el] to [my-page]:: #e34f26
+```
+
+Add a self-closing element to the page. These elements cannot have children, like ```input``` and ```img```
+
+---
+
+```scratch
+add [hr v] to [my-page]:: #e34f26
+```
+
+Add a self-closing unidentified element to the page. Only ```hr``` and ```br``` elements can be unidentified. iF an element is unidentified it cannot be removed, styled, or accessed alone.
+
+---
+
+```scratch
+remove element with id [new-el] from [my-page]:: #e34f26
+```
+
+Remove an element from the page. If it has children, they will be removed, too.
+
+---
+
+```scratch
+set [class v] of [new-el] to [new-class] in [my-page]:: #e34f26
+```
+
+Set an attribute for an element
+
+---
+
+```scratch
+change [new-el] in [my-page] to {
+}:: #e34f26
+```
+
+Change the inner HTML, or content, of an element. Not used for self-closing elements.
+
+---
+
+```scratch
+set (color v) for [id v] [new-el] to [red] in [my-page]:: #2965f1
+```
+
+Set a CSS property
+
+---
+
+```scratch
+get (color v) for [id v] [new-el] in [my-page]:: #2965f1 reporter
+```
+
+Get the defined property of an element
+
+---
+
+```scratch
+clear styling for [id v] [new-el] in [my-page]:: #2965f1
+```
+
+Clear the styling for a certain selector
+
+---
+
+```scratch
+clear styling of [my-page]:: #2965f1
+```
+
+Clear all styling in the page
+
+---
+
+```scratch
+when listener for [new-el] activated in [my-page]:: #e34f26 hat
+```
+
+Hat is started when the event listener is triggered
+
+---
+
+```scratch
+add event listener [click v] for [new-el] n [my-page]:: #e34f26
+```
+
+Add an event listener for an element. Use this after the element has been added.
+
+---
+
+```scratch
+remove event listener from [new-el] in [my-page]:: #e34f26
+```
+
+Remove the event listener from the element
+
+---
+
+```scratch
+listener of [new-el] in [my-page]:: #e34f26 reporter
+```
+
+Returns the current listener attached to the element
+
+---
+
+```scratch
+all elements in [my-page]:: #e34f26 reporter
+```
+
+Returns an array of all elements in the page
+
+---
+
+```scratch
+[inner HTML v] in [my-page]:: #e34f26 reporter
+```
+
+Returns a value based on the dropdown:
+* **inner HTML:** The html contnt inside of the element
+* **text content:** The text content inside of the element
+* **children:** An array of all elements inside of the element
+* **parent element:** The element's parent
+* **tag name:** The element's tag name
+* **value:** The value of the element, if an input
+* **checked:** The status of an element, if a checkbox input
+* **class list:** The classes of an element
+* **offset width:** Thw width of an element when displayed
+* **offset height:** Thw height of an element when displayed
+* **scroll width:** Thw scroll width of an element when displayed
+* **scroll height:** Thw scroll height of an element when displayed
+* **disabled:** If the inout is disabled
+
+---
+
+```scratch
+move [my-page] to x: [50] y: [50]:: #e34f26
+```
+
+Move the page to a different location on the stage
+
+---
+
+```scratch
+resize [my-page] to x: [50] y: [50]:: #e34f26
+```
+
+Resize the page
+
+---
+
+```scratch
+set [my-page] display settings to stage size :: #e34f26
+```
+
+Automatically set the display settings to fit the stage size
+
+---
+
+```scratch
+[x position v] of [my-page] :: #e34f26 reporter
+```
+
+Returns the page display settings. Based on the dropdown:
+* **x position:** The x position of the page
+* **y position:** The y position of the page
+* **width:** The width of the page
+* **height:** The height of the page
+
+---
+
+```scratch
+[width v] of the stage:: #e34f26 reporter
+```
+
+Get the width or height of the stage in pixels
+
diff --git a/src/lib/Documentation/pages.js b/src/lib/Documentation/pages.js
index 1fbb683a2..9ba6d59a7 100644
--- a/src/lib/Documentation/pages.js
+++ b/src/lib/Documentation/pages.js
@@ -42,6 +42,8 @@ import ProjectInterfaces from "./ProjectInterfaces.md?raw";
// Date Format V2
import DateFormatV2 from "./DateFormatV2.md?raw";
+// HTML Documents
+import HTMLDocuments from "./HtmlDocuments.md?raw";
// ODE
import ODE from "./ODE.md?raw";
@@ -84,6 +86,10 @@ export default {
// Project Interfaces
"ProjectInterfaces": ProjectInterfaces,
+ "DateFormatV2": DateFormatV2
+
+ // HTML Documents
+ "HTMLDocuments": HTMLDocuments,
"DateFormatV2": DateFormatV2,
// ODE
diff --git a/src/lib/extensions.js b/src/lib/extensions.js
index 77b887d29..7dc2ee88b 100644
--- a/src/lib/extensions.js
+++ b/src/lib/extensions.js
@@ -338,6 +338,15 @@ export default [
banner: "NamelessCat/corsproxy.avif",
creator: "NamelessCat",
},
+ {
+ name: "HTML Documents",
+ description: "Create HTML webpages and display them on the stage.",
+ code: "ScrTwPm/htmldocuments.js",
+ banner: "ScrTwPm/htmldocs.png",
+ creator: "ScrTwPm",
+ documentation: "HTMLDocuments",
+ isGitHub: true,
+ },
{
name: "Translation Keys",
description: "Use translation keys in your projects for multi-language support",
diff --git a/static/extensions/ScrTwPm/htmldocuments.js b/static/extensions/ScrTwPm/htmldocuments.js
new file mode 100644
index 000000000..37a76d50a
--- /dev/null
+++ b/static/extensions/ScrTwPm/htmldocuments.js
@@ -0,0 +1,2633 @@
+(function (Scratch) {
+ 'use strict';
+
+ const dom = new DOMParser()
+ const toAString = new XMLSerializer()
+
+ class PrevHTML {
+ constructor(html) {
+ this.customId = "scrtwpmhtmldocuments_prevhtml"
+ this.html = html
+ }
+ toString() {
+ return this.html
+ }
+ toReporterContent() {
+ let wrap = document.createElement('iframe')
+ wrap.setAttribute("srcdoc", this.html)
+ wrap.style.height = "95%"
+ wrap.style.width = "95%"
+ return wrap;
+ }
+ }
+
+
+ class HtmlCode {
+ constructor(html) {
+ this.customId = "scrtwpmhtmldocuments_htmlcode"
+ this.html = html
+ }
+ toString() {
+ return this.html
+ }
+ toReporterContent() {
+ let wrap = document.createElement('div')
+ wrap.innerText = this.html
+ // wrap.style.backgroundColor = "black"
+ // wrap.style.color = "white"
+ // wrap.style.border = "solid 10px black"
+ wrap.style.border = "solid 10px #ffffff00"
+ wrap.style.backgroundColor = "#ffffff00"
+ wrap.style.boxSizing = "border-box"
+ wrap.style.fontFamily = "Consolas"
+ wrap.style.height = "fit-content"
+ wrap.style.width = "fit-content"
+ return wrap;
+ }
+ }
+
+ class HTMLDocuments {
+ constructor(runtime) {
+ Scratch.vm.runtime.registerSerializer(
+ "scrtwpmhtmldocuments_htmlcode",
+ instance => ({ html: instance.html }),
+ instance => {
+ return (new HtmlCode(instance.html))
+ }
+ );
+ Scratch.vm.runtime.registerSerializer(
+ "scrtwpmhtmldocuments_prevhtml",
+ instance => ({ html: instance.html }),
+ instance => {
+ return (new PrevHTML(instance.html))
+ }
+ );
+
+ this.pages = new Map()
+ this.changePx = [
+ "block-size",
+ "border-block-end-width",
+ "border-block-start-width",
+ "border-block-width",
+ "border-bottom-left-radius",
+ "border-bottom-right-radius",
+ "border-bottom-width",
+ "border-image-outset",
+ "border-image-width",
+ "border-inline-end-width",
+ "border-inline-start-width",
+ "border-inline-width",
+ "border-left-width",
+ "border-right-width",
+ "border-spacing",
+ "border-top-left-radius",
+ "border-top-right-radius",
+ "border-top-width",
+ "border-width",
+ "bottom",
+ "column-gap",
+ "column-rule-width",
+ "column-width",
+ "contain-intrinsic-block-size",
+ "contain-intrinsic-height",
+ "contain-intrinsic-inline-size",
+ "contain-intrinsic-size",
+ "contain-intrinsic-width",
+ "cx",
+ "cy",
+ "font-size",
+ "gap",
+ "grid-auto-columns",
+ "grid-auto-rows",
+ "grid-template-columns",
+ "grid-template-rows",
+ "height",
+ "inline-size",
+ "left",
+ "letter-spacing",
+ "margin-block-end",
+ "margin-block-start",
+ "margin-block",
+ "margin-bottom",
+ "margin-inline-end",
+ "margin-inline-start",
+ "margin-inline",
+ "margin-left",
+ "margin-right",
+ "margin-top",
+ "max-block-size",
+ "max-height",
+ "max-inline-size",
+ "max-width",
+ "min-block-size",
+ "min-height",
+ "min-inline-size",
+ "min-width",
+ "object-position",
+ "offset-distance",
+ "offset-path",
+ "outline-offset",
+ "outline-width",
+ "padding-block-end",
+ "padding-block-start",
+ "padding-block",
+ "padding-bottom",
+ "padding-inline-end",
+ "padding-inline-start",
+ "padding-inline",
+ "padding-left",
+ "padding-right",
+ "padding-top",
+ "perspective",
+ "perspective-origin",
+ "r",
+ "right",
+ "row-gap",
+ "rx",
+ "ry",
+ "scroll-margin-bottom",
+ "scroll-margin-left",
+ "scroll-margin-right",
+ "scroll-margin-top",
+ "scroll-padding-bottom",
+ "scroll-padding-left",
+ "scroll-padding-right",
+ "scroll-padding-top",
+ "shape-margin",
+ "tab-size",
+ "text-decoration-thickness",
+ "text-indent",
+ "top",
+ "transform-origin",
+ "translate",
+ "width",
+ "word-spacing",
+ "x",
+ "y"
+ ]
+ this.viewing = []
+ this.runtime = runtime
+ }
+ getInfo() {
+ return {
+ id: 'scrtwpmhtmldocuments',
+ name: 'HTML Documents',
+ color1: '#e34f26',
+ menuIconURI: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADAklEQVRYR8WWX0hTURzHf+e6tRmaUdOGmwaRPQwpwwrESHroIbI/DxXZS6nR36fqoQejVRASQYQE/UMTKk2yhzYpqIdCKgRfIrBSNDLT/mgMN3W72909cc68h7uzP/dOXfu83O/vbNzz4dzzu/cgyDBIPf+PnWswK9KM3T1A586IAA4Hoej5t4URyDKyqEk4FLkKgoQLn30VSJ63QK41i2UtfL/C9GqqPjSSf6yhmOSMCFhvvdpnsK/szJiAzdVvRgiJsQLVJRhQ1BCYWt6xHA+LxcJyIrxHHfSqPAKlAwjRAtuLx8FgjrrjvAWkEHhPrqNRU+B37dYLoYnRS2xg/gI44L6Ngu4mWmgK/D272+Ef+NzHBjgB+doJCPV9YDUht1B/H2oKEPhWVAvgsWEInj/IaoLeNsQyhqk/Ms1zFiCIdZUsE/QKSH4Z/JORW6cm0PwW1J0xV4HpiTDIEl0KsHcN6hcwXn0KQr6V1TwamzCmBQEJYHd90S+QtasODHvqWc2TqoB57cZBy5VHJbTQI4ADPjC3fWQ1T6oCy50tl7M3bHbSQo+AFvweWHKnL2rP8AI2V/9qhNAQLdIicPcTywReQN0BhFiBHatmQDBkswGuFcXDFeSDzupkAjgUBN+pMpp1C4ztX/9E9k/vZQO8wJEtAPLsjtYQCL53Q6D1HM26BXwPmxyTHTcTvo7FhgMAP0dYnUzA17AN8PhoJOsVIPD7gH8bKpAlzn7shJzT99iYGm99CTuzpUWAkKwNlQ0oiRj8ntjvAEGXAGAMphtdAHnL2JBCIgHv8VIAOTIpew3PWYBHDoOptYdGRUB8+QDEzkaaeZTlx5IIRS+GF0BABb8J46EILCoth4LG9v8roExOWHrxfmtOeWUtG0jHCvg9YZDoeTcWW0dvBVqcF3l2s8QVUBirKZPlqZmk/8kpENhJJy5YBmtzd7thRWH0UWqWpDdXg32eqtGaTW/4Y3s8UG4e2Np6tf+YigCP5/qZ79Ovu4pIxqEA2N1DVcho7FZ+18s/56x1ML2/L6YAAAAQZGVCR0UxN0MwMUY2QkVFNEM0NUbEID3rAAAAAElFTkSuQmCC',
+ // blockIconURI: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADAklEQVRYR8WWX0hTURzHf+e6tRmaUdOGmwaRPQwpwwrESHroIbI/DxXZS6nR36fqoQejVRASQYQE/UMTKk2yhzYpqIdCKgRfIrBSNDLT/mgMN3W72909cc68h7uzP/dOXfu83O/vbNzz4dzzu/cgyDBIPf+PnWswK9KM3T1A586IAA4Hoej5t4URyDKyqEk4FLkKgoQLn30VSJ63QK41i2UtfL/C9GqqPjSSf6yhmOSMCFhvvdpnsK/szJiAzdVvRgiJsQLVJRhQ1BCYWt6xHA+LxcJyIrxHHfSqPAKlAwjRAtuLx8FgjrrjvAWkEHhPrqNRU+B37dYLoYnRS2xg/gI44L6Ngu4mWmgK/D272+Ef+NzHBjgB+doJCPV9YDUht1B/H2oKEPhWVAvgsWEInj/IaoLeNsQyhqk/Ms1zFiCIdZUsE/QKSH4Z/JORW6cm0PwW1J0xV4HpiTDIEl0KsHcN6hcwXn0KQr6V1TwamzCmBQEJYHd90S+QtasODHvqWc2TqoB57cZBy5VHJbTQI4ADPjC3fWQ1T6oCy50tl7M3bHbSQo+AFvweWHKnL2rP8AI2V/9qhNAQLdIicPcTywReQN0BhFiBHatmQDBkswGuFcXDFeSDzupkAjgUBN+pMpp1C4ztX/9E9k/vZQO8wJEtAPLsjtYQCL53Q6D1HM26BXwPmxyTHTcTvo7FhgMAP0dYnUzA17AN8PhoJOsVIPD7gH8bKpAlzn7shJzT99iYGm99CTuzpUWAkKwNlQ0oiRj8ntjvAEGXAGAMphtdAHnL2JBCIgHv8VIAOTIpew3PWYBHDoOptYdGRUB8+QDEzkaaeZTlx5IIRS+GF0BABb8J46EILCoth4LG9v8roExOWHrxfmtOeWUtG0jHCvg9YZDoeTcWW0dvBVqcF3l2s8QVUBirKZPlqZmk/8kpENhJJy5YBmtzd7thRWH0UWqWpDdXg32eqtGaTW/4Y3s8UG4e2Np6tf+YigCP5/qZ79Ovu4pIxqEA2N1DVcho7FZ+18s/56x1ML2/L6YAAAAQZGVCR0UxN0MwMUY2QkVFNEM0NUbEID3rAAAAAElFTkSuQmCC',
+ blocks: [
+
+ {
+ opcode: 'createPage',
+ blockType: Scratch.BlockType.COMMAND,
+ text: 'create new page named [PAGE]',
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ }
+ }
+ },
+ {
+ opcode: 'deletePage',
+ blockType: Scratch.BlockType.COMMAND,
+ text: 'delete page named [PAGE]',
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ }
+ }
+ },
+ {
+ opcode: 'clearPage',
+ blockType: Scratch.BlockType.COMMAND,
+ text: 'clear content of [PAGE]',
+ // notchAccepts: "htmldocuments-coolshape",
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ }
+ }
+ },
+ {
+ opcode: 'duplicatePage',
+ blockType: Scratch.BlockType.COMMAND,
+ text: 'duplicate page [PAGE] as [PAGE2]',
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ PAGE2: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page-2"
+ }
+ }
+ },
+ {
+ opcode: 'allPages',
+ blockType: Scratch.BlockType.REPORTER,
+ text: 'all pages',
+ disableMonitor: true,
+ },
+ {
+ opcode: 'getHTML',
+ blockType: Scratch.BlockType.REPORTER,
+ text: '[PAGE] html [GET]',
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ GET: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "get"
+ }
+ }
+ },
+ // {
+ // opcode: 'get',
+ // blockType: Scratch.BlockType.REPORTER,
+ // text: 'get',
+ // },
+ { blockType: Scratch.BlockType.LABEL, text: "Render HTML" },
+ {
+ opcode: 'displayPage',
+ blockType: Scratch.BlockType.COMMAND,
+ text: 'display page [PAGE]',
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ }
+ }
+ },
+ {
+ opcode: 'hidePage',
+ blockType: Scratch.BlockType.COMMAND,
+ text: 'hide page [PAGE]',
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ }
+ }
+ },
+ {
+ opcode: 'hidePageAll',
+ blockType: Scratch.BlockType.COMMAND,
+ text: 'hide all pages',
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ }
+ }
+ },
+ {
+ opcode: 'current',
+ blockType: Scratch.BlockType.REPORTER,
+ text: 'current pages displayed',
+ disableMonitor: true
+ },
+ { blockType: Scratch.BlockType.LABEL, text: "Element Management" },
+ {
+ opcode: 'nestEl',
+ blockType: Scratch.BlockType.CONDITIONAL,
+ text: 'add [EL] id [ID] to [PAGE]',
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ ID: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "new-el"
+ },
+ EL: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "nest",
+ defaultValue: "div"
+ }
+ }
+ },
+ {
+ opcode: 'noNestEl',
+ blockType: Scratch.BlockType.COMMAND,
+ text: 'add [EL] id [ID] to [PAGE]',
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ ID: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "new-el"
+ },
+ EL: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "nonest",
+ defaultValue: "input"
+ },
+ }
+ },
+ {
+ opcode: 'hrbr',
+ blockType: Scratch.BlockType.COMMAND,
+ text: 'add [EL] to [PAGE]',
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ EL: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "hrbr",
+ defaultValue: "hr"
+ },
+ }
+ },
+ {
+ opcode: 'text',
+ blockType: Scratch.BlockType.COMMAND,
+ text: 'add text [TEXT] to [PAGE]',
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ TEXT: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "Hello World!"
+ },
+ }
+ },
+ {
+ opcode: 'removeEl',
+ blockType: Scratch.BlockType.COMMAND,
+ text: 'remove element with id [ID] from [PAGE]',
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ ID: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "new-el"
+ },
+ }
+ },
+ {
+ opcode: 'setAttr',
+ blockType: Scratch.BlockType.COMMAND,
+ text: 'set [ATTR] of [ID] to [VAL] in [PAGE]',
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ ID: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "new-el"
+ },
+ VAL: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "new-class"
+ },
+ ATTR: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "class",
+ menu: "attr"
+ },
+ }
+
+ },
+
+
+ {
+ opcode: 'changeHTML',
+ blockType: Scratch.BlockType.CONDITIONAL,
+ text: 'change [ID] in [PAGE] to',
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ ID: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "new-el"
+ },
+ EL: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "nonest",
+ defaultValue: "input"
+ },
+ }
+ },
+
+ { blockType: Scratch.BlockType.LABEL, text: "CSS Styles" },
+
+ // {
+ // opcode: 'styleEl',
+ // blockType: Scratch.BlockType.CONDITIONAL,
+ // text: 'add style to [PAGE]',
+ // branchCount: 1,
+ // // notchAccepts: "htmldocuments-coolshape",
+ // branches: [
+ // { accepts: "htmldocuments-coolshape2" },
+ // ],
+ // arguments: {
+ // PAGE: {
+ // type: Scratch.ArgumentType.STRING,
+ // defaultValue: "my-page"
+ // },
+ // }
+ // },
+ {
+ opcode: 'property',
+ blockType: Scratch.BlockType.COMMAND,
+ text: 'set [PROPERTY] for [TYPE] [NAME] to [VALUE] in [PAGE]',
+ // notchAccepts: "htmldocuments-coolshape2",
+ arguments: {
+ PROPERTY: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "properties",
+ defaultValue: "color"
+ },
+ TYPE: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "types"
+ },
+ NAME: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "new-el"
+ },
+ VALUE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "red",
+ },
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page",
+ },
+ },
+ color1: "#2965f1",
+ color2: "#255bd9",
+ color3: "#255bd9",
+ // blockIconURI: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABOElEQVRYR2NkGGDAiGy/mu+T/3AODcGtzTJwe4e+A5gleGGeIQj+vvgMpqkaAqMOIDYEYMEPAiRFwdF57HA2NiAiIgJnYwNaaZ/A9KBxwP9/f/7c3qrACnUf/UMAOfhBgGAUEALoaSDG7i9DVYwgnI8eBTR3wLVZfGCLYIAkB4AAqaGAHgJUdwC1cwFdHVA55yPDxlMQPw6IA2C+H9oOIATwJUL0+AcBktMACCAnROukn3A2CIw6gJQQQA9+EMAQIBQFMACLCpgD0AsgEKCpA2AAX0FElgNAAN0RA+4AZIDuGOQQSOv9yHDkJqafKA4BfAA9EWIDI88ByCUfMiA6CkCA2GgARcH/f/8Z/r36AhfDBrBZDgJYBdGBqveDf4xMLESphQFcFqIDohShA1yhQ6ylyIBkDdQGAJCZFDDWLCQVAAAAEGRlQkc3OTYwRjM3Mzk1MUE0MTk1IBDo1wAAAABJRU5ErkJggg=='
+ },
+
+ {
+ opcode: 'getProperty',
+ blockType: Scratch.BlockType.REPORTER,
+ text: 'get [PROPERTY] of [TYPE] [NAME] in [PAGE]',
+ arguments: {
+ PROPERTY: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "properties",
+ defaultValue: "color"
+ },
+ TYPE: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "types"
+ },
+ NAME: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "new-el"
+ },
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page",
+ },
+ },
+ color1: "#2965f1",
+ color2: "#255bd9",
+ color3: "#255bd9",
+ // blockIconURI: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABOElEQVRYR2NkGGDAiGy/mu+T/3AODcGtzTJwe4e+A5gleGGeIQj+vvgMpqkaAqMOIDYEYMEPAiRFwdF57HA2NiAiIgJnYwNaaZ/A9KBxwP9/f/7c3qrACnUf/UMAOfhBgGAUEALoaSDG7i9DVYwgnI8eBTR3wLVZfGCLYIAkB4AAqaGAHgJUdwC1cwFdHVA55yPDxlMQPw6IA2C+H9oOIATwJUL0+AcBktMACCAnROukn3A2CIw6gJQQQA9+EMAQIBQFMACLCpgD0AsgEKCpA2AAX0FElgNAAN0RA+4AZIDuGOQQSOv9yHDkJqafKA4BfAA9EWIDI88ByCUfMiA6CkCA2GgARcH/f/8Z/r36AhfDBrBZDgJYBdGBqveDf4xMLESphQFcFqIDohShA1yhQ6ylyIBkDdQGAJCZFDDWLCQVAAAAEGRlQkc3OTYwRjM3Mzk1MUE0MTk1IBDo1wAAAABJRU5ErkJggg=='
+ },
+
+ {
+ opcode: 'removeStyle',
+ blockType: Scratch.BlockType.COMMAND,
+ text: 'clear styling for [TYPE] [NAME] in [PAGE]',
+ arguments: {
+ TYPE: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "types"
+ },
+ NAME: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "new-el"
+ },
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ },
+ color1: "#2965f1",
+ color2: "#255bd9",
+ color3: "#255bd9",
+ // blockIconURI: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABOElEQVRYR2NkGGDAiGy/mu+T/3AODcGtzTJwe4e+A5gleGGeIQj+vvgMpqkaAqMOIDYEYMEPAiRFwdF57HA2NiAiIgJnYwNaaZ/A9KBxwP9/f/7c3qrACnUf/UMAOfhBgGAUEALoaSDG7i9DVYwgnI8eBTR3wLVZfGCLYIAkB4AAqaGAHgJUdwC1cwFdHVA55yPDxlMQPw6IA2C+H9oOIATwJUL0+AcBktMACCAnROukn3A2CIw6gJQQQA9+EMAQIBQFMACLCpgD0AsgEKCpA2AAX0FElgNAAN0RA+4AZIDuGOQQSOv9yHDkJqafKA4BfAA9EWIDI88ByCUfMiA6CkCA2GgARcH/f/8Z/r36AhfDBrBZDgJYBdGBqveDf4xMLESphQFcFqIDohShA1yhQ6ylyIBkDdQGAJCZFDDWLCQVAAAAEGRlQkc3OTYwRjM3Mzk1MUE0MTk1IBDo1wAAAABJRU5ErkJggg=='
+ },
+
+ {
+ opcode: 'removeAllStyle',
+ blockType: Scratch.BlockType.COMMAND,
+ text: 'clear styling of [PAGE]',
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ },
+ color1: "#2965f1",
+ color2: "#255bd9",
+ color3: "#255bd9",
+ // blockIconURI: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABOElEQVRYR2NkGGDAiGy/mu+T/3AODcGtzTJwe4e+A5gleGGeIQj+vvgMpqkaAqMOIDYEYMEPAiRFwdF57HA2NiAiIgJnYwNaaZ/A9KBxwP9/f/7c3qrACnUf/UMAOfhBgGAUEALoaSDG7i9DVYwgnI8eBTR3wLVZfGCLYIAkB4AAqaGAHgJUdwC1cwFdHVA55yPDxlMQPw6IA2C+H9oOIATwJUL0+AcBktMACCAnROukn3A2CIw6gJQQQA9+EMAQIBQFMACLCpgD0AsgEKCpA2AAX0FElgNAAN0RA+4AZIDuGOQQSOv9yHDkJqafKA4BfAA9EWIDI88ByCUfMiA6CkCA2GgARcH/f/8Z/r36AhfDBrBZDgJYBdGBqveDf4xMLESphQFcFqIDohShA1yhQ6ylyIBkDdQGAJCZFDDWLCQVAAAAEGRlQkc3OTYwRjM3Mzk1MUE0MTk1IBDo1wAAAABJRU5ErkJggg=='
+ },
+
+ { blockType: Scratch.BlockType.LABEL, text: "Event Listeners" },
+
+
+ {
+ opcode: 'eve',
+ blockType: Scratch.BlockType.HAT,
+ text: 'when listener for [ID] activated in [PAGE]',
+ isEdgeActivated: false,
+ arguments: {
+ ID: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "new-el",
+ // defaultValue: '{"Hello":"Item 1", "Bye":"Item 2"}'
+ },
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ }
+ },
+ {
+ opcode: 'addeve',
+ blockType: Scratch.BlockType.COMMAND,
+ text: 'add event listener [EVE] for [ID] in [PAGE]',
+ arguments: {
+ EVE: { type: Scratch.ArgumentType.STRING, menu: 'eves', defaultValue: 'click' },
+ ID: { type: Scratch.ArgumentType.STRING, defaultValue: 'new-el' },
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ }
+ },
+ {
+ opcode: 'remeve',
+ blockType: Scratch.BlockType.COMMAND,
+ text: 'remove event listener from [ID] in [PAGE]',
+ arguments: {
+ EVE: { type: Scratch.ArgumentType.STRING, menu: 'eves', defaultValue: 'click' },
+ ID: { type: Scratch.ArgumentType.STRING, defaultValue: 'new-el' },
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ }
+ },
+ {
+ opcode: 'listener',
+ blockType: Scratch.BlockType.REPORTER,
+ text: 'listener of [ID] in [PAGE]',
+ arguments: {
+ ID: { type: Scratch.ArgumentType.STRING, defaultValue: 'new-el' },
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ }
+ },
+
+ { blockType: Scratch.BlockType.LABEL, text: "Webpage Data" },
+
+ {
+ opcode: 'allEls',
+ blockType: Scratch.BlockType.REPORTER,
+ text: 'all elements in [PAGE]',
+ arguments: {
+ EVE: { type: Scratch.ArgumentType.STRING, menu: 'eves', defaultValue: 'click' },
+ ID: { type: Scratch.ArgumentType.STRING, defaultValue: 'new-el' },
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ }
+ },
+ {
+ opcode: 'dataEl',
+ blockType: Scratch.BlockType.REPORTER,
+ text: '[OPTS] of [ID] in [PAGE]',
+ arguments: {
+ EVE: { type: Scratch.ArgumentType.STRING, menu: 'eves', defaultValue: 'click' },
+ ID: { type: Scratch.ArgumentType.STRING, defaultValue: 'new-el' },
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ OPTS: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "opts"
+ },
+ }
+ },
+
+
+ { blockType: Scratch.BlockType.LABEL, text: "Webpage Positioning" },
+
+ {
+ opcode: 'movePage',
+ blockType: Scratch.BlockType.COMMAND,
+ text: 'move [PAGE] to x: [X] y: [Y]',
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ X: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "50"
+ },
+ Y: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "50"
+ },
+ }
+ },
+ {
+ opcode: 'resizePage',
+ blockType: Scratch.BlockType.COMMAND,
+ text: 'resize [PAGE] to x: [X] y: [Y]',
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ X: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "300"
+ },
+ Y: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "200"
+ },
+ }
+ },
+ {
+ opcode: 'resetDefault',
+ blockType: Scratch.BlockType.COMMAND,
+ text: 'set [PAGE] display settings to stage size',
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ X: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "300"
+ },
+ Y: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: "200"
+ },
+ }
+ },
+ {
+ opcode: 'ppos',
+ blockType: Scratch.BlockType.REPORTER,
+ text: '[AXIS] of [PAGE]',
+ disableMonitor: true,
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ AXIS: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "axis"
+ },
+ }
+ },
+ {
+ opcode: 'spw',
+ blockType: Scratch.BlockType.REPORTER,
+ text: '[WH] of the stage',
+ disableMonitor: true,
+ arguments: {
+ WH: {
+ type: Scratch.ArgumentType.STRING,
+ menu: "wh"
+ },
+ }
+ },
+
+ ],
+ menus: {
+ nonest: {
+ acceptReporters: false,
+ items:
+ [
+ "area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "script", "source", "textarea", "track", "wbr"
+ ]
+ },
+ hrbr: {
+ acceptReporters: false,
+ items:
+ [
+ "br", "hr"
+ ]
+ },
+ nest: {
+ acceptReporters: false,
+ items:
+ [
+ "a", "abbr", "address", "article", "aside", "audio", "b", "bdi", "bdo", "blockquote", "button", "canvas", "caption", "cite", "code", "colgroup", "data", "datalist", "dd", "del", "details", "dfn", "dialog", "div", "dl", "dt", "em", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "header", "hgroup", "i", "iframe", "ins", "kbd", "label", "legend", "li", "main", "map", "mark", "menu", "meter", "nav", "object", "ol", "optgroup", "option", "output", "p", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "section", "select", "slot", "small", "span", "strong", "sub", "summary", "sup", "table", "tbody", "td", "template", "tfoot", "th", "thead", "time", "tr", "u", "ul", "var", "video"
+ ]
+ },
+ types: {
+ acceptReporters: false,
+ items: [
+ { text: "id", value: "#" },
+ { text: "class", value: "." },
+ { text: "tag", value: "" },
+ // {text: "animation", value: "@keyframes "},
+ ]
+ },
+ get: {
+ acceptReporters: false,
+ items: [
+ { text: "code", value: "code" },
+ { text: "preview", value: "preview" },
+ ]
+ },
+ sel: {
+ acceptReporters: false,
+ items: [
+ { text: "declaration", value: "dec" },
+ { text: "computed style", value: "comp" },
+ ]
+ },
+ opts: {
+ acceptReporters: false,
+ items: [
+ { text: "inner HTML", value: "innerHTML" },
+ { text: "text content", value: "textContent" },
+ { text: "children", value: "children" },
+ { text: "parent element", value: "parentElement" },
+ { text: "tag name", value: "tagName" },
+ // { text: "style", value: "style" },
+ { text: "value", value: "value" },
+ { text: "checked", value: "checked" },
+ { text: "class list", value: "classList" },
+ { text: "offset width", value: "offsetWidth" },
+ { text: "offset height", value: "offsetHeight" },
+ { text: "scroll width", value: "scrollWidth" },
+ { text: "scroll height", value: "scrollHeight" },
+ { text: "disabled", value: "disabled" },
+ // { text: "all data of", value: "all" },
+ ]
+ },
+ properties: {
+ acceptReporters: true,
+ items: [
+
+ 'align-content', 'align-items', 'align-self', 'all', 'animation', 'animation-delay', 'animation-direction', 'animation-duration', 'animation-fill-mode', 'animation-iter-count', 'animation-name', 'animation-play-state', 'animation-timing-fn', 'backface-visibility', 'background', 'background-attachment', 'background-blend-mode', 'background-clip', 'background-color', 'background-image', 'background-origin', 'background-position', 'background-repeat', 'background-size', 'border', 'border-bottom', 'border-bottom-color', 'border-bottom-left-rad', 'border-bottom-right-ra', 'border-bottom-style', 'border-bottom-width', 'border-collapse', 'border-color', 'border-image', 'border-image-outset', 'border-image-repeat', 'border-image-slice', 'border-image-source', 'border-image-width', 'border-left', 'border-left-color', 'border-left-style', 'border-left-width', 'border-radius', 'border-right', 'border-right-color', 'border-right-style', 'border-right-width', 'border-spacing', 'border-style', 'border-top', 'border-top-color', 'border-top-left-radius', 'border-top-right-radius', 'border-top-style', 'border-top-width', 'border-width', 'bottom', 'box-decoration-break', 'box-shadow', 'box-sizing', 'caption-side', 'caret-color',
+ 'clear', 'clip', 'clip-path', 'color', 'column-count', 'column-fill', 'column-gap', 'column-rule', 'column-rule-color', 'column-rule-style', 'column-rule-width', 'column-span', 'column-width', 'columns', 'content', 'counter-increment', 'counter-reset', 'cursor', 'direction', 'display', 'empty-cells', 'filter', 'flex', 'flex-basis', 'flex-direction', 'flex-flow', 'flex-grow', 'flex-shrink', 'flex-wrap', 'float', 'font',
+ 'font-family', 'font-kerning', 'font-size', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'gap', 'grid', 'grid-area', 'grid-auto-columns', 'grid-auto-flow', 'grid-auto-rows', 'grid-column', 'grid-column-end', 'grid-column-gap', 'grid-column-start', 'grid-gap', 'grid-row', 'grid-row-end', 'grid-row-gap', 'grid-row-start', 'grid-template', 'grid-template-areas', 'grid-template-columns', 'grid-template-rows', 'height', 'hyphens',
+ 'justify-content',
+ 'left', 'letter-spacing', 'line-height', 'list-style', 'list-style-image', 'list-style-position', 'list-style-type', 'margin', 'margin-bottom', 'margin-left', 'margin-right', 'margin-top', 'max-height', 'max-width',
+ 'min-height', 'min-width', 'object-fit', 'object-position', 'opacity', 'order', 'outline', 'outline-color', 'outline-offset', 'outline-style', 'outline-width', 'overflow', 'overflow-x', 'overflow-y', 'padding', 'padding-bottom', 'padding-left', 'padding-right', 'padding-top', 'page-break-after', 'page-break-before', 'page-break-inside', 'perspective', 'perspective-origin', 'pointer-events', 'position', 'quotes', 'right', 'row-gap', 'scroll-behavior', 'table-layout', 'text-align', 'text-align-last', 'text-decoration', 'text-decoration-color', 'text-decoration-line', 'text-decoration-style', 'text-indent', 'text-justify', 'text-overflow', 'text-shadow', 'text-transform', 'top', 'transform', 'transform-origin', 'transform-style', 'transition', 'transition-delay', 'transition-duration', 'transition-property', 'transition-timing-fn', 'user-select', 'vertical-align', 'visibility', 'white-space', 'width', 'word-break', 'word-spacing', 'word-wrap', 'writing-mode', 'z-index'
+
+ ]
+ },
+ jsproperties: {
+ acceptReporters: false,
+ items: [
+ { "text": "align-content", "value": "alignContent" }, { "text": "align-items", "value": "alignItems" }, { "text": "align-self", "value": "alignSelf" }, { "text": "all", "value": "all" }, { "text": "animation", "value": "animation" }, { "text": "animation-delay", "value": "animationDelay" }, { "text": "animation-direction", "value": "animationDirection" }, { "text": "animation-duration", "value": "animationDuration" }, { "text": "animation-fill-mode", "value": "animationFillMode" }, { "text": "animation-iter-count", "value": "animationIterCount" }, { "text": "animation-name", "value": "animationName" }, { "text": "animation-play-state", "value": "animationPlayState" }, { "text": "animation-timing-fn", "value": "animationTimingFn" }, { "text": "backface-visibility", "value": "backfaceVisibility" }, { "text": "background", "value": "background" }, { "text": "background-attachment", "value": "backgroundAttachment" }, { "text": "background-blend-mode", "value": "backgroundBlendMode" }, { "text": "background-clip", "value": "backgroundClip" }, { "text": "background-color", "value": "backgroundColor" }, { "text": "background-image", "value": "backgroundImage" }, { "text": "background-origin", "value": "backgroundOrigin" }, { "text": "background-position", "value": "backgroundPosition" }, { "text": "background-repeat", "value": "backgroundRepeat" }, { "text": "background-size", "value": "backgroundSize" }, { "text": "border", "value": "border" }, { "text": "border-bottom", "value": "borderBottom" }, { "text": "border-bottom-color", "value": "borderBottomColor" }, { "text": "border-bottom-left-rad", "value": "borderBottomLeftRad" }, { "text": "border-bottom-right-ra", "value": "borderBottomRightRa" }, { "text": "border-bottom-style", "value": "borderBottomStyle" }, { "text": "border-bottom-width", "value": "borderBottomWidth" }, { "text": "border-collapse", "value": "borderCollapse" }, { "text": "border-color", "value": "borderColor" }, { "text": "border-image", "value": "borderImage" }, { "text": "border-image-outset", "value": "borderImageOutset" }, { "text": "border-image-repeat", "value": "borderImageRepeat" }, { "text": "border-image-slice", "value": "borderImageSlice" }, { "text": "border-image-source", "value": "borderImageSource" }, { "text": "border-image-width", "value": "borderImageWidth" }, { "text": "border-left", "value": "borderLeft" }, { "text": "border-left-color", "value": "borderLeftColor" }, { "text": "border-left-style", "value": "borderLeftStyle" }, { "text": "border-left-width", "value": "borderLeftWidth" }, { "text": "border-radius", "value": "borderRadius" }, { "text": "border-right", "value": "borderRight" }, { "text": "border-right-color", "value": "borderRightColor" }, { "text": "border-right-style", "value": "borderRightStyle" }, { "text": "border-right-width", "value": "borderRightWidth" }, { "text": "border-spacing", "value": "borderSpacing" }, { "text": "border-style", "value": "borderStyle" }, { "text": "border-top", "value": "borderTop" }, { "text": "border-top-color", "value": "borderTopColor" }, { "text": "border-top-left-radius", "value": "borderTopLeftRadius" }, { "text": "border-top-right-radius", "value": "borderTopRightRadius" }, { "text": "border-top-style", "value": "borderTopStyle" }, { "text": "border-top-width", "value": "borderTopWidth" }, { "text": "border-width", "value": "borderWidth" }, { "text": "bottom", "value": "bottom" }, { "text": "box-decoration-break", "value": "boxDecorationBreak" }, { "text": "box-shadow", "value": "boxShadow" }, { "text": "box-sizing", "value": "boxSizing" }, { "text": "caption-side", "value": "captionSide" }, { "text": "caret-color", "value": "caretColor" }, { "text": "clear", "value": "clear" }, { "text": "clip", "value": "clip" }, { "text": "clip-path", "value": "clipPath" }, { "text": "color", "value": "color" }, { "text": "column-count", "value": "columnCount" }, { "text": "column-fill", "value": "columnFill" }, { "text": "column-gap", "value": "columnGap" }, { "text": "column-rule", "value": "columnRule" }, { "text": "column-rule-color", "value": "columnRuleColor" }, { "text": "column-rule-style", "value": "columnRuleStyle" }, { "text": "column-rule-width", "value": "columnRuleWidth" }, { "text": "column-span", "value": "columnSpan" }, { "text": "column-width", "value": "columnWidth" }, { "text": "columns", "value": "columns" }, { "text": "content", "value": "content" }, { "text": "counter-increment", "value": "counterIncrement" }, { "text": "counter-reset", "value": "counterReset" }, { "text": "cursor", "value": "cursor" }, { "text": "direction", "value": "direction" }, { "text": "display", "value": "display" }, { "text": "empty-cells", "value": "emptyCells" }, { "text": "filter", "value": "filter" }, { "text": "flex", "value": "flex" }, { "text": "flex-basis", "value": "flexBasis" }, { "text": "flex-direction", "value": "flexDirection" }, { "text": "flex-flow", "value": "flexFlow" }, { "text": "flex-grow", "value": "flexGrow" }, { "text": "flex-shrink", "value": "flexShrink" }, { "text": "flex-wrap", "value": "flexWrap" }, { "text": "float", "value": "float" }, { "text": "font", "value": "font" }, { "text": "font-family", "value": "fontFamily" }, { "text": "font-kerning", "value": "fontKerning" }, { "text": "font-size", "value": "fontSize" }, { "text": "font-stretch", "value": "fontStretch" }, { "text": "font-style", "value": "fontStyle" }, { "text": "font-variant", "value": "fontVariant" }, { "text": "font-weight", "value": "fontWeight" }, { "text": "gap", "value": "gap" }, { "text": "grid", "value": "grid" }, { "text": "grid-area", "value": "gridArea" }, { "text": "grid-auto-columns", "value": "gridAutoColumns" }, { "text": "grid-auto-flow", "value": "gridAutoFlow" }, { "text": "grid-auto-rows", "value": "gridAutoRows" }, { "text": "grid-column", "value": "gridColumn" }, { "text": "grid-column-end", "value": "gridColumnEnd" }, { "text": "grid-column-gap", "value": "gridColumnGap" }, { "text": "grid-column-start", "value": "gridColumnStart" }, { "text": "grid-gap", "value": "gridGap" }, { "text": "grid-row", "value": "gridRow" }, { "text": "grid-row-end", "value": "gridRowEnd" }, { "text": "grid-row-gap", "value": "gridRowGap" }, { "text": "grid-row-start", "value": "gridRowStart" }, { "text": "grid-template", "value": "gridTemplate" }, { "text": "grid-template-areas", "value": "gridTemplateAreas" }, { "text": "grid-template-columns", "value": "gridTemplateColumns" }, { "text": "grid-template-rows", "value": "gridTemplateRows" }, { "text": "height", "value": "height" }, { "text": "hyphens", "value": "hyphens" }, { "text": "justify-content", "value": "justifyContent" }, { "text": "left", "value": "left" }, { "text": "letter-spacing", "value": "letterSpacing" }, { "text": "line-height", "value": "lineHeight" }, { "text": "list-style", "value": "listStyle" }, { "text": "list-style-image", "value": "listStyleImage" }, { "text": "list-style-position", "value": "listStylePosition" }, { "text": "list-style-type", "value": "listStyleType" }, { "text": "margin", "value": "margin" }, { "text": "margin-bottom", "value": "marginBottom" }, { "text": "margin-left", "value": "marginLeft" }, { "text": "margin-right", "value": "marginRight" }, { "text": "margin-top", "value": "marginTop" }, { "text": "max-height", "value": "maxHeight" }, { "text": "max-width", "value": "maxWidth" }, { "text": "min-height", "value": "minHeight" }, { "text": "min-width", "value": "minWidth" }, { "text": "object-fit", "value": "objectFit" }, { "text": "object-position", "value": "objectPosition" }, { "text": "opacity", "value": "opacity" }, { "text": "order", "value": "order" }, { "text": "outline", "value": "outline" }, { "text": "outline-color", "value": "outlineColor" }, { "text": "outline-offset", "value": "outlineOffset" }, { "text": "outline-style", "value": "outlineStyle" }, { "text": "outline-width", "value": "outlineWidth" }, { "text": "overflow", "value": "overflow" }, { "text": "overflow-x", "value": "overflowX" }, { "text": "overflow-y", "value": "overflowY" }, { "text": "padding", "value": "padding" }, { "text": "padding-bottom", "value": "paddingBottom" }, { "text": "padding-left", "value": "paddingLeft" }, { "text": "padding-right", "value": "paddingRight" }, { "text": "padding-top", "value": "paddingTop" }, { "text": "page-break-after", "value": "pageBreakAfter" }, { "text": "page-break-before", "value": "pageBreakBefore" }, { "text": "page-break-inside", "value": "pageBreakInside" }, { "text": "perspective", "value": "perspective" }, { "text": "perspective-origin", "value": "perspectiveOrigin" }, { "text": "pointer-events", "value": "pointerEvents" }, { "text": "position", "value": "position" }, { "text": "quotes", "value": "quotes" }, { "text": "right", "value": "right" }, { "text": "row-gap", "value": "rowGap" }, { "text": "scroll-behavior", "value": "scrollBehavior" }, { "text": "table-layout", "value": "tableLayout" }, { "text": "text-align", "value": "textAlign" }, { "text": "text-align-last", "value": "textAlignLast" }, { "text": "text-decoration", "value": "textDecoration" }, { "text": "text-decoration-color", "value": "textDecorationColor" }, { "text": "text-decoration-line", "value": "textDecorationLine" }, { "text": "text-decoration-style", "value": "textDecorationStyle" }, { "text": "text-indent", "value": "textIndent" }, { "text": "text-justify", "value": "textJustify" }, { "text": "text-overflow", "value": "textOverflow" }, { "text": "text-shadow", "value": "textShadow" }, { "text": "text-transform", "value": "textTransform" }, { "text": "top", "value": "top" }, { "text": "transform", "value": "transform" }, { "text": "transform-origin", "value": "transformOrigin" }, { "text": "transform-style", "value": "transformStyle" }, { "text": "transition", "value": "transition" }, { "text": "transition-delay", "value": "transitionDelay" }, { "text": "transition-duration", "value": "transitionDuration" }, { "text": "transition-property", "value": "transitionProperty" }, { "text": "transition-timing-fn", "value": "transitionTimingFn" }, { "text": "user-select", "value": "userSelect" }, { "text": "vertical-align", "value": "verticalAlign" }, { "text": "visibility", "value": "visibility" }, { "text": "white-space", "value": "whiteSpace" }, { "text": "width", "value": "width" }, { "text": "word-break", "value": "wordBreak" }, { "text": "word-spacing", "value": "wordSpacing" }, { "text": "word-wrap", "value": "wordWrap" }, { "text": "writing-mode", "value": "writingMode" }, { "text": "z-index", "value": "zIndex" }
+ ]
+ },
+ axis: {
+ acceptReporters: false,
+ items: [
+ { text: "x position", value: "x" }, { text: "y position", value: "y" }, "width", "height"
+ ]
+ },
+ wh: {
+ acceptReporters: false,
+ items: [
+ "width", "height"
+ ]
+ },
+ eves: {
+ acceptReporters: false,
+ items: [
+ // Mouse & Pointer
+ "click", "dblclick", "mousedown", "mouseup", "mouseenter",
+ "mouseleave", "mousemove", "mouseover", "mouseout", "contextmenu", "wheel",
+
+ // Keyboard
+ "keydown", "keyup", "keypress",
+
+ // Form & Input
+ "submit", "input", "change", "focus", "blur", "reset", "invalid",
+
+ // Window & Document
+ "load", "DOMContentLoaded", "beforeunload", "unload", "resize",
+ "scroll", "error", "visibilitychange",
+
+ // Drag & Drop
+ "dragstart", "drag", "dragenter", "dragover", "dragleave", "drop", "dragend",
+
+ // Mobile & Media
+ "touchstart", "touchmove", "touchend", "play"
+ ]
+ },
+ attr: {
+ acceptReporters: false,
+ items: [
+ "accesskey", "autocapitalize", "autofocus", "class", "contenteditable",
+ "dir", "draggable", "enterkeyhint", "hidden", "inert", "inputmode",
+ "lang", "nonce", "part", "popover", "role", "slot", "style", "tabindex",
+ "title", "translate", "virtualkeyboardpolicy",
+ "accept", "accept-charset", "action", "align", "allow", "alt", "as",
+ "async", "autocomplete", "autoplay", "background", "bgcolor", "border",
+ "capture", "charset", "checked", "cite", "color", "cols", "colspan",
+ "content", "controls", "coords", "crossorigin", "csp", "data", "datetime",
+ "decoding", "default", "defer", "disabled", "download", "enctype", "for",
+ "form", "formaction", "formenctype", "formmethod", "formnovalidate",
+ "formtarget", "headers", "height", "high", "href", "hreflang", "http-equiv",
+ "imagesizes", "imagesrcset", "integrity", "ismap", "kind", "label", "list",
+ "loading", "loop", "low", "max", "maxlength", "min", "minlength", "media",
+ "method", "multiple", "muted", "name", "novalidate", "open", "optimum",
+ "pattern", "ping", "placeholder", "playsinline", "poster", "preload",
+ "readonly", "referrerpolicy", "rel", "required", "reversed", "rows",
+ "rowspan", "sandbox", "scope", "selected", "shape", "size", "sizes",
+ "span", "src", "srcdoc", "srclang", "srcset", "start", "step", "target",
+ "type", "usemap", "value", "width", "wrap"
+ ]
+
+ }
+
+ }
+ };
+ }
+
+ createPage(args, util) {
+ if (!this.pages.has(args.PAGE)) {
+ if (args.PAGE !== "") {
+ this.pages.set(args.PAGE, new Map().set("data", new Map().set("x", 5).set("y", 5).set("width", 470).set("height", 350)).set("code", dom.parseFromString("", 'text/html')).set("eves", new Map()))
+
+ } else {
+ throw new Error("Name cannot be empty")
+ }
+ }
+ }
+
+ duplicatePage(args, util) {
+ if ((this.pages).has(args.PAGE)) {
+ if (!this.pages.has(args.PAGE2)) {
+ if (args.PAGE !== "" && args.PAGE2 !== "") {
+ this.pages.set(args.PAGE2, new Map().set("data", this.pages.get(args.PAGE).get("data")).set("code", this.pages.get(args.PAGE).get("code")).set("eves", this.pages.get(args.PAGE).get("eves")))
+
+ } else {
+ throw new Error("Name cannot be empty")
+ }
+ } else {
+ throw new Error("Page already exists")
+ }
+ } else {
+ throw new Error("Page does not exist")
+ }
+ }
+ clearPage(args, util) {
+
+ // //if (Object.keys(this.pages).includes(args.PAGE)) {
+ // let pages = this.pages
+
+ this.pages.get(args.PAGE)?.set("code", dom.parseFromString("", 'text/html'))
+ this.pages.get(args.PAGE)?.set("eves", new Map())
+ // }
+ }
+ deletePage(args, util) {
+
+ //if (Object.keys(this.pages).includes(args.PAGE)) {
+ this.pages.delete(args.PAGE)
+ if (this.viewing.includes(args.PAGE)) {
+ const elements = document.querySelectorAll(`.htmlpage.display${args.PAGE}`);
+ elements.forEach(el => el.remove());
+ }
+
+ // }
+ }
+ allPages() {
+ return (JSON.stringify(Array.from(this.pages.keys())))
+ }
+ get() {
+ if (Object.keys(this.pages).includes("my-page")) {
+ return (JSON.stringify(this.pages["my-page"].code))
+ }
+ }
+
+
+ displayPage(args, util) {
+ return new Promise((resolve, reject) => {
+ try {
+ const elements = document.querySelectorAll(`.htmlpage.display${args.PAGE}`);
+ elements.forEach(el => el.remove());
+ const el = document.createElement("iframe");
+ el.setAttribute("srcdoc", toAString.serializeToString(this.pages.get(args.PAGE).get("code")))
+ el.setAttribute("class", `htmlpage display${args.PAGE}`)
+ el.style.position = 'absolute';
+ el.style.pointerEvents = 'auto';
+ el.style.zIndex = '10';
+ el.style.left = `${this.pages.get(args.PAGE).get("data").get("x")}px`;
+ el.style.top = `${this.pages.get(args.PAGE).get("data").get("y")}px`;
+ el.setAttribute("width", `${this.pages.get(args.PAGE).get("data").get("width")}px`)
+ el.setAttribute("height", `${this.pages.get(args.PAGE).get("data").get("height")}px`)
+ el.style.border = "1px solid black"
+ const container = Scratch.renderer.canvas.parentElement;
+ container.appendChild(el);
+ if (!this.viewing.includes(args.PAGE)) {
+ this.viewing.push(args.PAGE)
+ }
+ el.onload = () => {
+ for (const [key, value] of this.pages.get(args.PAGE)?.get("eves")) {
+ //
+
+ el.contentDocument.querySelector(`#${key}`).addEventListener(value, () => {
+ const triggerText = String(key);
+ const triggerTexta = String(args.PAGE);
+
+ const targetOpcode = 'scrtwpmhtmldocuments_eve';
+ const vm = Scratch.vm;
+
+ vm.runtime.targets.forEach(target => {
+ const blocks = target.blocks;
+ const scripts = blocks.getScripts();
+
+ scripts.forEach(rootBlockId => {
+ const block = blocks.getBlock(rootBlockId);
+
+ if (block && block.opcode === targetOpcode) {
+ let hatValue = '';
+ let hatValuea = '';
+
+ if (block.fields && block.fields.ID && block.fields.PAGE) {
+ hatValue = block.fields.ID.value;
+ hatValuea = block.fields.PAGE.value;
+ }
+ else if (block.inputs && block.inputs.ID && block.inputs.PAGE) {
+ const inputID = block.inputs.ID;
+ const inputPAGE = block.inputs.PAGE;
+
+ const shadowBlockID = blocks.getBlock(inputID.shadow);
+ const shadowBlockPAGE = blocks.getBlock(inputPAGE.shadow);
+
+ if (shadowBlockID && shadowBlockID.fields) {
+ const fieldKey = Object.keys(shadowBlockID.fields)[0];
+ hatValue = shadowBlockID.fields[fieldKey]?.value || '';
+ }
+ if (shadowBlockPAGE && shadowBlockPAGE.fields) {
+ const fieldKeya = Object.keys(shadowBlockPAGE.fields)[0];
+ hatValuea = shadowBlockPAGE.fields[fieldKeya]?.value || '';
+ }
+ }
+
+ if (hatValue === triggerText && hatValuea === triggerTexta) {
+ vm.runtime._pushThread(rootBlockId, target);
+ }
+ }
+ });
+ });
+ }
+
+ )
+
+
+ }
+ }
+ } catch (error) { }
+ resolve();
+ });
+ }
+
+ hidePageAll(args, util) {
+ const elements = document.querySelectorAll('.htmlpage');
+ elements.forEach(el => el.remove());
+ this.viewing = []
+ }
+
+ hidePage(args, util) {
+ const elements = document.querySelectorAll(`.htmlpage.display${args.PAGE}`);
+ elements.forEach(el => el.remove());
+ this.viewing.splice(this.viewing.indexOf(args.PAGE), 1)
+ }
+
+ noNestEl(args, util) {
+ if ((this.pages).has(args.PAGE)) {
+ if (args.ID !== "") {
+ if (!this.pages.get(args.PAGE)?.get("code").querySelector(`#${args.ID}`)) {
+
+
+ //
+ const blockContainer = util.thread.blockContainer;
+ const currentBlockId = util.thread.peekStack();
+ const currentBlock = blockContainer.getBlock(currentBlockId);
+ const target = util.thread.target;
+
+
+
+ let parentBlockId = ""
+ let parentBlock = ""
+
+
+ let childId = currentBlockId;
+ let blockId = target.blocks.getBlock(currentBlockId)?.parent;
+
+ let nest = ""
+ const loopOpcodes = [
+ 'scrtwpmhtmldocuments_nestEl',
+ 'scrtwpmhtmldocuments_changeHTML',
+ ];
+ // let blockId = currentBlockId;
+
+
+
+
+
+
+ while (blockId) {
+ const parentBlock = target.blocks.getBlock(blockId);
+ // if (!parentBlock) break;
+
+
+ if (loopOpcodes.includes(parentBlock.opcode)) {
+
+
+ const substackId = parentBlock.inputs?.SUBSTACK?.block;
+ // const substack2Id = parentBlock.inputs?.SUBSTACK2?.block;
+
+ //
+
+ if (substackId === childId) {
+
+
+ parentBlockId = blockId;
+
+ const pB = blockContainer.getBlock(parentBlockId)
+
+ if (pB && pB.el.bin.includes(currentBlockId) && pB.el.page === args.PAGE) {
+ nest = pB.el.el
+
+
+
+ let body = this.pages.get(args.PAGE)?.get("code").querySelector(nest === "" ? "body" : `#${nest}`)
+ //
+ let el = document.createElement(args.EL)
+ el.setAttribute("id", `${args.ID}`)
+ el = body.appendChild(el)
+
+ return
+ }
+
+ }
+
+
+ if (substackId) {
+ let checkId = substackId;
+ while (checkId) {
+ if (checkId === childId) {
+ }
+ checkId = target.blocks.getBlock(checkId)?.next;
+ }
+ }
+
+
+ // if (substack2Id) {
+ // let checkId = substack2Id;
+ // while (checkId) {
+ // if (checkId === childId) return blockId;
+ // checkId = target.blocks.getBlock(checkId)?.next;
+ // }
+ // }
+ }
+
+
+ childId = blockId;
+ blockId = parentBlock.parent;
+ }
+
+
+
+ //
+ //
+ // if (currentBlock && currentBlock.parent){
+ // const parentBlockId = currentBlock.parent;
+ // const parentBlock = blockContainer.getBlock(parentBlockId)
+ // if(parentBlock && parentBlock.el){
+ //
+ // nest = parentBlock.el.el
+ // }
+ // }
+ // this.pages.get(args.PAGE)?.set("code", toAString.serializeToString(this.pages.get(args.PAGE).get("code")))
+ //
+
+
+ // this.pages.get(args.PAGE)?.set("code", `${this.pages.get(args.PAGE)?.get("code")}<${args.EL} id="${args.ID}">`)
+ // this.pages.get(args.PAGE)?.set("code", document.createRange().createContextualFragment(this.pages.get(args.PAGE).get("code")))
+ //
+
+
+
+ let body = this.pages.get(args.PAGE)?.get("code").querySelector(nest === "" ? "body" : `#${nest}`)
+ //
+ let el = document.createElement(args.EL)
+ el.setAttribute("id", `${args.ID}`)
+ el = body.appendChild(el)
+ } else {
+ throw new Error(`Only one element with the id "${args.ID}" can exist in the document`)
+ }
+ } else {
+ throw new Error(`Element must have an id`)
+
+ // this.pages.get(args.PAGE)?.set("code", toAString.serializeToString(this.pages.get(args.PAGE).get("code")))
+ // this.pages.get(args.PAGE)?.set("code", `${this.pages.get(args.PAGE)?.get("code")}<${args.EL} >`)
+ }
+ }
+ }
+
+ hrbr(args, util) {
+ if ((this.pages).has(args.PAGE)) {
+ // if (args.ID !== "") {
+ // if (!this.pages.get(args.PAGE)?.get("code").querySelector(`#${args.ID}`)) {
+
+
+ //
+ const blockContainer = util.thread.blockContainer;
+ const currentBlockId = util.thread.peekStack();
+ const currentBlock = blockContainer.getBlock(currentBlockId);
+ const target = util.thread.target;
+
+
+
+ let parentBlockId = ""
+ let parentBlock = ""
+
+
+ let childId = currentBlockId;
+ let blockId = target.blocks.getBlock(currentBlockId)?.parent;
+
+ let nest = ""
+ const loopOpcodes = [
+ 'scrtwpmhtmldocuments_nestEl',
+ 'scrtwpmhtmldocuments_changeHTML',
+ ];
+ // let blockId = currentBlockId;
+
+
+
+
+
+
+ while (blockId) {
+ const parentBlock = target.blocks.getBlock(blockId);
+ // if (!parentBlock) break;
+
+
+ if (loopOpcodes.includes(parentBlock.opcode)) {
+
+
+ const substackId = parentBlock.inputs?.SUBSTACK?.block;
+ // const substack2Id = parentBlock.inputs?.SUBSTACK2?.block;
+
+ //
+
+ if (substackId === childId) {
+
+
+ parentBlockId = blockId;
+
+ const pB = blockContainer.getBlock(parentBlockId)
+
+ if (pB && pB.el.bin.includes(currentBlockId) && pB.el.page === args.PAGE) {
+ nest = pB.el.el
+
+
+
+ let body = this.pages.get(args.PAGE)?.get("code").querySelector(nest === "" ? "body" : `#${nest}`)
+ //
+ let el = document.createElement(args.EL)
+ el = body.appendChild(el)
+
+ return
+ }
+
+ }
+
+
+ if (substackId) {
+ let checkId = substackId;
+ while (checkId) {
+ if (checkId === childId) {
+ }
+ checkId = target.blocks.getBlock(checkId)?.next;
+ }
+ }
+
+
+ // if (substack2Id) {
+ // let checkId = substack2Id;
+ // while (checkId) {
+ // if (checkId === childId) return blockId;
+ // checkId = target.blocks.getBlock(checkId)?.next;
+ // }
+ // }
+ }
+
+
+ childId = blockId;
+ blockId = parentBlock.parent;
+ }
+
+
+
+ //
+ //
+ // if (currentBlock && currentBlock.parent){
+ // const parentBlockId = currentBlock.parent;
+ // const parentBlock = blockContainer.getBlock(parentBlockId)
+ // if(parentBlock && parentBlock.el){
+ //
+ // nest = parentBlock.el.el
+ // }
+ // }
+ // this.pages.get(args.PAGE)?.set("code", toAString.serializeToString(this.pages.get(args.PAGE).get("code")))
+ //
+
+
+ // this.pages.get(args.PAGE)?.set("code", `${this.pages.get(args.PAGE)?.get("code")}<${args.EL} id="${args.ID}">`)
+ // this.pages.get(args.PAGE)?.set("code", document.createRange().createContextualFragment(this.pages.get(args.PAGE).get("code")))
+ //
+
+
+
+ let body = this.pages.get(args.PAGE)?.get("code").querySelector(nest === "" ? "body" : `#${nest}`)
+ //
+ let el = document.createElement(args.EL)
+ el = body.appendChild(el)
+ // } else {
+ // throw new Error(`Only one element with the id "${args.ID}" can exist in the document`)
+ // }
+ // } else {
+ // throw new Error(`Element must have an id`)
+
+ // // this.pages.get(args.PAGE)?.set("code", toAString.serializeToString(this.pages.get(args.PAGE).get("code")))
+ // // this.pages.get(args.PAGE)?.set("code", `${this.pages.get(args.PAGE)?.get("code")}<${args.EL} >`)
+ // }
+ }
+ }
+
+ nestEl(args, util) {
+ if ((this.pages).has(args.PAGE)) {
+ if (util.stackFrame.startedBranch) {
+ // this.pages.get(args.PAGE)?.set("code", `${this.pages.get(args.PAGE)?.get("code")}${args.EL}>`)
+ // this.pages.get(args.PAGE)?.set("code", document.createRange().createContextualFragment(this.pages.get(args.PAGE).get("code")))
+ //
+ util.stackFrame.startedBranch = false;
+ return;
+ }
+ if (args.ID !== "") {
+ if (!this.pages.get(args.PAGE)?.get("code").querySelector(`#${args.ID}`)) {
+ const blockContainer = util.thread.blockContainer;
+ const currentBlockId = util.thread.peekStack(); //
+ const currentBlock = blockContainer.getBlock(currentBlockId);
+ if (!currentBlock) {
+ let body = this.pages.get(args.PAGE)?.get("code").querySelector("body")
+ //
+ let el = document.createElement(args.EL)
+ el.setAttribute("id", `${args.ID}`)
+ el = body.appendChild(el)
+ return
+ }
+ let blocksInLoop = []
+ function fn() {
+
+ const bc = util.thread.target.blocks;
+
+ const substackInput = currentBlock.inputs.SUBSTACK;
+ if (!substackInput || !substackInput.block) {
+
+ return;
+ }
+
+ const branchBlockIds = [];
+ // Helper function to recursively collect block IDs down the chain
+ const scanChain = (startBlockId) => {
+ let nextBlockId = startBlockId;
+
+ while (nextBlockId) {
+ branchBlockIds.push(nextBlockId);
+
+ const blockInfo = blockContainer.getBlock(nextBlockId);
+ if (!blockInfo) break;
+
+ // If this block is ANOTHER loop, dive inside its mouth first!
+ if (blockInfo.inputs && blockInfo.inputs.SUBSTACK && blockInfo.inputs.SUBSTACK.block) {
+ scanChain(blockInfo.inputs.SUBSTACK.block);
+ }
+
+ // If it's an If-Else block, it might also have a SUBSTACK2 mouth
+ if (blockInfo.inputs && blockInfo.inputs.SUBSTACK2 && blockInfo.inputs.SUBSTACK2.block) {
+ scanChain(blockInfo.inputs.SUBSTACK2.block);
+ }
+
+ // Move to the next block directly underneath
+ nextBlockId = blockInfo.next;
+ }
+ };
+
+ // Start scanning from the very first block inside your custom loop
+ scanChain(substackInput.block);
+
+ blocksInLoop = branchBlockIds
+
+ }
+
+ fn()
+
+ if (currentBlock) {
+ currentBlock.el = {
+ el: args.ID,
+ compiledScope: 'global',
+ bin: blocksInLoop,
+ page: args.PAGE
+ };
+ }
+ //
+
+ const target = util.thread.target;
+
+
+ let parentBlockId = ""
+ let parentBlock = ""
+ let nest = ""
+ const loopOpcodes = [
+ 'scrtwpmhtmldocuments_nestEl',
+ 'scrtwpmhtmldocuments_changeHTML',
+ ];
+ let blockId = currentBlockId;
+ while (blockId) {
+ const block = target.blocks.getBlock(blockId);
+ if (!block) break;
+
+ if (loopOpcodes.includes(block.opcode) && block !== currentBlock) {
+ // blockId = block.parent;
+
+ parentBlockId = block;
+
+ parentBlock = blockContainer.getBlock(parentBlockId)
+ // const pB = blockContainer.getBlock(parentBlockId)
+ if (parentBlockId && parentBlockId.el.bin.includes(currentBlockId) && parentBlockId.el.page === args.PAGE) {
+ nest = parentBlockId.el.el
+
+
+
+ let body = this.pages.get(args.PAGE)?.get("code").querySelector(nest === "" ? "body" : `#${nest}`)
+ //
+ let el = document.createElement(args.EL)
+ el.setAttribute("id", `${args.ID}`)
+ el = body.appendChild(el)
+
+
+ util.stackFrame.startedBranch = true;
+ util.startBranch(1);
+ return
+ }
+
+
+ }
+
+ blockId = block.parent; // Move to the parent block [3]
+
+ }
+ //
+ // if (currentBlock && currentBlock.parent && currentBlock.parent.el){
+ // nest = currentBlock.parent.el
+ // }
+ //
+ // this.pages.get(args.PAGE)?.set("code", toAString.serializeToString(this.pages.get(args.PAGE).get("code")))
+ let body = this.pages.get(args.PAGE)?.get("code").querySelector(nest === "" ? "body" : `#${nest}`)
+ //
+ let el = document.createElement(args.EL)
+ el.setAttribute("id", `${args.ID}`)
+ el = body.appendChild(el)
+ // this.pages.get(args.PAGE)?.set("code", `${this.pages.get(args.PAGE)?.get("code")}<${args.EL} id="${args.ID}">`)
+ } else {
+ throw new Error(`Only one element with the id "${args.ID}" can exist in the document`)
+ }
+ } else {
+ throw new Error(`Element must have an id`)
+ // this.pages.get(args.PAGE)?.set("code", toAString.serializeToString(this.pages.get(args.PAGE).get("code")))
+ // this.pages.get(args.PAGE)?.set("code", `${this.pages.get(args.PAGE)?.get("code")}<${args.EL} >`)
+ }
+ util.stackFrame.startedBranch = true;
+ util.startBranch(1, true);
+ }
+ }
+
+
+ changeHTML(args, util) {
+ if ((this.pages).has(args.PAGE)) {
+ if (util.stackFrame.startedBranch) {
+ // this.pages.get(args.PAGE)?.set("code", `${this.pages.get(args.PAGE)?.get("code")}${args.EL}>`)
+ // this.pages.get(args.PAGE)?.set("code", document.createRange().createContextualFragment(this.pages.get(args.PAGE).get("code")))
+ //
+ return new Promise((resolve, reject) => {
+ if (this.viewing.includes(args.PAGE) && document.querySelector(".htmlpage")) {
+ try {
+ const elements = document.querySelectorAll(`.htmlpage.display${args.PAGE}`);
+ elements.forEach(el => el.remove());
+ const el = document.createElement("iframe");
+ el.setAttribute("srcdoc", toAString.serializeToString(this.pages.get(args.PAGE).get("code")))
+ el.setAttribute("class", `htmlpage display${args.PAGE}`)
+ el.style.position = 'absolute';
+ el.style.pointerEvents = 'auto';
+ el.style.zIndex = '10';
+ el.style.left = `${this.pages.get(args.PAGE).get("data").get("x")}px`;
+ el.style.top = `${this.pages.get(args.PAGE).get("data").get("y")}px`;
+ el.setAttribute("width", `${this.pages.get(args.PAGE).get("data").get("width")}px`)
+ el.setAttribute("height", `${this.pages.get(args.PAGE).get("data").get("height")}px`)
+ el.style.border = "1px solid black"
+ const container = Scratch.renderer.canvas.parentElement;
+ container.appendChild(el);
+ if (!this.viewing.includes(args.PAGE)) {
+ this.viewing.push(args.PAGE)
+ }
+ el.onload = () => {
+ for (const [key, value] of this.pages.get(args.PAGE)?.get("eves")) {
+ //
+
+ el.contentDocument.querySelector(`#${key}`).addEventListener(value, () => {
+ const triggerText = String(key);
+ const triggerTexta = String(args.PAGE);
+
+ const targetOpcode = 'scrtwpmhtmldocuments_eve';
+ const vm = Scratch.vm;
+
+ vm.runtime.targets.forEach(target => {
+ const blocks = target.blocks;
+ const scripts = blocks.getScripts();
+
+ scripts.forEach(rootBlockId => {
+ const block = blocks.getBlock(rootBlockId);
+
+ if (block && block.opcode === targetOpcode) {
+ let hatValue = '';
+ let hatValuea = '';
+
+ if (block.fields && block.fields.ID && block.fields.PAGE) {
+ hatValue = block.fields.ID.value;
+ hatValuea = block.fields.PAGE.value;
+ }
+ else if (block.inputs && block.inputs.ID && block.inputs.PAGE) {
+ const inputID = block.inputs.ID;
+ const inputPAGE = block.inputs.PAGE;
+
+ const shadowBlockID = blocks.getBlock(inputID.shadow);
+ const shadowBlockPAGE = blocks.getBlock(inputPAGE.shadow);
+
+ if (shadowBlockID && shadowBlockID.fields) {
+ const fieldKey = Object.keys(shadowBlockID.fields)[0];
+ hatValue = shadowBlockID.fields[fieldKey]?.value || '';
+ }
+ if (shadowBlockPAGE && shadowBlockPAGE.fields) {
+ const fieldKeya = Object.keys(shadowBlockPAGE.fields)[0];
+ hatValuea = shadowBlockPAGE.fields[fieldKeya]?.value || '';
+ }
+ }
+
+ if (hatValue === triggerText && hatValuea === triggerTexta) {
+ vm.runtime._pushThread(rootBlockId, target);
+ }
+ }
+ });
+ });
+ }
+
+ )
+
+
+ }
+ }
+ } catch (error) { }
+ }
+ resolve();
+
+ });
+
+
+ }
+ let blocksInLoop = []
+ if (args.ID !== "") {
+ if (this.pages.get(args.PAGE)?.get("code").querySelector(`#${args.ID}`)) {
+
+
+ if (this.getInfo().menus.nest.items.includes(this.pages.get(args.PAGE)?.get("code").querySelector(`#${args.ID}`).tagName.toLowerCase())) {
+ const blockContainer = util.thread.blockContainer;
+ const currentBlockId = util.thread.peekStack(); //
+ const currentBlock = blockContainer.getBlock(currentBlockId);
+ if (!currentBlock) {
+ return
+ }
+ function fn() {
+ const bc = util.thread.target.blocks;
+ const currentBlock = bc.getBlock(currentBlockId);
+ if (!currentBlock) return;
+
+ // 3. Find the ID of the first block inside the loop's C-shaped slot (SUBSTACK)
+ const substackInput = currentBlock.inputs.SUBSTACK;
+ if (!substackInput || !substackInput.block) {
+
+ return;
+ }
+
+ const branchBlockIds = [];
+ let nextBlockId = substackInput.block;
+
+ // 4. Follow the chain of linked blocks sequentially down the branch
+ while (nextBlockId) {
+ branchBlockIds.push(nextBlockId);
+
+ const blockInfo = bc.getBlock(nextBlockId);
+ // Move down to the next block inline, stopping if there are no more
+ nextBlockId = blockInfo ? blockInfo.next : null;
+ }
+
+ // 5. Output the list of specific IDs
+ blocksInLoop = (branchBlockIds);
+ }
+
+ fn()
+
+ if (currentBlock) {
+ currentBlock.el = {
+ el: args.ID,
+ compiledScope: 'global',
+ bin: blocksInLoop,
+ page: args.PAGE
+ };
+ }
+ let body = this.pages.get(args.PAGE)?.get("code").querySelector(`#${args.ID}`)
+ body.innerHTML = ""
+ //
+
+ // const target = util.thread.target;
+
+
+ // let parentBlockId = ""
+ // let parentBlock = ""
+ // let nest = ""
+ // const loopOpcodes = [
+ // 'scrtwpmhtmldocuments_nestEl',
+ // ];
+ // let blockId = currentBlockId;
+ // while (blockId) {
+ // const block = target.blocks.getBlock(blockId);
+ // if (!block) break;
+
+ // if (loopOpcodes.includes(block.opcode) && block !== currentBlock) {
+ // // blockId = block.parent;
+ //
+ // parentBlockId = block;
+ //
+ // parentBlock = blockContainer.getBlock(parentBlockId)
+ // nest = parentBlockId.el.el
+ //
+
+
+ // // let body = this.pages.get(args.PAGE)?.get("code").querySelector(nest === "" ? "body" : `#${nest}`)
+ // // //
+ // // let el = document.createElement(args.EL)
+ // // el.setAttribute("id", `${args.ID}`)
+ // // el = body.appendChild(el)
+
+
+ // util.stackFrame.startedBranch = true;
+ // util.startBranch(1);
+ // return
+
+
+
+ // }
+
+ // blockId = block.parent; // Move to the parent block [3]
+
+ // }
+ //
+ // if (currentBlock && currentBlock.parent && currentBlock.parent.el){
+ // nest = currentBlock.parent.el
+ // }
+ //
+ // this.pages.get(args.PAGE)?.set("code", toAString.serializeToString(this.pages.get(args.PAGE).get("code")))
+ // let body = this.pages.get(args.PAGE)?.get("code").querySelector(nest === "" ? "body" : `#${nest}`)
+ // //
+ // let el = document.createElement(args.EL)
+ // el.setAttribute("id", `${args.ID}`)
+ // el = body.appendChild(el)
+ // this.pages.get(args.PAGE)?.set("code", `${this.pages.get(args.PAGE)?.get("code")}<${args.EL} id="${args.ID}">`)
+ } else {
+ const blockContainer = util.thread.blockContainer;
+ const currentBlockId = util.thread.peekStack(); //
+ const currentBlock = blockContainer.getBlock(currentBlockId);
+ if (currentBlock) {
+ currentBlock.el = {
+ el: args.ID,
+ compiledScope: 'global',
+ bin: blocksInLoop,
+ page: args.PAGE
+ };
+ }
+ }
+ } else {
+ throw new Error(`An element with the id "${args.ID}" doesn't exist in the page`)
+ }
+ } else {
+ // throw new Error(`Element must have an id`)
+ // this.pages.get(args.PAGE)?.set("code", toAString.serializeToString(this.pages.get(args.PAGE).get("code")))
+ // this.pages.get(args.PAGE)?.set("code", `${this.pages.get(args.PAGE)?.get("code")}<${args.EL} >`)
+ }
+ util.stackFrame.startedBranch = true;
+ util.startBranch(1, true);
+ }
+ }
+
+ text(args, util) {
+ //if (Object.keys(this.pages).includes(args.PAGE)) {
+
+ // this.pages.get(args.PAGE)?.set("code", `${this.pages.get(args.PAGE)?.get("code")}${text}`)
+
+
+
+ if ((this.pages).has(args.PAGE)) {
+ // if (args.ID !== "") {
+ // if (!this.pages.get(args.PAGE)?.get("code").querySelector(`#${args.ID}`)) {
+ //
+ const blockContainer = util.thread.blockContainer;
+ const currentBlockId = util.thread.peekStack();
+ const currentBlock = blockContainer.getBlock(currentBlockId);
+ const target = util.thread.target;
+
+
+
+ let parentBlockId = ""
+ let parentBlock = ""
+
+
+ let childId = currentBlockId;
+ let blockId = target.blocks.getBlock(currentBlockId)?.parent;
+
+ let nest = ""
+ const loopOpcodes = [
+ 'scrtwpmhtmldocuments_nestEl',
+ 'scrtwpmhtmldocuments_changeHTML',
+
+ ];
+ // let blockId = currentBlockId;
+
+
+
+
+
+
+ while (blockId) {
+ const parentBlock = target.blocks.getBlock(blockId);
+ // if (!parentBlock) break;
+
+
+ if (loopOpcodes.includes(parentBlock.opcode)) {
+
+
+ const substackId = parentBlock.inputs?.SUBSTACK?.block;
+ // const substack2Id = parentBlock.inputs?.SUBSTACK2?.block;
+
+ //
+
+ if (substackId === childId) {
+
+
+ parentBlockId = blockId;
+
+ const pB = blockContainer.getBlock(parentBlockId)
+
+ if (pB && pB.el.bin.includes(currentBlockId) && pB.el.page === args.PAGE) {
+ nest = pB.el.el
+
+
+ let body = this.pages.get(args.PAGE)?.get("code").querySelector(nest === "" ? "body" : `#${nest}`)
+ //
+
+ let text = document.createRange().createContextualFragment(args.TEXT)
+ let elements = text.querySelectorAll(`script`);
+ elements.forEach(el => el.remove());
+ text = toAString.serializeToString(text)
+ body.innerHTML += text
+
+
+ return
+ }
+ }
+
+
+ if (substackId) {
+ let checkId = substackId;
+ while (checkId) {
+ if (checkId === childId) {
+ }
+ checkId = target.blocks.getBlock(checkId)?.next;
+ }
+ }
+
+ // if (substack2Id) {
+ // let checkId = substack2Id;
+ // while (checkId) {
+ // if (checkId === childId) return blockId;
+ // checkId = target.blocks.getBlock(checkId)?.next;
+ // }
+ // }
+ }
+ childId = blockId;
+ blockId = parentBlock.parent;
+ }
+ //
+ //
+ // if (currentBlock && currentBlock.parent){
+ // const parentBlockId = currentBlock.parent;
+ // const parentBlock = blockContainer.getBlock(parentBlockId)
+ // if(parentBlock && parentBlock.el){
+ //
+ // nest = parentBlock.el.el
+ // }
+ // }
+ // this.pages.get(args.PAGE)?.set("code", toAString.serializeToString(this.pages.get(args.PAGE).get("code")))
+ //
+
+
+ // this.pages.get(args.PAGE)?.set("code", `${this.pages.get(args.PAGE)?.get("code")}<${args.EL} id="${args.ID}">`)
+ // this.pages.get(args.PAGE)?.set("code", document.createRange().createContextualFragment(this.pages.get(args.PAGE).get("code")))
+ //
+ // } else {
+ // throw new Error(`Only one element with the id "${args.ID}" can exist in the document`)
+ // }
+ // } else {
+ // throw new Error(`Element must have an id`)
+
+ // // this.pages.get(args.PAGE)?.set("code", toAString.serializeToString(this.pages.get(args.PAGE).get("code")))
+ // // this.pages.get(args.PAGE)?.set("code", `${this.pages.get(args.PAGE)?.get("code")}<${args.EL} >`)
+ // }
+
+ let body = this.pages.get(args.PAGE)?.get("code").querySelector(nest === "" ? "body" : `#${nest}`)
+ //
+
+ let text = document.createRange().createContextualFragment(args.TEXT)
+ let elements = text.querySelectorAll(`script`);
+ elements.forEach(el => el.remove());
+ text = toAString.serializeToString(text)
+ body.innerHTML += text
+
+ }
+
+
+
+ // }
+ }
+
+ removeEl(args, util) {
+ //if (Object.keys(this.pages).includes(args.PAGE)) {
+ if (this.pages.get(args.PAGE)?.get("code").querySelector(`#${args.ID}`)) {
+
+
+ if (this.pages.get(args.PAGE)?.get("code").querySelector(`#${args.ID}`)) {
+ let el = this.pages.get(args.PAGE)?.get("code").querySelector(`#${args.ID}`)
+ //
+ el.remove()
+ }
+
+
+ }
+ // }
+
+ }
+
+ styleEl(args, util) {
+ //if (Object.keys(this.pages).includes(args.PAGE)) {
+
+
+ if (util.stackFrame.startedBranch) {
+ this.pages.get(args.PAGE)?.set("code", `${this.pages.get(args.PAGE)?.get("code")}`)
+
+ util.stackFrame.startedBranch = false;
+ return;
+ }
+
+ const blockContainer = util.thread.blockContainer;
+ const currentBlockId = util.thread.peekStack(); //
+ const currentBlock = blockContainer.getBlock(currentBlockId);
+ if (currentBlock) {
+ currentBlock.stylePage = {
+ page: args.PAGE,
+ compiledScope: 'global',
+ };
+ }
+ this.pages.get(args.PAGE)?.set("code", `${this.pages.get(args.PAGE)?.get("code")}`
+
+ // util.stackFrame.startedBranch = false;
+ // return;
+ // }
+ // //do before
+ // const blockContainer = util.thread.blockContainer;
+ // const currentBlockId = util.thread.peekStack(); //
+ // const currentBlock = blockContainer.getBlock(currentBlockId);
+ // if (currentBlock) {
+ // currentBlock.stylePage = {
+ // page: args.PAGE,
+ // compiledScope: 'global',
+ // };
+
+ // this.pages[args.PAGE].code += `