@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,594 @@
1
+ const Runtime = require('../../engine/runtime');
2
+
3
+ const ArgumentType = require('../../extension-support/argument-type');
4
+ const BlockType = require('../../extension-support/block-type');
5
+ const Clone = require('../../util/clone');
6
+ const Cast = require('../../util/cast');
7
+ const formatMessage = require('format-message');
8
+ const Video = require('../../io/video');
9
+
10
+ const VideoMotion = require('./library');
11
+
12
+ /**
13
+ * Icon svg to be displayed in the blocks category menu, encoded as a data URI.
14
+ * @type {string}
15
+ */
16
+ // eslint-disable-next-line max-len
17
+ const menuIconURI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjBweCIgaGVpZ2h0PSIyMHB4IiB2aWV3Qm94PSIwIDAgMjAgMjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUyLjIgKDY3MTQ1KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5FeHRlbnNpb25zL1NvZnR3YXJlL1ZpZGVvLVNlbnNpbmctTWVudTwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxnIGlkPSJFeHRlbnNpb25zL1NvZnR3YXJlL1ZpZGVvLVNlbnNpbmctTWVudSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9InZpZGVvLW1vdGlvbiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsIDUuMDAwMDAwKSIgZmlsbC1ydWxlPSJub256ZXJvIj4KICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC1Db3B5IiBmaWxsPSIjMEVCRDhDIiBvcGFjaXR5PSIwLjI1IiBjeD0iMTYiIGN5PSI4IiByPSIyIj48L2NpcmNsZT4KICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC1Db3B5IiBmaWxsPSIjMEVCRDhDIiBvcGFjaXR5PSIwLjUiIGN4PSIxNiIgY3k9IjYiIHI9IjIiPjwvY2lyY2xlPgogICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsLUNvcHkiIGZpbGw9IiMwRUJEOEMiIG9wYWNpdHk9IjAuNzUiIGN4PSIxNiIgY3k9IjQiIHI9IjIiPjwvY2lyY2xlPgogICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsIiBmaWxsPSIjMEVCRDhDIiBjeD0iMTYiIGN5PSIyIiByPSIyIj48L2NpcmNsZT4KICAgICAgICAgICAgPHBhdGggZD0iTTExLjMzNTk3MzksMi4yMDk3ODgyNSBMOC4yNSw0LjIwOTk1NjQ5IEw4LjI1LDMuMDUgQzguMjUsMi4wNDQ4ODIyNyA3LjQ2ODU5MDMxLDEuMjUgNi41LDEuMjUgTDIuMDUsMS4yNSBDMS4wMzgwNzExOSwxLjI1IDAuMjUsMi4wMzgwNzExOSAwLjI1LDMuMDUgTDAuMjUsNyBDMC4yNSw3Ljk2MzY5OTM3IDEuMDQyMjQ5MTksOC43NTU5NDg1NiAyLjA1LDguOCBMNi41LDguOCBDNy40NTA4MzAwOSw4LjggOC4yNSw3Ljk3MzI3MjUgOC4yNSw3IEw4LjI1LDUuODU4NDUyNDEgTDguNjI4NjIzOTQsNi4wODU2MjY3NyBMMTEuNDI2Nzc2Nyw3Ljc3MzIyMzMgQzExLjQzNjg5NDMsNy43ODMzNDA5MSAxMS40NzU3NjU1LDcuOCAxMS41LDcuOCBDMTEuNjMzNDkzMiw3LjggMTEuNzUsNy42OTEyNjAzNCAxMS43NSw3LjU1IEwxMS43NSwyLjQgQzExLjc1LDIuNDE4MzgyNjkgMTEuNzIxOTAyOSwyLjM1MjgyMjgyIDExLjY4NTYyNjgsMi4yNzg2MjM5NCBDMTEuNjEyOTUyOCwyLjE1NzUwMDY5IDExLjQ3MDc5NjgsMi4xMjkwNjk1IDExLjMzNTk3MzksMi4yMDk3ODgyNSBaIiBpZD0idmlkZW9fMzdfIiBzdHJva2Utb3BhY2l0eT0iMC4xNSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjAuNSIgZmlsbD0iIzRENEQ0RCI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+';
18
+
19
+ /**
20
+ * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.
21
+ * @type {string}
22
+ */
23
+ // eslint-disable-next-line max-len
24
+ const blockIconURI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSIwIDAgNDAgNDAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUyLjIgKDY3MTQ1KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5FeHRlbnNpb25zL1NvZnR3YXJlL1ZpZGVvLVNlbnNpbmctQmxvY2s8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZyBpZD0iRXh0ZW5zaW9ucy9Tb2Z0d2FyZS9WaWRlby1TZW5zaW5nLUJsb2NrIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2Utb3BhY2l0eT0iMC4xNSI+CiAgICAgICAgPGcgaWQ9InZpZGVvLW1vdGlvbiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsIDEwLjAwMDAwMCkiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSIjMDAwMDAwIj4KICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC1Db3B5IiBmaWxsPSIjRkZGRkZGIiBvcGFjaXR5PSIwLjI1IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGN4PSIzMiIgY3k9IjE2IiByPSI0LjUiPjwvY2lyY2xlPgogICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsLUNvcHkiIGZpbGw9IiNGRkZGRkYiIG9wYWNpdHk9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjeD0iMzIiIGN5PSIxMiIgcj0iNC41Ij48L2NpcmNsZT4KICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC1Db3B5IiBmaWxsPSIjRkZGRkZGIiBvcGFjaXR5PSIwLjc1IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGN4PSIzMiIgY3k9IjgiIHI9IjQuNSI+PC9jaXJjbGU+CiAgICAgICAgICAgIDxjaXJjbGUgaWQ9Ik92YWwiIGZpbGw9IiNGRkZGRkYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgY3g9IjMyIiBjeT0iNCIgcj0iNC41Ij48L2NpcmNsZT4KICAgICAgICAgICAgPHBhdGggZD0iTTIyLjY3MTk0NzcsNC40MTk1NzY0OSBMMTYuNSw4LjQxOTkxMjk4IEwxNi41LDYuMSBDMTYuNSw0LjA4OTc2NDU0IDE0LjkzNzE4MDYsMi41IDEzLDIuNSBMNC4xLDIuNSBDMi4wNzYxNDIzNywyLjUgMC41LDQuMDc2MTQyMzcgMC41LDYuMSBMMC41LDE0IEMwLjUsMTUuOTI3Mzk4NyAyLjA4NDQ5ODM5LDE3LjUxMTg5NzEgNC4xLDE3LjYgTDEzLDE3LjYgQzE0LjkwMTY2MDIsMTcuNiAxNi41LDE1Ljk0NjU0NSAxNi41LDE0IEwxNi41LDExLjcxNjkwNDggTDIyLjc1NzI0NzksMTUuNDcxMjUzNSBMMjIuODUzNTUzNCwxNS41NDY0NDY2IEMyMi44NzM3ODg2LDE1LjU2NjY4MTggMjIuOTUxNTMxLDE1LjYgMjMsMTUuNiBDMjMuMjY2OTg2NSwxNS42IDIzLjUsMTUuMzgyNTIwNyAyMy41LDE1LjEgTDIzLjUsNC44IEMyMy41LDQuODM2NzY1MzggMjMuNDQzODA1OCw0LjcwNTY0NTYzIDIzLjM3MTI1MzUsNC41NTcyNDc4OCBDMjMuMjI1OTA1Niw0LjMxNTAwMTM5IDIyLjk0MTU5MzcsNC4yNTgxMzg5OSAyMi42NzE5NDc3LDQuNDE5NTc2NDkgWiIgaWQ9InZpZGVvXzM3XyIgZmlsbD0iIzRENEQ0RCI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+';
25
+
26
+ /**
27
+ * Sensor attribute video sensor block should report.
28
+ * @readonly
29
+ * @enum {string}
30
+ */
31
+ const SensingAttribute = {
32
+ /** The amount of motion. */
33
+ MOTION: 'motion',
34
+
35
+ /** The direction of the motion. */
36
+ DIRECTION: 'direction'
37
+ };
38
+
39
+ /**
40
+ * Subject video sensor block should report for.
41
+ * @readonly
42
+ * @enum {string}
43
+ */
44
+ const SensingSubject = {
45
+ /** The sensor traits of the whole stage. */
46
+ STAGE: 'Stage',
47
+
48
+ /** The senosr traits of the area overlapped by this sprite. */
49
+ SPRITE: 'this sprite'
50
+ };
51
+
52
+ /**
53
+ * States the video sensing activity can be set to.
54
+ * @readonly
55
+ * @enum {string}
56
+ */
57
+ const VideoState = {
58
+ /** Video turned off. */
59
+ OFF: 'off',
60
+
61
+ /** Video turned on with default y axis mirroring. */
62
+ ON: 'on',
63
+
64
+ /** Video turned on without default y axis mirroring. */
65
+ ON_FLIPPED: 'on-flipped'
66
+ };
67
+
68
+ /**
69
+ * Class for the motion-related blocks in Scratch 3.0
70
+ * @param {Runtime} runtime - the runtime instantiating this block package.
71
+ * @constructor
72
+ */
73
+ class Scratch3VideoSensingBlocks {
74
+ constructor (runtime) {
75
+ /**
76
+ * The runtime instantiating this block package.
77
+ * @type {Runtime}
78
+ */
79
+ this.runtime = runtime;
80
+
81
+ /**
82
+ * The motion detection algoritm used to power the motion amount and
83
+ * direction values.
84
+ * @type {VideoMotion}
85
+ */
86
+ this.detect = new VideoMotion();
87
+
88
+ /**
89
+ * The last millisecond epoch timestamp that the video stream was
90
+ * analyzed.
91
+ * @type {number}
92
+ */
93
+ this._lastUpdate = null;
94
+
95
+ /**
96
+ * A flag to determine if this extension has been installed in a project.
97
+ * It is set to false the first time getInfo is run.
98
+ * @type {boolean}
99
+ */
100
+ this.firstInstall = true;
101
+
102
+ if (this.runtime.ioDevices) {
103
+ // Configure the video device with values from globally stored locations.
104
+ this.runtime.on(Runtime.PROJECT_LOADED, this.updateVideoDisplay.bind(this));
105
+
106
+ // Clear target motion state values when the project starts.
107
+ this.runtime.on(Runtime.PROJECT_RUN_START, this.reset.bind(this));
108
+
109
+ // Kick off looping the analysis logic.
110
+ this._loop();
111
+ }
112
+ }
113
+
114
+ /**
115
+ * After analyzing a frame the amount of milliseconds until another frame
116
+ * is analyzed.
117
+ * @type {number}
118
+ */
119
+ static get INTERVAL () {
120
+ return 33;
121
+ }
122
+
123
+ /**
124
+ * Dimensions the video stream is analyzed at after its rendered to the
125
+ * sample canvas.
126
+ * @type {Array.<number>}
127
+ */
128
+ static get DIMENSIONS () {
129
+ return [480, 360];
130
+ }
131
+
132
+ /**
133
+ * The key to load & store a target's motion-related state.
134
+ * @type {string}
135
+ */
136
+ static get STATE_KEY () {
137
+ return 'Scratch.videoSensing';
138
+ }
139
+
140
+ /**
141
+ * The default motion-related state, to be used when a target has no existing motion state.
142
+ * @type {MotionState}
143
+ */
144
+ static get DEFAULT_MOTION_STATE () {
145
+ return {
146
+ motionFrameNumber: 0,
147
+ motionAmount: 0,
148
+ motionDirection: 0
149
+ };
150
+ }
151
+
152
+ /**
153
+ * The transparency setting of the video preview stored in a value
154
+ * accessible by any object connected to the virtual machine.
155
+ * @type {number}
156
+ */
157
+ get globalVideoTransparency () {
158
+ const stage = this.runtime.getTargetForStage();
159
+ if (stage) {
160
+ return stage.videoTransparency;
161
+ }
162
+ return 50;
163
+ }
164
+
165
+ set globalVideoTransparency (transparency) {
166
+ const stage = this.runtime.getTargetForStage();
167
+ if (stage) {
168
+ stage.videoTransparency = transparency;
169
+ }
170
+ }
171
+
172
+ /**
173
+ * The video state of the video preview stored in a value accessible by any
174
+ * object connected to the virtual machine.
175
+ * @type {number}
176
+ */
177
+ get globalVideoState () {
178
+ const stage = this.runtime.getTargetForStage();
179
+ if (stage) {
180
+ return stage.videoState;
181
+ }
182
+ // Though the default value for the stage is normally 'on', we need to default
183
+ // to 'off' here to prevent the video device from briefly activating
184
+ // while waiting for stage targets to be installed that say it should be off
185
+ return VideoState.OFF;
186
+ }
187
+
188
+ set globalVideoState (state) {
189
+ const stage = this.runtime.getTargetForStage();
190
+ if (stage) {
191
+ stage.videoState = state;
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Get the latest values for video transparency and state,
197
+ * and set the video device to use them.
198
+ */
199
+ updateVideoDisplay () {
200
+ this.setVideoTransparency({
201
+ TRANSPARENCY: this.globalVideoTransparency
202
+ });
203
+ this.videoToggle({
204
+ VIDEO_STATE: this.globalVideoState
205
+ });
206
+ }
207
+
208
+ /**
209
+ * Reset the extension's data motion detection data. This will clear out
210
+ * for example old frames, so the first analyzed frame will not be compared
211
+ * against a frame from before reset was called.
212
+ */
213
+ reset () {
214
+ this.detect.reset();
215
+
216
+ const targets = this.runtime.targets;
217
+ for (let i = 0; i < targets.length; i++) {
218
+ const state = targets[i].getCustomState(Scratch3VideoSensingBlocks.STATE_KEY);
219
+ if (state) {
220
+ state.motionAmount = 0;
221
+ state.motionDirection = 0;
222
+ }
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Occasionally step a loop to sample the video, stamp it to the preview
228
+ * skin, and add a TypedArray copy of the canvas's pixel data.
229
+ * @private
230
+ */
231
+ _loop () {
232
+ const loopTime = Math.max(this.runtime.currentStepTime, Scratch3VideoSensingBlocks.INTERVAL);
233
+ this._loopInterval = setTimeout(this._loop.bind(this), loopTime);
234
+
235
+ // Add frame to detector
236
+ const time = Date.now();
237
+ if (this._lastUpdate === null) {
238
+ this._lastUpdate = time;
239
+ }
240
+ const offset = time - this._lastUpdate;
241
+ if (offset > Scratch3VideoSensingBlocks.INTERVAL) {
242
+ const frame = this.runtime.ioDevices.video.getFrame({
243
+ format: Video.FORMAT_IMAGE_DATA,
244
+ dimensions: Scratch3VideoSensingBlocks.DIMENSIONS
245
+ });
246
+ if (frame) {
247
+ this._lastUpdate = time;
248
+ this.detect.addFrame(frame.data);
249
+ }
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Stop the video sampling loop. Only used for testing.
255
+ */
256
+ _stopLoop () {
257
+ clearTimeout(this._loopInterval);
258
+ }
259
+
260
+ /**
261
+ * Create data for a menu in scratch-blocks format, consisting of an array
262
+ * of objects with text and value properties. The text is a translated
263
+ * string, and the value is one-indexed.
264
+ * @param {object[]} info - An array of info objects each having a name
265
+ * property.
266
+ * @return {array} - An array of objects with text and value properties.
267
+ * @private
268
+ */
269
+ _buildMenu (info) {
270
+ return info.map((entry, index) => {
271
+ const obj = {};
272
+ obj.text = entry.name;
273
+ obj.value = entry.value || String(index + 1);
274
+ return obj;
275
+ });
276
+ }
277
+
278
+ /**
279
+ * @param {Target} target - collect motion state for this target.
280
+ * @returns {MotionState} the mutable motion state associated with that
281
+ * target. This will be created if necessary.
282
+ * @private
283
+ */
284
+ _getMotionState (target) {
285
+ let motionState = target.getCustomState(Scratch3VideoSensingBlocks.STATE_KEY);
286
+ if (!motionState) {
287
+ motionState = Clone.simple(Scratch3VideoSensingBlocks.DEFAULT_MOTION_STATE);
288
+ target.setCustomState(Scratch3VideoSensingBlocks.STATE_KEY, motionState);
289
+ }
290
+ return motionState;
291
+ }
292
+
293
+ static get SensingAttribute () {
294
+ return SensingAttribute;
295
+ }
296
+
297
+ /**
298
+ * An array of choices of whether a reporter should return the frame's
299
+ * motion amount or direction.
300
+ * @type {object[]}
301
+ * @param {string} name - the translatable name to display in sensor
302
+ * attribute menu
303
+ * @param {string} value - the serializable value of the attribute
304
+ */
305
+ get ATTRIBUTE_INFO () {
306
+ return [
307
+ {
308
+ name: formatMessage({
309
+ id: 'videoSensing.motion',
310
+ default: 'motion',
311
+ description: 'Attribute for the "video [ATTRIBUTE] on [SUBJECT]" block'
312
+ }),
313
+ value: SensingAttribute.MOTION
314
+ },
315
+ {
316
+ name: formatMessage({
317
+ id: 'videoSensing.direction',
318
+ default: 'direction',
319
+ description: 'Attribute for the "video [ATTRIBUTE] on [SUBJECT]" block'
320
+ }),
321
+ value: SensingAttribute.DIRECTION
322
+ }
323
+ ];
324
+ }
325
+
326
+ static get SensingSubject () {
327
+ return SensingSubject;
328
+ }
329
+
330
+ /**
331
+ * An array of info about the subject choices.
332
+ * @type {object[]}
333
+ * @param {string} name - the translatable name to display in the subject menu
334
+ * @param {string} value - the serializable value of the subject
335
+ */
336
+ get SUBJECT_INFO () {
337
+ return [
338
+ {
339
+ name: formatMessage({
340
+ id: 'videoSensing.sprite',
341
+ default: 'sprite',
342
+ description: 'Subject for the "video [ATTRIBUTE] on [SUBJECT]" block'
343
+ }),
344
+ value: SensingSubject.SPRITE
345
+ },
346
+ {
347
+ name: formatMessage({
348
+ id: 'videoSensing.stage',
349
+ default: 'stage',
350
+ description: 'Subject for the "video [ATTRIBUTE] on [SUBJECT]" block'
351
+ }),
352
+ value: SensingSubject.STAGE
353
+ }
354
+ ];
355
+ }
356
+
357
+ /**
358
+ * States the video sensing activity can be set to.
359
+ * @readonly
360
+ * @enum {string}
361
+ */
362
+ static get VideoState () {
363
+ return VideoState;
364
+ }
365
+
366
+ /**
367
+ * An array of info on video state options for the "turn video [STATE]" block.
368
+ * @type {object[]}
369
+ * @param {string} name - the translatable name to display in the video state menu
370
+ * @param {string} value - the serializable value stored in the block
371
+ */
372
+ get VIDEO_STATE_INFO () {
373
+ return [
374
+ {
375
+ name: formatMessage({
376
+ id: 'videoSensing.off',
377
+ default: 'off',
378
+ description: 'Option for the "turn video [STATE]" block'
379
+ }),
380
+ value: VideoState.OFF
381
+ },
382
+ {
383
+ name: formatMessage({
384
+ id: 'videoSensing.on',
385
+ default: 'on',
386
+ description: 'Option for the "turn video [STATE]" block'
387
+ }),
388
+ value: VideoState.ON
389
+ },
390
+ {
391
+ name: formatMessage({
392
+ id: 'videoSensing.onFlipped',
393
+ default: 'on flipped',
394
+ description: 'Option for the "turn video [STATE]" block that causes the video to be flipped' +
395
+ ' horizontally (reversed as in a mirror)'
396
+ }),
397
+ value: VideoState.ON_FLIPPED
398
+ }
399
+ ];
400
+ }
401
+
402
+ /**
403
+ * @returns {object} metadata for this extension and its blocks.
404
+ */
405
+ getInfo () {
406
+ // Set the video display properties to defaults the first time
407
+ // getInfo is run. This turns on the video device when it is
408
+ // first added to a project, and is overwritten by a PROJECT_LOADED
409
+ // event listener that later calls updateVideoDisplay
410
+ if (this.firstInstall) {
411
+ this.globalVideoState = VideoState.ON;
412
+ this.globalVideoTransparency = 50;
413
+ this.updateVideoDisplay();
414
+ this.firstInstall = false;
415
+ }
416
+
417
+ // Return extension definition
418
+ return {
419
+ id: 'videoSensing',
420
+ name: formatMessage({
421
+ id: 'videoSensing.categoryName',
422
+ default: 'Video Sensing',
423
+ description: 'Label for the video sensing extension category'
424
+ }),
425
+ blockIconURI: blockIconURI,
426
+ menuIconURI: menuIconURI,
427
+ blocks: [
428
+ {
429
+ // @todo this hat needs to be set itself to restart existing
430
+ // threads like Scratch 2's behaviour.
431
+ opcode: 'whenMotionGreaterThan',
432
+ text: formatMessage({
433
+ id: 'videoSensing.whenMotionGreaterThan',
434
+ default: 'when video motion > [REFERENCE]',
435
+ description: 'Event that triggers when the amount of motion is greater than [REFERENCE]'
436
+ }),
437
+ blockType: BlockType.HAT,
438
+ arguments: {
439
+ REFERENCE: {
440
+ type: ArgumentType.NUMBER,
441
+ defaultValue: 10
442
+ }
443
+ }
444
+ },
445
+ {
446
+ opcode: 'videoOn',
447
+ blockType: BlockType.REPORTER,
448
+ text: formatMessage({
449
+ id: 'videoSensing.videoOn',
450
+ default: 'video [ATTRIBUTE] on [SUBJECT]',
451
+ description: 'Reporter that returns the amount of [ATTRIBUTE] for the selected [SUBJECT]'
452
+ }),
453
+ arguments: {
454
+ ATTRIBUTE: {
455
+ type: ArgumentType.NUMBER,
456
+ menu: 'ATTRIBUTE',
457
+ defaultValue: SensingAttribute.MOTION
458
+ },
459
+ SUBJECT: {
460
+ type: ArgumentType.NUMBER,
461
+ menu: 'SUBJECT',
462
+ defaultValue: SensingSubject.SPRITE
463
+ }
464
+ }
465
+ },
466
+ {
467
+ opcode: 'videoToggle',
468
+ text: formatMessage({
469
+ id: 'videoSensing.videoToggle',
470
+ default: 'turn video [VIDEO_STATE]',
471
+ description: 'Controls display of the video preview layer'
472
+ }),
473
+ arguments: {
474
+ VIDEO_STATE: {
475
+ type: ArgumentType.NUMBER,
476
+ menu: 'VIDEO_STATE',
477
+ defaultValue: VideoState.ON
478
+ }
479
+ }
480
+ },
481
+ {
482
+ opcode: 'setVideoTransparency',
483
+ text: formatMessage({
484
+ id: 'videoSensing.setVideoTransparency',
485
+ default: 'set video transparency to [TRANSPARENCY]',
486
+ description: 'Controls transparency of the video preview layer'
487
+ }),
488
+ arguments: {
489
+ TRANSPARENCY: {
490
+ type: ArgumentType.NUMBER,
491
+ defaultValue: 50
492
+ }
493
+ }
494
+ }
495
+ ],
496
+ menus: {
497
+ ATTRIBUTE: {
498
+ acceptReporters: true,
499
+ items: this._buildMenu(this.ATTRIBUTE_INFO)
500
+ },
501
+ SUBJECT: {
502
+ acceptReporters: true,
503
+ items: this._buildMenu(this.SUBJECT_INFO)
504
+ },
505
+ VIDEO_STATE: {
506
+ acceptReporters: true,
507
+ items: this._buildMenu(this.VIDEO_STATE_INFO)
508
+ }
509
+ }
510
+ };
511
+ }
512
+
513
+ /**
514
+ * Analyze a part of the frame that a target overlaps.
515
+ * @param {Target} target - a target to determine where to analyze
516
+ * @returns {MotionState} the motion state for the given target
517
+ */
518
+ _analyzeLocalMotion (target) {
519
+ const drawable = this.runtime.renderer._allDrawables[target.drawableID];
520
+ const state = this._getMotionState(target);
521
+ this.detect.getLocalMotion(drawable, state);
522
+ return state;
523
+ }
524
+
525
+ /**
526
+ * A scratch reporter block handle that analyzes the last two frames and
527
+ * depending on the arguments, returns the motion or direction for the
528
+ * whole stage or just the target sprite.
529
+ * @param {object} args - the block arguments
530
+ * @param {BlockUtility} util - the block utility
531
+ * @returns {number} the motion amount or direction of the stage or sprite
532
+ */
533
+ videoOn (args, util) {
534
+ this.detect.analyzeFrame();
535
+
536
+ let state = this.detect;
537
+ if (args.SUBJECT === SensingSubject.SPRITE) {
538
+ state = this._analyzeLocalMotion(util.target);
539
+ }
540
+
541
+ if (args.ATTRIBUTE === SensingAttribute.MOTION) {
542
+ return state.motionAmount;
543
+ }
544
+ return state.motionDirection;
545
+ }
546
+
547
+ /**
548
+ * A scratch hat block edge handle that analyzes the last two frames where
549
+ * the target sprite overlaps and if it has more motion than the given
550
+ * reference value.
551
+ * @param {object} args - the block arguments
552
+ * @param {BlockUtility} util - the block utility
553
+ * @returns {boolean} true if the sprite overlaps more motion than the
554
+ * reference
555
+ */
556
+ whenMotionGreaterThan (args, util) {
557
+ this.detect.analyzeFrame();
558
+ const state = this._analyzeLocalMotion(util.target);
559
+ return state.motionAmount > Number(args.REFERENCE);
560
+ }
561
+
562
+ /**
563
+ * A scratch command block handle that configures the video state from
564
+ * passed arguments.
565
+ * @param {object} args - the block arguments
566
+ * @param {VideoState} args.VIDEO_STATE - the video state to set the device to
567
+ */
568
+ videoToggle (args) {
569
+ const state = args.VIDEO_STATE;
570
+ this.globalVideoState = state;
571
+ if (state === VideoState.OFF) {
572
+ this.runtime.ioDevices.video.disableVideo();
573
+ } else {
574
+ this.runtime.ioDevices.video.enableVideo();
575
+ // Mirror if state is ON. Do not mirror if state is ON_FLIPPED.
576
+ this.runtime.ioDevices.video.mirror = state === VideoState.ON;
577
+ }
578
+ }
579
+
580
+ /**
581
+ * A scratch command block handle that configures the video preview's
582
+ * transparency from passed arguments.
583
+ * @param {object} args - the block arguments
584
+ * @param {number} args.TRANSPARENCY - the transparency to set the video
585
+ * preview to
586
+ */
587
+ setVideoTransparency (args) {
588
+ const transparency = Cast.toNumber(args.TRANSPARENCY);
589
+ this.globalVideoTransparency = transparency;
590
+ this.runtime.ioDevices.video.setPreviewGhost(transparency);
591
+ }
592
+ }
593
+
594
+ module.exports = Scratch3VideoSensingBlocks;