blockly 7.20211209.2 → 8.0.0

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 (262) hide show
  1. package/blockly.d.ts +18963 -18432
  2. package/blockly.min.js +852 -844
  3. package/blockly_compressed.js +669 -664
  4. package/blockly_compressed.js.map +1 -1
  5. package/blocks/blocks.js +47 -0
  6. package/blocks/colour.js +13 -3
  7. package/blocks/lists.js +22 -13
  8. package/blocks/logic.js +13 -3
  9. package/blocks/loops.js +24 -11
  10. package/blocks/math.js +12 -3
  11. package/blocks/procedures.js +41 -27
  12. package/blocks/text.js +22 -13
  13. package/blocks/variables.js +14 -3
  14. package/blocks/variables_dynamic.js +13 -3
  15. package/blocks_compressed.js +146 -141
  16. package/blocks_compressed.js.map +1 -1
  17. package/core/block.js +1869 -1814
  18. package/core/block_drag_surface.js +201 -200
  19. package/core/block_dragger.js +377 -373
  20. package/core/block_svg.js +1593 -1479
  21. package/core/blockly.js +8 -22
  22. package/core/blocks.js +9 -2
  23. package/core/browser_events.js +22 -5
  24. package/core/bubble.js +841 -797
  25. package/core/bubble_dragger.js +213 -206
  26. package/core/bump_objects.js +2 -2
  27. package/core/clipboard.js +9 -9
  28. package/core/comment.js +353 -332
  29. package/core/common.js +46 -17
  30. package/core/component_manager.js +181 -174
  31. package/core/config.js +87 -0
  32. package/core/connection.js +595 -584
  33. package/core/connection_checker.js +242 -244
  34. package/core/connection_db.js +235 -230
  35. package/core/contextmenu.js +9 -6
  36. package/core/contextmenu_items.js +1 -2
  37. package/core/contextmenu_registry.js +93 -89
  38. package/core/css.js +474 -474
  39. package/core/delete_area.js +45 -42
  40. package/core/drag_target.js +57 -56
  41. package/core/dropdowndiv.js +153 -163
  42. package/core/events/events.js +2 -2
  43. package/core/events/events_abstract.js +89 -77
  44. package/core/events/events_block_base.js +37 -36
  45. package/core/events/events_block_change.js +130 -124
  46. package/core/events/events_block_create.js +73 -71
  47. package/core/events/events_block_delete.js +84 -82
  48. package/core/events/events_block_drag.js +50 -49
  49. package/core/events/events_block_move.js +147 -140
  50. package/core/events/events_bubble_open.js +51 -50
  51. package/core/events/events_click.js +48 -44
  52. package/core/events/events_comment_base.js +72 -69
  53. package/core/events/events_comment_change.js +63 -61
  54. package/core/events/events_comment_create.js +44 -42
  55. package/core/events/events_comment_delete.js +42 -40
  56. package/core/events/events_comment_move.js +106 -104
  57. package/core/events/events_marker_move.js +65 -64
  58. package/core/events/events_selected.js +46 -45
  59. package/core/events/events_theme_change.js +36 -35
  60. package/core/events/events_toolbox_item_select.js +46 -45
  61. package/core/events/events_trashcan_open.js +37 -36
  62. package/core/events/events_ui.js +47 -46
  63. package/core/events/events_ui_base.js +30 -29
  64. package/core/events/events_var_base.js +37 -36
  65. package/core/events/events_var_create.js +50 -48
  66. package/core/events/events_var_delete.js +50 -48
  67. package/core/events/events_var_rename.js +51 -49
  68. package/core/events/events_viewport.js +66 -65
  69. package/core/events/utils.js +29 -14
  70. package/core/events/workspace_events.js +49 -55
  71. package/core/extensions.js +4 -3
  72. package/core/field.js +1061 -997
  73. package/core/field_angle.js +462 -442
  74. package/core/field_checkbox.js +194 -182
  75. package/core/field_colour.js +519 -505
  76. package/core/field_dropdown.js +617 -598
  77. package/core/field_image.js +229 -220
  78. package/core/field_label.js +102 -91
  79. package/core/field_label_serializable.js +42 -41
  80. package/core/field_multilineinput.js +372 -358
  81. package/core/field_number.js +272 -253
  82. package/core/field_textinput.js +499 -467
  83. package/core/field_variable.js +458 -420
  84. package/core/flyout_base.js +1005 -952
  85. package/core/flyout_button.js +277 -260
  86. package/core/flyout_horizontal.js +304 -302
  87. package/core/flyout_metrics_manager.js +64 -64
  88. package/core/flyout_vertical.js +306 -300
  89. package/core/generator.js +459 -446
  90. package/core/gesture.js +829 -813
  91. package/core/grid.js +166 -163
  92. package/core/icon.js +168 -159
  93. package/core/inject.js +7 -5
  94. package/core/input.js +257 -248
  95. package/core/insertion_marker_manager.js +655 -624
  96. package/core/internal_constants.js +0 -129
  97. package/core/keyboard_nav/ast_node.js +605 -596
  98. package/core/keyboard_nav/basic_cursor.js +166 -165
  99. package/core/keyboard_nav/cursor.js +99 -97
  100. package/core/keyboard_nav/marker.js +83 -79
  101. package/core/keyboard_nav/tab_navigate_cursor.js +18 -23
  102. package/core/marker_manager.js +153 -141
  103. package/core/menu.js +377 -372
  104. package/core/menuitem.js +223 -217
  105. package/core/metrics_manager.js +403 -390
  106. package/core/mutator.js +468 -437
  107. package/core/names.js +229 -188
  108. package/core/options.js +290 -284
  109. package/core/procedures.js +29 -17
  110. package/core/registry.js +19 -16
  111. package/core/rendered_connection.js +482 -463
  112. package/core/renderers/common/block_rendering.js +9 -3
  113. package/core/renderers/common/constants.js +1119 -1112
  114. package/core/renderers/common/debug.js +14 -0
  115. package/core/renderers/common/debugger.js +338 -316
  116. package/core/renderers/common/drawer.js +380 -370
  117. package/core/renderers/common/i_path_object.js +2 -2
  118. package/core/renderers/common/info.js +626 -618
  119. package/core/renderers/common/marker_svg.js +579 -541
  120. package/core/renderers/common/path_object.js +203 -200
  121. package/core/renderers/common/renderer.js +220 -218
  122. package/core/renderers/geras/constants.js +36 -36
  123. package/core/renderers/geras/drawer.js +155 -147
  124. package/core/renderers/geras/highlight_constants.js +244 -238
  125. package/core/renderers/geras/highlighter.js +231 -179
  126. package/core/renderers/geras/info.js +392 -369
  127. package/core/renderers/geras/measurables/inline_input.js +25 -19
  128. package/core/renderers/geras/measurables/statement_input.js +23 -17
  129. package/core/renderers/geras/path_object.js +106 -121
  130. package/core/renderers/geras/renderer.js +96 -98
  131. package/core/renderers/measurables/base.js +30 -18
  132. package/core/renderers/measurables/bottom_row.js +83 -80
  133. package/core/renderers/measurables/connection.js +22 -15
  134. package/core/renderers/measurables/external_value_input.js +35 -22
  135. package/core/renderers/measurables/field.js +35 -20
  136. package/core/renderers/measurables/hat.js +18 -13
  137. package/core/renderers/measurables/icon.js +24 -17
  138. package/core/renderers/measurables/in_row_spacer.js +15 -13
  139. package/core/renderers/measurables/inline_input.js +43 -33
  140. package/core/renderers/measurables/input_connection.js +41 -28
  141. package/core/renderers/measurables/input_row.js +50 -44
  142. package/core/renderers/measurables/jagged_edge.js +14 -12
  143. package/core/renderers/measurables/next_connection.js +16 -14
  144. package/core/renderers/measurables/output_connection.js +26 -20
  145. package/core/renderers/measurables/previous_connection.js +16 -15
  146. package/core/renderers/measurables/round_corner.js +20 -18
  147. package/core/renderers/measurables/row.js +184 -168
  148. package/core/renderers/measurables/spacer_row.js +38 -23
  149. package/core/renderers/measurables/square_corner.js +18 -16
  150. package/core/renderers/measurables/statement_input.js +23 -20
  151. package/core/renderers/measurables/top_row.js +88 -85
  152. package/core/renderers/minimalist/constants.js +8 -7
  153. package/core/renderers/minimalist/drawer.js +11 -10
  154. package/core/renderers/minimalist/info.js +18 -18
  155. package/core/renderers/minimalist/renderer.js +40 -39
  156. package/core/renderers/thrasos/info.js +258 -248
  157. package/core/renderers/thrasos/renderer.js +20 -20
  158. package/core/renderers/zelos/constants.js +898 -873
  159. package/core/renderers/zelos/drawer.js +186 -169
  160. package/core/renderers/zelos/info.js +502 -479
  161. package/core/renderers/zelos/marker_svg.js +129 -115
  162. package/core/renderers/zelos/measurables/bottom_row.js +31 -30
  163. package/core/renderers/zelos/measurables/inputs.js +22 -21
  164. package/core/renderers/zelos/measurables/row_elements.js +14 -13
  165. package/core/renderers/zelos/measurables/top_row.js +34 -33
  166. package/core/renderers/zelos/path_object.js +181 -180
  167. package/core/renderers/zelos/renderer.js +91 -92
  168. package/core/scrollbar.js +759 -713
  169. package/core/scrollbar_pair.js +250 -245
  170. package/core/serialization/blocks.js +19 -9
  171. package/core/serialization/workspaces.js +3 -2
  172. package/core/shortcut_registry.js +286 -277
  173. package/core/sprites.js +31 -0
  174. package/core/theme.js +135 -141
  175. package/core/theme_manager.js +147 -143
  176. package/core/toolbox/category.js +602 -576
  177. package/core/toolbox/collapsible_category.js +226 -227
  178. package/core/toolbox/separator.js +70 -61
  179. package/core/toolbox/toolbox.js +934 -927
  180. package/core/toolbox/toolbox_item.js +115 -99
  181. package/core/tooltip.js +108 -35
  182. package/core/touch.js +8 -3
  183. package/core/touch_gesture.js +254 -251
  184. package/core/trashcan.js +606 -595
  185. package/core/utils/coordinate.js +97 -95
  186. package/core/utils/dom.js +2 -2
  187. package/core/utils/global.js +2 -0
  188. package/core/utils/rect.js +41 -37
  189. package/core/utils/sentinel.js +25 -0
  190. package/core/utils/size.js +30 -27
  191. package/core/utils/svg.js +18 -16
  192. package/core/variable_map.js +325 -341
  193. package/core/variable_model.js +55 -54
  194. package/core/variables.js +9 -2
  195. package/core/variables_dynamic.js +3 -1
  196. package/core/warning.js +126 -120
  197. package/core/widgetdiv.js +4 -4
  198. package/core/workspace.js +685 -664
  199. package/core/workspace_audio.js +124 -118
  200. package/core/workspace_comment.js +308 -298
  201. package/core/workspace_comment_svg.js +1029 -951
  202. package/core/workspace_drag_surface_svg.js +147 -140
  203. package/core/workspace_dragger.js +70 -71
  204. package/core/workspace_svg.js +2322 -2297
  205. package/core/xml.js +30 -20
  206. package/core/zoom_controls.js +431 -439
  207. package/dart_compressed.js +40 -43
  208. package/dart_compressed.js.map +1 -1
  209. package/generators/dart/colour.js +56 -64
  210. package/generators/dart/lists.js +61 -50
  211. package/generators/dart/math.js +160 -148
  212. package/generators/dart/text.js +83 -61
  213. package/generators/javascript/colour.js +37 -34
  214. package/generators/javascript/lists.js +50 -43
  215. package/generators/javascript/math.js +123 -139
  216. package/generators/javascript/text.js +67 -81
  217. package/generators/lua/colour.js +25 -23
  218. package/generators/lua/lists.js +97 -69
  219. package/generators/lua/logic.js +1 -2
  220. package/generators/lua/math.js +182 -144
  221. package/generators/lua/text.js +116 -99
  222. package/generators/php/colour.js +38 -32
  223. package/generators/php/lists.js +109 -89
  224. package/generators/php/math.js +90 -81
  225. package/generators/php/text.js +63 -61
  226. package/generators/python/colour.js +18 -18
  227. package/generators/python/lists.js +38 -30
  228. package/generators/python/loops.js +12 -8
  229. package/generators/python/math.js +104 -106
  230. package/generators/python/text.js +34 -30
  231. package/javascript_compressed.js +37 -39
  232. package/javascript_compressed.js.map +1 -1
  233. package/lua_compressed.js +39 -42
  234. package/lua_compressed.js.map +1 -1
  235. package/msg/az.js +2 -2
  236. package/msg/be.js +4 -4
  237. package/msg/cs.js +15 -15
  238. package/msg/de.js +1 -1
  239. package/msg/diq.js +1 -1
  240. package/msg/eo.js +1 -1
  241. package/msg/es.js +1 -1
  242. package/msg/fa.js +1 -1
  243. package/msg/fr.js +4 -4
  244. package/msg/he.js +1 -1
  245. package/msg/hr.js +2 -2
  246. package/msg/hy.js +2 -2
  247. package/msg/id.js +12 -12
  248. package/msg/inh.js +14 -14
  249. package/msg/ja.js +7 -7
  250. package/msg/lv.js +29 -29
  251. package/msg/pa.js +3 -3
  252. package/msg/smn.js +436 -0
  253. package/msg/te.js +1 -1
  254. package/msg/yue.js +1 -1
  255. package/msg/zh-hans.js +3 -3
  256. package/msg/zh-hant.js +3 -3
  257. package/package.json +7 -6
  258. package/php_compressed.js +38 -42
  259. package/php_compressed.js.map +1 -1
  260. package/python_compressed.js +26 -25
  261. package/python_compressed.js.map +1 -1
  262. package/blocks/all.js +0 -23
