scratch-blocks 2.0.1 → 2.0.3

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 (276) hide show
  1. package/AGENTS.md +140 -0
  2. package/commitlint.config.js +2 -2
  3. package/dist/main.mjs +1 -2
  4. package/dist/types/msg/scratch_msgs.d.ts.map +1 -1
  5. package/dist/types/src/block_reporting.d.ts.map +1 -1
  6. package/dist/types/src/blocks/colour.d.ts +0 -19
  7. package/dist/types/src/blocks/colour.d.ts.map +1 -1
  8. package/dist/types/src/blocks/control.d.ts +0 -19
  9. package/dist/types/src/blocks/control.d.ts.map +1 -1
  10. package/dist/types/src/blocks/data.d.ts +0 -19
  11. package/dist/types/src/blocks/data.d.ts.map +1 -1
  12. package/dist/types/src/blocks/event.d.ts +0 -19
  13. package/dist/types/src/blocks/event.d.ts.map +1 -1
  14. package/dist/types/src/blocks/looks.d.ts +0 -19
  15. package/dist/types/src/blocks/looks.d.ts.map +1 -1
  16. package/dist/types/src/blocks/math.d.ts +0 -19
  17. package/dist/types/src/blocks/math.d.ts.map +1 -1
  18. package/dist/types/src/blocks/matrix.d.ts +0 -19
  19. package/dist/types/src/blocks/matrix.d.ts.map +1 -1
  20. package/dist/types/src/blocks/motion.d.ts +0 -19
  21. package/dist/types/src/blocks/motion.d.ts.map +1 -1
  22. package/dist/types/src/blocks/note.d.ts +0 -19
  23. package/dist/types/src/blocks/note.d.ts.map +1 -1
  24. package/dist/types/src/blocks/operators.d.ts +0 -19
  25. package/dist/types/src/blocks/operators.d.ts.map +1 -1
  26. package/dist/types/src/blocks/procedures.d.ts +6 -9
  27. package/dist/types/src/blocks/procedures.d.ts.map +1 -1
  28. package/dist/types/src/blocks/sensing.d.ts +0 -19
  29. package/dist/types/src/blocks/sensing.d.ts.map +1 -1
  30. package/dist/types/src/blocks/sound.d.ts +0 -19
  31. package/dist/types/src/blocks/sound.d.ts.map +1 -1
  32. package/dist/types/src/blocks/text.d.ts +0 -19
  33. package/dist/types/src/blocks/text.d.ts.map +1 -1
  34. package/dist/types/src/blocks/vertical_extensions.d.ts +0 -19
  35. package/dist/types/src/blocks/vertical_extensions.d.ts.map +1 -1
  36. package/dist/types/src/checkable_continuous_flyout.d.ts +2 -7
  37. package/dist/types/src/checkable_continuous_flyout.d.ts.map +1 -1
  38. package/dist/types/src/checkbox_bubble.d.ts +13 -12
  39. package/dist/types/src/checkbox_bubble.d.ts.map +1 -1
  40. package/dist/types/src/colours.d.ts.map +1 -1
  41. package/dist/types/src/constants.d.ts +0 -7
  42. package/dist/types/src/constants.d.ts.map +1 -1
  43. package/dist/types/src/context_menu_items.d.ts +0 -5
  44. package/dist/types/src/context_menu_items.d.ts.map +1 -1
  45. package/dist/types/src/data_category.d.ts +2 -4
  46. package/dist/types/src/data_category.d.ts.map +1 -1
  47. package/dist/types/src/events/events_block_comment_base.d.ts +2 -3
  48. package/dist/types/src/events/events_block_comment_base.d.ts.map +1 -1
  49. package/dist/types/src/events/events_block_comment_change.d.ts +0 -5
  50. package/dist/types/src/events/events_block_comment_change.d.ts.map +1 -1
  51. package/dist/types/src/events/events_block_comment_collapse.d.ts +0 -5
  52. package/dist/types/src/events/events_block_comment_collapse.d.ts.map +1 -1
  53. package/dist/types/src/events/events_block_comment_create.d.ts +0 -5
  54. package/dist/types/src/events/events_block_comment_create.d.ts.map +1 -1
  55. package/dist/types/src/events/events_block_comment_delete.d.ts +0 -5
  56. package/dist/types/src/events/events_block_comment_delete.d.ts.map +1 -1
  57. package/dist/types/src/events/events_block_comment_move.d.ts +0 -5
  58. package/dist/types/src/events/events_block_comment_move.d.ts.map +1 -1
  59. package/dist/types/src/events/events_block_comment_resize.d.ts +0 -5
  60. package/dist/types/src/events/events_block_comment_resize.d.ts.map +1 -1
  61. package/dist/types/src/events/events_block_drag_end.d.ts +1 -2
  62. package/dist/types/src/events/events_block_drag_end.d.ts.map +1 -1
  63. package/dist/types/src/events/events_block_drag_outside.d.ts +1 -2
  64. package/dist/types/src/events/events_block_drag_outside.d.ts.map +1 -1
  65. package/dist/types/src/events/events_scratch_variable_create.d.ts +0 -5
  66. package/dist/types/src/events/events_scratch_variable_create.d.ts.map +1 -1
  67. package/dist/types/src/fields/field_colour_slider.d.ts +7 -31
  68. package/dist/types/src/fields/field_colour_slider.d.ts.map +1 -1
  69. package/dist/types/src/fields/field_matrix.d.ts +0 -19
  70. package/dist/types/src/fields/field_matrix.d.ts.map +1 -1
  71. package/dist/types/src/fields/field_note.d.ts +8 -23
  72. package/dist/types/src/fields/field_note.d.ts.map +1 -1
  73. package/dist/types/src/fields/field_textinput_removable.d.ts +2 -4
  74. package/dist/types/src/fields/field_textinput_removable.d.ts.map +1 -1
  75. package/dist/types/src/fields/field_variable_getter.d.ts +0 -19
  76. package/dist/types/src/fields/field_variable_getter.d.ts.map +1 -1
  77. package/dist/types/src/fields/field_vertical_separator.d.ts +0 -19
  78. package/dist/types/src/fields/field_vertical_separator.d.ts.map +1 -1
  79. package/dist/types/src/fields/scratch_field_angle.d.ts +0 -19
  80. package/dist/types/src/fields/scratch_field_angle.d.ts.map +1 -1
  81. package/dist/types/src/fields/scratch_field_dropdown.d.ts +0 -5
  82. package/dist/types/src/fields/scratch_field_dropdown.d.ts.map +1 -1
  83. package/dist/types/src/fields/scratch_field_number.d.ts +0 -19
  84. package/dist/types/src/fields/scratch_field_number.d.ts.map +1 -1
  85. package/dist/types/src/fields/scratch_field_variable.d.ts +5 -8
  86. package/dist/types/src/fields/scratch_field_variable.d.ts.map +1 -1
  87. package/dist/types/src/flyout_checkbox_icon.d.ts +2 -3
  88. package/dist/types/src/flyout_checkbox_icon.d.ts.map +1 -1
  89. package/dist/types/src/glows.d.ts +1 -3
  90. package/dist/types/src/glows.d.ts.map +1 -1
  91. package/dist/types/src/index.d.ts +50 -51
  92. package/dist/types/src/index.d.ts.map +1 -1
  93. package/dist/types/src/procedures.d.ts +6 -8
  94. package/dist/types/src/procedures.d.ts.map +1 -1
  95. package/dist/types/src/recyclable_block_flyout_inflater.d.ts +3 -5
  96. package/dist/types/src/recyclable_block_flyout_inflater.d.ts.map +1 -1
  97. package/dist/types/src/renderer/bowler_hat.d.ts +2 -3
  98. package/dist/types/src/renderer/bowler_hat.d.ts.map +1 -1
  99. package/dist/types/src/renderer/cat/cat_face.d.ts +6 -5
  100. package/dist/types/src/renderer/cat/cat_face.d.ts.map +1 -1
  101. package/dist/types/src/renderer/cat/constants.d.ts +2 -2
  102. package/dist/types/src/renderer/cat/constants.d.ts.map +1 -1
  103. package/dist/types/src/renderer/cat/drawer.d.ts +4 -5
  104. package/dist/types/src/renderer/cat/drawer.d.ts.map +1 -1
  105. package/dist/types/src/renderer/cat/path_object.d.ts +2 -3
  106. package/dist/types/src/renderer/cat/path_object.d.ts.map +1 -1
  107. package/dist/types/src/renderer/cat/render_info.d.ts +3 -4
  108. package/dist/types/src/renderer/cat/render_info.d.ts.map +1 -1
  109. package/dist/types/src/renderer/cat/renderer.d.ts +7 -8
  110. package/dist/types/src/renderer/cat/renderer.d.ts.map +1 -1
  111. package/dist/types/src/renderer/constants.d.ts +41 -0
  112. package/dist/types/src/renderer/constants.d.ts.map +1 -0
  113. package/dist/types/src/renderer/drawer.d.ts +7 -6
  114. package/dist/types/src/renderer/drawer.d.ts.map +1 -1
  115. package/dist/types/src/renderer/path_object.d.ts +1 -3
  116. package/dist/types/src/renderer/path_object.d.ts.map +1 -1
  117. package/dist/types/src/renderer/render_info.d.ts +3 -4
  118. package/dist/types/src/renderer/render_info.d.ts.map +1 -1
  119. package/dist/types/src/renderer/renderer.d.ts +9 -16
  120. package/dist/types/src/renderer/renderer.d.ts.map +1 -1
  121. package/dist/types/src/scratch_block_paster.d.ts +0 -5
  122. package/dist/types/src/scratch_block_paster.d.ts.map +1 -1
  123. package/dist/types/src/scratch_blocks_utils.d.ts +0 -20
  124. package/dist/types/src/scratch_blocks_utils.d.ts.map +1 -1
  125. package/dist/types/src/scratch_comment_bubble.d.ts +3 -6
  126. package/dist/types/src/scratch_comment_bubble.d.ts.map +1 -1
  127. package/dist/types/src/scratch_comment_icon.d.ts +3 -4
  128. package/dist/types/src/scratch_comment_icon.d.ts.map +1 -1
  129. package/dist/types/src/scratch_connection_checker.d.ts +0 -5
  130. package/dist/types/src/scratch_connection_checker.d.ts.map +1 -1
  131. package/dist/types/src/scratch_continuous_category.d.ts +5 -5
  132. package/dist/types/src/scratch_continuous_category.d.ts.map +1 -1
  133. package/dist/types/src/scratch_continuous_toolbox.d.ts +3 -6
  134. package/dist/types/src/scratch_continuous_toolbox.d.ts.map +1 -1
  135. package/dist/types/src/scratch_dragger.d.ts +2 -11
  136. package/dist/types/src/scratch_dragger.d.ts.map +1 -1
  137. package/dist/types/src/scratch_insertion_marker_previewer.d.ts +0 -5
  138. package/dist/types/src/scratch_insertion_marker_previewer.d.ts.map +1 -1
  139. package/dist/types/src/scratch_variable_map.d.ts +0 -5
  140. package/dist/types/src/scratch_variable_map.d.ts.map +1 -1
  141. package/dist/types/src/scratch_variable_model.d.ts +2 -3
  142. package/dist/types/src/scratch_variable_model.d.ts.map +1 -1
  143. package/dist/types/src/scratch_zoom_controls.d.ts +4 -6
  144. package/dist/types/src/scratch_zoom_controls.d.ts.map +1 -1
  145. package/dist/types/src/shadows.d.ts +2 -2
  146. package/dist/types/src/shadows.d.ts.map +1 -1
  147. package/dist/types/src/status_indicator_label.d.ts +4 -6
  148. package/dist/types/src/status_indicator_label.d.ts.map +1 -1
  149. package/dist/types/src/status_indicator_label_flyout_inflater.d.ts +1 -6
  150. package/dist/types/src/status_indicator_label_flyout_inflater.d.ts.map +1 -1
  151. package/dist/types/src/variables.d.ts +4 -8
  152. package/dist/types/src/variables.d.ts.map +1 -1
  153. package/dist/types/src/xml.d.ts +2 -3
  154. package/dist/types/src/xml.d.ts.map +1 -1
  155. package/dist/types/tests/jsunit/block_test.d.ts.map +1 -1
  156. package/dist/types/tests/jsunit/connection_db_test.d.ts +3 -3
  157. package/dist/types/tests/jsunit/connection_db_test.d.ts.map +1 -1
  158. package/dist/types/tests/jsunit/connection_test.d.ts.map +1 -1
  159. package/dist/types/tests/jsunit/event_test.d.ts.map +1 -1
  160. package/dist/types/tests/jsunit/extensions_test.d.ts.map +1 -1
  161. package/dist/types/tests/jsunit/field_number_test.d.ts.map +1 -1
  162. package/dist/types/tests/jsunit/field_test.d.ts.map +1 -1
  163. package/dist/types/tests/jsunit/field_variable_getter_test.d.ts.map +1 -1
  164. package/dist/types/tests/jsunit/field_variable_test.d.ts.map +1 -1
  165. package/dist/types/tests/jsunit/gesture_test.d.ts.map +1 -1
  166. package/dist/types/tests/jsunit/input_test.d.ts +1 -0
  167. package/dist/types/tests/jsunit/input_test.d.ts.map +1 -1
  168. package/dist/types/tests/jsunit/json_test.d.ts.map +1 -1
  169. package/dist/types/tests/jsunit/names_test.d.ts.map +1 -1
  170. package/dist/types/tests/jsunit/procedure_test.d.ts.map +1 -1
  171. package/dist/types/tests/jsunit/scratch_block_comment_test.d.ts.map +1 -1
  172. package/dist/types/tests/jsunit/svg_test.d.ts.map +1 -1
  173. package/dist/types/tests/jsunit/test_utilities.d.ts.map +1 -1
  174. package/dist/types/tests/jsunit/utils_test.d.ts.map +1 -1
  175. package/dist/types/tests/jsunit/variable_map_test.d.ts.map +1 -1
  176. package/dist/types/tests/jsunit/variable_model_test.d.ts.map +1 -1
  177. package/dist/types/tests/jsunit/widget_div_test.d.ts.map +1 -1
  178. package/dist/types/tests/jsunit/workspace_comment_test.d.ts.map +1 -1
  179. package/dist/types/tests/jsunit/workspace_test.d.ts.map +1 -1
  180. package/dist/types/tests/jsunit/workspace_undo_redo_test.d.ts.map +1 -1
  181. package/dist/types/tests/jsunit/xml_test.d.ts.map +1 -1
  182. package/dist/types/tests/workspace_svg/workspace_svg_test.d.ts.map +1 -1
  183. package/eslint.config.mjs +69 -0
  184. package/i18n/create_scratch_msgs.js +44 -45
  185. package/i18n/js_to_json.js +40 -32
  186. package/i18n/json_to_js.js +37 -37
  187. package/i18n/sync_tx_translations.js +64 -65
  188. package/i18n/test_scratch_msgs.js +66 -63
  189. package/msg/js/en.js +289 -287
  190. package/msg/json/en.json +284 -284
  191. package/msg/messages.js +289 -287
  192. package/msg/scratch_msgs.js +22959 -22970
  193. package/package.json +13 -10
  194. package/prettier.config.mjs +3 -0
  195. package/release.config.js +7 -7
  196. package/renovate.json5 +7 -9
  197. package/src/block_reporting.ts +16 -19
  198. package/src/blocks/colour.ts +12 -15
  199. package/src/blocks/control.ts +171 -174
  200. package/src/blocks/data.ts +233 -272
  201. package/src/blocks/event.ts +121 -123
  202. package/src/blocks/looks.ts +165 -167
  203. package/src/blocks/math.ts +44 -46
  204. package/src/blocks/matrix.ts +11 -13
  205. package/src/blocks/motion.ts +151 -153
  206. package/src/blocks/note.ts +11 -13
  207. package/src/blocks/operators.ts +158 -160
  208. package/src/blocks/procedures.ts +421 -511
  209. package/src/blocks/sensing.ts +163 -165
  210. package/src/blocks/sound.ts +58 -60
  211. package/src/blocks/text.ts +10 -12
  212. package/src/blocks/vertical_extensions.ts +86 -102
  213. package/src/checkable_continuous_flyout.ts +25 -42
  214. package/src/checkbox_bubble.ts +83 -100
  215. package/src/colours.ts +35 -37
  216. package/src/constants.ts +22 -29
  217. package/src/context_menu_items.ts +59 -84
  218. package/src/css.ts +3 -4
  219. package/src/data_category.ts +137 -251
  220. package/src/events/events_block_comment_base.ts +23 -30
  221. package/src/events/events_block_comment_change.ts +21 -42
  222. package/src/events/events_block_comment_collapse.ts +27 -36
  223. package/src/events/events_block_comment_create.ts +32 -46
  224. package/src/events/events_block_comment_delete.ts +12 -19
  225. package/src/events/events_block_comment_move.ts +27 -52
  226. package/src/events/events_block_comment_resize.ts +28 -55
  227. package/src/events/events_block_drag_end.ts +16 -26
  228. package/src/events/events_block_drag_outside.ts +12 -22
  229. package/src/events/events_scratch_variable_create.ts +37 -42
  230. package/src/fields/field_colour_slider.ts +186 -216
  231. package/src/fields/field_matrix.ts +202 -270
  232. package/src/fields/field_note.ts +277 -375
  233. package/src/fields/field_textinput_removable.ts +25 -40
  234. package/src/fields/field_variable_getter.ts +26 -31
  235. package/src/fields/field_vertical_separator.ts +19 -24
  236. package/src/fields/scratch_field_angle.ts +151 -187
  237. package/src/fields/scratch_field_dropdown.ts +15 -19
  238. package/src/fields/scratch_field_number.ts +125 -181
  239. package/src/fields/scratch_field_variable.ts +57 -75
  240. package/src/flyout_checkbox_icon.ts +18 -28
  241. package/src/glows.ts +52 -59
  242. package/src/index.ts +119 -133
  243. package/src/procedures.ts +150 -209
  244. package/src/recyclable_block_flyout_inflater.ts +14 -25
  245. package/src/renderer/bowler_hat.ts +6 -8
  246. package/src/renderer/cat/cat_face.ts +98 -99
  247. package/src/renderer/cat/constants.ts +67 -87
  248. package/src/renderer/cat/drawer.ts +21 -27
  249. package/src/renderer/cat/path_object.ts +3 -5
  250. package/src/renderer/cat/render_info.ts +5 -8
  251. package/src/renderer/cat/renderer.ts +12 -16
  252. package/src/renderer/constants.ts +34 -49
  253. package/src/renderer/drawer.ts +35 -51
  254. package/src/renderer/path_object.ts +4 -10
  255. package/src/renderer/render_info.ts +39 -56
  256. package/src/renderer/renderer.ts +17 -30
  257. package/src/scratch_block_paster.ts +12 -20
  258. package/src/scratch_blocks_utils.ts +4 -7
  259. package/src/scratch_comment_bubble.ts +76 -105
  260. package/src/scratch_comment_icon.ts +75 -124
  261. package/src/scratch_connection_checker.ts +7 -17
  262. package/src/scratch_continuous_category.ts +24 -28
  263. package/src/scratch_continuous_toolbox.ts +20 -27
  264. package/src/scratch_dragger.ts +42 -81
  265. package/src/scratch_insertion_marker_previewer.ts +6 -11
  266. package/src/scratch_variable_map.ts +5 -12
  267. package/src/scratch_variable_model.ts +6 -13
  268. package/src/scratch_zoom_controls.ts +101 -156
  269. package/src/shadows.ts +32 -37
  270. package/src/status_indicator_label.ts +54 -67
  271. package/src/status_indicator_label_flyout_inflater.ts +11 -21
  272. package/src/variables.ts +92 -141
  273. package/src/xml.ts +21 -35
  274. package/tsconfig.json +3 -7
  275. package/types/continuous-toolbox.d.ts +1 -1
  276. package/dist/main.mjs.LICENSE.txt +0 -163
