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
package/core/workspace.js CHANGED
@@ -15,8 +15,6 @@
15
15
  */
16
16
  goog.module('Blockly.Workspace');
17
17
 
18
- /* eslint-disable-next-line no-unused-vars */
19
- const Abstract = goog.requireType('Blockly.Events.Abstract');
20
18
  const arrayUtils = goog.require('Blockly.utils.array');
21
19
  const eventUtils = goog.require('Blockly.Events.utils');
22
20
  const idGenerator = goog.require('Blockly.utils.idGenerator');
@@ -25,6 +23,8 @@ const registry = goog.require('Blockly.registry');
25
23
  /* eslint-disable-next-line no-unused-vars */
26
24
  const toolbox = goog.requireType('Blockly.utils.toolbox');
27
25
  /* eslint-disable-next-line no-unused-vars */
26
+ const {Abstract} = goog.requireType('Blockly.Events.Abstract');
27
+ /* eslint-disable-next-line no-unused-vars */
28
28
  const {BlocklyOptions} = goog.requireType('Blockly.BlocklyOptions');
29
29
  /* eslint-disable-next-line no-unused-vars */
30
30
  const {Block} = goog.requireType('Blockly.Block');
@@ -53,763 +53,784 @@ const WorkspaceDB_ = Object.create(null);
53
53
  /**
54
54
  * Class for a workspace. This is a data structure that contains blocks.
55
55
  * There is no UI, and can be created headlessly.
56
- * @param {!Options=} opt_options Dictionary of options.
57
- * @constructor
58
56
  * @implements {IASTNodeLocation}
59
57
  * @alias Blockly.Workspace
60
58
  */