@@ -31,7 +31,7 @@ JavaScript['math_arithmetic'] = function(block) {
31
31
  'MINUS': [' - ', JavaScript.ORDER_SUBTRACTION],
32
32
  'MULTIPLY': [' * ', JavaScript.ORDER_MULTIPLICATION],
33
33
  'DIVIDE': [' / ', JavaScript.ORDER_DIVISION],
34
- 'POWER': [null, JavaScript.ORDER_NONE] // Handle power separately.
34
+ 'POWER': [null, JavaScript.ORDER_NONE], // Handle power separately.
35
35
  };
36
36
  const tuple = OPERATORS[block.getFieldValue('OP')];
37
37
  const operator = tuple[0];
@@ -137,11 +137,10 @@ JavaScript['math_constant'] = function(block) {
137
137
  const CONSTANTS = {
138
138
  'PI': ['Math.PI', JavaScript.ORDER_MEMBER],
139
139
  'E': ['Math.E', JavaScript.ORDER_MEMBER],
140
- 'GOLDEN_RATIO':
141
- ['(1 + Math.sqrt(5)) / 2', JavaScript.ORDER_DIVISION],
140
+ 'GOLDEN_RATIO': ['(1 + Math.sqrt(5)) / 2', JavaScript.ORDER_DIVISION],
142
141
  'SQRT2': ['Math.SQRT2', JavaScript.ORDER_MEMBER],
143
142
  'SQRT1_2': ['Math.SQRT1_2', JavaScript.ORDER_MEMBER],
144
- 'INFINITY': ['Infinity', JavaScript.ORDER_ATOMIC]
143
+ 'INFINITY': ['Infinity', JavaScript.ORDER_ATOMIC],
145
144
  };
146
145
  return CONSTANTS[block.getFieldValue('CONSTANT')];
147
146
  };
