diff --git a/src/lib/extensions.js b/src/lib/extensions.js
index 84d1c063a..c4c68ae6f 100644
--- a/src/lib/extensions.js
+++ b/src/lib/extensions.js
@@ -4,6 +4,7 @@
to help copy & pasting work better
pleas dont remove them :)
*/
+// a sakura
export default [
{
name: "Pen+",
@@ -33,15 +34,25 @@ export default [
description: "A storage extension with many more features. Featuring server storage (with a fast server), local storage and more!",
code: "Gen1x/storage_plus.js",
banner: "Gen1x/storage_plus.avif",
- creator: "G1nX",
+ isGithub: true,
+ creator: "Gen1x",
},
{
name: "MotionSprite",
description: "An advanced math-based animation system",
code: "TheShovel/qoan-renderer.js",
banner: "TheShovel/thumbnail-motionsprite.avif",
- isGitHub: true,
creator: "TheShovel",
+ isGitHub: true,
+ },
+ {
+ name: "Iris Text",
+ description: "A heavily-expanded text engine loosely based on Animated Text! Adds support for rich text, per-character control, and more!",
+ code: "Gen1x/iris-text.js",
+ banner: "Gen1x/iris-text.avif",
+ isGitHub: true,
+ creator: "Gen1xLol",
+ tags: ["new"],
},
{
name: "Boxed Physics",
@@ -55,7 +66,8 @@ export default [
description: "A fast, powerful and easy-to-use lighting engine powered by WebGL!",
code: "Gen1x/lighting.js",
banner: "Gen1x/lighting.avif",
- creator: "G1nX",
+ isGitHub: true,
+ creator: "Gen1xLol",
tags: ["new"],
},
{
@@ -64,24 +76,15 @@ export default [
code: "ObviousAlexC/3DMath.js",
banner: "ObviousAlexC/3DMath.svg",
creator: "pinksheep2917",
- },
- {
- name: "GPU.sb3",
- description: "Use WebGPU compute shaders to accelerate your projects. Use Pen+ for rendering, not this.",
- code: "derpygamer2142/gpusb3.js",
- banner: "derpygamer2142/gpusb3.png",
- creator: "insanetaco2000",
- creatorAlias: "derpygamer2142",
- isGitHub: false,
- unstable: true,
- unstableReason: "WebGPU is still experimental and not supported by all browsers and does not work when packaged to electron. Check compatibility at webgpu.io."
+ notes: "A bunch of new blocks were added by LSPECTRONIZTAR (with the help of Game Physics Cookbook by Gabor Szauer",
},
{
name: "Beat Sync",
description: "An extension designed to let you sync anything in your project to a musical beat, with incredible precision.",
code: "Gen1x/beat_sync.js",
banner: "Gen1x/beat_sync.avif",
- creator: "G1nX",
+ isGitHub: true,
+ creator: "Gen1xLol",
},
{
name: "Object",
@@ -91,16 +94,16 @@ export default [
creator: "skyhigh173",
isGitHub: true,
},
- /*{
- name: "Scope Variable",
- description: "Manage your variables in a block-like structure.",
- code: "0znzw/ScopeVars.js",
- banner: "0znzw/ScopeVars.avif",
- creator: "yuri-kiss",
+ {
+ name: "Array Buffers",
+ description: "Read, parse, and write raw data efficiently using Array Buffers",
+ code: "AndrewGaming587/agBuffer.js",
+ banner: "AndrewGaming587/agBuffer.svg",
+ creator: "AndrewScripter587",
+ creatorAlias: "AndrewGaming587",
isGitHub: true,
- unstable: true,
- unstableReason: "This extension uses unstable techniques that may not always work.",
- },*/
+ tags: ["customtype", "data", "utility", "new", "large"],
+ },
{
name: "Extra Timers",
description: "Allows for the creation and management of additional timer blocks.",
@@ -108,9 +111,8 @@ export default [
code: "steve0greatness/timers.js",
banner: "steve0greatness/timers.svg",
creator: "Steve0Greatness",
- isGitHub: true,
+ isGitHub: true,
},
-
{
name: "Dictation",
description: "Convert your voice into text. (not supported in all browsers)",
@@ -118,21 +120,13 @@ export default [
banner: "pooiod/Dictation.svg",
creator: "pooiod7",
},
- {
+ {
name: "Doodle Recognition",
description: "A implementation of Google's Quick Draw image vision model",
code: "TheShovel/doodlerec.js",
banner: "TheShovel/thumbnail-doodlerec.avif",
creator: "TheShovel",
isGitHub: true,
- },
- {
- name: "Stylus",
- description: "Get the pressure and tilt of a stylus or graphic tablet",
- code: "sourdoggy/stylus extension.js",
- banner: "sourdoggy/stylus_ext.avif",
- creator: "sourdoggy",
- isGitHub: true,
},
{
name: "Text To Speech: Redone",
@@ -166,47 +160,6 @@ export default [
creator: "MikeDev101",
isGitHub: true,
},
- {
- name: "Spritesheeter",
- description: "Load and manipulate spritesheets with customizable frames and XML support",
- code: "MubiLop/spritesheeter.js",
- banner: "MubiLop/spritesheeter.avif",
- creator: "cicerorph",
- creatorAlias: "MubiLop",
- isGitHub: true
- },
- {
- name: "Background Remover",
- description: "Removes background from images.",
- code: "dumzdev/removebg.js",
- banner: "dumzdev/removebgbanner.svg",
- creator: "dumzdev",
- },
- /*{
- name: "PenguinAI",
- description: "Talk to AI! Use Models like DALL-E-3, GPT, LLama, Claude, and more!",
- code: "MubiLop/penguingpt.js",
- banner: "MubiLop/penguingpt.avif",
- creator: "cicerorph",
- creatorAlias: "MubiLop",
- isGitHub: true,
- unstable: true,
- documentation: "PenguinAI",
- unstableReason: "AI models can generate unintended or inappropriate output.\nSome AI models may also become temporarily inaccessible.\n\nUse at your own risk.",
- },*/
-/*
- {
- name: "Block AI",
- description: "An AI powered chat bot to help you code in your projects.",
- code: "TheShovel/blockAI.js",
- banner: "TheShovel/thumbnail-blockAI.avif",
- creator: "TheShovel",
- creatorAlias: "TheShovel",
- isGitHub: true,
- unstable: true,
- unstableReason: "The AI model can generate inaccurate output and broken syntax!",
- },
-*/
{
name: "PenguinHook",
description: "Send Webhook requests, that can be Discord or any type of webhook.",
@@ -219,12 +172,12 @@ export default [
{
name: "Number Utilities",
description: "Adds blocks for number formatting and manipulation.",
+ notes: "Art made by hazel",
code: "MubiLop/numutils.js",
banner: "MubiLop/numutils.avif",
+ documentation: "NumberUtilities",
creator: "cicerorph",
creatorAlias: "MubiLop",
- notes: "Art made by hazel",
- documentation: "NumberUtilities",
isGitHub: true,
},
{
@@ -243,36 +196,15 @@ export default [
creator: "qxsck",
isGitHub: true,
},
- /* Use the TurboWarp version. This one is outdated and broken.
- {
- name: "Format Numbers",
- description: "Format large numbers into AD standard, fixed decimal, comma separated, or scientific notation.",
- code: "DogeisCut/FormatNumbers.js",
- banner: "DogeisCut/FormatNumbers.avif",
- creator: "DogeisCut",
- isGitHub: true,
- notes: "Gallery banner by Dillon",
- },
- */
{
name: "Date Format",
description: "Manage, format and compare dates.",
code: "ddededodediamante/dateFormatV2.js",
banner: "ddededodediamante/dateFormat.svg",
- creator: "ddededodediamante",
documentation: "DateFormatV2",
- tags: ["customtype", "utility", "small", "data", "time", "text"],
+ creator: "ddededodediamante",
isGitHub: true,
- },
- {
- name: "Translation Keys",
- description: "Use translation keys in your projects for multi-language support",
- code: "Embin/embintranslation.js",
- banner: "Embin/embintranslation.avif",
- creator: "embinmc",
- isGitHub: true,
- tags: ["data", "utility", "text"],
- creatorAlias: "Embin",
+ tags: ["customtype", "utility", "small", "data", "time", "text"],
},
{
name: "Yet Another String Extension",
@@ -280,8 +212,8 @@ export default [
code: "DogeisCut/YetAnotherStringExtension.js",
banner: "DogeisCut/YetAnotherStringExtension.svg",
creator: "DogeisCut",
- isGitHub: true,
- tags: ["builder", "small", "text", "utility"]
+ isGitHub: true,
+ tags: ["builder", "small", "text", "utility"],
},
{
name: "Regular Expressions",
@@ -289,8 +221,8 @@ export default [
code: "DogeisCut/dogeiscutRegularExpressions.js",
banner: "DogeisCut/dogeiscutRegularExpressions.svg",
creator: "DogeisCut",
- isGitHub: true,
- tags: ["customtype", "regex", "text", "utility"]
+ isGitHub: true,
+ tags: ["customtype", "regex", "text", "utility"],
},
{
name: "Sets",
@@ -298,8 +230,8 @@ export default [
code: "DogeisCut/dogeiscutSet.js",
banner: "DogeisCut/dogeiscutSet.svg",
creator: "DogeisCut",
- isGitHub: true,
- tags: ["customtype", "sets", "data", "utility"]
+ isGitHub: true,
+ tags: ["customtype", "sets", "data", "utility"],
},
{
name: "Iterators",
@@ -341,29 +273,30 @@ export default [
},
{
name: "Random Utilities",
- description: "Many blocks related to generating random values, including seed-based number generation, true number generation, UUID's, random strings, etc.",
+ description: "Many blocks related to generating random values. Includes seed-based number generation, true number generation, UUIDs, random strings, etc.",
code: "Gen1x/random_utils.js",
banner: "Gen1x/randomutils.avif",
- creator: "G1nX",
+ isGitHub: true,
+ creator: "Gen1xLol",
},
{
name: "Toast Notifications",
description: "Did you want alerts? Notifications that are easily customizable? This is the only and best notification extension!",
+ notes: "Additional code by themeatly2 and ddededodediamante",
code: "MubiLop/toastnotifs.js",
banner: "MubiLop/toastnotifs.avif",
creator: "cicerorph",
creatorAlias: "MubiLop",
- notes: "Additional code by themeatly2 and ddededodediamante",
isGitHub: true,
},
{
name: "Project Interfaces",
description: "Effortlessly create intuitive graphical user interfaces in your projects.",
+ notes: "Gallery banner by Dillon",
code: "LordCat0/ProjectInterfaces.js",
banner: "LordCat0/ProjectInterfaces.avif",
creator: "LordCat0",
creatorAlias: "Lord cat",
- notes: "Gallery banner by Dillon",
isGitHub: true,
},
{
@@ -372,7 +305,7 @@ export default [
code: "justablock/gitpenguin.js",
banner: "justablock/gitpenguin.avif",
creator: "justablock",
- isGitHub: false,
+ isGitHub: false,
},
{
name: "Pang API",
@@ -406,13 +339,22 @@ export default [
creator: "NamelessCat",
},
{
- name: "Extension Exposer",
- description: "Access the raw functions from other extensions.",
- code: "TheShovel/extexp.js",
- banner: "TheShovel/placeholder-extexp.avif",
- creator: "TheShovel",
+ name: "HTML Documents",
+ description: "Create HTML webpages and display them on the stage.",
+ code: "ScrTwPm/htmldocuments.js",
+ banner: "ScrTwPm/htmldocs.png",
+ creator: "ScrTwPm",
isGitHub: true,
- notes: "Some contributions by yuri-kiss :P",
+ },
+ {
+ name: "Translation Keys",
+ description: "Use translation keys in your projects for multi-language support",
+ code: "Embin/embintranslation.js",
+ banner: "Embin/embintranslation.avif",
+ creator: "embinmc",
+ creatorAlias: "Embin",
+ isGitHub: true,
+ tags: ["data", "utility", "text"],
},
{
name: "All Menus",
@@ -422,6 +364,15 @@ export default [
creator: "LilyMakesThings",
isGitHub: false,
},
+ {
+ name: "Extension Exposer",
+ description: "Access the raw functions from other extensions.",
+ notes: "Some contributions by yuri-kiss :P",
+ code: "TheShovel/extexp.js",
+ banner: "TheShovel/placeholder-extexp.avif",
+ creator: "TheShovel",
+ isGitHub: true,
+ },
{
name: "More Fields",
description: "Custom Field Types",
@@ -436,8 +387,8 @@ export default [
code: "DogeisCut/BeepBoxPlayer.js",
banner: "DogeisCut/BeepBoxPlayer.svg",
creator: "DogeisCut",
- isGitHub: true,
- tags: ["api", "music", "sounds"]
+ isGitHub: true,
+ tags: ["api", "music", "sounds"],
},
{
name: "Update File (Direct Access)",
@@ -454,9 +405,9 @@ export default [
description: "Upload files to the cloud by throwing it to outer space!",
code: "MubiLop/yeetyourfiles.js",
banner: "MubiLop/yeetyourfiles.png",
+ documentation: "YeetYourFiles",
creator: "cicerorph",
creatorAlias: "MubiLop",
- documentation: "YeetYourFiles",
isGitHub: true,
},
{
@@ -467,6 +418,16 @@ export default [
creator: "Codefoxy",
isGitHub: false,
},
+ {
+ name: "Device Motion",
+ description: "Use Device Motion API to get rotation or movement of the device. Works best on mobile devices.",
+ notes: "Extension thumbnail made by Dillon.",
+ code: "gaimerI17/DeviceMotion.js",
+ banner: "gaimerI17/DeviceMotion.avif",
+ creator: "gaimerI",
+ creatorAlias: "gaimerI17",
+ isGitHub: true,
+ },
{
name: "Video Sharing",
description: "Share your screen or camera to your projects!",
@@ -494,9 +455,9 @@ export default [
description: "Authenticate with anything and everything!",
code: "MubiLop/authpenguin.js",
banner: "MubiLop/authpenguin.avif",
+ documentation: "AuthPenguin",
creator: "cicerorph",
creatorAlias: "MubiLop",
- documentation: "AuthPenguin",
isGitHub: true,
},
{
@@ -516,6 +477,34 @@ export default [
creator: "enderhacker",
isGitHub: true,
},
+ {
+ name: "More Types",
+ description: "Adds more value types to PenguinMod, implementing Functions, Objects, Arrays, Sets, Maps, Symbols and Nothing.",
+ code: "VeryGoodScratcher42/More-Types.js",
+ banner: "VeryGoodScratcher42/More-Types.avif",
+ creator: "VeryGoodScratcher42",
+ isGitHub: false,
+ },
+ {
+ name: "Resolution",
+ description: "Provides utility blocks that simplify the creation of projects with dynamic resolution support.",
+ notes: "Gallery banner by Dillon",
+ code: "DogeisCut/Resolution.js",
+ banner: "DogeisCut/Resolution.svg",
+ documentation: "Resolution",
+ creator: "DogeisCut",
+ isGitHub: true,
+ tags: ["graphics", "utility"],
+ },
+ {
+ name: "Cryptography",
+ description: "Hash, encrypt and verify values using various algorithms. Actual security not guaranteed",
+ code: "gaimerI17/crypto.js",
+ banner: "gaimerI17/crypto.avif",
+ creator: "gaimerI",
+ creatorAlias: "gaimerI17",
+ isGitHub: true,
+ },
{
name: "Twitch",
description: "Communicate with your Twitch Chat on PenguinMod!\n\nPenguinMod is not affiliated with Twitch.",
@@ -529,15 +518,16 @@ export default [
description: "A powerful extension that lets you manage a chessboard, customize it, use Stockfish 17 (via API), a local chess engine (Lozza) and much, MUCH more!",
code: "Gen1x/chess-ext.js",
banner: "Gen1x/chess-ext.avif",
- creator: "G1nX",
- isGitHub: false,
+ isGitHub: true,
+ creator: "Gen1xLol",
},
{
name: "CATS",
description: "Blocks related to cats.",
code: "Gen1x/CATS.js",
banner: "Gen1x/cats.avif",
- creator: "G1nX",
+ isGitHub: true,
+ creator: "Gen1xLol",
},
{
name: "Free Servers",
@@ -578,8 +568,8 @@ export default [
code: "Monochromasity/howmanylines.js",
banner: "Monochromasity/placeholder-howmanylines.avif",
creator: "Monochromasity",
- tags: ["small"],
isGitHub: true,
+ tags: ["small"],
},
{
name: "Paint Utils",
@@ -590,35 +580,87 @@ export default [
isGitHub: true,
},
{
- name: "Resolution",
- description: "Provides utility blocks that simplify the creation of projects with dynamic resolution support.",
- code: "DogeisCut/Resolution.js",
- banner: "DogeisCut/Resolution.svg",
- creator: "DogeisCut",
- isGitHub: true,
- documentation: "Resolution",
- notes: "Gallery banner by Dillon",
- tags: ["graphics", "utility"]
+ name: "Smoke Detector",
+ description: "Plays a beep every 30 seconds.",
+ code: "electricfuzzball_pm/firealarm.js",
+ banner: "electricfuzzball_pm/firealarm.svg",
+ creator: "FloppyDisk-OSC",
+ creatorAlias: "ElectricFuzzball_PM",
+ isGitHub: true,
},
{
- name: "Device Motion",
- description: "Use Device Motion API to get rotation or movement of the device. Works best on mobile devices. \n Thumbnail by Dillon. ",
- code: "gaimerI17/DeviceMotion.js",
- banner: "gaimerI17/DeviceMotion.avif",
- creator: "gaimerI",
+ name: "Stage Brah",
+ description: "Spawn Brah cats in your codespace!",
+ code: "electricfuzzball_pm/StageBrah.js",
+ banner: "electricfuzzball_pm/StageBrah.svg",
+ creator: "FloppyDisk-OSC",
+ creatorAlias: "ElectricFuzzball_PM",
isGitHub: true,
- creatorAlias: "gaimerI17",
- notes: "Extension thumbnail made by Dillon."
},
{
- name: "Cryptography",
- description: "Hash, encrypt and verify values using various algorithms. Actual security not guaranteed",
- code: "gaimerI17/crypto.js",
- banner: "gaimerI17/crypto.avif",
- creator: "gaimerI",
+ name: "MIDI Controller",
+ description: "Use a MIDI keyboard to interact with projects!",
+ code: "electricfuzzball_pm/MIDI.js",
+ banner: "electricfuzzball_pm/MIDI.svg",
+ creator: "FloppyDisk-OSC",
+ creatorAlias: "ElectricFuzzball_PM",
isGitHub: true,
- creatorAlias: "gaimerI17",
},
+ {
+ name: "Black Mold",
+ description: "mmmmm black mold",
+ code: "electricfuzzball_pm/blackMold.js",
+ banner: "electricfuzzball_pm/blackMold.svg",
+ creator: "FloppyDisk-OSC",
+ creatorAlias: "ElectricFuzzball_PM",
+ isGitHub: true,
+ },
+ /*{
+ name: "Scope Variable",
+ description: "Manage your variables in a block-like structure.",
+ code: "0znzw/ScopeVars.js",
+ banner: "0znzw/ScopeVars.avif",
+ creator: "yuri-kiss",
+ isGitHub: true,
+ unstable: true,
+ unstableReason: "This extension uses unstable techniques that may not always work.",
+ },*/
+ /*{
+ name: "PenguinAI",
+ description: "Talk to AI! Use Models like DALL-E-3, GPT, LLama, Claude, and more!",
+ code: "MubiLop/penguingpt.js",
+ banner: "MubiLop/penguingpt.avif",
+ creator: "cicerorph",
+ creatorAlias: "MubiLop",
+ isGitHub: true,
+ unstable: true,
+ documentation: "PenguinAI",
+ unstableReason: "AI models can generate unintended or inappropriate output.\nSome AI models may also become temporarily inaccessible.\n\nUse at your own risk.",
+ },*/
+ /*
+ {
+ name: "Block AI",
+ description: "An AI powered chat bot to help you code in your projects.",
+ code: "TheShovel/blockAI.js",
+ banner: "TheShovel/thumbnail-blockAI.avif",
+ creator: "TheShovel",
+ creatorAlias: "TheShovel",
+ isGitHub: true,
+ unstable: true,
+ unstableReason: "The AI model can generate inaccurate output and broken syntax!",
+ },
+ */
+ /* Use the TurboWarp version. This one is outdated and broken.
+ {
+ name: "Format Numbers",
+ description: "Format large numbers into AD standard, fixed decimal, comma separated, or scientific notation.",
+ code: "DogeisCut/FormatNumbers.js",
+ banner: "DogeisCut/FormatNumbers.avif",
+ creator: "DogeisCut",
+ isGitHub: true,
+ notes: "Gallery banner by Dillon",
+ },
+ */
/* these extensions are completely dead as of now
{
name: "Online Captcha",
@@ -643,51 +685,4 @@ export default [
creator: "G1nX",
},
*/
- {
- name: "Smoke Detector",
- description: "Plays a beep every 30 seconds.",
- code: "electricfuzzball_pm/firealarm.js",
- banner: "electricfuzzball_pm/firealarm.svg",
- creator: "FloppyDisk-OSC",
- isGitHub: true,
- creatorAlias: "ElectricFuzzball_PM"
- },
- {
- name: "Stage Brah",
- description: "Spawn Brah cats in your codespace!",
- code: "electricfuzzball_pm/StageBrah.js",
- banner: "electricfuzzball_pm/StageBrah.svg",
- creator: "FloppyDisk-OSC",
- isGitHub: true,
- creatorAlias: "ElectricFuzzball_PM"
- },
- {
- name: "MIDI Controller",
- description: "Use a MIDI keyboard to interact with projects!",
- code: "electricfuzzball_pm/MIDI.js",
- banner: "electricfuzzball_pm/MIDI.svg",
- creator: "FloppyDisk-OSC",
- isGitHub: true,
- creatorAlias: "ElectricFuzzball_PM"
- },
- {
- name: "Array Buffers",
- description: "Read, parse, and write raw data efficiently using Array Buffers",
- code: "AndrewGaming587/agBuffer.js",
- banner: "AndrewGaming587/agBuffer.svg",
- creator: "AndrewScripter587",
- isGitHub: true,
- tags: ["customtype","data","utility","new","large"],
- creatorAlias: "AndrewGaming587"
- },
-
- {
- name: "Black Mold",
- description: "mmmmm black mold",
- code: "electricfuzzball_pm/blackMold.js",
- banner: "electricfuzzball_pm/blackMold.svg",
- creator: "FloppyDisk-OSC",
- isGitHub: true,
- creatorAlias: "ElectricFuzzball_PM"
- },
];
diff --git a/static/extensions/ScrTwPm/htmldocuments.js b/static/extensions/ScrTwPm/htmldocuments.js
new file mode 100644
index 000000000..065ebbd6b
--- /dev/null
+++ b/static/extensions/ScrTwPm/htmldocuments.js
@@ -0,0 +1,2416 @@
+(function (Scratch) {
+ 'use strict';
+
+ const dom = new DOMParser()
+ const toAString = new XMLSerializer()
+
+ class PrevHTML {
+ constructor(html) {
+ 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.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) {
+ 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: '#ff9900',
+ 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',
+ 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: 'allPages',
+ blockType: Scratch.BlockType.REPORTER,
+ text: 'all pages',
+ disableMonitor: true,
+ },
+ {
+ opcode: 'getHTML',
+ blockType: Scratch.BlockType.REPORTER,
+ text: 'get html code of [PAGE]',
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ }
+ }
+ },
+ {
+ opcode: 'previewHTML',
+ blockType: Scratch.BlockType.REPORTER,
+ text: 'preview [PAGE]',
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ }
+ }
+ },
+ // {
+ // 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: "green",
+ },
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page",
+ },
+ }
+ },
+
+ {
+ opcode: 'remallstyle',
+ blockType: Scratch.BlockType.COMMAND,
+ text: 'clear styling of [PAGE]',
+ arguments: {
+ PAGE: {
+ type: Scratch.ArgumentType.STRING,
+ defaultValue: "my-page"
+ },
+ }
+ },
+
+ { 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 listeners 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"
+ },
+ }
+ },
+
+ { 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 "},
+ ]
+ },
+ 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'
+
+ ]
+ },
+ axis: {
+ acceptReporters: false,
+ items: [
+ { text: "x position", value: "x" }, { text: "y position", value: "y" }, "width", "height"
+ ]
+ },
+ wh: {
+ acceptReporters: false,
+ items: [
+ "width", "height"
+ ]
+ },
+ eves: {
+ acceptReporters: true,
+ 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 (!Object.keys(this.pages).includes(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")
+ }
+ }
+ }
+ 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) => {
+ try {
+ if (this.viewing.includes(args.PAGE) && document.querySelector(".htmlpage")) {
+ 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();
+ util.stackFrame.startedBranch = false;
+ return;
+ });
+
+
+ }
+ 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 += `