@@ -1,5 +1,4 @@
1
1
  /**
2
- * @license
3
2
  * Visual Blocks Editor
4
3
  *
5
4
  * Copyright 2018 Massachusetts Institute of Technology
@@ -17,421 +16,377 @@
17
16
  * See the License for the specific language governing permissions and
18
17
  * limitations under the License.
19
18
  */
20
-
21
19
  /**
22
- * @fileoverview Note input field, for selecting a musical note on a piano.
20
+ * @file Note input field, for selecting a musical note on a piano.
23
21
  * @author ericr@media.mit.edu (Eric Rosenbaum)
24
22
  */
25
- import * as Blockly from "blockly/core";
23
+ import * as Blockly from 'blockly/core'
26
24
 
27
25
  /**
28
26
  * Class for a note input field, for selecting a musical note on a piano.
29
- * @param {(string|number)=} opt_value The initial content of the field. The
27
+ * @param opt_value The initial content of the field. The
30
28
  * value should cast to a number, and if it does not, '0' will be used.
31
- * @param {Function=} opt_validator An optional function that is called
29
+ * @param opt_validator An optional function that is called
32
30
  * to validate any constraints on what the user entered. Takes the new
33
31
  * text as an argument and returns the accepted text or null to abort
34
32
  * the change.
35
- * @extends {Blockly.FieldTextInput}
36
- * @constructor
37
33
  */