@@ -149,60 +148,54 @@ JavaScript['math_constant'] = function(block) {
149
148
  JavaScript['math_number_property'] = function(block) {
150
149
  // Check if a number is even, odd, prime, whole, positive, or negative
151
150
  // or if it is divisible by certain number. Returns true or false.
152
- const number_to_check = JavaScript.valueToCode(block, 'NUMBER_TO_CHECK',
153
- JavaScript.ORDER_MODULUS) || '0';
154
- const dropdown_property = block.getFieldValue('PROPERTY');
151
+ const PROPERTIES = {
152
+ 'EVEN': [' % 2 === 0', JavaScript.ORDER_MODULUS, JavaScript.ORDER_EQUALITY],
153
+ 'ODD': [' % 2 === 1', JavaScript.ORDER_MODULUS, JavaScript.ORDER_EQUALITY],
154
+ 'WHOLE': [' % 1 === 0', JavaScript.ORDER_MODULUS,
155
+ JavaScript.ORDER_EQUALITY],
156
+ 'POSITIVE': [' > 0', JavaScript.ORDER_RELATIONAL,
157
+ JavaScript.ORDER_RELATIONAL],
158
+ 'NEGATIVE': [' < 0', JavaScript.ORDER_RELATIONAL,
159
+ JavaScript.ORDER_RELATIONAL],
160
+ 'DIVISIBLE_BY': [null, JavaScript.ORDER_MODULUS, JavaScript.ORDER_EQUALITY],
161
+ 'PRIME': [null, JavaScript.ORDER_NONE, JavaScript.ORDER_FUNCTION_CALL],
162
+ };
163
+ const dropdownProperty = block.getFieldValue('PROPERTY');
164
+ const [suffix, inputOrder, outputOrder] = PROPERTIES[dropdownProperty];
165
+ const numberToCheck = JavaScript.valueToCode(block, 'NUMBER_TO_CHECK',
166
+ inputOrder) || '0';
155
167
  let code;
156
- if (dropdown_property === 'PRIME') {
168
+ if (dropdownProperty === 'PRIME') {
157
169
  // Prime is a special case as it is not a one-liner test.
158
- const functionName = JavaScript.provideFunction_(
159
- 'mathIsPrime',
160
- ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(n) {',
161
- ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods',
162
- ' if (n == 2 || n == 3) {',
163
- ' return true;',
164
- ' }',
165
- ' // False if n is NaN, negative, is 1, or not whole.',
166
- ' // And false if n is divisible by 2 or 3.',
167
- ' if (isNaN(n) || n <= 1 || n % 1 !== 0 || n % 2 === 0 ||' +
168
- ' n % 3 === 0) {',
169
- ' return false;',
170
- ' }',
171
- ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).',
172
- ' for (var x = 6; x <= Math.sqrt(n) + 1; x += 6) {',
173
- ' if (n % (x - 1) === 0 || n % (x + 1) === 0) {',
174
- ' return false;',
175
- ' }',
176
- ' }',
177
- ' return true;',
178
- '}']);
179
- code = functionName + '(' + number_to_check + ')';
180
- return [code, JavaScript.ORDER_FUNCTION_CALL];
170
+ const functionName = JavaScript.provideFunction_('mathIsPrime', `
171
+ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(n) {
172
+ // https://en.wikipedia.org/wiki/Primality_test#Naive_methods
173
+ if (n == 2 || n == 3) {
174
+ return true;
181
175
  }
182
- switch (dropdown_property) {
183
- case 'EVEN':
184
- code = number_to_check + ' % 2 === 0';
185
- break;
186
- case 'ODD':
187
- code = number_to_check + ' % 2 === 1';
188
- break;
189
- case 'WHOLE':
190
- code = number_to_check + ' % 1 === 0';
191
- break;
192
- case 'POSITIVE':
193
- code = number_to_check + ' > 0';
194
- break;
195
- case 'NEGATIVE':
196
- code = number_to_check + ' < 0';
197
- break;
198
- case 'DIVISIBLE_BY': {
199
- const divisor = JavaScript.valueToCode(block, 'DIVISOR',
200
- JavaScript.ORDER_MODULUS) || '0';
201
- code = number_to_check + ' % ' + divisor + ' === 0';
202
- break;
176
+ // False if n is NaN, negative, is 1, or not whole.
177
+ // And false if n is divisible by 2 or 3.
178
+ if (isNaN(n) || n <= 1 || n % 1 !== 0 || n % 2 === 0 || n % 3 === 0) {
179
+ return false;
180
+ }
181
+ // Check all the numbers of form 6k +/- 1, up to sqrt(n).
182
+ for (var x = 6; x <= Math.sqrt(n) + 1; x += 6) {
183
+ if (n % (x - 1) === 0 || n % (x + 1) === 0) {
184
+ return false;
203
185
  }
204
186
  }
205
- return [code, JavaScript.ORDER_EQUALITY];
187
+ return true;
188
+ }
189
+ `);
190
+ code = functionName + '(' + numberToCheck + ')';
191
+ } else if (dropdownProperty === 'DIVISIBLE_BY') {
192
+ const divisor = JavaScript.valueToCode(block, 'DIVISOR',
193
+ JavaScript.ORDER_MODULUS) || '0';
194
+ code = numberToCheck + ' % ' + divisor + ' === 0';
195
+ } else {
196
+ code = numberToCheck + suffix;
197
+ }
198
+ return [code, outputOrder];
206
199
  };
207
200
 
208
201
  JavaScript['math_change'] = function(block) {
@@ -243,13 +236,11 @@ JavaScript['math_on_list'] = function(block) {
243
236
  break;
244
237
  case 'AVERAGE': {
245
238
  // mathMean([null,null,1,3]) === 2.0.
246
- const functionName = JavaScript.provideFunction_(
247
- 'mathMean',
248
- ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ +
249
- '(myList) {',
250
- ' return myList.reduce(function(x, y) {return x + y;}) / ' +
251
- 'myList.length;',
252
- '}']);
239
+ const functionName = JavaScript.provideFunction_('mathMean', `
240
+ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(myList) {
241
+ return myList.reduce(function(x, y) {return x + y;}) / myList.length;
242
+ }
243
+ `);
253
244
  list = JavaScript.valueToCode(block, 'LIST',
254
245
  JavaScript.ORDER_NONE) || '[]';
255
246
  code = functionName + '(' + list + ')';
@@ -257,21 +248,18 @@ JavaScript['math_on_list'] = function(block) {
257
248
  }
258
249
  case 'MEDIAN': {
259
250
  // mathMedian([null,null,1,3]) === 2.0.
260
- const functionName = JavaScript.provideFunction_(
261
- 'mathMedian',
262
- ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ +
263
- '(myList) {',
264
- ' var localList = myList.filter(function (x) ' +
265
- '{return typeof x === \'number\';});',
266
- ' if (!localList.length) return null;',
267
- ' localList.sort(function(a, b) {return b - a;});',
268
- ' if (localList.length % 2 === 0) {',
269
- ' return (localList[localList.length / 2 - 1] + ' +
270
- 'localList[localList.length / 2]) / 2;',
271
- ' } else {',
272
- ' return localList[(localList.length - 1) / 2];',
273
- ' }',
274
- '}']);
251
+ const functionName = JavaScript.provideFunction_('mathMedian', `
252
+ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(myList) {
253
+ var localList = myList.filter(function (x) {return typeof x === \'number\';});
254
+ if (!localList.length) return null;
255
+ localList.sort(function(a, b) {return b - a;});
256
+ if (localList.length % 2 === 0) {
257
+ return (localList[localList.length / 2 - 1] + localList[localList.length / 2]) / 2;
258
+ } else {
259
+ return localList[(localList.length - 1) / 2];
260
+ }
261
+ }
262
+ `);
275
263
  list = JavaScript.valueToCode(block, 'LIST',
276
264
  JavaScript.ORDER_NONE) || '[]';
277
265
  code = functionName + '(' + list + ')';
@@ -281,70 +269,67 @@ JavaScript['math_on_list'] = function(block) {
281
269
  // As a list of numbers can contain more than one mode,
282
270
  // the returned result is provided as an array.
283
271
  // Mode of [3, 'x', 'x', 1, 1, 2, '3'] -> ['x', 1].
284
- const functionName = JavaScript.provideFunction_(
285
- 'mathModes',
286
- ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ +
287
- '(values) {',
288
- ' var modes = [];',
289
- ' var counts = [];',
290
- ' var maxCount = 0;',
291
- ' for (var i = 0; i < values.length; i++) {',
292
- ' var value = values[i];',
293
- ' var found = false;',
294
- ' var thisCount;',
295
- ' for (var j = 0; j < counts.length; j++) {',
296
- ' if (counts[j][0] === value) {',
297
- ' thisCount = ++counts[j][1];',
298
- ' found = true;',
299
- ' break;',
300
- ' }',
301
- ' }',
302
- ' if (!found) {',
303
- ' counts.push([value, 1]);',
304
- ' thisCount = 1;',
305
- ' }',
306
- ' maxCount = Math.max(thisCount, maxCount);',
307
- ' }',
308
- ' for (var j = 0; j < counts.length; j++) {',
309
- ' if (counts[j][1] === maxCount) {',
310
- ' modes.push(counts[j][0]);',
311
- ' }',
312
- ' }',
313
- ' return modes;',
314
- '}']);
272
+ const functionName = JavaScript.provideFunction_('mathModes', `
273
+ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(values) {
274
+ var modes = [];
275
+ var counts = [];
276
+ var maxCount = 0;
277
+ for (var i = 0; i < values.length; i++) {
278
+ var value = values[i];
279
+ var found = false;
280
+ var thisCount;
281
+ for (var j = 0; j < counts.length; j++) {
282
+ if (counts[j][0] === value) {
283
+ thisCount = ++counts[j][1];
284
+ found = true;
285
+ break;
286
+ }
287
+ }
288
+ if (!found) {
289
+ counts.push([value, 1]);
290
+ thisCount = 1;
291
+ }
292
+ maxCount = Math.max(thisCount, maxCount);
293
+ }
294
+ for (var j = 0; j < counts.length; j++) {
295
+ if (counts[j][1] === maxCount) {
296
+ modes.push(counts[j][0]);
297
+ }
298
+ }
299
+ return modes;
300
+ }
301
+ `);
315
302
  list = JavaScript.valueToCode(block, 'LIST',
316
303
  JavaScript.ORDER_NONE) || '[]';
317
304
  code = functionName + '(' + list + ')';
318
305
  break;
319
306
  }
320
307
  case 'STD_DEV': {
321
- const functionName = JavaScript.provideFunction_(
322
- 'mathStandardDeviation',
323
- ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ +
324
- '(numbers) {',
325
- ' var n = numbers.length;',
326
- ' if (!n) return null;',
327
- ' var mean = numbers.reduce(function(x, y) {return x + y;}) / n;',
328
- ' var variance = 0;',
329
- ' for (var j = 0; j < n; j++) {',
330
- ' variance += Math.pow(numbers[j] - mean, 2);',
331
- ' }',
332
- ' variance = variance / n;',
333
- ' return Math.sqrt(variance);',
334
- '}']);
308
+ const functionName = JavaScript.provideFunction_('mathStandardDeviation', `
309
+ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(numbers) {
310
+ var n = numbers.length;
311
+ if (!n) return null;
312
+ var mean = numbers.reduce(function(x, y) {return x + y;}) / n;
313
+ var variance = 0;
314
+ for (var j = 0; j < n; j++) {
315
+ variance += Math.pow(numbers[j] - mean, 2);
316
+ }
317
+ variance = variance / n;
318
+ return Math.sqrt(variance);
319
+ }
320
+ `);
335
321
  list = JavaScript.valueToCode(block, 'LIST',
336
322
  JavaScript.ORDER_NONE) || '[]';
337
323
  code = functionName + '(' + list + ')';
338
324
  break;
339
325
  }
340
326
  case 'RANDOM': {
341
- const functionName = JavaScript.provideFunction_(
342
- 'mathRandomList',
343
- ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ +
344
- '(list) {',
345
- ' var x = Math.floor(Math.random() * list.length);',
346
- ' return list[x];',
347
- '}']);
327
+ const functionName = JavaScript.provideFunction_('mathRandomList', `
328
+ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(list) {
329
+ var x = Math.floor(Math.random() * list.length);
330
+ return list[x];
331
+ }
332
+ `);
348
333
  list = JavaScript.valueToCode(block, 'LIST',
349
334
  JavaScript.ORDER_NONE) || '[]';
350
335
  code = functionName + '(' + list + ')';
@@ -385,18 +370,17 @@ JavaScript['math_random_int'] = function(block) {
385
370
  JavaScript.ORDER_NONE) || '0';
386
371
  const argument1 = JavaScript.valueToCode(block, 'TO',
387
372
  JavaScript.ORDER_NONE) || '0';
388
- const functionName = JavaScript.provideFunction_(
389
- 'mathRandomInt',
390
- ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ +
391
- '(a, b) {',
392
- ' if (a > b) {',
393
- ' // Swap a and b to ensure a is smaller.',
394
- ' var c = a;',
395
- ' a = b;',
396
- ' b = c;',
397
- ' }',
398
- ' return Math.floor(Math.random() * (b - a + 1) + a);',
399
- '}']);
373
+ const functionName = JavaScript.provideFunction_('mathRandomInt', `
374
+ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(a, b) {
375
+ if (a > b) {
376
+ // Swap a and b to ensure a is smaller.
377
+ var c = a;
378
+ a = b;
379
+ b = c;
380
+ }
381
+ return Math.floor(Math.random() * (b - a + 1) + a);
382
+ }
383
+ `);
400
384
  const code = functionName + '(' + argument0 + ', ' + argument1 + ')';
