Skip to content

Commit a30477c

Browse files
authored
Merge pull request #536 from rgantzos/main
2 parents c5c672e + f35150b commit a30477c

File tree

2 files changed

+167
-34
lines changed

2 files changed

+167
-34
lines changed

api/main.js

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,24 @@ if (
1111
ScratchTools.type = "Website";
1212
}
1313

14-
var storagePromises = []
14+
var storagePromises = [];
1515
ScratchTools.storage = {
16-
get: async function(key) {
16+
get: async function (key) {
1717
chrome.runtime.sendMessage(ScratchTools.id, { message: "storageGet", key });
1818
return new Promise((resolve, reject) => {
19-
storagePromises.push({ key: key, resolve })
19+
storagePromises.push({ key: key, resolve });
2020
});
2121
},
22-
set: async function({ key, value }) {
23-
chrome.runtime.sendMessage(ScratchTools.id, { message: "storageSet", key, value });
24-
}
25-
}
22+
set: async function ({ key, value }) {
23+
chrome.runtime.sendMessage(ScratchTools.id, {
24+
message: "storageSet",
25+
key,
26+
value,
27+
});
28+
},
29+
};
30+
31+
let waitForSingleElements = [];
2632

2733
var allSelectors = {};
2834
var allCallbacksForWait = {};
@@ -48,6 +54,21 @@ ScratchTools.waitForElements("head > *", function (el) {
4854
document.head.appendChild(stylesDiv);
4955
}
5056
});
57+
58+
ScratchTools.waitForElement = async function (selector) {
59+
return new Promise((resolve) => {
60+
if (document.querySelector(selector)) {
61+
resolve(document.querySelector(selector));
62+
} else {
63+
waitForSingleElements.push({
64+
selector: selector,
65+
resolved: false,
66+
resolve,
67+
});
68+
}
69+
});
70+
};
71+
5172
function enableScratchToolsSelectorsMutationObserver() {
5273
var ScratchToolsSelectorsMutationObserver = new MutationObserver(
5374
returnScratchToolsSelectorsMutationObserverCallbacks
@@ -70,6 +91,14 @@ function returnScratchToolsSelectorsMutationObserverCallbacks() {
7091
});
7192
});
7293
});
94+
waitForSingleElements
95+
.filter((promise) => !promise.resolved)
96+
.forEach(function (promise) {
97+
if (document.querySelector(promise.selector)) {
98+
promise.resolved = true;
99+
promise.resolve(document.querySelector(promise.selector));
100+
}
101+
});
73102
}
74103

75104
ScratchTools.createModal = function (titleText, description, buttons) {
@@ -254,4 +283,4 @@ ScratchTools.waitForElements(
254283
},
255284
"ste-full-settings-btn",
256285
false
257-
);
286+
);

api/vm.js

Lines changed: 130 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,114 @@ try {
2626
ScratchTools.console.warn("Unable to load Blockly.");
2727
}
2828

29-
ScratchTools.Scratch.contextMenus = {};
29+
ScratchTools.Scratch.scratchSound = function () {
30+
try {
31+
return document.querySelector("div.sound-editor_editor-container_iUSW-")[
32+
Object.keys(
33+
document.querySelector("div.sound-editor_editor-container_iUSW-")
34+
).find((key) => key.startsWith("__reactInternalInstance"))
35+
].return.return.return.stateNode;
36+
} catch (err) {
37+
return null;
38+
}
39+
};
40+
41+
ScratchTools.Scratch.scratchGui = function () {
42+
try {
43+
const app = document.querySelector("#app");
44+
return app[
45+
Object.keys(app).find((key) => key.startsWith("__reactContainer"))
46+
].child.stateNode.store.getState().scratchGui;
47+
} catch (err) {
48+
return null;
49+
}
50+
};
51+
52+
ScratchTools.traps = {
53+
scratchGui: ScratchTools.Scratch.scratchGui,
54+
scratchPaint: ScratchTools.Scratch.scratchPaint,
55+
scratchSound: ScratchTools.Scratch.scratchSound,
56+
getVm: function () {
57+
return vm;
58+
},
59+
getBlockly: function () {
60+
return Blockly;
61+
},
62+
getScratchBlocks,
63+
};
64+
65+
let openContextMenus = [];
66+
67+
const waitForContextMenu = function ({ id, callback, block, disabled, label }) {
68+
var insertion = openContextMenus.push({
69+
id,
70+
block,
71+
callback,
72+
enabled: !disabled,
73+
label,
74+
});
75+
return {
76+
delete: function() {
77+
delete openContextMenus[openContextMenus.indexOf(insertion)]
78+
}
79+
}
80+
};
81+
82+
let handledProcedures = {};
83+
ScratchTools.waitForElements(
84+
".blocklyDraggable",
85+
function (el) {
86+
if (el.dataset.id) {
87+
var block = ScratchTools.traps
88+
.getBlockly()
89+
.getMainWorkspace()
90+
.getBlockById(el.dataset.id);
91+
if (block) {
92+
if (block.type === "procedures_definition") {
93+
if (!handledProcedures[block.id]) {
94+
handledProcedures[block.id] = block.customContextMenu;
95+
}
96+
block.customContextMenu = function (menu) {
97+
handledProcedures[block.id](menu);
98+
openContextMenus.forEach(function (option) {
99+
if (option.block === block.id) {
100+
menu.push({
101+
enabled: option.enabled,
102+
text: option.label,
103+
callback: function (e) {
104+
option.callback(e);
105+
},
106+
});
107+
}
108+
});
109+
};
110+
} else {
111+
if (!block.customContextMenu) {
112+
block.customContextMenu = function (menu) {
113+
openContextMenus.forEach(function (option) {
114+
if (option.block === block.id) {
115+
menu.push({
116+
enabled: option.enabled,
117+
text: option.label,
118+
callback: function (e) {
119+
option.callback(e);
120+
},
121+
});
122+
}
123+
});
124+
};
125+
}
126+
}
127+
}
128+
}
129+
},
130+
"custom-menu-manager",
131+
false
132+
);
133+
134+
ScratchTools.traps.createContextMenu = waitForContextMenu;
30135

