@scratch/scratch-vm 11.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/.jsdoc.json +20 -0
  2. package/.nvmrc +1 -0
  3. package/CHANGELOG.md +7004 -0
  4. package/LICENSE +12 -0
  5. package/README.md +120 -0
  6. package/TRADEMARK +1 -0
  7. package/commitlint.config.js +4 -0
  8. package/docs/extensions.md +527 -0
  9. package/package.json +103 -0
  10. package/release.config.js +13 -0
  11. package/src/blocks/scratch3_control.js +206 -0
  12. package/src/blocks/scratch3_core_example.js +69 -0
  13. package/src/blocks/scratch3_data.js +257 -0
  14. package/src/blocks/scratch3_event.js +137 -0
  15. package/src/blocks/scratch3_looks.js +612 -0
  16. package/src/blocks/scratch3_motion.js +288 -0
  17. package/src/blocks/scratch3_operators.js +154 -0
  18. package/src/blocks/scratch3_procedures.js +79 -0
  19. package/src/blocks/scratch3_sensing.js +336 -0
  20. package/src/blocks/scratch3_sound.js +350 -0
  21. package/src/dispatch/central-dispatch.js +143 -0
  22. package/src/dispatch/shared-dispatch.js +235 -0
  23. package/src/dispatch/worker-dispatch.js +110 -0
  24. package/src/engine/adapter.js +176 -0
  25. package/src/engine/block-utility.js +242 -0
  26. package/src/engine/blocks-execute-cache.js +19 -0
  27. package/src/engine/blocks-runtime-cache.js +78 -0
  28. package/src/engine/blocks.js +1299 -0
  29. package/src/engine/comment.js +56 -0
  30. package/src/engine/execute.js +580 -0
  31. package/src/engine/monitor-record.js +23 -0
  32. package/src/engine/mutation-adapter.js +48 -0
  33. package/src/engine/profiler.js +390 -0
  34. package/src/engine/runtime.js +2686 -0
  35. package/src/engine/scratch-blocks-constants.js +27 -0
  36. package/src/engine/sequencer.js +361 -0
  37. package/src/engine/stage-layering.js +29 -0
  38. package/src/engine/target.js +799 -0
  39. package/src/engine/thread.js +404 -0
  40. package/src/engine/variable.js +70 -0
  41. package/src/extension-support/argument-type.js +47 -0
  42. package/src/extension-support/block-type.js +50 -0
  43. package/src/extension-support/define-messages.js +18 -0
  44. package/src/extension-support/extension-manager.js +440 -0
  45. package/src/extension-support/extension-metadata.js +64 -0
  46. package/src/extension-support/extension-worker.js +59 -0
  47. package/src/extension-support/reporter-scope.js +18 -0
  48. package/src/extension-support/target-type.js +17 -0
  49. package/src/extensions/scratch3_boost/index.js +2113 -0
  50. package/src/extensions/scratch3_ev3/index.js +1355 -0
  51. package/src/extensions/scratch3_gdx_for/index.js +981 -0
  52. package/src/extensions/scratch3_gdx_for/scratch-link-device-adapter.js +44 -0
  53. package/src/extensions/scratch3_makeymakey/index.js +396 -0
  54. package/src/extensions/scratch3_microbit/index.js +984 -0
  55. package/src/extensions/scratch3_music/assets/drums/1-snare.mp3 +0 -0
  56. package/src/extensions/scratch3_music/assets/drums/10-wood-block.mp3 +0 -0
  57. package/src/extensions/scratch3_music/assets/drums/11-cowbell.mp3 +0 -0
  58. package/src/extensions/scratch3_music/assets/drums/12-triangle.mp3 +0 -0
  59. package/src/extensions/scratch3_music/assets/drums/13-bongo.mp3 +0 -0
  60. package/src/extensions/scratch3_music/assets/drums/14-conga.mp3 +0 -0
  61. package/src/extensions/scratch3_music/assets/drums/15-cabasa.mp3 +0 -0
  62. package/src/extensions/scratch3_music/assets/drums/16-guiro.mp3 +0 -0
  63. package/src/extensions/scratch3_music/assets/drums/17-vibraslap.mp3 +0 -0
  64. package/src/extensions/scratch3_music/assets/drums/18-cuica.mp3 +0 -0
  65. package/src/extensions/scratch3_music/assets/drums/2-bass-drum.mp3 +0 -0
  66. package/src/extensions/scratch3_music/assets/drums/3-side-stick.mp3 +0 -0
  67. package/src/extensions/scratch3_music/assets/drums/4-crash-cymbal.mp3 +0 -0
  68. package/src/extensions/scratch3_music/assets/drums/5-open-hi-hat.mp3 +0 -0
  69. package/src/extensions/scratch3_music/assets/drums/6-closed-hi-hat.mp3 +0 -0
  70. package/src/extensions/scratch3_music/assets/drums/7-tambourine.mp3 +0 -0
  71. package/src/extensions/scratch3_music/assets/drums/8-hand-clap.mp3 +0 -0
  72. package/src/extensions/scratch3_music/assets/drums/9-claves.mp3 +0 -0
  73. package/src/extensions/scratch3_music/assets/instruments/1-piano/108.mp3 +0 -0
  74. package/src/extensions/scratch3_music/assets/instruments/1-piano/24.mp3 +0 -0
  75. package/src/extensions/scratch3_music/assets/instruments/1-piano/36.mp3 +0 -0
  76. package/src/extensions/scratch3_music/assets/instruments/1-piano/48.mp3 +0 -0
  77. package/src/extensions/scratch3_music/assets/instruments/1-piano/60.mp3 +0 -0
  78. package/src/extensions/scratch3_music/assets/instruments/1-piano/72.mp3 +0 -0
  79. package/src/extensions/scratch3_music/assets/instruments/1-piano/84.mp3 +0 -0
  80. package/src/extensions/scratch3_music/assets/instruments/1-piano/96.mp3 +0 -0
  81. package/src/extensions/scratch3_music/assets/instruments/10-clarinet/48.mp3 +0 -0
  82. package/src/extensions/scratch3_music/assets/instruments/10-clarinet/60.mp3 +0 -0
  83. package/src/extensions/scratch3_music/assets/instruments/11-saxophone/36.mp3 +0 -0
  84. package/src/extensions/scratch3_music/assets/instruments/11-saxophone/60.mp3 +0 -0
  85. package/src/extensions/scratch3_music/assets/instruments/11-saxophone/84.mp3 +0 -0
  86. package/src/extensions/scratch3_music/assets/instruments/12-flute/60.mp3 +0 -0
  87. package/src/extensions/scratch3_music/assets/instruments/12-flute/72.mp3 +0 -0
  88. package/src/extensions/scratch3_music/assets/instruments/13-wooden-flute/60.mp3 +0 -0
  89. package/src/extensions/scratch3_music/assets/instruments/13-wooden-flute/72.mp3 +0 -0
  90. package/src/extensions/scratch3_music/assets/instruments/14-bassoon/36.mp3 +0 -0
  91. package/src/extensions/scratch3_music/assets/instruments/14-bassoon/48.mp3 +0 -0
  92. package/src/extensions/scratch3_music/assets/instruments/14-bassoon/60.mp3 +0 -0
  93. package/src/extensions/scratch3_music/assets/instruments/15-choir/48.mp3 +0 -0
  94. package/src/extensions/scratch3_music/assets/instruments/15-choir/60.mp3 +0 -0
  95. package/src/extensions/scratch3_music/assets/instruments/15-choir/72.mp3 +0 -0
  96. package/src/extensions/scratch3_music/assets/instruments/16-vibraphone/60.mp3 +0 -0
  97. package/src/extensions/scratch3_music/assets/instruments/16-vibraphone/72.mp3 +0 -0
  98. package/src/extensions/scratch3_music/assets/instruments/17-music-box/60.mp3 +0 -0
  99. package/src/extensions/scratch3_music/assets/instruments/18-steel-drum/60.mp3 +0 -0
  100. package/src/extensions/scratch3_music/assets/instruments/19-marimba/60.mp3 +0 -0
  101. package/src/extensions/scratch3_music/assets/instruments/2-electric-piano/60.mp3 +0 -0
  102. package/src/extensions/scratch3_music/assets/instruments/20-synth-lead/60.mp3 +0 -0
  103. package/src/extensions/scratch3_music/assets/instruments/21-synth-pad/60.mp3 +0 -0
  104. package/src/extensions/scratch3_music/assets/instruments/3-organ/60.mp3 +0 -0
  105. package/src/extensions/scratch3_music/assets/instruments/4-guitar/60.mp3 +0 -0
  106. package/src/extensions/scratch3_music/assets/instruments/5-electric-guitar/60.mp3 +0 -0
  107. package/src/extensions/scratch3_music/assets/instruments/6-bass/36.mp3 +0 -0
  108. package/src/extensions/scratch3_music/assets/instruments/6-bass/48.mp3 +0 -0
  109. package/src/extensions/scratch3_music/assets/instruments/7-pizzicato/60.mp3 +0 -0
  110. package/src/extensions/scratch3_music/assets/instruments/8-cello/36.mp3 +0 -0
  111. package/src/extensions/scratch3_music/assets/instruments/8-cello/48.mp3 +0 -0
  112. package/src/extensions/scratch3_music/assets/instruments/8-cello/60.mp3 +0 -0
  113. package/src/extensions/scratch3_music/assets/instruments/9-trombone/36.mp3 +0 -0
  114. package/src/extensions/scratch3_music/assets/instruments/9-trombone/48.mp3 +0 -0
  115. package/src/extensions/scratch3_music/assets/instruments/9-trombone/60.mp3 +0 -0
  116. package/src/extensions/scratch3_music/index.js +1333 -0
  117. package/src/extensions/scratch3_music/manifest.js +63 -0
  118. package/src/extensions/scratch3_pen/index.js +770 -0
  119. package/src/extensions/scratch3_speech2text/index.js +700 -0
  120. package/src/extensions/scratch3_text2speech/index.js +766 -0
  121. package/src/extensions/scratch3_translate/index.js +286 -0
  122. package/src/extensions/scratch3_video_sensing/debug.js +13 -0
  123. package/src/extensions/scratch3_video_sensing/index.js +594 -0
  124. package/src/extensions/scratch3_video_sensing/library.js +384 -0
  125. package/src/extensions/scratch3_video_sensing/math.js +76 -0
  126. package/src/extensions/scratch3_video_sensing/view.js +509 -0
  127. package/src/extensions/scratch3_wedo2/index.js +1616 -0
  128. package/src/import/load-costume.js +416 -0
  129. package/src/import/load-sound.js +116 -0
  130. package/src/index.js +10 -0
  131. package/src/io/ble.js +256 -0
  132. package/src/io/bt.js +202 -0
  133. package/src/io/clock.js +39 -0
  134. package/src/io/cloud.js +169 -0
  135. package/src/io/keyboard.js +146 -0
  136. package/src/io/mouse.js +145 -0
  137. package/src/io/mouseWheel.js +28 -0
  138. package/src/io/userData.js +24 -0
  139. package/src/io/video.js +210 -0
  140. package/src/playground/benchmark.css +83 -0
  141. package/src/playground/benchmark.js +753 -0
  142. package/src/playground/index.html +111 -0
  143. package/src/playground/suite.css +78 -0
  144. package/src/playground/suite.html +25 -0
  145. package/src/playground/suite.js +544 -0
  146. package/src/playground/video-sensing.html +18 -0
  147. package/src/playground/video-sensing.js +133 -0
  148. package/src/serialization/deserialize-assets.js +178 -0
  149. package/src/serialization/sb2.js +1295 -0
  150. package/src/serialization/sb2_specmap.js +1818 -0
  151. package/src/serialization/sb3.js +1320 -0
  152. package/src/serialization/serialize-assets.js +60 -0
  153. package/src/sprites/rendered-target.js +1115 -0
  154. package/src/sprites/sprite.js +177 -0
  155. package/src/util/base64-util.js +48 -0
  156. package/src/util/cast.js +218 -0
  157. package/src/util/clone.js +17 -0
  158. package/src/util/color.js +204 -0
  159. package/src/util/fetch-with-timeout.js +53 -0
  160. package/src/util/get-monitor-id.js +33 -0
  161. package/src/util/jsonrpc.js +114 -0
  162. package/src/util/log.js +4 -0
  163. package/src/util/math-util.js +122 -0
  164. package/src/util/maybe-format-message.js +18 -0
  165. package/src/util/new-block-ids.js +33 -0
  166. package/src/util/rateLimiter.js +73 -0
  167. package/src/util/scratch-link-websocket.js +133 -0
  168. package/src/util/string-util.js +94 -0
  169. package/src/util/task-queue.js +203 -0
  170. package/src/util/timer.js +103 -0
  171. package/src/util/uid.js +29 -0
  172. package/src/util/variable-util.js +48 -0
  173. package/src/util/xml-escape.js +33 -0
  174. package/src/virtual-machine.js +1586 -0
  175. package/webpack.config.js +134 -0