38
34
  export class FieldNote extends Blockly.FieldTextInput {
39
35
  /**
40
36
  * Width of the field. Computed when drawing it, and used for animation.
41
37
  */
42
- private fieldEditorWidth_ = 0;
38
+ private fieldEditorWidth_ = 0
43
39
 
44
40
  /**
45
41
  * Height of the field. Computed when drawing it.
46
42
  */
47
- private fieldEditorHeight_ = 0;
43
+ private fieldEditorHeight_ = 0
48
44
 
49
45
  /**
50
46
  * The piano SVG.
51
47
  */
52
- private pianoSVG_: SVGElement | null = null;
48
+ private pianoSVG_: SVGElement | null = null
53
49
 
54
50
  /**
55
51
  * Array of SVG elements representing the clickable piano keys.
56
52
  */
57
- private keySVGs_: SVGElement[] = [];
53
+ private keySVGs_: SVGElement[] = []
58
54
 
59
55
  /**
60
56
  * Note name indicator at the top of the field.
61
57
  */
62
- private noteNameText_: SVGElement | null = null;
58
+ private noteNameText_: SVGElement | null = null
63
59
 
64
60
  /**
65
61
  * Note name indicator on the low C key.
66
62
  */
67
- private lowCText_: SVGElement | null = null;
63
+ private lowCText_: SVGElement | null = null
68
64
 
69
65
  /**
70
66
  * Note name indicator on the low C key.
71
67
  */
72
- private highCText_: SVGElement | null = null;
68
+ private highCText_: SVGElement | null = null
73
69
 
74
70
  /**
75
71
  * Octave number of the currently displayed range of keys.
76
72
  */
77
- private displayedOctave_: number | null = null;
73
+ private displayedOctave_: number | null = null
78
74
 
79
75
  /**
80
76
  * Current animation position of the piano SVG, as it shifts left or right to
81
77
  * change octaves.
82
78
  */
83
- private animationPos_ = 0;
79
+ private animationPos_ = 0
84
80
 
85
81
  /**
86
82
  * Target position for the animation as the piano SVG shifts left or right.
87
83
  */
88
- private animationTarget_ = 0;
84
+ private animationTarget_ = 0
89
85
 
90
86
  /**
91
87
  * A flag indicating that the mouse is currently down. Used in combination with
92
88
  * mouse enter events to update the key selection while dragging.
93
89
  */
94
- private mouseIsDown_ = false;
90
+ private mouseIsDown_ = false
95
91
 
96
92
  /**
97
93
  * An array of wrappers for mouse down events on piano keys.
98
94
  */
99
- private mouseDownWrappers_: Blockly.browserEvents.Data[] = [];
95
+ private mouseDownWrappers_: Blockly.browserEvents.Data[] = []
100
96
 
101
97
  /**
102
98
  * A wrapper for the mouse up event.
103
99
  */
104
- private mouseUpWrapper_: Blockly.browserEvents.Data | null = null;
100
+ private mouseUpWrapper_: Blockly.browserEvents.Data | null = null
105
101
 
106
102
  /**
107
103
  * An array of wrappers for mouse enter events on piano keys.
108
104
  */
109
- private mouseEnterWrappers_: Blockly.browserEvents.Data[] = [];
105
+ private mouseEnterWrappers_: Blockly.browserEvents.Data[] = []
110
106
 
111
107
  /**
112
108
  * A wrapper for the mouse down event on the octave down button.
113
109
  */
114
- private octaveDownMouseDownWrapper_: Blockly.browserEvents.Data | null = null;
110
+ private octaveDownMouseDownWrapper_: Blockly.browserEvents.Data | null = null
115
111
 
116
112
  /**
117
113
  * A wrapper for the mouse down event on the octave up button.
118
114
  */
119
- private octaveUpMouseDownWrapper_: Blockly.browserEvents.Data | null = null;
115
+ private octaveUpMouseDownWrapper_: Blockly.browserEvents.Data | null = null
120
116
 
121
117
  /**
122
118
  * Inset in pixels of content displayed in the field, caused by parent properties.
123
119
  * The inset is actually determined by the CSS property blocklyDropDownDiv- it is
124
120
  * the sum of the padding and border thickness.
125
121
  */
126
- static INSET = 5;
122
+ static INSET = 5
127
123
 
128
124
  /**
129
125
  * Height of the top area of the field, in px.
130
126
  */
131
- static readonly TOP_MENU_HEIGHT = 32 - FieldNote.INSET;
127
+ static readonly TOP_MENU_HEIGHT = 32 - FieldNote.INSET
132
128
 
133
129
  /**
134
130
  * Padding on the top and sides of the field, in px.
135
131
  */
136
- static readonly EDGE_PADDING = 1;
132
+ static readonly EDGE_PADDING = 1
137
133
 
138
134
  /**
139
135
  * Height of the drop shadow on the piano, in px.
140
136
  */
141
- static readonly SHADOW_HEIGHT = 4;
137
+ static readonly SHADOW_HEIGHT = 4
142
138
 
143
139
  /**
144
140
  * Color for the shadow on the piano.
145
141
  */
146
- static readonly SHADOW_COLOR = "#000";
142
+ static readonly SHADOW_COLOR = '#000'
147
143
 
148
144
  /**
149
145
  * Opacity for the shadow on the piano.
150
146
  */
151
- static readonly SHADOW_OPACITY = 0.2;
147
+ static readonly SHADOW_OPACITY = 0.2
152
148
 
153
149
  /**
154
150
  * A color for the white piano keys.
155
151
  */
156
- static readonly WHITE_KEY_COLOR = "#FFFFFF";
152
+ static readonly WHITE_KEY_COLOR = '#FFFFFF'
157
153
 
158
154
  /**
159
155
  * A color for the black piano keys.
160
156
  */
161
- static readonly BLACK_KEY_COLOR = "#323133";
157
+ static readonly BLACK_KEY_COLOR = '#323133'
162
158
 
163
159
  /**
164
160
  * A color for stroke around black piano keys.
165
161
  */
166
- static readonly BLACK_KEY_STROKE = "#555555";
162
+ static readonly BLACK_KEY_STROKE = '#555555'
167
163
 
168
164
  /**
169
165
  * A color for the selected state of a piano key.
170
166
  */
171
- static readonly KEY_SELECTED_COLOR = "#b0d6ff";
167
+ static readonly KEY_SELECTED_COLOR = '#b0d6ff'
172
168
 
173
169
  /**
174
170
  * The number of white keys in one octave on the piano.
175
171
  */
176
- static readonly NUM_WHITE_KEYS = 8;
172
+ static readonly NUM_WHITE_KEYS = 8
177
173
 
178
174
  /**
179
175
  * Height of a white piano key, in px.
180
176
  */
181
- static readonly WHITE_KEY_HEIGHT = 72;
177
+ static readonly WHITE_KEY_HEIGHT = 72
182
178
 
183
179
  /**
184
180
  * Width of a white piano key, in px.
185
181
  */
186
- static readonly WHITE_KEY_WIDTH = 40;
182
+ static readonly WHITE_KEY_WIDTH = 40
187
183
 
188
184
  /**
189
185
  * Height of a black piano key, in px.
190
186
  */
191
- static readonly BLACK_KEY_HEIGHT = 40;
187
+ static readonly BLACK_KEY_HEIGHT = 40
192
188
 
193
189
  /**
194
190
  * Width of a black piano key, in px.
195
191
  */
196
- static readonly BLACK_KEY_WIDTH = 32;
192
+ static readonly BLACK_KEY_WIDTH = 32
197
193
 
198
194
  /**
199
195
  * Radius of the curved bottom corner of a piano key, in px.
200
196
  */
201
- static readonly KEY_RADIUS = 6;
197
+ static readonly KEY_RADIUS = 6
202
198
 
203
199
  /**
204
200
  * Bottom padding for the labels on C keys.
205
201
  */
206
- static readonly KEY_LABEL_PADDING = 8;
202
+ static readonly KEY_LABEL_PADDING = 8
207
203
 
208
204
  /**
209
205
  * An array of objects with data describing the keys on the piano.
210
206
  */
211
207
  static readonly KEY_INFO = [
212
- { name: "C", pitch: 0 },
213
- { name: "C♯", pitch: 1, isBlack: true },
214
- { name: "D", pitch: 2 },
215
- { name: "E♭", pitch: 3, isBlack: true },
216
- { name: "E", pitch: 4 },
217
- { name: "F", pitch: 5 },
218
- { name: "F♯", pitch: 6, isBlack: true },
219
- { name: "G", pitch: 7 },
220
- { name: "G♯", pitch: 8, isBlack: true },
221
- { name: "A", pitch: 9 },
222
- { name: "B♭", pitch: 10, isBlack: true },
223
- { name: "B", pitch: 11 },
224
- { name: "C", pitch: 12 },
225
- ];
208
+ { name: 'C', pitch: 0 },
209
+ { name: 'C♯', pitch: 1, isBlack: true },
210
+ { name: 'D', pitch: 2 },
211
+ { name: 'E♭', pitch: 3, isBlack: true },
212
+ { name: 'E', pitch: 4 },
213
+ { name: 'F', pitch: 5 },
214
+ { name: 'F♯', pitch: 6, isBlack: true },
215
+ { name: 'G', pitch: 7 },
216
+ { name: 'G♯', pitch: 8, isBlack: true },
217
+ { name: 'A', pitch: 9 },
218
+ { name: 'B♭', pitch: 10, isBlack: true },
219
+ { name: 'B', pitch: 11 },
220
+ { name: 'C', pitch: 12 },
221
+ ]
226
222
 