401
385
  return [code, JavaScript.ORDER_FUNCTION_CALL];
402
386
  };
@@ -71,18 +71,18 @@ JavaScript['text_join'] = function(block) {
71
71
  // Create a string made up of any number of elements of any type.
72
72
  switch (block.itemCount_) {
73
73
  case 0:
74
- return ['\'\'', JavaScript.ORDER_ATOMIC];
74
+ return ["''", JavaScript.ORDER_ATOMIC];
75
75
  case 1: {
76
76
  const element = JavaScript.valueToCode(block, 'ADD0',
77
- JavaScript.ORDER_NONE) || '\'\'';
77
+ JavaScript.ORDER_NONE) || "''";
78
78
  const codeAndOrder = forceString(element);
79
79
  return codeAndOrder;
80
80
  }
81
81
  case 2: {
82
82
  const element0 = JavaScript.valueToCode(block, 'ADD0',
83
- JavaScript.ORDER_NONE) || '\'\'';
83
+ JavaScript.ORDER_NONE) || "''";
84
84
  const element1 = JavaScript.valueToCode(block, 'ADD1',
85
- JavaScript.ORDER_NONE) || '\'\'';
85
+ JavaScript.ORDER_NONE) || "''";
86
86
  const code = forceString(element0)[0] +
87
87
  ' + ' + forceString(element1)[0];