package/LICENSE ADDED
@@ -0,0 +1,12 @@
1
+ Copyright (c) 2016, Massachusetts Institute of Technology
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5
+
6
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7
+
8
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9
+
10
+ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
11
+
12
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,120 @@
1
+ ## scratch-vm
2
+ #### Scratch VM is a library for representing, running, and maintaining the state of computer programs written using [Scratch Blocks](https://github.com/scratchfoundation/scratch-blocks).
3
+
4
+ [![CI/CD](https://github.com/scratchfoundation/scratch-vm/actions/workflows/ci-cd.yml/badge.svg)](https://github.com/scratchfoundation/scratch-vm/actions/workflows/ci-cd.yml)
5
+
6
+ ## Installation
7
+ This requires you to have Git and Node.js installed.
8
+
9
+ To install as a dependency for your own application:
10
+ ```bash
11
+ npm install scratch-vm
12
+ ```
13
+ To set up a development environment to edit scratch-vm yourself:
14
+ ```bash
15
+ git clone https://github.com/scratchfoundation/scratch-vm.git
16
+ cd scratch-vm
17
+ npm install
18
+ ```
19
+
20
+ ## Development Server
21
+ This requires Node.js to be installed.
22
+
23
+ For convenience, we've included a development server with the VM. This is sometimes useful when running in an environment that's loading remote resources (e.g., SVGs from the Scratch server). If you would like to use your modified VM with the full Scratch 3.0 GUI, [follow the instructions to link the VM to the GUI](https://github.com/scratchfoundation/scratch-gui/wiki/Getting-Started).
24
+
25
+ ## Running the Development Server
26
+ Open a Command Prompt or Terminal in the repository and run:
27
+ ```bash
28
+ npm start
29
+ ```
30
+
31
+ ## Playground
32
+ To view the Playground, make sure the dev server's running and go to [http://localhost:8073/playground/](http://localhost:8073/playground/) - you will be directed to the playground, which demonstrates various tools and internal state.
33
+
34
+ ![VM Playground Screenshot](https://i.imgur.com/nOCNqEc.gif)
35
+
36
+
37
+ ## Standalone Build
38
+ ```bash
39
+ npm run build
40
+ ```
41
+
42
+ ```html
43
+ <script src="/path/to/dist/web/scratch-vm.js"></script>
44
+ <script>
45
+ var vm = new window.VirtualMachine();
46
+ // do things
47
+ </script>
48
+ ```
49
+
50
+ ## How to include in a Node.js App
51
+ For an extended setup example, check out the /src/playground directory, which includes a fully running VM instance.
52
+ ```js
53
+ var VirtualMachine = require('@scratch/scratch-vm');
54
+ var vm = new VirtualMachine();
55
+
56
+ // Block events
57
+ Scratch.workspace.addChangeListener(vm.blockListener);
58
+
59
+ // Run threads
60
+ vm.start();
61
+ ```
62
+
63
+ ## Abstract Syntax Tree
64
+
65
+ #### Overview
66
+ The Virtual Machine constructs and maintains the state of an [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) (AST) by listening to events emitted by the [scratch-blocks](https://github.com/scratchfoundation/scratch-blocks) workspace via the `blockListener`. Each target (code-running object, for example, a sprite) keeps an AST for its blocks. At any time, the current state of an AST can be viewed by inspecting the `vm.runtime.targets[...].blocks` object.
67
+
68
+ #### Anatomy of a Block
69
+ The VM's block representation contains all the important information for execution and storage. Here's an example representing the "when key pressed" script on a workspace:
70
+ ```json
71
+ {
72
+ "_blocks": {
73
+ "Q]PK~yJ@BTV8Y~FfISeo": {
74
+ "id": "Q]PK~yJ@BTV8Y~FfISeo",
75
+ "opcode": "event_whenkeypressed",
76
+ "inputs": {
77
+ },
78
+ "fields": {
79
+ "KEY_OPTION": {
80
+ "name": "KEY_OPTION",
81
+ "value": "space"
82
+ }
83
+ },
84
+ "next": null,
85
+ "topLevel": true,
86
+ "parent": null,
87
+ "shadow": false,
88
+ "x": -69.333333333333,
89
+ "y": 174
90
+ }
91
+ },
92
+ "_scripts": [
93
+ "Q]PK~yJ@BTV8Y~FfISeo"
94
+ ]
95
+ }
96
+ ```
97
+
98
+ ## Testing
99
+ ```bash
100
+ npm test
101
+ ```
102
+
103
+ ```bash
104
+ npm run coverage
105
+ ```
106
+
107
+ ## Publishing to GitHub Pages
108
+ ```bash
109
+ npm run deploy
110
+ ```
111
+
112
+ This will push the currently built playground to the gh-pages branch of the
113
+ currently tracked remote. If you would like to change where to push to, add
114
+ a repo url argument:
115
+ ```bash
116
+ npm run deploy -- -r <your repo url>
117
+ ```
118
+
119
+ ## Donate
120
+ We provide [Scratch](https://scratch.mit.edu) free of charge, and want to keep it that way! Please consider making a [donation](https://secure.donationpay.org/scratchfoundation/) to support our continued engineering, design, community, and resource development efforts. Donations of any size are appreciated. Thank you!
package/TRADEMARK ADDED
@@ -0,0 +1 @@
1
+ The Scratch trademarks, including the Scratch name, logo, the Scratch Cat, Gobo, Pico, Nano, Tera and Giga graphics (the "Marks"), are property of the Massachusetts Institute of Technology (MIT). Marks may not be used to endorse or promote products derived from this software without specific prior written permission.
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ extends: ['@commitlint/config-conventional'],
3
+ ignores: [message => message.startsWith('chore(release):')]
4
+ };
@@ -0,0 +1,527 @@
1
+ # Scratch 3.0 Extensions
2
+
3
+ This document describes technical topics related to Scratch 3.0 extension development, including the Scratch 3.0
4
+ extension specification.
5
+
6
+ ## Types of Extensions
7
+
8
+ There are four types of extensions that can define everything from the Scratch's core library (such as the "Looks" and
9
+ "Operators" categories) to unofficial extensions that can be loaded from a remote URL.
10
+
11
+ **Scratch 3.0 does not yet support unofficial extensions.**
12
+
13
+ | | Core | Team | Official | Unofficial |
14
+ | ------------------------------ | ---- | ---- | -------- | ---------- |
15
+ | Developed by Scratch Team | √ | √ | O | X |
16
+ | Maintained by Scratch Team | √ | √ | O | X |
17
+ | Shown in Library | X | √ | √ | X |
18
+ | Sandboxed | X | X | √ | √ |
19
+ | Can save projects to community | √ | √ | √ | X |
20
+
21
+ ## JavaScript Environment
22
+
23
+ Most Scratch 3.0 is written using JavaScript features not yet commonly supported by browsers. For compatibility we
24
+ transpile the code to ES5 before publishing or deploying. Any extension included in the `scratch-vm` repository may
25
+ use ES6+ features and may use `require` to reference other code within the `scratch-vm` repository.
26
+
27
+ Unofficial extensions must be self-contained. Authors of unofficial extensions are responsible for ensuring browser
28
+ compatibility for those extensions, including transpiling if necessary.
29
+
30
+ ## Translation
31
+
32
+ Scratch extensions use the [ICU message format](http://userguide.icu-project.org/formatparse/messages) to handle
33
+ translation across languages. For **core, team, and official** extensions, the function `formatMessage` is used to
34
+ wrap any ICU messages that need to be exported to the [Scratch Transifex group](https://www.transifex.com/llk/public/)
35
+ for translation.
36
+
37
+ **All extensions** may additionally define a `translation_map` object within the `getInfo` function which can provide
38
+ translations within an extension itself. The "Annotated Example" below provides a more complete illustration of how
39
+ translation within an extension can be managed. **WARNING:** the `translation_map` feature is currently in the
40
+ proposal phase and may change before implementation.
41
+
42
+ ## Backwards Compatibility
43
+
44
+ Scratch is designed to be fully backwards compatible. Because of this, block definitions and opcodes should *never*
45
+ change in a way that could cause previously saved projects to fail to load or to act in unexpected / inconsistent
46
+ ways.
47
+
48
+ ## Defining an Extension
49
+
50
+ Scratch extensions are defined as a single Javascript class which accepts either a reference to the Scratch
51
+ [VM](https://github.com/scratchfoundation/scratch-vm) runtime or a "runtime proxy" which handles communication with the Scratch VM
52
+ across a well defined worker boundary (i.e. the sandbox).
53
+
54
+ ```js
55
+ class SomeBlocks {
56
+ constructor (runtime) {
57
+ /**
58
+ * Store this for later communication with the Scratch VM runtime.
59
+ * If this extension is running in a sandbox then `runtime` is an async proxy object.
60
+ * @type {Runtime}
61
+ */
62
+ this.runtime = runtime;
63
+ }
64
+
65
+ // ...
66
+ }
67
+ ```
68
+
69
+ All extensions must define a function called `getInfo` which returns an object that contains the information needed to
70
+ render both the blocks and the extension itself.
71
+
72
+ ```js
73
+ // Core, Team, and Official extensions can `require` VM code:
74
+ const ArgumentType = require('../../extension-support/argument-type');
75
+ const BlockType = require('../../extension-support/block-type');
76
+
77
+ class SomeBlocks {
78
+ // ...
79
+ getInfo () {
80
+ return {
81
+ id: 'someBlocks',
82
+ name: 'Some Blocks',
83
+ blocks: [
84
+ {
85
+ opcode: 'myReporter',
86
+ blockType: BlockType.REPORTER,
87
+ text: 'letter [LETTER_NUM] of [TEXT]',
88
+ arguments: {
89
+ LETTER_NUM: {
90
+ type: ArgumentType.STRING,
91
+ defaultValue: '1'
92
+ },
93
+ TEXT: {
94
+ type: ArgumentType.STRING,
95
+ defaultValue: 'text'
96
+ }
97
+ }
98
+ }
99
+ ]
100
+ };
101
+ }
102
+ // ...
103
+ }
104
+ ```
105
+
106
+ Finally the extension must define a function for any "opcode" defined in the blocks. For example:
107
+
108
+ ```js
109
+ class SomeBlocks {
110
+ // ...
111
+ myReporter (args) {
112
+ return args.TEXT.charAt(args.LETTER_NUM);
113
+ };
114
+ // ...
115
+ }
116
+ ```
117
+ ### Block Arguments
118
+ In addition to displaying text, blocks can have arguments in the form of slots to take other blocks getting plugged in, or dropdown menus to select an argument value from a list of possible values.
119
+
120
+ The possible types of block arguments are as follows:
121
+
122
+ - String - a string input, this is a type-able field which also accepts other reporter blocks to be plugged in
123
+ - Number - an input similar to the string input, but the type-able values are constrained to numbers.
124
+ - Angle - an input similar to the number input, but it has an additional UI to be able to pick an angle from a
125
+ circular dial
126
+ - Boolean - an input for a boolean (hexagonal shaped) reporter block. This field is not type-able.
127
+ - Color - an input which displays a color swatch. This field has additional UI to pick a color by choosing values for the color's hue, saturation and brightness. Optionally, the defaultValue for the color picker can also be chosen if the extension developer wishes to display the same color every time the extension is added. If the defaultValue is left out, the default behavior of picking a random color when the extension is loaded will be used.
128
+ - Matrix - an input which displays a 5 x 5 matrix of cells, where each cell can be filled in or clear.
129
+ - Note - a numeric input which can select a musical note. This field has additional UI to select a note from a
130
+ visual keyboard.
131
+ - Image - an inline image displayed on a block. This is a special argument type in that it does not represent a value and does not accept other blocks to be plugged-in in place of this block field. See the section below about "Adding an Inline Image".
132
+
133
+ #### Adding an Inline Image
134
+ In addition to specifying block arguments (an example of string arguments shown in the code snippet above),
135
+ you can also specify an inline image for the block. You must include a dataURI for the image. If left unspecified, blank space will be allocated for the image and a warning will be logged in the console.
136
+ You can optionally also specify `flipRTL`, a property indicating whether the image should be flipped horizontally when the editor has a right to left language selected as its locale. By default, the image is not flipped.
137
+
138
+ ```js
139
+ return {
140
+ // ...
141
+ blocks: [
142
+ {
143
+ //...
144
+ arguments: {
145
+ MY_IMAGE: {
146
+ type: ArgumentType.IMAGE,
147
+ dataURI: 'myImageData',
148
+ alt: 'This is an image',
149
+ flipRTL: true
150
+ }
151
+ }
152
+ }
153
+ ]
154
+ }
155
+ ```
156
+
157
+
158
+
159
+
160
+ #### Defining a Menu
161
+
162
+ To display a drop-down menu for a block argument, specify the `menu` property of that argument and a matching item in
163
+ the `menus` section of your extension's definition:
164
+
165
+ ```js
166
+ return {
167
+ // ...
168
+ blocks: [
169
+ {
170
+ // ...
171
+ arguments: {
172
+ FOO: {
173
+ type: ArgumentType.NUMBER,
174
+ menu: 'fooMenu'
175
+ }
176
+ }
177
+ }
178
+ ],
179
+ menus: {
180
+ fooMenu: {
181
+ items: ['a', 'b', 'c']
182
+ }
183
+ }
184
+ }
185
+ ```
186
+
187
+ The items in a menu may be specified with an array or with the name of a function which returns an array. The two
188
+ simplest forms for menu definitions are:
189
+
190
+ ```js
191
+ getInfo () {
192
+ return {
193
+ menus: {
194
+ staticMenu: ['static 1', 'static 2', 'static 3'],
195
+ dynamicMenu: 'getDynamicMenuItems'
196
+ }
197
+ };
198
+ }
199
+ // this member function will be called each time the menu opens
200
+ getDynamicMenuItems () {
201
+ return ['dynamic 1', 'dynamic 2', 'dynamic 3'];
202
+ }
203
+ ```
204
+
205
+ The examples above are shorthand for these equivalent definitions:
206
+
207
+ ```js
208
+ getInfo () {
209
+ return {
210
+ menus: {
211
+ staticMenu: {
212
+ items: ['static 1', 'static 2', 'static 3']
213
+ },
214
+ dynamicMenu: {
215
+ items: 'getDynamicMenuItems'
216
+ }
217
+ }
218
+ };
219
+ }
220
+ // this member function will be called each time the menu opens
221
+ getDynamicMenuItems () {
222
+ return ['dynamic 1', 'dynamic 2', 'dynamic 3'];
223
+ }
224
+ ```
225
+
226
+ If a menu item needs a label that doesn't match its value -- for example, if the label needs to be displayed in the
227
+ user's language but the value needs to stay constant -- the menu item may be an object instead of a string. This works
228
+ for both static and dynamic menu items:
229
+
230
+ ```js
231
+ menus: {
232
+ staticMenu: [
233
+ {
234
+ text: formatMessage(/* ... */),
235
+ value: 42
236
+ }
237
+ ]
238
+ }
239
+ ```
240
+
241
+ ##### Accepting reporters ("droppable" menus)
242
+
243
+ By default it is not possible to specify the value of a dropdown menu by inserting a reporter block. While we
244
+ encourage extension authors to make their menus accept reporters when possible, doing so requires careful
245
+ consideration to avoid confusion and frustration on the part of those using the extension.
246
+
247
+ A few of these considerations include:
248
+
249
+ * The valid values for the menu should not change when the user changes the Scratch language setting.
250
+ * In particular, changing languages should never break a working project.
251
+ * The average Scratch user should be able to figure out the valid values for this input without referring to extension
252
+ documentation.
253
+ * One way to ensure this is to make an item's text match or include the item's value. For example, the official Music
254
+ extension contains menu items with names like "(1) Piano" with value 1, "(8) Cello" with value 8, and so on.
255
+ * The block should accept any value as input, even "invalid" values.
256
+ * Scratch has no concept of a runtime error!
257
+ * For a command block, sometimes the best option is to do nothing.
258
+ * For a reporter, returning zero or the empty string might make sense.
259
+ * The block should be forgiving in its interpretation of inputs.
260
+ * For example, if the block expects a string and receives a number it may make sense to interpret the number as a
261
+ string instead of treating it as invalid input.
262
+
263
+ The `acceptReporters` flag indicates that the user can drop a reporter onto the menu input:
264
+
265
+ ```js
266
+ menus: {
267
+ staticMenu: {
268
+ acceptReporters: true,
269
+ items: [/*...*/]
270
+ },
271
+ dynamicMenu: {
272
+ acceptReporters: true,
273
+ items: 'getDynamicMenuItems'
274
+ }
275
+ }
276
+ ```
277
+
278
+ ## Annotated Example
279
+
280
+ ```js
281
+ // Core, Team, and Official extensions can `require` VM code:
282
+ const ArgumentType = require('../../extension-support/argument-type');
283
+ const BlockType = require('../../extension-support/block-type');
284
+ const TargetType = require('../../extension-support/target-type');
285
+
286
+ // ...or VM dependencies:
287
+ const formatMessage = require('format-message');
288
+
289
+ // Core, Team, and Official extension classes should be registered statically with the Extension Manager.
290
+ // See: scratch-vm/src/extension-support/extension-manager.js
291
+ class SomeBlocks {
292
+ constructor (runtime) {
293
+ /**
294
+ * Store this for later communication with the Scratch VM runtime.
295
+ * If this extension is running in a sandbox then `runtime` is an async proxy object.
296
+ * @type {Runtime}
297
+ */
298
+ this.runtime = runtime;
299
+ }
300
+
301
+ /**
302
+ * @return {object} This extension's metadata.
303
+ */
304
+ getInfo () {
305
+ return {
306
+ // Required: the machine-readable name of this extension.
307
+ // Will be used as the extension's namespace.
308
+ // Allowed characters are those matching the regular expression [\w-]: A-Z, a-z, 0-9, and hyphen ("-").
309
+ id: 'someBlocks',
310
+
311
+ // Core extensions only: override the default extension block colors.
312
+ color1: '#FF8C1A',
313
+ color2: '#DB6E00',
314
+
315
+ // Optional: the human-readable name of this extension as string.
316
+ // This and any other string to be displayed in the Scratch UI may either be
317
+ // a string or a call to `formatMessage`; a plain string will not be
318
+ // translated whereas a call to `formatMessage` will connect the string
319
+ // to the translation map (see below). The `formatMessage` call is
320
+ // similar to `formatMessage` from `react-intl` in form, but will actually
321
+ // call some extension support code to do its magic. For example, we will
322
+ // internally namespace the messages such that two extensions could have
323
+ // messages with the same ID without colliding.
324
+ // See also: https://github.com/yahoo/react-intl/wiki/API#formatmessage
325
+ name: formatMessage({
326
+ id: 'extensionName',
327
+ defaultMessage: 'Some Blocks',
328
+ description: 'The name of the "Some Blocks" extension'
329
+ }),
330
+
331
+ // Optional: URI for a block icon, to display at the edge of each block for this
332
+ // extension. Data URI OK.
333
+ // TODO: what file types are OK? All web images? Just PNG?
334
+ blockIconURI: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAFCAAAAACyOJm3AAAAFklEQVQYV2P4DwMMEMgAI/+DEUIMBgAEWB7i7uidhAAAAABJRU5ErkJggg==',
335
+
336
+ // Optional: URI for an icon to be displayed in the blocks category menu.
337
+ // If not present, the menu will display the block icon, if one is present.
338
+ // Otherwise, the category menu shows its default filled circle.
339
+ // Data URI OK.
340
+ // TODO: what file types are OK? All web images? Just PNG?
341
+ menuIconURI: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAFCAAAAACyOJm3AAAAFklEQVQYV2P4DwMMEMgAI/+DEUIMBgAEWB7i7uidhAAAAABJRU5ErkJggg==',
342
+
343
+ // Optional: Link to documentation content for this extension.
344
+ // If not present, offer no link.
345
+ docsURI: 'https://....',
346
+
347
+ // Required: the list of blocks implemented by this extension,
348
+ // in the order intended for display.
349
+ blocks: [
350
+ {
351
+ // Required: the machine-readable name of this operation.
352
+ // This will appear in project JSON.
353
+ opcode: 'myReporter', // becomes 'someBlocks.myReporter'
354
+
355
+ // Required: the kind of block we're defining, from a predefined list.
356
+ // Fully supported block types:
357
+ // BlockType.BOOLEAN - same as REPORTER but returns a Boolean value
358
+ // BlockType.COMMAND - a normal command block, like "move {} steps"
359
+ // BlockType.HAT - starts a stack if its value changes from falsy to truthy ("edge triggered")
360
+ // BlockType.REPORTER - returns a value, like "direction"
361
+ // Block types in development or for internal use only:
362
+ // BlockType.BUTTON - place a button in the block palette
363
+ // BlockType.CONDITIONAL - control flow, like "if {}" or "if {} else {}"
364
+ // A CONDITIONAL block may return the one-based index of a branch to
365
+ // run, or it may return zero/falsy to run no branch.
366
+ // BlockType.EVENT - starts a stack in response to an event (full spec TBD)
367
+ // BlockType.LOOP - control flow, like "repeat {} {}" or "forever {}"
368
+ // A LOOP block is like a CONDITIONAL block with two differences:
369
+ // - the block is assumed to have exactly one child branch, and
370
+ // - each time a child branch finishes, the loop block is called again.
371
+ blockType: BlockType.REPORTER,
372
+
373
+ // Required for CONDITIONAL blocks, ignored for others: the number of
374
+ // child branches this block controls. An "if" or "repeat" block would
375
+ // specify a branch count of 1; an "if-else" block would specify a
376
+ // branch count of 2.
377
+ // TODO: should we support dynamic branch count for "switch"-likes?
378
+ branchCount: 0,
379
+
380
+ // Optional, default false: whether or not this block ends a stack.
381
+ // The "forever" and "stop all" blocks would specify true here.
382
+ terminal: true,
383
+
384
+ // Optional, default false: whether or not to block all threads while
385
+ // this block is busy. This is for things like the "touching color"
386
+ // block in compatibility mode, and is only needed if the VM runs in a
387
+ // worker. We might even consider omitting it from extension docs...
388
+ blockAllThreads: false,
389
+
390
+ // Required: the human-readable text on this block, including argument
391
+ // placeholders. Argument placeholders should be in [MACRO_CASE] and
392
+ // must be [ENCLOSED_WITHIN_SQUARE_BRACKETS].
393
+ text: formatMessage({
394
+ id: 'myReporter',
395
+ defaultMessage: 'letter [LETTER_NUM] of [TEXT]',
396
+ description: 'Label on the "myReporter" block'
397
+ }),
398
+
399
+ // Required: describe each argument.
400
+ // Argument order may change during translation, so arguments are
401
+ // identified by their placeholder name. In those situations where
402
+ // arguments must be ordered or assigned an ordinal, such as interaction
403
+ // with Scratch Blocks, arguments are ordered as they are in the default
404
+ // translation (probably English).
405
+ arguments: {
406
+ // Required: the ID of the argument, which will be the name in the
407
+ // args object passed to the implementation function.
408
+ LETTER_NUM: {
409
+ // Required: type of the argument / shape of the block input
410
+ type: ArgumentType.NUMBER,
411
+
412
+ // Optional: the default value of the argument
413
+ default: 1
414
+ },
415
+
416
+ // Required: the ID of the argument, which will be the name in the
417
+ // args object passed to the implementation function.
418
+ TEXT: {
419
+ // Required: type of the argument / shape of the block input
420
+ type: ArgumentType.STRING,
421
+
422
+ // Optional: the default value of the argument
423
+ default: formatMessage({
424
+ id: 'myReporter.TEXT_default',
425
+ defaultMessage: 'text',
426
+ description: 'Default for "TEXT" argument of "someBlocks.myReporter"'
427
+ })
428
+ }
429
+ },
430
+
431
+ // Optional: the function implementing this block.
432
+ // If absent, assume `func` is the same as `opcode`.
433
+ func: 'myReporter',
434
+
435
+ // Optional: list of target types for which this block should appear.
436
+ // If absent, assume it applies to all builtin targets -- that is:
437
+ // [TargetType.SPRITE, TargetType.STAGE]
438
+ filter: [TargetType.SPRITE]
439
+ },
440
+ {
441
+ // Another block...
442
+ }
443
+ ],
444
+
445
+ // Optional: define extension-specific menus here.
446
+ menus: {
447
+ // Required: an identifier for this menu, unique within this extension.
448
+ menuA: [
449
+ // Static menu: list items which should appear in the menu.
450
+ {
451
+ // Required: the value of the menu item when it is chosen.
452
+ value: 'itemId1',
453
+
454
+ // Optional: the human-readable label for this item.
455
+ // Use `value` as the text if this is absent.
456
+ text: formatMessage({
457
+ id: 'menuA_item1',
458
+ defaultMessage: 'Item One',
459
+ description: 'Label for item 1 of menu A in "Some Blocks" extension'
460
+ })
461
+ },
462
+
463
+ // The simplest form of a list item is a string which will be used as
464
+ // both value and text.
465
+ 'itemId2'
466
+ ],
467
+
468
+ // Dynamic menu: returns an array as above.
469
+ // Called each time the menu is opened.
470
+ menuB: 'getItemsForMenuB',
471
+
472
+ // The examples above are shorthand for setting only the `items` property in this full form:
473
+ menuC: {
474
+ // This flag makes a "droppable" menu: the menu will allow dropping a reporter in for the input.
475
+ acceptReporters: true,
476
+
477
+ // The `item` property may be an array or function name as in previous menu examples.
478
+ items: [/*...*/] || 'getItemsForMenuC'
479
+ }
480
+ },
481
+
482
+ // Optional: translations (UNSTABLE - NOT YET SUPPORTED)
483
+ translation_map: {
484
+ de: {
485
+ 'extensionName': 'Einige Blöcke',
486
+ 'myReporter': 'Buchstabe [LETTER_NUM] von [TEXT]',
487
+ 'myReporter.TEXT_default': 'Text',
488
+ 'menuA_item1': 'Artikel eins',
489
+
490
+ // Dynamic menus can be translated too
491
+ 'menuB_example': 'Beispiel',
492
+
493
+ // This message contains ICU placeholders (see `myReporter()` below)
494
+ 'myReporter.result': 'Buchstabe {LETTER_NUM} von {TEXT} ist {LETTER}.'
495
+ },
496
+ it: {
497
+ // ...
498
+ }
499
+ }
500
+ };
501
+ };
502
+
503
+ /**
504
+ * Implement myReporter.
505
+ * @param {object} args - the block's arguments.
506
+ * @property {string} MY_ARG - the string value of the argument.
507
+ * @returns {string} a string which includes the block argument value.
508
+ */
509
+ myReporter (args) {
510
+ // This message contains ICU placeholders, not Scratch placeholders
511
+ const message = formatMessage({
512
+ id: 'myReporter.result',
513
+ defaultMessage: 'Letter {LETTER_NUM} of {TEXT} is {LETTER}.',
514
+ description: 'The text template for the "myReporter" block result'
515
+ });
516
+
517
+ // Note: this implementation is not Unicode-clean; it's just here as an example.
518
+ const result = args.TEXT.charAt(args.LETTER_NUM);
519
+
520
+ return message.format({
521
+ LETTER_NUM: args.LETTER_NUM,
522
+ TEXT: args.TEXT,
523
+ LETTER: result
524
+ });
525
+ };
526
+ }
527
+ ```