Skip to content
This repository was archived by the owner on Oct 2, 2024. It is now read-only.

Commit 88a00a7

Browse files
committed
Update DevTools extension
1 parent f94e861 commit 88a00a7

File tree

1 file changed

+77
-0
lines changed

1 file changed

+77
-0
lines changed

inject/run-addon.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,39 @@ const path = document.querySelector("script[id='devtools-extension-module']").ge
99
const getURL = (x) => `${path}${x}`;
1010
const scriptUrl = getURL(`addon/${MAIN_JS}`);
1111

12+
class WaitForElementSingleton {
13+
constructor() {
14+
this._waitForElementSet = new WeakSet();
15+
this.getBindedFunc = () => this.waitForElement.bind(this);
16+
}
17+
waitForElement(selector, opts = {}) {
18+
// Identical to SA
19+
const markAsSeen = !!opts.markAsSeen;
20+
const firstQuery = document.querySelectorAll(selector);
21+
for (const element of firstQuery) {
22+
if (this._waitForElementSet.has(element)) continue;
23+
if (markAsSeen) this._waitForElementSet.add(element);
24+
return Promise.resolve(element);
25+
}
26+
return new Promise((resolve) =>
27+
new MutationObserver((mutationsList, observer) => {
28+
const elements = document.querySelectorAll(selector);
29+
for (const element of elements) {
30+
if (this._waitForElementSet.has(element)) continue;
31+
observer.disconnect();
32+
resolve(element);
33+
if (markAsSeen) this._waitForElementSet.add(element);
34+
break;
35+
}
36+
}).observe(document.documentElement, {
37+
attributes: false,
38+
childList: true,
39+
subtree: true,
40+
})
41+
);
42+
}
43+
}
44+
1245
const addon = {
1346
self: {
1447
_isDevtoolsExtension: true,
@@ -19,6 +52,50 @@ const addon = {
1952
return Object.values(document.querySelector('div[class^="stage-wrapper_stage-wrapper_"]')).find((x) => x.child)
2053
.child.child.child.stateNode.props.vm;
2154
},
55+
56+
// All of these are needed for getBlockly()
57+
_cache: Object.create(null),
58+
_getEditorMode() {
59+
const isWWW = !!document.querySelector("meta[name='format-detection']");
60+
const editorMode = (() => {
61+
const pathname = location.pathname.toLowerCase();
62+
const split = pathname.split("/").filter(Boolean);
63+
if (!split[0] || split[0] !== "projects") return null;
64+
if (split.includes("editor")) return "editor";
65+
if (split.includes("fullscreen")) return "fullscreen";
66+
if (split.includes("embed")) return "embed";
67+
return "projectpage";
68+
})();
69+
return isWWW && editorMode;
70+
},
71+
_waitForElement: new WaitForElementSingleton().getBindedFunc(),
72+
_react_internal_key: undefined,
73+
get REACT_INTERNAL_PREFIX() {
74+
return "__reactInternalInstance$";
75+
},
76+
77+
async getBlockly() {
78+
// Identical to SA
79+
if (this._cache.Blockly) return this._cache.Blockly;
80+
const editorMode = this._getEditorMode();
81+
if (!editorMode || editorMode === "embed") throw new Error("Cannot access Blockly on this page");
82+
const BLOCKS_CLASS = '[class^="gui_blocks-wrapper"]';
83+
let elem = document.querySelector(BLOCKS_CLASS);
84+
if (!elem) {
85+
elem = await this._waitForElement(BLOCKS_CLASS);
86+
}
87+
if (!this._react_internal_key) {
88+
this._react_internal_key = Object.keys(elem).find((key) => key.startsWith(this.REACT_INTERNAL_PREFIX));
89+
}
90+
const internal = elem[this._react_internal_key];
91+
let childable = internal;
92+
/* eslint-disable no-empty */
93+
while (
94+
((childable = childable.child), !childable || !childable.stateNode || !childable.stateNode.ScratchBlocks)
95+
) {}
96+
/* eslint-enable no-empty */
97+
return (this._cache.Blockly = childable.stateNode.ScratchBlocks);
98+
},
2299
},
23100
scratchClass(...args) {
24101
const classNamesArr = [

0 commit comments

Comments
 (0)