88
88
  return [code, JavaScript.ORDER_ADDITION];
@@ -91,7 +91,7 @@ JavaScript['text_join'] = function(block) {
91
91
  const elements = new Array(block.itemCount_);
92
92
  for (let i = 0; i < block.itemCount_; i++) {
93
93
  elements[i] = JavaScript.valueToCode(block, 'ADD' + i,
94
- JavaScript.ORDER_NONE) || '\'\'';
94
+ JavaScript.ORDER_NONE) || "''";
95
95
  }
96
96
  const code = '[' + elements.join(',') + '].join(\'\')';
97
97
  return [code, JavaScript.ORDER_FUNCTION_CALL];
@@ -104,7 +104,7 @@ JavaScript['text_append'] = function(block) {
104
104
  const varName = JavaScript.nameDB_.getName(
105
105
  block.getFieldValue('VAR'), NameType.VARIABLE);
106
106
  const value = JavaScript.valueToCode(block, 'TEXT',
107
- JavaScript.ORDER_NONE) || '\'\'';
107
+ JavaScript.ORDER_NONE) || "''";
108
108
  const code = varName + ' += ' +
109
109
  forceString(value)[0] + ';\n';
110
110
  return code;
@@ -113,14 +113,14 @@ JavaScript['text_append'] = function(block) {
113
113
  JavaScript['text_length'] = function(block) {
114
114
  // String or array length.
115
115
  const text = JavaScript.valueToCode(block, 'VALUE',
116
- JavaScript.ORDER_MEMBER) || '\'\'';
116
+ JavaScript.ORDER_MEMBER) || "''";
117
117
  return [text + '.length', JavaScript.ORDER_MEMBER];
118
118
  };
