@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/package.json ADDED
@@ -0,0 +1,103 @@
1
+ {
2
+ "name": "@scratch/scratch-vm",
3
+ "version": "11.0.0-beta.1",
4
+ "description": "Virtual Machine for Scratch 3.0",
5
+ "author": "Massachusetts Institute of Technology",
6
+ "license": "AGPL-3.0-only",
7
+ "homepage": "https://github.com/scratchfoundation/scratch-vm#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/scratchfoundation/scratch-editor.git"
11
+ },
12
+ "main": "./dist/node/scratch-vm.js",
13
+ "browser": "./dist/web/scratch-vm.js",
14
+ "exports": {
15
+ "webpack": "./src/index.js",
16
+ "browser": "./dist/web/scratch-vm.js",
17
+ "node": "./dist/node/scratch-vm.js",
18
+ "default": "./src/index.js"
19
+ },
20
+ "scripts": {
21
+ "build": "npm run docs && webpack --progress",
22
+ "coverage": "tap ./test/{unit,integration}/*.js --coverage --coverage-report=lcov",
23
+ "docs": "jsdoc -c .jsdoc.json",
24
+ "i18n:src": "mkdirp translations/core && format-message extract --out-file translations/core/en.json src/extensions/**/index.js",
25
+ "i18n:push": "tx-push-src scratch-editor extensions translations/core/en.json",
26
+ "lint": "eslint . && format-message lint src/**/*.js",
27
+ "prepublish": "in-publish && npm run build || not-in-publish",
28
+ "start": "webpack serve",
29
+ "tap": "tap ./test/{unit,integration}/*.js",
30
+ "tap:unit": "tap ./test/unit/*.js",
31
+ "tap:integration": "tap ./test/integration/*.js",
32
+ "test": "npm run lint && npm run tap",
33
+ "watch": "webpack --progress --watch"
34
+ },
35
+ "browserslist": [
36
+ "Chrome >= 63",
37
+ "Edge >= 15",
38
+ "Firefox >= 57",
39
+ "Safari >= 11"
40
+ ],
41
+ "tap": {
42
+ "branches": 60,
43
+ "functions": 70,
44
+ "lines": 70,
45
+ "statements": 70
46
+ },
47
+ "dependencies": {
48
+ "@scratch/scratch-render": "11.0.0-beta.1",
49
+ "@scratch/scratch-svg-renderer": "11.0.0-beta.1",
50
+ "@vernier/godirect": "^1.5.0",
51
+ "arraybuffer-loader": "^1.0.6",
52
+ "atob": "^2.1.2",
53
+ "btoa": "^1.2.1",
54
+ "canvas-toBlob": "^1.0.0",
55
+ "decode-html": "^2.0.0",
56
+ "diff-match-patch": "^1.0.4",
57
+ "format-message": "^6.2.1",
58
+ "htmlparser2": "^3.10.0",
59
+ "immutable": "^3.8.1",
60
+ "jszip": "^3.1.5",
61
+ "minilog": "^3.1.0",
62
+ "scratch-audio": "^1.0.6",
63
+ "scratch-parser": "^5.1.1",
64
+ "scratch-sb1-converter": "^1.0.0",
65
+ "scratch-storage": "^4.0.24",
66
+ "scratch-translate-extension-languages": "^1.0.0",
67
+ "text-encoding": "^0.7.0",
68
+ "uuid": "^8.3.2",
69
+ "web-worker": "^1.3.0"
70
+ },
71
+ "devDependencies": {
72
+ "@babel/core": "7.25.2",
73
+ "@babel/eslint-parser": "7.25.1",
74
+ "@babel/preset-env": "7.25.4",
75
+ "adm-zip": "0.4.11",
76
+ "babel-loader": "9.1.3",
77
+ "callsite": "1.0.0",
78
+ "copy-webpack-plugin": "4.6.0",
79
+ "docdash": "1.2.0",
80
+ "eslint": "8.57.0",
81
+ "eslint-config-scratch": "9.0.8",
82
+ "expose-loader": "1.0.3",
83
+ "file-loader": "6.2.0",
84
+ "format-message-cli": "6.2.4",
85
+ "in-publish": "2.0.1",
86
+ "js-md5": "0.7.3",
87
+ "jsdoc": "3.6.11",
88
+ "json": "^9.0.4",
89
+ "pngjs": "3.4.0",
90
+ "scratch-blocks": "1.1.206",
91
+ "scratch-l10n": "3.18.286",
92
+ "scratch-render-fonts": "1.0.92",
93
+ "scratch-semantic-release-config": "1.0.14",
94
+ "scratch-webpack-configuration": "1.6.0",
95
+ "script-loader": "0.7.2",
96
+ "semantic-release": "19.0.5",
97
+ "stats.js": "0.17.0",
98
+ "tap": "16.3.10",
99
+ "webpack": "5.94.0",
100
+ "webpack-cli": "4.10.0",
101
+ "webpack-dev-server": "3.11.3"
102
+ }
103
+ }
@@ -0,0 +1,13 @@
1
+ module.exports = {
2
+ extends: 'scratch-semantic-release-config',
3
+ branches: [
4
+ {
5
+ name: 'develop'
6
+ // default channel
7
+ },
8
+ {
9
+ name: 'hotfix/*',
10
+ channel: 'hotfix'
11
+ }
12
+ ]
13
+ };
@@ -0,0 +1,206 @@
1
+ const Cast = require('../util/cast');
2
+
3
+ class Scratch3ControlBlocks {
4
+ constructor (runtime) {
5
+ /**
6
+ * The runtime instantiating this block package.
7
+ * @type {Runtime}
8
+ */
9
+ this.runtime = runtime;
10
+
11
+ /**
12
+ * The "counter" block value. For compatibility with 2.0.
13
+ * @type {number}
14
+ */
15
+ this._counter = 0;
16
+
17
+ this.runtime.on('RUNTIME_DISPOSED', this.clearCounter.bind(this));
18
+ }
19
+
20
+ /**
21
+ * Retrieve the block primitives implemented by this package.
22
+ * @return {object.<string, Function>} Mapping of opcode to Function.
23
+ */
24
+ getPrimitives () {
25
+ return {
26
+ control_repeat: this.repeat,
27
+ control_repeat_until: this.repeatUntil,
28
+ control_while: this.repeatWhile,
29
+ control_for_each: this.forEach,
30
+ control_forever: this.forever,
31
+ control_wait: this.wait,
32
+ control_wait_until: this.waitUntil,
33
+ control_if: this.if,
34
+ control_if_else: this.ifElse,
35
+ control_stop: this.stop,
36
+ control_create_clone_of: this.createClone,
37
+ control_delete_this_clone: this.deleteClone,
38
+ control_get_counter: this.getCounter,
39
+ control_incr_counter: this.incrCounter,
40
+ control_clear_counter: this.clearCounter,
41
+ control_all_at_once: this.allAtOnce
42
+ };
43
+ }
44
+
45
+ getHats () {
46
+ return {
47
+ control_start_as_clone: {
48
+ restartExistingThreads: false
49
+ }
50
+ };
51
+ }
52
+
53
+ repeat (args, util) {
54
+ const times = Math.round(Cast.toNumber(args.TIMES));
55
+ // Initialize loop
56
+ if (typeof util.stackFrame.loopCounter === 'undefined') {
57
+ util.stackFrame.loopCounter = times;
58
+ }
59
+ // Only execute once per frame.
60
+ // When the branch finishes, `repeat` will be executed again and
61
+ // the second branch will be taken, yielding for the rest of the frame.
62
+ // Decrease counter
63
+ util.stackFrame.loopCounter--;
64
+ // If we still have some left, start the branch.
65
+ if (util.stackFrame.loopCounter >= 0) {
66
+ util.startBranch(1, true);
67
+ }
68
+ }
69
+
70
+ repeatUntil (args, util) {
71
+ const condition = Cast.toBoolean(args.CONDITION);
72
+ // If the condition is false (repeat UNTIL), start the branch.
73
+ if (!condition) {
74
+ util.startBranch(1, true);
75
+ }
76
+ }
77
+
78
+ repeatWhile (args, util) {
79
+ const condition = Cast.toBoolean(args.CONDITION);
80
+ // If the condition is true (repeat WHILE), start the branch.
81
+ if (condition) {
82
+ util.startBranch(1, true);
83
+ }
84
+ }
85
+
86
+ forEach (args, util) {
87
+ const variable = util.target.lookupOrCreateVariable(
88
+ args.VARIABLE.id, args.VARIABLE.name);
89
+
90
+ if (typeof util.stackFrame.index === 'undefined') {
91
+ util.stackFrame.index = 0;
92
+ }
93
+
94
+ if (util.stackFrame.index < Number(args.VALUE)) {
95
+ util.stackFrame.index++;
96
+ variable.value = util.stackFrame.index;
97
+ util.startBranch(1, true);
98
+ }
99
+ }
100
+
101
+ waitUntil (args, util) {
102
+ const condition = Cast.toBoolean(args.CONDITION);
103
+ if (!condition) {
104
+ util.yield();
105
+ }
106
+ }
107
+
108
+ forever (args, util) {
109
+ util.startBranch(1, true);
110
+ }
111
+
112
+ wait (args, util) {
113
+ if (util.stackTimerNeedsInit()) {
114
+ const duration = Math.max(0, 1000 * Cast.toNumber(args.DURATION));
115
+
116
+ util.startStackTimer(duration);
117
+ this.runtime.requestRedraw();
118
+ util.yield();
119
+ } else if (!util.stackTimerFinished()) {
120
+ util.yield();
121
+ }
122
+ }
123
+
124
+ if (args, util) {
125
+ const condition = Cast.toBoolean(args.CONDITION);
126
+ if (condition) {
127
+ util.startBranch(1, false);
128
+ }
129
+ }
130
+
131
+ ifElse (args, util) {
132
+ const condition = Cast.toBoolean(args.CONDITION);
133
+ if (condition) {
134
+ util.startBranch(1, false);
135
+ } else {
136
+ util.startBranch(2, false);
137
+ }
138
+ }
139
+
140
+ stop (args, util) {
141
+ const option = args.STOP_OPTION;
142
+ if (option === 'all') {
143
+ util.stopAll();
144
+ } else if (option === 'other scripts in sprite' ||
145
+ option === 'other scripts in stage') {
146
+ util.stopOtherTargetThreads();
147
+ } else if (option === 'this script') {
148
+ util.stopThisScript();
149
+ }
150
+ }
151
+
152
+ createClone (args, util) {
153
+ // Cast argument to string
154
+ args.CLONE_OPTION = Cast.toString(args.CLONE_OPTION);
155
+
156
+ // Set clone target
157
+ let cloneTarget;
158
+ if (args.CLONE_OPTION === '_myself_') {
159
+ cloneTarget = util.target;
160
+ } else {
161
+ cloneTarget = this.runtime.getSpriteTargetByName(args.CLONE_OPTION);
162
+ }
163
+
164
+ // If clone target is not found, return
165
+ if (!cloneTarget) return;
166
+
167
+ // Create clone
168
+ const newClone = cloneTarget.makeClone();
169
+ if (newClone) {
170
+ this.runtime.addTarget(newClone);
171
+
172
+ // Place behind the original target.
173
+ newClone.goBehindOther(cloneTarget);
174
+ }
175
+ }
176
+
177
+ deleteClone (args, util) {
178
+ if (util.target.isOriginal) return;
179
+ this.runtime.disposeTarget(util.target);
180
+ this.runtime.stopForTarget(util.target);
181
+ }
182
+
183
+ getCounter () {
184
+ return this._counter;
185
+ }
186
+
187
+ clearCounter () {
188
+ this._counter = 0;
189
+ }
190
+
191
+ incrCounter () {
192
+ this._counter++;
193
+ }
194
+
195
+ allAtOnce (args, util) {
196
+ // Since the "all at once" block is implemented for compatiblity with
197
+ // Scratch 2.0 projects, it behaves the same way it did in 2.0, which
198
+ // is to simply run the contained script (like "if 1 = 1").
199
+ // (In early versions of Scratch 2.0, it would work the same way as
200
+ // "run without screen refresh" custom blocks do now, but this was
201
+ // removed before the release of 2.0.)
202
+ util.startBranch(1, false);
203
+ }
204
+ }
205
+
206
+ module.exports = Scratch3ControlBlocks;
@@ -0,0 +1,69 @@
1
+ const BlockType = require('../extension-support/block-type');
2
+ const ArgumentType = require('../extension-support/argument-type');
3
+
4
+ /* eslint-disable-next-line max-len */
5
+ const blockIconURI = 'data:image/svg+xml,%3Csvg id="rotate-counter-clockwise" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:%233d79cc;%7D.cls-2%7Bfill:%23fff;%7D%3C/style%3E%3C/defs%3E%3Ctitle%3Erotate-counter-clockwise%3C/title%3E%3Cpath class="cls-1" d="M22.68,12.2a1.6,1.6,0,0,1-1.27.63H13.72a1.59,1.59,0,0,1-1.16-2.58l1.12-1.41a4.82,4.82,0,0,0-3.14-.77,4.31,4.31,0,0,0-2,.8,4.25,4.25,0,0,0-1.34,1.73,5.06,5.06,0,0,0,.54,4.62A5.58,5.58,0,0,0,12,17.74h0a2.26,2.26,0,0,1-.16,4.52A10.25,10.25,0,0,1,3.74,18,10.14,10.14,0,0,1,2.25,8.78,9.7,9.7,0,0,1,5.08,4.64,9.92,9.92,0,0,1,9.66,2.5a10.66,10.66,0,0,1,7.72,1.68l1.08-1.35a1.57,1.57,0,0,1,1.24-.6,1.6,1.6,0,0,1,1.54,1.21l1.7,7.37A1.57,1.57,0,0,1,22.68,12.2Z"/%3E%3Cpath class="cls-2" d="M21.38,11.83H13.77a.59.59,0,0,1-.43-1l1.75-2.19a5.9,5.9,0,0,0-4.7-1.58,5.07,5.07,0,0,0-4.11,3.17A6,6,0,0,0,7,15.77a6.51,6.51,0,0,0,5,2.92,1.31,1.31,0,0,1-.08,2.62,9.3,9.3,0,0,1-7.35-3.82A9.16,9.16,0,0,1,3.17,9.12,8.51,8.51,0,0,1,5.71,5.4,8.76,8.76,0,0,1,9.82,3.48a9.71,9.71,0,0,1,7.75,2.07l1.67-2.1a.59.59,0,0,1,1,.21L22,11.08A.59.59,0,0,1,21.38,11.83Z"/%3E%3C/svg%3E';
6
+
7
+ /**
8
+ * An example core block implemented using the extension spec.
9
+ * This is not loaded as part of the core blocks in the VM but it is provided
10
+ * and used as part of tests.
11
+ */
12
+ class Scratch3CoreExample {
13
+ constructor (runtime) {
14
+ /**
15
+ * The runtime instantiating this block package.
16
+ * @type {Runtime}
17
+ */
18
+ this.runtime = runtime;
19
+ }
20
+
21
+ /**
22
+ * @returns {object} metadata for this extension and its blocks.
23
+ */
24
+ getInfo () {
25
+ return {
26
+ id: 'coreExample',
27
+ name: 'CoreEx', // This string does not need to be translated as this extension is only used as an example.
28
+ blocks: [
29
+ {
30
+ func: 'MAKE_A_VARIABLE',
31
+ blockType: BlockType.BUTTON,
32
+ text: 'make a variable (CoreEx)'
33
+ },
34
+ {
35
+ opcode: 'exampleOpcode',
36
+ blockType: BlockType.REPORTER,
37
+ text: 'example block'
38
+ },
39
+ {
40
+ opcode: 'exampleWithInlineImage',
41
+ blockType: BlockType.COMMAND,
42
+ text: 'block with image [CLOCKWISE] inline',
43
+ arguments: {
44
+ CLOCKWISE: {
45
+ type: ArgumentType.IMAGE,
46
+ dataURI: blockIconURI
47
+ }
48
+ }
49
+ }
50
+ ]
51
+ };
52
+ }
53
+
54
+ /**
55
+ * Example opcode just returns the name of the stage target.
56
+ * @returns {string} The name of the first target in the project.
57
+ */
58
+ exampleOpcode () {
59
+ const stage = this.runtime.getTargetForStage();
60
+ return stage ? stage.getName() : 'no stage yet';
61
+ }
62
+
63
+ exampleWithInlineImage () {
64
+ return;
65
+ }
66
+
67
+ }
68
+
69
+ module.exports = Scratch3CoreExample;
@@ -0,0 +1,257 @@
1
+ const Cast = require('../util/cast');
2
+
3
+ class Scratch3DataBlocks {
4
+ constructor (runtime) {
5
+ /**
6
+ * The runtime instantiating this block package.
7
+ * @type {Runtime}
8
+ */
9
+ this.runtime = runtime;
10
+ }
11
+
12
+ /**
13
+ * Retrieve the block primitives implemented by this package.
14
+ * @return {object.<string, Function>} Mapping of opcode to Function.
15
+ */
16
+ getPrimitives () {
17
+ return {
18
+ data_variable: this.getVariable,
19
+ data_setvariableto: this.setVariableTo,
20
+ data_changevariableby: this.changeVariableBy,
21
+ data_hidevariable: this.hideVariable,
22
+ data_showvariable: this.showVariable,
23
+ data_listcontents: this.getListContents,
24
+ data_addtolist: this.addToList,
25
+ data_deleteoflist: this.deleteOfList,
26
+ data_deletealloflist: this.deleteAllOfList,
27
+ data_insertatlist: this.insertAtList,
28
+ data_replaceitemoflist: this.replaceItemOfList,
29
+ data_itemoflist: this.getItemOfList,
30
+ data_itemnumoflist: this.getItemNumOfList,
31
+ data_lengthoflist: this.lengthOfList,
32
+ data_listcontainsitem: this.listContainsItem,
33
+ data_hidelist: this.hideList,
34
+ data_showlist: this.showList
35
+ };
36
+ }
37
+
38
+ getVariable (args, util) {
39
+ const variable = util.target.lookupOrCreateVariable(
40
+ args.VARIABLE.id, args.VARIABLE.name);
41
+ return variable.value;
42
+ }
43
+
44
+ setVariableTo (args, util) {
45
+ const variable = util.target.lookupOrCreateVariable(
46
+ args.VARIABLE.id, args.VARIABLE.name);
47
+ variable.value = args.VALUE;
48
+
49
+ if (variable.isCloud) {
50
+ util.ioQuery('cloud', 'requestUpdateVariable', [variable.name, args.VALUE]);
51
+ }
52
+ }
53
+
54
+ changeVariableBy (args, util) {
55
+ const variable = util.target.lookupOrCreateVariable(
56
+ args.VARIABLE.id, args.VARIABLE.name);
57
+ const castedValue = Cast.toNumber(variable.value);
58
+ const dValue = Cast.toNumber(args.VALUE);
59
+ const newValue = castedValue + dValue;
60
+ variable.value = newValue;
61
+
62
+ if (variable.isCloud) {
63
+ util.ioQuery('cloud', 'requestUpdateVariable', [variable.name, newValue]);
64
+ }
65
+ }
66
+
67
+ changeMonitorVisibility (id, visible) {
68
+ // Send the monitor blocks an event like the flyout checkbox event.
69
+ // This both updates the monitor state and changes the isMonitored block flag.
70
+ this.runtime.monitorBlocks.changeBlock({
71
+ id: id, // Monitor blocks for variables are the variable ID.
72
+ element: 'checkbox', // Mimic checkbox event from flyout.
73
+ value: visible
74
+ }, this.runtime);
75
+ }
76
+
77
+ showVariable (args) {
78
+ this.changeMonitorVisibility(args.VARIABLE.id, true);
79
+ }
80
+
81
+ hideVariable (args) {
82
+ this.changeMonitorVisibility(args.VARIABLE.id, false);
83
+ }
84
+
85
+ showList (args) {
86
+ this.changeMonitorVisibility(args.LIST.id, true);
87
+ }
88
+
89
+ hideList (args) {
90
+ this.changeMonitorVisibility(args.LIST.id, false);
91
+ }
92
+
93
+ getListContents (args, util) {
94
+ const list = util.target.lookupOrCreateList(
95
+ args.LIST.id, args.LIST.name);
96
+
97
+ // If block is running for monitors, return copy of list as an array if changed.
98
+ if (util.thread.updateMonitor) {
99
+ // Return original list value if up-to-date, which doesn't trigger monitor update.
100
+ if (list._monitorUpToDate) return list.value;
101
+ // If value changed, reset the flag and return a copy to trigger monitor update.
102
+ // Because monitors use Immutable data structures, only new objects trigger updates.
103
+ list._monitorUpToDate = true;
104
+ return list.value.slice();
105
+ }
106
+
107
+ // Determine if the list is all single letters.
108
+ // If it is, report contents joined together with no separator.
109
+ // If it's not, report contents joined together with a space.
110
+ let allSingleLetters = true;
111
+ for (let i = 0; i < list.value.length; i++) {
112
+ const listItem = list.value[i];
113
+ if (!((typeof listItem === 'string') &&
114
+ (listItem.length === 1))) {
115
+ allSingleLetters = false;
116
+ break;
117
+ }
118
+ }
119
+ if (allSingleLetters) {
120
+ return list.value.join('');
121
+ }
122
+ return list.value.join(' ');
123
+
124
+ }
125
+
126
+ addToList (args, util) {
127
+ const list = util.target.lookupOrCreateList(
128
+ args.LIST.id, args.LIST.name);
129
+ if (list.value.length < Scratch3DataBlocks.LIST_ITEM_LIMIT) {
130
+ list.value.push(args.ITEM);
131
+ list._monitorUpToDate = false;
132
+ }
133
+ }
134
+
135
+ deleteOfList (args, util) {
136
+ const list = util.target.lookupOrCreateList(
137
+ args.LIST.id, args.LIST.name);
138
+ const index = Cast.toListIndex(args.INDEX, list.value.length, true);
139
+ if (index === Cast.LIST_INVALID) {
140
+ return;
141
+ } else if (index === Cast.LIST_ALL) {
142
+ list.value = [];
143
+ return;
144
+ }
145
+ list.value.splice(index - 1, 1);
146
+ list._monitorUpToDate = false;
147
+ }
148
+
149
+ deleteAllOfList (args, util) {
150
+ const list = util.target.lookupOrCreateList(
151
+ args.LIST.id, args.LIST.name);
152
+ list.value = [];
153
+ return;
154
+ }
155
+
156
+ insertAtList (args, util) {
157
+ const item = args.ITEM;
158
+ const list = util.target.lookupOrCreateList(
159
+ args.LIST.id, args.LIST.name);
160
+ const index = Cast.toListIndex(args.INDEX, list.value.length + 1, false);
161
+ if (index === Cast.LIST_INVALID) {
162
+ return;
163
+ }
164
+ const listLimit = Scratch3DataBlocks.LIST_ITEM_LIMIT;
165
+ if (index > listLimit) return;
166
+ list.value.splice(index - 1, 0, item);
167
+ if (list.value.length > listLimit) {
168
+ // If inserting caused the list to grow larger than the limit,
169
+ // remove the last element in the list
170
+ list.value.pop();
171
+ }
172
+ list._monitorUpToDate = false;
173
+ }
174
+
175
+ replaceItemOfList (args, util) {
176
+ const item = args.ITEM;
177
+ const list = util.target.lookupOrCreateList(
178
+ args.LIST.id, args.LIST.name);
179
+ const index = Cast.toListIndex(args.INDEX, list.value.length, false);
180
+ if (index === Cast.LIST_INVALID) {
181
+ return;
182
+ }
183
+ list.value[index - 1] = item;
184
+ list._monitorUpToDate = false;
185
+ }
186
+
187
+ getItemOfList (args, util) {
188
+ const list = util.target.lookupOrCreateList(
189
+ args.LIST.id, args.LIST.name);
190
+ const index = Cast.toListIndex(args.INDEX, list.value.length, false);
191
+ if (index === Cast.LIST_INVALID) {
192
+ return '';
193
+ }
194
+ return list.value[index - 1];
195
+ }
196
+
197
+ getItemNumOfList (args, util) {
198
+ const item = args.ITEM;
199
+ const list = util.target.lookupOrCreateList(
200
+ args.LIST.id, args.LIST.name);
201
+
202
+ // Go through the list items one-by-one using Cast.compare. This is for
203
+ // cases like checking if 123 is contained in a list [4, 7, '123'] --
204
+ // Scratch considers 123 and '123' to be equal.
205
+ for (let i = 0; i < list.value.length; i++) {
206
+ if (Cast.compare(list.value[i], item) === 0) {
207
+ return i + 1;
208
+ }
209
+ }
210
+
211
+ // We don't bother using .indexOf() at all, because it would end up with
212
+ // edge cases such as the index of '123' in [4, 7, 123, '123', 9].
213
+ // If we use indexOf(), this block would return 4 instead of 3, because
214
+ // indexOf() sees the first occurence of the string 123 as the fourth
215
+ // item in the list. With Scratch, this would be confusing -- after all,
216
+ // '123' and 123 look the same, so one would expect the block to say
217
+ // that the first occurrence of '123' (or 123) to be the third item.
218
+
219
+ // Default to 0 if there's no match. Since Scratch lists are 1-indexed,
220
+ // we don't have to worry about this conflicting with the "this item is
221
+ // the first value" number (in JS that is 0, but in Scratch it's 1).
222
+ return 0;
223
+ }
224
+
225
+ lengthOfList (args, util) {
226
+ const list = util.target.lookupOrCreateList(
227
+ args.LIST.id, args.LIST.name);
228
+ return list.value.length;
229
+ }
230
+
231
+ listContainsItem (args, util) {
232
+ const item = args.ITEM;
233
+ const list = util.target.lookupOrCreateList(
234
+ args.LIST.id, args.LIST.name);
235
+ if (list.value.indexOf(item) >= 0) {
236
+ return true;
237
+ }
238
+ // Try using Scratch comparison operator on each item.
239
+ // (Scratch considers the string '123' equal to the number 123).
240
+ for (let i = 0; i < list.value.length; i++) {
241
+ if (Cast.compare(list.value[i], item) === 0) {
242
+ return true;
243
+ }
244
+ }
245
+ return false;
246
+ }
247
+
248
+ /**
249
+ * Type representation for list variables.
250
+ * @const {number}
251
+ */
252
+ static get LIST_ITEM_LIMIT () {
253
+ return 200000;
254
+ }
255
+ }
256
+
257
+ module.exports = Scratch3DataBlocks;