blockly 7.20211209.4 → 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
@@ -44,11 +44,15 @@ PHP['lists_create_with'] = function(block) {
44
44
 
45
45
  PHP['lists_repeat'] = function(block) {
46
46
  // Create a list with one element repeated.
47
- const functionName = PHP.provideFunction_('lists_repeat', [
48
- 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($value, $count) {',
49
- ' $array = array();', ' for ($index = 0; $index < $count; $index++) {',
50
- ' $array[] = $value;', ' }', ' return $array;', '}'
51
- ]);
47
+ const functionName = PHP.provideFunction_('lists_repeat', `
48
+ function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($value, $count) {
49
+ $array = array();
50
+ for ($index = 0; $index < $count; $index++) {
51
+ $array[] = $value;
52
+ }
53
+ return $array;
54
+ }
55
+ `);
52
56
  const element = PHP.valueToCode(block, 'ITEM', PHP.ORDER_NONE) || 'null';
53
57
  const repeatCount = PHP.valueToCode(block, 'NUM', PHP.ORDER_NONE) || '0';
54
58
  const code = functionName + '(' + element + ', ' + repeatCount + ')';
@@ -57,12 +61,16 @@ PHP['lists_repeat'] = function(block) {
57
61
 
58
62
  PHP['lists_length'] = function(block) {
59
63
  // String or array length.
60
- const functionName = PHP.provideFunction_('length', [
61
- 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($value) {',
62
- ' if (is_string($value)) {', ' return strlen($value);', ' } else {',
63
- ' return count($value);', ' }', '}'
64
- ]);
65
- const list = PHP.valueToCode(block, 'VALUE', PHP.ORDER_NONE) || '\'\'';
64
+ const functionName = PHP.provideFunction_('length', `
65
+ function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($value) {
66
+ if (is_string($value)) {
67
+ return strlen($value);
68
+ } else {
69
+ return count($value);
70
+ }
71
+ }
72
+ `);
73
+ const list = PHP.valueToCode(block, 'VALUE', PHP.ORDER_NONE) || "''";
66
74
  return [functionName + '(' + list + ')', PHP.ORDER_FUNCTION_CALL];
67
75
  };
68
76
 
@@ -75,7 +83,7 @@ PHP['lists_isEmpty'] = function(block) {
75
83
 
76
84
  PHP['lists_indexOf'] = function(block) {
77
85
  // Find an item in the list.
78
- const argument0 = PHP.valueToCode(block, 'FIND', PHP.ORDER_NONE) || '\'\'';
86
+ const argument0 = PHP.valueToCode(block, 'FIND', PHP.ORDER_NONE) || "''";
79
87
  const argument1 = PHP.valueToCode(block, 'VALUE', PHP.ORDER_MEMBER) || '[]';
80
88
  let errorIndex = ' -1';
81
89
  let indexAdjustment = '';
@@ -86,23 +94,25 @@ PHP['lists_indexOf'] = function(block) {
86
94
  let functionName;
87
95
  if (block.getFieldValue('END') === 'FIRST') {
88
96
  // indexOf
89
- functionName = PHP.provideFunction_('indexOf', [
90
- 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($haystack, $needle) {',
91
- ' for ($index = 0; $index < count($haystack); $index++) {',
92
- ' if ($haystack[$index] == $needle) return $index' + indexAdjustment +
93
- ';',
94
- ' }', ' return ' + errorIndex + ';', '}'
95
- ]);
97
+ functionName = PHP.provideFunction_('indexOf', `
98
+ function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($haystack, $needle) {
99
+ for ($index = 0; $index < count($haystack); $index++) {
100
+ if ($haystack[$index] == $needle) return $index${indexAdjustment};
101
+ }
102
+ return ${errorIndex};
103
+ }
104
+ `);
96
105
  } else {
97
106
  // lastIndexOf
98
- functionName = PHP.provideFunction_('lastIndexOf', [
99
- 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($haystack, $needle) {',
100
- ' $last = ' + errorIndex + ';',
101
- ' for ($index = 0; $index < count($haystack); $index++) {',
102
- ' if ($haystack[$index] == $needle) $last = $index' + indexAdjustment +
103
- ';',
104
- ' }', ' return $last;', '}'
105
- ]);
107
+ functionName = PHP.provideFunction_('lastIndexOf', `
108
+ function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($haystack, $needle) {
109
+ $last = ${errorIndex};
110
+ for ($index = 0; $index < count($haystack); $index++) {
111
+ if ($haystack[$index] == $needle) $last = $index${indexAdjustment};
112
+ }
113
+ return $last;
114
+ }
115
+ `);
106
116
  }
107
117
 
108
118
  const code = functionName + '(' + argument1 + ', ' + argument0 + ')';
@@ -191,26 +201,30 @@ PHP['lists_getIndex'] = function(block) {
191
201
  case 'RANDOM': {
192
202
  const list = PHP.valueToCode(block, 'VALUE', PHP.ORDER_NONE) || 'array()';
193
203
  if (mode === 'GET') {
194
- const functionName = PHP.provideFunction_('lists_get_random_item', [
195
- 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($list) {',
196
- ' return $list[rand(0,count($list)-1)];', '}'
197
- ]);
204
+ const functionName = PHP.provideFunction_('lists_get_random_item', `
205
+ function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($list) {
206
+ return $list[rand(0,count($list)-1)];
207
+ }
208
+ `);
198
209
  const code = functionName + '(' + list + ')';
199
210
  return [code, PHP.ORDER_FUNCTION_CALL];
200
211
  } else if (mode === 'GET_REMOVE') {
201
212
  const functionName =
202
- PHP.provideFunction_('lists_get_remove_random_item', [
203
- 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '(&$list) {',
204
- ' $x = rand(0,count($list)-1);', ' unset($list[$x]);',
205
- ' return array_values($list);', '}'
206
- ]);
213
+ PHP.provideFunction_('lists_get_remove_random_item', `
214
+ function ${PHP.FUNCTION_NAME_PLACEHOLDER_}(&$list) {
215
+ $x = rand(0,count($list)-1);
216
+ unset($list[$x]);
217
+ return array_values($list);
218
+ }
219
+ `);
207
220
  const code = functionName + '(' + list + ')';
208
221
  return [code, PHP.ORDER_FUNCTION_CALL];
209
222
  } else if (mode === 'REMOVE') {
210
- const functionName = PHP.provideFunction_('lists_remove_random_item', [
211
- 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '(&$list) {',
212
- ' unset($list[rand(0,count($list)-1)]);', '}'
213
- ]);
223
+ const functionName = PHP.provideFunction_('lists_remove_random_item', `
224
+ function ${PHP.FUNCTION_NAME_PLACEHOLDER_}(&$list) {
225
+ unset($list[rand(0,count($list)-1)]);
226
+ }
227
+ `);
214
228
  return functionName + '(' + list + ');\n';
215
229
  }
216
230
  break;
@@ -252,10 +266,11 @@ PHP['lists_setIndex'] = function(block) {
252
266
  case 'LAST': {
253
267
  const list = PHP.valueToCode(block, 'LIST', PHP.ORDER_NONE) || 'array()';
254
268
  if (mode === 'SET') {
255
- const functionName = PHP.provideFunction_('lists_set_last_item', [
256
- 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '(&$list, $value) {',
257
- ' $list[count($list) - 1] = $value;', '}'
258
- ]);
269
+ const functionName = PHP.provideFunction_('lists_set_last_item', `
270
+ function ${PHP.FUNCTION_NAME_PLACEHOLDER_}(&$list, $value) {
271
+ $list[count($list) - 1] = $value;
272
+ }
273
+ `);
259
274
  return functionName + '(' + list + ', ' + value + ');\n';
260
275
  } else if (mode === 'INSERT') {
261
276
  return 'array_push(' + list + ', ' + value + ');\n';
@@ -279,18 +294,18 @@ PHP['lists_setIndex'] = function(block) {
279
294
  const list = PHP.valueToCode(block, 'LIST', PHP.ORDER_NONE) || 'array()';
280
295
  const at = PHP.getAdjusted(block, 'AT', 1);
281
296
  if (mode === 'SET') {
282
- const functionName = PHP.provideFunction_('lists_set_from_end', [
283
- 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ +
284
- '(&$list, $at, $value) {',
285
- ' $list[count($list) - $at] = $value;', '}'
286
- ]);
297
+ const functionName = PHP.provideFunction_('lists_set_from_end', `
298
+ function ${PHP.FUNCTION_NAME_PLACEHOLDER_}(&$list, $at, $value) {
299
+ $list[count($list) - $at] = $value;
300
+ }
301
+ `);
287
302
  return functionName + '(' + list + ', ' + at + ', ' + value + ');\n';
288
303
  } else if (mode === 'INSERT') {
289
- const functionName = PHP.provideFunction_('lists_insert_from_end', [
290
- 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ +
291
- '(&$list, $at, $value) {',
292
- ' return array_splice($list, count($list) - $at, 0, $value);', '}'
293
- ]);
304
+ const functionName = PHP.provideFunction_('lists_insert_from_end', `
305
+ function ${PHP.FUNCTION_NAME_PLACEHOLDER_}(&$list, $at, $value) {
306
+ return array_splice($list, count($list) - $at, 0, $value);
307
+ }
308
+ `);
294
309
  return functionName + '(' + list + ', ' + at + ', ' + value + ');\n';
295
310
  }
296
311
  break;
@@ -382,29 +397,28 @@ PHP['lists_getSublist'] = function(block) {
382
397
  } else {
383
398
  const at1 = PHP.getAdjusted(block, 'AT1');
384
399
  const at2 = PHP.getAdjusted(block, 'AT2');
385
- const functionName = PHP.provideFunction_('lists_get_sublist', [
386
- 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ +
387
- '($list, $where1, $at1, $where2, $at2) {',
388
- ' if ($where1 == \'FROM_END\') {',
389
- ' $at1 = count($list) - 1 - $at1;',
390
- ' } else if ($where1 == \'FIRST\') {',
391
- ' $at1 = 0;',
392
- ' } else if ($where1 != \'FROM_START\') {',
393
- ' throw new Exception(\'Unhandled option (lists_get_sublist).\');',
394
- ' }',
395
- ' $length = 0;',
396
- ' if ($where2 == \'FROM_START\') {',
397
- ' $length = $at2 - $at1 + 1;',
398
- ' } else if ($where2 == \'FROM_END\') {',
399
- ' $length = count($list) - $at1 - $at2;',
400
- ' } else if ($where2 == \'LAST\') {',
401
- ' $length = count($list) - $at1;',
402
- ' } else {',
403
- ' throw new Exception(\'Unhandled option (lists_get_sublist).\');',
404
- ' }',
405
- ' return array_slice($list, $at1, $length);',
406
- '}'
407
- ]);
400
+ const functionName = PHP.provideFunction_('lists_get_sublist', `
401
+ function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($list, $where1, $at1, $where2, $at2) {
402
+ if ($where1 == 'FROM_END') {
403
+ $at1 = count($list) - 1 - $at1;
404
+ } else if ($where1 == 'FIRST') {
405
+ $at1 = 0;
406
+ } else if ($where1 != 'FROM_START') {
407
+ throw new Exception('Unhandled option (lists_get_sublist).');
408
+ }
409
+ $length = 0;
410
+ if ($where2 == 'FROM_START') {
411
+ $length = $at2 - $at1 + 1;
412
+ } else if ($where2 == 'FROM_END') {
413
+ $length = count($list) - $at1 - $at2;
414
+ } else if ($where2 == 'LAST') {
415
+ $length = count($list) - $at1;
416
+ } else {
417
+ throw new Exception('Unhandled option (lists_get_sublist).');
418
+ }
419
+ return array_slice($list, $at1, $length);
420
+ }
421
+ `);
408
422
  code = functionName + '(' + list + ', \'' + where1 + '\', ' + at1 + ', \'' +
409
423
  where2 + '\', ' + at2 + ')';
410
424
  }
@@ -416,16 +430,22 @@ PHP['lists_sort'] = function(block) {
416
430
  const listCode = PHP.valueToCode(block, 'LIST', PHP.ORDER_NONE) || 'array()';
417
431
  const direction = block.getFieldValue('DIRECTION') === '1' ? 1 : -1;
418
432
  const type = block.getFieldValue('TYPE');
419
- const functionName = PHP.provideFunction_('lists_sort', [
420
- 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ +
421
- '($list, $type, $direction) {',
422
- ' $sortCmpFuncs = array(', ' "NUMERIC" => "strnatcasecmp",',
423
- ' "TEXT" => "strcmp",', ' "IGNORE_CASE" => "strcasecmp"', ' );',
424
- ' $sortCmp = $sortCmpFuncs[$type];',
425
- ' $list2 = $list;', // Clone list.
426
- ' usort($list2, $sortCmp);', ' if ($direction == -1) {',
427
- ' $list2 = array_reverse($list2);', ' }', ' return $list2;', '}'
428
- ]);
433
+ const functionName = PHP.provideFunction_('lists_sort', `
434
+ function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($list, $type, $direction) {
435
+ $sortCmpFuncs = array(
436
+ 'NUMERIC' => 'strnatcasecmp',
437
+ 'TEXT' => 'strcmp',
438
+ 'IGNORE_CASE' => 'strcasecmp'
439
+ );
440
+ $sortCmp = $sortCmpFuncs[$type];
441
+ $list2 = $list;
442
+ usort($list2, $sortCmp);
443
+ if ($direction == -1) {
444
+ $list2 = array_reverse($list2);
445
+ }
446
+ return $list2;
447
+ }
448
+ `);
429
449
  const sortCode =
430
450
  functionName + '(' + listCode + ', "' + type + '", ' + direction + ')';
431
451
  return [sortCode, PHP.ORDER_FUNCTION_CALL];
@@ -434,12 +454,12 @@ PHP['lists_sort'] = function(block) {
434
454
  PHP['lists_split'] = function(block) {
435
455
  // Block for splitting text into a list, or joining a list into text.
436
456
  let value_input = PHP.valueToCode(block, 'INPUT', PHP.ORDER_NONE);
437
- const value_delim = PHP.valueToCode(block, 'DELIM', PHP.ORDER_NONE) || '\'\'';
457
+ const value_delim = PHP.valueToCode(block, 'DELIM', PHP.ORDER_NONE) || "''";
438
458
  const mode = block.getFieldValue('MODE');
439
459
  let functionName;
440
460
  if (mode === 'SPLIT') {
441
461
  if (!value_input) {
442
- value_input = '\'\'';
462
+ value_input = "''";
443
463
  }
444
464
  functionName = 'explode';
445
465
  } else if (mode === 'JOIN') {
@@ -34,7 +34,7 @@ PHP['math_arithmetic'] = function(block) {
34
34
  'MINUS': [' - ', PHP.ORDER_SUBTRACTION],
35
35
  'MULTIPLY': [' * ', PHP.ORDER_MULTIPLICATION],
36
36
  'DIVIDE': [' / ', PHP.ORDER_DIVISION],
37
- 'POWER': [' ** ', PHP.ORDER_POWER]
37
+ 'POWER': [' ** ', PHP.ORDER_POWER],
38
38
  };
39
39
  const tuple = OPERATORS[block.getFieldValue('OP')];
40
40
  const operator = tuple[0];
@@ -134,7 +134,7 @@ PHP['math_constant'] = function(block) {
134
134
  'GOLDEN_RATIO': ['(1 + sqrt(5)) / 2', PHP.ORDER_DIVISION],
135
135
  'SQRT2': ['M_SQRT2', PHP.ORDER_ATOMIC],
136
136
  'SQRT1_2': ['M_SQRT1_2', PHP.ORDER_ATOMIC],
137
- 'INFINITY': ['INF', PHP.ORDER_ATOMIC]
137
+ 'INFINITY': ['INF', PHP.ORDER_ATOMIC],
138
138
  };
139
139
  return CONSTANTS[block.getFieldValue('CONSTANT')];
140
140
  };
@@ -142,53 +142,55 @@ PHP['math_constant'] = function(block) {
142
142
  PHP['math_number_property'] = function(block) {
143
143
  // Check if a number is even, odd, prime, whole, positive, or negative
144
144
  // or if it is divisible by certain number. Returns true or false.
145
- const number_to_check =
146
- PHP.valueToCode(block, 'NUMBER_TO_CHECK', PHP.ORDER_MODULUS) || '0';
147
- const dropdown_property = block.getFieldValue('PROPERTY');
145
+ const PROPERTIES = {
146
+ 'EVEN': ['', ' % 2 == 0', PHP.ORDER_MODULUS, PHP.ORDER_EQUALITY],
147
+ 'ODD': ['', ' % 2 == 1', PHP.ORDER_MODULUS, PHP.ORDER_EQUALITY],
148
+ 'WHOLE': ['is_int(', ')', PHP.ORDER_NONE, PHP.ORDER_FUNCTION_CALL],
149
+ 'POSITIVE': ['', ' > 0', PHP.ORDER_RELATIONAL, PHP.ORDER_RELATIONAL],
150
+ 'NEGATIVE': ['', ' < 0', PHP.ORDER_RELATIONAL, PHP.ORDER_RELATIONAL],
151
+ 'DIVISIBLE_BY': [null, null, PHP.ORDER_MODULUS, PHP.ORDER_EQUALITY],
152
+ 'PRIME': [null, null, PHP.ORDER_NONE, PHP.ORDER_FUNCTION_CALL],
153
+ };
154
+ const dropdownProperty = block.getFieldValue('PROPERTY');
155
+ const [prefix, suffix, inputOrder, outputOrder] = PROPERTIES[dropdownProperty];
156
+ const numberToCheck = PHP.valueToCode(block, 'NUMBER_TO_CHECK',
157
+ inputOrder) || '0';
148
158
  let code;
149
- if (dropdown_property === 'PRIME') {
159
+ if (dropdownProperty === 'PRIME') {
150
160
  // Prime is a special case as it is not a one-liner test.
151
- const functionName = PHP.provideFunction_('math_isPrime', [
152
- 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($n) {',
153
- ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods',
154
- ' if ($n == 2 || $n == 3) {', ' return true;', ' }',
155
- ' // False if n is NaN, negative, is 1, or not whole.',
156
- ' // And false if n is divisible by 2 or 3.',
157
- ' if (!is_numeric($n) || $n <= 1 || $n % 1 != 0 || $n % 2 == 0 ||' +
158
- ' $n % 3 == 0) {',
159
- ' return false;', ' }',
160
- ' // Check all the numbers of form 6k +/- 1, up to sqrt(n).',
161
- ' for ($x = 6; $x <= sqrt($n) + 1; $x += 6) {',
162
- ' if ($n % ($x - 1) == 0 || $n % ($x + 1) == 0) {',
163
- ' return false;', ' }', ' }', ' return true;', '}'
164
- ]);
165
- code = functionName + '(' + number_to_check + ')';
166
- return [code, PHP.ORDER_FUNCTION_CALL];
161
+ const functionName = PHP.provideFunction_('math_isPrime', `
162
+ function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($n) {
163
+ // https://en.wikipedia.org/wiki/Primality_test#Naive_methods
164
+ if ($n == 2 || $n == 3) {
165
+ return true;
167
166
  }
168
- switch (dropdown_property) {
169
- case 'EVEN':
170
- code = number_to_check + ' % 2 == 0';
171
- break;
172
- case 'ODD':
173
- code = number_to_check + ' % 2 == 1';
174
- break;
175
- case 'WHOLE':
176
- code = 'is_int(' + number_to_check + ')';
177
- break;
178
- case 'POSITIVE':
179
- code = number_to_check + ' > 0';
180
- break;
181
- case 'NEGATIVE':
182
- code = number_to_check + ' < 0';
183
- break;
184
- case 'DIVISIBLE_BY': {
185
- const divisor =
186
- PHP.valueToCode(block, 'DIVISOR', PHP.ORDER_MODULUS) || '0';
187
- code = number_to_check + ' % ' + divisor + ' == 0';
188
- break;
167
+ // False if n is NaN, negative, is 1, or not whole.
168
+ // And false if n is divisible by 2 or 3.
169
+ if (!is_numeric($n) || $n <= 1 || $n % 1 != 0 || $n % 2 == 0 || $n % 3 == 0) {
170
+ return false;
171
+ }
172
+ // Check all the numbers of form 6k +/- 1, up to sqrt(n).
173
+ for ($x = 6; $x <= sqrt($n) + 1; $x += 6) {
174
+ if ($n % ($x - 1) == 0 || $n % ($x + 1) == 0) {
175
+ return false;
189
176
  }
190
177
  }
191
- return [code, PHP.ORDER_EQUALITY];
178
+ return true;
179
+ }
180
+ `);
181
+ code = functionName + '(' + numberToCheck + ')';
182
+ } else if (dropdownProperty === 'DIVISIBLE_BY') {
183
+ const divisor = PHP.valueToCode(block, 'DIVISOR',
184
+ PHP.ORDER_MODULUS) || '0';
185
+ if (divisor === '0') {
186
+ return ['false', PHP.ORDER_ATOMIC];
187
+
188
+ }
189
+ code = numberToCheck + ' % ' + divisor + ' == 0';
190
+ } else {
191
+ code = prefix + numberToCheck + suffix;
192
+ }
193
+ return [code, outputOrder];
192
194
  };
193
195
 
194
196
  PHP['math_change'] = function(block) {
@@ -226,23 +228,23 @@ PHP['math_on_list'] = function(block) {
226
228
  code = 'max(' + list + ')';
227
229
  break;
228
230
  case 'AVERAGE': {
229
- const functionName = PHP.provideFunction_('math_mean', [
230
- 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($myList) {',
231
- ' return array_sum($myList) / count($myList);', '}'
232
- ]);
231
+ const functionName = PHP.provideFunction_('math_mean', `
232
+ function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($myList) {
233
+ return array_sum($myList) / count($myList);
234
+ }
235
+ `);
233
236
  list = PHP.valueToCode(block, 'LIST', PHP.ORDER_NONE) || 'array()';
234
237
  code = functionName + '(' + list + ')';
235
238
  break;
236
239
  }
237
240
  case 'MEDIAN': {
238
- const functionName = PHP.provideFunction_('math_median', [
239
- 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($arr) {',
240
- ' sort($arr,SORT_NUMERIC);',
241
- ' return (count($arr) % 2) ? $arr[floor(count($arr)/2)] : ',
242
- ' ($arr[floor(count($arr)/2)] + $arr[floor(count($arr)/2)' +
243
- ' - 1]) / 2;',
244
- '}'
245
- ]);
241
+ const functionName = PHP.provideFunction_('math_median', `
242
+ function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($arr) {
243
+ sort($arr,SORT_NUMERIC);
244
+ return (count($arr) % 2) ? $arr[floor(count($arr) / 2)] :
245
+ ($arr[floor(count($arr) / 2)] + $arr[floor(count($arr) / 2) - 1]) / 2;
246
+ }
247
+ `);
246
248
  list = PHP.valueToCode(block, 'LIST', PHP.ORDER_NONE) || '[]';
247
249
  code = functionName + '(' + list + ')';
248
250
  break;
@@ -251,36 +253,40 @@ PHP['math_on_list'] = function(block) {
251
253
  // As a list of numbers can contain more than one mode,
252
254
  // the returned result is provided as an array.
253
255
  // Mode of [3, 'x', 'x', 1, 1, 2, '3'] -> ['x', 1].
254
- const functionName = PHP.provideFunction_('math_modes', [
255
- 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($values) {',
256
- ' if (empty($values)) return array();',
257
- ' $counts = array_count_values($values);',
258
- ' arsort($counts); // Sort counts in descending order',
259
- ' $modes = array_keys($counts, current($counts), true);',
260
- ' return $modes;', '}'
261
- ]);
256
+ const functionName = PHP.provideFunction_('math_modes', `
257
+ function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($values) {
258
+ if (empty($values)) return array();
259
+ $counts = array_count_values($values);
260
+ arsort($counts); // Sort counts in descending order
261
+ $modes = array_keys($counts, current($counts), true);
262
+ return $modes;
263
+ }
264
+ `);
262
265
  list = PHP.valueToCode(block, 'LIST', PHP.ORDER_NONE) || '[]';
263
266
  code = functionName + '(' + list + ')';
264
267
  break;
265
268
  }
266
269
  case 'STD_DEV': {
267
- const functionName = PHP.provideFunction_('math_standard_deviation', [
268
- 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($numbers) {',
269
- ' $n = count($numbers);', ' if (!$n) return null;',
270
- ' $mean = array_sum($numbers) / count($numbers);',
271
- ' foreach($numbers as $key => $num) $devs[$key] = ' +
272
- 'pow($num - $mean, 2);',
273
- ' return sqrt(array_sum($devs) / (count($devs) - 1));', '}'
274
- ]);
270
+ const functionName = PHP.provideFunction_('math_standard_deviation', `
271
+ function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($numbers) {
272
+ $n = count($numbers);
273
+ if (!$n) return null;
274
+ $mean = array_sum($numbers) / count($numbers);
275
+ foreach($numbers as $key => $num) $devs[$key] = pow($num - $mean, 2);
276
+ return sqrt(array_sum($devs) / (count($devs) - 1));
277
+ }
278
+ `);
275
279
  list = PHP.valueToCode(block, 'LIST', PHP.ORDER_NONE) || '[]';
276
280
  code = functionName + '(' + list + ')';
277
281
  break;
278
282
  }
279
283
  case 'RANDOM': {
280
- const functionName = PHP.provideFunction_('math_random_list', [
281
- 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($list) {',
282
- ' $x = rand(0, count($list)-1);', ' return $list[$x];', '}'
283
- ]);
284
+ const functionName = PHP.provideFunction_('math_random_list', `
285
+ function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($list) {
286
+ $x = rand(0, count($list)-1);
287
+ return $list[$x];
288
+ }
289
+ `);
284
290
  list = PHP.valueToCode(block, 'LIST', PHP.ORDER_NONE) || '[]';
285
291
  code = functionName + '(' + list + ')';
286
292
  break;
@@ -315,11 +321,14 @@ PHP['math_random_int'] = function(block) {
315
321
  // Random integer between [X] and [Y].
316
322
  const argument0 = PHP.valueToCode(block, 'FROM', PHP.ORDER_NONE) || '0';
317
323
  const argument1 = PHP.valueToCode(block, 'TO', PHP.ORDER_NONE) || '0';
318
- const functionName = PHP.provideFunction_('math_random_int', [
319
- 'function ' + PHP.FUNCTION_NAME_PLACEHOLDER_ + '($a, $b) {',
320
- ' if ($a > $b) {', ' return rand($b, $a);', ' }',
321
- ' return rand($a, $b);', '}'
322
- ]);
324
+ const functionName = PHP.provideFunction_('math_random_int', `
325
+ function ${PHP.FUNCTION_NAME_PLACEHOLDER_}($a, $b) {
326
+ if ($a > $b) {
327
+ return rand($b, $a);
328
+ }
329
+ return rand($a, $b);
330
+ }
331
+ `);
323
332
  const code = functionName + '(' + argument0 + ', ' + argument1 + ')';
324
333
  return [code, PHP.ORDER_FUNCTION_CALL];
325
334
  };