136+
ScratchTools.Scratch.contextMenus = {};
31137
ScratchTools.Scratch.waitForContextMenu = function (info) {
32138
if (ScratchTools.Scratch.contextMenus[info.block] !== undefined) {
33139
ScratchTools.Scratch.contextMenus[info.block][info.id] = info.callback;
@@ -79,29 +185,6 @@ ScratchTools.Scratch.scratchPaint = function () {
79185
}
80186
};
81187

82-
ScratchTools.Scratch.scratchSound = function () {
83-
try {
84-
return document.querySelector("div.sound-editor_editor-container_iUSW-")[
85-
Object.keys(
86-
document.querySelector("div.sound-editor_editor-container_iUSW-")
87-
).find((key) => key.startsWith("__reactInternalInstance"))
88-
].return.return.return.stateNode;
89-
} catch (err) {
90-
return null;
91-
}
92-
};
93-
94-
ScratchTools.Scratch.scratchGui = function () {
95-
try {
96-
const app = document.querySelector("#app");
97-
return app[
98-
Object.keys(app).find((key) => key.startsWith("__reactContainer"))
99-
].child.stateNode.store.getState().scratchGui;
100-
} catch (err) {
101-
return null;
102-
}
103-
};
104-
105188
async function alertForUpdates() {
106189
if (ScratchTools.Scratch.scratchGui().mode.hasEverEnteredEditor) {
107190
var purple = await (
@@ -110,7 +193,8 @@ async function alertForUpdates() {
110193
)
111194
).json();
112195
if (purple.purple) {
113-
var alertedForPurple = await ScratchTools.storage.get("purpleAlert") || false;
196+
var alertedForPurple =
197+
(await ScratchTools.storage.get("purpleAlert")) || false;
114198
if (!alertedForPurple) {
115199
await ScratchTools.storage.set({ key: "purpleAlert", value: true });
116200
ScratchTools.modals.create({
@@ -123,4 +207,24 @@ async function alertForUpdates() {
123207
}
124208
}
125209

126-
ScratchTools.waitForElements("div[class^='gui_menu-bar-position_']", alertForUpdates, "purple-notification", false)
210+
function getScratchBlocks() {
211+
var blocksWrapper = document.querySelector(
212+
'div[class^="gui_blocks-wrapper"]'
213+
);
214+
var key = Object.keys(blocksWrapper).find((key) =>
215+
key.startsWith("__reactInternalInstance$")
216+
);
217+
const internal = blocksWrapper[key];
218+
var recent = internal.child;
219+
while (!recent.stateNode?.ScratchBlocks) {
220+
recent = recent.child;
221+
}
222+
return recent.stateNode.ScratchBlocks || null;
223+
}
224+
225+
ScratchTools.waitForElements(
226+
"div[class^='gui_menu-bar-position_']",
227+
alertForUpdates,
228+
"purple-notification",
229+
false
230+
);

0 commit comments

Comments
 (0)