227
223
  /**
228
224
  * The MIDI note number of the highest note selectable on the piano.
229
225
  */
230
- static readonly MAX_NOTE = 130;
226
+ static readonly MAX_NOTE = 130
231
227
 
232
228
  /**
233
229
  * The fraction of the distance to the target location to move the piano at each
234
230
  * step of the animation.
235
231
  */
236
- static readonly ANIMATION_FRACTION = 0.2;
232
+ static readonly ANIMATION_FRACTION = 0.2
237
233
 
238
234
  /**
239
235
  * Path to the arrow svg icon, used on the octave buttons.
240
236
  */
241
- static readonly ARROW_SVG_PATH = "icons/arrow_button.svg";
237
+ static readonly ARROW_SVG_PATH = 'icons/arrow_button.svg'
242
238
 
243
239
  /**
244
240
  * The size of the square octave buttons.
245
241
  */
246
- static readonly OCTAVE_BUTTON_SIZE = 32;
242
+ static readonly OCTAVE_BUTTON_SIZE = 32
247
243
 
248
244
  /**
249
245
  * Construct a FieldNote from a JSON arg object.
250
- *
251
246
  * @param options A JSON object with options.
252
247
  * @returns The new field instance.
253
248
  */
254
249
  static fromJson(options: FieldNoteJsonConfig): FieldNote {
255
- return new FieldNote(options["note"]);
250
+ return new FieldNote(options.note)
256
251
  }
257
252
 
258
253
  /**
259
254
  * Clean up this FieldNote, as well as the inherited FieldTextInput.
260
255
  */
261
256
  dispose() {
262
- super.dispose();
263
- this.mouseDownWrappers_.forEach(function (wrapper) {
264
- Blockly.browserEvents.unbind(wrapper);
265
- });
266
- this.mouseEnterWrappers_.forEach(function (wrapper) {
267
- Blockly.browserEvents.unbind(wrapper);
268
- });
257
+ super.dispose()
258
+ this.mouseDownWrappers_.forEach(wrapper => {
259
+ Blockly.browserEvents.unbind(wrapper)
260
+ })
261
+ this.mouseEnterWrappers_.forEach(wrapper => {
262
+ Blockly.browserEvents.unbind(wrapper)
263
+ })
269
264
  if (this.mouseUpWrapper_) {
270
- Blockly.browserEvents.unbind(this.mouseUpWrapper_);
265
+ Blockly.browserEvents.unbind(this.mouseUpWrapper_)
271
266
  }
272
267
  if (this.octaveDownMouseDownWrapper_) {
273
- Blockly.browserEvents.unbind(this.octaveDownMouseDownWrapper_);
268
+ Blockly.browserEvents.unbind(this.octaveDownMouseDownWrapper_)
274
269
  }
275
270
  if (this.octaveUpMouseDownWrapper_) {
276
- Blockly.browserEvents.unbind(this.octaveUpMouseDownWrapper_);
271
+ Blockly.browserEvents.unbind(this.octaveUpMouseDownWrapper_)
277
272
  }
278
- this.pianoSVG_ = null;
279
- this.keySVGs_.length = 0;
280
- this.noteNameText_ = null;
281
- this.lowCText_ = null;
282
- this.highCText_ = null;
273
+ this.pianoSVG_ = null
274
+ this.keySVGs_.length = 0
275
+ this.noteNameText_ = null
276
+ this.lowCText_ = null
277
+ this.highCText_ = null
283
278
  }
284
279
 
285
280
  /**
286
281
  * Show a field with piano keys.
282
+ * @param event The triggering pointer event.
283
+ * @param quietInput If true, suppress the sound preview while the editor opens.
287
284
  */
