@@ -9,6 +9,39 @@ const path = document.querySelector("script[id='devtools-extension-module']").ge
99const getURL = ( x ) => `${ path } ${ x } ` ;
1010const 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+
1245const 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