119
119
 
120
120
  JavaScript['text_isEmpty'] = function(block) {
121
121
  // Is the string null or array empty?
122
122
  const text = JavaScript.valueToCode(block, 'VALUE',
123
- JavaScript.ORDER_MEMBER) || '\'\'';
123
+ JavaScript.ORDER_MEMBER) || "''";
124
124
  return ['!' + text + '.length', JavaScript.ORDER_LOGICAL_NOT];
125
125
  };
126
126
 
@@ -129,9 +129,9 @@ JavaScript['text_indexOf'] = function(block) {
129
129
  const operator = block.getFieldValue('END') === 'FIRST' ?
130
130
  'indexOf' : 'lastIndexOf';
131
131
  const substring = JavaScript.valueToCode(block, 'FIND',
132
- JavaScript.ORDER_NONE) || '\'\'';
132
+ JavaScript.ORDER_NONE) || "''";
133
133
  const text = JavaScript.valueToCode(block, 'VALUE',
134
- JavaScript.ORDER_MEMBER) || '\'\'';
134
+ JavaScript.ORDER_MEMBER) || "''";
135
135
  const code = text + '.' + operator + '(' + substring + ')';
136
136
  // Adjust index if using one-based indices.
137
137
  if (block.workspace.options.oneBasedIndex) {
@@ -146,8 +146,7 @@ JavaScript['text_charAt'] = function(block) {
146
146
  const where = block.getFieldValue('WHERE') || 'FROM_START';
147
147
  const textOrder = (where === 'RANDOM') ? JavaScript.ORDER_NONE :
148
148
  JavaScript.ORDER_MEMBER;
149
- const text = JavaScript.valueToCode(block, 'VALUE',
150
- textOrder) || '\'\'';
149
+ const text = JavaScript.valueToCode(block, 'VALUE', textOrder) || "''";
151
150
  switch (where) {
152
151
  case 'FIRST': {
153
152
  const code = text + '.charAt(0)';
@@ -169,13 +168,12 @@ JavaScript['text_charAt'] = function(block) {
169
168
  return [code, JavaScript.ORDER_FUNCTION_CALL];
170
169
  }
171
170
  case 'RANDOM': {
172
- const functionName = JavaScript.provideFunction_(
173
- 'textRandomLetter',
174
- ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ +
175
- '(text) {',
176
- ' var x = Math.floor(Math.random() * text.length);',
177
- ' return text[x];',
178
- '}']);
171
+ const functionName = JavaScript.provideFunction_('textRandomLetter', `
172
+ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(text) {
173
+ var x = Math.floor(Math.random() * text.length);
174
+ return text[x];
175
+ }
176
+ `);
179
177
  const code = functionName + '(' + text + ')';
180
178
  return [code, JavaScript.ORDER_FUNCTION_CALL];
181
179
  }
@@ -191,8 +189,7 @@ JavaScript['text_getSubstring'] = function(block) {
191
189
  where2 !== 'FROM_END' && where2 !== 'LAST');
192
190
  const textOrder = requiresLengthCall ? JavaScript.ORDER_MEMBER :
193
191
  JavaScript.ORDER_NONE;
194
- const text = JavaScript.valueToCode(block, 'STRING',
195
- textOrder) || '\'\'';
192
+ const text = JavaScript.valueToCode(block, 'STRING', textOrder) || "''";
196
193
  let code;
197
194
  if (where1 === 'FIRST' && where2 === 'LAST') {
198
195
  code = text;
@@ -238,22 +235,20 @@ JavaScript['text_getSubstring'] = function(block) {
238
235
  const at2 = JavaScript.getAdjusted(block, 'AT2');
239
236
  const wherePascalCase = {'FIRST': 'First', 'LAST': 'Last',
240
237
  'FROM_START': 'FromStart', 'FROM_END': 'FromEnd'};
238
+ // The value for 'FROM_END' and'FROM_START' depends on `at` so
239
+ // we add it as a parameter.
240
+ const at1Param =
241
+ (where1 === 'FROM_END' || where1 === 'FROM_START') ? ', at1' : '';
242
+ const at2Param =
243
+ (where2 === 'FROM_END' || where2 === 'FROM_START') ? ', at2' : '';
241
244
  const functionName = JavaScript.provideFunction_(
242
- 'subsequence' + wherePascalCase[where1] + wherePascalCase[where2], [
243
- 'function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ +
244
- '(sequence' +
245
- // The value for 'FROM_END' and'FROM_START' depends on `at` so
246
- // we add it as a parameter.
247
- ((where1 === 'FROM_END' || where1 === 'FROM_START') ? ', at1' :
248
- '') +
249
- ((where2 === 'FROM_END' || where2 === 'FROM_START') ? ', at2' :
250
- '') +
251
- ') {',
252
- ' var start = ' + getSubstringIndex('sequence', where1, 'at1') + ';',
253
- ' var end = ' + getSubstringIndex('sequence', where2, 'at2') +
254
- ' + 1;',
255
- ' return sequence.slice(start, end);', '}'
256
- ]);
245
+ 'subsequence' + wherePascalCase[where1] + wherePascalCase[where2], `
246
+ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(sequence${at1Param}${at2Param}) {
247
+ var start = ${getSubstringIndex('sequence', where1, 'at1')};
248
+ var end = ${getSubstringIndex('sequence', where2, 'at2')} + 1;
249
+ return sequence.slice(start, end);
250
+ }
251
+ `);
257
252
  code = functionName + '(' + text +
258
253
  // The value for 'FROM_END' and 'FROM_START' depends on `at` so we
259
254
  // pass it.
@@ -269,27 +264,23 @@ JavaScript['text_changeCase'] = function(block) {
269
264
  const OPERATORS = {
270
265
  'UPPERCASE': '.toUpperCase()',
271
266
  'LOWERCASE': '.toLowerCase()',
272
- 'TITLECASE': null
267
+ 'TITLECASE': null,
273
268
  };
274
269
  const operator = OPERATORS[block.getFieldValue('CASE')];
275
- const textOrder = operator ? JavaScript.ORDER_MEMBER :
276
- JavaScript.ORDER_NONE;
277
- const text = JavaScript.valueToCode(block, 'TEXT',
278
- textOrder) || '\'\'';
270
+ const textOrder = operator ? JavaScript.ORDER_MEMBER : JavaScript.ORDER_NONE;
271
+ const text = JavaScript.valueToCode(block, 'TEXT', textOrder) || "''";
279
272
  let code;
280
273
  if (operator) {
281
274
  // Upper and lower case are functions built into JavaScript.
282
275
  code = text + operator;
283
276
  } else {
284
277
  // Title case is not a native JavaScript function. Define one.
285
- const functionName = JavaScript.provideFunction_(
286
- 'textToTitleCase',
287
- ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ +
288
- '(str) {',
289
- ' return str.replace(/\\S+/g,',
290
- ' function(txt) {return txt[0].toUpperCase() + ' +
291
- 'txt.substring(1).toLowerCase();});',
292
- '}']);
278
+ const functionName = JavaScript.provideFunction_('textToTitleCase', `
279
+ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(str) {
280
+ return str.replace(/\\S+/g,
281
+ function(txt) {return txt[0].toUpperCase() + txt.substring(1).toLowerCase();});
282
+ }
283
+ `);
293
284
  code = functionName + '(' + text + ')';
294
285
  }
295
286
  return [code, JavaScript.ORDER_FUNCTION_CALL];
@@ -300,18 +291,18 @@ JavaScript['text_trim'] = function(block) {
300
291
  const OPERATORS = {
301
292
  'LEFT': ".replace(/^[\\s\\xa0]+/, '')",
302
293
  'RIGHT': ".replace(/[\\s\\xa0]+$/, '')",
303
- 'BOTH': '.trim()'
294
+ 'BOTH': '.trim()',
304
295
  };
305
296
  const operator = OPERATORS[block.getFieldValue('MODE')];
306
297
  const text = JavaScript.valueToCode(block, 'TEXT',
307
- JavaScript.ORDER_MEMBER) || '\'\'';
298
+ JavaScript.ORDER_MEMBER) || "''";
308
299
  return [text + operator, JavaScript.ORDER_FUNCTION_CALL];
309
300
  };
310
301
 
311
302
  JavaScript['text_print'] = function(block) {
312
303
  // Print statement.
313
304
  const msg = JavaScript.valueToCode(block, 'TEXT',
314
- JavaScript.ORDER_NONE) || '\'\'';
305
+ JavaScript.ORDER_NONE) || "''";
315
306
  return 'window.alert(' + msg + ');\n';
316
307
  };
317
308
 
@@ -323,8 +314,7 @@ JavaScript['text_prompt_ext'] = function(block) {
323
314
  msg = JavaScript.quote_(block.getFieldValue('TEXT'));
324
315
  } else {
325
316
  // External message.
326
- msg = JavaScript.valueToCode(block, 'TEXT',
327
- JavaScript.ORDER_NONE) || '\'\'';
317
+ msg = JavaScript.valueToCode(block, 'TEXT', JavaScript.ORDER_NONE) || "''";
328
318
  }
329
319
  let code = 'window.prompt(' + msg + ')';
330
320
  const toNumber = block.getFieldValue('TYPE') === 'NUMBER';
@@ -338,48 +328,44 @@ JavaScript['text_prompt'] = JavaScript['text_prompt_ext'];
338
328
 
339
329
  JavaScript['text_count'] = function(block) {
340
330
  const text = JavaScript.valueToCode(block, 'TEXT',
341
- JavaScript.ORDER_NONE) || '\'\'';
331
+ JavaScript.ORDER_NONE) || "''";
342
332
  const sub = JavaScript.valueToCode(block, 'SUB',
343
- JavaScript.ORDER_NONE) || '\'\'';
344
- const functionName = JavaScript.provideFunction_(
345
- 'textCount',
346
- ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ +
347
- '(haystack, needle) {',
348
- ' if (needle.length === 0) {',
349
- ' return haystack.length + 1;',
350
- ' } else {',
351
- ' return haystack.split(needle).length - 1;',
352
- ' }',
353
- '}']);
333
+ JavaScript.ORDER_NONE) || "''";
334
+ const functionName = JavaScript.provideFunction_('textCount', `
335
+ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(haystack, needle) {
336
+ if (needle.length === 0) {
337
+ return haystack.length + 1;
338
+ } else {
339
+ return haystack.split(needle).length - 1;
340
+ }
341
+ }
342
+ `);
354
343
  const code = functionName + '(' + text + ', ' + sub + ')';