288
285
  showEditor_(event: PointerEvent, quietInput = false) {
289
- super.showEditor_(event, quietInput, false);
286
+ super.showEditor_(event, quietInput, false)
290
287
 
291
288
  // Build the SVG DOM.
292
- const div = Blockly.DropDownDiv.getContentDiv();
289
+ const div = Blockly.DropDownDiv.getContentDiv()
293
290
 
294
- this.fieldEditorWidth_ =
295
- FieldNote.NUM_WHITE_KEYS * FieldNote.WHITE_KEY_WIDTH +
296
- FieldNote.EDGE_PADDING;
297
- this.fieldEditorHeight_ =
298
- FieldNote.TOP_MENU_HEIGHT +
299
- FieldNote.WHITE_KEY_HEIGHT +
300
- FieldNote.EDGE_PADDING;
291
+ this.fieldEditorWidth_ = FieldNote.NUM_WHITE_KEYS * FieldNote.WHITE_KEY_WIDTH + FieldNote.EDGE_PADDING
292
+ this.fieldEditorHeight_ = FieldNote.TOP_MENU_HEIGHT + FieldNote.WHITE_KEY_HEIGHT + FieldNote.EDGE_PADDING
301
293
 
302
294
  const svg = Blockly.utils.dom.createSvgElement(
303
- "svg",
295
+ 'svg',
304
296
  {
305
- xmlns: "http://www.w3.org/2000/svg",
306
- "xmlns:html": "http://www.w3.org/1999/xhtml",
307
- "xmlns:xlink": "http://www.w3.org/1999/xlink",
308
- version: "1.1",
309
- height: this.fieldEditorHeight_ + "px",
310
- width: this.fieldEditorWidth_ + "px",
297
+ xmlns: 'http://www.w3.org/2000/svg',
298
+ 'xmlns:html': 'http://www.w3.org/1999/xhtml',
299
+ 'xmlns:xlink': 'http://www.w3.org/1999/xlink',
300
+ version: '1.1',
301
+ height: this.fieldEditorHeight_ + 'px',
302
+ width: this.fieldEditorWidth_ + 'px',
311
303
  },
312
- div
313
- );
304
+ div,
305
+ )
314
306
 
315
307
  // Add the white and black keys
316
308
  // Since we are adding the keys from left to right in order, they need
317
309
  // to be in two groups in order to layer correctly.
318
- this.pianoSVG_ = Blockly.utils.dom.createSvgElement("g", {}, svg);
319
- const whiteKeyGroup = Blockly.utils.dom.createSvgElement(
320
- "g",
321
- {},
322
- this.pianoSVG_
323
- );
324
- const blackKeyGroup = Blockly.utils.dom.createSvgElement(
325
- "g",
326
- {},
327
- this.pianoSVG_
328
- );
310
+ this.pianoSVG_ = Blockly.utils.dom.createSvgElement('g', {}, svg)
311
+ const whiteKeyGroup = Blockly.utils.dom.createSvgElement('g', {}, this.pianoSVG_)
312
+ const blackKeyGroup = Blockly.utils.dom.createSvgElement('g', {}, this.pianoSVG_)
329
313
 
330
314
  // Add three piano octaves, so we can animate moving up or down an octave.
331
315
  // Only the middle octave gets bound to events.
332
- this.keySVGs_ = [];
333
- this.addPianoOctave_(
334
- -this.fieldEditorWidth_ + FieldNote.EDGE_PADDING,
335
- whiteKeyGroup,
336
- blackKeyGroup,
337
- null
338
- );
339
- this.addPianoOctave_(0, whiteKeyGroup, blackKeyGroup, this.keySVGs_);
340
- this.addPianoOctave_(
341
- this.fieldEditorWidth_ - FieldNote.EDGE_PADDING,
342
- whiteKeyGroup,
343
- blackKeyGroup,
344
- null
345
- );
316
+ this.keySVGs_ = []
317
+ this.addPianoOctave_(-this.fieldEditorWidth_ + FieldNote.EDGE_PADDING, whiteKeyGroup, blackKeyGroup, null)
318
+ this.addPianoOctave_(0, whiteKeyGroup, blackKeyGroup, this.keySVGs_)
319
+ this.addPianoOctave_(this.fieldEditorWidth_ - FieldNote.EDGE_PADDING, whiteKeyGroup, blackKeyGroup, null)
346
320
 
347
321
  // Note name indicator at the top of the field
348
322
  this.noteNameText_ = Blockly.utils.dom.createSvgElement(
349
- "text",
323
+ 'text',
350
324
  {
351
325
  x: this.fieldEditorWidth_ / 2,
352
326
  y: FieldNote.TOP_MENU_HEIGHT / 2,
353
- class: "blocklyText",
354
- "text-anchor": "middle",
355
- "dominant-baseline": "middle",
327
+ class: 'blocklyText',
328
+ 'text-anchor': 'middle',
329
+ 'dominant-baseline': 'middle',
356
330
  },
357
- svg
358
- );
331
+ svg,
332
+ )
359
333
 
360
334
  // Note names on the low and high C keys
361
- const lowCX = FieldNote.WHITE_KEY_WIDTH / 2;
362
- this.lowCText_ = this.addCKeyLabel_(lowCX, svg);
363
- const highCX =
364
- lowCX + FieldNote.WHITE_KEY_WIDTH * (FieldNote.NUM_WHITE_KEYS - 1);
365
- this.highCText_ = this.addCKeyLabel_(highCX, svg);
335
+ const lowCX = FieldNote.WHITE_KEY_WIDTH / 2
336
+ this.lowCText_ = this.addCKeyLabel_(lowCX, svg)
337
+ const highCX = lowCX + FieldNote.WHITE_KEY_WIDTH * (FieldNote.NUM_WHITE_KEYS - 1)
338
+ this.highCText_ = this.addCKeyLabel_(highCX, svg)
366
339
 
367
340
  // Horizontal line at the top of the keys
368
341
  Blockly.utils.dom.createSvgElement(
369
- "line",
342
+ 'line',
370
343
  {
371
- stroke: (
372
- this.sourceBlock_.getParent() as Blockly.BlockSvg
373
- ).getColourTertiary(),
344
+ stroke: (this.sourceBlock_!.getParent() as Blockly.BlockSvg).getColourTertiary(),
374
345
  x1: 0,
375
346
  y1: FieldNote.TOP_MENU_HEIGHT,
376
347
  x2: this.fieldEditorWidth_,
377
348
  y2: FieldNote.TOP_MENU_HEIGHT,
378
349
  },
379
- svg
380
- );
350
+ svg,
351
+ )
381
352
 
382
353
  // Drop shadow at the top of the keys
383
354
  Blockly.utils.dom.createSvgElement(
384
- "rect",
355
+ 'rect',
385
356
  {
386
357
  x: 0,
387
358
  y: FieldNote.TOP_MENU_HEIGHT,
388
359
  width: this.fieldEditorWidth_,
389
360
  height: FieldNote.SHADOW_HEIGHT,
390
361
  fill: FieldNote.SHADOW_COLOR,
391
- "fill-opacity": FieldNote.SHADOW_OPACITY,
362
+ 'fill-opacity': FieldNote.SHADOW_OPACITY,
392
363
  },
393
- svg
394
- );
364
+ svg,
365
+ )
395
366
 
396
367
  // Octave buttons
397
- const octaveDownButton = this.addOctaveButton_(0, true, svg);
368
+ const octaveDownButton = this.addOctaveButton_(0, true, svg)
398
369
  const octaveUpButton = this.addOctaveButton_(
399
- this.fieldEditorWidth_ +
400
- FieldNote.INSET * 2 -
401
- FieldNote.OCTAVE_BUTTON_SIZE,
370
+ this.fieldEditorWidth_ + FieldNote.INSET * 2 - FieldNote.OCTAVE_BUTTON_SIZE,
402
371
  false,
403
- svg
404
- );
405
-
406
- this.octaveDownMouseDownWrapper_ = Blockly.browserEvents.bind(
407
- octaveDownButton,
408
- "mousedown",
409
- this,
410
- function () {
411
- this.changeOctaveBy_(-1);
412
- }
413
- );
414
- this.octaveUpMouseDownWrapper_ = Blockly.browserEvents.bind(
415
- octaveUpButton,
416
- "mousedown",
417
- this,
418
- function () {
419
- this.changeOctaveBy_(1);
420
- }
421
- );
422
- const sourceBlock = this.getSourceBlock() as Blockly.BlockSvg;
423
- Blockly.DropDownDiv.setColour(
424
- sourceBlock.getParent().getColour(),
425
- sourceBlock.getParent().getColourTertiary()
426
- );
427
- Blockly.DropDownDiv.showPositionedByBlock(this, sourceBlock);
428
-
429
- this.updateSelection_();
372
+ svg,
373
+ )
374
+
375
+ this.octaveDownMouseDownWrapper_ = Blockly.browserEvents.bind(octaveDownButton, 'mousedown', this, () => {
376
+ this.changeOctaveBy_(-1)
377
+ })
378
+ this.octaveUpMouseDownWrapper_ = Blockly.browserEvents.bind(octaveUpButton, 'mousedown', this, () => {
379
+ this.changeOctaveBy_(1)
380
+ })
381
+ const sourceBlock = this.getSourceBlock() as Blockly.BlockSvg
382
+ Blockly.DropDownDiv.setColour(sourceBlock.getParent()!.getColour(), sourceBlock.getParent()!.getColourTertiary())
383
+ Blockly.DropDownDiv.showPositionedByBlock(this, sourceBlock)
384
+
385
+ this.updateSelection_()
430
386
  }
