blockly 7.20211209.4 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blockly.d.ts +18963 -18432
- package/blockly.min.js +852 -844
- package/blockly_compressed.js +669 -664
- 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 +41 -27
- package/blocks/text.js +22 -13
- package/blocks/variables.js +14 -3
- package/blocks/variables_dynamic.js +13 -3
- package/blocks_compressed.js +146 -141
- 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 +19 -9
- 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/dart_compressed.js +40 -43
- package/dart_compressed.js.map +1 -1
- 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/javascript_compressed.js +37 -39
- package/javascript_compressed.js.map +1 -1
- package/lua_compressed.js +39 -42
- package/lua_compressed.js.map +1 -1
- package/msg/az.js +2 -2
- package/msg/be.js +4 -4
- package/msg/cs.js +15 -15
- package/msg/de.js +1 -1
- package/msg/diq.js +1 -1
- package/msg/eo.js +1 -1
- package/msg/es.js +1 -1
- package/msg/fa.js +1 -1
- package/msg/fr.js +4 -4
- package/msg/he.js +1 -1
- package/msg/hr.js +2 -2
- package/msg/hy.js +2 -2
- package/msg/id.js +12 -12
- package/msg/inh.js +14 -14
- package/msg/ja.js +7 -7
- package/msg/lv.js +29 -29
- package/msg/pa.js +3 -3
- package/msg/smn.js +436 -0
- package/msg/te.js +1 -1
- package/msg/yue.js +1 -1
- package/msg/zh-hans.js +3 -3
- package/msg/zh-hant.js +3 -3
- package/package.json +7 -6
- package/php_compressed.js +38 -42
- package/php_compressed.js.map +1 -1
- package/python_compressed.js +26 -25
- package/python_compressed.js.map +1 -1
- package/blocks/all.js +0 -23
package/core/connection.js
CHANGED
|
@@ -20,6 +20,8 @@ const blocks = goog.require('Blockly.serialization.blocks');
|
|
|
20
20
|
const eventUtils = goog.require('Blockly.Events.utils');
|
|
21
21
|
/* eslint-disable-next-line no-unused-vars */
|
|
22
22
|
const {Block} = goog.requireType('Blockly.Block');
|
|
23
|
+
/* eslint-disable-next-line no-unused-vars */
|
|
24
|
+
const {BlockMove} = goog.requireType('Blockly.Events.BlockMove');
|
|
23
25
|
const {ConnectionType} = goog.require('Blockly.ConnectionType');
|
|
24
26
|
/* eslint-disable-next-line no-unused-vars */
|
|
25
27
|
const {IASTNodeLocationWithBlock} = goog.require('Blockly.IASTNodeLocationWithBlock');
|
|
@@ -35,233 +37,642 @@ goog.require('Blockly.constants');
|
|
|
35
37
|
|
|
36
38
|
/**
|
|
37
39
|
* Class for a connection between blocks.
|
|
38
|
-
* @param {!Block} source The block establishing this connection.
|
|
39
|
-
* @param {number} type The type of the connection.
|
|
40
|
-
* @constructor
|
|
41
40
|
* @implements {IASTNodeLocationWithBlock}
|
|
42
41
|
* @alias Blockly.Connection
|
|
43
42
|
*/
|
|
44
|
-
|
|
43
|
+
class Connection {
|
|
45
44
|
/**
|
|
46
|
-
* @
|
|
45
|
+
* @param {!Block} source The block establishing this connection.
|
|
46
|
+
* @param {number} type The type of the connection.
|
|
47
|
+
*/
|
|
48
|
+
constructor(source, type) {
|
|
49
|
+
/**
|
|
50
|
+
* @type {!Block}
|
|
51
|
+
* @protected
|
|
52
|
+
*/
|
|
53
|
+
this.sourceBlock_ = source;
|
|
54
|
+
/** @type {number} */
|
|
55
|
+
this.type = type;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Connection this connection connects to. Null if not connected.
|
|
59
|
+
* @type {Connection}
|
|
60
|
+
*/
|
|
61
|
+
this.targetConnection = null;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Has this connection been disposed of?
|
|
65
|
+
* @type {boolean}
|
|
66
|
+
* @package
|
|
67
|
+
*/
|
|
68
|
+
this.disposed = false;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* List of compatible value types. Null if all types are compatible.
|
|
72
|
+
* @type {Array}
|
|
73
|
+
* @private
|
|
74
|
+
*/
|
|
75
|
+
this.check_ = null;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* DOM representation of a shadow block, or null if none.
|
|
79
|
+
* @type {Element}
|
|
80
|
+
* @private
|
|
81
|
+
*/
|
|
82
|
+
this.shadowDom_ = null;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Horizontal location of this connection.
|
|
86
|
+
* @type {number}
|
|
87
|
+
* @package
|
|
88
|
+
*/
|
|
89
|
+
this.x = 0;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Vertical location of this connection.
|
|
93
|
+
* @type {number}
|
|
94
|
+
* @package
|
|
95
|
+
*/
|
|
96
|
+
this.y = 0;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @type {?blocks.State}
|
|
100
|
+
* @private
|
|
101
|
+
*/
|
|
102
|
+
this.shadowState_ = null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Connect two connections together. This is the connection on the superior
|
|
107
|
+
* block.
|
|
108
|
+
* @param {!Connection} childConnection Connection on inferior block.
|
|
47
109
|
* @protected
|
|
48
110
|
*/
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
111
|
+
connect_(childConnection) {
|
|
112
|
+
const INPUT = ConnectionType.INPUT_VALUE;
|
|
113
|
+
const parentConnection = this;
|
|
114
|
+
const parentBlock = parentConnection.getSourceBlock();
|
|
115
|
+
const childBlock = childConnection.getSourceBlock();
|
|
116
|
+
|
|
117
|
+
// Make sure the childConnection is available.
|
|
118
|
+
if (childConnection.isConnected()) {
|
|
119
|
+
childConnection.disconnect();
|
|
120
|
+
}
|
|
53
121
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
122
|
+
// Make sure the parentConnection is available.
|
|
123
|
+
let orphan;
|
|
124
|
+
if (parentConnection.isConnected()) {
|
|
125
|
+
const shadowState = parentConnection.stashShadowState_();
|
|
126
|
+
const target = parentConnection.targetBlock();
|
|
127
|
+
if (target.isShadow()) {
|
|
128
|
+
target.dispose(false);
|
|
129
|
+
} else {
|
|
130
|
+
parentConnection.disconnect();
|
|
131
|
+
orphan = target;
|
|
132
|
+
}
|
|
133
|
+
parentConnection.applyShadowState_(shadowState);
|
|
134
|
+
}
|
|
66
135
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
*/
|
|
71
|
-
|
|
136
|
+
// Connect the new connection to the parent.
|
|
137
|
+
let event;
|
|
138
|
+
if (eventUtils.isEnabled()) {
|
|
139
|
+
event = /** @type {!BlockMove} */
|
|
140
|
+
(new (eventUtils.get(eventUtils.BLOCK_MOVE))(childBlock));
|
|
141
|
+
}
|
|
142
|
+
connectReciprocally(parentConnection, childConnection);
|
|
143
|
+
childBlock.setParent(parentBlock);
|
|
144
|
+
if (event) {
|
|
145
|
+
event.recordNew();
|
|
146
|
+
eventUtils.fire(event);
|
|
147
|
+
}
|
|
72
148
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
149
|
+
// Deal with the orphan if it exists.
|
|
150
|
+
if (orphan) {
|
|
151
|
+
const orphanConnection = parentConnection.type === INPUT ?
|
|
152
|
+
orphan.outputConnection :
|
|
153
|
+
orphan.previousConnection;
|
|
154
|
+
const connection = Connection.getConnectionForOrphanedConnection(
|
|
155
|
+
childBlock, /** @type {!Connection} */ (orphanConnection));
|
|
156
|
+
if (connection) {
|
|
157
|
+
orphanConnection.connect(connection);
|
|
158
|
+
} else {
|
|
159
|
+
orphanConnection.onFailedConnect(parentConnection);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
79
163
|
|
|
80
|
-
/**
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
164
|
+
/**
|
|
165
|
+
* Dispose of this connection and deal with connected blocks.
|
|
166
|
+
* @package
|
|
167
|
+
*/
|
|
168
|
+
dispose() {
|
|
169
|
+
// isConnected returns true for shadows and non-shadows.
|
|
170
|
+
if (this.isConnected()) {
|
|
171
|
+
// Destroy the attached shadow block & its children (if it exists).
|
|
172
|
+
this.setShadowStateInternal_();
|
|
173
|
+
|
|
174
|
+
const targetBlock = this.targetBlock();
|
|
175
|
+
if (targetBlock) {
|
|
176
|
+
// Disconnect the attached normal block.
|
|
177
|
+
targetBlock.unplug();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
86
180
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
* @type {Element}
|
|
90
|
-
* @private
|
|
91
|
-
*/
|
|
92
|
-
Connection.prototype.shadowDom_ = null;
|
|
181
|
+
this.disposed = true;
|
|
182
|
+
}
|
|
93
183
|
|
|
94
|
-
/**
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
184
|
+
/**
|
|
185
|
+
* Get the source block for this connection.
|
|
186
|
+
* @return {!Block} The source block.
|
|
187
|
+
*/
|
|
188
|
+
getSourceBlock() {
|
|
189
|
+
return this.sourceBlock_;
|
|
190
|
+
}
|
|
100
191
|
|
|
101
|
-
/**
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
192
|
+
/**
|
|
193
|
+
* Does the connection belong to a superior block (higher in the source
|
|
194
|
+
* stack)?
|
|
195
|
+
* @return {boolean} True if connection faces down or right.
|
|
196
|
+
*/
|
|
197
|
+
isSuperior() {
|
|
198
|
+
return this.type === ConnectionType.INPUT_VALUE ||
|
|
199
|
+
this.type === ConnectionType.NEXT_STATEMENT;
|
|
200
|
+
}
|
|
107
201
|
|
|
108
|
-
/**
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
202
|
+
/**
|
|
203
|
+
* Is the connection connected?
|
|
204
|
+
* @return {boolean} True if connection is connected to another connection.
|
|
205
|
+
*/
|
|
206
|
+
isConnected() {
|
|
207
|
+
return !!this.targetConnection;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Get the workspace's connection type checker object.
|
|
212
|
+
* @return {!IConnectionChecker} The connection type checker for the
|
|
213
|
+
* source block's workspace.
|
|
214
|
+
* @package
|
|
215
|
+
*/
|
|
216
|
+
getConnectionChecker() {
|
|
217
|
+
return this.sourceBlock_.workspace.connectionChecker;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Called when an attempted connection fails. NOP by default (i.e. for
|
|
222
|
+
* headless workspaces).
|
|
223
|
+
* @param {!Connection} _otherConnection Connection that this connection
|
|
224
|
+
* failed to connect to.
|
|
225
|
+
* @package
|
|
226
|
+
*/
|
|
227
|
+
onFailedConnect(_otherConnection) {
|
|
228
|
+
// NOP
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Connect this connection to another connection.
|
|
233
|
+
* @param {!Connection} otherConnection Connection to connect to.
|
|
234
|
+
* @return {boolean} Whether the the blocks are now connected or not.
|
|
235
|
+
*/
|
|
236
|
+
connect(otherConnection) {
|
|
237
|
+
if (this.targetConnection === otherConnection) {
|
|
238
|
+
// Already connected together. NOP.
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const checker = this.getConnectionChecker();
|
|
243
|
+
if (checker.canConnect(this, otherConnection, false)) {
|
|
244
|
+
const eventGroup = eventUtils.getGroup();
|
|
245
|
+
if (!eventGroup) {
|
|
246
|
+
eventUtils.setGroup(true);
|
|
247
|
+
}
|
|
248
|
+
// Determine which block is superior (higher in the source stack).
|
|
249
|
+
if (this.isSuperior()) {
|
|
250
|
+
// Superior block.
|
|
251
|
+
this.connect_(otherConnection);
|
|
252
|
+
} else {
|
|
253
|
+
// Inferior block.
|
|
254
|
+
otherConnection.connect_(this);
|
|
255
|
+
}
|
|
256
|
+
if (!eventGroup) {
|
|
257
|
+
eventUtils.setGroup(false);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return this.isConnected();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Disconnect this connection.
|
|
266
|
+
*/
|
|
267
|
+
disconnect() {
|
|
268
|
+
const otherConnection = this.targetConnection;
|
|
269
|
+
if (!otherConnection) {
|
|
270
|
+
throw Error('Source connection not connected.');
|
|
271
|
+
}
|
|
272
|
+
if (otherConnection.targetConnection !== this) {
|
|
273
|
+
throw Error('Target connection not connected to source connection.');
|
|
274
|
+
}
|
|
275
|
+
let parentBlock;
|
|
276
|
+
let childBlock;
|
|
277
|
+
let parentConnection;
|
|
278
|
+
if (this.isSuperior()) {
|
|
279
|
+
// Superior block.
|
|
280
|
+
parentBlock = this.sourceBlock_;
|
|
281
|
+
childBlock = otherConnection.getSourceBlock();
|
|
282
|
+
parentConnection = this;
|
|
132
283
|
} else {
|
|
133
|
-
|
|
134
|
-
|
|
284
|
+
// Inferior block.
|
|
285
|
+
parentBlock = otherConnection.getSourceBlock();
|
|
286
|
+
childBlock = this.sourceBlock_;
|
|
287
|
+
parentConnection = otherConnection;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const eventGroup = eventUtils.getGroup();
|
|
291
|
+
if (!eventGroup) {
|
|
292
|
+
eventUtils.setGroup(true);
|
|
293
|
+
}
|
|
294
|
+
this.disconnectInternal_(parentBlock, childBlock);
|
|
295
|
+
if (!childBlock.isShadow()) {
|
|
296
|
+
// If we were disconnecting a shadow, no need to spawn a new one.
|
|
297
|
+
parentConnection.respawnShadow_();
|
|
298
|
+
}
|
|
299
|
+
if (!eventGroup) {
|
|
300
|
+
eventUtils.setGroup(false);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Disconnect two blocks that are connected by this connection.
|
|
306
|
+
* @param {!Block} parentBlock The superior block.
|
|
307
|
+
* @param {!Block} childBlock The inferior block.
|
|
308
|
+
* @protected
|
|
309
|
+
*/
|
|
310
|
+
disconnectInternal_(parentBlock, childBlock) {
|
|
311
|
+
let event;
|
|
312
|
+
if (eventUtils.isEnabled()) {
|
|
313
|
+
event = /** @type {!BlockMove} */
|
|
314
|
+
(new (eventUtils.get(eventUtils.BLOCK_MOVE))(childBlock));
|
|
315
|
+
}
|
|
316
|
+
const otherConnection = this.targetConnection;
|
|
317
|
+
otherConnection.targetConnection = null;
|
|
318
|
+
this.targetConnection = null;
|
|
319
|
+
childBlock.setParent(null);
|
|
320
|
+
if (event) {
|
|
321
|
+
event.recordNew();
|
|
322
|
+
eventUtils.fire(event);
|
|
135
323
|
}
|
|
136
|
-
parentConnection.applyShadowState_(shadowState);
|
|
137
324
|
}
|
|
138
325
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
326
|
+
/**
|
|
327
|
+
* Respawn the shadow block if there was one connected to the this connection.
|
|
328
|
+
* @protected
|
|
329
|
+
*/
|
|
330
|
+
respawnShadow_() {
|
|
331
|
+
// Have to keep respawnShadow_ for backwards compatibility.
|
|
332
|
+
this.createShadowBlock_(true);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Returns the block that this connection connects to.
|
|
337
|
+
* @return {?Block} The connected block or null if none is connected.
|
|
338
|
+
*/
|
|
339
|
+
targetBlock() {
|
|
340
|
+
if (this.isConnected()) {
|
|
341
|
+
return this.targetConnection.getSourceBlock();
|
|
342
|
+
}
|
|
343
|
+
return null;
|
|
143
344
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Function to be called when this connection's compatible types have changed.
|
|
348
|
+
* @protected
|
|
349
|
+
*/
|
|
350
|
+
onCheckChanged_() {
|
|
351
|
+
// The new value type may not be compatible with the existing connection.
|
|
352
|
+
if (this.isConnected() &&
|
|
353
|
+
(!this.targetConnection ||
|
|
354
|
+
!this.getConnectionChecker().canConnect(
|
|
355
|
+
this, this.targetConnection, false))) {
|
|
356
|
+
const child = this.isSuperior() ? this.targetBlock() : this.sourceBlock_;
|
|
357
|
+
child.unplug();
|
|
358
|
+
}
|
|
149
359
|
}
|
|
150
360
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
361
|
+
/**
|
|
362
|
+
* Change a connection's compatibility.
|
|
363
|
+
* @param {?(string|!Array<string>)} check Compatible value type or list of
|
|
364
|
+
* value types. Null if all types are compatible.
|
|
365
|
+
* @return {!Connection} The connection being modified
|
|
366
|
+
* (to allow chaining).
|
|
367
|
+
*/
|
|
368
|
+
setCheck(check) {
|
|
369
|
+
if (check) {
|
|
370
|
+
// Ensure that check is in an array.
|
|
371
|
+
if (!Array.isArray(check)) {
|
|
372
|
+
check = [check];
|
|
373
|
+
}
|
|
374
|
+
this.check_ = check;
|
|
375
|
+
this.onCheckChanged_();
|
|
160
376
|
} else {
|
|
161
|
-
|
|
377
|
+
this.check_ = null;
|
|
162
378
|
}
|
|
379
|
+
return this;
|
|
163
380
|
}
|
|
164
|
-
};
|
|
165
381
|
|
|
382
|
+
/**
|
|
383
|
+
* Get a connection's compatibility.
|
|
384
|
+
* @return {?Array} List of compatible value types.
|
|
385
|
+
* Null if all types are compatible.
|
|
386
|
+
* @public
|
|
387
|
+
*/
|
|
388
|
+
getCheck() {
|
|
389
|
+
return this.check_;
|
|
390
|
+
}
|
|
166
391
|
|
|
167
|
-
/**
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
392
|
+
/**
|
|
393
|
+
* Changes the connection's shadow block.
|
|
394
|
+
* @param {?Element} shadowDom DOM representation of a block or null.
|
|
395
|
+
*/
|
|
396
|
+
setShadowDom(shadowDom) {
|
|
397
|
+
this.setShadowStateInternal_({shadowDom: shadowDom});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Returns the xml representation of the connection's shadow block.
|
|
402
|
+
* @param {boolean=} returnCurrent If true, and the shadow block is currently
|
|
403
|
+
* attached to this connection, this serializes the state of that block
|
|
404
|
+
* and returns it (so that field values are correct). Otherwise the saved
|
|
405
|
+
* shadowDom is just returned.
|
|
406
|
+
* @return {?Element} Shadow DOM representation of a block or null.
|
|
407
|
+
*/
|
|
408
|
+
getShadowDom(returnCurrent) {
|
|
409
|
+
return (returnCurrent && this.targetBlock().isShadow()) ?
|
|
410
|
+
/** @type {!Element} */ (Xml.blockToDom(
|
|
411
|
+
/** @type {!Block} */ (this.targetBlock()))) :
|
|
412
|
+
this.shadowDom_;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Changes the connection's shadow block.
|
|
417
|
+
* @param {?blocks.State} shadowState An state represetation of the block or
|
|
418
|
+
* null.
|
|
419
|
+
*/
|
|
420
|
+
setShadowState(shadowState) {
|
|
421
|
+
this.setShadowStateInternal_({shadowState: shadowState});
|
|
422
|
+
}
|
|
176
423
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
424
|
+
/**
|
|
425
|
+
* Returns the serialized object representation of the connection's shadow
|
|
426
|
+
* block.
|
|
427
|
+
* @param {boolean=} returnCurrent If true, and the shadow block is currently
|
|
428
|
+
* attached to this connection, this serializes the state of that block
|
|
429
|
+
* and returns it (so that field values are correct). Otherwise the saved
|
|
430
|
+
* state is just returned.
|
|
431
|
+
* @return {?blocks.State} Serialized object representation of the block, or
|
|
432
|
+
* null.
|
|
433
|
+
*/
|
|
434
|
+
getShadowState(returnCurrent) {
|
|
435
|
+
if (returnCurrent && this.targetBlock() && this.targetBlock().isShadow()) {
|
|
436
|
+
return blocks.save(/** @type {!Block} */ (this.targetBlock()));
|
|
181
437
|
}
|
|
438
|
+
return this.shadowState_;
|
|
182
439
|
}
|
|
183
440
|
|
|
184
|
-
|
|
185
|
-
|
|
441
|
+
/**
|
|
442
|
+
* Find all nearby compatible connections to this connection.
|
|
443
|
+
* Type checking does not apply, since this function is used for bumping.
|
|
444
|
+
*
|
|
445
|
+
* Headless configurations (the default) do not have neighboring connection,
|
|
446
|
+
* and always return an empty list (the default).
|
|
447
|
+
* {@link Blockly.RenderedConnection} overrides this behavior with a list
|
|
448
|
+
* computed from the rendered positioning.
|
|
449
|
+
* @param {number} _maxLimit The maximum radius to another connection.
|
|
450
|
+
* @return {!Array<!Connection>} List of connections.
|
|
451
|
+
* @package
|
|
452
|
+
*/
|
|
453
|
+
neighbours(_maxLimit) {
|
|
454
|
+
return [];
|
|
455
|
+
}
|
|
186
456
|
|
|
187
|
-
/**
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
457
|
+
/**
|
|
458
|
+
* Get the parent input of a connection.
|
|
459
|
+
* @return {?Input} The input that the connection belongs to or null if
|
|
460
|
+
* no parent exists.
|
|
461
|
+
* @package
|
|
462
|
+
*/
|
|
463
|
+
getParentInput() {
|
|
464
|
+
let parentInput = null;
|
|
465
|
+
const inputs = this.sourceBlock_.inputList;
|
|
466
|
+
for (let i = 0; i < inputs.length; i++) {
|
|
467
|
+
if (inputs[i].connection === this) {
|
|
468
|
+
parentInput = inputs[i];
|
|
469
|
+
break;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return parentInput;
|
|
473
|
+
}
|
|
194
474
|
|
|
195
|
-
/**
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
475
|
+
/**
|
|
476
|
+
* This method returns a string describing this Connection in developer terms
|
|
477
|
+
* (English only). Intended to on be used in console logs and errors.
|
|
478
|
+
* @return {string} The description.
|
|
479
|
+
*/
|
|
480
|
+
toString() {
|
|
481
|
+
const block = this.sourceBlock_;
|
|
482
|
+
if (!block) {
|
|
483
|
+
return 'Orphan Connection';
|
|
484
|
+
}
|
|
485
|
+
let msg;
|
|
486
|
+
if (block.outputConnection === this) {
|
|
487
|
+
msg = 'Output Connection of ';
|
|
488
|
+
} else if (block.previousConnection === this) {
|
|
489
|
+
msg = 'Previous Connection of ';
|
|
490
|
+
} else if (block.nextConnection === this) {
|
|
491
|
+
msg = 'Next Connection of ';
|
|
492
|
+
} else {
|
|
493
|
+
let parentInput = null;
|
|
494
|
+
for (let i = 0, input; (input = block.inputList[i]); i++) {
|
|
495
|
+
if (input.connection === this) {
|
|
496
|
+
parentInput = input;
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if (parentInput) {
|
|
501
|
+
msg = 'Input "' + parentInput.name + '" connection on ';
|
|
502
|
+
} else {
|
|
503
|
+
console.warn('Connection not actually connected to sourceBlock_');
|
|
504
|
+
return 'Orphan Connection';
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return msg + block.toDevString();
|
|
508
|
+
}
|
|
203
509
|
|
|
204
|
-
/**
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
510
|
+
/**
|
|
511
|
+
* Returns the state of the shadowDom_ and shadowState_ properties, then
|
|
512
|
+
* temporarily sets those properties to null so no shadow respawns.
|
|
513
|
+
* @return {{shadowDom: ?Element, shadowState: ?blocks.State}} The state of
|
|
514
|
+
* both the shadowDom_ and shadowState_ properties.
|
|
515
|
+
* @private
|
|
516
|
+
*/
|
|
517
|
+
stashShadowState_() {
|
|
518
|
+
const shadowDom = this.getShadowDom(true);
|
|
519
|
+
const shadowState = this.getShadowState(true);
|
|
520
|
+
// Set to null so it doesn't respawn.
|
|
521
|
+
this.shadowDom_ = null;
|
|
522
|
+
this.shadowState_ = null;
|
|
523
|
+
return {shadowDom, shadowState};
|
|
524
|
+
}
|
|
211
525
|
|
|
212
|
-
/**
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
526
|
+
/**
|
|
527
|
+
* Reapplies the stashed state of the shadowDom_ and shadowState_ properties.
|
|
528
|
+
* @param {{shadowDom: ?Element, shadowState: ?blocks.State}} param0 The state
|
|
529
|
+
* to reapply to the shadowDom_ and shadowState_ properties.
|
|
530
|
+
* @private
|
|
531
|
+
*/
|
|
532
|
+
applyShadowState_({shadowDom, shadowState}) {
|
|
533
|
+
this.shadowDom_ = shadowDom;
|
|
534
|
+
this.shadowState_ = shadowState;
|
|
535
|
+
}
|
|
221
536
|
|
|
222
|
-
/**
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
537
|
+
/**
|
|
538
|
+
* Sets the state of the shadow of this connection.
|
|
539
|
+
* @param {{shadowDom: (?Element|undefined), shadowState:
|
|
540
|
+
* (?blocks.State|undefined)}=} param0 The state to set the shadow of this
|
|
541
|
+
* connection to.
|
|
542
|
+
* @private
|
|
543
|
+
*/
|
|
544
|
+
setShadowStateInternal_({shadowDom = null, shadowState = null} = {}) {
|
|
545
|
+
// One or both of these should always be null.
|
|
546
|
+
// If neither is null, the shadowState will get priority.
|
|
547
|
+
this.shadowDom_ = shadowDom;
|
|
548
|
+
this.shadowState_ = shadowState;
|
|
549
|
+
|
|
550
|
+
const target = this.targetBlock();
|
|
551
|
+
if (!target) {
|
|
552
|
+
this.respawnShadow_();
|
|
553
|
+
if (this.targetBlock() && this.targetBlock().isShadow()) {
|
|
554
|
+
this.serializeShadow_(this.targetBlock());
|
|
555
|
+
}
|
|
556
|
+
} else if (target.isShadow()) {
|
|
557
|
+
target.dispose(false);
|
|
558
|
+
this.respawnShadow_();
|
|
559
|
+
if (this.targetBlock() && this.targetBlock().isShadow()) {
|
|
560
|
+
this.serializeShadow_(this.targetBlock());
|
|
561
|
+
}
|
|
562
|
+
} else {
|
|
563
|
+
const shadow = this.createShadowBlock_(false);
|
|
564
|
+
this.serializeShadow_(shadow);
|
|
565
|
+
if (shadow) {
|
|
566
|
+
shadow.dispose(false);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
232
570
|
|
|
233
|
-
/**
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
571
|
+
/**
|
|
572
|
+
* Creates a shadow block based on the current shadowState_ or shadowDom_.
|
|
573
|
+
* shadowState_ gets priority.
|
|
574
|
+
* @param {boolean} attemptToConnect Whether to try to connect the shadow
|
|
575
|
+
* block to this connection or not.
|
|
576
|
+
* @return {?Block} The shadow block that was created, or null if both the
|
|
577
|
+
* shadowState_ and shadowDom_ are null.
|
|
578
|
+
* @private
|
|
579
|
+
*/
|
|
580
|
+
createShadowBlock_(attemptToConnect) {
|
|
581
|
+
const parentBlock = this.getSourceBlock();
|
|
582
|
+
const shadowState = this.getShadowState();
|
|
583
|
+
const shadowDom = this.getShadowDom();
|
|
584
|
+
if (!parentBlock.workspace || (!shadowState && !shadowDom)) {
|
|
585
|
+
return null;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
let blockShadow;
|
|
589
|
+
if (shadowState) {
|
|
590
|
+
blockShadow = blocks.appendInternal(shadowState, parentBlock.workspace, {
|
|
591
|
+
parentConnection: attemptToConnect ? this : undefined,
|
|
592
|
+
isShadow: true,
|
|
593
|
+
recordUndo: false,
|
|
594
|
+
});
|
|
595
|
+
return blockShadow;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
if (shadowDom) {
|
|
599
|
+
blockShadow = Xml.domToBlock(shadowDom, parentBlock.workspace);
|
|
600
|
+
if (attemptToConnect) {
|
|
601
|
+
if (this.type === ConnectionType.INPUT_VALUE) {
|
|
602
|
+
if (!blockShadow.outputConnection) {
|
|
603
|
+
throw new Error('Shadow block is missing an output connection');
|
|
604
|
+
}
|
|
605
|
+
if (!this.connect(blockShadow.outputConnection)) {
|
|
606
|
+
throw new Error('Could not connect shadow block to connection');
|
|
607
|
+
}
|
|
608
|
+
} else if (this.type === ConnectionType.NEXT_STATEMENT) {
|
|
609
|
+
if (!blockShadow.previousConnection) {
|
|
610
|
+
throw new Error('Shadow block is missing previous connection');
|
|
611
|
+
}
|
|
612
|
+
if (!this.connect(blockShadow.previousConnection)) {
|
|
613
|
+
throw new Error('Could not connect shadow block to connection');
|
|
614
|
+
}
|
|
615
|
+
} else {
|
|
616
|
+
throw new Error(
|
|
617
|
+
'Cannot connect a shadow block to a previous/output connection');
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
return blockShadow;
|
|
621
|
+
}
|
|
622
|
+
return null;
|
|
242
623
|
}
|
|
243
624
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
625
|
+
/**
|
|
626
|
+
* Saves the given shadow block to both the shadowDom_ and shadowState_
|
|
627
|
+
* properties, in their respective serialized forms.
|
|
628
|
+
* @param {?Block} shadow The shadow to serialize, or null.
|
|
629
|
+
* @private
|
|
630
|
+
*/
|
|
631
|
+
serializeShadow_(shadow) {
|
|
632
|
+
if (!shadow) {
|
|
633
|
+
return;
|
|
249
634
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
635
|
+
this.shadowDom_ = /** @type {!Element} */ (Xml.blockToDom(shadow));
|
|
636
|
+
this.shadowState_ = blocks.save(shadow);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Returns the connection (starting at the startBlock) which will accept
|
|
641
|
+
* the given connection. This includes compatible connection types and
|
|
642
|
+
* connection checks.
|
|
643
|
+
* @param {!Block} startBlock The block on which to start the search.
|
|
644
|
+
* @param {!Connection} orphanConnection The connection that is looking
|
|
645
|
+
* for a home.
|
|
646
|
+
* @return {?Connection} The suitable connection point on the chain of
|
|
647
|
+
* blocks, or null.
|
|
648
|
+
*/
|
|
649
|
+
static getConnectionForOrphanedConnection(startBlock, orphanConnection) {
|
|
650
|
+
if (orphanConnection.type === ConnectionType.OUTPUT_VALUE) {
|
|
651
|
+
return getConnectionForOrphanedOutput(
|
|
652
|
+
startBlock, orphanConnection.getSourceBlock());
|
|
257
653
|
}
|
|
258
|
-
|
|
259
|
-
|
|
654
|
+
// Otherwise we're dealing with a stack.
|
|
655
|
+
const connection = startBlock.lastConnectionInStack(true);
|
|
656
|
+
const checker = orphanConnection.getConnectionChecker();
|
|
657
|
+
if (connection && checker.canConnect(orphanConnection, connection, false)) {
|
|
658
|
+
return connection;
|
|
260
659
|
}
|
|
660
|
+
return null;
|
|
261
661
|
}
|
|
662
|
+
}
|
|
262
663
|
|
|
263
|
-
|
|
264
|
-
|
|
664
|
+
/**
|
|
665
|
+
* Constants for checking whether two connections are compatible.
|
|
666
|
+
*/
|
|
667
|
+
Connection.CAN_CONNECT = 0;
|
|
668
|
+
Connection.REASON_SELF_CONNECTION = 1;
|
|
669
|
+
Connection.REASON_WRONG_TYPE = 2;
|
|
670
|
+
Connection.REASON_TARGET_NULL = 3;
|
|
671
|
+
Connection.REASON_CHECKS_FAILED = 4;
|
|
672
|
+
Connection.REASON_DIFFERENT_WORKSPACES = 5;
|
|
673
|
+
Connection.REASON_SHADOW_PARENT = 6;
|
|
674
|
+
Connection.REASON_DRAG_CHECKS_FAILED = 7;
|
|
675
|
+
Connection.REASON_PREVIOUS_AND_OUTPUT = 8;
|
|
265
676
|
|
|
266
677
|
/**
|
|
267
678
|
* Update two connections to target each other.
|
|
@@ -328,404 +739,4 @@ const getConnectionForOrphanedOutput = function(startBlock, orphanBlock) {
|
|
|
328
739
|
return null;
|
|
329
740
|
};
|
|
330
741
|
|
|
331
|
-
/**
|
|
332
|
-
* Returns the connection (starting at the startBlock) which will accept
|
|
333
|
-
* the given connection. This includes compatible connection types and
|
|
334
|
-
* connection checks.
|
|
335
|
-
* @param {!Block} startBlock The block on which to start the search.
|
|
336
|
-
* @param {!Connection} orphanConnection The connection that is looking
|
|
337
|
-
* for a home.
|
|
338
|
-
* @return {?Connection} The suitable connection point on the chain of
|
|
339
|
-
* blocks, or null.
|
|
340
|
-
*/
|
|
341
|
-
Connection.getConnectionForOrphanedConnection = function(
|
|
342
|
-
startBlock, orphanConnection) {
|
|
343
|
-
if (orphanConnection.type === ConnectionType.OUTPUT_VALUE) {
|
|
344
|
-
return getConnectionForOrphanedOutput(
|
|
345
|
-
startBlock, orphanConnection.getSourceBlock());
|
|
346
|
-
}
|
|
347
|
-
// Otherwise we're dealing with a stack.
|
|
348
|
-
const connection = startBlock.lastConnectionInStack(true);
|
|
349
|
-
const checker = orphanConnection.getConnectionChecker();
|
|
350
|
-
if (connection && checker.canConnect(orphanConnection, connection, false)) {
|
|
351
|
-
return connection;
|
|
352
|
-
}
|
|
353
|
-
return null;
|
|
354
|
-
};
|
|
355
|
-
|
|
356
|
-
/**
|
|
357
|
-
* Disconnect this connection.
|
|
358
|
-
*/
|
|
359
|
-
Connection.prototype.disconnect = function() {
|
|
360
|
-
const otherConnection = this.targetConnection;
|
|
361
|
-
if (!otherConnection) {
|
|
362
|
-
throw Error('Source connection not connected.');
|
|
363
|
-
}
|
|
364
|
-
if (otherConnection.targetConnection !== this) {
|
|
365
|
-
throw Error('Target connection not connected to source connection.');
|
|
366
|
-
}
|
|
367
|
-
let parentBlock;
|
|
368
|
-
let childBlock;
|
|
369
|
-
let parentConnection;
|
|
370
|
-
if (this.isSuperior()) {
|
|
371
|
-
// Superior block.
|
|
372
|
-
parentBlock = this.sourceBlock_;
|
|
373
|
-
childBlock = otherConnection.getSourceBlock();
|
|
374
|
-
parentConnection = this;
|
|
375
|
-
} else {
|
|
376
|
-
// Inferior block.
|
|
377
|
-
parentBlock = otherConnection.getSourceBlock();
|
|
378
|
-
childBlock = this.sourceBlock_;
|
|
379
|
-
parentConnection = otherConnection;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
const eventGroup = eventUtils.getGroup();
|
|
383
|
-
if (!eventGroup) {
|
|
384
|
-
eventUtils.setGroup(true);
|
|
385
|
-
}
|
|
386
|
-
this.disconnectInternal_(parentBlock, childBlock);
|
|
387
|
-
if (!childBlock.isShadow()) {
|
|
388
|
-
// If we were disconnecting a shadow, no need to spawn a new one.
|
|
389
|
-
parentConnection.respawnShadow_();
|
|
390
|
-
}
|
|
391
|
-
if (!eventGroup) {
|
|
392
|
-
eventUtils.setGroup(false);
|
|
393
|
-
}
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
/**
|
|
397
|
-
* Disconnect two blocks that are connected by this connection.
|
|
398
|
-
* @param {!Block} parentBlock The superior block.
|
|
399
|
-
* @param {!Block} childBlock The inferior block.
|
|
400
|
-
* @protected
|
|
401
|
-
*/
|
|
402
|
-
Connection.prototype.disconnectInternal_ = function(parentBlock, childBlock) {
|
|
403
|
-
let event;
|
|
404
|
-
if (eventUtils.isEnabled()) {
|
|
405
|
-
event = new (eventUtils.get(eventUtils.BLOCK_MOVE))(childBlock);
|
|
406
|
-
}
|
|
407
|
-
const otherConnection = this.targetConnection;
|
|
408
|
-
otherConnection.targetConnection = null;
|
|
409
|
-
this.targetConnection = null;
|
|
410
|
-
childBlock.setParent(null);
|
|
411
|
-
if (event) {
|
|
412
|
-
event.recordNew();
|
|
413
|
-
eventUtils.fire(event);
|
|
414
|
-
}
|
|
415
|
-
};
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* Respawn the shadow block if there was one connected to the this connection.
|
|
419
|
-
* @protected
|
|
420
|
-
*/
|
|
421
|
-
Connection.prototype.respawnShadow_ = function() {
|
|
422
|
-
// Have to keep respawnShadow_ for backwards compatibility.
|
|
423
|
-
this.createShadowBlock_(true);
|
|
424
|
-
};
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* Returns the block that this connection connects to.
|
|
428
|
-
* @return {?Block} The connected block or null if none is connected.
|
|
429
|
-
*/
|
|
430
|
-
Connection.prototype.targetBlock = function() {
|
|
431
|
-
if (this.isConnected()) {
|
|
432
|
-
return this.targetConnection.getSourceBlock();
|
|
433
|
-
}
|
|
434
|
-
return null;
|
|
435
|
-
};
|
|
436
|
-
|
|
437
|
-
/**
|
|
438
|
-
* Function to be called when this connection's compatible types have changed.
|
|
439
|
-
* @protected
|
|
440
|
-
*/
|
|
441
|
-
Connection.prototype.onCheckChanged_ = function() {
|
|
442
|
-
// The new value type may not be compatible with the existing connection.
|
|
443
|
-
if (this.isConnected() &&
|
|
444
|
-
(!this.targetConnection ||
|
|
445
|
-
!this.getConnectionChecker().canConnect(
|
|
446
|
-
this, this.targetConnection, false))) {
|
|
447
|
-
const child = this.isSuperior() ? this.targetBlock() : this.sourceBlock_;
|
|
448
|
-
child.unplug();
|
|
449
|
-
}
|
|
450
|
-
};
|
|
451
|
-
|
|
452
|
-
/**
|
|
453
|
-
* Change a connection's compatibility.
|
|
454
|
-
* @param {?(string|!Array<string>)} check Compatible value type or list of
|
|
455
|
-
* value types. Null if all types are compatible.
|
|
456
|
-
* @return {!Connection} The connection being modified
|
|
457
|
-
* (to allow chaining).
|
|
458
|
-
*/
|
|
459
|
-
Connection.prototype.setCheck = function(check) {
|
|
460
|
-
if (check) {
|
|
461
|
-
// Ensure that check is in an array.
|
|
462
|
-
if (!Array.isArray(check)) {
|
|
463
|
-
check = [check];
|
|
464
|
-
}
|
|
465
|
-
this.check_ = check;
|
|
466
|
-
this.onCheckChanged_();
|
|
467
|
-
} else {
|
|
468
|
-
this.check_ = null;
|
|
469
|
-
}
|
|
470
|
-
return this;
|
|
471
|
-
};
|
|
472
|
-
|
|
473
|
-
/**
|
|
474
|
-
* Get a connection's compatibility.
|
|
475
|
-
* @return {?Array} List of compatible value types.
|
|
476
|
-
* Null if all types are compatible.
|
|
477
|
-
* @public
|
|
478
|
-
*/
|
|
479
|
-
Connection.prototype.getCheck = function() {
|
|
480
|
-
return this.check_;
|
|
481
|
-
};
|
|
482
|
-
|
|
483
|
-
/**
|
|
484
|
-
* Changes the connection's shadow block.
|
|
485
|
-
* @param {?Element} shadowDom DOM representation of a block or null.
|
|
486
|
-
*/
|
|
487
|
-
Connection.prototype.setShadowDom = function(shadowDom) {
|
|
488
|
-
this.setShadowStateInternal_({shadowDom: shadowDom});
|
|
489
|
-
};
|
|
490
|
-
|
|
491
|
-
/**
|
|
492
|
-
* Returns the xml representation of the connection's shadow block.
|
|
493
|
-
* @param {boolean=} returnCurrent If true, and the shadow block is currently
|
|
494
|
-
* attached to this connection, this serializes the state of that block
|
|
495
|
-
* and returns it (so that field values are correct). Otherwise the saved
|
|
496
|
-
* shadowDom is just returned.
|
|
497
|
-
* @return {?Element} Shadow DOM representation of a block or null.
|
|
498
|
-
*/
|
|
499
|
-
Connection.prototype.getShadowDom = function(returnCurrent) {
|
|
500
|
-
return (returnCurrent && this.targetBlock().isShadow()) ?
|
|
501
|
-
/** @type {!Element} */ (Xml.blockToDom(
|
|
502
|
-
/** @type {!Block} */ (this.targetBlock()))) :
|
|
503
|
-
this.shadowDom_;
|
|
504
|
-
};
|
|
505
|
-
|
|
506
|
-
/**
|
|
507
|
-
* Changes the connection's shadow block.
|
|
508
|
-
* @param {?blocks.State} shadowState An state represetation of the block or
|
|
509
|
-
* null.
|
|
510
|
-
*/
|
|
511
|
-
Connection.prototype.setShadowState = function(shadowState) {
|
|
512
|
-
this.setShadowStateInternal_({shadowState: shadowState});
|
|
513
|
-
};
|
|
514
|
-
|
|
515
|
-
/**
|
|
516
|
-
* Returns the serialized object representation of the connection's shadow
|
|
517
|
-
* block.
|
|
518
|
-
* @param {boolean=} returnCurrent If true, and the shadow block is currently
|
|
519
|
-
* attached to this connection, this serializes the state of that block
|
|
520
|
-
* and returns it (so that field values are correct). Otherwise the saved
|
|
521
|
-
* state is just returned.
|
|
522
|
-
* @return {?blocks.State} Serialized object representation of the block, or
|
|
523
|
-
* null.
|
|
524
|
-
*/
|
|
525
|
-
Connection.prototype.getShadowState = function(returnCurrent) {
|
|
526
|
-
if (returnCurrent && this.targetBlock() && this.targetBlock().isShadow()) {
|
|
527
|
-
return blocks.save(/** @type {!Block} */ (this.targetBlock()));
|
|
528
|
-
}
|
|
529
|
-
return this.shadowState_;
|
|
530
|
-
};
|
|
531
|
-
|
|
532
|
-
/**
|
|
533
|
-
* Find all nearby compatible connections to this connection.
|
|
534
|
-
* Type checking does not apply, since this function is used for bumping.
|
|
535
|
-
*
|
|
536
|
-
* Headless configurations (the default) do not have neighboring connection,
|
|
537
|
-
* and always return an empty list (the default).
|
|
538
|
-
* {@link Blockly.RenderedConnection} overrides this behavior with a list
|
|
539
|
-
* computed from the rendered positioning.
|
|
540
|
-
* @param {number} _maxLimit The maximum radius to another connection.
|
|
541
|
-
* @return {!Array<!Connection>} List of connections.
|
|
542
|
-
* @package
|
|
543
|
-
*/
|
|
544
|
-
Connection.prototype.neighbours = function(_maxLimit) {
|
|
545
|
-
return [];
|
|
546
|
-
};
|
|
547
|
-
|
|
548
|
-
/**
|
|
549
|
-
* Get the parent input of a connection.
|
|
550
|
-
* @return {?Input} The input that the connection belongs to or null if
|
|
551
|
-
* no parent exists.
|
|
552
|
-
* @package
|
|
553
|
-
*/
|
|
554
|
-
Connection.prototype.getParentInput = function() {
|
|
555
|
-
let parentInput = null;
|
|
556
|
-
const inputs = this.sourceBlock_.inputList;
|
|
557
|
-
for (let i = 0; i < inputs.length; i++) {
|
|
558
|
-
if (inputs[i].connection === this) {
|
|
559
|
-
parentInput = inputs[i];
|
|
560
|
-
break;
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
return parentInput;
|
|
564
|
-
};
|
|
565
|
-
|
|
566
|
-
/**
|
|
567
|
-
* This method returns a string describing this Connection in developer terms
|
|
568
|
-
* (English only). Intended to on be used in console logs and errors.
|
|
569
|
-
* @return {string} The description.
|
|
570
|
-
*/
|
|
571
|
-
Connection.prototype.toString = function() {
|
|
572
|
-
const block = this.sourceBlock_;
|
|
573
|
-
if (!block) {
|
|
574
|
-
return 'Orphan Connection';
|
|
575
|
-
}
|
|
576
|
-
let msg;
|
|
577
|
-
if (block.outputConnection === this) {
|
|
578
|
-
msg = 'Output Connection of ';
|
|
579
|
-
} else if (block.previousConnection === this) {
|
|
580
|
-
msg = 'Previous Connection of ';
|
|
581
|
-
} else if (block.nextConnection === this) {
|
|
582
|
-
msg = 'Next Connection of ';
|
|
583
|
-
} else {
|
|
584
|
-
let parentInput = null;
|
|
585
|
-
for (let i = 0, input; (input = block.inputList[i]); i++) {
|
|
586
|
-
if (input.connection === this) {
|
|
587
|
-
parentInput = input;
|
|
588
|
-
break;
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
if (parentInput) {
|
|
592
|
-
msg = 'Input "' + parentInput.name + '" connection on ';
|
|
593
|
-
} else {
|
|
594
|
-
console.warn('Connection not actually connected to sourceBlock_');
|
|
595
|
-
return 'Orphan Connection';
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
return msg + block.toDevString();
|
|
599
|
-
};
|
|
600
|
-
|
|
601
|
-
/**
|
|
602
|
-
* Returns the state of the shadowDom_ and shadowState_ properties, then
|
|
603
|
-
* temporarily sets those properties to null so no shadow respawns.
|
|
604
|
-
* @return {{shadowDom: ?Element, shadowState: ?blocks.State}} The state of both
|
|
605
|
-
* the shadowDom_ and shadowState_ properties.
|
|
606
|
-
* @private
|
|
607
|
-
*/
|
|
608
|
-
Connection.prototype.stashShadowState_ = function() {
|
|
609
|
-
const shadowDom = this.getShadowDom(true);
|
|
610
|
-
const shadowState = this.getShadowState(true);
|
|
611
|
-
// Set to null so it doesn't respawn.
|
|
612
|
-
this.shadowDom_ = null;
|
|
613
|
-
this.shadowState_ = null;
|
|
614
|
-
return {shadowDom, shadowState};
|
|
615
|
-
};
|
|
616
|
-
|
|
617
|
-
/**
|
|
618
|
-
* Reapplies the stashed state of the shadowDom_ and shadowState_ properties.
|
|
619
|
-
* @param {{shadowDom: ?Element, shadowState: ?blocks.State}} param0 The state
|
|
620
|
-
* to reapply to the shadowDom_ and shadowState_ properties.
|
|
621
|
-
* @private
|
|
622
|
-
*/
|
|
623
|
-
Connection.prototype.applyShadowState_ = function({shadowDom, shadowState}) {
|
|
624
|
-
this.shadowDom_ = shadowDom;
|
|
625
|
-
this.shadowState_ = shadowState;
|
|
626
|
-
};
|
|
627
|
-
|
|
628
|
-
/**
|
|
629
|
-
* Sets the state of the shadow of this connection.
|
|
630
|
-
* @param {{shadowDom: (?Element|undefined), shadowState:
|
|
631
|
-
* (?blocks.State|undefined)}=} param0 The state to set the shadow of this
|
|
632
|
-
* connection to.
|
|
633
|
-
* @private
|
|
634
|
-
*/
|
|
635
|
-
Connection.prototype.setShadowStateInternal_ = function(
|
|
636
|
-
{shadowDom = null, shadowState = null} = {}) {
|
|
637
|
-
// One or both of these should always be null.
|
|
638
|
-
// If neither is null, the shadowState will get priority.
|
|
639
|
-
this.shadowDom_ = shadowDom;
|
|
640
|
-
this.shadowState_ = shadowState;
|
|
641
|
-
|
|
642
|
-
const target = this.targetBlock();
|
|
643
|
-
if (!target) {
|
|
644
|
-
this.respawnShadow_();
|
|
645
|
-
if (this.targetBlock() && this.targetBlock().isShadow()) {
|
|
646
|
-
this.serializeShadow_(this.targetBlock());
|
|
647
|
-
}
|
|
648
|
-
} else if (target.isShadow()) {
|
|
649
|
-
target.dispose(false);
|
|
650
|
-
this.respawnShadow_();
|
|
651
|
-
if (this.targetBlock() && this.targetBlock().isShadow()) {
|
|
652
|
-
this.serializeShadow_(this.targetBlock());
|
|
653
|
-
}
|
|
654
|
-
} else {
|
|
655
|
-
const shadow = this.createShadowBlock_(false);
|
|
656
|
-
this.serializeShadow_(shadow);
|
|
657
|
-
if (shadow) {
|
|
658
|
-
shadow.dispose(false);
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
};
|
|
662
|
-
|
|
663
|
-
/**
|
|
664
|
-
* Creates a shadow block based on the current shadowState_ or shadowDom_.
|
|
665
|
-
* shadowState_ gets priority.
|
|
666
|
-
* @param {boolean} attemptToConnect Whether to try to connect the shadow block
|
|
667
|
-
* to this connection or not.
|
|
668
|
-
* @return {?Block} The shadow block that was created, or null if both the
|
|
669
|
-
* shadowState_ and shadowDom_ are null.
|
|
670
|
-
* @private
|
|
671
|
-
*/
|
|
672
|
-
Connection.prototype.createShadowBlock_ = function(attemptToConnect) {
|
|
673
|
-
const parentBlock = this.getSourceBlock();
|
|
674
|
-
const shadowState = this.getShadowState();
|
|
675
|
-
const shadowDom = this.getShadowDom();
|
|
676
|
-
if (!parentBlock.workspace || (!shadowState && !shadowDom)) {
|
|
677
|
-
return null;
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
let blockShadow;
|
|
681
|
-
if (shadowState) {
|
|
682
|
-
blockShadow = blocks.appendInternal(shadowState, parentBlock.workspace, {
|
|
683
|
-
parentConnection: attemptToConnect ? this : undefined,
|
|
684
|
-
isShadow: true,
|
|
685
|
-
recordUndo: false,
|
|
686
|
-
});
|
|
687
|
-
return blockShadow;
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
if (shadowDom) {
|
|
691
|
-
blockShadow = Xml.domToBlock(shadowDom, parentBlock.workspace);
|
|
692
|
-
if (attemptToConnect) {
|
|
693
|
-
if (this.type === ConnectionType.INPUT_VALUE) {
|
|
694
|
-
if (!blockShadow.outputConnection) {
|
|
695
|
-
throw new Error('Shadow block is missing an output connection');
|
|
696
|
-
}
|
|
697
|
-
if (!this.connect(blockShadow.outputConnection)) {
|
|
698
|
-
throw new Error('Could not connect shadow block to connection');
|
|
699
|
-
}
|
|
700
|
-
} else if (this.type === ConnectionType.NEXT_STATEMENT) {
|
|
701
|
-
if (!blockShadow.previousConnection) {
|
|
702
|
-
throw new Error('Shadow block is missing previous connection');
|
|
703
|
-
}
|
|
704
|
-
if (!this.connect(blockShadow.previousConnection)) {
|
|
705
|
-
throw new Error('Could not connect shadow block to connection');
|
|
706
|
-
}
|
|
707
|
-
} else {
|
|
708
|
-
throw new Error(
|
|
709
|
-
'Cannot connect a shadow block to a previous/output connection');
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
return blockShadow;
|
|
713
|
-
}
|
|
714
|
-
return null;
|
|
715
|
-
};
|
|
716
|
-
|
|
717
|
-
/**
|
|
718
|
-
* Saves the given shadow block to both the shadowDom_ and shadowState_
|
|
719
|
-
* properties, in their respective serialized forms.
|
|
720
|
-
* @param {?Block} shadow The shadow to serialize, or null.
|
|
721
|
-
* @private
|
|
722
|
-
*/
|
|
723
|
-
Connection.prototype.serializeShadow_ = function(shadow) {
|
|
724
|
-
if (!shadow) {
|
|
725
|
-
return;
|
|
726
|
-
}
|
|
727
|
-
this.shadowDom_ = /** @type {!Element} */ (Xml.blockToDom(shadow));
|
|
728
|
-
this.shadowState_ = blocks.save(shadow);
|
|
729
|
-
};
|
|
730
|
-
|
|
731
742
|
exports.Connection = Connection;
|