61
- const Workspace = function(opt_options) {
62
- /** @type {string} */
63
- this.id = idGenerator.genUid();
64
- WorkspaceDB_[this.id] = this;
65
- /** @type {!Options} */
66
- this.options =
67
- opt_options || new Options(/** @type {!BlocklyOptions} */ ({}));
68
- /** @type {boolean} */
69
- this.RTL = !!this.options.RTL;
70
- /** @type {boolean} */
71
- this.horizontalLayout = !!this.options.horizontalLayout;
72
- /** @type {toolbox.Position} */
73
- this.toolboxPosition = this.options.toolboxPosition;
74
-
75
- const connectionCheckerClass = registry.getClassFromOptions(
76
- registry.Type.CONNECTION_CHECKER, this.options, true);
77
- /**
78
- * An object that encapsulates logic for safety, type, and dragging checks.
79
- * @type {!IConnectionChecker}
80
- */
81
- this.connectionChecker = new connectionCheckerClass(this);
82
-
83
- /**
84
- * @type {!Array<!Block>}
85
- * @private
86
- */
87
- this.topBlocks_ = [];
59
+ class Workspace {
88
60
  /**
89
- * @type {!Array<!WorkspaceComment>}
90
- * @private
61
+ * @param {!Options=} opt_options Dictionary of options.
91
62
  */
92
- this.topComments_ = [];
63
+ constructor(opt_options) {
64
+ /** @type {string} */
65
+ this.id = idGenerator.genUid();
66
+ WorkspaceDB_[this.id] = this;
67
+ /** @type {!Options} */
68
+ this.options =
69
+ opt_options || new Options(/** @type {!BlocklyOptions} */ ({}));
70
+ /** @type {boolean} */
71
+ this.RTL = !!this.options.RTL;
72
+ /** @type {boolean} */
73
+ this.horizontalLayout = !!this.options.horizontalLayout;
74
+ /** @type {toolbox.Position} */
75
+ this.toolboxPosition = this.options.toolboxPosition;
76
+
77
+ /**
78
+ * Returns `true` if the workspace is visible and `false` if it's headless.
79
+ * @type {boolean}
80
+ */
81
+ this.rendered = false;
82
+
83
+ /**
84
+ * Is this workspace the surface for a flyout?
85
+ * @type {boolean}
86
+ */
87
+ this.isFlyout = false;
88
+
89
+ /**
90
+ * Is this workspace the surface for a mutator?
91
+ * @type {boolean}
92
+ * @package
93
+ */
94
+ this.isMutator = false;
95
+
96
+ /**
97
+ * Returns `true` if the workspace is currently in the process of a bulk
98
+ * clear.
99
+ * @type {boolean}
100
+ * @package
101
+ */
102
+ this.isClearing = false;
103
+
104
+ /**
105
+ * Maximum number of undo events in stack. `0` turns off undo, `Infinity`
106
+ * sets it to unlimited.
107
+ * @type {number}
108
+ */
109
+ this.MAX_UNDO = 1024;
110
+
111
+ /**
112
+ * Set of databases for rapid lookup of connection locations.
113
+ * @type {Array<!ConnectionDB>}
114
+ */
115
+ this.connectionDBList = null;
116
+
117
+ const connectionCheckerClass = registry.getClassFromOptions(
118
+ registry.Type.CONNECTION_CHECKER, this.options, true);
119
+ /**
120
+ * An object that encapsulates logic for safety, type, and dragging checks.
121
+ * @type {!IConnectionChecker}
122
+ */
123
+ this.connectionChecker = new connectionCheckerClass(this);
124
+
125
+ /**
126
+ * @type {!Array<!Block>}
127
+ * @private
128
+ */
129
+ this.topBlocks_ = [];
130
+ /**
131
+ * @type {!Array<!WorkspaceComment>}
132
+ * @private
133
+ */
134
+ this.topComments_ = [];
135
+ /**
136
+ * @type {!Object}
137
+ * @private
138
+ */
139
+ this.commentDB_ = Object.create(null);
140
+ /**
141
+ * @type {!Array<!Function>}
142
+ * @private
143
+ */
144
+ this.listeners_ = [];
145
+ /**
146
+ * @type {!Array<!Abstract>}
147
+ * @protected
148
+ */
149
+ this.undoStack_ = [];
150
+ /**
151
+ * @type {!Array<!Abstract>}
152
+ * @protected
153
+ */
154
+ this.redoStack_ = [];
155
+ /**
156
+ * @type {!Object}
157
+ * @private
158
+ */
159
+ this.blockDB_ = Object.create(null);
160
+ /**
161
+ * @type {!Object}
162
+ * @private
163
+ */
164
+ this.typedBlocksDB_ = Object.create(null);
165
+
166
+ /**
167
+ * A map from variable type to list of variable names. The lists contain
168
+ * all of the named variables in the workspace, including variables that are
169
+ * not currently in use.
170
+ * @type {!VariableMap}
171
+ * @private
172
+ */
173
+ this.variableMap_ = new VariableMap(this);
174
+
175
+ /**
176
+ * Blocks in the flyout can refer to variables that don't exist in the main
177
+ * workspace. For instance, the "get item in list" block refers to an
178
+ * "item" variable regardless of whether the variable has been created yet.
179
+ * A FieldVariable must always refer to a VariableModel. We reconcile
180
+ * these by tracking "potential" variables in the flyout. These variables
181
+ * become real when references to them are dragged into the main workspace.
182
+ * @type {?VariableMap}
183
+ * @private
184
+ */
185
+ this.potentialVariableMap_ = null;
186
+ }
187
+
93
188
  /**
94
- * @type {!Object}
95
- * @private
189
+ * Dispose of this workspace.
190
+ * Unlink from all DOM elements to prevent memory leaks.
191
+ * @suppress {checkTypes}
96
192
  */
97
- this.commentDB_ = Object.create(null);
193
+ dispose() {
194
+ this.listeners_.length = 0;
195
+ this.clear();
196
+ // Remove from workspace database.
197
+ delete WorkspaceDB_[this.id];
198
+ }
199
+
98
200
  /**
99
- * @type {!Array<!Function>}
201
+ * Compare function for sorting objects (blocks, comments, etc) by position;
202
+ * top to bottom (with slight LTR or RTL bias).
203
+ * @param {!Block | !WorkspaceComment} a The first object to
204
+ * compare.
205
+ * @param {!Block | !WorkspaceComment} b The second object to
206
+ * compare.
207
+ * @return {number} The comparison value. This tells Array.sort() how to
208
+ * change object a's index.
100
209
  * @private
101
210
  */
102
- this.listeners_ = [];
103
- /**
104
- * @type {!Array<!Abstract>}
105
- * @protected
106
- */
107
- this.undoStack_ = [];
108
- /**
109
- * @type {!Array<!Abstract>}
110
- * @protected
111
- */
112
- this.redoStack_ = [];
211
+ sortObjects_(a, b) {
212
+ const aXY = a.getRelativeToSurfaceXY();
213
+ const bXY = b.getRelativeToSurfaceXY();
214
+ return (aXY.y + Workspace.prototype.sortObjects_.offset * aXY.x) -
215
+ (bXY.y + Workspace.prototype.sortObjects_.offset * bXY.x);
216
+ }
217
+
113
218
  /**
114
- * @type {!Object}
115
- * @private
219
+ * Adds a block to the list of top blocks.
220
+ * @param {!Block} block Block to add.
116
221
  */
117
- this.blockDB_ = Object.create(null);
222
+ addTopBlock(block) {
223
+ this.topBlocks_.push(block);
224
+ }
225
+
118
226
  /**
119
- * @type {!Object}
120
- * @private
227
+ * Removes a block from the list of top blocks.
228
+ * @param {!Block} block Block to remove.
121
229
  */
122
- this.typedBlocksDB_ = Object.create(null);
230
+ removeTopBlock(block) {
231
+ if (!arrayUtils.removeElem(this.topBlocks_, block)) {
232
+ throw Error('Block not present in workspace\'s list of top-most blocks.');
233
+ }
234
+ }
123
235
 
124
236
  /**
125
- * A map from variable type to list of variable names. The lists contain all
126
- * of the named variables in the workspace, including variables
127
- * that are not currently in use.
128
- * @type {!VariableMap}
129
- * @private
237
+ * Finds the top-level blocks and returns them. Blocks are optionally sorted
238
+ * by position; top to bottom (with slight LTR or RTL bias).
239
+ * @param {boolean} ordered Sort the list if true.
240
+ * @return {!Array<!Block>} The top-level block objects.
130
241
  */
131
- this.variableMap_ = new VariableMap(this);
242
+ getTopBlocks(ordered) {
243
+ // Copy the topBlocks_ list.
244
+ const blocks = [].concat(this.topBlocks_);
245
+ if (ordered && blocks.length > 1) {
246
+ this.sortObjects_.offset = Math.sin(math.toRadians(Workspace.SCAN_ANGLE));
247
+ if (this.RTL) {
248
+ this.sortObjects_.offset *= -1;
249
+ }
250
+ blocks.sort(this.sortObjects_);
251
+ }
252
+ return blocks;
253
+ }
132
254
 
133
255
  /**
134
- * Blocks in the flyout can refer to variables that don't exist in the main
135
- * workspace. For instance, the "get item in list" block refers to an "item"
136
- * variable regardless of whether the variable has been created yet.
137
- * A FieldVariable must always refer to a VariableModel. We reconcile
138
- * these by tracking "potential" variables in the flyout. These variables
139
- * become real when references to them are dragged into the main workspace.
140
- * @type {?VariableMap}
141
- * @private
256
+ * Add a block to the list of blocks keyed by type.
257
+ * @param {!Block} block Block to add.
142
258
  */
143
- this.potentialVariableMap_ = null;
144
- };
145
-
146
- /**
147
- * Returns `true` if the workspace is visible and `false` if it's headless.
148
- * @type {boolean}
149
- */
150
- Workspace.prototype.rendered = false;
151
-
152
- /**
153
- * Returns `true` if the workspace is currently in the process of a bulk clear.
154
- * @type {boolean}
155
- * @package
156
- */
157
- Workspace.prototype.isClearing = false;
158
-
159
- /**
160
- * Maximum number of undo events in stack. `0` turns off undo, `Infinity` sets
161
- * it to unlimited.
162
- * @type {number}
163
- */
164
- Workspace.prototype.MAX_UNDO = 1024;
165
-
166
- /**
167
- * Set of databases for rapid lookup of connection locations.
168
- * @type {Array<!ConnectionDB>}
169
- */
170
- Workspace.prototype.connectionDBList = null;
171
-
172
- /**
173
- * Dispose of this workspace.
174
- * Unlink from all DOM elements to prevent memory leaks.
175
- * @suppress {checkTypes}
176
- */
177
- Workspace.prototype.dispose = function() {
178
- this.listeners_.length = 0;
179
- this.clear();
180
- // Remove from workspace database.
181
- delete WorkspaceDB_[this.id];
182
- };
183
-
184
- /**
185
- * Angle away from the horizontal to sweep for blocks. Order of execution is
186
- * generally top to bottom, but a small angle changes the scan to give a bit of
187
- * a left to right bias (reversed in RTL). Units are in degrees.
188
- * See: https://tvtropes.org/pmwiki/pmwiki.php/Main/DiagonalBilling
189
- */
190
- Workspace.SCAN_ANGLE = 3;
191
-
192
- /**
193
- * Compare function for sorting objects (blocks, comments, etc) by position;
194
- * top to bottom (with slight LTR or RTL bias).
195
- * @param {!Block | !WorkspaceComment} a The first object to
196
- * compare.
197
- * @param {!Block | !WorkspaceComment} b The second object to
198
- * compare.
199
- * @return {number} The comparison value. This tells Array.sort() how to change
200
- * object a's index.
201
- * @private
202
- */
203
- Workspace.prototype.sortObjects_ = function(a, b) {
204
- const aXY = a.getRelativeToSurfaceXY();
205
- const bXY = b.getRelativeToSurfaceXY();
206
- return (aXY.y + Workspace.prototype.sortObjects_.offset * aXY.x) -
207
- (bXY.y + Workspace.prototype.sortObjects_.offset * bXY.x);
208
- };
209
-
210
- /**
211
- * Adds a block to the list of top blocks.
212
- * @param {!Block} block Block to add.
213
- */
214
- Workspace.prototype.addTopBlock = function(block) {
215
- this.topBlocks_.push(block);
216
- };
217
-
218
- /**
219
- * Removes a block from the list of top blocks.
220
- * @param {!Block} block Block to remove.
221
- */
222
- Workspace.prototype.removeTopBlock = function(block) {
223
- if (!arrayUtils.removeElem(this.topBlocks_, block)) {
224
- throw Error('Block not present in workspace\'s list of top-most blocks.');
259
+ addTypedBlock(block) {
260
+ if (!this.typedBlocksDB_[block.type]) {
261
+ this.typedBlocksDB_[block.type] = [];
262
+ }
263
+ this.typedBlocksDB_[block.type].push(block);
225
264
  }
226
- };
227
265
 
228
- /**
229
- * Finds the top-level blocks and returns them. Blocks are optionally sorted
230
- * by position; top to bottom (with slight LTR or RTL bias).
231
- * @param {boolean} ordered Sort the list if true.
232
- * @return {!Array<!Block>} The top-level block objects.
233
- */
234
- Workspace.prototype.getTopBlocks = function(ordered) {
235
- // Copy the topBlocks_ list.
236
- const blocks = [].concat(this.topBlocks_);
237
- if (ordered && blocks.length > 1) {
238
- this.sortObjects_.offset = Math.sin(math.toRadians(Workspace.SCAN_ANGLE));
239
- if (this.RTL) {
240
- this.sortObjects_.offset *= -1;
266
+ /**
267
+ * Remove a block from the list of blocks keyed by type.
268
+ * @param {!Block} block Block to remove.
269
+ */
270
+ removeTypedBlock(block) {
271
+ arrayUtils.removeElem(this.typedBlocksDB_[block.type], block);
272
+ if (!this.typedBlocksDB_[block.type].length) {
273
+ delete this.typedBlocksDB_[block.type];
241
274
  }
242
- blocks.sort(this.sortObjects_);
243
275
  }
244
- return blocks;
245
- };
246
276
 
247
- /**
248
- * Add a block to the list of blocks keyed by type.
249
- * @param {!Block} block Block to add.
250
- */
251
- Workspace.prototype.addTypedBlock = function(block) {
252
- if (!this.typedBlocksDB_[block.type]) {
253
- this.typedBlocksDB_[block.type] = [];
254
- }
255
- this.typedBlocksDB_[block.type].push(block);
256
- };
277
+ /**
278
+ * Finds the blocks with the associated type and returns them. Blocks are
279
+ * optionally sorted by position; top to bottom (with slight LTR or RTL bias).
280
+ * @param {string} type The type of block to search for.
281
+ * @param {boolean} ordered Sort the list if true.
282
+ * @return {!Array<!Block>} The blocks of the given type.
283
+ */
284
+ getBlocksByType(type, ordered) {
285
+ if (!this.typedBlocksDB_[type]) {
286
+ return [];
287
+ }
288
+ const blocks = this.typedBlocksDB_[type].slice(0);
289
+ if (ordered && blocks.length > 1) {
290
+ this.sortObjects_.offset = Math.sin(math.toRadians(Workspace.SCAN_ANGLE));
291
+ if (this.RTL) {
292
+ this.sortObjects_.offset *= -1;
293
+ }
294
+ blocks.sort(this.sortObjects_);
295
+ }
257
296
 
258
- /**
259
- * Remove a block from the list of blocks keyed by type.
260
- * @param {!Block} block Block to remove.
261
- */
262
- Workspace.prototype.removeTypedBlock = function(block) {
263
- arrayUtils.removeElem(this.typedBlocksDB_[block.type], block);
264
- if (!this.typedBlocksDB_[block.type].length) {
265
- delete this.typedBlocksDB_[block.type];
297
+ return blocks.filter(function(block) {
298
+ return !block.isInsertionMarker();
299
+ });
266
300
  }
267
- };
268
301
 
269
- /**
270
- * Finds the blocks with the associated type and returns them. Blocks are
271
- * optionally sorted by position; top to bottom (with slight LTR or RTL bias).
272
- * @param {string} type The type of block to search for.
273
- * @param {boolean} ordered Sort the list if true.
274
- * @return {!Array<!Block>} The blocks of the given type.
275
- */
276
- Workspace.prototype.getBlocksByType = function(type, ordered) {
277
- if (!this.typedBlocksDB_[type]) {
278
- return [];
279
- }
280
- const blocks = this.typedBlocksDB_[type].slice(0);
281
- if (ordered && blocks.length > 1) {
282
- this.sortObjects_.offset = Math.sin(math.toRadians(Workspace.SCAN_ANGLE));
283
- if (this.RTL) {
284
- this.sortObjects_.offset *= -1;
302
+ /**
303
+ * Adds a comment to the list of top comments.
304
+ * @param {!WorkspaceComment} comment comment to add.
305
+ * @package
306
+ */
307
+ addTopComment(comment) {
308
+ this.topComments_.push(comment);
309
+
310
+ // Note: If the comment database starts to hold block comments, this may
311
+ // need to move to a separate function.
312
+ if (this.commentDB_[comment.id]) {
313
+ console.warn(
314
+ 'Overriding an existing comment on this workspace, with id "' +
315
+ comment.id + '"');
285
316
  }
286
- blocks.sort(this.sortObjects_);
287
- }
288
-
289
- return blocks.filter(function(block) {
290
- return !block.isInsertionMarker();
291
- });
292
- };
293
-
294
- /**
295
- * Adds a comment to the list of top comments.
296
- * @param {!WorkspaceComment} comment comment to add.
297
- * @package
298
- */
299
- Workspace.prototype.addTopComment = function(comment) {
300
- this.topComments_.push(comment);
301
-
302
- // Note: If the comment database starts to hold block comments, this may need
303
- // to move to a separate function.
304
- if (this.commentDB_[comment.id]) {
305
- console.warn(
306
- 'Overriding an existing comment on this workspace, with id "' +
307
- comment.id + '"');
317
+ this.commentDB_[comment.id] = comment;
308
318
  }
309
- this.commentDB_[comment.id] = comment;
310
- };
311
319
 
312
- /**
313
- * Removes a comment from the list of top comments.
314
- * @param {!WorkspaceComment} comment comment to remove.
315
- * @package
316
- */
317
- Workspace.prototype.removeTopComment = function(comment) {
318
- if (!arrayUtils.removeElem(this.topComments_, comment)) {
319
- throw Error(
320
- 'Comment not present in workspace\'s list of top-most ' +
321
- 'comments.');
320
+ /**
321
+ * Removes a comment from the list of top comments.
322
+ * @param {!WorkspaceComment} comment comment to remove.
323
+ * @package
324
+ */
325
+ removeTopComment(comment) {
326
+ if (!arrayUtils.removeElem(this.topComments_, comment)) {
327
+ throw Error(
328
+ 'Comment not present in workspace\'s list of top-most ' +
329
+ 'comments.');
330
+ }
331
+ // Note: If the comment database starts to hold block comments, this may
332
+ // need to move to a separate function.
333
+ delete this.commentDB_[comment.id];
322
334
  }
323
- // Note: If the comment database starts to hold block comments, this may need
324
- // to move to a separate function.
325
- delete this.commentDB_[comment.id];
326
- };
327
335
 
328
- /**
329
- * Finds the top-level comments and returns them. Comments are optionally
330
- * sorted by position; top to bottom (with slight LTR or RTL bias).
331
- * @param {boolean} ordered Sort the list if true.
332
- * @return {!Array<!WorkspaceComment>} The top-level comment objects.
333
- * @package
334
- */
335
- Workspace.prototype.getTopComments = function(ordered) {
336
- // Copy the topComments_ list.
337
- const comments = [].concat(this.topComments_);
338
- if (ordered && comments.length > 1) {
339
- this.sortObjects_.offset = Math.sin(math.toRadians(Workspace.SCAN_ANGLE));
340
- if (this.RTL) {
341
- this.sortObjects_.offset *= -1;
336
+ /**
337
+ * Finds the top-level comments and returns them. Comments are optionally
338
+ * sorted by position; top to bottom (with slight LTR or RTL bias).
339
+ * @param {boolean} ordered Sort the list if true.
340
+ * @return {!Array<!WorkspaceComment>} The top-level comment objects.
341
+ * @package
342
+ */
343
+ getTopComments(ordered) {
344
+ // Copy the topComments_ list.
345
+ const comments = [].concat(this.topComments_);
346
+ if (ordered && comments.length > 1) {
347
+ this.sortObjects_.offset = Math.sin(math.toRadians(Workspace.SCAN_ANGLE));
348
+ if (this.RTL) {
349
+ this.sortObjects_.offset *= -1;
350
+ }
351
+ comments.sort(this.sortObjects_);
342
352
  }
343
- comments.sort(this.sortObjects_);
353
+ return comments;
344
354
  }
345
- return comments;
346
- };
347
355
 
348
- /**
349
- * Find all blocks in workspace. Blocks are optionally sorted
350
- * by position; top to bottom (with slight LTR or RTL bias).
351
- * @param {boolean} ordered Sort the list if true.
352
- * @return {!Array<!Block>} Array of blocks.
353
- */
354
- Workspace.prototype.getAllBlocks = function(ordered) {
355
- let blocks;
356
- if (ordered) {
357
- // Slow, but ordered.
358
- const topBlocks = this.getTopBlocks(true);
359
- blocks = [];
360
- for (let i = 0; i < topBlocks.length; i++) {
361
- blocks.push.apply(blocks, topBlocks[i].getDescendants(true));
362
- }
363
- } else {
364
- // Fast, but in no particular order.
365
- blocks = this.getTopBlocks(false);
366
- for (let i = 0; i < blocks.length; i++) {
367
- blocks.push.apply(blocks, blocks[i].getChildren(false));
356
+ /**
357
+ * Find all blocks in workspace. Blocks are optionally sorted
358
+ * by position; top to bottom (with slight LTR or RTL bias).
359
+ * @param {boolean} ordered Sort the list if true.
360
+ * @return {!Array<!Block>} Array of blocks.
361
+ */
362
+ getAllBlocks(ordered) {
363
+ let blocks;
364
+ if (ordered) {
365
+ // Slow, but ordered.
366
+ const topBlocks = this.getTopBlocks(true);
367
+ blocks = [];
368
+ for (let i = 0; i < topBlocks.length; i++) {
369
+ blocks.push.apply(blocks, topBlocks[i].getDescendants(true));
370
+ }
371
+ } else {
372
+ // Fast, but in no particular order.
373
+ blocks = this.getTopBlocks(false);
374
+ for (let i = 0; i < blocks.length; i++) {
375
+ blocks.push.apply(blocks, blocks[i].getChildren(false));
376
+ }
368
377
  }
369
- }
370
378
 
371
- // Insertion markers exist on the workspace for rendering reasons, but aren't
372
- // "real" blocks from a developer perspective.
373
- const filtered = blocks.filter(function(block) {
374
- return !block.isInsertionMarker();
375
- });
379
+ // Insertion markers exist on the workspace for rendering reasons, but
380
+ // aren't "real" blocks from a developer perspective.
381
+ const filtered = blocks.filter(function(block) {
382
+ return !block.isInsertionMarker();
383
+ });
376
384
 
377
- return filtered;
378
- };
385
+ return filtered;
386
+ }
379
387
 
380
- /**
381
- * Dispose of all blocks and comments in workspace.
382
- */
383
- Workspace.prototype.clear = function() {
384
- this.isClearing = true;
385
- try {
386
- const existingGroup = eventUtils.getGroup();
387
- if (!existingGroup) {
388
- eventUtils.setGroup(true);
389
- }
390
- while (this.topBlocks_.length) {
391
- this.topBlocks_[0].dispose(false);
392
- }
393
- while (this.topComments_.length) {
394
- this.topComments_[this.topComments_.length - 1].dispose();
395
- }
396
- if (!existingGroup) {
397
- eventUtils.setGroup(false);
398
- }
399
- this.variableMap_.clear();
400
- if (this.potentialVariableMap_) {
401
- this.potentialVariableMap_.clear();
388
+ /**
389
+ * Dispose of all blocks and comments in workspace.
390
+ */
391
+ clear() {
392
+ this.isClearing = true;
393
+ try {
394
+ const existingGroup = eventUtils.getGroup();
395
+ if (!existingGroup) {
396
+ eventUtils.setGroup(true);
397
+ }
398
+ while (this.topBlocks_.length) {
399
+ this.topBlocks_[0].dispose(false);
400
+ }
401
+ while (this.topComments_.length) {
402
+ this.topComments_[this.topComments_.length - 1].dispose();
403
+ }
404
+ if (!existingGroup) {
405
+ eventUtils.setGroup(false);
406
+ }
407
+ this.variableMap_.clear();
408
+ if (this.potentialVariableMap_) {
409
+ this.potentialVariableMap_.clear();
410
+ }
411
+ } finally {
412
+ this.isClearing = false;
402
413
  }
403
- } finally {
404
- this.isClearing = false;
405
414
  }
406
- };
407
415
 
408
- /* Begin functions that are just pass-throughs to the variable map. */
409
- /**
410
- * Rename a variable by updating its name in the variable map. Identify the
411
- * variable to rename with the given ID.
412
- * @param {string} id ID of the variable to rename.
413
- * @param {string} newName New variable name.
414
- */
415
- Workspace.prototype.renameVariableById = function(id, newName) {
416
- this.variableMap_.renameVariableById(id, newName);
417
- };
416
+ /* Begin functions that are just pass-throughs to the variable map. */
418
417
 
419
- /**
420
- * Create a variable with a given name, optional type, and optional ID.
421
- * @param {string} name The name of the variable. This must be unique across
422
- * variables and procedures.
423
- * @param {?string=} opt_type The type of the variable like 'int' or 'string'.
424
- * Does not need to be unique. Field_variable can filter variables based on
425
- * their type. This will default to '' which is a specific type.
426
- * @param {?string=} opt_id The unique ID of the variable. This will default to
427
- * a UUID.
428
- * @return {!VariableModel} The newly created variable.
429
- */
430
- Workspace.prototype.createVariable = function(name, opt_type, opt_id) {
431
- return this.variableMap_.createVariable(name, opt_type, opt_id);
432
- };
418
+ /**
419
+ * Rename a variable by updating its name in the variable map. Identify the
420
+ * variable to rename with the given ID.
421
+ * @param {string} id ID of the variable to rename.
422
+ * @param {string} newName New variable name.
423
+ */
424
+ renameVariableById(id, newName) {
425
+ this.variableMap_.renameVariableById(id, newName);
426
+ }
433
427
 
434
- /**
435
- * Find all the uses of the given variable, which is identified by ID.
436
- * @param {string} id ID of the variable to find.
437
- * @return {!Array<!Block>} Array of block usages.
438
- */
439
- Workspace.prototype.getVariableUsesById = function(id) {
440
- return this.variableMap_.getVariableUsesById(id);
441
- };
428
+ /**
429
+ * Create a variable with a given name, optional type, and optional ID.
430
+ * @param {string} name The name of the variable. This must be unique across
431
+ * variables and procedures.
432
+ * @param {?string=} opt_type The type of the variable like 'int' or 'string'.
433
+ * Does not need to be unique. Field_variable can filter variables based
434
+ * on their type. This will default to '' which is a specific type.
435
+ * @param {?string=} opt_id The unique ID of the variable. This will default
436
+ * to a UUID.
437
+ * @return {!VariableModel} The newly created variable.
438
+ */
439
+ createVariable(name, opt_type, opt_id) {
440
+ return this.variableMap_.createVariable(name, opt_type, opt_id);
441
+ }
442
442
 
443
- /**
444
- * Delete a variables by the passed in ID and all of its uses from this
445
- * workspace. May prompt the user for confirmation.
446
- * @param {string} id ID of variable to delete.
447
- */
448
- Workspace.prototype.deleteVariableById = function(id) {
449
- this.variableMap_.deleteVariableById(id);
450
- };
443
+ /**
444
+ * Find all the uses of the given variable, which is identified by ID.
445
+ * @param {string} id ID of the variable to find.
446
+ * @return {!Array<!Block>} Array of block usages.
447
+ */
448
+ getVariableUsesById(id) {
449
+ return this.variableMap_.getVariableUsesById(id);
450
+ }
451
451
 
452
- /**
453
- * Find the variable by the given name and return it. Return null if not found.
454
- * @param {string} name The name to check for.
455
- * @param {string=} opt_type The type of the variable. If not provided it
456
- * defaults to the empty string, which is a specific type.
457
- * @return {?VariableModel} The variable with the given name.
458
- */
459
- // TODO (#1559): Possibly delete this function after resolving #1559.
460
- Workspace.prototype.getVariable = function(name, opt_type) {
461
- return this.variableMap_.getVariable(name, opt_type);
462
- };
452
+ /**
453
+ * Delete a variables by the passed in ID and all of its uses from this
454
+ * workspace. May prompt the user for confirmation.
455
+ * @param {string} id ID of variable to delete.
456
+ */
457
+ deleteVariableById(id) {
458
+ this.variableMap_.deleteVariableById(id);
459
+ }
463
460
 
464
- /**
465
- * Find the variable by the given ID and return it. Return null if not found.
466
- * @param {string} id The ID to check for.
467
- * @return {?VariableModel} The variable with the given ID.
468
- */
469
- Workspace.prototype.getVariableById = function(id) {
470
- return this.variableMap_.getVariableById(id);
471
- };
461
+ /**
462
+ * Find the variable by the given name and return it. Return null if not
463
+ * found.
464
+ * @param {string} name The name to check for.
465
+ * @param {string=} opt_type The type of the variable. If not provided it
466
+ * defaults to the empty string, which is a specific type.
467
+ * @return {?VariableModel} The variable with the given name.
468
+ */
469
+ getVariable(name, opt_type) {
470
+ // TODO (#1559): Possibly delete this function after resolving #1559.
471
+ return this.variableMap_.getVariable(name, opt_type);
472
+ }
472
473
 
473
- /**
474
- * Find the variable with the specified type. If type is null, return list of
475
- * variables with empty string type.
476
- * @param {?string} type Type of the variables to find.
477
- * @return {!Array<!VariableModel>} The sought after variables of the
478
- * passed in type. An empty array if none are found.
479
- */
480
- Workspace.prototype.getVariablesOfType = function(type) {
481
- return this.variableMap_.getVariablesOfType(type);
482
- };
474
+ /**
475
+ * Find the variable by the given ID and return it. Return null if not found.
476
+ * @param {string} id The ID to check for.
477
+ * @return {?VariableModel} The variable with the given ID.
478
+ */
479
+ getVariableById(id) {
480
+ return this.variableMap_.getVariableById(id);
481
+ }
483
482
 
484
- /**
485
- * Return all variable types.
486
- * @return {!Array<string>} List of variable types.
487
- * @package
488
- */
489
- Workspace.prototype.getVariableTypes = function() {
490
- return this.variableMap_.getVariableTypes(this);
491
- };
483
+ /**
484
+ * Find the variable with the specified type. If type is null, return list of
485
+ * variables with empty string type.
486
+ * @param {?string} type Type of the variables to find.
487
+ * @return {!Array<!VariableModel>} The sought after variables of the
488
+ * passed in type. An empty array if none are found.
489
+ */
490
+ getVariablesOfType(type) {
491
+ return this.variableMap_.getVariablesOfType(type);
492
+ }
492
493
 
493
- /**
494
- * Return all variables of all types.
495
- * @return {!Array<!VariableModel>} List of variable models.
496
- */
497
- Workspace.prototype.getAllVariables = function() {
498
- return this.variableMap_.getAllVariables();
499
- };
494
+ /**
495
+ * Return all variable types.
496
+ * @return {!Array<string>} List of variable types.
497
+ * @package
498
+ */
499
+ getVariableTypes() {
500
+ return this.variableMap_.getVariableTypes(this);
501
+ }
500
502
 
501
- /**
502
- * Returns all variable names of all types.
503
- * @return {!Array<string>} List of all variable names of all types.
504
- */
505
- Workspace.prototype.getAllVariableNames = function() {
506
- return this.variableMap_.getAllVariableNames();
507
- };
503
+ /**
504
+ * Return all variables of all types.
505
+ * @return {!Array<!VariableModel>} List of variable models.
506
+ */
507
+ getAllVariables() {
508
+ return this.variableMap_.getAllVariables();
509
+ }
508
510
 
509
- /* End functions that are just pass-throughs to the variable map. */
511
+ /**
512
+ * Returns all variable names of all types.
513
+ * @return {!Array<string>} List of all variable names of all types.
514
+ */
515
+ getAllVariableNames() {
516
+ return this.variableMap_.getAllVariableNames();
517
+ }
510
518
 
511
- /**
512
- * Returns the horizontal offset of the workspace.
513
- * Intended for LTR/RTL compatibility in XML.
514
- * Not relevant for a headless workspace.
515
- * @return {number} Width.
516
- */
517
- Workspace.prototype.getWidth = function() {
518
- return 0;
519
- };
519
+ /* End functions that are just pass-throughs to the variable map. */
520
520
 
521
- /**
522
- * Obtain a newly created block.
523
- * @param {!string} prototypeName Name of the language object containing
524
- * type-specific functions for this block.
525
- * @param {string=} opt_id Optional ID. Use this ID if provided, otherwise
526
- * create a new ID.
527
- * @return {!Block} The created block.
528
- */
529
- Workspace.prototype.newBlock = function(prototypeName, opt_id) {
530
- const {Block} = goog.module.get('Blockly.Block');
531
- return new Block(this, prototypeName, opt_id);
532
- };
521
+ /**
522
+ * Returns the horizontal offset of the workspace.
523
+ * Intended for LTR/RTL compatibility in XML.
524
+ * Not relevant for a headless workspace.
525
+ * @return {number} Width.
526
+ */
527
+ getWidth() {
528
+ return 0;
529
+ }
533
530
 
534
- /**
535
- * The number of blocks that may be added to the workspace before reaching
536
- * the maxBlocks.
537
- * @return {number} Number of blocks left.
538
- */
539
- Workspace.prototype.remainingCapacity = function() {
540
- if (isNaN(this.options.maxBlocks)) {
541
- return Infinity;
531
+ /**
532
+ * Obtain a newly created block.
533
+ * @param {!string} prototypeName Name of the language object containing
534
+ * type-specific functions for this block.
535
+ * @param {string=} opt_id Optional ID. Use this ID if provided, otherwise
536
+ * create a new ID.
537
+ * @return {!Block} The created block.
538
+ */
539
+ newBlock(prototypeName, opt_id) {
540
+ const {Block} = goog.module.get('Blockly.Block');
541
+ return new Block(this, prototypeName, opt_id);
542
542
  }
543
543
 
544
- return this.options.maxBlocks - this.getAllBlocks(false).length;
545
- };
544
+ /**
545
+ * The number of blocks that may be added to the workspace before reaching
546
+ * the maxBlocks.
547
+ * @return {number} Number of blocks left.
548
+ */
549
+ remainingCapacity() {
550
+ if (isNaN(this.options.maxBlocks)) {
551
+ return Infinity;
552
+ }
546
553
 
547
- /**
548
- * The number of blocks of the given type that may be added to the workspace
549
- * before reaching the maxInstances allowed for that type.
550
- * @param {string} type Type of block to return capacity for.
551
- * @return {number} Number of blocks of type left.
552
- */
553
- Workspace.prototype.remainingCapacityOfType = function(type) {
554
- if (!this.options.maxInstances) {
555
- return Infinity;
554
+ return this.options.maxBlocks - this.getAllBlocks(false).length;
556
555
  }
557
556
 
558
- const maxInstanceOfType = (this.options.maxInstances[type] !== undefined) ?
559
- this.options.maxInstances[type] :
560
- Infinity;
557
+ /**
558
+ * The number of blocks of the given type that may be added to the workspace
559
+ * before reaching the maxInstances allowed for that type.
560
+ * @param {string} type Type of block to return capacity for.
561
+ * @return {number} Number of blocks of type left.
562
+ */
563
+ remainingCapacityOfType(type) {
564
+ if (!this.options.maxInstances) {
565
+ return Infinity;
566
+ }
561
567
 
562
- return maxInstanceOfType - this.getBlocksByType(type, false).length;
563
- };
568
+ const maxInstanceOfType = (this.options.maxInstances[type] !== undefined) ?
569
+ this.options.maxInstances[type] :
570
+ Infinity;
564
571
 
565
- /**
566
- * Check if there is remaining capacity for blocks of the given counts to be
567
- * created. If the total number of blocks represented by the map is more than
568
- * the total remaining capacity, it returns false. If a type count is more
569
- * than the remaining capacity for that type, it returns false.
570
- * @param {!Object} typeCountsMap A map of types to counts (usually representing
571
- * blocks to be created).
572
- * @return {boolean} True if there is capacity for the given map,
573
- * false otherwise.
574
- */
575
- Workspace.prototype.isCapacityAvailable = function(typeCountsMap) {
576
- if (!this.hasBlockLimits()) {
577
- return true;
572
+ return maxInstanceOfType - this.getBlocksByType(type, false).length;
578
573
  }
579
- let copyableBlocksCount = 0;
580
- for (const type in typeCountsMap) {
581
- if (typeCountsMap[type] > this.remainingCapacityOfType(type)) {
574
+
575
+ /**
576
+ * Check if there is remaining capacity for blocks of the given counts to be
577
+ * created. If the total number of blocks represented by the map is more
578
+ * than the total remaining capacity, it returns false. If a type count is
579
+ * more than the remaining capacity for that type, it returns false.
580
+ * @param {!Object} typeCountsMap A map of types to counts (usually
581
+ * representing
582
+ * blocks to be created).
583
+ * @return {boolean} True if there is capacity for the given map,
584
+ * false otherwise.
585
+ */
586
+ isCapacityAvailable(typeCountsMap) {
587
+ if (!this.hasBlockLimits()) {
588
+ return true;
589
+ }
590
+ let copyableBlocksCount = 0;
591
+ for (const type in typeCountsMap) {
592
+ if (typeCountsMap[type] > this.remainingCapacityOfType(type)) {
593
+ return false;
594
+ }
595
+ copyableBlocksCount += typeCountsMap[type];
596
+ }
597
+ if (copyableBlocksCount > this.remainingCapacity()) {
582
598
  return false;
583
599
  }
584
- copyableBlocksCount += typeCountsMap[type];
585
- }
586
- if (copyableBlocksCount > this.remainingCapacity()) {
587
- return false;
600
+ return true;
588
601
  }
589
- return true;
590
- };
591
602
 
592
- /**
593
- * Checks if the workspace has any limits on the maximum number of blocks,
594
- * or the maximum number of blocks of specific types.
595
- * @return {boolean} True if it has block limits, false otherwise.
596
- */
597
- Workspace.prototype.hasBlockLimits = function() {
598
- return this.options.maxBlocks !== Infinity || !!this.options.maxInstances;
599
- };
603
+ /**
604
+ * Checks if the workspace has any limits on the maximum number of blocks,
605
+ * or the maximum number of blocks of specific types.
606
+ * @return {boolean} True if it has block limits, false otherwise.
607
+ */
608
+ hasBlockLimits() {
609
+ return this.options.maxBlocks !== Infinity || !!this.options.maxInstances;
610
+ }
600
611
 
601
- /**
602
- * Gets the undo stack for workplace.
603
- * @return {!Array<!Abstract>} undo stack
604
- * @package
605
- */
606
- Workspace.prototype.getUndoStack = function() {
607
- return this.undoStack_;
608
- };
612
+ /**
613
+ * Gets the undo stack for workplace.
614
+ * @return {!Array<!Abstract>} undo stack
615
+ * @package
616
+ */
617
+ getUndoStack() {
618
+ return this.undoStack_;
619
+ }
609
620
 
610
- /**
611
- * Gets the redo stack for workplace.
612
- * @return {!Array<!Abstract>} redo stack
613
- * @package
614
- */
615
- Workspace.prototype.getRedoStack = function() {
616
- return this.redoStack_;
617
- };
621
+ /**
622
+ * Gets the redo stack for workplace.
623
+ * @return {!Array<!Abstract>} redo stack
624
+ * @package
625
+ */
626
+ getRedoStack() {
627
+ return this.redoStack_;
628
+ }
618
629
 
619
- /**
620
- * Undo or redo the previous action.
621
- * @param {boolean} redo False if undo, true if redo.
622
- */
623
- Workspace.prototype.undo = function(redo) {
624
- const inputStack = redo ? this.redoStack_ : this.undoStack_;
625
- const outputStack = redo ? this.undoStack_ : this.redoStack_;
626
- const inputEvent = inputStack.pop();
627
- if (!inputEvent) {
628
- return;
629
- }
630
- let events = [inputEvent];
631
- // Do another undo/redo if the next one is of the same group.
632
- while (inputStack.length && inputEvent.group &&
633
- inputEvent.group === inputStack[inputStack.length - 1].group) {
634
- events.push(inputStack.pop());
635
- }
636
- // Push these popped events on the opposite stack.
637
- for (let i = 0; i < events.length; i++) {
638
- const event = events[i];
639
- outputStack.push(event);
640
- }
641
- events = eventUtils.filter(events, redo);
642
- eventUtils.setRecordUndo(false);
643
- try {
630
+ /**
631
+ * Undo or redo the previous action.
632
+ * @param {boolean} redo False if undo, true if redo.
633
+ */
634
+ undo(redo) {
635
+ const inputStack = redo ? this.redoStack_ : this.undoStack_;
636
+ const outputStack = redo ? this.undoStack_ : this.redoStack_;
637
+ const inputEvent = inputStack.pop();
638
+ if (!inputEvent) {
639
+ return;
640
+ }
641
+ let events = [inputEvent];
642
+ // Do another undo/redo if the next one is of the same group.
643
+ while (inputStack.length && inputEvent.group &&
644
+ inputEvent.group === inputStack[inputStack.length - 1].group) {
645
+ events.push(inputStack.pop());
646
+ }
647
+ // Push these popped events on the opposite stack.
644
648
  for (let i = 0; i < events.length; i++) {
645
649
  const event = events[i];
646
- event.run(redo);
650
+ outputStack.push(event);
651
+ }
652
+ events = eventUtils.filter(events, redo);
653
+ eventUtils.setRecordUndo(false);
654
+ try {
655
+ for (let i = 0; i < events.length; i++) {
656
+ const event = events[i];
657
+ event.run(redo);
658
+ }
659
+ } finally {
660
+ eventUtils.setRecordUndo(true);
647
661
  }
648
- } finally {
649
- eventUtils.setRecordUndo(true);
650
662
  }
651
- };
652
663
 
653
- /**
654
- * Clear the undo/redo stacks.
655
- */
656
- Workspace.prototype.clearUndo = function() {
657
- this.undoStack_.length = 0;
658
- this.redoStack_.length = 0;
659
- // Stop any events already in the firing queue from being undoable.
660
- eventUtils.clearPendingUndo();
661
- };
664
+ /**
665
+ * Clear the undo/redo stacks.
666
+ */
667
+ clearUndo() {
668
+ this.undoStack_.length = 0;
669
+ this.redoStack_.length = 0;
670
+ // Stop any events already in the firing queue from being undoable.
671
+ eventUtils.clearPendingUndo();
672
+ }
662
673
 
663
- /**
664
- * When something in this workspace changes, call a function.
665
- * Note that there may be a few recent events already on the stack. Thus the
666
- * new change listener might be called with events that occurred a few
667
- * milliseconds before the change listener was added.
668
- * @param {!Function} func Function to call.
669
- * @return {!Function} Obsolete return value, ignore.
670
- */
671
- Workspace.prototype.addChangeListener = function(func) {
672
- this.listeners_.push(func);
673
- return func;
674
- };
674
+ /**
675
+ * When something in this workspace changes, call a function.
676
+ * Note that there may be a few recent events already on the stack. Thus the
677
+ * new change listener might be called with events that occurred a few
678
+ * milliseconds before the change listener was added.
679
+ * @param {!Function} func Function to call.
680
+ * @return {!Function} Obsolete return value, ignore.
681
+ */
682
+ addChangeListener(func) {
683
+ this.listeners_.push(func);
684
+ return func;
685
+ }
675
686
 
676
- /**
677
- * Stop listening for this workspace's changes.
678
- * @param {!Function} func Function to stop calling.
679
- */
680
- Workspace.prototype.removeChangeListener = function(func) {
681
- arrayUtils.removeElem(this.listeners_, func);
682
- };
687
+ /**
688
+ * Stop listening for this workspace's changes.
689
+ * @param {!Function} func Function to stop calling.
690
+ */
691
+ removeChangeListener(func) {
692
+ arrayUtils.removeElem(this.listeners_, func);
693
+ }
683
694
 
684
- /**
685
- * Fire a change event.
686
- * @param {!Abstract} event Event to fire.
687
- */
688
- Workspace.prototype.fireChangeListener = function(event) {
689
- if (event.recordUndo) {
690
- this.undoStack_.push(event);
691
- this.redoStack_.length = 0;
692
- while (this.undoStack_.length > this.MAX_UNDO && this.MAX_UNDO >= 0) {
693
- this.undoStack_.shift();
695
+ /**
696
+ * Fire a change event.
697
+ * @param {!Abstract} event Event to fire.
698
+ */
699
+ fireChangeListener(event) {
700
+ if (event.recordUndo) {
701
+ this.undoStack_.push(event);
702
+ this.redoStack_.length = 0;
703
+ while (this.undoStack_.length > this.MAX_UNDO && this.MAX_UNDO >= 0) {
704
+ this.undoStack_.shift();
705
+ }
706
+ }
707
+ for (let i = 0; i < this.listeners_.length; i++) {
708
+ const func = this.listeners_[i];
709
+ func(event);
694
710
  }
695
711
  }
696
- for (let i = 0; i < this.listeners_.length; i++) {
697
- const func = this.listeners_[i];
698
- func(event);
699
- }
700
- };
701
712
 
702
- /**
703
- * Find the block on this workspace with the specified ID.
704
- * @param {string} id ID of block to find.
705
- * @return {?Block} The sought after block, or null if not found.
706
- */
707
- Workspace.prototype.getBlockById = function(id) {
708
- return this.blockDB_[id] || null;
709
- };
713
+ /**
714
+ * Find the block on this workspace with the specified ID.
715
+ * @param {string} id ID of block to find.
716
+ * @return {?Block} The sought after block, or null if not found.
717
+ */
718
+ getBlockById(id) {
719
+ return this.blockDB_[id] || null;
720
+ }
710
721
 
711
- /**
712
- * Set a block on this workspace with the specified ID.
713
- * @param {string} id ID of block to set.
714
- * @param {Block} block The block to set.
715
- * @package
716
- */
717
- Workspace.prototype.setBlockById = function(id, block) {
718
- this.blockDB_[id] = block;
719
- };
722
+ /**
723
+ * Set a block on this workspace with the specified ID.
724
+ * @param {string} id ID of block to set.
725
+ * @param {Block} block The block to set.
726
+ * @package
727
+ */
728
+ setBlockById(id, block) {
729
+ this.blockDB_[id] = block;
730
+ }
720
731
 
721
- /**
722
- * Delete a block off this workspace with the specified ID.
723
- * @param {string} id ID of block to delete.
724
- * @package
725
- */
726
- Workspace.prototype.removeBlockById = function(id) {
727
- delete this.blockDB_[id];
728
- };
732
+ /**
733
+ * Delete a block off this workspace with the specified ID.
734
+ * @param {string} id ID of block to delete.
735
+ * @package
736
+ */
737
+ removeBlockById(id) {
738
+ delete this.blockDB_[id];
739
+ }
729
740
 
730
- /**
731
- * Find the comment on this workspace with the specified ID.
732
- * @param {string} id ID of comment to find.
733
- * @return {?WorkspaceComment} The sought after comment, or null if not
734
- * found.
735
- * @package
736
- */
737
- Workspace.prototype.getCommentById = function(id) {
738
- return this.commentDB_[id] || null;
739
- };
741
+ /**
742
+ * Find the comment on this workspace with the specified ID.
743
+ * @param {string} id ID of comment to find.
744
+ * @return {?WorkspaceComment} The sought after comment, or null if not
745
+ * found.
746
+ * @package
747
+ */
748
+ getCommentById(id) {
749
+ return this.commentDB_[id] || null;
750
+ }
740
751
 
741
- /**
742
- * Checks whether all value and statement inputs in the workspace are filled
743
- * with blocks.
744
- * @param {boolean=} opt_shadowBlocksAreFilled An optional argument controlling
745
- * whether shadow blocks are counted as filled. Defaults to true.
746
- * @return {boolean} True if all inputs are filled, false otherwise.
747
- */
748
- Workspace.prototype.allInputsFilled = function(opt_shadowBlocksAreFilled) {
749
- const blocks = this.getTopBlocks(false);
750
- for (let i = 0; i < blocks.length; i++) {
751
- const block = blocks[i];
752
- if (!block.allInputsFilled(opt_shadowBlocksAreFilled)) {
753
- return false;
752
+ /**
753
+ * Checks whether all value and statement inputs in the workspace are filled
754
+ * with blocks.
755
+ * @param {boolean=} opt_shadowBlocksAreFilled An optional argument
756
+ * controlling whether shadow blocks are counted as filled. Defaults to
757
+ * true.
758
+ * @return {boolean} True if all inputs are filled, false otherwise.
759
+ */
760
+ allInputsFilled(opt_shadowBlocksAreFilled) {
761
+ const blocks = this.getTopBlocks(false);
762
+ for (let i = 0; i < blocks.length; i++) {
763
+ const block = blocks[i];
764
+ if (!block.allInputsFilled(opt_shadowBlocksAreFilled)) {
765
+ return false;
766
+ }
754
767
  }
768
+ return true;
755
769
  }
756
- return true;
757
- };
758
770
 
759
- /**
760
- * Return the variable map that contains "potential" variables.
761
- * These exist in the flyout but not in the workspace.
762
- * @return {?VariableMap} The potential variable map.
763
- * @package
764
- */
765
- Workspace.prototype.getPotentialVariableMap = function() {
766
- return this.potentialVariableMap_;
767
- };
771
+ /**
772
+ * Return the variable map that contains "potential" variables.
773
+ * These exist in the flyout but not in the workspace.
774
+ * @return {?VariableMap} The potential variable map.
775
+ * @package
776
+ */
777
+ getPotentialVariableMap() {
778
+ return this.potentialVariableMap_;
779
+ }
768
780
 
769
- /**
770
- * Create and store the potential variable map for this workspace.
771
- * @package
772
- */
773
- Workspace.prototype.createPotentialVariableMap = function() {
774
- this.potentialVariableMap_ = new VariableMap(this);
775
- };
781
+ /**
782
+ * Create and store the potential variable map for this workspace.
783
+ * @package
784
+ */
785
+ createPotentialVariableMap() {
786
+ this.potentialVariableMap_ = new VariableMap(this);
787
+ }
776
788
 
777
- /**
778
- * Return the map of all variables on the workspace.
779
- * @return {!VariableMap} The variable map.
780
- */
781
- Workspace.prototype.getVariableMap = function() {
782
- return this.variableMap_;
783
- };
789
+ /**
790
+ * Return the map of all variables on the workspace.
791
+ * @return {!VariableMap} The variable map.
792
+ */
793
+ getVariableMap() {
794
+ return this.variableMap_;
795
+ }
784
796
 
785
- /**
786
- * Set the map of all variables on the workspace.
787
- * @param {!VariableMap} variableMap The variable map.
788
- * @package
789
- */
790
- Workspace.prototype.setVariableMap = function(variableMap) {
791
- this.variableMap_ = variableMap;
792
- };
797
+ /**
798
+ * Set the map of all variables on the workspace.
799
+ * @param {!VariableMap} variableMap The variable map.
800
+ * @package
801
+ */
802
+ setVariableMap(variableMap) {
803
+ this.variableMap_ = variableMap;
804
+ }
793
805
 
794
- /**
795
- * Find the workspace with the specified ID.
796
- * @param {string} id ID of workspace to find.
797
- * @return {?Workspace} The sought after workspace or null if not found.
798
- */
799
- Workspace.getById = function(id) {
800
- return WorkspaceDB_[id] || null;
801
- };
806
+ /**
807
+ * Find the workspace with the specified ID.
808
+ * @param {string} id ID of workspace to find.
809
+ * @return {?Workspace} The sought after workspace or null if not found.
810
+ */
811
+ static getById(id) {
812
+ return WorkspaceDB_[id] || null;
813
+ }
814
+
815
+ /**
816
+ * Find all workspaces.
817
+ * @return {!Array<!Workspace>} Array of workspaces.
818
+ */
819
+ static getAll() {
820
+ const workspaces = [];
821
+ for (const workspaceId in WorkspaceDB_) {
822
+ workspaces.push(WorkspaceDB_[workspaceId]);
823
+ }
824
+ return workspaces;
825
+ }
826
+ }
802
827
 
803
828
  /**
804
- * Find all workspaces.
805
- * @return {!Array<!Workspace>} Array of workspaces.
829
+ * Angle away from the horizontal to sweep for blocks. Order of execution is
830
+ * generally top to bottom, but a small angle changes the scan to give a bit of
831
+ * a left to right bias (reversed in RTL). Units are in degrees.
832
+ * See: https://tvtropes.org/pmwiki/pmwiki.php/Main/DiagonalBilling
806
833
  */
807
- Workspace.getAll = function() {
808
- const workspaces = [];
809
- for (const workspaceId in WorkspaceDB_) {
810
- workspaces.push(WorkspaceDB_[workspaceId]);
811
- }
812
- return workspaces;
813
- };
834
+ Workspace.SCAN_ANGLE = 3;
814
835
 
815
836
  exports.Workspace = Workspace;