431
387
 
432
388
  /**
433
389
  * Add one octave of piano keys drawn using SVG.
434
- *
435
390
  * @param x The x position of the left edge of this octave of keys.
436
391
  * @param whiteKeyGroup The group for all white piano keys.
437
392
  * @param blackKeyGroup The group for all black piano keys.
@@ -441,59 +396,47 @@ export class FieldNote extends Blockly.FieldTextInput {
441
396
  x: number,
442
397
  whiteKeyGroup: SVGElement,
443
398
  blackKeyGroup: SVGElement,
444
- keySVGarray: SVGElement[]
399
+ keySVGarray: SVGElement[] | null,
445
400
  ) {
446
- let xIncrement, width, height, fill, stroke, group;
447
- x += FieldNote.EDGE_PADDING / 2;
448
- const y = FieldNote.TOP_MENU_HEIGHT;
449
- for (var i = 0; i < FieldNote.KEY_INFO.length; i++) {
401
+ let xIncrement, width, height, fill, stroke, group
402
+ x += FieldNote.EDGE_PADDING / 2
403
+ const y = FieldNote.TOP_MENU_HEIGHT
404
+ for (let i = 0; i < FieldNote.KEY_INFO.length; i++) {
450
405
  // Draw a black or white key
451
406
  if (FieldNote.KEY_INFO[i].isBlack) {
452
407
  // Black keys are shifted back half a key
453
- x -= FieldNote.BLACK_KEY_WIDTH / 2;
454
- xIncrement = FieldNote.BLACK_KEY_WIDTH / 2;
455
- width = FieldNote.BLACK_KEY_WIDTH;
456
- height = FieldNote.BLACK_KEY_HEIGHT;
457
- fill = FieldNote.BLACK_KEY_COLOR;
458
- stroke = FieldNote.BLACK_KEY_STROKE;
459
- group = blackKeyGroup;
408
+ x -= FieldNote.BLACK_KEY_WIDTH / 2
409
+ xIncrement = FieldNote.BLACK_KEY_WIDTH / 2
410
+ width = FieldNote.BLACK_KEY_WIDTH
411
+ height = FieldNote.BLACK_KEY_HEIGHT
412
+ fill = FieldNote.BLACK_KEY_COLOR
413
+ stroke = FieldNote.BLACK_KEY_STROKE
414
+ group = blackKeyGroup
460
415
  } else {
461
- xIncrement = FieldNote.WHITE_KEY_WIDTH;
462
- width = FieldNote.WHITE_KEY_WIDTH;
463
- height = FieldNote.WHITE_KEY_HEIGHT;
464
- fill = FieldNote.WHITE_KEY_COLOR;
465
- stroke = (
466
- this.sourceBlock_.getParent() as Blockly.BlockSvg
467
- ).getColourTertiary();
468
- group = whiteKeyGroup;
416
+ xIncrement = FieldNote.WHITE_KEY_WIDTH
417
+ width = FieldNote.WHITE_KEY_WIDTH
418
+ height = FieldNote.WHITE_KEY_HEIGHT
419
+ fill = FieldNote.WHITE_KEY_COLOR
420
+ stroke = (this.sourceBlock_!.getParent() as Blockly.BlockSvg).getColourTertiary()
421
+ group = whiteKeyGroup
469
422
  }
470
423
  const attr = {
471
424
  d: this.getPianoKeyPath_(x, y, width, height),
472
425
  fill: fill,
473
426
  stroke: stroke,
474
- };
475
- x += xIncrement;
427
+ }
428
+ x += xIncrement
476
429
 
477
- const keySVG = Blockly.utils.dom.createSvgElement("path", attr, group);
430
+ const keySVG = Blockly.utils.dom.createSvgElement('path', attr, group)
478
431
 
479
432
  if (keySVGarray) {
480
- keySVGarray[i] = keySVG;
481
- keySVG.setAttribute("data-pitch", `${FieldNote.KEY_INFO[i].pitch}`);
482
- keySVG.setAttribute("data-name", `${FieldNote.KEY_INFO[i].name}`);
483
- keySVG.setAttribute("data-isBlack", `${FieldNote.KEY_INFO[i].isBlack}`);
484
-
485
- this.mouseDownWrappers_[i] = Blockly.browserEvents.bind(
486
- keySVG,
487
- "mousedown",
488
- this,
489
- this.onMouseDownOnKey_
490
- );
491
- this.mouseEnterWrappers_[i] = Blockly.browserEvents.bind(
492
- keySVG,
493
- "mouseenter",
494
- this,
495
- this.onMouseEnter_
496
- );
433
+ keySVGarray[i] = keySVG
434
+ keySVG.setAttribute('data-pitch', `${FieldNote.KEY_INFO[i].pitch}`)
435
+ keySVG.setAttribute('data-name', `${FieldNote.KEY_INFO[i].name}`)
436
+ keySVG.setAttribute('data-isBlack', `${FieldNote.KEY_INFO[i].isBlack}`)
437
+
438
+ this.mouseDownWrappers_[i] = Blockly.browserEvents.bind(keySVG, 'mousedown', this, this.onMouseDownOnKey_)
439
+ this.mouseEnterWrappers_[i] = Blockly.browserEvents.bind(keySVG, 'mouseenter', this, this.onMouseEnter_)
497
440
  }
498
441
  }
499
442
  }
@@ -501,217 +444,184 @@ export class FieldNote extends Blockly.FieldTextInput {
501
444
  /**
502
445
  * Construct the SVG path string for a piano key shape: a rectangle with rounded
503
446
  * corners at the bottom.
504
- *
505
447
  * @param x the x position for the key.
506
448
  * @param y the y position for the key.
507
- * @param width the width of the key.
508
- * @param height the height of the key.
449
+ * @param width the horizontal extent of the key in pixels.
450
+ * @param height the vertical extent of the key in pixels.
509
451
  * @returns the SVG path as a string.
510
452
  */
511
- private getPianoKeyPath_(
512
- x: number,
513
- y: number,
514
- width: number,
515
- height: number
516
- ): string {
453
+ private getPianoKeyPath_(x: number, y: number, width: number, height: number): string {
517
454
  return (
518
- "M" +
455
+ 'M' +
519
456
  x +
520
- " " +
457
+ ' ' +
521
458
  y +
522
- " " +
523
- "L" +
459
+ ' ' +
460
+ 'L' +
524
461
  x +
525
- " " +
462
+ ' ' +
526
463
  (y + height - FieldNote.KEY_RADIUS) +
527
- " " +
528
- "Q" +
464
+ ' ' +
465
+ 'Q' +
529
466
  x +
530
- " " +
467
+ ' ' +
531
468
  (y + height) +
532
- " " +
469
+ ' ' +
533
470
  (x + FieldNote.KEY_RADIUS) +
534
- " " +
471
+ ' ' +
535
472
  (y + height) +
536
- " " +
537
- "L" +
473
+ ' ' +
474
+ 'L' +
538
475
  (x + width - FieldNote.KEY_RADIUS) +
539
- " " +
476
+ ' ' +
540
477
  (y + height) +
541
- " " +
542
- "Q" +
478
+ ' ' +
479
+ 'Q' +
543
480
  (x + width) +
544
- " " +
481
+ ' ' +
545
482
  (y + height) +
546
- " " +
483
+ ' ' +
547
484
  (x + width) +
548
- " " +
485
+ ' ' +
549
486
  (y + height - FieldNote.KEY_RADIUS) +
550
- " " +
551
- "L" +
487
+ ' ' +
488
+ 'L' +
552
489
  (x + width) +
553
- " " +
490
+ ' ' +
554
491
  y +
555
- " " +
556
- "L" +
492
+ ' ' +
493
+ 'L' +
557
494
  x +
558
- " " +
495
+ ' ' +
559
496
  y
560
- );
497
+ )
561
498
  }
562
499
 
563
500
  /**
564
501
  * Add a button for switching the displayed octave of the piano up or down.
565
- *
566
502
  * @param x The x position of the button.
567
503
  * @param flipped If true, the icon should be flipped.
568
504
  * @param svg The svg element to add the buttons to.
569
505
  * @returns A group containing the button SVG elements.
570
506
  */
