@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
@@ -0,0 +1,146 @@
1
+ const Cast = require('../util/cast');
2
+
3
+ /**
4
+ * Names used internally for keys used in scratch, also known as "scratch keys".
5
+ * @enum {string}
6
+ */
7
+ const KEY_NAME = {
8
+ SPACE: 'space',
9
+ LEFT: 'left arrow',
10
+ UP: 'up arrow',
11
+ RIGHT: 'right arrow',
12
+ DOWN: 'down arrow',
13
+ ENTER: 'enter'
14
+ };
15
+
16
+ /**
17
+ * An array of the names of scratch keys.
18
+ * @type {Array<string>}
19
+ */
20
+ const KEY_NAME_LIST = Object.keys(KEY_NAME).map(name => KEY_NAME[name]);
21
+
22
+ class Keyboard {
23
+ constructor (runtime) {
24
+ /**
25
+ * List of currently pressed scratch keys.
26
+ * A scratch key is:
27
+ * A key you can press on a keyboard, excluding modifier keys.
28
+ * An uppercase string of length one;
29
+ * except for special key names for arrow keys and space (e.g. 'left arrow').
30
+ * Can be a non-english unicode letter like: æ ø ש נ 手 廿.
31
+ * @type{Array.<string>}
32
+ */
33
+ this._keysPressed = [];
34
+ /**
35
+ * Reference to the owning Runtime.
36
+ * Can be used, for example, to activate hats.
37
+ * @type{!Runtime}
38
+ */
39
+ this.runtime = runtime;
40
+ }
41
+
42
+ /**
43
+ * Convert from a keyboard event key name to a Scratch key name.
44
+ * @param {string} keyString the input key string.
45
+ * @return {string} the corresponding Scratch key, or an empty string.
46
+ */
47
+ _keyStringToScratchKey (keyString) {
48
+ keyString = Cast.toString(keyString);
49
+ // Convert space and arrow keys to their Scratch key names.
50
+ switch (keyString) {
51
+ case ' ': return KEY_NAME.SPACE;
52
+ case 'ArrowLeft':
53
+ case 'Left': return KEY_NAME.LEFT;
54
+ case 'ArrowUp':
55
+ case 'Up': return KEY_NAME.UP;
56
+ case 'Right':
57
+ case 'ArrowRight': return KEY_NAME.RIGHT;
58
+ case 'Down':
59
+ case 'ArrowDown': return KEY_NAME.DOWN;
60
+ case 'Enter': return KEY_NAME.ENTER;
61
+ }
62
+ // Ignore modifier keys
63
+ if (keyString.length > 1) {
64
+ return '';
65
+ }
66
+ return keyString.toUpperCase();
67
+ }
68
+
69
+ /**
70
+ * Convert from a block argument to a Scratch key name.
71
+ * @param {string} keyArg the input arg.
72
+ * @return {string} the corresponding Scratch key.
73
+ */
74
+ _keyArgToScratchKey (keyArg) {
75
+ // If a number was dropped in, try to convert from ASCII to Scratch key.
76
+ if (typeof keyArg === 'number') {
77
+ // Check for the ASCII range containing numbers, some punctuation,
78
+ // and uppercase letters.
79
+ if (keyArg >= 48 && keyArg <= 90) {
80
+ return String.fromCharCode(keyArg);
81
+ }
82
+ switch (keyArg) {
83
+ case 32: return KEY_NAME.SPACE;
84
+ case 37: return KEY_NAME.LEFT;
85
+ case 38: return KEY_NAME.UP;
86
+ case 39: return KEY_NAME.RIGHT;
87
+ case 40: return KEY_NAME.DOWN;
88
+ }
89
+ }
90
+
91
+ keyArg = Cast.toString(keyArg);
92
+
93
+ // If the arg matches a special key name, return it.
94
+ if (KEY_NAME_LIST.includes(keyArg)) {
95
+ return keyArg;
96
+ }
97
+
98
+ // Use only the first character.
99
+ if (keyArg.length > 1) {
100
+ keyArg = keyArg[0];
101
+ }
102
+
103
+ // Check for the space character.
104
+ if (keyArg === ' ') {
105
+ return KEY_NAME.SPACE;
106
+ }
107
+
108
+ return keyArg.toUpperCase();
109
+ }
110
+
111
+ /**
112
+ * Keyboard DOM event handler.
113
+ * @param {object} data Data from DOM event.
114
+ */
115
+ postData (data) {
116
+ if (!data.key) return;
117
+ const scratchKey = this._keyStringToScratchKey(data.key);
118
+ if (scratchKey === '') return;
119
+ const index = this._keysPressed.indexOf(scratchKey);
120
+ if (data.isDown) {
121
+ this.runtime.emit('KEY_PRESSED', scratchKey);
122
+ // If not already present, add to the list.
123
+ if (index < 0) {
124
+ this._keysPressed.push(scratchKey);
125
+ }
126
+ } else if (index > -1) {
127
+ // If already present, remove from the list.
128
+ this._keysPressed.splice(index, 1);
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Get key down state for a specified key.
134
+ * @param {Any} keyArg key argument.
135
+ * @return {boolean} Is the specified key down?
136
+ */
137
+ getKeyIsDown (keyArg) {
138
+ if (keyArg === 'any') {
139
+ return this._keysPressed.length > 0;
140
+ }
141
+ const scratchKey = this._keyArgToScratchKey(keyArg);
142
+ return this._keysPressed.indexOf(scratchKey) > -1;
143
+ }
144
+ }
145
+
146
+ module.exports = Keyboard;
@@ -0,0 +1,145 @@
1
+ const MathUtil = require('../util/math-util');
2
+
3
+ class Mouse {
4
+ constructor (runtime) {
5
+ this._x = 0;
6
+ this._y = 0;
7
+ this._isDown = false;
8
+ /**
9
+ * Reference to the owning Runtime.
10
+ * Can be used, for example, to activate hats.
11
+ * @type{!Runtime}
12
+ */
13
+ this.runtime = runtime;
14
+ }
15
+
16
+ /**
17
+ * Activate "event_whenthisspriteclicked" hats.
18
+ * @param {Target} target to trigger hats on.
19
+ * @private
20
+ */
21
+ _activateClickHats (target) {
22
+ // Activate both "this sprite clicked" and "stage clicked"
23
+ // They were separated into two opcodes for labeling,
24
+ // but should act the same way.
25
+ // Intentionally not checking isStage to make it work when sharing blocks.
26
+ // @todo the blocks should be converted from one to another when shared
27
+ this.runtime.startHats('event_whenthisspriteclicked',
28
+ null, target);
29
+ this.runtime.startHats('event_whenstageclicked',
30
+ null, target);
31
+ }
32
+
33
+ /**
34
+ * Find a target by XY location
35
+ * @param {number} x X position to be sent to the renderer.
36
+ * @param {number} y Y position to be sent to the renderer.
37
+ * @return {Target} the target at that location
38
+ * @private
39
+ */
40
+ _pickTarget (x, y) {
41
+ if (this.runtime.renderer) {
42
+ const drawableID = this.runtime.renderer.pick(x, y);
43
+ for (let i = 0; i < this.runtime.targets.length; i++) {
44
+ const target = this.runtime.targets[i];
45
+ if (Object.prototype.hasOwnProperty.call(target, 'drawableID') &&
46
+ target.drawableID === drawableID) {
47
+ return target;
48
+ }
49
+ }
50
+ }
51
+ // Return the stage if no target was found
52
+ return this.runtime.getTargetForStage();
53
+ }
54
+
55
+ /**
56
+ * Mouse DOM event handler.
57
+ * @param {object} data Data from DOM event.
58
+ */
59
+ postData (data) {
60
+ if (data.x) {
61
+ this._clientX = data.x;
62
+ this._scratchX = Math.round(MathUtil.clamp(
63
+ 480 * ((data.x / data.canvasWidth) - 0.5),
64
+ -240,
65
+ 240
66
+ ));
67
+ }
68
+ if (data.y) {
69
+ this._clientY = data.y;
70
+ this._scratchY = Math.round(MathUtil.clamp(
71
+ -360 * ((data.y / data.canvasHeight) - 0.5),
72
+ -180,
73
+ 180
74
+ ));
75
+ }
76
+ if (typeof data.isDown !== 'undefined') {
77
+ const previousDownState = this._isDown;
78
+ this._isDown = data.isDown;
79
+
80
+ // Do not trigger if down state has not changed
81
+ if (previousDownState === this._isDown) return;
82
+
83
+ // Never trigger click hats at the end of a drag
84
+ if (data.wasDragged) return;
85
+
86
+ // Do not activate click hats for clicks outside canvas bounds
87
+ if (!(data.x > 0 && data.x < data.canvasWidth &&
88
+ data.y > 0 && data.y < data.canvasHeight)) return;
89
+
90
+ const target = this._pickTarget(data.x, data.y);
91
+ const isNewMouseDown = !previousDownState && this._isDown;
92
+ const isNewMouseUp = previousDownState && !this._isDown;
93
+
94
+ // Draggable targets start click hats on mouse up.
95
+ // Non-draggable targets start click hats on mouse down.
96
+ if (target.draggable && isNewMouseUp) {
97
+ this._activateClickHats(target);
98
+ } else if (!target.draggable && isNewMouseDown) {
99
+ this._activateClickHats(target);
100
+ }
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Get the X position of the mouse in client coordinates.
106
+ * @return {number} Non-clamped X position of the mouse cursor.
107
+ */
108
+ getClientX () {
109
+ return this._clientX;
110
+ }
111
+
112
+ /**
113
+ * Get the Y position of the mouse in client coordinates.
114
+ * @return {number} Non-clamped Y position of the mouse cursor.
115
+ */
116
+ getClientY () {
117
+ return this._clientY;
118
+ }
119
+
120
+ /**
121
+ * Get the X position of the mouse in scratch coordinates.
122
+ * @return {number} Clamped and integer rounded X position of the mouse cursor.
123
+ */
124
+ getScratchX () {
125
+ return this._scratchX;
126
+ }
127
+
128
+ /**
129
+ * Get the Y position of the mouse in scratch coordinates.
130
+ * @return {number} Clamped and integer rounded Y position of the mouse cursor.
131
+ */
132
+ getScratchY () {
133
+ return this._scratchY;
134
+ }
135
+
136
+ /**
137
+ * Get the down state of the mouse.
138
+ * @return {boolean} Is the mouse down?
139
+ */
140
+ getIsDown () {
141
+ return this._isDown;
142
+ }
143
+ }
144
+
145
+ module.exports = Mouse;
@@ -0,0 +1,28 @@
1
+ class MouseWheel {
2
+ constructor (runtime) {
3
+ /**
4
+ * Reference to the owning Runtime.
5
+ * @type{!Runtime}
6
+ */
7
+ this.runtime = runtime;
8
+ }
9
+
10
+ /**
11
+ * Mouse wheel DOM event handler.
12
+ * @param {object} data Data from DOM event.
13
+ */
14
+ postData (data) {
15
+ const matchFields = {};
16
+ if (data.deltaY < 0) {
17
+ matchFields.KEY_OPTION = 'up arrow';
18
+ } else if (data.deltaY > 0) {
19
+ matchFields.KEY_OPTION = 'down arrow';
20
+ } else {
21
+ return;
22
+ }
23
+
24
+ this.runtime.startHats('event_whenkeypressed', matchFields);
25
+ }
26
+ }
27
+
28
+ module.exports = MouseWheel;
@@ -0,0 +1,24 @@
1
+ class UserData {
2
+ constructor () {
3
+ this._username = '';
4
+ }
5
+
6
+ /**
7
+ * Handler for updating the username
8
+ * @param {object} data Data posted to this ioDevice.
9
+ * @property {!string} username The new username.
10
+ */
11
+ postData (data) {
12
+ this._username = data.username;
13
+ }
14
+
15
+ /**
16
+ * Getter for username. Initially empty string, until set via postData.
17
+ * @returns {!string} The current username
18
+ */
19
+ getUsername () {
20
+ return this._username;
21
+ }
22
+ }
23
+
24
+ module.exports = UserData;
@@ -0,0 +1,210 @@
1
+ const StageLayering = require('../engine/stage-layering');
2
+
3
+ class Video {
4
+ constructor (runtime) {
5
+ this.runtime = runtime;
6
+
7
+ /**
8
+ * @typedef VideoProvider
9
+ * @property {Function} enableVideo - Requests camera access from the user, and upon success,
10
+ * enables the video feed
11
+ * @property {Function} disableVideo - Turns off the video feed
12
+ * @property {Function} getFrame - Return frame data from the video feed in
13
+ * specified dimensions, format, and mirroring.
14
+ */
15
+ this.provider = null;
16
+
17
+ /**
18
+ * Id representing a Scratch Renderer skin the video is rendered to for
19
+ * previewing.
20
+ * @type {number}
21
+ */
22
+ this._skinId = -1;
23
+
24
+ /**
25
+ * Id for a drawable using the video's skin that will render as a video
26
+ * preview.
27
+ * @type {Drawable}
28
+ */
29
+ this._drawable = -1;
30
+
31
+ /**
32
+ * Store the last state of the video transparency ghost effect
33
+ * @type {number}
34
+ */
35
+ this._ghost = 0;
36
+
37
+ /**
38
+ * Store a flag that allows the preview to be forced transparent.
39
+ * @type {number}
40
+ */
41
+ this._forceTransparentPreview = false;
42
+ }
43
+
44
+ static get FORMAT_IMAGE_DATA () {
45
+ return 'image-data';
46
+ }
47
+
48
+ static get FORMAT_CANVAS () {
49
+ return 'canvas';
50
+ }
51
+
52
+ /**
53
+ * Dimensions the video stream is analyzed at after its rendered to the
54
+ * sample canvas.
55
+ * @type {Array.<number>}
56
+ */
57
+ static get DIMENSIONS () {
58
+ return [480, 360];
59
+ }
60
+
61
+ /**
62
+ * Order preview drawable is inserted at in the renderer.
63
+ * @type {number}
64
+ */
65
+ static get ORDER () {
66
+ return 1;
67
+ }
68
+
69
+ /**
70
+ * Set a video provider for this device. A default implementation of
71
+ * a video provider can be found in scratch-gui/src/lib/video/video-provider
72
+ * @param {VideoProvider} provider - Video provider to use
73
+ */
74
+ setProvider (provider) {
75
+ this.provider = provider;
76
+ }
77
+
78
+ /**
79
+ * Request video be enabled. Sets up video, creates video skin and enables preview.
80
+ *
81
+ * ioDevices.video.requestVideo()
82
+ *
83
+ * @return {Promise.<Video>} resolves a promise to this IO device when video is ready.
84
+ */
85
+ enableVideo () {
86
+ if (!this.provider) return null;
87
+ return this.provider.enableVideo().then(() => this._setupPreview());
88
+ }
89
+
90
+ /**
91
+ * Disable video stream (turn video off)
92
+ * @return {void}
93
+ */
94
+ disableVideo () {
95
+ this._disablePreview();
96
+ if (!this.provider) return null;
97
+ this.provider.disableVideo();
98
+ }
99
+
100
+ /**
101
+ * Return frame data from the video feed in a specified dimensions, format, and mirroring.
102
+ *
103
+ * @param {object} frameInfo A descriptor of the frame you would like to receive.
104
+ * @param {Array.<number>} frameInfo.dimensions [width, height] array of numbers. Defaults to [480,360]
105
+ * @param {boolean} frameInfo.mirror If you specificly want a mirror/non-mirror frame, defaults to the global
106
+ * mirror state (ioDevices.video.mirror)
107
+ * @param {string} frameInfo.format Requested video format, available formats are 'image-data' and 'canvas'.
108
+ * @param {number} frameInfo.cacheTimeout Will reuse previous image data if the time since capture is less than
109
+ * the cacheTimeout. Defaults to 16ms.
110
+ *
111
+ * @return {ArrayBuffer|Canvas|string|null} Frame data in requested format, null when errors.
112
+ */
113
+ getFrame ({
114
+ dimensions = Video.DIMENSIONS,
115
+ mirror = this.mirror,
116
+ format = Video.FORMAT_IMAGE_DATA,
117
+ cacheTimeout = this._frameCacheTimeout
118
+ }) {
119
+ if (this.provider) return this.provider.getFrame({dimensions, mirror, format, cacheTimeout});
120
+ return null;
121
+ }
122
+
123
+ /**
124
+ * Set the preview ghost effect
125
+ * @param {number} ghost from 0 (visible) to 100 (invisible) - ghost effect
126
+ */
127
+ setPreviewGhost (ghost) {
128
+ this._ghost = ghost;
129
+ // Confirm that the default value has been changed to a valid id for the drawable
130
+ if (this._drawable !== -1) {
131
+ this.runtime.renderer.updateDrawableEffect(
132
+ this._drawable,
133
+ 'ghost',
134
+ this._forceTransparentPreview ? 100 : ghost
135
+ );
136
+ }
137
+ }
138
+
139
+ _disablePreview () {
140
+ if (this._skinId !== -1) {
141
+ this.runtime.renderer.updateBitmapSkin(this._skinId, new ImageData(...Video.DIMENSIONS), 1);
142
+ this.runtime.renderer.updateDrawableVisible(this._drawable, false);
143
+ }
144
+ this._renderPreviewFrame = null;
145
+ }
146
+
147
+ _setupPreview () {
148
+ const {renderer} = this.runtime;
149
+ if (!renderer) return;
150
+
151
+ if (this._skinId === -1 && this._drawable === -1) {
152
+ this._skinId = renderer.createBitmapSkin(new ImageData(...Video.DIMENSIONS), 1);
153
+ this._drawable = renderer.createDrawable(StageLayering.VIDEO_LAYER);
154
+ renderer.updateDrawableSkinId(this._drawable, this._skinId);
155
+ }
156
+
157
+ // if we haven't already created and started a preview frame render loop, do so
158
+ if (!this._renderPreviewFrame) {
159
+ renderer.updateDrawableEffect(this._drawable, 'ghost', this._forceTransparentPreview ? 100 : this._ghost);
160
+ renderer.updateDrawableVisible(this._drawable, true);
161
+
162
+ this._renderPreviewFrame = () => {
163
+ clearTimeout(this._renderPreviewTimeout);
164
+ if (!this._renderPreviewFrame) {
165
+ return;
166
+ }
167
+
168
+ this._renderPreviewTimeout = setTimeout(this._renderPreviewFrame, this.runtime.currentStepTime);
169
+
170
+ const imageData = this.getFrame({
171
+ format: Video.FORMAT_IMAGE_DATA,
172
+ cacheTimeout: this.runtime.currentStepTime
173
+ });
174
+
175
+ if (!imageData) {
176
+ renderer.updateBitmapSkin(this._skinId, new ImageData(...Video.DIMENSIONS), 1);
177
+ return;
178
+ }
179
+
180
+ renderer.updateBitmapSkin(this._skinId, imageData, 1);
181
+ this.runtime.requestRedraw();
182
+ };
183
+
184
+ this._renderPreviewFrame();
185
+ }
186
+ }
187
+
188
+ get videoReady () {
189
+ if (this.provider) return this.provider.videoReady;
190
+ return false;
191
+ }
192
+
193
+ /**
194
+ * Method implemented by all IO devices to allow external changes.
195
+ * The only change available externally is hiding the preview, used e.g. to
196
+ * prevent drawing the preview into project thumbnails.
197
+ * @param {object} - data passed to this IO device.
198
+ * @property {boolean} forceTransparentPreview - whether the preview should be forced transparent.
199
+ */
200
+ postData ({forceTransparentPreview}) {
201
+ this._forceTransparentPreview = forceTransparentPreview;
202
+ // Setting the ghost to the current value will pick up the forceTransparentPreview
203
+ // flag and override the current ghost. The complexity is to prevent blocks
204
+ // from overriding forceTransparentPreview
205
+ this.setPreviewGhost(this._ghost);
206
+ }
207
+ }
208
+
209
+
210
+ module.exports = Video;
@@ -0,0 +1,83 @@
1
+ body {
2
+ font-family: monospace;
3
+ }
4
+ p {
5
+ max-width: 400px;
6
+ }
7
+ @media (min-width: 960px) {
8
+ .profile-tables {
9
+ top: 0px;
10
+ position: absolute;
11
+ left: 450px;
12
+ }
13
+ }
14
+ .share {
15
+ display: none;
16
+ }
17
+ .share label {
18
+ cursor: pointer;
19
+ }
20
+ .share[href] {
21
+ display: inline;
22
+ }
23
+
24
+ .render .profile-tables {
25
+ position: static;
26
+ left: 0;
27
+ }
28
+ .render .description {
29
+ display: none;
30
+ }
31
+ .render .run-push {
32
+ display: none;
33
+ }
34
+ #scratch-stage {
35
+ border: 5px solid black;
36
+ display: block;
37
+ width: 400px;
38
+ height: 300px;
39
+ }
40
+ .render #scratch-stage {
41
+ display: none;
42
+ }
43
+ .loading label, .profile-count label{
44
+ width: 15em;
45
+ display: inline-block;
46
+ }
47
+ .render .loading {
48
+ display: none;
49
+ }
50
+ .profile-tables table {
51
+ margin: 30px 0 30px 0px;
52
+ }
53
+ .profile-tables th {
54
+ border-bottom: 1px solid #333;
55
+ text-align: center;
56
+ }
57
+ .profile-tables th:first-child {
58
+ width: 215px;
59
+ }
60
+ .profile-tables th, .profile-tables td {
61
+ min-width: 85px;
62
+ border-bottom: 1px solid #ccc;
63
+ border-spacing: 0;
64
+ border-collapse: collapse;
65
+ padding: 5px;
66
+ }
67
+ .profile-tables td:not(:first-child) {
68
+ text-align: center;
69
+ }
70
+ .profile-tables img{
71
+ margin: 0 auto;
72
+ display: block;
73
+ clear: both;
74
+ width: 20%;
75
+ }
76
+
77
+ .slow {
78
+ background-color: #ffa1a1;
79
+ }
80
+ .profiler-count-running {
81
+ height: 4em;
82
+ background-color: #dddddd;
83
+ }