blockly 7.20211209.4 → 8.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blockly.d.ts +18963 -18432
- package/blockly.min.js +5 -4
- package/blockly_compressed.js +4 -3
- package/blockly_compressed.js.map +1 -1
- package/blocks/blocks.js +47 -0
- package/blocks/colour.js +13 -3
- package/blocks/lists.js +22 -13
- package/blocks/logic.js +13 -3
- package/blocks/loops.js +24 -11
- package/blocks/math.js +12 -3
- package/blocks/procedures.js +45 -32
- package/blocks/text.js +22 -13
- package/blocks/variables.js +14 -3
- package/blocks/variables_dynamic.js +13 -3
- package/blocks_compressed.js +1 -1
- package/blocks_compressed.js.map +1 -1
- package/core/block.js +1869 -1814
- package/core/block_drag_surface.js +201 -200
- package/core/block_dragger.js +377 -373
- package/core/block_svg.js +1593 -1479
- package/core/blockly.js +8 -22
- package/core/blocks.js +9 -2
- package/core/browser_events.js +22 -5
- package/core/bubble.js +841 -797
- package/core/bubble_dragger.js +213 -206
- package/core/bump_objects.js +2 -2
- package/core/clipboard.js +9 -9
- package/core/comment.js +353 -332
- package/core/common.js +46 -17
- package/core/component_manager.js +181 -174
- package/core/config.js +87 -0
- package/core/connection.js +595 -584
- package/core/connection_checker.js +242 -244
- package/core/connection_db.js +235 -230
- package/core/contextmenu.js +9 -6
- package/core/contextmenu_items.js +1 -2
- package/core/contextmenu_registry.js +93 -89
- package/core/css.js +474 -474
- package/core/delete_area.js +45 -42
- package/core/drag_target.js +57 -56
- package/core/dropdowndiv.js +153 -163
- package/core/events/events.js +2 -2
- package/core/events/events_abstract.js +89 -77
- package/core/events/events_block_base.js +37 -36
- package/core/events/events_block_change.js +130 -124
- package/core/events/events_block_create.js +73 -71
- package/core/events/events_block_delete.js +84 -82
- package/core/events/events_block_drag.js +50 -49
- package/core/events/events_block_move.js +147 -140
- package/core/events/events_bubble_open.js +51 -50
- package/core/events/events_click.js +48 -44
- package/core/events/events_comment_base.js +72 -69
- package/core/events/events_comment_change.js +63 -61
- package/core/events/events_comment_create.js +44 -42
- package/core/events/events_comment_delete.js +42 -40
- package/core/events/events_comment_move.js +106 -104
- package/core/events/events_marker_move.js +65 -64
- package/core/events/events_selected.js +46 -45
- package/core/events/events_theme_change.js +36 -35
- package/core/events/events_toolbox_item_select.js +46 -45
- package/core/events/events_trashcan_open.js +37 -36
- package/core/events/events_ui.js +47 -46
- package/core/events/events_ui_base.js +30 -29
- package/core/events/events_var_base.js +37 -36
- package/core/events/events_var_create.js +50 -48
- package/core/events/events_var_delete.js +50 -48
- package/core/events/events_var_rename.js +51 -49
- package/core/events/events_viewport.js +66 -65
- package/core/events/utils.js +29 -14
- package/core/events/workspace_events.js +49 -55
- package/core/extensions.js +4 -3
- package/core/field.js +1061 -997
- package/core/field_angle.js +462 -442
- package/core/field_checkbox.js +194 -182
- package/core/field_colour.js +519 -505
- package/core/field_dropdown.js +617 -598
- package/core/field_image.js +229 -220
- package/core/field_label.js +102 -91
- package/core/field_label_serializable.js +42 -41
- package/core/field_multilineinput.js +372 -358
- package/core/field_number.js +272 -253
- package/core/field_textinput.js +499 -467
- package/core/field_variable.js +458 -420
- package/core/flyout_base.js +1005 -952
- package/core/flyout_button.js +277 -260
- package/core/flyout_horizontal.js +304 -302
- package/core/flyout_metrics_manager.js +64 -64
- package/core/flyout_vertical.js +306 -300
- package/core/generator.js +459 -446
- package/core/gesture.js +829 -813
- package/core/grid.js +166 -163
- package/core/icon.js +168 -159
- package/core/inject.js +7 -5
- package/core/input.js +257 -248
- package/core/insertion_marker_manager.js +655 -624
- package/core/internal_constants.js +0 -129
- package/core/keyboard_nav/ast_node.js +605 -596
- package/core/keyboard_nav/basic_cursor.js +166 -165
- package/core/keyboard_nav/cursor.js +99 -97
- package/core/keyboard_nav/marker.js +83 -79
- package/core/keyboard_nav/tab_navigate_cursor.js +18 -23
- package/core/marker_manager.js +153 -141
- package/core/menu.js +377 -372
- package/core/menuitem.js +223 -217
- package/core/metrics_manager.js +403 -390
- package/core/mutator.js +468 -437
- package/core/names.js +229 -188
- package/core/options.js +290 -284
- package/core/procedures.js +29 -17
- package/core/registry.js +19 -16
- package/core/rendered_connection.js +482 -463
- package/core/renderers/common/block_rendering.js +9 -3
- package/core/renderers/common/constants.js +1119 -1112
- package/core/renderers/common/debug.js +14 -0
- package/core/renderers/common/debugger.js +338 -316
- package/core/renderers/common/drawer.js +380 -370
- package/core/renderers/common/i_path_object.js +2 -2
- package/core/renderers/common/info.js +626 -618
- package/core/renderers/common/marker_svg.js +579 -541
- package/core/renderers/common/path_object.js +203 -200
- package/core/renderers/common/renderer.js +220 -218
- package/core/renderers/geras/constants.js +36 -36
- package/core/renderers/geras/drawer.js +155 -147
- package/core/renderers/geras/highlight_constants.js +244 -238
- package/core/renderers/geras/highlighter.js +231 -179
- package/core/renderers/geras/info.js +392 -369
- package/core/renderers/geras/measurables/inline_input.js +25 -19
- package/core/renderers/geras/measurables/statement_input.js +23 -17
- package/core/renderers/geras/path_object.js +106 -121
- package/core/renderers/geras/renderer.js +96 -98
- package/core/renderers/measurables/base.js +30 -18
- package/core/renderers/measurables/bottom_row.js +83 -80
- package/core/renderers/measurables/connection.js +22 -15
- package/core/renderers/measurables/external_value_input.js +35 -22
- package/core/renderers/measurables/field.js +35 -20
- package/core/renderers/measurables/hat.js +18 -13
- package/core/renderers/measurables/icon.js +24 -17
- package/core/renderers/measurables/in_row_spacer.js +15 -13
- package/core/renderers/measurables/inline_input.js +43 -33
- package/core/renderers/measurables/input_connection.js +41 -28
- package/core/renderers/measurables/input_row.js +50 -44
- package/core/renderers/measurables/jagged_edge.js +14 -12
- package/core/renderers/measurables/next_connection.js +16 -14
- package/core/renderers/measurables/output_connection.js +26 -20
- package/core/renderers/measurables/previous_connection.js +16 -15
- package/core/renderers/measurables/round_corner.js +20 -18
- package/core/renderers/measurables/row.js +184 -168
- package/core/renderers/measurables/spacer_row.js +38 -23
- package/core/renderers/measurables/square_corner.js +18 -16
- package/core/renderers/measurables/statement_input.js +23 -20
- package/core/renderers/measurables/top_row.js +88 -85
- package/core/renderers/minimalist/constants.js +8 -7
- package/core/renderers/minimalist/drawer.js +11 -10
- package/core/renderers/minimalist/info.js +18 -18
- package/core/renderers/minimalist/renderer.js +40 -39
- package/core/renderers/thrasos/info.js +258 -248
- package/core/renderers/thrasos/renderer.js +20 -20
- package/core/renderers/zelos/constants.js +898 -873
- package/core/renderers/zelos/drawer.js +186 -169
- package/core/renderers/zelos/info.js +502 -479
- package/core/renderers/zelos/marker_svg.js +129 -115
- package/core/renderers/zelos/measurables/bottom_row.js +31 -30
- package/core/renderers/zelos/measurables/inputs.js +22 -21
- package/core/renderers/zelos/measurables/row_elements.js +14 -13
- package/core/renderers/zelos/measurables/top_row.js +34 -33
- package/core/renderers/zelos/path_object.js +181 -180
- package/core/renderers/zelos/renderer.js +91 -92
- package/core/scrollbar.js +759 -713
- package/core/scrollbar_pair.js +250 -245
- package/core/serialization/blocks.js +26 -10
- package/core/serialization/workspaces.js +3 -2
- package/core/shortcut_registry.js +286 -277
- package/core/sprites.js +31 -0
- package/core/theme.js +135 -141
- package/core/theme_manager.js +147 -143
- package/core/toolbox/category.js +602 -576
- package/core/toolbox/collapsible_category.js +226 -227
- package/core/toolbox/separator.js +70 -61
- package/core/toolbox/toolbox.js +934 -927
- package/core/toolbox/toolbox_item.js +115 -99
- package/core/tooltip.js +108 -35
- package/core/touch.js +8 -3
- package/core/touch_gesture.js +254 -251
- package/core/trashcan.js +606 -595
- package/core/utils/coordinate.js +97 -95
- package/core/utils/dom.js +2 -2
- package/core/utils/global.js +2 -0
- package/core/utils/rect.js +41 -37
- package/core/utils/sentinel.js +25 -0
- package/core/utils/size.js +30 -27
- package/core/utils/svg.js +18 -16
- package/core/variable_map.js +325 -341
- package/core/variable_model.js +55 -54
- package/core/variables.js +9 -2
- package/core/variables_dynamic.js +3 -1
- package/core/warning.js +126 -120
- package/core/widgetdiv.js +4 -4
- package/core/workspace.js +685 -664
- package/core/workspace_audio.js +124 -118
- package/core/workspace_comment.js +308 -298
- package/core/workspace_comment_svg.js +1029 -951
- package/core/workspace_drag_surface_svg.js +147 -140
- package/core/workspace_dragger.js +70 -71
- package/core/workspace_svg.js +2322 -2297
- package/core/xml.js +30 -20
- package/core/zoom_controls.js +431 -439
- package/generators/dart/colour.js +56 -64
- package/generators/dart/lists.js +61 -50
- package/generators/dart/math.js +160 -148
- package/generators/dart/text.js +83 -61
- package/generators/javascript/colour.js +37 -34
- package/generators/javascript/lists.js +50 -43
- package/generators/javascript/math.js +123 -139
- package/generators/javascript/text.js +67 -81
- package/generators/lua/colour.js +25 -23
- package/generators/lua/lists.js +97 -69
- package/generators/lua/logic.js +1 -2
- package/generators/lua/math.js +182 -144
- package/generators/lua/text.js +116 -99
- package/generators/php/colour.js +38 -32
- package/generators/php/lists.js +109 -89
- package/generators/php/math.js +90 -81
- package/generators/php/text.js +63 -61
- package/generators/python/colour.js +18 -18
- package/generators/python/lists.js +38 -30
- package/generators/python/loops.js +12 -8
- package/generators/python/math.js +104 -106
- package/generators/python/text.js +34 -30
- package/msg/smn.js +436 -0
- package/package.json +7 -6
- package/blocks/all.js +0 -23
package/core/block_svg.js
CHANGED
|
@@ -25,13 +25,15 @@ const constants = goog.require('Blockly.constants');
|
|
|
25
25
|
const dom = goog.require('Blockly.utils.dom');
|
|
26
26
|
const eventUtils = goog.require('Blockly.Events.utils');
|
|
27
27
|
const internalConstants = goog.require('Blockly.internalConstants');
|
|
28
|
-
const object = goog.require('Blockly.utils.object');
|
|
29
28
|
const svgMath = goog.require('Blockly.utils.svgMath');
|
|
30
29
|
const userAgent = goog.require('Blockly.utils.userAgent');
|
|
31
30
|
const {ASTNode} = goog.require('Blockly.ASTNode');
|
|
32
31
|
const {Block} = goog.require('Blockly.Block');
|
|
33
32
|
/* eslint-disable-next-line no-unused-vars */
|
|
33
|
+
const {BlockMove} = goog.requireType('Blockly.Events.BlockMove');
|
|
34
|
+
/* eslint-disable-next-line no-unused-vars */
|
|
34
35
|
const {Comment} = goog.requireType('Blockly.Comment');
|
|
36
|
+
const {config} = goog.require('Blockly.config');
|
|
35
37
|
const {ConnectionType} = goog.require('Blockly.ConnectionType');
|
|
36
38
|
/* eslint-disable-next-line no-unused-vars */
|
|
37
39
|
const {Connection} = goog.requireType('Blockly.Connection');
|
|
@@ -81,1700 +83,1812 @@ goog.require('Blockly.Touch');
|
|
|
81
83
|
/**
|
|
82
84
|
* Class for a block's SVG representation.
|
|
83
85
|
* Not normally called directly, workspace.newBlock() is preferred.
|
|
84
|
-
* @param {!WorkspaceSvg} workspace The block's workspace.
|
|
85
|
-
* @param {?string} prototypeName Name of the language object containing
|
|
86
|
-
* type-specific functions for this block.
|
|
87
|
-
* @param {string=} opt_id Optional ID. Use this ID if provided, otherwise
|
|
88
|
-
* create a new ID.
|
|
89
86
|
* @extends {Block}
|
|
90
87
|
* @implements {IASTNodeLocationSvg}
|
|
91
88
|
* @implements {IBoundedElement}
|
|
92
89
|
* @implements {ICopyable}
|
|
93
90
|
* @implements {IDraggable}
|
|
94
|
-
* @constructor
|
|
95
91
|
* @alias Blockly.BlockSvg
|
|
96
92
|
*/
|
|
97
|
-
|
|
98
|
-
// Create core elements for the block.
|
|
93
|
+
class BlockSvg extends Block {
|
|
99
94
|
/**
|
|
100
|
-
* @
|
|
101
|
-
* @
|
|
95
|
+
* @param {!WorkspaceSvg} workspace The block's workspace.
|
|
96
|
+
* @param {string} prototypeName Name of the language object containing
|
|
97
|
+
* type-specific functions for this block.
|
|
98
|
+
* @param {string=} opt_id Optional ID. Use this ID if provided, otherwise
|
|
99
|
+
* create a new ID.
|
|
102
100
|
*/
|
|
103
|
-
|
|
104
|
-
|
|
101
|
+
constructor(workspace, prototypeName, opt_id) {
|
|
102
|
+
super(workspace, prototypeName, opt_id);
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* An optional method called when a mutator dialog is first opened.
|
|
106
|
+
* This function must create and initialize a top-level block for the
|
|
107
|
+
* mutator dialog, and return it. This function should also populate this
|
|
108
|
+
* top-level block with any sub-blocks which are appropriate. This method
|
|
109
|
+
* must also be coupled with defining a `compose` method for the default
|
|
110
|
+
* mutation dialog button and UI to appear.
|
|
111
|
+
* @type {undefined|?function(WorkspaceSvg):!BlockSvg}
|
|
112
|
+
*/
|
|
113
|
+
this.decompose = this.decompose;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* An optional method called when a mutator dialog saves its content.
|
|
117
|
+
* This function is called to modify the original block according to new
|
|
118
|
+
* settings. This method must also be coupled with defining a `decompose`
|
|
119
|
+
* method for the default mutation dialog button and UI to appear.
|
|
120
|
+
* @type {undefined|?function(!BlockSvg)}
|
|
121
|
+
*/
|
|
122
|
+
this.compose = this.compose;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* An optional method called by the default mutator UI which gives the block
|
|
126
|
+
* a chance to save information about what child blocks are connected to
|
|
127
|
+
* what mutated connections.
|
|
128
|
+
* @type {undefined|?function(!BlockSvg)}
|
|
129
|
+
*/
|
|
130
|
+
this.saveConnections = this.saveConnections;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* An optional method for defining custom block context menu items.
|
|
134
|
+
* @type {undefined|?function(!Array<!Object>)}
|
|
135
|
+
*/
|
|
136
|
+
this.customContextMenu = this.customContextMenu;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* An property used internally to reference the block's rendering debugger.
|
|
140
|
+
* @type {?BlockRenderingDebug}
|
|
141
|
+
* @package
|
|
142
|
+
*/
|
|
143
|
+
this.renderingDebugger = null;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Height of this block, not including any statement blocks above or below.
|
|
147
|
+
* Height is in workspace units.
|
|
148
|
+
* @type {number}
|
|
149
|
+
*/
|
|
150
|
+
this.height = 0;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Width of this block, including any connected value blocks.
|
|
154
|
+
* Width is in workspace units.
|
|
155
|
+
* @type {number}
|
|
156
|
+
*/
|
|
157
|
+
this.width = 0;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Map from IDs for warnings text to PIDs of functions to apply them.
|
|
161
|
+
* Used to be able to maintain multiple warnings.
|
|
162
|
+
* @type {Object<string, number>}
|
|
163
|
+
* @private
|
|
164
|
+
*/
|
|
165
|
+
this.warningTextDb_ = null;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Block's mutator icon (if any).
|
|
169
|
+
* @type {?Mutator}
|
|
170
|
+
*/
|
|
171
|
+
this.mutator = null;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Block's comment icon (if any).
|
|
175
|
+
* @type {?Comment}
|
|
176
|
+
* @deprecated August 2019. Use getCommentIcon instead.
|
|
177
|
+
*/
|
|
178
|
+
this.comment = null;
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Block's comment icon (if any).
|
|
182
|
+
* @type {?Comment}
|
|
183
|
+
* @private
|
|
184
|
+
*/
|
|
185
|
+
this.commentIcon_ = null;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Block's warning icon (if any).
|
|
189
|
+
* @type {?Warning}
|
|
190
|
+
*/
|
|
191
|
+
this.warning = null;
|
|
192
|
+
|
|
193
|
+
// Create core elements for the block.
|
|
194
|
+
/**
|
|
195
|
+
* @type {!SVGGElement}
|
|
196
|
+
* @private
|
|
197
|
+
*/
|
|
198
|
+
this.svgGroup_ = dom.createSvgElement(Svg.G, {}, null);
|
|
199
|
+
this.svgGroup_.translate_ = '';
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* A block style object.
|
|
203
|
+
* @type {!Theme.BlockStyle}
|
|
204
|
+
*/
|
|
205
|
+
this.style = workspace.getRenderer().getConstants().getBlockStyle(null);
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* The renderer's path object.
|
|
209
|
+
* @type {IPathObject}
|
|
210
|
+
* @package
|
|
211
|
+
*/
|
|
212
|
+
this.pathObject =
|
|
213
|
+
workspace.getRenderer().makePathObject(this.svgGroup_, this.style);
|
|
214
|
+
|
|
215
|
+
/** @type {boolean} */
|
|
216
|
+
this.rendered = false;
|
|
217
|
+
/**
|
|
218
|
+
* Is this block currently rendering? Used to stop recursive render calls
|
|
219
|
+
* from actually triggering a re-render.
|
|
220
|
+
* @type {boolean}
|
|
221
|
+
* @private
|
|
222
|
+
*/
|
|
223
|
+
this.renderIsInProgress_ = false;
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Whether mousedown events have been bound yet.
|
|
227
|
+
* @type {boolean}
|
|
228
|
+
* @private
|
|
229
|
+
*/
|
|
230
|
+
this.eventsInit_ = false;
|
|
231
|
+
|
|
232
|
+
/** @type {!WorkspaceSvg} */
|
|
233
|
+
this.workspace;
|
|
234
|
+
/** @type {RenderedConnection} */
|
|
235
|
+
this.outputConnection;
|
|
236
|
+
/** @type {RenderedConnection} */
|
|
237
|
+
this.nextConnection;
|
|
238
|
+
/** @type {RenderedConnection} */
|
|
239
|
+
this.previousConnection;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Whether to move the block to the drag surface when it is dragged.
|
|
243
|
+
* True if it should move, false if it should be translated directly.
|
|
244
|
+
* @type {boolean}
|
|
245
|
+
* @private
|
|
246
|
+
*/
|
|
247
|
+
this.useDragSurface_ =
|
|
248
|
+
svgMath.is3dSupported() && !!workspace.getBlockDragSurface();
|
|
249
|
+
|
|
250
|
+
const svgPath = this.pathObject.svgPath;
|
|
251
|
+
svgPath.tooltip = this;
|
|
252
|
+
Tooltip.bindMouseEvents(svgPath);
|
|
253
|
+
|
|
254
|
+
// Expose this block's ID on its top-level SVG group.
|
|
255
|
+
if (this.svgGroup_.dataset) {
|
|
256
|
+
this.svgGroup_.dataset['id'] = this.id;
|
|
257
|
+
} else if (userAgent.IE) {
|
|
258
|
+
// SVGElement.dataset is not available on IE11, but data-* properties
|
|
259
|
+
// can be set with setAttribute().
|
|
260
|
+
this.svgGroup_.setAttribute('data-id', this.id);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
this.doInit_();
|
|
264
|
+
}
|
|
105
265
|
|
|
106
266
|
/**
|
|
107
|
-
*
|
|
108
|
-
*
|
|
267
|
+
* Create and initialize the SVG representation of the block.
|
|
268
|
+
* May be called more than once.
|
|
109
269
|
*/
|
|
110
|
-
|
|
270
|
+
initSvg() {
|
|
271
|
+
if (!this.workspace.rendered) {
|
|
272
|
+
throw TypeError('Workspace is headless.');
|
|
273
|
+
}
|
|
274
|
+
for (let i = 0, input; (input = this.inputList[i]); i++) {
|
|
275
|
+
input.init();
|
|
276
|
+
}
|
|
277
|
+
const icons = this.getIcons();
|
|
278
|
+
for (let i = 0; i < icons.length; i++) {
|
|
279
|
+
icons[i].createIcon();
|
|
280
|
+
}
|
|
281
|
+
this.applyColour();
|
|
282
|
+
this.pathObject.updateMovable(this.isMovable());
|
|
283
|
+
const svg = this.getSvgRoot();
|
|
284
|
+
if (!this.workspace.options.readOnly && !this.eventsInit_ && svg) {
|
|
285
|
+
browserEvents.conditionalBind(svg, 'mousedown', this, this.onMouseDown_);
|
|
286
|
+
}
|
|
287
|
+
this.eventsInit_ = true;
|
|
288
|
+
|
|
289
|
+
if (!svg.parentNode) {
|
|
290
|
+
this.workspace.getCanvas().appendChild(svg);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
111
293
|
|
|
112
294
|
/**
|
|
113
|
-
*
|
|
114
|
-
* @
|
|
115
|
-
* @package
|
|
295
|
+
* Get the secondary colour of a block.
|
|
296
|
+
* @return {?string} #RRGGBB string.
|
|
116
297
|
*/
|
|
117
|
-
|
|
118
|
-
|
|
298
|
+
getColourSecondary() {
|
|
299
|
+
return this.style.colourSecondary;
|
|
300
|
+
}
|
|
119
301
|
|
|
120
|
-
/** @type {boolean} */
|
|
121
|
-
this.rendered = false;
|
|
122
302
|
/**
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
* @type {boolean}
|
|
126
|
-
* @private
|
|
303
|
+
* Get the tertiary colour of a block.
|
|
304
|
+
* @return {?string} #RRGGBB string.
|
|
127
305
|
*/
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
/** @type {!WorkspaceSvg} */
|
|
132
|
-
this.workspace = workspace;
|
|
133
|
-
|
|
134
|
-
/** @type {RenderedConnection} */
|
|
135
|
-
this.outputConnection = null;
|
|
136
|
-
/** @type {RenderedConnection} */
|
|
137
|
-
this.nextConnection = null;
|
|
138
|
-
/** @type {RenderedConnection} */
|
|
139
|
-
this.previousConnection = null;
|
|
306
|
+
getColourTertiary() {
|
|
307
|
+
return this.style.colourTertiary;
|
|
308
|
+
}
|
|
140
309
|
|
|
141
310
|
/**
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* @type {boolean}
|
|
145
|
-
* @private
|
|
311
|
+
* Selects this block. Highlights the block visually and fires a select event
|
|
312
|
+
* if the block is not already selected.
|
|
146
313
|
*/
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
314
|
+
select() {
|
|
315
|
+
if (this.isShadow() && this.getParent()) {
|
|
316
|
+
// Shadow blocks should not be selected.
|
|
317
|
+
this.getParent().select();
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (common.getSelected() === this) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
let oldId = null;
|
|
324
|
+
if (common.getSelected()) {
|
|
325
|
+
oldId = common.getSelected().id;
|
|
326
|
+
// Unselect any previously selected block.
|
|
327
|
+
eventUtils.disable();
|
|
328
|
+
try {
|
|
329
|
+
common.getSelected().unselect();
|
|
330
|
+
} finally {
|
|
331
|
+
eventUtils.enable();
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
const event = new (eventUtils.get(eventUtils.SELECTED))(
|
|
335
|
+
oldId, this.id, this.workspace.id);
|
|
336
|
+
eventUtils.fire(event);
|
|
337
|
+
common.setSelected(this);
|
|
338
|
+
this.addSelect();
|
|
162
339
|
}
|
|
163
|
-
};
|
|
164
|
-
object.inherits(BlockSvg, Block);
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Height of this block, not including any statement blocks above or below.
|
|
168
|
-
* Height is in workspace units.
|
|
169
|
-
*/
|
|
170
|
-
BlockSvg.prototype.height = 0;
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Width of this block, including any connected value blocks.
|
|
174
|
-
* Width is in workspace units.
|
|
175
|
-
*/
|
|
176
|
-
BlockSvg.prototype.width = 0;
|
|
177
340
|
|
|
178
|
-
/**
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
341
|
+
/**
|
|
342
|
+
* Unselects this block. Unhighlights the block and fires a select (false)
|
|
343
|
+
* event if the block is currently selected.
|
|
344
|
+
*/
|
|
345
|
+
unselect() {
|
|
346
|
+
if (common.getSelected() !== this) {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
const event = new (eventUtils.get(eventUtils.SELECTED))(
|
|
350
|
+
this.id, null, this.workspace.id);
|
|
351
|
+
event.workspaceId = this.workspace.id;
|
|
352
|
+
eventUtils.fire(event);
|
|
353
|
+
common.setSelected(null);
|
|
354
|
+
this.removeSelect();
|
|
355
|
+
}
|
|
185
356
|
|
|
186
|
-
/**
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
357
|
+
/**
|
|
358
|
+
* Returns a list of mutator, comment, and warning icons.
|
|
359
|
+
* @return {!Array<!Icon>} List of icons.
|
|
360
|
+
*/
|
|
361
|
+
getIcons() {
|
|
362
|
+
const icons = [];
|
|
363
|
+
if (this.mutator) {
|
|
364
|
+
icons.push(this.mutator);
|
|
365
|
+
}
|
|
366
|
+
if (this.commentIcon_) {
|
|
367
|
+
icons.push(this.commentIcon_);
|
|
368
|
+
}
|
|
369
|
+
if (this.warning) {
|
|
370
|
+
icons.push(this.warning);
|
|
371
|
+
}
|
|
372
|
+
return icons;
|
|
373
|
+
}
|
|
192
374
|
|
|
193
|
-
/**
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
375
|
+
/**
|
|
376
|
+
* Sets the parent of this block to be a new block or null.
|
|
377
|
+
* @param {?Block} newParent New parent block.
|
|
378
|
+
* @package
|
|
379
|
+
* @override
|
|
380
|
+
*/
|
|
381
|
+
setParent(newParent) {
|
|
382
|
+
const oldParent = this.parentBlock_;
|
|
383
|
+
if (newParent === oldParent) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
201
386
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
* dialog, and return it. This function should also populate this top-level
|
|
206
|
-
* block with any sub-blocks which are appropriate. This method must also be
|
|
207
|
-
* coupled with defining a `compose` method for the default mutation dialog
|
|
208
|
-
* button and UI to appear.
|
|
209
|
-
* @type {?function(WorkspaceSvg):!BlockSvg}
|
|
210
|
-
*/
|
|
211
|
-
BlockSvg.prototype.decompose;
|
|
387
|
+
dom.startTextWidthCache();
|
|
388
|
+
super.setParent(newParent);
|
|
389
|
+
dom.stopTextWidthCache();
|
|
212
390
|
|
|
213
|
-
|
|
214
|
-
* An optional method called when a mutator dialog saves its content.
|
|
215
|
-
* This function is called to modify the original block according to new
|
|
216
|
-
* settings. This method must also be coupled with defining a `decompose`
|
|
217
|
-
* method for the default mutation dialog button and UI to appear.
|
|
218
|
-
* @type {?function(!BlockSvg)}
|
|
219
|
-
*/
|
|
220
|
-
BlockSvg.prototype.compose;
|
|
391
|
+
const svgRoot = this.getSvgRoot();
|
|
221
392
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
393
|
+
// Bail early if workspace is clearing, or we aren't rendered.
|
|
394
|
+
// We won't need to reattach ourselves anywhere.
|
|
395
|
+
if (this.workspace.isClearing || !svgRoot) {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
227
398
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
399
|
+
const oldXY = this.getRelativeToSurfaceXY();
|
|
400
|
+
if (newParent) {
|
|
401
|
+
(/** @type {!BlockSvg} */ (newParent)).getSvgRoot().appendChild(svgRoot);
|
|
402
|
+
const newXY = this.getRelativeToSurfaceXY();
|
|
403
|
+
// Move the connections to match the child's new position.
|
|
404
|
+
this.moveConnections(newXY.x - oldXY.x, newXY.y - oldXY.y);
|
|
405
|
+
} else if (oldParent) {
|
|
406
|
+
// If we are losing a parent, we want to move our DOM element to the
|
|
407
|
+
// root of the workspace.
|
|
408
|
+
this.workspace.getCanvas().appendChild(svgRoot);
|
|
409
|
+
this.translate(oldXY.x, oldXY.y);
|
|
410
|
+
}
|
|
234
411
|
|
|
235
|
-
|
|
236
|
-
* Create and initialize the SVG representation of the block.
|
|
237
|
-
* May be called more than once.
|
|
238
|
-
*/
|
|
239
|
-
BlockSvg.prototype.initSvg = function() {
|
|
240
|
-
if (!this.workspace.rendered) {
|
|
241
|
-
throw TypeError('Workspace is headless.');
|
|
242
|
-
}
|
|
243
|
-
for (let i = 0, input; (input = this.inputList[i]); i++) {
|
|
244
|
-
input.init();
|
|
245
|
-
}
|
|
246
|
-
const icons = this.getIcons();
|
|
247
|
-
for (let i = 0; i < icons.length; i++) {
|
|
248
|
-
icons[i].createIcon();
|
|
249
|
-
}
|
|
250
|
-
this.applyColour();
|
|
251
|
-
this.pathObject.updateMovable(this.isMovable());
|
|
252
|
-
const svg = this.getSvgRoot();
|
|
253
|
-
if (!this.workspace.options.readOnly && !this.eventsInit_ && svg) {
|
|
254
|
-
browserEvents.conditionalBind(svg, 'mousedown', this, this.onMouseDown_);
|
|
412
|
+
this.applyColour();
|
|
255
413
|
}
|
|
256
|
-
this.eventsInit_ = true;
|
|
257
414
|
|
|
258
|
-
|
|
259
|
-
|
|
415
|
+
/**
|
|
416
|
+
* Return the coordinates of the top-left corner of this block relative to the
|
|
417
|
+
* drawing surface's origin (0,0), in workspace units.
|
|
418
|
+
* If the block is on the workspace, (0, 0) is the origin of the workspace
|
|
419
|
+
* coordinate system.
|
|
420
|
+
* This does not change with workspace scale.
|
|
421
|
+
* @return {!Coordinate} Object with .x and .y properties in
|
|
422
|
+
* workspace coordinates.
|
|
423
|
+
*/
|
|
424
|
+
getRelativeToSurfaceXY() {
|
|
425
|
+
let x = 0;
|
|
426
|
+
let y = 0;
|
|
427
|
+
|
|
428
|
+
const dragSurfaceGroup = this.useDragSurface_ ?
|
|
429
|
+
this.workspace.getBlockDragSurface().getGroup() :
|
|
430
|
+
null;
|
|
431
|
+
|
|
432
|
+
let element = this.getSvgRoot();
|
|
433
|
+
if (element) {
|
|
434
|
+
do {
|
|
435
|
+
// Loop through this block and every parent.
|
|
436
|
+
const xy = svgMath.getRelativeXY(element);
|
|
437
|
+
x += xy.x;
|
|
438
|
+
y += xy.y;
|
|
439
|
+
// If this element is the current element on the drag surface, include
|
|
440
|
+
// the translation of the drag surface itself.
|
|
441
|
+
if (this.useDragSurface_ &&
|
|
442
|
+
this.workspace.getBlockDragSurface().getCurrentBlock() ===
|
|
443
|
+
element) {
|
|
444
|
+
const surfaceTranslation =
|
|
445
|
+
this.workspace.getBlockDragSurface().getSurfaceTranslation();
|
|
446
|
+
x += surfaceTranslation.x;
|
|
447
|
+
y += surfaceTranslation.y;
|
|
448
|
+
}
|
|
449
|
+
element = /** @type {!SVGElement} */ (element.parentNode);
|
|
450
|
+
} while (element && element !== this.workspace.getCanvas() &&
|
|
451
|
+
element !== dragSurfaceGroup);
|
|
452
|
+
}
|
|
453
|
+
return new Coordinate(x, y);
|
|
260
454
|
}
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Get the secondary colour of a block.
|
|
265
|
-
* @return {?string} #RRGGBB string.
|
|
266
|
-
*/
|
|
267
|
-
BlockSvg.prototype.getColourSecondary = function() {
|
|
268
|
-
return this.style.colourSecondary;
|
|
269
|
-
};
|
|
270
455
|
|
|
271
|
-
/**
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
// Shadow blocks should not be selected.
|
|
286
|
-
this.getParent().select();
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
if (common.getSelected() === this) {
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
let oldId = null;
|
|
293
|
-
if (common.getSelected()) {
|
|
294
|
-
oldId = common.getSelected().id;
|
|
295
|
-
// Unselect any previously selected block.
|
|
296
|
-
eventUtils.disable();
|
|
297
|
-
try {
|
|
298
|
-
common.getSelected().unselect();
|
|
299
|
-
} finally {
|
|
300
|
-
eventUtils.enable();
|
|
456
|
+
/**
|
|
457
|
+
* Move a block by a relative offset.
|
|
458
|
+
* @param {number} dx Horizontal offset in workspace units.
|
|
459
|
+
* @param {number} dy Vertical offset in workspace units.
|
|
460
|
+
*/
|
|
461
|
+
moveBy(dx, dy) {
|
|
462
|
+
if (this.parentBlock_) {
|
|
463
|
+
throw Error('Block has parent.');
|
|
464
|
+
}
|
|
465
|
+
const eventsEnabled = eventUtils.isEnabled();
|
|
466
|
+
let event;
|
|
467
|
+
if (eventsEnabled) {
|
|
468
|
+
event = /** @type {!BlockMove} */
|
|
469
|
+
(new (eventUtils.get(eventUtils.BLOCK_MOVE))(this));
|
|
301
470
|
}
|
|
471
|
+
const xy = this.getRelativeToSurfaceXY();
|
|
472
|
+
this.translate(xy.x + dx, xy.y + dy);
|
|
473
|
+
this.moveConnections(dx, dy);
|
|
474
|
+
if (eventsEnabled) {
|
|
475
|
+
event.recordNew();
|
|
476
|
+
eventUtils.fire(event);
|
|
477
|
+
}
|
|
478
|
+
this.workspace.resizeContents();
|
|
302
479
|
}
|
|
303
|
-
const event = new (eventUtils.get(eventUtils.SELECTED))(
|
|
304
|
-
oldId, this.id, this.workspace.id);
|
|
305
|
-
eventUtils.fire(event);
|
|
306
|
-
common.setSelected(this);
|
|
307
|
-
this.addSelect();
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Unselects this block. Unhighlights the block and fires a select (false) event
|
|
312
|
-
* if the block is currently selected.
|
|
313
|
-
*/
|
|
314
|
-
BlockSvg.prototype.unselect = function() {
|
|
315
|
-
if (common.getSelected() !== this) {
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
318
|
-
const event = new (eventUtils.get(eventUtils.SELECTED))(
|
|
319
|
-
this.id, null, this.workspace.id);
|
|
320
|
-
event.workspaceId = this.workspace.id;
|
|
321
|
-
eventUtils.fire(event);
|
|
322
|
-
common.setSelected(null);
|
|
323
|
-
this.removeSelect();
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* Block's mutator icon (if any).
|
|
328
|
-
* @type {?Mutator}
|
|
329
|
-
*/
|
|
330
|
-
BlockSvg.prototype.mutator = null;
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Block's comment icon (if any).
|
|
334
|
-
* @type {?Comment}
|
|
335
|
-
* @deprecated August 2019. Use getCommentIcon instead.
|
|
336
|
-
*/
|
|
337
|
-
BlockSvg.prototype.comment = null;
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* Block's comment icon (if any).
|
|
341
|
-
* @type {?Comment}
|
|
342
|
-
* @private
|
|
343
|
-
*/
|
|
344
|
-
BlockSvg.prototype.commentIcon_ = null;
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* Block's warning icon (if any).
|
|
348
|
-
* @type {?Warning}
|
|
349
|
-
*/
|
|
350
|
-
BlockSvg.prototype.warning = null;
|
|
351
480
|
|
|
352
|
-
/**
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
if (this.commentIcon_) {
|
|
362
|
-
icons.push(this.commentIcon_);
|
|
363
|
-
}
|
|
364
|
-
if (this.warning) {
|
|
365
|
-
icons.push(this.warning);
|
|
481
|
+
/**
|
|
482
|
+
* Transforms a block by setting the translation on the transform attribute
|
|
483
|
+
* of the block's SVG.
|
|
484
|
+
* @param {number} x The x coordinate of the translation in workspace units.
|
|
485
|
+
* @param {number} y The y coordinate of the translation in workspace units.
|
|
486
|
+
*/
|
|
487
|
+
translate(x, y) {
|
|
488
|
+
this.getSvgRoot().setAttribute(
|
|
489
|
+
'transform', 'translate(' + x + ',' + y + ')');
|
|
366
490
|
}
|
|
367
|
-
return icons;
|
|
368
|
-
};
|
|
369
491
|
|
|
370
|
-
/**
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
492
|
+
/**
|
|
493
|
+
* Move this block to its workspace's drag surface, accounting for
|
|
494
|
+
* positioning. Generally should be called at the same time as
|
|
495
|
+
* setDragging_(true). Does nothing if useDragSurface_ is false.
|
|
496
|
+
* @package
|
|
497
|
+
*/
|
|
498
|
+
moveToDragSurface() {
|
|
499
|
+
if (!this.useDragSurface_) {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
// The translation for drag surface blocks,
|
|
503
|
+
// is equal to the current relative-to-surface position,
|
|
504
|
+
// to keep the position in sync as it move on/off the surface.
|
|
505
|
+
// This is in workspace coordinates.
|
|
506
|
+
const xy = this.getRelativeToSurfaceXY();
|
|
507
|
+
this.clearTransformAttributes_();
|
|
508
|
+
this.workspace.getBlockDragSurface().translateSurface(xy.x, xy.y);
|
|
509
|
+
// Execute the move on the top-level SVG component
|
|
510
|
+
const svg = this.getSvgRoot();
|
|
511
|
+
if (svg) {
|
|
512
|
+
this.workspace.getBlockDragSurface().setBlocksAndShow(svg);
|
|
513
|
+
}
|
|
380
514
|
}
|
|
381
515
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
// We won't need to reattach ourselves anywhere.
|
|
390
|
-
if (this.workspace.isClearing || !svgRoot) {
|
|
391
|
-
return;
|
|
516
|
+
/**
|
|
517
|
+
* Move a block to a position.
|
|
518
|
+
* @param {Coordinate} xy The position to move to in workspace units.
|
|
519
|
+
*/
|
|
520
|
+
moveTo(xy) {
|
|
521
|
+
const curXY = this.getRelativeToSurfaceXY();
|
|
522
|
+
this.moveBy(xy.x - curXY.x, xy.y - curXY.y);
|
|
392
523
|
}
|
|
393
524
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
this.
|
|
404
|
-
|
|
525
|
+
/**
|
|
526
|
+
* Move this block back to the workspace block canvas.
|
|
527
|
+
* Generally should be called at the same time as setDragging_(false).
|
|
528
|
+
* Does nothing if useDragSurface_ is false.
|
|
529
|
+
* @param {!Coordinate} newXY The position the block should take on
|
|
530
|
+
* on the workspace canvas, in workspace coordinates.
|
|
531
|
+
* @package
|
|
532
|
+
*/
|
|
533
|
+
moveOffDragSurface(newXY) {
|
|
534
|
+
if (!this.useDragSurface_) {
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
// Translate to current position, turning off 3d.
|
|
538
|
+
this.translate(newXY.x, newXY.y);
|
|
539
|
+
this.workspace.getBlockDragSurface().clearAndHide(
|
|
540
|
+
this.workspace.getCanvas());
|
|
405
541
|
}
|
|
406
542
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
this.workspace.getBlockDragSurface().getGroup() :
|
|
425
|
-
null;
|
|
426
|
-
|
|
427
|
-
let element = this.getSvgRoot();
|
|
428
|
-
if (element) {
|
|
429
|
-
do {
|
|
430
|
-
// Loop through this block and every parent.
|
|
431
|
-
const xy = svgMath.getRelativeXY(element);
|
|
432
|
-
x += xy.x;
|
|
433
|
-
y += xy.y;
|
|
434
|
-
// If this element is the current element on the drag surface, include
|
|
435
|
-
// the translation of the drag surface itself.
|
|
436
|
-
if (this.useDragSurface_ &&
|
|
437
|
-
this.workspace.getBlockDragSurface().getCurrentBlock() === element) {
|
|
438
|
-
const surfaceTranslation =
|
|
439
|
-
this.workspace.getBlockDragSurface().getSurfaceTranslation();
|
|
440
|
-
x += surfaceTranslation.x;
|
|
441
|
-
y += surfaceTranslation.y;
|
|
442
|
-
}
|
|
443
|
-
element = /** @type {!SVGElement} */ (element.parentNode);
|
|
444
|
-
} while (element && element !== this.workspace.getCanvas() &&
|
|
445
|
-
element !== dragSurfaceGroup);
|
|
543
|
+
/**
|
|
544
|
+
* Move this block during a drag, taking into account whether we are using a
|
|
545
|
+
* drag surface to translate blocks.
|
|
546
|
+
* This block must be a top-level block.
|
|
547
|
+
* @param {!Coordinate} newLoc The location to translate to, in
|
|
548
|
+
* workspace coordinates.
|
|
549
|
+
* @package
|
|
550
|
+
*/
|
|
551
|
+
moveDuringDrag(newLoc) {
|
|
552
|
+
if (this.useDragSurface_) {
|
|
553
|
+
this.workspace.getBlockDragSurface().translateSurface(newLoc.x, newLoc.y);
|
|
554
|
+
} else {
|
|
555
|
+
this.svgGroup_.translate_ =
|
|
556
|
+
'translate(' + newLoc.x + ',' + newLoc.y + ')';
|
|
557
|
+
this.svgGroup_.setAttribute(
|
|
558
|
+
'transform', this.svgGroup_.translate_ + this.svgGroup_.skew_);
|
|
559
|
+
}
|
|
446
560
|
}
|
|
447
|
-
return new Coordinate(x, y);
|
|
448
|
-
};
|
|
449
561
|
|
|
450
|
-
/**
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
throw Error('Block has parent.');
|
|
458
|
-
}
|
|
459
|
-
const eventsEnabled = eventUtils.isEnabled();
|
|
460
|
-
let event;
|
|
461
|
-
if (eventsEnabled) {
|
|
462
|
-
event = new (eventUtils.get(eventUtils.BLOCK_MOVE))(this);
|
|
463
|
-
}
|
|
464
|
-
const xy = this.getRelativeToSurfaceXY();
|
|
465
|
-
this.translate(xy.x + dx, xy.y + dy);
|
|
466
|
-
this.moveConnections(dx, dy);
|
|
467
|
-
if (eventsEnabled) {
|
|
468
|
-
event.recordNew();
|
|
469
|
-
eventUtils.fire(event);
|
|
562
|
+
/**
|
|
563
|
+
* Clear the block of transform="..." attributes.
|
|
564
|
+
* Used when the block is switching from 3d to 2d transform or vice versa.
|
|
565
|
+
* @private
|
|
566
|
+
*/
|
|
567
|
+
clearTransformAttributes_() {
|
|
568
|
+
this.getSvgRoot().removeAttribute('transform');
|
|
470
569
|
}
|
|
471
|
-
this.workspace.resizeContents();
|
|
472
|
-
};
|
|
473
570
|
|
|
474
|
-
/**
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
}
|
|
506
|
-
};
|
|
571
|
+
/**
|
|
572
|
+
* Snap this block to the nearest grid point.
|
|
573
|
+
*/
|
|
574
|
+
snapToGrid() {
|
|
575
|
+
if (!this.workspace) {
|
|
576
|
+
return; // Deleted block.
|
|
577
|
+
}
|
|
578
|
+
if (this.workspace.isDragging()) {
|
|
579
|
+
return; // Don't bump blocks during a drag.
|
|
580
|
+
}
|
|
581
|
+
if (this.getParent()) {
|
|
582
|
+
return; // Only snap top-level blocks.
|
|
583
|
+
}
|
|
584
|
+
if (this.isInFlyout) {
|
|
585
|
+
return; // Don't move blocks around in a flyout.
|
|
586
|
+
}
|
|
587
|
+
const grid = this.workspace.getGrid();
|
|
588
|
+
if (!grid || !grid.shouldSnap()) {
|
|
589
|
+
return; // Config says no snapping.
|
|
590
|
+
}
|
|
591
|
+
const spacing = grid.getSpacing();
|
|
592
|
+
const half = spacing / 2;
|
|
593
|
+
const xy = this.getRelativeToSurfaceXY();
|
|
594
|
+
const dx =
|
|
595
|
+
Math.round(Math.round((xy.x - half) / spacing) * spacing + half - xy.x);
|
|
596
|
+
const dy =
|
|
597
|
+
Math.round(Math.round((xy.y - half) / spacing) * spacing + half - xy.y);
|
|
598
|
+
if (dx || dy) {
|
|
599
|
+
this.moveBy(dx, dy);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
507
602
|
|
|
508
|
-
/**
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
603
|
+
/**
|
|
604
|
+
* Returns the coordinates of a bounding box describing the dimensions of this
|
|
605
|
+
* block and any blocks stacked below it.
|
|
606
|
+
* Coordinate system: workspace coordinates.
|
|
607
|
+
* @return {!Rect} Object with coordinates of the bounding box.
|
|
608
|
+
*/
|
|
609
|
+
getBoundingRectangle() {
|
|
610
|
+
const blockXY = this.getRelativeToSurfaceXY();
|
|
611
|
+
const blockBounds = this.getHeightWidth();
|
|
612
|
+
let left;
|
|
613
|
+
let right;
|
|
614
|
+
if (this.RTL) {
|
|
615
|
+
left = blockXY.x - blockBounds.width;
|
|
616
|
+
right = blockXY.x;
|
|
617
|
+
} else {
|
|
618
|
+
left = blockXY.x;
|
|
619
|
+
right = blockXY.x + blockBounds.width;
|
|
620
|
+
}
|
|
621
|
+
return new Rect(blockXY.y, blockXY.y + blockBounds.height, left, right);
|
|
622
|
+
}
|
|
516
623
|
|
|
517
|
-
/**
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
624
|
+
/**
|
|
625
|
+
* Notify every input on this block to mark its fields as dirty.
|
|
626
|
+
* A dirty field is a field that needs to be re-rendered.
|
|
627
|
+
*/
|
|
628
|
+
markDirty() {
|
|
629
|
+
this.pathObject.constants = (/** @type {!WorkspaceSvg} */ (this.workspace))
|
|
630
|
+
.getRenderer()
|
|
631
|
+
.getConstants();
|
|
632
|
+
for (let i = 0, input; (input = this.inputList[i]); i++) {
|
|
633
|
+
input.markDirty();
|
|
634
|
+
}
|
|
528
635
|
}
|
|
529
|
-
// Translate to current position, turning off 3d.
|
|
530
|
-
this.translate(newXY.x, newXY.y);
|
|
531
|
-
this.workspace.getBlockDragSurface().clearAndHide(this.workspace.getCanvas());
|
|
532
|
-
};
|
|
533
636
|
|
|
534
|
-
/**
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
637
|
+
/**
|
|
638
|
+
* Set whether the block is collapsed or not.
|
|
639
|
+
* @param {boolean} collapsed True if collapsed.
|
|
640
|
+
*/
|
|
641
|
+
setCollapsed(collapsed) {
|
|
642
|
+
if (this.collapsed_ === collapsed) {
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
super.setCollapsed(collapsed);
|
|
646
|
+
if (!collapsed) {
|
|
647
|
+
this.updateCollapsed_();
|
|
648
|
+
} else if (this.rendered) {
|
|
649
|
+
this.render();
|
|
650
|
+
// Don't bump neighbours. Users like to store collapsed functions together
|
|
651
|
+
// and bumping makes them go out of alignment.
|
|
652
|
+
}
|
|
549
653
|
}
|
|
550
|
-
};
|
|
551
654
|
|
|
552
|
-
/**
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
655
|
+
/**
|
|
656
|
+
* Makes sure that when the block is collapsed, it is rendered correctly
|
|
657
|
+
* for that state.
|
|
658
|
+
* @private
|
|
659
|
+
*/
|
|
660
|
+
updateCollapsed_() {
|
|
661
|
+
const collapsed = this.isCollapsed();
|
|
662
|
+
const collapsedInputName = constants.COLLAPSED_INPUT_NAME;
|
|
663
|
+
const collapsedFieldName = constants.COLLAPSED_FIELD_NAME;
|
|
664
|
+
|
|
665
|
+
for (let i = 0, input; (input = this.inputList[i]); i++) {
|
|
666
|
+
if (input.name !== collapsedInputName) {
|
|
667
|
+
input.setVisible(!collapsed);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
560
670
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
return; // Deleted block.
|
|
567
|
-
}
|
|
568
|
-
if (this.workspace.isDragging()) {
|
|
569
|
-
return; // Don't bump blocks during a drag.
|
|
570
|
-
}
|
|
571
|
-
if (this.getParent()) {
|
|
572
|
-
return; // Only snap top-level blocks.
|
|
573
|
-
}
|
|
574
|
-
if (this.isInFlyout) {
|
|
575
|
-
return; // Don't move blocks around in a flyout.
|
|
576
|
-
}
|
|
577
|
-
const grid = this.workspace.getGrid();
|
|
578
|
-
if (!grid || !grid.shouldSnap()) {
|
|
579
|
-
return; // Config says no snapping.
|
|
580
|
-
}
|
|
581
|
-
const spacing = grid.getSpacing();
|
|
582
|
-
const half = spacing / 2;
|
|
583
|
-
const xy = this.getRelativeToSurfaceXY();
|
|
584
|
-
const dx =
|
|
585
|
-
Math.round(Math.round((xy.x - half) / spacing) * spacing + half - xy.x);
|
|
586
|
-
const dy =
|
|
587
|
-
Math.round(Math.round((xy.y - half) / spacing) * spacing + half - xy.y);
|
|
588
|
-
if (dx || dy) {
|
|
589
|
-
this.moveBy(dx, dy);
|
|
590
|
-
}
|
|
591
|
-
};
|
|
671
|
+
if (!collapsed) {
|
|
672
|
+
this.updateDisabled();
|
|
673
|
+
this.removeInput(collapsedInputName);
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
592
676
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
* @return {!Rect} Object with coordinates of the bounding box.
|
|
598
|
-
*/
|
|
599
|
-
BlockSvg.prototype.getBoundingRectangle = function() {
|
|
600
|
-
const blockXY = this.getRelativeToSurfaceXY();
|
|
601
|
-
const blockBounds = this.getHeightWidth();
|
|
602
|
-
let left;
|
|
603
|
-
let right;
|
|
604
|
-
if (this.RTL) {
|
|
605
|
-
left = blockXY.x - blockBounds.width;
|
|
606
|
-
right = blockXY.x;
|
|
607
|
-
} else {
|
|
608
|
-
left = blockXY.x;
|
|
609
|
-
right = blockXY.x + blockBounds.width;
|
|
610
|
-
}
|
|
611
|
-
return new Rect(blockXY.y, blockXY.y + blockBounds.height, left, right);
|
|
612
|
-
};
|
|
677
|
+
const icons = this.getIcons();
|
|
678
|
+
for (let i = 0, icon; (icon = icons[i]); i++) {
|
|
679
|
+
icon.setVisible(false);
|
|
680
|
+
}
|
|
613
681
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
input.markDirty();
|
|
682
|
+
const text = this.toString(internalConstants.COLLAPSE_CHARS);
|
|
683
|
+
const field = this.getField(collapsedFieldName);
|
|
684
|
+
if (field) {
|
|
685
|
+
field.setValue(text);
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
const input = this.getInput(collapsedInputName) ||
|
|
689
|
+
this.appendDummyInput(collapsedInputName);
|
|
690
|
+
input.appendField(new FieldLabel(text), collapsedFieldName);
|
|
624
691
|
}
|
|
625
|
-
};
|
|
626
692
|
|
|
627
|
-
/**
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
}
|
|
643
|
-
};
|
|
693
|
+
/**
|
|
694
|
+
* Open the next (or previous) FieldTextInput.
|
|
695
|
+
* @param {!Field} start Current field.
|
|
696
|
+
* @param {boolean} forward If true go forward, otherwise backward.
|
|
697
|
+
*/
|
|
698
|
+
tab(start, forward) {
|
|
699
|
+
const tabCursor = new TabNavigateCursor();
|
|
700
|
+
tabCursor.setCurNode(ASTNode.createFieldNode(start));
|
|
701
|
+
const currentNode = tabCursor.getCurNode();
|
|
702
|
+
|
|
703
|
+
if (forward) {
|
|
704
|
+
tabCursor.next();
|
|
705
|
+
} else {
|
|
706
|
+
tabCursor.prev();
|
|
707
|
+
}
|
|
644
708
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
*/
|
|
650
|
-
BlockSvg.prototype.updateCollapsed_ = function() {
|
|
651
|
-
const collapsed = this.isCollapsed();
|
|
652
|
-
const collapsedInputName = constants.COLLAPSED_INPUT_NAME;
|
|
653
|
-
const collapsedFieldName = constants.COLLAPSED_FIELD_NAME;
|
|
709
|
+
const nextNode = tabCursor.getCurNode();
|
|
710
|
+
if (nextNode && nextNode !== currentNode) {
|
|
711
|
+
const nextField = /** @type {!Field} */ (nextNode.getLocation());
|
|
712
|
+
nextField.showEditor();
|
|
654
713
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
714
|
+
// Also move the cursor if we're in keyboard nav mode.
|
|
715
|
+
if (this.workspace.keyboardAccessibilityMode) {
|
|
716
|
+
this.workspace.getCursor().setCurNode(nextNode);
|
|
717
|
+
}
|
|
658
718
|
}
|
|
659
719
|
}
|
|
660
720
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
721
|
+
/**
|
|
722
|
+
* Handle a mouse-down on an SVG block.
|
|
723
|
+
* @param {!Event} e Mouse down event or touch start event.
|
|
724
|
+
* @private
|
|
725
|
+
*/
|
|
726
|
+
onMouseDown_(e) {
|
|
727
|
+
const gesture = this.workspace && this.workspace.getGesture(e);
|
|
728
|
+
if (gesture) {
|
|
729
|
+
gesture.handleBlockStart(e, this);
|
|
730
|
+
}
|
|
665
731
|
}
|
|
666
732
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
733
|
+
/**
|
|
734
|
+
* Load the block's help page in a new window.
|
|
735
|
+
* @package
|
|
736
|
+
*/
|
|
737
|
+
showHelp() {
|
|
738
|
+
const url =
|
|
739
|
+
(typeof this.helpUrl === 'function') ? this.helpUrl() : this.helpUrl;
|
|
740
|
+
if (url) {
|
|
741
|
+
window.open(url);
|
|
742
|
+
}
|
|
670
743
|
}
|
|
671
744
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
745
|
+
/**
|
|
746
|
+
* Generate the context menu for this block.
|
|
747
|
+
* @return {?Array<!Object>} Context menu options or null if no menu.
|
|
748
|
+
* @protected
|
|
749
|
+
*/
|
|
750
|
+
generateContextMenu() {
|
|
751
|
+
if (this.workspace.options.readOnly || !this.contextMenu) {
|
|
752
|
+
return null;
|
|
753
|
+
}
|
|
754
|
+
const menuOptions = ContextMenuRegistry.registry.getContextMenuOptions(
|
|
755
|
+
ContextMenuRegistry.ScopeType.BLOCK, {block: this});
|
|
682
756
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
*/
|
|
688
|
-
BlockSvg.prototype.tab = function(start, forward) {
|
|
689
|
-
const tabCursor = new TabNavigateCursor();
|
|
690
|
-
tabCursor.setCurNode(ASTNode.createFieldNode(start));
|
|
691
|
-
const currentNode = tabCursor.getCurNode();
|
|
757
|
+
// Allow the block to add or modify menuOptions.
|
|
758
|
+
if (this.customContextMenu) {
|
|
759
|
+
this.customContextMenu(menuOptions);
|
|
760
|
+
}
|
|
692
761
|
|
|
693
|
-
|
|
694
|
-
tabCursor.next();
|
|
695
|
-
} else {
|
|
696
|
-
tabCursor.prev();
|
|
762
|
+
return menuOptions;
|
|
697
763
|
}
|
|
698
764
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
765
|
+
/**
|
|
766
|
+
* Show the context menu for this block.
|
|
767
|
+
* @param {!Event} e Mouse event.
|
|
768
|
+
* @package
|
|
769
|
+
*/
|
|
770
|
+
showContextMenu(e) {
|
|
771
|
+
const menuOptions = this.generateContextMenu();
|
|
703
772
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
773
|
+
if (menuOptions && menuOptions.length) {
|
|
774
|
+
ContextMenu.show(e, menuOptions, this.RTL);
|
|
775
|
+
ContextMenu.setCurrentBlock(this);
|
|
707
776
|
}
|
|
708
777
|
}
|
|
709
|
-
};
|
|
710
778
|
|
|
711
|
-
/**
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
779
|
+
/**
|
|
780
|
+
* Move the connections for this block and all blocks attached under it.
|
|
781
|
+
* Also update any attached bubbles.
|
|
782
|
+
* @param {number} dx Horizontal offset from current location, in workspace
|
|
783
|
+
* units.
|
|
784
|
+
* @param {number} dy Vertical offset from current location, in workspace
|
|
785
|
+
* units.
|
|
786
|
+
* @package
|
|
787
|
+
*/
|
|
788
|
+
moveConnections(dx, dy) {
|
|
789
|
+
if (!this.rendered) {
|
|
790
|
+
// Rendering is required to lay out the blocks.
|
|
791
|
+
// This is probably an invisible block attached to a collapsed block.
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
const myConnections = this.getConnections_(false);
|
|
795
|
+
for (let i = 0; i < myConnections.length; i++) {
|
|
796
|
+
myConnections[i].moveBy(dx, dy);
|
|
797
|
+
}
|
|
798
|
+
const icons = this.getIcons();
|
|
799
|
+
for (let i = 0; i < icons.length; i++) {
|
|
800
|
+
icons[i].computeIconLocation();
|
|
801
|
+
}
|
|
722
802
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
BlockSvg.prototype.showHelp = function() {
|
|
728
|
-
const url =
|
|
729
|
-
(typeof this.helpUrl === 'function') ? this.helpUrl() : this.helpUrl;
|
|
730
|
-
if (url) {
|
|
731
|
-
window.open(url);
|
|
803
|
+
// Recurse through all blocks attached under this one.
|
|
804
|
+
for (let i = 0; i < this.childBlocks_.length; i++) {
|
|
805
|
+
(/** @type {!BlockSvg} */ (this.childBlocks_[i])).moveConnections(dx, dy);
|
|
806
|
+
}
|
|
732
807
|
}
|
|
733
|
-
};
|
|
734
808
|
|
|
735
|
-
/**
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
809
|
+
/**
|
|
810
|
+
* Recursively adds or removes the dragging class to this node and its
|
|
811
|
+
* children.
|
|
812
|
+
* @param {boolean} adding True if adding, false if removing.
|
|
813
|
+
* @package
|
|
814
|
+
*/
|
|
815
|
+
setDragging(adding) {
|
|
816
|
+
if (adding) {
|
|
817
|
+
const group = this.getSvgRoot();
|
|
818
|
+
group.translate_ = '';
|
|
819
|
+
group.skew_ = '';
|
|
820
|
+
common.draggingConnections.push(...this.getConnections_(true));
|
|
821
|
+
dom.addClass(
|
|
822
|
+
/** @type {!Element} */ (this.svgGroup_), 'blocklyDragging');
|
|
823
|
+
} else {
|
|
824
|
+
common.draggingConnections.length = 0;
|
|
825
|
+
dom.removeClass(
|
|
826
|
+
/** @type {!Element} */ (this.svgGroup_), 'blocklyDragging');
|
|
827
|
+
}
|
|
828
|
+
// Recurse through all blocks attached under this one.
|
|
829
|
+
for (let i = 0; i < this.childBlocks_.length; i++) {
|
|
830
|
+
(/** @type {!BlockSvg} */ (this.childBlocks_[i])).setDragging(adding);
|
|
831
|
+
}
|
|
743
832
|
}
|
|
744
|
-
const menuOptions = ContextMenuRegistry.registry.getContextMenuOptions(
|
|
745
|
-
ContextMenuRegistry.ScopeType.BLOCK, {block: this});
|
|
746
833
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
834
|
+
/**
|
|
835
|
+
* Set whether this block is movable or not.
|
|
836
|
+
* @param {boolean} movable True if movable.
|
|
837
|
+
*/
|
|
838
|
+
setMovable(movable) {
|
|
839
|
+
super.setMovable(movable);
|
|
840
|
+
this.pathObject.updateMovable(movable);
|
|
750
841
|
}
|
|
751
842
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
if (menuOptions && menuOptions.length) {
|
|
764
|
-
ContextMenu.show(e, menuOptions, this.RTL);
|
|
765
|
-
ContextMenu.setCurrentBlock(this);
|
|
843
|
+
/**
|
|
844
|
+
* Set whether this block is editable or not.
|
|
845
|
+
* @param {boolean} editable True if editable.
|
|
846
|
+
*/
|
|
847
|
+
setEditable(editable) {
|
|
848
|
+
super.setEditable(editable);
|
|
849
|
+
const icons = this.getIcons();
|
|
850
|
+
for (let i = 0; i < icons.length; i++) {
|
|
851
|
+
icons[i].updateEditable();
|
|
852
|
+
}
|
|
766
853
|
}
|
|
767
|
-
};
|
|
768
854
|
|
|
769
|
-
/**
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
*/
|
|
778
|
-
BlockSvg.prototype.moveConnections = function(dx, dy) {
|
|
779
|
-
if (!this.rendered) {
|
|
780
|
-
// Rendering is required to lay out the blocks.
|
|
781
|
-
// This is probably an invisible block attached to a collapsed block.
|
|
782
|
-
return;
|
|
783
|
-
}
|
|
784
|
-
const myConnections = this.getConnections_(false);
|
|
785
|
-
for (let i = 0; i < myConnections.length; i++) {
|
|
786
|
-
myConnections[i].moveBy(dx, dy);
|
|
855
|
+
/**
|
|
856
|
+
* Sets whether this block is a shadow block or not.
|
|
857
|
+
* @param {boolean} shadow True if a shadow.
|
|
858
|
+
* @package
|
|
859
|
+
*/
|
|
860
|
+
setShadow(shadow) {
|
|
861
|
+
super.setShadow(shadow);
|
|
862
|
+
this.applyColour();
|
|
787
863
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
864
|
+
|
|
865
|
+
/**
|
|
866
|
+
* Set whether this block is an insertion marker block or not.
|
|
867
|
+
* Once set this cannot be unset.
|
|
868
|
+
* @param {boolean} insertionMarker True if an insertion marker.
|
|
869
|
+
* @package
|
|
870
|
+
*/
|
|
871
|
+
setInsertionMarker(insertionMarker) {
|
|
872
|
+
if (this.isInsertionMarker_ === insertionMarker) {
|
|
873
|
+
return; // No change.
|
|
874
|
+
}
|
|
875
|
+
this.isInsertionMarker_ = insertionMarker;
|
|
876
|
+
if (this.isInsertionMarker_) {
|
|
877
|
+
this.setColour(
|
|
878
|
+
this.workspace.getRenderer().getConstants().INSERTION_MARKER_COLOUR);
|
|
879
|
+
this.pathObject.updateInsertionMarker(true);
|
|
880
|
+
}
|
|
791
881
|
}
|
|
792
882
|
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
883
|
+
/**
|
|
884
|
+
* Return the root node of the SVG or null if none exists.
|
|
885
|
+
* @return {!SVGGElement} The root SVG node (probably a group).
|
|
886
|
+
*/
|
|
887
|
+
getSvgRoot() {
|
|
888
|
+
return this.svgGroup_;
|
|
796
889
|
}
|
|
797
|
-
};
|
|
798
890
|
|
|
799
|
-
/**
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
dom.
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
891
|
+
/**
|
|
892
|
+
* Dispose of this block.
|
|
893
|
+
* @param {boolean=} healStack If true, then try to heal any gap by connecting
|
|
894
|
+
* the next statement with the previous statement. Otherwise, dispose of
|
|
895
|
+
* all children of this block.
|
|
896
|
+
* @param {boolean=} animate If true, show a disposal animation and sound.
|
|
897
|
+
* @suppress {checkTypes}
|
|
898
|
+
*/
|
|
899
|
+
dispose(healStack, animate) {
|
|
900
|
+
if (!this.workspace) {
|
|
901
|
+
// The block has already been deleted.
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
Tooltip.dispose();
|
|
905
|
+
Tooltip.unbindMouseEvents(this.pathObject.svgPath);
|
|
906
|
+
dom.startTextWidthCache();
|
|
907
|
+
// Save the block's workspace temporarily so we can resize the
|
|
908
|
+
// contents once the block is disposed.
|
|
909
|
+
const blockWorkspace = this.workspace;
|
|
910
|
+
// If this block is being dragged, unlink the mouse events.
|
|
911
|
+
if (common.getSelected() === this) {
|
|
912
|
+
this.unselect();
|
|
913
|
+
this.workspace.cancelCurrentGesture();
|
|
914
|
+
}
|
|
915
|
+
// If this block has a context menu open, close it.
|
|
916
|
+
if (ContextMenu.getCurrentBlock() === this) {
|
|
917
|
+
ContextMenu.hide();
|
|
918
|
+
}
|
|
822
919
|
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
this.pathObject.updateMovable(movable);
|
|
830
|
-
};
|
|
920
|
+
if (animate && this.rendered) {
|
|
921
|
+
this.unplug(healStack);
|
|
922
|
+
blockAnimations.disposeUiEffect(this);
|
|
923
|
+
}
|
|
924
|
+
// Stop rerendering.
|
|
925
|
+
this.rendered = false;
|
|
831
926
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
for (let i = 0; i < icons.length; i++) {
|
|
840
|
-
icons[i].updateEditable();
|
|
841
|
-
}
|
|
842
|
-
};
|
|
927
|
+
// Clear pending warnings.
|
|
928
|
+
if (this.warningTextDb_) {
|
|
929
|
+
for (const n in this.warningTextDb_) {
|
|
930
|
+
clearTimeout(this.warningTextDb_[n]);
|
|
931
|
+
}
|
|
932
|
+
this.warningTextDb_ = null;
|
|
933
|
+
}
|
|
843
934
|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
BlockSvg.prototype.setShadow = function(shadow) {
|
|
850
|
-
BlockSvg.superClass_.setShadow.call(this, shadow);
|
|
851
|
-
this.applyColour();
|
|
852
|
-
};
|
|
935
|
+
const icons = this.getIcons();
|
|
936
|
+
for (let i = 0; i < icons.length; i++) {
|
|
937
|
+
icons[i].dispose();
|
|
938
|
+
}
|
|
939
|
+
super.dispose(!!healStack);
|
|
853
940
|
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
*/
|
|
860
|
-
BlockSvg.prototype.setInsertionMarker = function(insertionMarker) {
|
|
861
|
-
if (this.isInsertionMarker_ === insertionMarker) {
|
|
862
|
-
return; // No change.
|
|
863
|
-
}
|
|
864
|
-
this.isInsertionMarker_ = insertionMarker;
|
|
865
|
-
if (this.isInsertionMarker_) {
|
|
866
|
-
this.setColour(
|
|
867
|
-
this.workspace.getRenderer().getConstants().INSERTION_MARKER_COLOUR);
|
|
868
|
-
this.pathObject.updateInsertionMarker(true);
|
|
941
|
+
dom.removeNode(this.svgGroup_);
|
|
942
|
+
blockWorkspace.resizeContents();
|
|
943
|
+
// Sever JavaScript to DOM connections.
|
|
944
|
+
this.svgGroup_ = null;
|
|
945
|
+
dom.stopTextWidthCache();
|
|
869
946
|
}
|
|
870
|
-
};
|
|
871
947
|
|
|
872
|
-
/**
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
* @param {boolean=} healStack If true, then try to heal any gap by connecting
|
|
883
|
-
* the next statement with the previous statement. Otherwise, dispose of
|
|
884
|
-
* all children of this block.
|
|
885
|
-
* @param {boolean=} animate If true, show a disposal animation and sound.
|
|
886
|
-
* @suppress {checkTypes}
|
|
887
|
-
*/
|
|
888
|
-
BlockSvg.prototype.dispose = function(healStack, animate) {
|
|
889
|
-
if (!this.workspace) {
|
|
890
|
-
// The block has already been deleted.
|
|
891
|
-
return;
|
|
892
|
-
}
|
|
893
|
-
Tooltip.dispose();
|
|
894
|
-
Tooltip.unbindMouseEvents(this.pathObject.svgPath);
|
|
895
|
-
dom.startTextWidthCache();
|
|
896
|
-
// Save the block's workspace temporarily so we can resize the
|
|
897
|
-
// contents once the block is disposed.
|
|
898
|
-
const blockWorkspace = this.workspace;
|
|
899
|
-
// If this block is being dragged, unlink the mouse events.
|
|
900
|
-
if (common.getSelected() === this) {
|
|
901
|
-
this.unselect();
|
|
902
|
-
this.workspace.cancelCurrentGesture();
|
|
903
|
-
}
|
|
904
|
-
// If this block has a context menu open, close it.
|
|
905
|
-
if (ContextMenu.getCurrentBlock() === this) {
|
|
906
|
-
ContextMenu.hide();
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
if (animate && this.rendered) {
|
|
910
|
-
this.unplug(healStack);
|
|
911
|
-
blockAnimations.disposeUiEffect(this);
|
|
912
|
-
}
|
|
913
|
-
// Stop rerendering.
|
|
914
|
-
this.rendered = false;
|
|
915
|
-
|
|
916
|
-
// Clear pending warnings.
|
|
917
|
-
if (this.warningTextDb_) {
|
|
918
|
-
for (const n in this.warningTextDb_) {
|
|
919
|
-
clearTimeout(this.warningTextDb_[n]);
|
|
948
|
+
/**
|
|
949
|
+
* Delete a block and hide chaff when doing so. The block will not be deleted
|
|
950
|
+
* if it's in a flyout. This is called from the context menu and keyboard
|
|
951
|
+
* shortcuts as the full delete action. If you are disposing of a block from
|
|
952
|
+
* the workspace and don't need to perform flyout checks, handle event
|
|
953
|
+
* grouping, or hide chaff, then use `block.dispose()` directly.
|
|
954
|
+
*/
|
|
955
|
+
checkAndDelete() {
|
|
956
|
+
if (this.workspace.isFlyout) {
|
|
957
|
+
return;
|
|
920
958
|
}
|
|
921
|
-
|
|
959
|
+
eventUtils.setGroup(true);
|
|
960
|
+
this.workspace.hideChaff();
|
|
961
|
+
if (this.outputConnection) {
|
|
962
|
+
// Do not attempt to heal rows
|
|
963
|
+
// (https://github.com/google/blockly/issues/4832)
|
|
964
|
+
this.dispose(false, true);
|
|
965
|
+
} else {
|
|
966
|
+
this.dispose(/* heal */ true, true);
|
|
967
|
+
}
|
|
968
|
+
eventUtils.setGroup(false);
|
|
922
969
|
}
|
|
923
970
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
971
|
+
/**
|
|
972
|
+
* Encode a block for copying.
|
|
973
|
+
* @return {?ICopyable.CopyData} Copy metadata, or null if the block is
|
|
974
|
+
* an insertion marker.
|
|
975
|
+
* @package
|
|
976
|
+
*/
|
|
977
|
+
toCopyData() {
|
|
978
|
+
if (this.isInsertionMarker_) {
|
|
979
|
+
return null;
|
|
980
|
+
}
|
|
981
|
+
return {
|
|
982
|
+
saveInfo: /** @type {!blocks.State} */ (
|
|
983
|
+
blocks.save(this, {addCoordinates: true, addNextBlocks: false})),
|
|
984
|
+
source: this.workspace,
|
|
985
|
+
typeCounts: common.getBlockTypeCounts(this, true),
|
|
986
|
+
};
|
|
927
987
|
}
|
|
928
|
-
BlockSvg.superClass_.dispose.call(this, !!healStack);
|
|
929
988
|
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
/**
|
|
938
|
-
* Delete a block and hide chaff when doing so. The block will not be deleted if
|
|
939
|
-
* it's in a flyout. This is called from the context menu and keyboard shortcuts
|
|
940
|
-
* as the full delete action. If you are disposing of a block from the workspace
|
|
941
|
-
* and don't need to perform flyout checks, handle event grouping, or hide
|
|
942
|
-
* chaff, then use `block.dispose()` directly.
|
|
943
|
-
*/
|
|
944
|
-
BlockSvg.prototype.checkAndDelete = function() {
|
|
945
|
-
if (this.workspace.isFlyout) {
|
|
946
|
-
return;
|
|
947
|
-
}
|
|
948
|
-
eventUtils.setGroup(true);
|
|
949
|
-
this.workspace.hideChaff();
|
|
950
|
-
if (this.outputConnection) {
|
|
951
|
-
// Do not attempt to heal rows
|
|
952
|
-
// (https://github.com/google/blockly/issues/4832)
|
|
953
|
-
this.dispose(false, true);
|
|
954
|
-
} else {
|
|
955
|
-
this.dispose(/* heal */ true, true);
|
|
956
|
-
}
|
|
957
|
-
eventUtils.setGroup(false);
|
|
958
|
-
};
|
|
959
|
-
|
|
960
|
-
/**
|
|
961
|
-
* Encode a block for copying.
|
|
962
|
-
* @return {?ICopyable.CopyData} Copy metadata, or null if the block is
|
|
963
|
-
* an insertion marker.
|
|
964
|
-
* @package
|
|
965
|
-
*/
|
|
966
|
-
BlockSvg.prototype.toCopyData = function() {
|
|
967
|
-
if (this.isInsertionMarker_) {
|
|
968
|
-
return null;
|
|
969
|
-
}
|
|
970
|
-
return {
|
|
971
|
-
saveInfo: /** @type {!blocks.State} */ (
|
|
972
|
-
blocks.save(this, {addCoordinates: true, addNextBlocks: false})),
|
|
973
|
-
source: this.workspace,
|
|
974
|
-
typeCounts: common.getBlockTypeCounts(this, true),
|
|
975
|
-
};
|
|
976
|
-
};
|
|
989
|
+
/**
|
|
990
|
+
* Updates the colour of the block to match the block's state.
|
|
991
|
+
* @package
|
|
992
|
+
*/
|
|
993
|
+
applyColour() {
|
|
994
|
+
this.pathObject.applyColour(this);
|
|
977
995
|
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
BlockSvg.prototype.applyColour = function() {
|
|
983
|
-
this.pathObject.applyColour(this);
|
|
996
|
+
const icons = this.getIcons();
|
|
997
|
+
for (let i = 0; i < icons.length; i++) {
|
|
998
|
+
icons[i].applyColour();
|
|
999
|
+
}
|
|
984
1000
|
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1001
|
+
for (let x = 0, input; (input = this.inputList[x]); x++) {
|
|
1002
|
+
for (let y = 0, field; (field = input.fieldRow[y]); y++) {
|
|
1003
|
+
field.applyColour();
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
988
1006
|
}
|
|
989
1007
|
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
1008
|
+
/**
|
|
1009
|
+
* Updates the color of the block (and children) to match the current disabled
|
|
1010
|
+
* state.
|
|
1011
|
+
* @package
|
|
1012
|
+
*/
|
|
1013
|
+
updateDisabled() {
|
|
1014
|
+
const children =
|
|
1015
|
+
/** @type {!Array<!BlockSvg>} */ (this.getChildren(false));
|
|
1016
|
+
this.applyColour();
|
|
1017
|
+
if (this.isCollapsed()) {
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
for (let i = 0, child; (child = children[i]); i++) {
|
|
1021
|
+
if (child.rendered) {
|
|
1022
|
+
child.updateDisabled();
|
|
1023
|
+
}
|
|
993
1024
|
}
|
|
994
1025
|
}
|
|
995
|
-
};
|
|
996
1026
|
|
|
997
|
-
/**
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
this.applyColour();
|
|
1005
|
-
if (this.isCollapsed()) {
|
|
1006
|
-
return;
|
|
1027
|
+
/**
|
|
1028
|
+
* Get the comment icon attached to this block, or null if the block has no
|
|
1029
|
+
* comment.
|
|
1030
|
+
* @return {?Comment} The comment icon attached to this block, or null.
|
|
1031
|
+
*/
|
|
1032
|
+
getCommentIcon() {
|
|
1033
|
+
return this.commentIcon_;
|
|
1007
1034
|
}
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1035
|
+
|
|
1036
|
+
/**
|
|
1037
|
+
* Set this block's comment text.
|
|
1038
|
+
* @param {?string} text The text, or null to delete.
|
|
1039
|
+
*/
|
|
1040
|
+
setCommentText(text) {
|
|
1041
|
+
const {Comment} = goog.module.get('Blockly.Comment');
|
|
1042
|
+
if (!Comment) {
|
|
1043
|
+
throw Error('Missing require for Blockly.Comment');
|
|
1044
|
+
}
|
|
1045
|
+
if (this.commentModel.text === text) {
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
super.setCommentText(text);
|
|
1049
|
+
|
|
1050
|
+
const shouldHaveComment = text !== null;
|
|
1051
|
+
if (!!this.commentIcon_ === shouldHaveComment) {
|
|
1052
|
+
// If the comment's state of existence is correct, but the text is new
|
|
1053
|
+
// that means we're just updating a comment.
|
|
1054
|
+
this.commentIcon_.updateText();
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
if (shouldHaveComment) {
|
|
1058
|
+
this.commentIcon_ = new Comment(this);
|
|
1059
|
+
this.comment = this.commentIcon_; // For backwards compatibility.
|
|
1060
|
+
} else {
|
|
1061
|
+
this.commentIcon_.dispose();
|
|
1062
|
+
this.commentIcon_ = null;
|
|
1063
|
+
this.comment = null; // For backwards compatibility.
|
|
1064
|
+
}
|
|
1065
|
+
if (this.rendered) {
|
|
1066
|
+
this.render();
|
|
1067
|
+
// Adding or removing a comment icon will cause the block to change shape.
|
|
1068
|
+
this.bumpNeighbours();
|
|
1011
1069
|
}
|
|
1012
1070
|
}
|
|
1013
|
-
};
|
|
1014
|
-
|
|
1015
|
-
/**
|
|
1016
|
-
* Get the comment icon attached to this block, or null if the block has no
|
|
1017
|
-
* comment.
|
|
1018
|
-
* @return {?Comment} The comment icon attached to this block, or null.
|
|
1019
|
-
*/
|
|
1020
|
-
BlockSvg.prototype.getCommentIcon = function() {
|
|
1021
|
-
return this.commentIcon_;
|
|
1022
|
-
};
|
|
1023
1071
|
|
|
1024
|
-
/**
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1072
|
+
/**
|
|
1073
|
+
* Set this block's warning text.
|
|
1074
|
+
* @param {?string} text The text, or null to delete.
|
|
1075
|
+
* @param {string=} opt_id An optional ID for the warning text to be able to
|
|
1076
|
+
* maintain multiple warnings.
|
|
1077
|
+
*/
|
|
1078
|
+
setWarningText(text, opt_id) {
|
|
1079
|
+
const {Warning} = goog.module.get('Blockly.Warning');
|
|
1080
|
+
if (!Warning) {
|
|
1081
|
+
throw Error('Missing require for Blockly.Warning');
|
|
1082
|
+
}
|
|
1083
|
+
if (!this.warningTextDb_) {
|
|
1084
|
+
// Create a database of warning PIDs.
|
|
1085
|
+
// Only runs once per block (and only those with warnings).
|
|
1086
|
+
this.warningTextDb_ = Object.create(null);
|
|
1087
|
+
}
|
|
1088
|
+
const id = opt_id || '';
|
|
1089
|
+
if (!id) {
|
|
1090
|
+
// Kill all previous pending processes, this edit supersedes them all.
|
|
1091
|
+
for (const n of Object.keys(this.warningTextDb_)) {
|
|
1092
|
+
clearTimeout(this.warningTextDb_[n]);
|
|
1093
|
+
delete this.warningTextDb_[n];
|
|
1094
|
+
}
|
|
1095
|
+
} else if (this.warningTextDb_[id]) {
|
|
1096
|
+
// Only queue up the latest change. Kill any earlier pending process.
|
|
1097
|
+
clearTimeout(this.warningTextDb_[id]);
|
|
1098
|
+
delete this.warningTextDb_[id];
|
|
1099
|
+
}
|
|
1100
|
+
if (this.workspace.isDragging()) {
|
|
1101
|
+
// Don't change the warning text during a drag.
|
|
1102
|
+
// Wait until the drag finishes.
|
|
1103
|
+
const thisBlock = this;
|
|
1104
|
+
this.warningTextDb_[id] = setTimeout(function() {
|
|
1105
|
+
if (thisBlock.workspace) { // Check block wasn't deleted.
|
|
1106
|
+
delete thisBlock.warningTextDb_[id];
|
|
1107
|
+
thisBlock.setWarningText(text, id);
|
|
1108
|
+
}
|
|
1109
|
+
}, 100);
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
if (this.isInFlyout) {
|
|
1113
|
+
text = null;
|
|
1114
|
+
}
|
|
1059
1115
|
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
this.warningTextDb_ = Object.create(null);
|
|
1075
|
-
}
|
|
1076
|
-
const id = opt_id || '';
|
|
1077
|
-
if (!id) {
|
|
1078
|
-
// Kill all previous pending processes, this edit supersedes them all.
|
|
1079
|
-
for (const n of Object.keys(this.warningTextDb_)) {
|
|
1080
|
-
clearTimeout(this.warningTextDb_[n]);
|
|
1081
|
-
delete this.warningTextDb_[n];
|
|
1082
|
-
}
|
|
1083
|
-
} else if (this.warningTextDb_[id]) {
|
|
1084
|
-
// Only queue up the latest change. Kill any earlier pending process.
|
|
1085
|
-
clearTimeout(this.warningTextDb_[id]);
|
|
1086
|
-
delete this.warningTextDb_[id];
|
|
1087
|
-
}
|
|
1088
|
-
if (this.workspace.isDragging()) {
|
|
1089
|
-
// Don't change the warning text during a drag.
|
|
1090
|
-
// Wait until the drag finishes.
|
|
1091
|
-
const thisBlock = this;
|
|
1092
|
-
this.warningTextDb_[id] = setTimeout(function() {
|
|
1093
|
-
if (thisBlock.workspace) { // Check block wasn't deleted.
|
|
1094
|
-
delete thisBlock.warningTextDb_[id];
|
|
1095
|
-
thisBlock.setWarningText(text, id);
|
|
1116
|
+
let changedState = false;
|
|
1117
|
+
if (typeof text === 'string') {
|
|
1118
|
+
// Bubble up to add a warning on top-most collapsed block.
|
|
1119
|
+
let parent = this.getSurroundParent();
|
|
1120
|
+
let collapsedParent = null;
|
|
1121
|
+
while (parent) {
|
|
1122
|
+
if (parent.isCollapsed()) {
|
|
1123
|
+
collapsedParent = parent;
|
|
1124
|
+
}
|
|
1125
|
+
parent = parent.getSurroundParent();
|
|
1126
|
+
}
|
|
1127
|
+
if (collapsedParent) {
|
|
1128
|
+
collapsedParent.setWarningText(
|
|
1129
|
+
Msg['COLLAPSED_WARNINGS_WARNING'], BlockSvg.COLLAPSED_WARNING_ID);
|
|
1096
1130
|
}
|
|
1097
|
-
}, 100);
|
|
1098
|
-
return;
|
|
1099
|
-
}
|
|
1100
|
-
if (this.isInFlyout) {
|
|
1101
|
-
text = null;
|
|
1102
|
-
}
|
|
1103
1131
|
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
let parent = this.getSurroundParent();
|
|
1108
|
-
let collapsedParent = null;
|
|
1109
|
-
while (parent) {
|
|
1110
|
-
if (parent.isCollapsed()) {
|
|
1111
|
-
collapsedParent = parent;
|
|
1132
|
+
if (!this.warning) {
|
|
1133
|
+
this.warning = new Warning(this);
|
|
1134
|
+
changedState = true;
|
|
1112
1135
|
}
|
|
1113
|
-
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
Msg['COLLAPSED_WARNINGS_WARNING'], BlockSvg.COLLAPSED_WARNING_ID);
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
if (!this.warning) {
|
|
1121
|
-
this.warning = new Warning(this);
|
|
1122
|
-
changedState = true;
|
|
1123
|
-
}
|
|
1124
|
-
this.warning.setText(/** @type {string} */ (text), id);
|
|
1125
|
-
} else {
|
|
1126
|
-
// Dispose all warnings if no ID is given.
|
|
1127
|
-
if (this.warning && !id) {
|
|
1128
|
-
this.warning.dispose();
|
|
1129
|
-
changedState = true;
|
|
1130
|
-
} else if (this.warning) {
|
|
1131
|
-
const oldText = this.warning.getText();
|
|
1132
|
-
this.warning.setText('', id);
|
|
1133
|
-
const newText = this.warning.getText();
|
|
1134
|
-
if (!newText) {
|
|
1136
|
+
this.warning.setText(/** @type {string} */ (text), id);
|
|
1137
|
+
} else {
|
|
1138
|
+
// Dispose all warnings if no ID is given.
|
|
1139
|
+
if (this.warning && !id) {
|
|
1135
1140
|
this.warning.dispose();
|
|
1141
|
+
changedState = true;
|
|
1142
|
+
} else if (this.warning) {
|
|
1143
|
+
const oldText = this.warning.getText();
|
|
1144
|
+
this.warning.setText('', id);
|
|
1145
|
+
const newText = this.warning.getText();
|
|
1146
|
+
if (!newText) {
|
|
1147
|
+
this.warning.dispose();
|
|
1148
|
+
}
|
|
1149
|
+
changedState = oldText !== newText;
|
|
1136
1150
|
}
|
|
1137
|
-
|
|
1151
|
+
}
|
|
1152
|
+
if (changedState && this.rendered) {
|
|
1153
|
+
this.render();
|
|
1154
|
+
// Adding or removing a warning icon will cause the block to change shape.
|
|
1155
|
+
this.bumpNeighbours();
|
|
1138
1156
|
}
|
|
1139
1157
|
}
|
|
1140
|
-
if (changedState && this.rendered) {
|
|
1141
|
-
this.render();
|
|
1142
|
-
// Adding or removing a warning icon will cause the block to change shape.
|
|
1143
|
-
this.bumpNeighbours();
|
|
1144
|
-
}
|
|
1145
|
-
};
|
|
1146
1158
|
|
|
1147
|
-
/**
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1159
|
+
/**
|
|
1160
|
+
* Give this block a mutator dialog.
|
|
1161
|
+
* @param {?Mutator} mutator A mutator dialog instance or null to remove.
|
|
1162
|
+
*/
|
|
1163
|
+
setMutator(mutator) {
|
|
1164
|
+
if (this.mutator && this.mutator !== mutator) {
|
|
1165
|
+
this.mutator.dispose();
|
|
1166
|
+
}
|
|
1167
|
+
if (mutator) {
|
|
1168
|
+
mutator.setBlock(this);
|
|
1169
|
+
this.mutator = mutator;
|
|
1170
|
+
mutator.createIcon();
|
|
1171
|
+
}
|
|
1172
|
+
if (this.rendered) {
|
|
1173
|
+
this.render();
|
|
1174
|
+
// Adding or removing a mutator icon will cause the block to change shape.
|
|
1175
|
+
this.bumpNeighbours();
|
|
1176
|
+
}
|
|
1164
1177
|
}
|
|
1165
|
-
};
|
|
1166
1178
|
|
|
1167
|
-
/**
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1179
|
+
/**
|
|
1180
|
+
* Set whether the block is enabled or not.
|
|
1181
|
+
* @param {boolean} enabled True if enabled.
|
|
1182
|
+
*/
|
|
1183
|
+
setEnabled(enabled) {
|
|
1184
|
+
if (this.isEnabled() !== enabled) {
|
|
1185
|
+
super.setEnabled(enabled);
|
|
1186
|
+
if (this.rendered && !this.getInheritedDisabled()) {
|
|
1187
|
+
this.updateDisabled();
|
|
1188
|
+
}
|
|
1176
1189
|
}
|
|
1177
1190
|
}
|
|
1178
|
-
};
|
|
1179
1191
|
|
|
1180
|
-
/**
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1192
|
+
/**
|
|
1193
|
+
* Set whether the block is highlighted or not. Block highlighting is
|
|
1194
|
+
* often used to visually mark blocks currently being executed.
|
|
1195
|
+
* @param {boolean} highlighted True if highlighted.
|
|
1196
|
+
*/
|
|
1197
|
+
setHighlighted(highlighted) {
|
|
1198
|
+
if (!this.rendered) {
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1201
|
+
this.pathObject.updateHighlighted(highlighted);
|
|
1188
1202
|
}
|
|
1189
|
-
this.pathObject.updateHighlighted(highlighted);
|
|
1190
|
-
};
|
|
1191
|
-
|
|
1192
|
-
/**
|
|
1193
|
-
* Adds the visual "select" effect to the block, but does not actually select
|
|
1194
|
-
* it or fire an event.
|
|
1195
|
-
* @see BlockSvg#select
|
|
1196
|
-
*/
|
|
1197
|
-
BlockSvg.prototype.addSelect = function() {
|
|
1198
|
-
this.pathObject.updateSelected(true);
|
|
1199
|
-
};
|
|
1200
1203
|
|
|
1201
|
-
/**
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
}
|
|
1204
|
+
/**
|
|
1205
|
+
* Adds the visual "select" effect to the block, but does not actually select
|
|
1206
|
+
* it or fire an event.
|
|
1207
|
+
* @see BlockSvg#select
|
|
1208
|
+
*/
|
|
1209
|
+
addSelect() {
|
|
1210
|
+
this.pathObject.updateSelected(true);
|
|
1211
|
+
}
|
|
1209
1212
|
|
|
1210
|
-
/**
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
};
|
|
1213
|
+
/**
|
|
1214
|
+
* Removes the visual "select" effect from the block, but does not actually
|
|
1215
|
+
* unselect it or fire an event.
|
|
1216
|
+
* @see BlockSvg#unselect
|
|
1217
|
+
*/
|
|
1218
|
+
removeSelect() {
|
|
1219
|
+
this.pathObject.updateSelected(false);
|
|
1220
|
+
}
|
|
1219
1221
|
|
|
1222
|
+
/**
|
|
1223
|
+
* Update the cursor over this block by adding or removing a class.
|
|
1224
|
+
* @param {boolean} enable True if the delete cursor should be shown, false
|
|
1225
|
+
* otherwise.
|
|
1226
|
+
* @package
|
|
1227
|
+
*/
|
|
1228
|
+
setDeleteStyle(enable) {
|
|
1229
|
+
this.pathObject.updateDraggingDelete(enable);
|
|
1230
|
+
}
|
|
1220
1231
|
|
|
1221
|
-
// Overrides of functions on Blockly.Block that take into account whether the
|
|
1222
|
-
// block has been rendered.
|
|
1223
|
-
/**
|
|
1224
|
-
* Get the colour of a block.
|
|
1225
|
-
* @return {string} #RRGGBB string.
|
|
1226
|
-
*/
|
|
1227
|
-
BlockSvg.prototype.getColour = function() {
|
|
1228
|
-
return this.style.colourPrimary;
|
|
1229
|
-
};
|
|
1232
|
+
// Overrides of functions on Blockly.Block that take into account whether the
|
|
1230
1233
|
|
|
1231
|
-
|
|
1232
|
-
* Change the colour of a block.
|
|
1233
|
-
* @param {number|string} colour HSV hue value, or #RRGGBB string.
|
|
1234
|
-
*/
|
|
1235
|
-
BlockSvg.prototype.setColour = function(colour) {
|
|
1236
|
-
BlockSvg.superClass_.setColour.call(this, colour);
|
|
1237
|
-
const styleObj =
|
|
1238
|
-
this.workspace.getRenderer().getConstants().getBlockStyleForColour(
|
|
1239
|
-
this.colour_);
|
|
1234
|
+
// block has been rendered.
|
|
1240
1235
|
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1236
|
+
/**
|
|
1237
|
+
* Get the colour of a block.
|
|
1238
|
+
* @return {string} #RRGGBB string.
|
|
1239
|
+
*/
|
|
1240
|
+
getColour() {
|
|
1241
|
+
return this.style.colourPrimary;
|
|
1242
|
+
}
|
|
1244
1243
|
|
|
1245
|
-
|
|
1246
|
-
|
|
1244
|
+
/**
|
|
1245
|
+
* Change the colour of a block.
|
|
1246
|
+
* @param {number|string} colour HSV hue value, or #RRGGBB string.
|
|
1247
|
+
*/
|
|
1248
|
+
setColour(colour) {
|
|
1249
|
+
super.setColour(colour);
|
|
1250
|
+
const styleObj =
|
|
1251
|
+
this.workspace.getRenderer().getConstants().getBlockStyleForColour(
|
|
1252
|
+
this.colour_);
|
|
1247
1253
|
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
* @throws {Error} if the block style does not exist.
|
|
1252
|
-
*/
|
|
1253
|
-
BlockSvg.prototype.setStyle = function(blockStyleName) {
|
|
1254
|
-
const blockStyle =
|
|
1255
|
-
this.workspace.getRenderer().getConstants().getBlockStyle(blockStyleName);
|
|
1256
|
-
this.styleName_ = blockStyleName;
|
|
1257
|
-
|
|
1258
|
-
if (blockStyle) {
|
|
1259
|
-
this.hat = blockStyle.hat;
|
|
1260
|
-
this.pathObject.setStyle(blockStyle);
|
|
1261
|
-
// Set colour to match Block.
|
|
1262
|
-
this.colour_ = blockStyle.colourPrimary;
|
|
1263
|
-
this.style = blockStyle;
|
|
1254
|
+
this.pathObject.setStyle(styleObj.style);
|
|
1255
|
+
this.style = styleObj.style;
|
|
1256
|
+
this.styleName_ = styleObj.name;
|
|
1264
1257
|
|
|
1265
1258
|
this.applyColour();
|
|
1266
|
-
} else {
|
|
1267
|
-
throw Error('Invalid style name: ' + blockStyleName);
|
|
1268
1259
|
}
|
|
1269
|
-
};
|
|
1270
|
-
|
|
1271
|
-
/**
|
|
1272
|
-
* Move this block to the front of the visible workspace.
|
|
1273
|
-
* <g> tags do not respect z-index so SVG renders them in the
|
|
1274
|
-
* order that they are in the DOM. By placing this block first within the
|
|
1275
|
-
* block group's <g>, it will render on top of any other blocks.
|
|
1276
|
-
* @package
|
|
1277
|
-
*/
|
|
1278
|
-
BlockSvg.prototype.bringToFront = function() {
|
|
1279
|
-
let block = this;
|
|
1280
|
-
do {
|
|
1281
|
-
const root = block.getSvgRoot();
|
|
1282
|
-
const parent = root.parentNode;
|
|
1283
|
-
const childNodes = parent.childNodes;
|
|
1284
|
-
// Avoid moving the block if it's already at the bottom.
|
|
1285
|
-
if (childNodes[childNodes.length - 1] !== root) {
|
|
1286
|
-
parent.appendChild(root);
|
|
1287
|
-
}
|
|
1288
|
-
block = block.getParent();
|
|
1289
|
-
} while (block);
|
|
1290
|
-
};
|
|
1291
1260
|
|
|
1292
|
-
/**
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1261
|
+
/**
|
|
1262
|
+
* Set the style and colour values of a block.
|
|
1263
|
+
* @param {string} blockStyleName Name of the block style.
|
|
1264
|
+
* @throws {Error} if the block style does not exist.
|
|
1265
|
+
*/
|
|
1266
|
+
setStyle(blockStyleName) {
|
|
1267
|
+
const blockStyle =
|
|
1268
|
+
this.workspace.getRenderer().getConstants().getBlockStyle(
|
|
1269
|
+
blockStyleName);
|
|
1270
|
+
this.styleName_ = blockStyleName;
|
|
1271
|
+
|
|
1272
|
+
if (blockStyle) {
|
|
1273
|
+
this.hat = blockStyle.hat;
|
|
1274
|
+
this.pathObject.setStyle(blockStyle);
|
|
1275
|
+
// Set colour to match Block.
|
|
1276
|
+
this.colour_ = blockStyle.colourPrimary;
|
|
1277
|
+
this.style = blockStyle;
|
|
1278
|
+
|
|
1279
|
+
this.applyColour();
|
|
1280
|
+
} else {
|
|
1281
|
+
throw Error('Invalid style name: ' + blockStyleName);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1300
1284
|
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1285
|
+
/**
|
|
1286
|
+
* Move this block to the front of the visible workspace.
|
|
1287
|
+
* <g> tags do not respect z-index so SVG renders them in the
|
|
1288
|
+
* order that they are in the DOM. By placing this block first within the
|
|
1289
|
+
* block group's <g>, it will render on top of any other blocks.
|
|
1290
|
+
* @package
|
|
1291
|
+
*/
|
|
1292
|
+
bringToFront() {
|
|
1293
|
+
let block = this;
|
|
1294
|
+
do {
|
|
1295
|
+
const root = block.getSvgRoot();
|
|
1296
|
+
const parent = root.parentNode;
|
|
1297
|
+
const childNodes = parent.childNodes;
|
|
1298
|
+
// Avoid moving the block if it's already at the bottom.
|
|
1299
|
+
if (childNodes[childNodes.length - 1] !== root) {
|
|
1300
|
+
parent.appendChild(root);
|
|
1301
|
+
}
|
|
1302
|
+
block = block.getParent();
|
|
1303
|
+
} while (block);
|
|
1304
1304
|
}
|
|
1305
|
-
};
|
|
1306
1305
|
|
|
1307
|
-
/**
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1306
|
+
/**
|
|
1307
|
+
* Set whether this block can chain onto the bottom of another block.
|
|
1308
|
+
* @param {boolean} newBoolean True if there can be a previous statement.
|
|
1309
|
+
* @param {(string|Array<string>|null)=} opt_check Statement type or
|
|
1310
|
+
* list of statement types. Null/undefined if any type could be
|
|
1311
|
+
* connected.
|
|
1312
|
+
*/
|
|
1313
|
+
setPreviousStatement(newBoolean, opt_check) {
|
|
1314
|
+
super.setPreviousStatement(newBoolean, opt_check);
|
|
1315
1315
|
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1316
|
+
if (this.rendered) {
|
|
1317
|
+
this.render();
|
|
1318
|
+
this.bumpNeighbours();
|
|
1319
|
+
}
|
|
1319
1320
|
}
|
|
1320
|
-
};
|
|
1321
1321
|
|
|
1322
|
-
/**
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1322
|
+
/**
|
|
1323
|
+
* Set whether another block can chain onto the bottom of this block.
|
|
1324
|
+
* @param {boolean} newBoolean True if there can be a next statement.
|
|
1325
|
+
* @param {(string|Array<string>|null)=} opt_check Statement type or
|
|
1326
|
+
* list of statement types. Null/undefined if any type could be
|
|
1327
|
+
* connected.
|
|
1328
|
+
*/
|
|
1329
|
+
setNextStatement(newBoolean, opt_check) {
|
|
1330
|
+
super.setNextStatement(newBoolean, opt_check);
|
|
1331
1331
|
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1332
|
+
if (this.rendered) {
|
|
1333
|
+
this.render();
|
|
1334
|
+
this.bumpNeighbours();
|
|
1335
|
+
}
|
|
1335
1336
|
}
|
|
1336
|
-
};
|
|
1337
1337
|
|
|
1338
|
-
/**
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1338
|
+
/**
|
|
1339
|
+
* Set whether this block returns a value.
|
|
1340
|
+
* @param {boolean} newBoolean True if there is an output.
|
|
1341
|
+
* @param {(string|Array<string>|null)=} opt_check Returned type or list
|
|
1342
|
+
* of returned types. Null or undefined if any type could be returned
|
|
1343
|
+
* (e.g. variable get).
|
|
1344
|
+
*/
|
|
1345
|
+
setOutput(newBoolean, opt_check) {
|
|
1346
|
+
super.setOutput(newBoolean, opt_check);
|
|
1344
1347
|
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
+
if (this.rendered) {
|
|
1349
|
+
this.render();
|
|
1350
|
+
this.bumpNeighbours();
|
|
1351
|
+
}
|
|
1348
1352
|
}
|
|
1349
|
-
};
|
|
1350
1353
|
|
|
1351
|
-
/**
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
* @throws {Error} if the input is not present and opt_quiet is not true.
|
|
1358
|
-
*/
|
|
1359
|
-
BlockSvg.prototype.removeInput = function(name, opt_quiet) {
|
|
1360
|
-
const removed = BlockSvg.superClass_.removeInput.call(this, name, opt_quiet);
|
|
1354
|
+
/**
|
|
1355
|
+
* Set whether value inputs are arranged horizontally or vertically.
|
|
1356
|
+
* @param {boolean} newBoolean True if inputs are horizontal.
|
|
1357
|
+
*/
|
|
1358
|
+
setInputsInline(newBoolean) {
|
|
1359
|
+
super.setInputsInline(newBoolean);
|
|
1361
1360
|
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1361
|
+
if (this.rendered) {
|
|
1362
|
+
this.render();
|
|
1363
|
+
this.bumpNeighbours();
|
|
1364
|
+
}
|
|
1366
1365
|
}
|
|
1367
1366
|
|
|
1368
|
-
|
|
1369
|
-
|
|
1367
|
+
/**
|
|
1368
|
+
* Remove an input from this block.
|
|
1369
|
+
* @param {string} name The name of the input.
|
|
1370
|
+
* @param {boolean=} opt_quiet True to prevent error if input is not present.
|
|
1371
|
+
* @return {boolean} True if operation succeeds, false if input is not present
|
|
1372
|
+
* and opt_quiet is true
|
|
1373
|
+
* @throws {Error} if the input is not present and opt_quiet is not true.
|
|
1374
|
+
*/
|
|
1375
|
+
removeInput(name, opt_quiet) {
|
|
1376
|
+
const removed = super.removeInput(name, opt_quiet);
|
|
1370
1377
|
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
BlockSvg.prototype.moveNumberedInputBefore = function(inputIndex, refIndex) {
|
|
1377
|
-
BlockSvg.superClass_.moveNumberedInputBefore.call(this, inputIndex, refIndex);
|
|
1378
|
+
if (this.rendered) {
|
|
1379
|
+
this.render();
|
|
1380
|
+
// Removing an input will cause the block to change shape.
|
|
1381
|
+
this.bumpNeighbours();
|
|
1382
|
+
}
|
|
1378
1383
|
|
|
1379
|
-
|
|
1380
|
-
this.render();
|
|
1381
|
-
// Moving an input will cause the block to change shape.
|
|
1382
|
-
this.bumpNeighbours();
|
|
1384
|
+
return removed;
|
|
1383
1385
|
}
|
|
1384
|
-
};
|
|
1385
1386
|
|
|
1386
|
-
/**
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
*/
|
|
1395
|
-
BlockSvg.prototype.appendInput_ = function(type, name) {
|
|
1396
|
-
const input = BlockSvg.superClass_.appendInput_.call(this, type, name);
|
|
1387
|
+
/**
|
|
1388
|
+
* Move a numbered input to a different location on this block.
|
|
1389
|
+
* @param {number} inputIndex Index of the input to move.
|
|
1390
|
+
* @param {number} refIndex Index of input that should be after the moved
|
|
1391
|
+
* input.
|
|
1392
|
+
*/
|
|
1393
|
+
moveNumberedInputBefore(inputIndex, refIndex) {
|
|
1394
|
+
super.moveNumberedInputBefore(inputIndex, refIndex);
|
|
1397
1395
|
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1396
|
+
if (this.rendered) {
|
|
1397
|
+
this.render();
|
|
1398
|
+
// Moving an input will cause the block to change shape.
|
|
1399
|
+
this.bumpNeighbours();
|
|
1400
|
+
}
|
|
1402
1401
|
}
|
|
1403
|
-
return input;
|
|
1404
|
-
};
|
|
1405
1402
|
|
|
1406
|
-
/**
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1403
|
+
/**
|
|
1404
|
+
* Add a value input, statement input or local variable to this block.
|
|
1405
|
+
* @param {number} type One of Blockly.inputTypes.
|
|
1406
|
+
* @param {string} name Language-neutral identifier which may used to find
|
|
1407
|
+
* this input again. Should be unique to this block.
|
|
1408
|
+
* @return {!Input} The input object created.
|
|
1409
|
+
* @protected
|
|
1410
|
+
* @override
|
|
1411
|
+
*/
|
|
1412
|
+
appendInput_(type, name) {
|
|
1413
|
+
const input = super.appendInput_(type, name);
|
|
1414
|
+
|
|
1415
|
+
if (this.rendered) {
|
|
1416
|
+
this.render();
|
|
1417
|
+
// Adding an input will cause the block to change shape.
|
|
1418
|
+
this.bumpNeighbours();
|
|
1419
|
+
}
|
|
1420
|
+
return input;
|
|
1418
1421
|
}
|
|
1419
|
-
|
|
1420
|
-
|
|
1422
|
+
|
|
1423
|
+
/**
|
|
1424
|
+
* Sets whether this block's connections are tracked in the database or not.
|
|
1425
|
+
*
|
|
1426
|
+
* Used by the deserializer to be more efficient. Setting a connection's
|
|
1427
|
+
* tracked_ value to false keeps it from adding itself to the db when it
|
|
1428
|
+
* gets its first moveTo call, saving expensive ops for later.
|
|
1429
|
+
* @param {boolean} track If true, start tracking. If false, stop tracking.
|
|
1430
|
+
* @package
|
|
1431
|
+
*/
|
|
1432
|
+
setConnectionTracking(track) {
|
|
1433
|
+
if (this.previousConnection) {
|
|
1434
|
+
/** @type {!RenderedConnection} */ (this.previousConnection)
|
|
1435
|
+
.setTracking(track);
|
|
1436
|
+
}
|
|
1437
|
+
if (this.outputConnection) {
|
|
1438
|
+
/** @type {!RenderedConnection} */ (this.outputConnection)
|
|
1439
|
+
.setTracking(track);
|
|
1440
|
+
}
|
|
1441
|
+
if (this.nextConnection) {
|
|
1442
|
+
/** @type {!RenderedConnection} */ (this.nextConnection)
|
|
1443
|
+
.setTracking(track);
|
|
1444
|
+
const child =
|
|
1445
|
+
/** @type {!RenderedConnection} */ (this.nextConnection)
|
|
1446
|
+
.targetBlock();
|
|
1447
|
+
if (child) {
|
|
1448
|
+
child.setConnectionTracking(track);
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
if (this.collapsed_) {
|
|
1453
|
+
// When track is true, we don't want to start tracking collapsed
|
|
1454
|
+
// connections. When track is false, we're already not tracking
|
|
1455
|
+
// collapsed connections, so no need to update.
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
for (let i = 0; i < this.inputList.length; i++) {
|
|
1460
|
+
const conn =
|
|
1461
|
+
/** @type {!RenderedConnection} */ (this.inputList[i].connection);
|
|
1462
|
+
if (conn) {
|
|
1463
|
+
conn.setTracking(track);
|
|
1464
|
+
|
|
1465
|
+
// Pass tracking on down the chain.
|
|
1466
|
+
const block = conn.targetBlock();
|
|
1467
|
+
if (block) {
|
|
1468
|
+
block.setConnectionTracking(track);
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1421
1472
|
}
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1473
|
+
|
|
1474
|
+
/**
|
|
1475
|
+
* Returns connections originating from this block.
|
|
1476
|
+
* @param {boolean} all If true, return all connections even hidden ones.
|
|
1477
|
+
* Otherwise, for a non-rendered block return an empty list, and for a
|
|
1478
|
+
* collapsed block don't return inputs connections.
|
|
1479
|
+
* @return {!Array<!RenderedConnection>} Array of connections.
|
|
1480
|
+
* @package
|
|
1481
|
+
*/
|
|
1482
|
+
getConnections_(all) {
|
|
1483
|
+
const myConnections = [];
|
|
1484
|
+
if (all || this.rendered) {
|
|
1485
|
+
if (this.outputConnection) {
|
|
1486
|
+
myConnections.push(this.outputConnection);
|
|
1487
|
+
}
|
|
1488
|
+
if (this.previousConnection) {
|
|
1489
|
+
myConnections.push(this.previousConnection);
|
|
1490
|
+
}
|
|
1491
|
+
if (this.nextConnection) {
|
|
1492
|
+
myConnections.push(this.nextConnection);
|
|
1493
|
+
}
|
|
1494
|
+
if (all || !this.collapsed_) {
|
|
1495
|
+
for (let i = 0, input; (input = this.inputList[i]); i++) {
|
|
1496
|
+
if (input.connection) {
|
|
1497
|
+
myConnections.push(input.connection);
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1427
1501
|
}
|
|
1502
|
+
return myConnections;
|
|
1428
1503
|
}
|
|
1429
1504
|
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1505
|
+
/**
|
|
1506
|
+
* Walks down a stack of blocks and finds the last next connection on the
|
|
1507
|
+
* stack.
|
|
1508
|
+
* @param {boolean} ignoreShadows If true,the last connection on a non-shadow
|
|
1509
|
+
* block will be returned. If false, this will follow shadows to find the
|
|
1510
|
+
* last connection.
|
|
1511
|
+
* @return {?RenderedConnection} The last next connection on the stack,
|
|
1512
|
+
* or null.
|
|
1513
|
+
* @package
|
|
1514
|
+
* @override
|
|
1515
|
+
*/
|
|
1516
|
+
lastConnectionInStack(ignoreShadows) {
|
|
1517
|
+
return /** @type {RenderedConnection} */ (
|
|
1518
|
+
super.lastConnectionInStack(ignoreShadows));
|
|
1435
1519
|
}
|
|
1436
1520
|
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1521
|
+
/**
|
|
1522
|
+
* Find the connection on this block that corresponds to the given connection
|
|
1523
|
+
* on the other block.
|
|
1524
|
+
* Used to match connections between a block and its insertion marker.
|
|
1525
|
+
* @param {!Block} otherBlock The other block to match against.
|
|
1526
|
+
* @param {!Connection} conn The other connection to match.
|
|
1527
|
+
* @return {?RenderedConnection} The matching connection on this block,
|
|
1528
|
+
* or null.
|
|
1529
|
+
* @package
|
|
1530
|
+
* @override
|
|
1531
|
+
*/
|
|
1532
|
+
getMatchingConnection(otherBlock, conn) {
|
|
1533
|
+
return /** @type {RenderedConnection} */ (
|
|
1534
|
+
super.getMatchingConnection(otherBlock, conn));
|
|
1535
|
+
}
|
|
1441
1536
|
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1537
|
+
/**
|
|
1538
|
+
* Create a connection of the specified type.
|
|
1539
|
+
* @param {number} type The type of the connection to create.
|
|
1540
|
+
* @return {!RenderedConnection} A new connection of the specified type.
|
|
1541
|
+
* @protected
|
|
1542
|
+
*/
|
|
1543
|
+
makeConnection_(type) {
|
|
1544
|
+
return new RenderedConnection(this, type);
|
|
1448
1545
|
}
|
|
1449
|
-
};
|
|
1450
1546
|
|
|
1451
|
-
/**
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
*/
|
|
1459
|
-
BlockSvg.prototype.getConnections_ = function(all) {
|
|
1460
|
-
const myConnections = [];
|
|
1461
|
-
if (all || this.rendered) {
|
|
1462
|
-
if (this.outputConnection) {
|
|
1463
|
-
myConnections.push(this.outputConnection);
|
|
1547
|
+
/**
|
|
1548
|
+
* Bump unconnected blocks out of alignment. Two blocks which aren't actually
|
|
1549
|
+
* connected should not coincidentally line up on screen.
|
|
1550
|
+
*/
|
|
1551
|
+
bumpNeighbours() {
|
|
1552
|
+
if (!this.workspace) {
|
|
1553
|
+
return; // Deleted block.
|
|
1464
1554
|
}
|
|
1465
|
-
if (this.
|
|
1466
|
-
|
|
1555
|
+
if (this.workspace.isDragging()) {
|
|
1556
|
+
return; // Don't bump blocks during a drag.
|
|
1467
1557
|
}
|
|
1468
|
-
|
|
1469
|
-
|
|
1558
|
+
const rootBlock = this.getRootBlock();
|
|
1559
|
+
if (rootBlock.isInFlyout) {
|
|
1560
|
+
return; // Don't move blocks around in a flyout.
|
|
1470
1561
|
}
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1562
|
+
// Loop through every connection on this block.
|
|
1563
|
+
const myConnections = this.getConnections_(false);
|
|
1564
|
+
for (let i = 0, connection; (connection = myConnections[i]); i++) {
|
|
1565
|
+
const renderedConn = /** @type {!RenderedConnection} */ (connection);
|
|
1566
|
+
// Spider down from this block bumping all sub-blocks.
|
|
1567
|
+
if (renderedConn.isConnected() && renderedConn.isSuperior()) {
|
|
1568
|
+
renderedConn.targetBlock().bumpNeighbours();
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
const neighbours = connection.neighbours(config.snapRadius);
|
|
1572
|
+
for (let j = 0, otherConnection; (otherConnection = neighbours[j]); j++) {
|
|
1573
|
+
const renderedOther =
|
|
1574
|
+
/** @type {!RenderedConnection} */ (otherConnection);
|
|
1575
|
+
// If both connections are connected, that's probably fine. But if
|
|
1576
|
+
// either one of them is unconnected, then there could be confusion.
|
|
1577
|
+
if (!renderedConn.isConnected() || !renderedOther.isConnected()) {
|
|
1578
|
+
// Only bump blocks if they are from different tree structures.
|
|
1579
|
+
if (renderedOther.getSourceBlock().getRootBlock() !== rootBlock) {
|
|
1580
|
+
// Always bump the inferior block.
|
|
1581
|
+
if (renderedConn.isSuperior()) {
|
|
1582
|
+
renderedOther.bumpAwayFrom(renderedConn);
|
|
1583
|
+
} else {
|
|
1584
|
+
renderedConn.bumpAwayFrom(renderedOther);
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1475
1587
|
}
|
|
1476
1588
|
}
|
|
1477
1589
|
}
|
|
1478
1590
|
}
|
|
1479
|
-
return myConnections;
|
|
1480
|
-
};
|
|
1481
1591
|
|
|
1482
|
-
/**
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
*/
|
|
1492
|
-
BlockSvg.prototype.lastConnectionInStack = function(ignoreShadows) {
|
|
1493
|
-
return /** @type {RenderedConnection} */ (
|
|
1494
|
-
BlockSvg.superClass_.lastConnectionInStack.call(this, ignoreShadows));
|
|
1495
|
-
};
|
|
1592
|
+
/**
|
|
1593
|
+
* Schedule snapping to grid and bumping neighbours to occur after a brief
|
|
1594
|
+
* delay.
|
|
1595
|
+
* @package
|
|
1596
|
+
*/
|
|
1597
|
+
scheduleSnapAndBump() {
|
|
1598
|
+
const block = this;
|
|
1599
|
+
// Ensure that any snap and bump are part of this move's event group.
|
|
1600
|
+
const group = eventUtils.getGroup();
|
|
1496
1601
|
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
* @param {!Connection} conn The other connection to match.
|
|
1503
|
-
* @return {?RenderedConnection} The matching connection on this block,
|
|
1504
|
-
* or null.
|
|
1505
|
-
* @package
|
|
1506
|
-
* @override
|
|
1507
|
-
*/
|
|
1508
|
-
BlockSvg.prototype.getMatchingConnection = function(otherBlock, conn) {
|
|
1509
|
-
return /** @type {RenderedConnection} */ (
|
|
1510
|
-
BlockSvg.superClass_.getMatchingConnection.call(this, otherBlock, conn));
|
|
1511
|
-
};
|
|
1602
|
+
setTimeout(function() {
|
|
1603
|
+
eventUtils.setGroup(group);
|
|
1604
|
+
block.snapToGrid();
|
|
1605
|
+
eventUtils.setGroup(false);
|
|
1606
|
+
}, config.bumpDelay / 2);
|
|
1512
1607
|
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
BlockSvg.prototype.makeConnection_ = function(type) {
|
|
1520
|
-
return new RenderedConnection(this, type);
|
|
1521
|
-
};
|
|
1608
|
+
setTimeout(function() {
|
|
1609
|
+
eventUtils.setGroup(group);
|
|
1610
|
+
block.bumpNeighbours();
|
|
1611
|
+
eventUtils.setGroup(false);
|
|
1612
|
+
}, config.bumpDelay);
|
|
1613
|
+
}
|
|
1522
1614
|
|
|
1523
|
-
/**
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
if (connection.isConnected() && connection.isSuperior()) {
|
|
1543
|
-
connection.targetBlock().bumpNeighbours();
|
|
1544
|
-
}
|
|
1545
|
-
|
|
1546
|
-
const neighbours = connection.neighbours(internalConstants.SNAP_RADIUS);
|
|
1547
|
-
for (let j = 0, otherConnection; (otherConnection = neighbours[j]); j++) {
|
|
1548
|
-
// If both connections are connected, that's probably fine. But if
|
|
1549
|
-
// either one of them is unconnected, then there could be confusion.
|
|
1550
|
-
if (!connection.isConnected() || !otherConnection.isConnected()) {
|
|
1551
|
-
// Only bump blocks if they are from different tree structures.
|
|
1552
|
-
if (otherConnection.getSourceBlock().getRootBlock() !== rootBlock) {
|
|
1553
|
-
// Always bump the inferior block.
|
|
1554
|
-
if (connection.isSuperior()) {
|
|
1555
|
-
otherConnection.bumpAwayFrom(connection);
|
|
1556
|
-
} else {
|
|
1557
|
-
connection.bumpAwayFrom(otherConnection);
|
|
1558
|
-
}
|
|
1559
|
-
}
|
|
1560
|
-
}
|
|
1615
|
+
/**
|
|
1616
|
+
* Position a block so that it doesn't move the target block when connected.
|
|
1617
|
+
* The block to position is usually either the first block in a dragged stack
|
|
1618
|
+
* or an insertion marker.
|
|
1619
|
+
* @param {!RenderedConnection} sourceConnection The connection on the
|
|
1620
|
+
* moving block's stack.
|
|
1621
|
+
* @param {!RenderedConnection} targetConnection The connection that
|
|
1622
|
+
* should stay stationary as this block is positioned.
|
|
1623
|
+
* @package
|
|
1624
|
+
*/
|
|
1625
|
+
positionNearConnection(sourceConnection, targetConnection) {
|
|
1626
|
+
// We only need to position the new block if it's before the existing one,
|
|
1627
|
+
// otherwise its position is set by the previous block.
|
|
1628
|
+
if (sourceConnection.type === ConnectionType.NEXT_STATEMENT ||
|
|
1629
|
+
sourceConnection.type === ConnectionType.INPUT_VALUE) {
|
|
1630
|
+
const dx = targetConnection.x - sourceConnection.x;
|
|
1631
|
+
const dy = targetConnection.y - sourceConnection.y;
|
|
1632
|
+
|
|
1633
|
+
this.moveBy(dx, dy);
|
|
1561
1634
|
}
|
|
1562
1635
|
}
|
|
1563
|
-
};
|
|
1564
1636
|
|
|
1565
|
-
/**
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
const group = eventUtils.getGroup();
|
|
1574
|
-
|
|
1575
|
-
setTimeout(function() {
|
|
1576
|
-
eventUtils.setGroup(group);
|
|
1577
|
-
block.snapToGrid();
|
|
1578
|
-
eventUtils.setGroup(false);
|
|
1579
|
-
}, internalConstants.BUMP_DELAY / 2);
|
|
1637
|
+
/**
|
|
1638
|
+
* Return the parent block or null if this block is at the top level.
|
|
1639
|
+
* @return {?BlockSvg} The block (if any) that holds the current block.
|
|
1640
|
+
* @override
|
|
1641
|
+
*/
|
|
1642
|
+
getParent() {
|
|
1643
|
+
return /** @type {?BlockSvg} */ (super.getParent());
|
|
1644
|
+
}
|
|
1580
1645
|
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
};
|
|
1646
|
+
/**
|
|
1647
|
+
* @return {?BlockSvg} The block (if any) that surrounds the current block.
|
|
1648
|
+
* @override
|
|
1649
|
+
*/
|
|
1650
|
+
getSurroundParent() {
|
|
1651
|
+
return /** @type {?BlockSvg} */ (super.getSurroundParent());
|
|
1652
|
+
}
|
|
1587
1653
|
|
|
1588
|
-
/**
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
* should stay stationary as this block is positioned.
|
|
1596
|
-
* @package
|
|
1597
|
-
*/
|
|
1598
|
-
BlockSvg.prototype.positionNearConnection = function(
|
|
1599
|
-
sourceConnection, targetConnection) {
|
|
1600
|
-
// We only need to position the new block if it's before the existing one,
|
|
1601
|
-
// otherwise its position is set by the previous block.
|
|
1602
|
-
if (sourceConnection.type === ConnectionType.NEXT_STATEMENT ||
|
|
1603
|
-
sourceConnection.type === ConnectionType.INPUT_VALUE) {
|
|
1604
|
-
const dx = targetConnection.x - sourceConnection.x;
|
|
1605
|
-
const dy = targetConnection.y - sourceConnection.y;
|
|
1654
|
+
/**
|
|
1655
|
+
* @return {?BlockSvg} The next statement block or null.
|
|
1656
|
+
* @override
|
|
1657
|
+
*/
|
|
1658
|
+
getNextBlock() {
|
|
1659
|
+
return /** @type {?BlockSvg} */ (super.getNextBlock());
|
|
1660
|
+
}
|
|
1606
1661
|
|
|
1607
|
-
|
|
1662
|
+
/**
|
|
1663
|
+
* @return {?BlockSvg} The previou statement block or null.
|
|
1664
|
+
* @override
|
|
1665
|
+
*/
|
|
1666
|
+
getPreviousBlock() {
|
|
1667
|
+
return /** @type {?BlockSvg} */ (super.getPreviousBlock());
|
|
1608
1668
|
}
|
|
1609
|
-
};
|
|
1610
1669
|
|
|
1611
|
-
/**
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1670
|
+
/**
|
|
1671
|
+
* @return {?RenderedConnection} The first statement connection or null.
|
|
1672
|
+
* @package
|
|
1673
|
+
* @override
|
|
1674
|
+
*/
|
|
1675
|
+
getFirstStatementConnection() {
|
|
1676
|
+
return /** @type {?RenderedConnection} */ (
|
|
1677
|
+
super.getFirstStatementConnection());
|
|
1678
|
+
}
|
|
1619
1679
|
|
|
1620
|
-
/**
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
*/
|
|
1626
|
-
|
|
1627
|
-
return /** @type {!BlockSvg} */ (
|
|
1628
|
-
BlockSvg.superClass_.getRootBlock.call(this));
|
|
1629
|
-
};
|
|
1680
|
+
/**
|
|
1681
|
+
* @return {!BlockSvg} The top block in a stack.
|
|
1682
|
+
* @override
|
|
1683
|
+
*/
|
|
1684
|
+
getTopStackBlock() {
|
|
1685
|
+
return /** @type {!BlockSvg} */ (super.getTopStackBlock());
|
|
1686
|
+
}
|
|
1630
1687
|
|
|
1631
|
-
/**
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
return; // Don't allow recursive renders.
|
|
1688
|
+
/**
|
|
1689
|
+
* @param {boolean} ordered Sort the list if true.
|
|
1690
|
+
* @return {!Array<!BlockSvg>} Children of this block.
|
|
1691
|
+
* @override
|
|
1692
|
+
*/
|
|
1693
|
+
getChildren(ordered) {
|
|
1694
|
+
return /** @type {!Array<!BlockSvg>} */ (super.getChildren(ordered));
|
|
1639
1695
|
}
|
|
1640
|
-
this.renderIsInProgress_ = true;
|
|
1641
|
-
try {
|
|
1642
|
-
this.rendered = true;
|
|
1643
|
-
dom.startTextWidthCache();
|
|
1644
1696
|
|
|
1645
|
-
|
|
1646
|
-
|
|
1697
|
+
/**
|
|
1698
|
+
* @param {boolean} ordered Sort the list if true.
|
|
1699
|
+
* @return {!Array<!BlockSvg>} Descendants of this block.
|
|
1700
|
+
* @override
|
|
1701
|
+
*/
|
|
1702
|
+
getDescendants(ordered) {
|
|
1703
|
+
return /** @type {!Array<!BlockSvg>} */ (super.getDescendants(ordered));
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
/**
|
|
1707
|
+
* @param {string} name The name of the input.
|
|
1708
|
+
* @return {?BlockSvg} The attached value block, or null if the input is
|
|
1709
|
+
* either disconnected or if the input does not exist.
|
|
1710
|
+
* @override
|
|
1711
|
+
*/
|
|
1712
|
+
getInputTargetBlock(name) {
|
|
1713
|
+
return /** @type {?BlockSvg} */ (super.getInputTargetBlock(name));
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
/**
|
|
1717
|
+
* Return the top-most block in this block's tree.
|
|
1718
|
+
* This will return itself if this block is at the top level.
|
|
1719
|
+
* @return {!BlockSvg} The root block.
|
|
1720
|
+
* @override
|
|
1721
|
+
*/
|
|
1722
|
+
getRootBlock() {
|
|
1723
|
+
return /** @type {!BlockSvg} */ (super.getRootBlock());
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
/**
|
|
1727
|
+
* Lays out and reflows a block based on its contents and settings.
|
|
1728
|
+
* @param {boolean=} opt_bubble If false, just render this block.
|
|
1729
|
+
* If true, also render block's parent, grandparent, etc. Defaults to true.
|
|
1730
|
+
*/
|
|
1731
|
+
render(opt_bubble) {
|
|
1732
|
+
if (this.renderIsInProgress_) {
|
|
1733
|
+
return; // Don't allow recursive renders.
|
|
1647
1734
|
}
|
|
1648
|
-
this.
|
|
1649
|
-
|
|
1735
|
+
this.renderIsInProgress_ = true;
|
|
1736
|
+
try {
|
|
1737
|
+
this.rendered = true;
|
|
1738
|
+
dom.startTextWidthCache();
|
|
1650
1739
|
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1740
|
+
if (this.isCollapsed()) {
|
|
1741
|
+
this.updateCollapsed_();
|
|
1742
|
+
}
|
|
1743
|
+
this.workspace.getRenderer().render(this);
|
|
1744
|
+
this.updateConnectionLocations_();
|
|
1745
|
+
|
|
1746
|
+
if (opt_bubble !== false) {
|
|
1747
|
+
const parentBlock = this.getParent();
|
|
1748
|
+
if (parentBlock) {
|
|
1749
|
+
parentBlock.render(true);
|
|
1750
|
+
} else {
|
|
1751
|
+
// Top-most block. Fire an event to allow scrollbars to resize.
|
|
1752
|
+
this.workspace.resizeContents();
|
|
1753
|
+
}
|
|
1658
1754
|
}
|
|
1659
|
-
}
|
|
1660
1755
|
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1756
|
+
dom.stopTextWidthCache();
|
|
1757
|
+
this.updateMarkers_();
|
|
1758
|
+
} finally {
|
|
1759
|
+
this.renderIsInProgress_ = false;
|
|
1760
|
+
}
|
|
1665
1761
|
}
|
|
1666
|
-
};
|
|
1667
1762
|
|
|
1668
|
-
/**
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1763
|
+
/**
|
|
1764
|
+
* Redraw any attached marker or cursor svgs if needed.
|
|
1765
|
+
* @protected
|
|
1766
|
+
*/
|
|
1767
|
+
updateMarkers_() {
|
|
1768
|
+
if (this.workspace.keyboardAccessibilityMode && this.pathObject.cursorSvg) {
|
|
1769
|
+
this.workspace.getCursor().draw();
|
|
1770
|
+
}
|
|
1771
|
+
if (this.workspace.keyboardAccessibilityMode && this.pathObject.markerSvg) {
|
|
1772
|
+
// TODO(#4592): Update all markers on the block.
|
|
1773
|
+
this.workspace.getMarker(MarkerManager.LOCAL_MARKER).draw();
|
|
1774
|
+
}
|
|
1679
1775
|
}
|
|
1680
|
-
};
|
|
1681
1776
|
|
|
1682
|
-
/**
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
conn
|
|
1703
|
-
|
|
1704
|
-
conn.
|
|
1777
|
+
/**
|
|
1778
|
+
* Update all of the connections on this block with the new locations
|
|
1779
|
+
* calculated during rendering. Also move all of the connected blocks based
|
|
1780
|
+
* on the new connection locations.
|
|
1781
|
+
* @private
|
|
1782
|
+
*/
|
|
1783
|
+
updateConnectionLocations_() {
|
|
1784
|
+
const blockTL = this.getRelativeToSurfaceXY();
|
|
1785
|
+
// Don't tighten previous or output connections because they are inferior
|
|
1786
|
+
// connections.
|
|
1787
|
+
if (this.previousConnection) {
|
|
1788
|
+
this.previousConnection.moveToOffset(blockTL);
|
|
1789
|
+
}
|
|
1790
|
+
if (this.outputConnection) {
|
|
1791
|
+
this.outputConnection.moveToOffset(blockTL);
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
for (let i = 0; i < this.inputList.length; i++) {
|
|
1795
|
+
const conn =
|
|
1796
|
+
/** @type {!RenderedConnection} */ (this.inputList[i].connection);
|
|
1797
|
+
if (conn) {
|
|
1798
|
+
conn.moveToOffset(blockTL);
|
|
1799
|
+
if (conn.isConnected()) {
|
|
1800
|
+
conn.tighten();
|
|
1801
|
+
}
|
|
1705
1802
|
}
|
|
1706
1803
|
}
|
|
1707
|
-
}
|
|
1708
1804
|
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1805
|
+
if (this.nextConnection) {
|
|
1806
|
+
this.nextConnection.moveToOffset(blockTL);
|
|
1807
|
+
if (this.nextConnection.isConnected()) {
|
|
1808
|
+
this.nextConnection.tighten();
|
|
1809
|
+
}
|
|
1713
1810
|
}
|
|
1714
1811
|
}
|
|
1715
|
-
};
|
|
1716
1812
|
|
|
1717
|
-
/**
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
}
|
|
1813
|
+
/**
|
|
1814
|
+
* Add the cursor SVG to this block's SVG group.
|
|
1815
|
+
* @param {SVGElement} cursorSvg The SVG root of the cursor to be added to the
|
|
1816
|
+
* block SVG group.
|
|
1817
|
+
* @package
|
|
1818
|
+
*/
|
|
1819
|
+
setCursorSvg(cursorSvg) {
|
|
1820
|
+
this.pathObject.setCursorSvg(cursorSvg);
|
|
1821
|
+
}
|
|
1726
1822
|
|
|
1727
|
-
/**
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
}
|
|
1823
|
+
/**
|
|
1824
|
+
* Add the marker SVG to this block's SVG group.
|
|
1825
|
+
* @param {SVGElement} markerSvg The SVG root of the marker to be added to the
|
|
1826
|
+
* block SVG group.
|
|
1827
|
+
* @package
|
|
1828
|
+
*/
|
|
1829
|
+
setMarkerSvg(markerSvg) {
|
|
1830
|
+
this.pathObject.setMarkerSvg(markerSvg);
|
|
1831
|
+
}
|
|
1736
1832
|
|
|
1737
|
-
/**
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
}
|
|
1833
|
+
/**
|
|
1834
|
+
* Returns a bounding box describing the dimensions of this block
|
|
1835
|
+
* and any blocks stacked below it.
|
|
1836
|
+
* @return {!{height: number, width: number}} Object with height and width
|
|
1837
|
+
* properties in workspace units.
|
|
1838
|
+
* @package
|
|
1839
|
+
*/
|
|
1840
|
+
getHeightWidth() {
|
|
1841
|
+
let height = this.height;
|
|
1842
|
+
let width = this.width;
|
|
1843
|
+
// Recursively add size of subsequent blocks.
|
|
1844
|
+
const nextBlock = this.getNextBlock();
|
|
1845
|
+
if (nextBlock) {
|
|
1846
|
+
const nextHeightWidth = nextBlock.getHeightWidth();
|
|
1847
|
+
const workspace = /** @type {!WorkspaceSvg} */ (this.workspace);
|
|
1848
|
+
const tabHeight = workspace.getRenderer().getConstants().NOTCH_HEIGHT;
|
|
1849
|
+
height += nextHeightWidth.height - tabHeight;
|
|
1850
|
+
width = Math.max(width, nextHeightWidth.width);
|
|
1851
|
+
}
|
|
1852
|
+
return {height: height, width: width};
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
/**
|
|
1856
|
+
* Visual effect to show that if the dragging block is dropped, this block
|
|
1857
|
+
* will be replaced. If a shadow block, it will disappear. Otherwise it will
|
|
1858
|
+
* bump.
|
|
1859
|
+
* @param {boolean} add True if highlighting should be added.
|
|
1860
|
+
* @package
|
|
1861
|
+
*/
|
|
1862
|
+
fadeForReplacement(add) {
|
|
1863
|
+
this.pathObject.updateReplacementFade(add);
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
/**
|
|
1867
|
+
* Visual effect to show that if the dragging block is dropped it will connect
|
|
1868
|
+
* to this input.
|
|
1869
|
+
* @param {Connection} conn The connection on the input to highlight.
|
|
1870
|
+
* @param {boolean} add True if highlighting should be added.
|
|
1871
|
+
* @package
|
|
1872
|
+
*/
|
|
1873
|
+
highlightShapeForInput(conn, add) {
|
|
1874
|
+
this.pathObject.updateShapeForInputHighlight(conn, add);
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1758
1877
|
|
|
1759
1878
|
/**
|
|
1760
|
-
*
|
|
1761
|
-
*
|
|
1762
|
-
* @
|
|
1763
|
-
* @package
|
|
1879
|
+
* Constant for identifying rows that are to be rendered inline.
|
|
1880
|
+
* Don't collide with Blockly.inputTypes.
|
|
1881
|
+
* @const
|
|
1764
1882
|
*/
|
|
1765
|
-
BlockSvg.
|
|
1766
|
-
this.pathObject.updateReplacementFade(add);
|
|
1767
|
-
};
|
|
1883
|
+
BlockSvg.INLINE = -1;
|
|
1768
1884
|
|
|
1769
1885
|
/**
|
|
1770
|
-
*
|
|
1771
|
-
*
|
|
1772
|
-
*
|
|
1773
|
-
* @
|
|
1774
|
-
* @
|
|
1886
|
+
* ID to give the "collapsed warnings" warning. Allows us to remove the
|
|
1887
|
+
* "collapsed warnings" warning without removing any warnings that belong to
|
|
1888
|
+
* the block.
|
|
1889
|
+
* @type {string}
|
|
1890
|
+
* @const
|
|
1775
1891
|
*/
|
|
1776
|
-
BlockSvg.
|
|
1777
|
-
this.pathObject.updateShapeForInputHighlight(conn, add);
|
|
1778
|
-
};
|
|
1892
|
+
BlockSvg.COLLAPSED_WARNING_ID = 'TEMP_COLLAPSED_WARNING_';
|
|
1779
1893
|
|
|
1780
1894
|
exports.BlockSvg = BlockSvg;
|