571
- private addOctaveButton_(
572
- x: number,
573
- flipped: boolean,
574
- svg: SVGElement
575
- ): SVGElement {
576
- const group = Blockly.utils.dom.createSvgElement("g", {}, svg);
577
- const imageSize = FieldNote.OCTAVE_BUTTON_SIZE;
507
+ private addOctaveButton_(x: number, flipped: boolean, svg: SVGElement): SVGElement {
508
+ const group = Blockly.utils.dom.createSvgElement('g', {}, svg)
509
+ const imageSize = FieldNote.OCTAVE_BUTTON_SIZE
578
510
  const arrow = Blockly.utils.dom.createSvgElement(
579
- "image",
511
+ 'image',
580
512
  {
581
513
  width: imageSize,
582
514
  height: imageSize,
583
515
  x: x - FieldNote.INSET,
584
516
  y: -1 * FieldNote.INSET,
585
517
  },
586
- group
587
- );
518
+ group,
519
+ )
588
520
  arrow.setAttributeNS(
589
- "http://www.w3.org/1999/xlink",
590
- "xlink:href",
591
- Blockly.getMainWorkspace().options.pathToMedia + FieldNote.ARROW_SVG_PATH
592
- );
521
+ 'http://www.w3.org/1999/xlink',
522
+ 'xlink:href',
523
+ Blockly.getMainWorkspace().options.pathToMedia + FieldNote.ARROW_SVG_PATH,
524
+ )
593
525
  Blockly.utils.dom.createSvgElement(
594
- "line",
526
+ 'line',
595
527
  {
596
- stroke: (
597
- this.sourceBlock_.getParent() as Blockly.BlockSvg
598
- ).getColourTertiary(),
528
+ stroke: (this.sourceBlock_!.getParent() as Blockly.BlockSvg).getColourTertiary(),
599
529
  x1: x - FieldNote.INSET,
600
530
  y1: 0,
601
531
  x2: x - FieldNote.INSET,
602
532
  y2: FieldNote.TOP_MENU_HEIGHT - FieldNote.INSET,
603
533
  },
604
- group
605
- );
534
+ group,
535
+ )
606
536
  if (flipped) {
607
- const translateX =
608
- -1 * FieldNote.OCTAVE_BUTTON_SIZE + FieldNote.INSET * 2;
609
- group.setAttribute(
610
- "transform",
611
- "scale(-1, 1) " + "translate(" + translateX + ", 0)"
612
- );
537
+ const translateX = -1 * FieldNote.OCTAVE_BUTTON_SIZE + FieldNote.INSET * 2
538
+ group.setAttribute('transform', 'scale(-1, 1) ' + 'translate(' + translateX + ', 0)')
613
539
  }
614
- return group;
540
+ return group
615
541
  }
616
542
 
617
543
  /**
618
544
  * Add an SVG text label for display on the C keys of the piano.
619
- *
620
545
  * @param x The x position for the label.
621
546
  * @param svg The SVG element to add the label to.
622
547
  * @returns The SVG element containing the label.
623
548
  */
624
549
  private addCKeyLabel_(x: number, svg: SVGElement): SVGElement {
625
550
  return Blockly.utils.dom.createSvgElement(
626
- "text",
551
+ 'text',
627
552
  {
628
553
  x: x,
629
- y:
630
- FieldNote.TOP_MENU_HEIGHT +
631
- FieldNote.WHITE_KEY_HEIGHT -
632
- FieldNote.KEY_LABEL_PADDING,
633
- class: "scratchNotePickerKeyLabel",
634
- "text-anchor": "middle",
554
+ y: FieldNote.TOP_MENU_HEIGHT + FieldNote.WHITE_KEY_HEIGHT - FieldNote.KEY_LABEL_PADDING,
555
+ class: 'scratchNotePickerKeyLabel',
556
+ 'text-anchor': 'middle',
635
557
  },
636
- svg
637
- );
558
+ svg,
559
+ )
638
560
  }
639
561
 
640
562
  /**
641
563
  * Set the visibility of the C key labels.
642
- *
643
564
  * @param visible If true, set labels to be visible.
644
565
  */
645
566
  private setCKeyLabelsVisible_(visible: boolean) {
646
- if (visible) {
647
- this.fadeSvgToOpacity_(this.lowCText_, 1);
648
- this.fadeSvgToOpacity_(this.highCText_, 1);
649
- } else {
650
- this.fadeSvgToOpacity_(this.lowCText_, 0);
651
- this.fadeSvgToOpacity_(this.highCText_, 0);
567
+ if (!this.lowCText_ || !this.highCText_) {
568
+ console.warn('FieldNote.setCKeyLabelsVisible_: C-key label DOM is not fully initialized')
569
+ return
652
570
  }
571
+ const opacity = visible ? 1 : 0
572
+ this.fadeSvgToOpacity_(this.lowCText_, opacity)
573
+ this.fadeSvgToOpacity_(this.highCText_, opacity)
653
574
  }
654
575
 
655
576
  /**
656
577
  * Animate an SVG to fade it in or out to a target opacity.
657
- *
658
578
  * @param svg The SVG element to apply the fade to.
659
579
  * @param opacity The target opacity.
660
580
  */
661
581
  private fadeSvgToOpacity_(svg: SVGElement, opacity: number) {
662
- svg.setAttribute(
663
- "style",
664
- "opacity: " + opacity + "; transition: opacity 0.1s;"
665
- );
582
+ svg.setAttribute('style', 'opacity: ' + opacity + '; transition: opacity 0.1s;')
666
583
  }
667
584
 
668
585
  /**
669
586
  * Handle the mouse down event on a piano key.
670
- *
671
587
  * @param e Mouse down event.
672
588
  */
673
589
  private onMouseDownOnKey_(e: PointerEvent) {
674
- this.mouseIsDown_ = true;
675
- this.mouseUpWrapper_ = Blockly.browserEvents.bind(
676
- document.body,
677
- "mouseup",
678
- this,
679
- this.onMouseUp_
680
- );
681
- this.selectNoteWithMouseEvent_(e);
590
+ this.mouseIsDown_ = true
591
+ this.mouseUpWrapper_ = Blockly.browserEvents.bind(document.body, 'mouseup', this, this.onMouseUp_)
592
+ this.selectNoteWithMouseEvent_(e)
682
593
  }
683
594
 
684
595
  /**
685
596
  * Handle the mouse up event following a mouse down on a piano key.
686
597
  */
687
598
  private onMouseUp_() {
688
- this.mouseIsDown_ = false;
689
- Blockly.browserEvents.unbind(this.mouseUpWrapper_);
690
- this.mouseUpWrapper_ = null;
599
+ this.mouseIsDown_ = false
600
+ if (this.mouseUpWrapper_) {
601
+ Blockly.browserEvents.unbind(this.mouseUpWrapper_)
602
+ this.mouseUpWrapper_ = null
603
+ }
691
604
  }
692
605
 
693
606
  /**
694
607
  * Handle the event when the mouse enters a piano key.
695
- *
696
608
  * @param e Mouse enter event.
697
609
  */
698
610
  private onMouseEnter_(e: PointerEvent) {
699
611
  if (this.mouseIsDown_) {
700
- this.selectNoteWithMouseEvent_(e);
612
+ this.selectNoteWithMouseEvent_(e)
701
613
  }
702
614
  }
703
615
 
704
616
  /**
705
617
  * Use the data in a mouse event to select a new note, and play it.
706
- *
707
618
  * @param e Mouse event.
708
619
  */
709
620
  private selectNoteWithMouseEvent_(e: PointerEvent) {
710
621
  const newNoteNum =
711
- Number((e.target as HTMLElement).getAttribute("data-pitch")) +
712
- this.displayedOctave_ * 12;
713
- this.setEditorValue_(newNoteNum);
714
- this.playNoteInternal_();
622
+ Number((e.target as HTMLElement).getAttribute('data-pitch')) + (this.displayedOctave_ ?? 0) * 12
623
+ this.setEditorValue_(newNoteNum)
624
+ this.playNoteInternal_()
715
625
  }
716
626
 
717
627
  /**
@@ -719,94 +629,86 @@ export class FieldNote extends Blockly.FieldTextInput {
719
629
  */
720
630
  private playNoteInternal_() {
721
631
  if (FieldNote.playNote_) {
722
- FieldNote.playNote_(Number(this.getValue()), "Music");
632
+ FieldNote.playNote_(Number(this.getValue()!), 'Music')
723
633
  }
724
634
  }
725
635
 
726
636
  /**
727
637
  * Function to play a musical note corresponding to the key selected.
728
638
  * Overridden externally.
729
- *
730
639
  * @param noteNum the MIDI note number to play.
731
640
  * @param id An id to select a scratch extension to play the note.
732
641
  */
733
642
  static playNote_ = function (noteNum: number, id: string) {
734
- return;
735
- };
643
+ return
644
+ }
736
645
 
