@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.
- package/.jsdoc.json +20 -0
- package/.nvmrc +1 -0
- package/CHANGELOG.md +7004 -0
- package/LICENSE +12 -0
- package/README.md +120 -0
- package/TRADEMARK +1 -0
- package/commitlint.config.js +4 -0
- package/docs/extensions.md +527 -0
- package/package.json +103 -0
- package/release.config.js +13 -0
- package/src/blocks/scratch3_control.js +206 -0
- package/src/blocks/scratch3_core_example.js +69 -0
- package/src/blocks/scratch3_data.js +257 -0
- package/src/blocks/scratch3_event.js +137 -0
- package/src/blocks/scratch3_looks.js +612 -0
- package/src/blocks/scratch3_motion.js +288 -0
- package/src/blocks/scratch3_operators.js +154 -0
- package/src/blocks/scratch3_procedures.js +79 -0
- package/src/blocks/scratch3_sensing.js +336 -0
- package/src/blocks/scratch3_sound.js +350 -0
- package/src/dispatch/central-dispatch.js +143 -0
- package/src/dispatch/shared-dispatch.js +235 -0
- package/src/dispatch/worker-dispatch.js +110 -0
- package/src/engine/adapter.js +176 -0
- package/src/engine/block-utility.js +242 -0
- package/src/engine/blocks-execute-cache.js +19 -0
- package/src/engine/blocks-runtime-cache.js +78 -0
- package/src/engine/blocks.js +1299 -0
- package/src/engine/comment.js +56 -0
- package/src/engine/execute.js +580 -0
- package/src/engine/monitor-record.js +23 -0
- package/src/engine/mutation-adapter.js +48 -0
- package/src/engine/profiler.js +390 -0
- package/src/engine/runtime.js +2686 -0
- package/src/engine/scratch-blocks-constants.js +27 -0
- package/src/engine/sequencer.js +361 -0
- package/src/engine/stage-layering.js +29 -0
- package/src/engine/target.js +799 -0
- package/src/engine/thread.js +404 -0
- package/src/engine/variable.js +70 -0
- package/src/extension-support/argument-type.js +47 -0
- package/src/extension-support/block-type.js +50 -0
- package/src/extension-support/define-messages.js +18 -0
- package/src/extension-support/extension-manager.js +440 -0
- package/src/extension-support/extension-metadata.js +64 -0
- package/src/extension-support/extension-worker.js +59 -0
- package/src/extension-support/reporter-scope.js +18 -0
- package/src/extension-support/target-type.js +17 -0
- package/src/extensions/scratch3_boost/index.js +2113 -0
- package/src/extensions/scratch3_ev3/index.js +1355 -0
- package/src/extensions/scratch3_gdx_for/index.js +981 -0
- package/src/extensions/scratch3_gdx_for/scratch-link-device-adapter.js +44 -0
- package/src/extensions/scratch3_makeymakey/index.js +396 -0
- package/src/extensions/scratch3_microbit/index.js +984 -0
- package/src/extensions/scratch3_music/assets/drums/1-snare.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/drums/10-wood-block.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/drums/11-cowbell.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/drums/12-triangle.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/drums/13-bongo.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/drums/14-conga.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/drums/15-cabasa.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/drums/16-guiro.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/drums/17-vibraslap.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/drums/18-cuica.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/drums/2-bass-drum.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/drums/3-side-stick.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/drums/4-crash-cymbal.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/drums/5-open-hi-hat.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/drums/6-closed-hi-hat.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/drums/7-tambourine.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/drums/8-hand-clap.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/drums/9-claves.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/1-piano/108.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/1-piano/24.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/1-piano/36.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/1-piano/48.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/1-piano/60.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/1-piano/72.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/1-piano/84.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/1-piano/96.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/10-clarinet/48.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/10-clarinet/60.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/11-saxophone/36.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/11-saxophone/60.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/11-saxophone/84.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/12-flute/60.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/12-flute/72.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/13-wooden-flute/60.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/13-wooden-flute/72.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/14-bassoon/36.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/14-bassoon/48.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/14-bassoon/60.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/15-choir/48.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/15-choir/60.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/15-choir/72.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/16-vibraphone/60.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/16-vibraphone/72.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/17-music-box/60.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/18-steel-drum/60.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/19-marimba/60.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/2-electric-piano/60.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/20-synth-lead/60.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/21-synth-pad/60.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/3-organ/60.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/4-guitar/60.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/5-electric-guitar/60.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/6-bass/36.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/6-bass/48.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/7-pizzicato/60.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/8-cello/36.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/8-cello/48.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/8-cello/60.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/9-trombone/36.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/9-trombone/48.mp3 +0 -0
- package/src/extensions/scratch3_music/assets/instruments/9-trombone/60.mp3 +0 -0
- package/src/extensions/scratch3_music/index.js +1333 -0
- package/src/extensions/scratch3_music/manifest.js +63 -0
- package/src/extensions/scratch3_pen/index.js +770 -0
- package/src/extensions/scratch3_speech2text/index.js +700 -0
- package/src/extensions/scratch3_text2speech/index.js +766 -0
- package/src/extensions/scratch3_translate/index.js +286 -0
- package/src/extensions/scratch3_video_sensing/debug.js +13 -0
- package/src/extensions/scratch3_video_sensing/index.js +594 -0
- package/src/extensions/scratch3_video_sensing/library.js +384 -0
- package/src/extensions/scratch3_video_sensing/math.js +76 -0
- package/src/extensions/scratch3_video_sensing/view.js +509 -0
- package/src/extensions/scratch3_wedo2/index.js +1616 -0
- package/src/import/load-costume.js +416 -0
- package/src/import/load-sound.js +116 -0
- package/src/index.js +10 -0
- package/src/io/ble.js +256 -0
- package/src/io/bt.js +202 -0
- package/src/io/clock.js +39 -0
- package/src/io/cloud.js +169 -0
- package/src/io/keyboard.js +146 -0
- package/src/io/mouse.js +145 -0
- package/src/io/mouseWheel.js +28 -0
- package/src/io/userData.js +24 -0
- package/src/io/video.js +210 -0
- package/src/playground/benchmark.css +83 -0
- package/src/playground/benchmark.js +753 -0
- package/src/playground/index.html +111 -0
- package/src/playground/suite.css +78 -0
- package/src/playground/suite.html +25 -0
- package/src/playground/suite.js +544 -0
- package/src/playground/video-sensing.html +18 -0
- package/src/playground/video-sensing.js +133 -0
- package/src/serialization/deserialize-assets.js +178 -0
- package/src/serialization/sb2.js +1295 -0
- package/src/serialization/sb2_specmap.js +1818 -0
- package/src/serialization/sb3.js +1320 -0
- package/src/serialization/serialize-assets.js +60 -0
- package/src/sprites/rendered-target.js +1115 -0
- package/src/sprites/sprite.js +177 -0
- package/src/util/base64-util.js +48 -0
- package/src/util/cast.js +218 -0
- package/src/util/clone.js +17 -0
- package/src/util/color.js +204 -0
- package/src/util/fetch-with-timeout.js +53 -0
- package/src/util/get-monitor-id.js +33 -0
- package/src/util/jsonrpc.js +114 -0
- package/src/util/log.js +4 -0
- package/src/util/math-util.js +122 -0
- package/src/util/maybe-format-message.js +18 -0
- package/src/util/new-block-ids.js +33 -0
- package/src/util/rateLimiter.js +73 -0
- package/src/util/scratch-link-websocket.js +133 -0
- package/src/util/string-util.js +94 -0
- package/src/util/task-queue.js +203 -0
- package/src/util/timer.js +103 -0
- package/src/util/uid.js +29 -0
- package/src/util/variable-util.js +48 -0
- package/src/util/xml-escape.js +33 -0
- package/src/virtual-machine.js +1586 -0
- 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,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;
|