355
344
  return [code, JavaScript.ORDER_FUNCTION_CALL];
356
345
  };
357
346
 
358
347
  JavaScript['text_replace'] = function(block) {
359
348
  const text = JavaScript.valueToCode(block, 'TEXT',
360
- JavaScript.ORDER_NONE) || '\'\'';
349
+ JavaScript.ORDER_NONE) || "''";
361
350
  const from = JavaScript.valueToCode(block, 'FROM',
362
- JavaScript.ORDER_NONE) || '\'\'';
363
- const to = JavaScript.valueToCode(block, 'TO',
364
- JavaScript.ORDER_NONE) || '\'\'';
351
+ JavaScript.ORDER_NONE) || "''";
352
+ const to = JavaScript.valueToCode(block, 'TO', JavaScript.ORDER_NONE) || "''";
365
353
  // The regex escaping code below is taken from the implementation of
366
354
  // goog.string.regExpEscape.
367
- const functionName = JavaScript.provideFunction_(
368
- 'textReplace',
369
- ['function ' + JavaScript.FUNCTION_NAME_PLACEHOLDER_ +
370
- '(haystack, needle, replacement) {',
371
- ' needle = ' +
372
- 'needle.replace(/([-()\\[\\]{}+?*.$\\^|,:#<!\\\\])/g,"\\\\$1")',
373
- ' .replace(/\\x08/g,"\\\\x08");',
374
- ' return haystack.replace(new RegExp(needle, \'g\'), replacement);',
375
- '}']);
355
+ const functionName = JavaScript.provideFunction_('textReplace', `
356
+ function ${JavaScript.FUNCTION_NAME_PLACEHOLDER_}(haystack, needle, replacement) {
357
+ needle = needle.replace(/([-()\\[\\]{}+?*.$\\^|,:#<!\\\\])/g, '\\\\$1')
358
+ .replace(/\\x08/g, '\\\\x08');
359
+ return haystack.replace(new RegExp(needle, \'g\'), replacement);
360
+ }
361
+ `);
376
362
  const code = functionName + '(' + text + ', ' + from + ', ' + to + ')';
377
363
  return [code, JavaScript.ORDER_FUNCTION_CALL];
378
364
  };
379
365
 
380
366
  JavaScript['text_reverse'] = function(block) {
381
367
  const text = JavaScript.valueToCode(block, 'TEXT',
382
- JavaScript.ORDER_MEMBER) || '\'\'';
383
- const code = text + '.split(\'\').reverse().join(\'\')';
368
+ JavaScript.ORDER_MEMBER) || "''";
369
+ const code = text + ".split('').reverse().join('')";
384
370
  return [code, JavaScript.ORDER_FUNCTION_CALL];
385
371
  };