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
|
@@ -39,694 +39,703 @@ const {Workspace} = goog.requireType('Blockly.Workspace');
|
|
|
39
39
|
* Class for an AST node.
|
|
40
40
|
* It is recommended that you use one of the createNode methods instead of
|
|
41
41
|
* creating a node directly.
|
|
42
|
-
* @param {string} type The type of the location.
|
|
43
|
-
* Must be in ASTNode.types.
|
|
44
|
-
* @param {!IASTNodeLocation} location The position in the AST.
|
|
45
|
-
* @param {!ASTNode.Params=} opt_params Optional dictionary of options.
|
|
46
|
-
* @constructor
|
|
47
42
|
* @alias Blockly.ASTNode
|
|
48
43
|
*/
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
class ASTNode {
|
|
45
|
+
/**
|
|
46
|
+
* @param {string} type The type of the location.
|
|
47
|
+
* Must be in ASTNode.types.
|
|
48
|
+
* @param {!IASTNodeLocation} location The position in the AST.
|
|
49
|
+
* @param {!ASTNode.Params=} opt_params Optional dictionary of options.
|
|
50
|
+
* @alias Blockly.ASTNode
|
|
51
|
+
*/
|
|
52
|
+
constructor(type, location, opt_params) {
|
|
53
|
+
if (!location) {
|
|
54
|
+
throw Error('Cannot create a node without a location.');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The type of the location.
|
|
59
|
+
* One of ASTNode.types
|
|
60
|
+
* @type {string}
|
|
61
|
+
* @private
|
|
62
|
+
*/
|
|
63
|
+
this.type_ = type;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Whether the location points to a connection.
|
|
67
|
+
* @type {boolean}
|
|
68
|
+
* @private
|
|
69
|
+
*/
|
|
70
|
+
this.isConnection_ = ASTNode.isConnectionType_(type);
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The location of the AST node.
|
|
74
|
+
* @type {!IASTNodeLocation}
|
|
75
|
+
* @private
|
|
76
|
+
*/
|
|
77
|
+
this.location_ = location;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The coordinate on the workspace.
|
|
81
|
+
* @type {Coordinate}
|
|
82
|
+
* @private
|
|
83
|
+
*/
|
|
84
|
+
this.wsCoordinate_ = null;
|
|
85
|
+
|
|
86
|
+
this.processParams_(opt_params || null);
|
|
52
87
|
}
|
|
53
88
|
|
|
54
89
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* @type {string}
|
|
90
|
+
* Parse the optional parameters.
|
|
91
|
+
* @param {?ASTNode.Params} params The user specified parameters.
|
|
58
92
|
* @private
|
|
59
93
|
*/
|
|
60
|
-
|
|
94
|
+
processParams_(params) {
|
|
95
|
+
if (!params) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (params.wsCoordinate) {
|
|
99
|
+
this.wsCoordinate_ = params.wsCoordinate;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
61
102
|
|
|
62
103
|
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
104
|
+
* Gets the value pointed to by this node.
|
|
105
|
+
* It is the callers responsibility to check the node type to figure out what
|
|
106
|
+
* type of object they get back from this.
|
|
107
|
+
* @return {!IASTNodeLocation} The current field, connection, workspace, or
|
|
108
|
+
* block the cursor is on.
|
|
66
109
|
*/
|
|
67
|
-
|
|
110
|
+
getLocation() {
|
|
111
|
+
return this.location_;
|
|
112
|
+
}
|
|
68
113
|
|
|
69
114
|
/**
|
|
70
|
-
* The
|
|
71
|
-
*
|
|
72
|
-
* @
|
|
115
|
+
* The type of the current location.
|
|
116
|
+
* One of ASTNode.types
|
|
117
|
+
* @return {string} The type of the location.
|
|
73
118
|
*/
|
|
74
|
-
|
|
119
|
+
getType() {
|
|
120
|
+
return this.type_;
|
|
121
|
+
}
|
|
75
122
|
|
|
76
123
|
/**
|
|
77
124
|
* The coordinate on the workspace.
|
|
78
|
-
* @
|
|
79
|
-
*
|
|
125
|
+
* @return {Coordinate} The workspace coordinate or null if the
|
|
126
|
+
* location is not a workspace.
|
|
80
127
|
*/
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
this.processParams_(opt_params || null);
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* @typedef {{
|
|
88
|
-
* wsCoordinate: Coordinate
|
|
89
|
-
* }}
|
|
90
|
-
*/
|
|
91
|
-
ASTNode.Params;
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Object holding different types for an AST node.
|
|
95
|
-
* @enum {string}
|
|
96
|
-
*/
|
|
97
|
-
ASTNode.types = {
|
|
98
|
-
FIELD: 'field',
|
|
99
|
-
BLOCK: 'block',
|
|
100
|
-
INPUT: 'input',
|
|
101
|
-
OUTPUT: 'output',
|
|
102
|
-
NEXT: 'next',
|
|
103
|
-
PREVIOUS: 'previous',
|
|
104
|
-
STACK: 'stack',
|
|
105
|
-
WORKSPACE: 'workspace',
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* True to navigate to all fields. False to only navigate to clickable fields.
|
|
110
|
-
* @type {boolean}
|
|
111
|
-
*/
|
|
112
|
-
ASTNode.NAVIGATE_ALL_FIELDS = false;
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* The default y offset to use when moving the cursor from a stack to the
|
|
116
|
-
* workspace.
|
|
117
|
-
* @type {number}
|
|
118
|
-
* @private
|
|
119
|
-
*/
|
|
120
|
-
ASTNode.DEFAULT_OFFSET_Y = -20;
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Whether an AST node of the given type points to a connection.
|
|
124
|
-
* @param {string} type The type to check. One of ASTNode.types.
|
|
125
|
-
* @return {boolean} True if a node of the given type points to a connection.
|
|
126
|
-
* @private
|
|
127
|
-
*/
|
|
128
|
-
ASTNode.isConnectionType_ = function(type) {
|
|
129
|
-
switch (type) {
|
|
130
|
-
case ASTNode.types.PREVIOUS:
|
|
131
|
-
case ASTNode.types.NEXT:
|
|
132
|
-
case ASTNode.types.INPUT:
|
|
133
|
-
case ASTNode.types.OUTPUT:
|
|
134
|
-
return true;
|
|
128
|
+
getWsCoordinate() {
|
|
129
|
+
return this.wsCoordinate_;
|
|
135
130
|
}
|
|
136
|
-
return false;
|
|
137
|
-
};
|
|
138
131
|
|
|
139
|
-
/**
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return null;
|
|
132
|
+
/**
|
|
133
|
+
* Whether the node points to a connection.
|
|
134
|
+
* @return {boolean} [description]
|
|
135
|
+
* @package
|
|
136
|
+
*/
|
|
137
|
+
isConnection() {
|
|
138
|
+
return this.isConnection_;
|
|
147
139
|
}
|
|
148
|
-
return new ASTNode(ASTNode.types.FIELD, field);
|
|
149
|
-
};
|
|
150
140
|
|
|
151
|
-
/**
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
141
|
+
/**
|
|
142
|
+
* Given an input find the next editable field or an input with a non null
|
|
143
|
+
* connection in the same block. The current location must be an input
|
|
144
|
+
* connection.
|
|
145
|
+
* @return {ASTNode} The AST node holding the next field or connection
|
|
146
|
+
* or null if there is no editable field or input connection after the
|
|
147
|
+
* given input.
|
|
148
|
+
* @private
|
|
149
|
+
*/
|
|
150
|
+
findNextForInput_() {
|
|
151
|
+
const location = /** @type {!Connection} */ (this.location_);
|
|
152
|
+
const parentInput = location.getParentInput();
|
|
153
|
+
const block = parentInput.getSourceBlock();
|
|
154
|
+
const curIdx = block.inputList.indexOf(parentInput);
|
|
155
|
+
for (let i = curIdx + 1; i < block.inputList.length; i++) {
|
|
156
|
+
const input = block.inputList[i];
|
|
157
|
+
const fieldRow = input.fieldRow;
|
|
158
|
+
for (let j = 0; j < fieldRow.length; j++) {
|
|
159
|
+
const field = fieldRow[j];
|
|
160
|
+
if (field.isClickable() || ASTNode.NAVIGATE_ALL_FIELDS) {
|
|
161
|
+
return ASTNode.createFieldNode(field);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (input.connection) {
|
|
165
|
+
return ASTNode.createInputNode(input);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
160
168
|
return null;
|
|
161
169
|
}
|
|
162
|
-
const type = connection.type;
|
|
163
|
-
if (type === ConnectionType.INPUT_VALUE) {
|
|
164
|
-
return ASTNode.createInputNode(connection.getParentInput());
|
|
165
|
-
} else if (
|
|
166
|
-
type === ConnectionType.NEXT_STATEMENT && connection.getParentInput()) {
|
|
167
|
-
return ASTNode.createInputNode(connection.getParentInput());
|
|
168
|
-
} else if (type === ConnectionType.NEXT_STATEMENT) {
|
|
169
|
-
return new ASTNode(ASTNode.types.NEXT, connection);
|
|
170
|
-
} else if (type === ConnectionType.OUTPUT_VALUE) {
|
|
171
|
-
return new ASTNode(ASTNode.types.OUTPUT, connection);
|
|
172
|
-
} else if (type === ConnectionType.PREVIOUS_STATEMENT) {
|
|
173
|
-
return new ASTNode(ASTNode.types.PREVIOUS, connection);
|
|
174
|
-
}
|
|
175
|
-
return null;
|
|
176
|
-
};
|
|
177
170
|
|
|
178
|
-
/**
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
171
|
+
/**
|
|
172
|
+
* Given a field find the next editable field or an input with a non null
|
|
173
|
+
* connection in the same block. The current location must be a field.
|
|
174
|
+
* @return {ASTNode} The AST node pointing to the next field or
|
|
175
|
+
* connection or null if there is no editable field or input connection
|
|
176
|
+
* after the given input.
|
|
177
|
+
* @private
|
|
178
|
+
*/
|
|
179
|
+
findNextForField_() {
|
|
180
|
+
const location = /** @type {!Field} */ (this.location_);
|
|
181
|
+
const input = location.getParentInput();
|
|
182
|
+
const block = location.getSourceBlock();
|
|
183
|
+
const curIdx = block.inputList.indexOf(/** @type {!Input} */ (input));
|
|
184
|
+
let fieldIdx = input.fieldRow.indexOf(location) + 1;
|
|
185
|
+
for (let i = curIdx; i < block.inputList.length; i++) {
|
|
186
|
+
const newInput = block.inputList[i];
|
|
187
|
+
const fieldRow = newInput.fieldRow;
|
|
188
|
+
while (fieldIdx < fieldRow.length) {
|
|
189
|
+
if (fieldRow[fieldIdx].isClickable() || ASTNode.NAVIGATE_ALL_FIELDS) {
|
|
190
|
+
return ASTNode.createFieldNode(fieldRow[fieldIdx]);
|
|
191
|
+
}
|
|
192
|
+
fieldIdx++;
|
|
193
|
+
}
|
|
194
|
+
fieldIdx = 0;
|
|
195
|
+
if (newInput.connection) {
|
|
196
|
+
return ASTNode.createInputNode(newInput);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
186
199
|
return null;
|
|
187
200
|
}
|
|
188
|
-
return new ASTNode(ASTNode.types.INPUT, input.connection);
|
|
189
|
-
};
|
|
190
201
|
|
|
191
|
-
/**
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
202
|
+
/**
|
|
203
|
+
* Given an input find the previous editable field or an input with a non null
|
|
204
|
+
* connection in the same block. The current location must be an input
|
|
205
|
+
* connection.
|
|
206
|
+
* @return {ASTNode} The AST node holding the previous field or
|
|
207
|
+
* connection.
|
|
208
|
+
* @private
|
|
209
|
+
*/
|
|
210
|
+
findPrevForInput_() {
|
|
211
|
+
const location = /** @type {!Connection} */ (this.location_);
|
|
212
|
+
const parentInput = location.getParentInput();
|
|
213
|
+
const block = parentInput.getSourceBlock();
|
|
214
|
+
const curIdx = block.inputList.indexOf(parentInput);
|
|
215
|
+
for (let i = curIdx; i >= 0; i--) {
|
|
216
|
+
const input = block.inputList[i];
|
|
217
|
+
if (input.connection && input !== parentInput) {
|
|
218
|
+
return ASTNode.createInputNode(input);
|
|
219
|
+
}
|
|
220
|
+
const fieldRow = input.fieldRow;
|
|
221
|
+
for (let j = fieldRow.length - 1; j >= 0; j--) {
|
|
222
|
+
const field = fieldRow[j];
|
|
223
|
+
if (field.isClickable() || ASTNode.NAVIGATE_ALL_FIELDS) {
|
|
224
|
+
return ASTNode.createFieldNode(field);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
198
228
|
return null;
|
|
199
229
|
}
|
|
200
|
-
return new ASTNode(ASTNode.types.BLOCK, block);
|
|
201
|
-
};
|
|
202
230
|
|
|
203
|
-
/**
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
*/
|
|
211
|
-
|
|
212
|
-
|
|
231
|
+
/**
|
|
232
|
+
* Given a field find the previous editable field or an input with a non null
|
|
233
|
+
* connection in the same block. The current location must be a field.
|
|
234
|
+
* @return {ASTNode} The AST node holding the previous input or field.
|
|
235
|
+
* @private
|
|
236
|
+
*/
|
|
237
|
+
findPrevForField_() {
|
|
238
|
+
const location = /** @type {!Field} */ (this.location_);
|
|
239
|
+
const parentInput = location.getParentInput();
|
|
240
|
+
const block = location.getSourceBlock();
|
|
241
|
+
const curIdx = block.inputList.indexOf(
|
|
242
|
+
/** @type {!Input} */ (parentInput));
|
|
243
|
+
let fieldIdx = parentInput.fieldRow.indexOf(location) - 1;
|
|
244
|
+
for (let i = curIdx; i >= 0; i--) {
|
|
245
|
+
const input = block.inputList[i];
|
|
246
|
+
if (input.connection && input !== parentInput) {
|
|
247
|
+
return ASTNode.createInputNode(input);
|
|
248
|
+
}
|
|
249
|
+
const fieldRow = input.fieldRow;
|
|
250
|
+
while (fieldIdx > -1) {
|
|
251
|
+
if (fieldRow[fieldIdx].isClickable() || ASTNode.NAVIGATE_ALL_FIELDS) {
|
|
252
|
+
return ASTNode.createFieldNode(fieldRow[fieldIdx]);
|
|
253
|
+
}
|
|
254
|
+
fieldIdx--;
|
|
255
|
+
}
|
|
256
|
+
// Reset the fieldIdx to the length of the field row of the previous
|
|
257
|
+
// input.
|
|
258
|
+
if (i - 1 >= 0) {
|
|
259
|
+
fieldIdx = block.inputList[i - 1].fieldRow.length - 1;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
213
262
|
return null;
|
|
214
263
|
}
|
|
215
|
-
return new ASTNode(ASTNode.types.STACK, topBlock);
|
|
216
|
-
};
|
|
217
264
|
|
|
218
|
-
/**
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
265
|
+
/**
|
|
266
|
+
* Navigate between stacks of blocks on the workspace.
|
|
267
|
+
* @param {boolean} forward True to go forward. False to go backwards.
|
|
268
|
+
* @return {ASTNode} The first block of the next stack or null if there
|
|
269
|
+
* are no blocks on the workspace.
|
|
270
|
+
* @private
|
|
271
|
+
*/
|
|
272
|
+
navigateBetweenStacks_(forward) {
|
|
273
|
+
let curLocation = this.getLocation();
|
|
274
|
+
if (curLocation.getSourceBlock) {
|
|
275
|
+
curLocation = /** @type {!IASTNodeLocationWithBlock} */ (curLocation)
|
|
276
|
+
.getSourceBlock();
|
|
277
|
+
}
|
|
278
|
+
if (!curLocation || !curLocation.workspace) {
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
const curRoot = curLocation.getRootBlock();
|
|
282
|
+
const topBlocks = curRoot.workspace.getTopBlocks(true);
|
|
283
|
+
for (let i = 0; i < topBlocks.length; i++) {
|
|
284
|
+
const topBlock = topBlocks[i];
|
|
285
|
+
if (curRoot.id === topBlock.id) {
|
|
286
|
+
const offset = forward ? 1 : -1;
|
|
287
|
+
const resultIndex = i + offset;
|
|
288
|
+
if (resultIndex === -1 || resultIndex === topBlocks.length) {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
return ASTNode.createStackNode(topBlocks[resultIndex]);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
throw Error(
|
|
295
|
+
'Couldn\'t find ' + (forward ? 'next' : 'previous') + ' stack?!');
|
|
229
296
|
}
|
|
230
|
-
const params = {wsCoordinate: wsCoordinate};
|
|
231
|
-
return new ASTNode(ASTNode.types.WORKSPACE, workspace, params);
|
|
232
|
-
};
|
|
233
297
|
|
|
234
|
-
/**
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
298
|
+
/**
|
|
299
|
+
* Finds the top most AST node for a given block.
|
|
300
|
+
* This is either the previous connection, output connection or block
|
|
301
|
+
* depending on what kind of connections the block has.
|
|
302
|
+
* @param {!Block} block The block that we want to find the top
|
|
303
|
+
* connection on.
|
|
304
|
+
* @return {!ASTNode} The AST node containing the top connection.
|
|
305
|
+
* @private
|
|
306
|
+
*/
|
|
307
|
+
findTopASTNodeForBlock_(block) {
|
|
308
|
+
const topConnection = getParentConnection(block);
|
|
309
|
+
if (topConnection) {
|
|
310
|
+
return /** @type {!ASTNode} */ (
|
|
311
|
+
ASTNode.createConnectionNode(topConnection));
|
|
312
|
+
} else {
|
|
313
|
+
return /** @type {!ASTNode} */ (ASTNode.createBlockNode(block));
|
|
314
|
+
}
|
|
249
315
|
}
|
|
250
|
-
return topConnection;
|
|
251
|
-
};
|
|
252
316
|
|
|
253
|
-
/**
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
317
|
+
/**
|
|
318
|
+
* Get the AST node pointing to the input that the block is nested under or if
|
|
319
|
+
* the block is not nested then get the stack AST node.
|
|
320
|
+
* @param {Block} block The source block of the current location.
|
|
321
|
+
* @return {ASTNode} The AST node pointing to the input connection or
|
|
322
|
+
* the top block of the stack this block is in.
|
|
323
|
+
* @private
|
|
324
|
+
*/
|
|
325
|
+
getOutAstNodeForBlock_(block) {
|
|
326
|
+
if (!block) {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
// If the block doesn't have a previous connection then it is the top of the
|
|
330
|
+
// substack.
|
|
331
|
+
const topBlock = block.getTopStackBlock();
|
|
332
|
+
const topConnection = getParentConnection(topBlock);
|
|
333
|
+
// If the top connection has a parentInput, create an AST node pointing to
|
|
334
|
+
// that input.
|
|
335
|
+
if (topConnection && topConnection.targetConnection &&
|
|
336
|
+
topConnection.targetConnection.getParentInput()) {
|
|
337
|
+
return ASTNode.createInputNode(
|
|
338
|
+
topConnection.targetConnection.getParentInput());
|
|
339
|
+
} else {
|
|
340
|
+
// Go to stack level if you are not underneath an input.
|
|
341
|
+
return ASTNode.createStackNode(topBlock);
|
|
342
|
+
}
|
|
267
343
|
}
|
|
268
|
-
return astNode;
|
|
269
|
-
};
|
|
270
344
|
|
|
271
|
-
/**
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
345
|
+
/**
|
|
346
|
+
* Find the first editable field or input with a connection on a given block.
|
|
347
|
+
* @param {!Block} block The source block of the current location.
|
|
348
|
+
* @return {ASTNode} An AST node pointing to the first field or input.
|
|
349
|
+
* Null if there are no editable fields or inputs with connections on the
|
|
350
|
+
* block.
|
|
351
|
+
* @private
|
|
352
|
+
*/
|
|
353
|
+
findFirstFieldOrInput_(block) {
|
|
354
|
+
const inputs = block.inputList;
|
|
355
|
+
for (let i = 0; i < inputs.length; i++) {
|
|
356
|
+
const input = inputs[i];
|
|
357
|
+
const fieldRow = input.fieldRow;
|
|
358
|
+
for (let j = 0; j < fieldRow.length; j++) {
|
|
359
|
+
const field = fieldRow[j];
|
|
360
|
+
if (field.isClickable() || ASTNode.NAVIGATE_ALL_FIELDS) {
|
|
361
|
+
return ASTNode.createFieldNode(field);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (input.connection) {
|
|
365
|
+
return ASTNode.createInputNode(input);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return null;
|
|
282
369
|
}
|
|
283
|
-
};
|
|
284
370
|
|
|
285
|
-
/**
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
return this.type_;
|
|
303
|
-
};
|
|
371
|
+
/**
|
|
372
|
+
* Finds the source block of the location of this node.
|
|
373
|
+
* @return {Block} The source block of the location, or null if the node
|
|
374
|
+
* is of type workspace.
|
|
375
|
+
*/
|
|
376
|
+
getSourceBlock() {
|
|
377
|
+
if (this.getType() === ASTNode.types.BLOCK) {
|
|
378
|
+
return /** @type {Block} */ (this.getLocation());
|
|
379
|
+
} else if (this.getType() === ASTNode.types.STACK) {
|
|
380
|
+
return /** @type {Block} */ (this.getLocation());
|
|
381
|
+
} else if (this.getType() === ASTNode.types.WORKSPACE) {
|
|
382
|
+
return null;
|
|
383
|
+
} else {
|
|
384
|
+
return /** @type {IASTNodeLocationWithBlock} */ (this.getLocation())
|
|
385
|
+
.getSourceBlock();
|
|
386
|
+
}
|
|
387
|
+
}
|
|
304
388
|
|
|
305
|
-
/**
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
389
|
+
/**
|
|
390
|
+
* Find the element to the right of the current element in the AST.
|
|
391
|
+
* @return {ASTNode} An AST node that wraps the next field, connection,
|
|
392
|
+
* block, or workspace. Or null if there is no node to the right.
|
|
393
|
+
*/
|
|
394
|
+
next() {
|
|
395
|
+
switch (this.type_) {
|
|
396
|
+
case ASTNode.types.STACK:
|
|
397
|
+
return this.navigateBetweenStacks_(true);
|
|
398
|
+
|
|
399
|
+
case ASTNode.types.OUTPUT: {
|
|
400
|
+
const connection = /** @type {!Connection} */ (this.location_);
|
|
401
|
+
return ASTNode.createBlockNode(connection.getSourceBlock());
|
|
402
|
+
}
|
|
403
|
+
case ASTNode.types.FIELD:
|
|
404
|
+
return this.findNextForField_();
|
|
313
405
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
* @return {boolean} [description]
|
|
317
|
-
* @package
|
|
318
|
-
*/
|
|
319
|
-
ASTNode.prototype.isConnection = function() {
|
|
320
|
-
return this.isConnection_;
|
|
321
|
-
};
|
|
406
|
+
case ASTNode.types.INPUT:
|
|
407
|
+
return this.findNextForInput_();
|
|
322
408
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
ASTNode.
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
const curIdx = block.inputList.indexOf(parentInput);
|
|
337
|
-
for (let i = curIdx + 1; i < block.inputList.length; i++) {
|
|
338
|
-
const input = block.inputList[i];
|
|
339
|
-
const fieldRow = input.fieldRow;
|
|
340
|
-
for (let j = 0; j < fieldRow.length; j++) {
|
|
341
|
-
const field = fieldRow[j];
|
|
342
|
-
if (field.isClickable() || ASTNode.NAVIGATE_ALL_FIELDS) {
|
|
343
|
-
return ASTNode.createFieldNode(field);
|
|
409
|
+
case ASTNode.types.BLOCK: {
|
|
410
|
+
const block = /** @type {!Block} */ (this.location_);
|
|
411
|
+
const nextConnection = block.nextConnection;
|
|
412
|
+
return ASTNode.createConnectionNode(nextConnection);
|
|
413
|
+
}
|
|
414
|
+
case ASTNode.types.PREVIOUS: {
|
|
415
|
+
const connection = /** @type {!Connection} */ (this.location_);
|
|
416
|
+
return ASTNode.createBlockNode(connection.getSourceBlock());
|
|
417
|
+
}
|
|
418
|
+
case ASTNode.types.NEXT: {
|
|
419
|
+
const connection = /** @type {!Connection} */ (this.location_);
|
|
420
|
+
const targetConnection = connection.targetConnection;
|
|
421
|
+
return ASTNode.createConnectionNode(targetConnection);
|
|
344
422
|
}
|
|
345
423
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
}
|
|
424
|
+
|
|
425
|
+
return null;
|
|
349
426
|
}
|
|
350
|
-
return null;
|
|
351
|
-
};
|
|
352
427
|
|
|
353
|
-
/**
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
ASTNode.
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
428
|
+
/**
|
|
429
|
+
* Find the element one level below and all the way to the left of the current
|
|
430
|
+
* location.
|
|
431
|
+
* @return {ASTNode} An AST node that wraps the next field, connection,
|
|
432
|
+
* workspace, or block. Or null if there is nothing below this node.
|
|
433
|
+
*/
|
|
434
|
+
in() {
|
|
435
|
+
switch (this.type_) {
|
|
436
|
+
case ASTNode.types.WORKSPACE: {
|
|
437
|
+
const workspace = /** @type {!Workspace} */ (this.location_);
|
|
438
|
+
const topBlocks = workspace.getTopBlocks(true);
|
|
439
|
+
if (topBlocks.length > 0) {
|
|
440
|
+
return ASTNode.createStackNode(topBlocks[0]);
|
|
441
|
+
}
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
case ASTNode.types.STACK: {
|
|
445
|
+
const block = /** @type {!Block} */ (this.location_);
|
|
446
|
+
return this.findTopASTNodeForBlock_(block);
|
|
447
|
+
}
|
|
448
|
+
case ASTNode.types.BLOCK: {
|
|
449
|
+
const block = /** @type {!Block} */ (this.location_);
|
|
450
|
+
return this.findFirstFieldOrInput_(block);
|
|
451
|
+
}
|
|
452
|
+
case ASTNode.types.INPUT: {
|
|
453
|
+
const connection = /** @type {!Connection} */ (this.location_);
|
|
454
|
+
const targetConnection = connection.targetConnection;
|
|
455
|
+
return ASTNode.createConnectionNode(targetConnection);
|
|
373
456
|
}
|
|
374
|
-
fieldIdx++;
|
|
375
|
-
}
|
|
376
|
-
fieldIdx = 0;
|
|
377
|
-
if (newInput.connection) {
|
|
378
|
-
return ASTNode.createInputNode(newInput);
|
|
379
457
|
}
|
|
458
|
+
|
|
459
|
+
return null;
|
|
380
460
|
}
|
|
381
|
-
return null;
|
|
382
|
-
};
|
|
383
461
|
|
|
384
|
-
/**
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
ASTNode.
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
462
|
+
/**
|
|
463
|
+
* Find the element to the left of the current element in the AST.
|
|
464
|
+
* @return {ASTNode} An AST node that wraps the previous field,
|
|
465
|
+
* connection, workspace or block. Or null if no node exists to the left.
|
|
466
|
+
* null.
|
|
467
|
+
*/
|
|
468
|
+
prev() {
|
|
469
|
+
switch (this.type_) {
|
|
470
|
+
case ASTNode.types.STACK:
|
|
471
|
+
return this.navigateBetweenStacks_(false);
|
|
472
|
+
|
|
473
|
+
case ASTNode.types.OUTPUT:
|
|
474
|
+
return null;
|
|
475
|
+
|
|
476
|
+
case ASTNode.types.FIELD:
|
|
477
|
+
return this.findPrevForField_();
|
|
478
|
+
|
|
479
|
+
case ASTNode.types.INPUT:
|
|
480
|
+
return this.findPrevForInput_();
|
|
481
|
+
|
|
482
|
+
case ASTNode.types.BLOCK: {
|
|
483
|
+
const block = /** @type {!Block} */ (this.location_);
|
|
484
|
+
const topConnection = getParentConnection(block);
|
|
485
|
+
return ASTNode.createConnectionNode(topConnection);
|
|
486
|
+
}
|
|
487
|
+
case ASTNode.types.PREVIOUS: {
|
|
488
|
+
const connection = /** @type {!Connection} */ (this.location_);
|
|
489
|
+
const targetConnection = connection.targetConnection;
|
|
490
|
+
if (targetConnection && !targetConnection.getParentInput()) {
|
|
491
|
+
return ASTNode.createConnectionNode(targetConnection);
|
|
492
|
+
}
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
case ASTNode.types.NEXT: {
|
|
496
|
+
const connection = /** @type {!Connection} */ (this.location_);
|
|
497
|
+
return ASTNode.createBlockNode(connection.getSourceBlock());
|
|
407
498
|
}
|
|
408
499
|
}
|
|
500
|
+
|
|
501
|
+
return null;
|
|
409
502
|
}
|
|
410
|
-
return null;
|
|
411
|
-
};
|
|
412
503
|
|
|
413
|
-
/**
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
504
|
+
/**
|
|
505
|
+
* Find the next element that is one position above and all the way to the
|
|
506
|
+
* left of the current location.
|
|
507
|
+
* @return {ASTNode} An AST node that wraps the next field, connection,
|
|
508
|
+
* workspace or block. Or null if we are at the workspace level.
|
|
509
|
+
*/
|
|
510
|
+
out() {
|
|
511
|
+
switch (this.type_) {
|
|
512
|
+
case ASTNode.types.STACK: {
|
|
513
|
+
const block = /** @type {!Block} */ (this.location_);
|
|
514
|
+
const blockPos = block.getRelativeToSurfaceXY();
|
|
515
|
+
// TODO: Make sure this is in the bounds of the workspace.
|
|
516
|
+
const wsCoordinate =
|
|
517
|
+
new Coordinate(blockPos.x, blockPos.y + ASTNode.DEFAULT_OFFSET_Y);
|
|
518
|
+
return ASTNode.createWorkspaceNode(block.workspace, wsCoordinate);
|
|
519
|
+
}
|
|
520
|
+
case ASTNode.types.OUTPUT: {
|
|
521
|
+
const connection = /** @type {!Connection} */ (this.location_);
|
|
522
|
+
const target = connection.targetConnection;
|
|
523
|
+
if (target) {
|
|
524
|
+
return ASTNode.createConnectionNode(target);
|
|
525
|
+
}
|
|
526
|
+
return ASTNode.createStackNode(connection.getSourceBlock());
|
|
527
|
+
}
|
|
528
|
+
case ASTNode.types.FIELD: {
|
|
529
|
+
const field = /** @type {!Field} */ (this.location_);
|
|
530
|
+
return ASTNode.createBlockNode(field.getSourceBlock());
|
|
531
|
+
}
|
|
532
|
+
case ASTNode.types.INPUT: {
|
|
533
|
+
const connection = /** @type {!Connection} */ (this.location_);
|
|
534
|
+
return ASTNode.createBlockNode(connection.getSourceBlock());
|
|
535
|
+
}
|
|
536
|
+
case ASTNode.types.BLOCK: {
|
|
537
|
+
const block = /** @type {!Block} */ (this.location_);
|
|
538
|
+
return this.getOutAstNodeForBlock_(block);
|
|
539
|
+
}
|
|
540
|
+
case ASTNode.types.PREVIOUS: {
|
|
541
|
+
const connection = /** @type {!Connection} */ (this.location_);
|
|
542
|
+
return this.getOutAstNodeForBlock_(connection.getSourceBlock());
|
|
543
|
+
}
|
|
544
|
+
case ASTNode.types.NEXT: {
|
|
545
|
+
const connection = /** @type {!Connection} */ (this.location_);
|
|
546
|
+
return this.getOutAstNodeForBlock_(connection.getSourceBlock());
|
|
435
547
|
}
|
|
436
|
-
fieldIdx--;
|
|
437
|
-
}
|
|
438
|
-
// Reset the fieldIdx to the length of the field row of the previous input.
|
|
439
|
-
if (i - 1 >= 0) {
|
|
440
|
-
fieldIdx = block.inputList[i - 1].fieldRow.length - 1;
|
|
441
548
|
}
|
|
442
|
-
}
|
|
443
|
-
return null;
|
|
444
|
-
};
|
|
445
549
|
|
|
446
|
-
/**
|
|
447
|
-
* Navigate between stacks of blocks on the workspace.
|
|
448
|
-
* @param {boolean} forward True to go forward. False to go backwards.
|
|
449
|
-
* @return {ASTNode} The first block of the next stack or null if there
|
|
450
|
-
* are no blocks on the workspace.
|
|
451
|
-
* @private
|
|
452
|
-
*/
|
|
453
|
-
ASTNode.prototype.navigateBetweenStacks_ = function(forward) {
|
|
454
|
-
let curLocation = this.getLocation();
|
|
455
|
-
if (curLocation.getSourceBlock) {
|
|
456
|
-
curLocation = /** @type {!IASTNodeLocationWithBlock} */ (curLocation)
|
|
457
|
-
.getSourceBlock();
|
|
458
|
-
}
|
|
459
|
-
if (!curLocation || !curLocation.workspace) {
|
|
460
550
|
return null;
|
|
461
551
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Whether an AST node of the given type points to a connection.
|
|
555
|
+
* @param {string} type The type to check. One of ASTNode.types.
|
|
556
|
+
* @return {boolean} True if a node of the given type points to a connection.
|
|
557
|
+
* @private
|
|
558
|
+
*/
|
|
559
|
+
static isConnectionType_(type) {
|
|
560
|
+
switch (type) {
|
|
561
|
+
case ASTNode.types.PREVIOUS:
|
|
562
|
+
case ASTNode.types.NEXT:
|
|
563
|
+
case ASTNode.types.INPUT:
|
|
564
|
+
case ASTNode.types.OUTPUT:
|
|
565
|
+
return true;
|
|
473
566
|
}
|
|
567
|
+
return false;
|
|
474
568
|
}
|
|
475
|
-
throw Error('Couldn\'t find ' + (forward ? 'next' : 'previous') + ' stack?!');
|
|
476
|
-
};
|
|
477
569
|
|
|
478
|
-
/**
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
ASTNode.
|
|
488
|
-
const topConnection = getParentConnection(block);
|
|
489
|
-
if (topConnection) {
|
|
490
|
-
return /** @type {!ASTNode} */ (
|
|
491
|
-
ASTNode.createConnectionNode(topConnection));
|
|
492
|
-
} else {
|
|
493
|
-
return /** @type {!ASTNode} */ (ASTNode.createBlockNode(block));
|
|
570
|
+
/**
|
|
571
|
+
* Create an AST node pointing to a field.
|
|
572
|
+
* @param {Field} field The location of the AST node.
|
|
573
|
+
* @return {ASTNode} An AST node pointing to a field.
|
|
574
|
+
*/
|
|
575
|
+
static createFieldNode(field) {
|
|
576
|
+
if (!field) {
|
|
577
|
+
return null;
|
|
578
|
+
}
|
|
579
|
+
return new ASTNode(ASTNode.types.FIELD, field);
|
|
494
580
|
}
|
|
495
|
-
};
|
|
496
581
|
|
|
497
|
-
/**
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
582
|
+
/**
|
|
583
|
+
* Creates an AST node pointing to a connection. If the connection has a
|
|
584
|
+
* parent input then create an AST node of type input that will hold the
|
|
585
|
+
* connection.
|
|
586
|
+
* @param {Connection} connection This is the connection the node will
|
|
587
|
+
* point to.
|
|
588
|
+
* @return {ASTNode} An AST node pointing to a connection.
|
|
589
|
+
*/
|
|
590
|
+
static createConnectionNode(connection) {
|
|
591
|
+
if (!connection) {
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
594
|
+
const type = connection.type;
|
|
595
|
+
if (type === ConnectionType.INPUT_VALUE) {
|
|
596
|
+
return ASTNode.createInputNode(connection.getParentInput());
|
|
597
|
+
} else if (
|
|
598
|
+
type === ConnectionType.NEXT_STATEMENT && connection.getParentInput()) {
|
|
599
|
+
return ASTNode.createInputNode(connection.getParentInput());
|
|
600
|
+
} else if (type === ConnectionType.NEXT_STATEMENT) {
|
|
601
|
+
return new ASTNode(ASTNode.types.NEXT, connection);
|
|
602
|
+
} else if (type === ConnectionType.OUTPUT_VALUE) {
|
|
603
|
+
return new ASTNode(ASTNode.types.OUTPUT, connection);
|
|
604
|
+
} else if (type === ConnectionType.PREVIOUS_STATEMENT) {
|
|
605
|
+
return new ASTNode(ASTNode.types.PREVIOUS, connection);
|
|
606
|
+
}
|
|
507
607
|
return null;
|
|
508
608
|
}
|
|
509
|
-
// If the block doesn't have a previous connection then it is the top of the
|
|
510
|
-
// substack.
|
|
511
|
-
const topBlock = block.getTopStackBlock();
|
|
512
|
-
const topConnection = getParentConnection(topBlock);
|
|
513
|
-
// If the top connection has a parentInput, create an AST node pointing to
|
|
514
|
-
// that input.
|
|
515
|
-
if (topConnection && topConnection.targetConnection &&
|
|
516
|
-
topConnection.targetConnection.getParentInput()) {
|
|
517
|
-
return ASTNode.createInputNode(
|
|
518
|
-
topConnection.targetConnection.getParentInput());
|
|
519
|
-
} else {
|
|
520
|
-
// Go to stack level if you are not underneath an input.
|
|
521
|
-
return ASTNode.createStackNode(topBlock);
|
|
522
|
-
}
|
|
523
|
-
};
|
|
524
609
|
|
|
525
|
-
/**
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
for (let i = 0; i < inputs.length; i++) {
|
|
535
|
-
const input = inputs[i];
|
|
536
|
-
const fieldRow = input.fieldRow;
|
|
537
|
-
for (let j = 0; j < fieldRow.length; j++) {
|
|
538
|
-
const field = fieldRow[j];
|
|
539
|
-
if (field.isClickable() || ASTNode.NAVIGATE_ALL_FIELDS) {
|
|
540
|
-
return ASTNode.createFieldNode(field);
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
if (input.connection) {
|
|
544
|
-
return ASTNode.createInputNode(input);
|
|
610
|
+
/**
|
|
611
|
+
* Creates an AST node pointing to an input. Stores the input connection as
|
|
612
|
+
* the location.
|
|
613
|
+
* @param {Input} input The input used to create an AST node.
|
|
614
|
+
* @return {ASTNode} An AST node pointing to a input.
|
|
615
|
+
*/
|
|
616
|
+
static createInputNode(input) {
|
|
617
|
+
if (!input || !input.connection) {
|
|
618
|
+
return null;
|
|
545
619
|
}
|
|
620
|
+
return new ASTNode(ASTNode.types.INPUT, input.connection);
|
|
546
621
|
}
|
|
547
|
-
return null;
|
|
548
|
-
};
|
|
549
622
|
|
|
550
|
-
/**
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
return
|
|
560
|
-
} else if (this.getType() === ASTNode.types.WORKSPACE) {
|
|
561
|
-
return null;
|
|
562
|
-
} else {
|
|
563
|
-
return /** @type {IASTNodeLocationWithBlock} */ (this.getLocation())
|
|
564
|
-
.getSourceBlock();
|
|
623
|
+
/**
|
|
624
|
+
* Creates an AST node pointing to a block.
|
|
625
|
+
* @param {Block} block The block used to create an AST node.
|
|
626
|
+
* @return {ASTNode} An AST node pointing to a block.
|
|
627
|
+
*/
|
|
628
|
+
static createBlockNode(block) {
|
|
629
|
+
if (!block) {
|
|
630
|
+
return null;
|
|
631
|
+
}
|
|
632
|
+
return new ASTNode(ASTNode.types.BLOCK, block);
|
|
565
633
|
}
|
|
566
|
-
};
|
|
567
634
|
|
|
568
|
-
/**
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
return ASTNode.createBlockNode(connection.getSourceBlock());
|
|
635
|
+
/**
|
|
636
|
+
* Create an AST node of type stack. A stack, represented by its top block, is
|
|
637
|
+
* the set of all blocks connected to a top block, including the top
|
|
638
|
+
* block.
|
|
639
|
+
* @param {Block} topBlock A top block has no parent and can be found
|
|
640
|
+
* in the list returned by workspace.getTopBlocks().
|
|
641
|
+
* @return {ASTNode} An AST node of type stack that points to the top
|
|
642
|
+
* block on the stack.
|
|
643
|
+
*/
|
|
644
|
+
static createStackNode(topBlock) {
|
|
645
|
+
if (!topBlock) {
|
|
646
|
+
return null;
|
|
581
647
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
case ASTNode.types.INPUT:
|
|
586
|
-
return this.findNextForInput_();
|
|
648
|
+
return new ASTNode(ASTNode.types.STACK, topBlock);
|
|
649
|
+
}
|
|
587
650
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
const targetConnection = connection.targetConnection;
|
|
600
|
-
return ASTNode.createConnectionNode(targetConnection);
|
|
651
|
+
/**
|
|
652
|
+
* Creates an AST node pointing to a workspace.
|
|
653
|
+
* @param {!Workspace} workspace The workspace that we are on.
|
|
654
|
+
* @param {Coordinate} wsCoordinate The position on the workspace
|
|
655
|
+
* for this node.
|
|
656
|
+
* @return {ASTNode} An AST node pointing to a workspace and a position
|
|
657
|
+
* on the workspace.
|
|
658
|
+
*/
|
|
659
|
+
static createWorkspaceNode(workspace, wsCoordinate) {
|
|
660
|
+
if (!wsCoordinate || !workspace) {
|
|
661
|
+
return null;
|
|
601
662
|
}
|
|
663
|
+
const params = {wsCoordinate: wsCoordinate};
|
|
664
|
+
return new ASTNode(ASTNode.types.WORKSPACE, workspace, params);
|
|
602
665
|
}
|
|
603
666
|
|
|
604
|
-
|
|
605
|
-
|
|
667
|
+
/**
|
|
668
|
+
* Creates an AST node for the top position on a block.
|
|
669
|
+
* This is either an output connection, previous connection, or block.
|
|
670
|
+
* @param {!Block} block The block to find the top most AST node on.
|
|
671
|
+
* @return {ASTNode} The AST node holding the top most position on the
|
|
672
|
+
* block.
|
|
673
|
+
*/
|
|
674
|
+
static createTopNode(block) {
|
|
675
|
+
let astNode;
|
|
676
|
+
const topConnection = getParentConnection(block);
|
|
677
|
+
if (topConnection) {
|
|
678
|
+
astNode = ASTNode.createConnectionNode(topConnection);
|
|
679
|
+
} else {
|
|
680
|
+
astNode = ASTNode.createBlockNode(block);
|
|
681
|
+
}
|
|
682
|
+
return astNode;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
606
685
|
|
|
607
686
|
/**
|
|
608
|
-
*
|
|
609
|
-
*
|
|
610
|
-
*
|
|
611
|
-
* workspace, or block. Or null if there is nothing below this node.
|
|
687
|
+
* @typedef {{
|
|
688
|
+
* wsCoordinate: Coordinate
|
|
689
|
+
* }}
|
|
612
690
|
*/
|
|
613
|
-
ASTNode.
|
|
614
|
-
switch (this.type_) {
|
|
615
|
-
case ASTNode.types.WORKSPACE: {
|
|
616
|
-
const workspace = /** @type {!Workspace} */ (this.location_);
|
|
617
|
-
const topBlocks = workspace.getTopBlocks(true);
|
|
618
|
-
if (topBlocks.length > 0) {
|
|
619
|
-
return ASTNode.createStackNode(topBlocks[0]);
|
|
620
|
-
}
|
|
621
|
-
break;
|
|
622
|
-
}
|
|
623
|
-
case ASTNode.types.STACK: {
|
|
624
|
-
const block = /** @type {!Block} */ (this.location_);
|
|
625
|
-
return this.findTopASTNodeForBlock_(block);
|
|
626
|
-
}
|
|
627
|
-
case ASTNode.types.BLOCK: {
|
|
628
|
-
const block = /** @type {!Block} */ (this.location_);
|
|
629
|
-
return this.findFirstFieldOrInput_(block);
|
|
630
|
-
}
|
|
631
|
-
case ASTNode.types.INPUT: {
|
|
632
|
-
const connection = /** @type {!Connection} */ (this.location_);
|
|
633
|
-
const targetConnection = connection.targetConnection;
|
|
634
|
-
return ASTNode.createConnectionNode(targetConnection);
|
|
635
|
-
}
|
|
636
|
-
}
|
|
691
|
+
ASTNode.Params;
|
|
637
692
|
|
|
638
|
-
|
|
693
|
+
/**
|
|
694
|
+
* Object holding different types for an AST node.
|
|
695
|
+
* @enum {string}
|
|
696
|
+
*/
|
|
697
|
+
ASTNode.types = {
|
|
698
|
+
FIELD: 'field',
|
|
699
|
+
BLOCK: 'block',
|
|
700
|
+
INPUT: 'input',
|
|
701
|
+
OUTPUT: 'output',
|
|
702
|
+
NEXT: 'next',
|
|
703
|
+
PREVIOUS: 'previous',
|
|
704
|
+
STACK: 'stack',
|
|
705
|
+
WORKSPACE: 'workspace',
|
|
639
706
|
};
|
|
640
707
|
|
|
641
708
|
/**
|
|
642
|
-
*
|
|
643
|
-
* @
|
|
644
|
-
* connection, workspace or block. Or null if no node exists to the left.
|
|
645
|
-
* null.
|
|
709
|
+
* True to navigate to all fields. False to only navigate to clickable fields.
|
|
710
|
+
* @type {boolean}
|
|
646
711
|
*/
|
|
647
|
-
ASTNode.
|
|
648
|
-
switch (this.type_) {
|
|
649
|
-
case ASTNode.types.STACK:
|
|
650
|
-
return this.navigateBetweenStacks_(false);
|
|
651
|
-
|
|
652
|
-
case ASTNode.types.OUTPUT:
|
|
653
|
-
return null;
|
|
654
|
-
|
|
655
|
-
case ASTNode.types.FIELD:
|
|
656
|
-
return this.findPrevForField_();
|
|
657
|
-
|
|
658
|
-
case ASTNode.types.INPUT:
|
|
659
|
-
return this.findPrevForInput_();
|
|
660
|
-
|
|
661
|
-
case ASTNode.types.BLOCK: {
|
|
662
|
-
const block = /** @type {!Block} */ (this.location_);
|
|
663
|
-
const topConnection = getParentConnection(block);
|
|
664
|
-
return ASTNode.createConnectionNode(topConnection);
|
|
665
|
-
}
|
|
666
|
-
case ASTNode.types.PREVIOUS: {
|
|
667
|
-
const connection = /** @type {!Connection} */ (this.location_);
|
|
668
|
-
const targetConnection = connection.targetConnection;
|
|
669
|
-
if (targetConnection && !targetConnection.getParentInput()) {
|
|
670
|
-
return ASTNode.createConnectionNode(targetConnection);
|
|
671
|
-
}
|
|
672
|
-
break;
|
|
673
|
-
}
|
|
674
|
-
case ASTNode.types.NEXT: {
|
|
675
|
-
const connection = /** @type {!Connection} */ (this.location_);
|
|
676
|
-
return ASTNode.createBlockNode(connection.getSourceBlock());
|
|
677
|
-
}
|
|
678
|
-
}
|
|
712
|
+
ASTNode.NAVIGATE_ALL_FIELDS = false;
|
|
679
713
|
|
|
680
|
-
|
|
681
|
-
|
|
714
|
+
/**
|
|
715
|
+
* The default y offset to use when moving the cursor from a stack to the
|
|
716
|
+
* workspace.
|
|
717
|
+
* @type {number}
|
|
718
|
+
* @private
|
|
719
|
+
*/
|
|
720
|
+
ASTNode.DEFAULT_OFFSET_Y = -20;
|
|
682
721
|
|
|
683
722
|
/**
|
|
684
|
-
*
|
|
685
|
-
*
|
|
686
|
-
*
|
|
687
|
-
*
|
|
723
|
+
* Gets the parent connection on a block.
|
|
724
|
+
* This is either an output connection, previous connection or undefined.
|
|
725
|
+
* If both connections exist return the one that is actually connected
|
|
726
|
+
* to another block.
|
|
727
|
+
* @param {!Block} block The block to find the parent connection on.
|
|
728
|
+
* @return {Connection} The connection connecting to the parent of the
|
|
729
|
+
* block.
|
|
730
|
+
* @private
|
|
688
731
|
*/
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
// TODO: Make sure this is in the bounds of the workspace.
|
|
695
|
-
const wsCoordinate =
|
|
696
|
-
new Coordinate(blockPos.x, blockPos.y + ASTNode.DEFAULT_OFFSET_Y);
|
|
697
|
-
return ASTNode.createWorkspaceNode(block.workspace, wsCoordinate);
|
|
698
|
-
}
|
|
699
|
-
case ASTNode.types.OUTPUT: {
|
|
700
|
-
const connection = /** @type {!Connection} */ (this.location_);
|
|
701
|
-
const target = connection.targetConnection;
|
|
702
|
-
if (target) {
|
|
703
|
-
return ASTNode.createConnectionNode(target);
|
|
704
|
-
}
|
|
705
|
-
return ASTNode.createStackNode(connection.getSourceBlock());
|
|
706
|
-
}
|
|
707
|
-
case ASTNode.types.FIELD: {
|
|
708
|
-
const field = /** @type {!Field} */ (this.location_);
|
|
709
|
-
return ASTNode.createBlockNode(field.getSourceBlock());
|
|
710
|
-
}
|
|
711
|
-
case ASTNode.types.INPUT: {
|
|
712
|
-
const connection = /** @type {!Connection} */ (this.location_);
|
|
713
|
-
return ASTNode.createBlockNode(connection.getSourceBlock());
|
|
714
|
-
}
|
|
715
|
-
case ASTNode.types.BLOCK: {
|
|
716
|
-
const block = /** @type {!Block} */ (this.location_);
|
|
717
|
-
return this.getOutAstNodeForBlock_(block);
|
|
718
|
-
}
|
|
719
|
-
case ASTNode.types.PREVIOUS: {
|
|
720
|
-
const connection = /** @type {!Connection} */ (this.location_);
|
|
721
|
-
return this.getOutAstNodeForBlock_(connection.getSourceBlock());
|
|
722
|
-
}
|
|
723
|
-
case ASTNode.types.NEXT: {
|
|
724
|
-
const connection = /** @type {!Connection} */ (this.location_);
|
|
725
|
-
return this.getOutAstNodeForBlock_(connection.getSourceBlock());
|
|
726
|
-
}
|
|
732
|
+
const getParentConnection = function(block) {
|
|
733
|
+
let topConnection = block.outputConnection;
|
|
734
|
+
if (!topConnection ||
|
|
735
|
+
(block.previousConnection && block.previousConnection.isConnected())) {
|
|
736
|
+
topConnection = block.previousConnection;
|
|
727
737
|
}
|
|
728
|
-
|
|
729
|
-
return null;
|
|
738
|
+
return topConnection;
|
|
730
739
|
};
|
|
731
740
|
|
|
732
741
|
exports.ASTNode = ASTNode;
|