737
646
  /**
738
647
  * Change the selected note by a number of octaves, and start the animation.
739
- *
740
648
  * @param octaves The number of octaves to change by.
741
649
  */
742
650
  private changeOctaveBy_(octaves: number) {
743
- this.displayedOctave_ += octaves;
651
+ this.displayedOctave_ = (this.displayedOctave_ ?? 0) + octaves
744
652
  if (this.displayedOctave_ < 0) {
745
- this.displayedOctave_ = 0;
746
- return;
653
+ this.displayedOctave_ = 0
654
+ return
747
655
  }
748
- const maxOctave = Math.floor(FieldNote.MAX_NOTE / 12);
656
+ const maxOctave = Math.floor(FieldNote.MAX_NOTE / 12)
749
657
  if (this.displayedOctave_ > maxOctave) {
750
- this.displayedOctave_ = maxOctave;
751
- return;
658
+ this.displayedOctave_ = maxOctave
659
+ return
752
660
  }
753
661
 
754
- const newNote = Number(this.getText()) + octaves * 12;
755
- this.setEditorValue_(newNote);
662
+ const newNote = Number(this.getText()) + octaves * 12
663
+ this.setEditorValue_(newNote)
756
664
 
757
- this.animationTarget_ = this.fieldEditorWidth_ * octaves * -1;
758
- this.animationPos_ = 0;
759
- this.stepOctaveAnimation_();
760
- this.setCKeyLabelsVisible_(false);
665
+ this.animationTarget_ = this.fieldEditorWidth_ * octaves * -1
666
+ this.animationPos_ = 0
667
+ this.stepOctaveAnimation_()
668
+ this.setCKeyLabelsVisible_(false)
761
669
  }
762
670
 
763
671
  /**
764
672
  * Animate the piano up or down an octave by sliding it to the left or right.
765
673
  */
766
674
  private stepOctaveAnimation_() {
767
- const absDiff = Math.abs(this.animationPos_ - this.animationTarget_);
675
+ const absDiff = Math.abs(this.animationPos_ - this.animationTarget_)
768
676
  if (absDiff < 1) {
769
- this.pianoSVG_.setAttribute("transform", "translate(0, 0)");
770
- this.setCKeyLabelsVisible_(true);
771
- this.playNoteInternal_();
772
- return;
677
+ this.pianoSVG_?.setAttribute('transform', 'translate(0, 0)')
678
+ this.setCKeyLabelsVisible_(true)
679
+ this.playNoteInternal_()
680
+ return
773
681
  }
774
- this.animationPos_ +=
775
- (this.animationTarget_ - this.animationPos_) *
776
- FieldNote.ANIMATION_FRACTION;
777
- this.pianoSVG_.setAttribute(
778
- "transform",
779
- "translate(" + this.animationPos_ + ",0)"
780
- );
781
- requestAnimationFrame(this.stepOctaveAnimation_.bind(this));
682
+ this.animationPos_ += (this.animationTarget_ - this.animationPos_) * FieldNote.ANIMATION_FRACTION
683
+ this.pianoSVG_?.setAttribute('transform', 'translate(' + this.animationPos_ + ',0)')
684
+ requestAnimationFrame(this.stepOctaveAnimation_.bind(this))
782
685
  }
783
686
 
784
687
  doValueUpdate_(newValue: string) {
785
- super.doValueUpdate_(newValue);
688
+ super.doValueUpdate_(newValue)
786
689
 
787
690
  if (!this.textElement_) {
788
691
  // Not rendered yet.
789
- return;
692
+ return
790
693
  }
791
694
 
792
- this.updateSelection_();
695
+ this.updateSelection_()
793
696
  }
794
697
 
795
698
  /**
796
699
  * For a MIDI note number, find the index of the corresponding piano key.
797
- *
798
700
  * @param noteNum The note number.
799
701
  * @returns The index of the piano key.
800
702
  */
801
703
  private noteNumToKeyIndex_(noteNum: number): number {
802
- return Math.floor(noteNum) - this.displayedOctave_ * 12;
704
+ return Math.floor(noteNum) - (this.displayedOctave_ ?? 0) * 12
803
705
  }
804
706
 
805
707
  /**
806
708
  * Update the selected note and labels on the field.
807
709
  */
808
710
  private updateSelection_() {
809
- const noteNum = Number(this.getText());
711
+ const noteNum = Number(this.getText())
810
712
 
811
713
  // If the note is outside the currently displayed octave, update it
812
714
  if (
@@ -814,65 +716,65 @@ export class FieldNote extends Blockly.FieldTextInput {
814
716
  noteNum > this.displayedOctave_ * 12 + 12 ||
815
717
  noteNum < this.displayedOctave_ * 12
816
718
  ) {
817
- this.displayedOctave_ = Math.floor(noteNum / 12);
719
+ this.displayedOctave_ = Math.floor(noteNum / 12)
818
720
  }
819
721
 
820
- const index = this.noteNumToKeyIndex_(noteNum);
722
+ const index = this.noteNumToKeyIndex_(noteNum)
821
723
 
822
724
  // Clear the highlight on all keys
823
- this.keySVGs_.forEach(function (svg) {
824
- const isBlack = svg.getAttribute("data-isBlack");
825
- if (isBlack === "true") {
826
- svg.setAttribute("fill", FieldNote.BLACK_KEY_COLOR);
725
+ this.keySVGs_.forEach(svg => {
726
+ const isBlack = svg.getAttribute('data-isBlack')
727
+ if (isBlack === 'true') {
728
+ svg.setAttribute('fill', FieldNote.BLACK_KEY_COLOR)
827
729
  } else {
828
- svg.setAttribute("fill", FieldNote.WHITE_KEY_COLOR);
730
+ svg.setAttribute('fill', FieldNote.WHITE_KEY_COLOR)
829
731
  }
830
- });
732
+ })
831
733
  // Set the highlight on the selected key
832
734
  if (this.keySVGs_[index]) {
833
- this.keySVGs_[index].setAttribute("fill", FieldNote.KEY_SELECTED_COLOR);
735
+ this.keySVGs_[index].setAttribute('fill', FieldNote.KEY_SELECTED_COLOR)
834
736
  // Update the note name text
835
- const noteName = FieldNote.KEY_INFO[index].name;
836
- this.noteNameText_.textContent =
837
- noteName + " (" + Math.floor(noteNum) + ")";
737
+ const noteName = FieldNote.KEY_INFO[index].name
738
+ if (this.noteNameText_) {
739
+ this.noteNameText_.textContent = noteName + ' (' + Math.floor(noteNum) + ')'
740
+ }
838
741
  // Update the low and high C note names
839
- const lowCNum = this.displayedOctave_ * 12;
840
- this.lowCText_.textContent = "C(" + lowCNum + ")";
841
- this.highCText_.textContent = "C(" + (lowCNum + 12) + ")";
742
+ const lowCNum = (this.displayedOctave_ ?? 0) * 12
743
+ if (this.lowCText_) this.lowCText_.textContent = 'C(' + lowCNum + ')'
744
+ if (this.highCText_) this.highCText_.textContent = 'C(' + (lowCNum + 12) + ')'
842
745
  }
843
746
  }
844
747
 
845
748
  /**
846
749
  * Ensure that only a valid MIDI note number may be entered.
847
- *
848
750
  * @param text The user's text.
849
751
  * @returns A string representing a valid note number, or null if invalid.
850
752
  */
851
753
  doClassValidation_(text: string): string | null {
852
754
  if (text === null) {
853
- return null;
755
+ return null
854
756
  }
855
- var n = parseFloat(text || "0");
757
+ let n = parseFloat(text || '0')
856
758
  if (isNaN(n)) {
857
- return null;
759
+ return null
858
760
  }
859
761
  if (n < 0) {
860
- n = 0;
762
+ n = 0
861
763
  }
862
764
  if (n > FieldNote.MAX_NOTE) {
863
- n = FieldNote.MAX_NOTE;
765
+ n = FieldNote.MAX_NOTE
864
766
  }
865
- return String(n);
767
+ return String(n)
866
768
  }
867
769
  }
868
770
 
869
771
  interface FieldNoteJsonConfig extends Blockly.FieldTextInputFromJsonConfig {
870
- note: string;
772
+ note: string
871
773
  }
872
774
 
873
775
  /**
874
776
  * Register the field and any dependencies.
875
777
  */
876
778
  export function registerFieldNote() {
877
- Blockly.fieldRegistry.register("field_note", FieldNote);
779
+ Blockly.fieldRegistry.register('field_note', FieldNote)
878
780
  }