blockly 7.20211209.2 → 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/trashcan.js
CHANGED
|
@@ -15,19 +15,19 @@
|
|
|
15
15
|
*/
|
|
16
16
|
goog.module('Blockly.Trashcan');
|
|
17
17
|
|
|
18
|
-
/* eslint-disable-next-line no-unused-vars */
|
|
19
|
-
const Abstract = goog.requireType('Blockly.Events.Abstract');
|
|
20
18
|
/* eslint-disable-next-line no-unused-vars */
|
|
21
19
|
const blocks = goog.requireType('Blockly.serialization.blocks');
|
|
22
20
|
const browserEvents = goog.require('Blockly.browserEvents');
|
|
23
21
|
const dom = goog.require('Blockly.utils.dom');
|
|
24
22
|
const eventUtils = goog.require('Blockly.Events.utils');
|
|
25
|
-
const internalConstants = goog.require('Blockly.internalConstants');
|
|
26
|
-
const object = goog.require('Blockly.utils.object');
|
|
27
23
|
const registry = goog.require('Blockly.registry');
|
|
28
24
|
const toolbox = goog.require('Blockly.utils.toolbox');
|
|
29
25
|
const uiPosition = goog.require('Blockly.uiPosition');
|
|
30
26
|
/* eslint-disable-next-line no-unused-vars */
|
|
27
|
+
const {Abstract} = goog.requireType('Blockly.Events.Abstract');
|
|
28
|
+
/* eslint-disable-next-line no-unused-vars */
|
|
29
|
+
const {BlockDelete} = goog.requireType('Blockly.Events.BlockDelete');
|
|
30
|
+
/* eslint-disable-next-line no-unused-vars */
|
|
31
31
|
const {BlocklyOptions} = goog.requireType('Blockly.BlocklyOptions');
|
|
32
32
|
const {ComponentManager} = goog.require('Blockly.ComponentManager');
|
|
33
33
|
const {DeleteArea} = goog.require('Blockly.DeleteArea');
|
|
@@ -44,6 +44,7 @@ const {MetricsManager} = goog.requireType('Blockly.MetricsManager');
|
|
|
44
44
|
const {Options} = goog.require('Blockly.Options');
|
|
45
45
|
const {Rect} = goog.require('Blockly.utils.Rect');
|
|
46
46
|
const {Size} = goog.require('Blockly.utils.Size');
|
|
47
|
+
const {SPRITE} = goog.require('Blockly.sprite');
|
|
47
48
|
const {Svg} = goog.require('Blockly.utils.Svg');
|
|
48
49
|
/* eslint-disable-next-line no-unused-vars */
|
|
49
50
|
const {WorkspaceSvg} = goog.requireType('Blockly.WorkspaceSvg');
|
|
@@ -53,694 +54,704 @@ goog.require('Blockly.Events.TrashcanOpen');
|
|
|
53
54
|
|
|
54
55
|
/**
|
|
55
56
|
* Class for a trash can.
|
|
56
|
-
* @param {!WorkspaceSvg} workspace The workspace to sit in.
|
|
57
|
-
* @constructor
|
|
58
57
|
* @implements {IAutoHideable}
|
|
59
58
|
* @implements {IPositionable}
|
|
60
59
|
* @extends {DeleteArea}
|
|
61
60
|
* @alias Blockly.Trashcan
|
|
62
61
|
*/
|
|
63
|
-
|
|
64
|
-
Trashcan.superClass_.constructor.call(this);
|
|
62
|
+
class Trashcan extends DeleteArea {
|
|
65
63
|
/**
|
|
66
|
-
* The workspace
|
|
67
|
-
* @type {!WorkspaceSvg}
|
|
68
|
-
* @private
|
|
64
|
+
* @param {!WorkspaceSvg} workspace The workspace to sit in.
|
|
69
65
|
*/
|
|
70
|
-
|
|
66
|
+
constructor(workspace) {
|
|
67
|
+
super();
|
|
68
|
+
/**
|
|
69
|
+
* The workspace the trashcan sits in.
|
|
70
|
+
* @type {!WorkspaceSvg}
|
|
71
|
+
* @private
|
|
72
|
+
*/
|
|
73
|
+
this.workspace_ = workspace;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The unique id for this component that is used to register with the
|
|
77
|
+
* ComponentManager.
|
|
78
|
+
* @type {string}
|
|
79
|
+
*/
|
|
80
|
+
this.id = 'trashcan';
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* A list of JSON (stored as strings) representing blocks in the trashcan.
|
|
84
|
+
* @type {!Array<string>}
|
|
85
|
+
* @private
|
|
86
|
+
*/
|
|
87
|
+
this.contents_ = [];
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The trashcan flyout.
|
|
91
|
+
* @type {IFlyout}
|
|
92
|
+
* @package
|
|
93
|
+
*/
|
|
94
|
+
this.flyout = null;
|
|
95
|
+
|
|
96
|
+
if (this.workspace_.options.maxTrashcanContents <= 0) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Current open/close state of the lid.
|
|
102
|
+
* @type {boolean}
|
|
103
|
+
*/
|
|
104
|
+
this.isLidOpen = false;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* The minimum openness of the lid. Used to indicate if the trashcan
|
|
108
|
+
* contains blocks.
|
|
109
|
+
* @type {number}
|
|
110
|
+
* @private
|
|
111
|
+
*/
|
|
112
|
+
this.minOpenness_ = 0;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The SVG group containing the trash can.
|
|
116
|
+
* @type {SVGElement}
|
|
117
|
+
* @private
|
|
118
|
+
*/
|
|
119
|
+
this.svgGroup_ = null;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The SVG image element of the trash can lid.
|
|
123
|
+
* @type {SVGElement}
|
|
124
|
+
* @private
|
|
125
|
+
*/
|
|
126
|
+
this.svgLid_ = null;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Task ID of opening/closing animation.
|
|
130
|
+
* @type {number}
|
|
131
|
+
* @private
|
|
132
|
+
*/
|
|
133
|
+
this.lidTask_ = 0;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Current state of lid opening (0.0 = closed, 1.0 = open).
|
|
137
|
+
* @type {number}
|
|
138
|
+
* @private
|
|
139
|
+
*/
|
|
140
|
+
this.lidOpen_ = 0;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Left coordinate of the trash can.
|
|
144
|
+
* @type {number}
|
|
145
|
+
* @private
|
|
146
|
+
*/
|
|
147
|
+
this.left_ = 0;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Top coordinate of the trash can.
|
|
151
|
+
* @type {number}
|
|
152
|
+
* @private
|
|
153
|
+
*/
|
|
154
|
+
this.top_ = 0;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Whether this trash can has been initialized.
|
|
158
|
+
* @type {boolean}
|
|
159
|
+
* @private
|
|
160
|
+
*/
|
|
161
|
+
this.initialized_ = false;
|
|
162
|
+
|
|
163
|
+
// Create flyout options.
|
|
164
|
+
const flyoutWorkspaceOptions = new Options(
|
|
165
|
+
/** @type {!BlocklyOptions} */
|
|
166
|
+
({
|
|
167
|
+
'scrollbars': true,
|
|
168
|
+
'parentWorkspace': this.workspace_,
|
|
169
|
+
'rtl': this.workspace_.RTL,
|
|
170
|
+
'oneBasedIndex': this.workspace_.options.oneBasedIndex,
|
|
171
|
+
'renderer': this.workspace_.options.renderer,
|
|
172
|
+
'rendererOverrides': this.workspace_.options.rendererOverrides,
|
|
173
|
+
'move': {
|
|
174
|
+
'scrollbars': true,
|
|
175
|
+
},
|
|
176
|
+
}));
|
|
177
|
+
// Create vertical or horizontal flyout.
|
|
178
|
+
if (this.workspace_.horizontalLayout) {
|
|
179
|
+
flyoutWorkspaceOptions.toolboxPosition =
|
|
180
|
+
this.workspace_.toolboxPosition === toolbox.Position.TOP ?
|
|
181
|
+
toolbox.Position.BOTTOM :
|
|
182
|
+
toolbox.Position.TOP;
|
|
183
|
+
const HorizontalFlyout = registry.getClassFromOptions(
|
|
184
|
+
registry.Type.FLYOUTS_HORIZONTAL_TOOLBOX, this.workspace_.options,
|
|
185
|
+
true);
|
|
186
|
+
this.flyout = new HorizontalFlyout(flyoutWorkspaceOptions);
|
|
187
|
+
} else {
|
|
188
|
+
flyoutWorkspaceOptions.toolboxPosition =
|
|
189
|
+
this.workspace_.toolboxPosition === toolbox.Position.RIGHT ?
|
|
190
|
+
toolbox.Position.LEFT :
|
|
191
|
+
toolbox.Position.RIGHT;
|
|
192
|
+
const VerticalFlyout = registry.getClassFromOptions(
|
|
193
|
+
registry.Type.FLYOUTS_VERTICAL_TOOLBOX, this.workspace_.options,
|
|
194
|
+
true);
|
|
195
|
+
this.flyout = new VerticalFlyout(flyoutWorkspaceOptions);
|
|
196
|
+
}
|
|
197
|
+
this.workspace_.addChangeListener(this.onDelete_.bind(this));
|
|
198
|
+
}
|
|
71
199
|
|
|
72
200
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* @type {string}
|
|
201
|
+
* Create the trash can elements.
|
|
202
|
+
* @return {!SVGElement} The trash can's SVG group.
|
|
76
203
|
*/
|
|
77
|
-
|
|
204
|
+
createDom() {
|
|
205
|
+
/* Here's the markup that will be generated:
|
|
206
|
+
<g class="blocklyTrash">
|
|
207
|
+
<clippath id="blocklyTrashBodyClipPath837493">
|
|
208
|
+
<rect width="47" height="45" y="15"></rect>
|
|
209
|
+
</clippath>
|
|
210
|
+
<image width="64" height="92" y="-32" xlink:href="media/sprites.png"
|
|
211
|
+
clip-path="url(#blocklyTrashBodyClipPath837493)"></image>
|
|
212
|
+
<clippath id="blocklyTrashLidClipPath837493">
|
|
213
|
+
<rect width="47" height="15"></rect>
|
|
214
|
+
</clippath>
|
|
215
|
+
<image width="84" height="92" y="-32" xlink:href="media/sprites.png"
|
|
216
|
+
clip-path="url(#blocklyTrashLidClipPath837493)"></image>
|
|
217
|
+
</g>
|
|
218
|
+
*/
|
|
219
|
+
this.svgGroup_ =
|
|
220
|
+
dom.createSvgElement(Svg.G, {'class': 'blocklyTrash'}, null);
|
|
221
|
+
let clip;
|
|
222
|
+
const rnd = String(Math.random()).substring(2);
|
|
223
|
+
clip = dom.createSvgElement(
|
|
224
|
+
Svg.CLIPPATH, {'id': 'blocklyTrashBodyClipPath' + rnd}, this.svgGroup_);
|
|
225
|
+
dom.createSvgElement(
|
|
226
|
+
Svg.RECT, {'width': WIDTH, 'height': BODY_HEIGHT, 'y': LID_HEIGHT},
|
|
227
|
+
clip);
|
|
228
|
+
const body = dom.createSvgElement(
|
|
229
|
+
Svg.IMAGE, {
|
|
230
|
+
'width': SPRITE.width,
|
|
231
|
+
'x': -SPRITE_LEFT,
|
|
232
|
+
'height': SPRITE.height,
|
|
233
|
+
'y': -SPRITE_TOP,
|
|
234
|
+
'clip-path': 'url(#blocklyTrashBodyClipPath' + rnd + ')',
|
|
235
|
+
},
|
|
236
|
+
this.svgGroup_);
|
|
237
|
+
body.setAttributeNS(
|
|
238
|
+
dom.XLINK_NS, 'xlink:href',
|
|
239
|
+
this.workspace_.options.pathToMedia + SPRITE.url);
|
|
240
|
+
|
|
241
|
+
clip = dom.createSvgElement(
|
|
242
|
+
Svg.CLIPPATH, {'id': 'blocklyTrashLidClipPath' + rnd}, this.svgGroup_);
|
|
243
|
+
dom.createSvgElement(
|
|
244
|
+
Svg.RECT, {'width': WIDTH, 'height': LID_HEIGHT}, clip);
|
|
245
|
+
this.svgLid_ = dom.createSvgElement(
|
|
246
|
+
Svg.IMAGE, {
|
|
247
|
+
'width': SPRITE.width,
|
|
248
|
+
'x': -SPRITE_LEFT,
|
|
249
|
+
'height': SPRITE.height,
|
|
250
|
+
'y': -SPRITE_TOP,
|
|
251
|
+
'clip-path': 'url(#blocklyTrashLidClipPath' + rnd + ')',
|
|
252
|
+
},
|
|
253
|
+
this.svgGroup_);
|
|
254
|
+
this.svgLid_.setAttributeNS(
|
|
255
|
+
dom.XLINK_NS, 'xlink:href',
|
|
256
|
+
this.workspace_.options.pathToMedia + SPRITE.url);
|
|
257
|
+
|
|
258
|
+
// bindEventWithChecks_ quashes events too aggressively. See:
|
|
259
|
+
// https://groups.google.com/forum/#!topic/blockly/QF4yB9Wx00s
|
|
260
|
+
// Using bindEventWithChecks_ for blocking mousedown causes issue in mobile.
|
|
261
|
+
// See #4303
|
|
262
|
+
browserEvents.bind(
|
|
263
|
+
this.svgGroup_, 'mousedown', this, this.blockMouseDownWhenOpenable_);
|
|
264
|
+
browserEvents.bind(this.svgGroup_, 'mouseup', this, this.click);
|
|
265
|
+
// Bind to body instead of this.svgGroup_ so that we don't get lid jitters
|
|
266
|
+
browserEvents.bind(body, 'mouseover', this, this.mouseOver_);
|
|
267
|
+
browserEvents.bind(body, 'mouseout', this, this.mouseOut_);
|
|
268
|
+
this.animateLid_();
|
|
269
|
+
return this.svgGroup_;
|
|
270
|
+
}
|
|
78
271
|
|
|
79
272
|
/**
|
|
80
|
-
*
|
|
81
|
-
* @type {!Array<string>}
|
|
82
|
-
* @private
|
|
273
|
+
* Initializes the trash can.
|
|
83
274
|
*/
|
|
84
|
-
|
|
275
|
+
init() {
|
|
276
|
+
if (this.workspace_.options.maxTrashcanContents > 0) {
|
|
277
|
+
dom.insertAfter(
|
|
278
|
+
this.flyout.createDom(Svg.SVG), this.workspace_.getParentSvg());
|
|
279
|
+
this.flyout.init(this.workspace_);
|
|
280
|
+
}
|
|
281
|
+
this.workspace_.getComponentManager().addComponent({
|
|
282
|
+
component: this,
|
|
283
|
+
weight: 1,
|
|
284
|
+
capabilities: [
|
|
285
|
+
ComponentManager.Capability.AUTOHIDEABLE,
|
|
286
|
+
ComponentManager.Capability.DELETE_AREA,
|
|
287
|
+
ComponentManager.Capability.DRAG_TARGET,
|
|
288
|
+
ComponentManager.Capability.POSITIONABLE,
|
|
289
|
+
],
|
|
290
|
+
});
|
|
291
|
+
this.initialized_ = true;
|
|
292
|
+
this.setLidOpen(false);
|
|
293
|
+
}
|
|
85
294
|
|
|
86
295
|
/**
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
* @
|
|
296
|
+
* Dispose of this trash can.
|
|
297
|
+
* Unlink from all DOM elements to prevent memory leaks.
|
|
298
|
+
* @suppress {checkTypes}
|
|
90
299
|
*/
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
'scrollbars': true,
|
|
101
|
-
'parentWorkspace': this.workspace_,
|
|
102
|
-
'rtl': this.workspace_.RTL,
|
|
103
|
-
'oneBasedIndex': this.workspace_.options.oneBasedIndex,
|
|
104
|
-
'renderer': this.workspace_.options.renderer,
|
|
105
|
-
'rendererOverrides': this.workspace_.options.rendererOverrides,
|
|
106
|
-
'move': {
|
|
107
|
-
'scrollbars': true,
|
|
108
|
-
},
|
|
109
|
-
}));
|
|
110
|
-
// Create vertical or horizontal flyout.
|
|
111
|
-
if (this.workspace_.horizontalLayout) {
|
|
112
|
-
flyoutWorkspaceOptions.toolboxPosition =
|
|
113
|
-
this.workspace_.toolboxPosition === toolbox.Position.TOP ?
|
|
114
|
-
toolbox.Position.BOTTOM :
|
|
115
|
-
toolbox.Position.TOP;
|
|
116
|
-
const HorizontalFlyout = registry.getClassFromOptions(
|
|
117
|
-
registry.Type.FLYOUTS_HORIZONTAL_TOOLBOX, this.workspace_.options,
|
|
118
|
-
true);
|
|
119
|
-
this.flyout = new HorizontalFlyout(flyoutWorkspaceOptions);
|
|
120
|
-
} else {
|
|
121
|
-
flyoutWorkspaceOptions.toolboxPosition =
|
|
122
|
-
this.workspace_.toolboxPosition === toolbox.Position.RIGHT ?
|
|
123
|
-
toolbox.Position.LEFT :
|
|
124
|
-
toolbox.Position.RIGHT;
|
|
125
|
-
const VerticalFlyout = registry.getClassFromOptions(
|
|
126
|
-
registry.Type.FLYOUTS_VERTICAL_TOOLBOX, this.workspace_.options, true);
|
|
127
|
-
this.flyout = new VerticalFlyout(flyoutWorkspaceOptions);
|
|
300
|
+
dispose() {
|
|
301
|
+
this.workspace_.getComponentManager().removeComponent('trashcan');
|
|
302
|
+
if (this.svgGroup_) {
|
|
303
|
+
dom.removeNode(this.svgGroup_);
|
|
304
|
+
this.svgGroup_ = null;
|
|
305
|
+
}
|
|
306
|
+
this.svgLid_ = null;
|
|
307
|
+
this.workspace_ = null;
|
|
308
|
+
clearTimeout(this.lidTask_);
|
|
128
309
|
}
|
|
129
|
-
this.workspace_.addChangeListener(this.onDelete_.bind(this));
|
|
130
|
-
};
|
|
131
|
-
object.inherits(Trashcan, DeleteArea);
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Width of both the trash can and lid images.
|
|
135
|
-
*/
|
|
136
|
-
const WIDTH = 47;
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Height of the trashcan image (minus lid).
|
|
140
|
-
*/
|
|
141
|
-
const BODY_HEIGHT = 44;
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Height of the lid image.
|
|
145
|
-
*/
|
|
146
|
-
const LID_HEIGHT = 16;
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Distance between trashcan and bottom or top edge of workspace.
|
|
150
|
-
*/
|
|
151
|
-
const MARGIN_VERTICAL = 20;
|
|
152
310
|
|
|
153
|
-
/**
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const MARGIN_HOTSPOT = 10;
|
|
311
|
+
/**
|
|
312
|
+
* Whether the trashcan has contents.
|
|
313
|
+
* @return {boolean} True if the trashcan has contents.
|
|
314
|
+
* @private
|
|
315
|
+
*/
|
|
316
|
+
hasContents_() {
|
|
317
|
+
return !!this.contents_.length;
|
|
318
|
+
}
|
|
162
319
|
|
|
163
|
-
/**
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
320
|
+
/**
|
|
321
|
+
* Returns true if the trashcan contents-flyout is currently open.
|
|
322
|
+
* @return {boolean} True if the trashcan contents-flyout is currently open.
|
|
323
|
+
*/
|
|
324
|
+
contentsIsOpen() {
|
|
325
|
+
return !!this.flyout && this.flyout.isVisible();
|
|
326
|
+
}
|
|
167
327
|
|
|
168
|
-
/**
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
328
|
+
/**
|
|
329
|
+
* Opens the trashcan flyout.
|
|
330
|
+
*/
|
|
331
|
+
openFlyout() {
|
|
332
|
+
if (this.contentsIsOpen()) {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
const contents = this.contents_.map(function(string) {
|
|
336
|
+
return JSON.parse(string);
|
|
337
|
+
});
|
|
338
|
+
this.flyout.show(contents);
|
|
339
|
+
this.fireUiEvent_(true);
|
|
340
|
+
}
|
|
172
341
|
|
|
173
|
-
/**
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
342
|
+
/**
|
|
343
|
+
* Closes the trashcan flyout.
|
|
344
|
+
*/
|
|
345
|
+
closeFlyout() {
|
|
346
|
+
if (!this.contentsIsOpen()) {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
this.flyout.hide();
|
|
350
|
+
this.fireUiEvent_(false);
|
|
351
|
+
this.workspace_.recordDragTargets();
|
|
352
|
+
}
|
|
178
353
|
|
|
179
|
-
/**
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
354
|
+
/**
|
|
355
|
+
* Hides the component. Called in WorkspaceSvg.hideChaff.
|
|
356
|
+
* @param {boolean} onlyClosePopups Whether only popups should be closed.
|
|
357
|
+
* Flyouts should not be closed if this is true.
|
|
358
|
+
*/
|
|
359
|
+
autoHide(onlyClosePopups) {
|
|
360
|
+
// For now the trashcan flyout always autocloses because it overlays the
|
|
361
|
+
// trashcan UI (no trashcan to click to close it).
|
|
362
|
+
if (!onlyClosePopups && this.flyout) {
|
|
363
|
+
this.closeFlyout();
|
|
364
|
+
}
|
|
365
|
+
}
|
|
183
366
|
|
|
184
|
-
/**
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
367
|
+
/**
|
|
368
|
+
* Empties the trashcan's contents. If the contents-flyout is currently open
|
|
369
|
+
* it will be closed.
|
|
370
|
+
*/
|
|
371
|
+
emptyContents() {
|
|
372
|
+
if (!this.hasContents_()) {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
this.contents_.length = 0;
|
|
376
|
+
this.setMinOpenness_(0);
|
|
377
|
+
this.closeFlyout();
|
|
378
|
+
}
|
|
188
379
|
|
|
189
|
-
/**
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
380
|
+
/**
|
|
381
|
+
* Positions the trashcan.
|
|
382
|
+
* It is positioned in the opposite corner to the corner the
|
|
383
|
+
* categories/toolbox starts at.
|
|
384
|
+
* @param {!MetricsManager.UiMetrics} metrics The workspace metrics.
|
|
385
|
+
* @param {!Array<!Rect>} savedPositions List of rectangles that
|
|
386
|
+
* are already on the workspace.
|
|
387
|
+
*/
|
|
388
|
+
position(metrics, savedPositions) {
|
|
389
|
+
// Not yet initialized.
|
|
390
|
+
if (!this.initialized_) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
193
393
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const
|
|
394
|
+
const cornerPosition =
|
|
395
|
+
uiPosition.getCornerOppositeToolbox(this.workspace_, metrics);
|
|
396
|
+
|
|
397
|
+
const height = BODY_HEIGHT + LID_HEIGHT;
|
|
398
|
+
const startRect = uiPosition.getStartPositionRect(
|
|
399
|
+
cornerPosition, new Size(WIDTH, height), MARGIN_HORIZONTAL,
|
|
400
|
+
MARGIN_VERTICAL, metrics, this.workspace_);
|
|
401
|
+
|
|
402
|
+
const verticalPosition = cornerPosition.vertical;
|
|
403
|
+
const bumpDirection = verticalPosition === uiPosition.verticalPosition.TOP ?
|
|
404
|
+
uiPosition.bumpDirection.DOWN :
|
|
405
|
+
uiPosition.bumpDirection.UP;
|
|
406
|
+
const positionRect = uiPosition.bumpPositionRect(
|
|
407
|
+
startRect, MARGIN_VERTICAL, bumpDirection, savedPositions);
|
|
408
|
+
|
|
409
|
+
this.top_ = positionRect.top;
|
|
410
|
+
this.left_ = positionRect.left;
|
|
411
|
+
this.svgGroup_.setAttribute(
|
|
412
|
+
'transform', 'translate(' + this.left_ + ',' + this.top_ + ')');
|
|
413
|
+
}
|
|
198
414
|
|
|
199
|
-
/**
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
415
|
+
/**
|
|
416
|
+
* Returns the bounding rectangle of the UI element in pixel units relative to
|
|
417
|
+
* the Blockly injection div.
|
|
418
|
+
* @return {?Rect} The UI elements's bounding box. Null if
|
|
419
|
+
* bounding box should be ignored by other UI elements.
|
|
420
|
+
*/
|
|
421
|
+
getBoundingRectangle() {
|
|
422
|
+
const bottom = this.top_ + BODY_HEIGHT + LID_HEIGHT;
|
|
423
|
+
const right = this.left_ + WIDTH;
|
|
424
|
+
return new Rect(this.top_, bottom, this.left_, right);
|
|
425
|
+
}
|
|
204
426
|
|
|
205
|
-
/**
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
427
|
+
/**
|
|
428
|
+
* Returns the bounding rectangle of the drag target area in pixel units
|
|
429
|
+
* relative to viewport.
|
|
430
|
+
* @return {?Rect} The component's bounding box. Null if drag
|
|
431
|
+
* target area should be ignored.
|
|
432
|
+
*/
|
|
433
|
+
getClientRect() {
|
|
434
|
+
if (!this.svgGroup_) {
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
210
437
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
*
|
|
214
|
-
|
|
215
|
-
*
|
|
216
|
-
|
|
217
|
-
|
|
438
|
+
const trashRect = this.svgGroup_.getBoundingClientRect();
|
|
439
|
+
const top = trashRect.top + SPRITE_TOP - MARGIN_HOTSPOT;
|
|
440
|
+
const bottom = top + LID_HEIGHT + BODY_HEIGHT + 2 * MARGIN_HOTSPOT;
|
|
441
|
+
const left = trashRect.left + SPRITE_LEFT - MARGIN_HOTSPOT;
|
|
442
|
+
const right = left + WIDTH + 2 * MARGIN_HOTSPOT;
|
|
443
|
+
return new Rect(top, bottom, left, right);
|
|
444
|
+
}
|
|
218
445
|
|
|
219
|
-
/**
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
446
|
+
/**
|
|
447
|
+
* Handles when a cursor with a block or bubble is dragged over this drag
|
|
448
|
+
* target.
|
|
449
|
+
* @param {!IDraggable} _dragElement The block or bubble currently being
|
|
450
|
+
* dragged.
|
|
451
|
+
* @override
|
|
452
|
+
*/
|
|
453
|
+
onDragOver(_dragElement) {
|
|
454
|
+
this.setLidOpen(this.wouldDelete_);
|
|
455
|
+
}
|
|
225
456
|
|
|
226
|
-
/**
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
457
|
+
/**
|
|
458
|
+
* Handles when a cursor with a block or bubble exits this drag target.
|
|
459
|
+
* @param {!IDraggable} _dragElement The block or bubble currently being
|
|
460
|
+
* dragged.
|
|
461
|
+
* @override
|
|
462
|
+
*/
|
|
463
|
+
onDragExit(_dragElement) {
|
|
464
|
+
this.setLidOpen(false);
|
|
465
|
+
}
|
|
232
466
|
|
|
233
|
-
/**
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
467
|
+
/**
|
|
468
|
+
* Handles when a block or bubble is dropped on this component.
|
|
469
|
+
* Should not handle delete here.
|
|
470
|
+
* @param {!IDraggable} _dragElement The block or bubble currently being
|
|
471
|
+
* dragged.
|
|
472
|
+
* @override
|
|
473
|
+
*/
|
|
474
|
+
onDrop(_dragElement) {
|
|
475
|
+
setTimeout(this.setLidOpen.bind(this, false), 100);
|
|
476
|
+
}
|
|
239
477
|
|
|
240
|
-
/**
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
478
|
+
/**
|
|
479
|
+
* Flip the lid open or shut.
|
|
480
|
+
* @param {boolean} state True if open.
|
|
481
|
+
* @package
|
|
482
|
+
*/
|
|
483
|
+
setLidOpen(state) {
|
|
484
|
+
if (this.isLidOpen === state) {
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
clearTimeout(this.lidTask_);
|
|
488
|
+
this.isLidOpen = state;
|
|
489
|
+
this.animateLid_();
|
|
490
|
+
}
|
|
246
491
|
|
|
247
|
-
/**
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
492
|
+
/**
|
|
493
|
+
* Rotate the lid open or closed by one step. Then wait and recurse.
|
|
494
|
+
* @private
|
|
495
|
+
*/
|
|
496
|
+
animateLid_() {
|
|
497
|
+
const frames = ANIMATION_FRAMES;
|
|
253
498
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
* @private
|
|
258
|
-
*/
|
|
259
|
-
Trashcan.prototype.top_ = 0;
|
|
499
|
+
const delta = 1 / (frames + 1);
|
|
500
|
+
this.lidOpen_ += this.isLidOpen ? delta : -delta;
|
|
501
|
+
this.lidOpen_ = Math.min(Math.max(this.lidOpen_, this.minOpenness_), 1);
|
|
260
502
|
|
|
261
|
-
|
|
262
|
-
* Whether this has been initialized.
|
|
263
|
-
* @type {boolean}
|
|
264
|
-
* @private
|
|
265
|
-
*/
|
|
266
|
-
Trashcan.prototype.initialized_ = false;
|
|
503
|
+
this.setLidAngle_(this.lidOpen_ * MAX_LID_ANGLE);
|
|
267
504
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
*/
|
|
272
|
-
Trashcan.prototype.createDom = function() {
|
|
273
|
-
/* Here's the markup that will be generated:
|
|
274
|
-
<g class="blocklyTrash">
|
|
275
|
-
<clippath id="blocklyTrashBodyClipPath837493">
|
|
276
|
-
<rect width="47" height="45" y="15"></rect>
|
|
277
|
-
</clippath>
|
|
278
|
-
<image width="64" height="92" y="-32" xlink:href="media/sprites.png"
|
|
279
|
-
clip-path="url(#blocklyTrashBodyClipPath837493)"></image>
|
|
280
|
-
<clippath id="blocklyTrashLidClipPath837493">
|
|
281
|
-
<rect width="47" height="15"></rect>
|
|
282
|
-
</clippath>
|
|
283
|
-
<image width="84" height="92" y="-32" xlink:href="media/sprites.png"
|
|
284
|
-
clip-path="url(#blocklyTrashLidClipPath837493)"></image>
|
|
285
|
-
</g>
|
|
286
|
-
*/
|
|
287
|
-
this.svgGroup_ = dom.createSvgElement(Svg.G, {'class': 'blocklyTrash'}, null);
|
|
288
|
-
let clip;
|
|
289
|
-
const rnd = String(Math.random()).substring(2);
|
|
290
|
-
clip = dom.createSvgElement(
|
|
291
|
-
Svg.CLIPPATH, {'id': 'blocklyTrashBodyClipPath' + rnd}, this.svgGroup_);
|
|
292
|
-
dom.createSvgElement(
|
|
293
|
-
Svg.RECT, {'width': WIDTH, 'height': BODY_HEIGHT, 'y': LID_HEIGHT}, clip);
|
|
294
|
-
const body = dom.createSvgElement(
|
|
295
|
-
Svg.IMAGE, {
|
|
296
|
-
'width': internalConstants.SPRITE.width,
|
|
297
|
-
'x': -SPRITE_LEFT,
|
|
298
|
-
'height': internalConstants.SPRITE.height,
|
|
299
|
-
'y': -SPRITE_TOP,
|
|
300
|
-
'clip-path': 'url(#blocklyTrashBodyClipPath' + rnd + ')',
|
|
301
|
-
},
|
|
302
|
-
this.svgGroup_);
|
|
303
|
-
body.setAttributeNS(
|
|
304
|
-
dom.XLINK_NS, 'xlink:href',
|
|
305
|
-
this.workspace_.options.pathToMedia + internalConstants.SPRITE.url);
|
|
306
|
-
|
|
307
|
-
clip = dom.createSvgElement(
|
|
308
|
-
Svg.CLIPPATH, {'id': 'blocklyTrashLidClipPath' + rnd}, this.svgGroup_);
|
|
309
|
-
dom.createSvgElement(Svg.RECT, {'width': WIDTH, 'height': LID_HEIGHT}, clip);
|
|
310
|
-
this.svgLid_ = dom.createSvgElement(
|
|
311
|
-
Svg.IMAGE, {
|
|
312
|
-
'width': internalConstants.SPRITE.width,
|
|
313
|
-
'x': -SPRITE_LEFT,
|
|
314
|
-
'height': internalConstants.SPRITE.height,
|
|
315
|
-
'y': -SPRITE_TOP,
|
|
316
|
-
'clip-path': 'url(#blocklyTrashLidClipPath' + rnd + ')',
|
|
317
|
-
},
|
|
318
|
-
this.svgGroup_);
|
|
319
|
-
this.svgLid_.setAttributeNS(
|
|
320
|
-
dom.XLINK_NS, 'xlink:href',
|
|
321
|
-
this.workspace_.options.pathToMedia + internalConstants.SPRITE.url);
|
|
322
|
-
|
|
323
|
-
// bindEventWithChecks_ quashes events too aggressively. See:
|
|
324
|
-
// https://groups.google.com/forum/#!topic/blockly/QF4yB9Wx00s
|
|
325
|
-
// Using bindEventWithChecks_ for blocking mousedown causes issue in mobile.
|
|
326
|
-
// See #4303
|
|
327
|
-
browserEvents.bind(
|
|
328
|
-
this.svgGroup_, 'mousedown', this, this.blockMouseDownWhenOpenable_);
|
|
329
|
-
browserEvents.bind(this.svgGroup_, 'mouseup', this, this.click);
|
|
330
|
-
// Bind to body instead of this.svgGroup_ so that we don't get lid jitters
|
|
331
|
-
browserEvents.bind(body, 'mouseover', this, this.mouseOver_);
|
|
332
|
-
browserEvents.bind(body, 'mouseout', this, this.mouseOut_);
|
|
333
|
-
this.animateLid_();
|
|
334
|
-
return this.svgGroup_;
|
|
335
|
-
};
|
|
505
|
+
// Linear interpolation between min and max.
|
|
506
|
+
const opacity = OPACITY_MIN + this.lidOpen_ * (OPACITY_MAX - OPACITY_MIN);
|
|
507
|
+
this.svgGroup_.style.opacity = opacity;
|
|
336
508
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
if (this.workspace_.options.maxTrashcanContents > 0) {
|
|
342
|
-
dom.insertAfter(
|
|
343
|
-
this.flyout.createDom(Svg.SVG), this.workspace_.getParentSvg());
|
|
344
|
-
this.flyout.init(this.workspace_);
|
|
509
|
+
if (this.lidOpen_ > this.minOpenness_ && this.lidOpen_ < 1) {
|
|
510
|
+
this.lidTask_ =
|
|
511
|
+
setTimeout(this.animateLid_.bind(this), ANIMATION_LENGTH / frames);
|
|
512
|
+
}
|
|
345
513
|
}
|
|
346
|
-
this.workspace_.getComponentManager().addComponent({
|
|
347
|
-
component: this,
|
|
348
|
-
weight: 1,
|
|
349
|
-
capabilities: [
|
|
350
|
-
ComponentManager.Capability.AUTOHIDEABLE,
|
|
351
|
-
ComponentManager.Capability.DELETE_AREA,
|
|
352
|
-
ComponentManager.Capability.DRAG_TARGET,
|
|
353
|
-
ComponentManager.Capability.POSITIONABLE,
|
|
354
|
-
],
|
|
355
|
-
});
|
|
356
|
-
this.initialized_ = true;
|
|
357
|
-
this.setLidOpen(false);
|
|
358
|
-
};
|
|
359
514
|
|
|
360
|
-
/**
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
this.
|
|
515
|
+
/**
|
|
516
|
+
* Set the angle of the trashcan's lid.
|
|
517
|
+
* @param {number} lidAngle The angle at which to set the lid.
|
|
518
|
+
* @private
|
|
519
|
+
*/
|
|
520
|
+
setLidAngle_(lidAngle) {
|
|
521
|
+
const openAtRight =
|
|
522
|
+
this.workspace_.toolboxPosition === toolbox.Position.RIGHT ||
|
|
523
|
+
(this.workspace_.horizontalLayout && this.workspace_.RTL);
|
|
524
|
+
this.svgLid_.setAttribute(
|
|
525
|
+
'transform',
|
|
526
|
+
'rotate(' + (openAtRight ? -lidAngle : lidAngle) + ',' +
|
|
527
|
+
(openAtRight ? 4 : WIDTH - 4) + ',' + (LID_HEIGHT - 2) + ')');
|
|
370
528
|
}
|
|
371
|
-
this.svgLid_ = null;
|
|
372
|
-
this.workspace_ = null;
|
|
373
|
-
clearTimeout(this.lidTask_);
|
|
374
|
-
};
|
|
375
|
-
|
|
376
|
-
/**
|
|
377
|
-
* Whether the trashcan has contents.
|
|
378
|
-
* @return {boolean} True if the trashcan has contents.
|
|
379
|
-
* @private
|
|
380
|
-
*/
|
|
381
|
-
Trashcan.prototype.hasContents_ = function() {
|
|
382
|
-
return !!this.contents_.length;
|
|
383
|
-
};
|
|
384
529
|
|
|
385
|
-
/**
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
if (this.contentsIsOpen()) {
|
|
398
|
-
return;
|
|
530
|
+
/**
|
|
531
|
+
* Sets the minimum openness of the trashcan lid. If the lid is currently
|
|
532
|
+
* closed, this will update lid's position.
|
|
533
|
+
* @param {number} newMin The new minimum openness of the lid. Should be
|
|
534
|
+
* between 0 and 1.
|
|
535
|
+
* @private
|
|
536
|
+
*/
|
|
537
|
+
setMinOpenness_(newMin) {
|
|
538
|
+
this.minOpenness_ = newMin;
|
|
539
|
+
if (!this.isLidOpen) {
|
|
540
|
+
this.setLidAngle_(newMin * MAX_LID_ANGLE);
|
|
541
|
+
}
|
|
399
542
|
}
|
|
400
|
-
const contents = this.contents_.map(function(string) {
|
|
401
|
-
return JSON.parse(string);
|
|
402
|
-
});
|
|
403
|
-
this.flyout.show(contents);
|
|
404
|
-
this.fireUiEvent_(true);
|
|
405
|
-
};
|
|
406
543
|
|
|
407
|
-
/**
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
544
|
+
/**
|
|
545
|
+
* Flip the lid shut.
|
|
546
|
+
* Called externally after a drag.
|
|
547
|
+
*/
|
|
548
|
+
closeLid() {
|
|
549
|
+
this.setLidOpen(false);
|
|
413
550
|
}
|
|
414
|
-
this.flyout.hide();
|
|
415
|
-
this.fireUiEvent_(false);
|
|
416
|
-
this.workspace_.recordDragTargets();
|
|
417
|
-
};
|
|
418
551
|
|
|
419
|
-
/**
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
if (!onlyClosePopups && this.flyout) {
|
|
428
|
-
this.closeFlyout();
|
|
552
|
+
/**
|
|
553
|
+
* Inspect the contents of the trash.
|
|
554
|
+
*/
|
|
555
|
+
click() {
|
|
556
|
+
if (!this.hasContents_()) {
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
this.openFlyout();
|
|
429
560
|
}
|
|
430
|
-
};
|
|
431
561
|
|
|
432
|
-
/**
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
562
|
+
/**
|
|
563
|
+
* Fires a UI event for trashcan flyout open or close.
|
|
564
|
+
* @param {boolean} trashcanOpen Whether the flyout is opening.
|
|
565
|
+
* @private
|
|
566
|
+
*/
|
|
567
|
+
fireUiEvent_(trashcanOpen) {
|
|
568
|
+
const uiEvent = new (eventUtils.get(eventUtils.TRASHCAN_OPEN))(
|
|
569
|
+
trashcanOpen, this.workspace_.id);
|
|
570
|
+
eventUtils.fire(uiEvent);
|
|
439
571
|
}
|
|
440
|
-
this.contents_.length = 0;
|
|
441
|
-
this.setMinOpenness_(0);
|
|
442
|
-
this.closeFlyout();
|
|
443
|
-
};
|
|
444
572
|
|
|
445
|
-
/**
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
// Not yet initialized.
|
|
455
|
-
if (!this.initialized_) {
|
|
456
|
-
return;
|
|
573
|
+
/**
|
|
574
|
+
* Prevents a workspace scroll and click event if the trashcan has blocks.
|
|
575
|
+
* @param {!Event} e A mouse down event.
|
|
576
|
+
* @private
|
|
577
|
+
*/
|
|
578
|
+
blockMouseDownWhenOpenable_(e) {
|
|
579
|
+
if (!this.contentsIsOpen() && this.hasContents_()) {
|
|
580
|
+
e.stopPropagation(); // Don't start a workspace scroll.
|
|
581
|
+
}
|
|
457
582
|
}
|
|
458
583
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
const bumpDirection = verticalPosition === uiPosition.verticalPosition.TOP ?
|
|
469
|
-
uiPosition.bumpDirection.DOWN :
|
|
470
|
-
uiPosition.bumpDirection.UP;
|
|
471
|
-
const positionRect = uiPosition.bumpPositionRect(
|
|
472
|
-
startRect, MARGIN_VERTICAL, bumpDirection, savedPositions);
|
|
473
|
-
|
|
474
|
-
this.top_ = positionRect.top;
|
|
475
|
-
this.left_ = positionRect.left;
|
|
476
|
-
this.svgGroup_.setAttribute(
|
|
477
|
-
'transform', 'translate(' + this.left_ + ',' + this.top_ + ')');
|
|
478
|
-
};
|
|
584
|
+
/**
|
|
585
|
+
* Indicate that the trashcan can be clicked (by opening it) if it has blocks.
|
|
586
|
+
* @private
|
|
587
|
+
*/
|
|
588
|
+
mouseOver_() {
|
|
589
|
+
if (this.hasContents_()) {
|
|
590
|
+
this.setLidOpen(true);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
479
593
|
|
|
480
|
-
/**
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
};
|
|
594
|
+
/**
|
|
595
|
+
* Close the lid of the trashcan if it was open (Vis. it was indicating it had
|
|
596
|
+
* blocks).
|
|
597
|
+
* @private
|
|
598
|
+
*/
|
|
599
|
+
mouseOut_() {
|
|
600
|
+
// No need to do a .hasBlocks check here because if it doesn't the trashcan
|
|
601
|
+
// won't be open in the first place, and setOpen won't run.
|
|
602
|
+
this.setLidOpen(false);
|
|
603
|
+
}
|
|
491
604
|
|
|
492
|
-
/**
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
605
|
+
/**
|
|
606
|
+
* Handle a BLOCK_DELETE event. Adds deleted blocks oldXml to the content
|
|
607
|
+
* array.
|
|
608
|
+
* @param {!Abstract} event Workspace event.
|
|
609
|
+
* @private
|
|
610
|
+
*/
|
|
611
|
+
onDelete_(event) {
|
|
612
|
+
if (this.workspace_.options.maxTrashcanContents <= 0 ||
|
|
613
|
+
event.type !== eventUtils.BLOCK_DELETE) {
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
const deleteEvent = /** @type {!BlockDelete} */ (event);
|
|
617
|
+
if (event.type === eventUtils.BLOCK_DELETE && !deleteEvent.wasShadow) {
|
|
618
|
+
const cleanedJson = this.cleanBlockJson_(deleteEvent.oldJson);
|
|
619
|
+
if (this.contents_.indexOf(cleanedJson) !== -1) {
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
this.contents_.unshift(cleanedJson);
|
|
623
|
+
while (this.contents_.length >
|
|
624
|
+
this.workspace_.options.maxTrashcanContents) {
|
|
625
|
+
this.contents_.pop();
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
this.setMinOpenness_(HAS_BLOCKS_LID_ANGLE);
|
|
629
|
+
}
|
|
501
630
|
}
|
|
502
631
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
632
|
+
/**
|
|
633
|
+
* Converts JSON representing a block into text that can be stored in the
|
|
634
|
+
* content array.
|
|
635
|
+
* @param {!blocks.State} json A JSON representation of
|
|
636
|
+
* a block's state.
|
|
637
|
+
* @return {string} Text representing the JSON, cleaned of all unnecessary
|
|
638
|
+
* attributes.
|
|
639
|
+
* @private
|
|
640
|
+
*/
|
|
641
|
+
cleanBlockJson_(json) {
|
|
642
|
+
// Create a deep copy.
|
|
643
|
+
json = /** @type {!blocks.State} */ (JSON.parse(JSON.stringify(json)));
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Reshape JSON into a nicer format.
|
|
647
|
+
* @param {!blocks.State} json The JSON to clean.
|
|
648
|
+
*/
|
|
649
|
+
function cleanRec(json) {
|
|
650
|
+
if (!json) {
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
delete json['id'];
|
|
655
|
+
delete json['x'];
|
|
656
|
+
delete json['y'];
|
|
657
|
+
delete json['enabled'];
|
|
658
|
+
|
|
659
|
+
if (json['icons'] && json['icons']['comment']) {
|
|
660
|
+
const comment = json['icons']['comment'];
|
|
661
|
+
delete comment['height'];
|
|
662
|
+
delete comment['width'];
|
|
663
|
+
delete comment['pinned'];
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const inputs = json['inputs'];
|
|
667
|
+
for (const name in inputs) {
|
|
668
|
+
const input = inputs[name];
|
|
669
|
+
cleanRec(input['block']);
|
|
670
|
+
cleanRec(input['shadow']);
|
|
671
|
+
}
|
|
672
|
+
if (json['next']) {
|
|
673
|
+
const next = json['next'];
|
|
674
|
+
cleanRec(next['block']);
|
|
675
|
+
cleanRec(next['shadow']);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
510
678
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
* @override
|
|
517
|
-
*/
|
|
518
|
-
Trashcan.prototype.onDragOver = function(_dragElement) {
|
|
519
|
-
this.setLidOpen(this.wouldDelete_);
|
|
520
|
-
};
|
|
679
|
+
cleanRec(json);
|
|
680
|
+
json['kind'] = 'BLOCK';
|
|
681
|
+
return JSON.stringify(json);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
521
684
|
|
|
522
685
|
/**
|
|
523
|
-
*
|
|
524
|
-
* @param {!IDraggable} _dragElement The block or bubble currently being
|
|
525
|
-
* dragged.
|
|
526
|
-
* @override
|
|
686
|
+
* Width of both the trash can and lid images.
|
|
527
687
|
*/
|
|
528
|
-
|
|
529
|
-
this.setLidOpen(false);
|
|
530
|
-
};
|
|
688
|
+
const WIDTH = 47;
|
|
531
689
|
|
|
532
690
|
/**
|
|
533
|
-
*
|
|
534
|
-
* Should not handle delete here.
|
|
535
|
-
* @param {!IDraggable} _dragElement The block or bubble currently being
|
|
536
|
-
* dragged.
|
|
537
|
-
* @override
|
|
691
|
+
* Height of the trashcan image (minus lid).
|
|
538
692
|
*/
|
|
539
|
-
|
|
540
|
-
setTimeout(this.setLidOpen.bind(this, false), 100);
|
|
541
|
-
};
|
|
693
|
+
const BODY_HEIGHT = 44;
|
|
542
694
|
|
|
543
695
|
/**
|
|
544
|
-
*
|
|
545
|
-
* @param {boolean} state True if open.
|
|
546
|
-
* @package
|
|
696
|
+
* Height of the lid image.
|
|
547
697
|
*/
|
|
548
|
-
|
|
549
|
-
if (this.isLidOpen === state) {
|
|
550
|
-
return;
|
|
551
|
-
}
|
|
552
|
-
clearTimeout(this.lidTask_);
|
|
553
|
-
this.isLidOpen = state;
|
|
554
|
-
this.animateLid_();
|
|
555
|
-
};
|
|
698
|
+
const LID_HEIGHT = 16;
|
|
556
699
|
|
|
557
700
|
/**
|
|
558
|
-
*
|
|
559
|
-
* @private
|
|
701
|
+
* Distance between trashcan and bottom or top edge of workspace.
|
|
560
702
|
*/
|
|
561
|
-
|
|
562
|
-
const frames = ANIMATION_FRAMES;
|
|
563
|
-
|
|
564
|
-
const delta = 1 / (frames + 1);
|
|
565
|
-
this.lidOpen_ += this.isLidOpen ? delta : -delta;
|
|
566
|
-
this.lidOpen_ = Math.min(Math.max(this.lidOpen_, this.minOpenness_), 1);
|
|
567
|
-
|
|
568
|
-
this.setLidAngle_(this.lidOpen_ * MAX_LID_ANGLE);
|
|
569
|
-
|
|
570
|
-
// Linear interpolation between min and max.
|
|
571
|
-
const opacity = OPACITY_MIN + this.lidOpen_ * (OPACITY_MAX - OPACITY_MIN);
|
|
572
|
-
this.svgGroup_.style.opacity = opacity;
|
|
573
|
-
|
|
574
|
-
if (this.lidOpen_ > this.minOpenness_ && this.lidOpen_ < 1) {
|
|
575
|
-
this.lidTask_ =
|
|
576
|
-
setTimeout(this.animateLid_.bind(this), ANIMATION_LENGTH / frames);
|
|
577
|
-
}
|
|
578
|
-
};
|
|
703
|
+
const MARGIN_VERTICAL = 20;
|
|
579
704
|
|
|
580
705
|
/**
|
|
581
|
-
*
|
|
582
|
-
* @param {number} lidAngle The angle at which to set the lid.
|
|
583
|
-
* @private
|
|
706
|
+
* Distance between trashcan and right or left edge of workspace.
|
|
584
707
|
*/
|
|
585
|
-
|
|
586
|
-
const openAtRight =
|
|
587
|
-
this.workspace_.toolboxPosition === toolbox.Position.RIGHT ||
|
|
588
|
-
(this.workspace_.horizontalLayout && this.workspace_.RTL);
|
|
589
|
-
this.svgLid_.setAttribute(
|
|
590
|
-
'transform',
|
|
591
|
-
'rotate(' + (openAtRight ? -lidAngle : lidAngle) + ',' +
|
|
592
|
-
(openAtRight ? 4 : WIDTH - 4) + ',' + (LID_HEIGHT - 2) + ')');
|
|
593
|
-
};
|
|
708
|
+
const MARGIN_HORIZONTAL = 20;
|
|
594
709
|
|
|
595
710
|
/**
|
|
596
|
-
*
|
|
597
|
-
* closed, this will update lid's position.
|
|
598
|
-
* @param {number} newMin The new minimum openness of the lid. Should be between
|
|
599
|
-
* 0 and 1.
|
|
600
|
-
* @private
|
|
711
|
+
* Extent of hotspot on all sides beyond the size of the image.
|
|
601
712
|
*/
|
|
602
|
-
|
|
603
|
-
this.minOpenness_ = newMin;
|
|
604
|
-
if (!this.isLidOpen) {
|
|
605
|
-
this.setLidAngle_(newMin * MAX_LID_ANGLE);
|
|
606
|
-
}
|
|
607
|
-
};
|
|
713
|
+
const MARGIN_HOTSPOT = 10;
|
|
608
714
|
|
|
609
715
|
/**
|
|
610
|
-
*
|
|
611
|
-
* Called externally after a drag.
|
|
716
|
+
* Location of trashcan in sprite image.
|
|
612
717
|
*/
|
|
613
|
-
|
|
614
|
-
this.setLidOpen(false);
|
|
615
|
-
};
|
|
718
|
+
const SPRITE_LEFT = 0;
|
|
616
719
|
|
|
617
720
|
/**
|
|
618
|
-
*
|
|
721
|
+
* Location of trashcan in sprite image.
|
|
619
722
|
*/
|
|
620
|
-
|
|
621
|
-
if (!this.hasContents_()) {
|
|
622
|
-
return;
|
|
623
|
-
}
|
|
624
|
-
this.openFlyout();
|
|
625
|
-
};
|
|
723
|
+
const SPRITE_TOP = 32;
|
|
626
724
|
|
|
627
725
|
/**
|
|
628
|
-
*
|
|
629
|
-
*
|
|
630
|
-
* @private
|
|
726
|
+
* The openness of the lid when the trashcan contains blocks.
|
|
727
|
+
* (0.0 = closed, 1.0 = open)
|
|
631
728
|
*/
|
|
632
|
-
|
|
633
|
-
const uiEvent = new (eventUtils.get(eventUtils.TRASHCAN_OPEN))(
|
|
634
|
-
trashcanOpen, this.workspace_.id);
|
|
635
|
-
eventUtils.fire(uiEvent);
|
|
636
|
-
};
|
|
729
|
+
const HAS_BLOCKS_LID_ANGLE = 0.1;
|
|
637
730
|
|
|
638
731
|
/**
|
|
639
|
-
*
|
|
640
|
-
* @param {!Event} e A mouse down event.
|
|
641
|
-
* @private
|
|
732
|
+
* The length of the lid open/close animation in milliseconds.
|
|
642
733
|
*/
|
|
643
|
-
|
|
644
|
-
if (!this.contentsIsOpen() && this.hasContents_()) {
|
|
645
|
-
e.stopPropagation(); // Don't start a workspace scroll.
|
|
646
|
-
}
|
|
647
|
-
};
|
|
734
|
+
const ANIMATION_LENGTH = 80;
|
|
648
735
|
|
|
649
736
|
/**
|
|
650
|
-
*
|
|
651
|
-
* @private
|
|
737
|
+
* The number of frames in the animation.
|
|
652
738
|
*/
|
|
653
|
-
|
|
654
|
-
if (this.hasContents_()) {
|
|
655
|
-
this.setLidOpen(true);
|
|
656
|
-
}
|
|
657
|
-
};
|
|
739
|
+
const ANIMATION_FRAMES = 4;
|
|
658
740
|
|
|
659
741
|
/**
|
|
660
|
-
*
|
|
661
|
-
* blocks).
|
|
662
|
-
* @private
|
|
742
|
+
* The minimum (resting) opacity of the trashcan and lid.
|
|
663
743
|
*/
|
|
664
|
-
|
|
665
|
-
// No need to do a .hasBlocks check here because if it doesn't the trashcan
|
|
666
|
-
// won't be open in the first place, and setOpen won't run.
|
|
667
|
-
this.setLidOpen(false);
|
|
668
|
-
};
|
|
744
|
+
const OPACITY_MIN = 0.4;
|
|
669
745
|
|
|
670
746
|
/**
|
|
671
|
-
*
|
|
672
|
-
* @param {!Abstract} event Workspace event.
|
|
673
|
-
* @private
|
|
747
|
+
* The maximum (hovered) opacity of the trashcan and lid.
|
|
674
748
|
*/
|
|
675
|
-
|
|
676
|
-
if (this.workspace_.options.maxTrashcanContents <= 0) {
|
|
677
|
-
return;
|
|
678
|
-
}
|
|
679
|
-
if (event.type === eventUtils.BLOCK_DELETE && !event.wasShadow) {
|
|
680
|
-
const cleanedJson = this.cleanBlockJson_(event.oldJson);
|
|
681
|
-
if (this.contents_.indexOf(cleanedJson) !== -1) {
|
|
682
|
-
return;
|
|
683
|
-
}
|
|
684
|
-
this.contents_.unshift(cleanedJson);
|
|
685
|
-
while (this.contents_.length >
|
|
686
|
-
this.workspace_.options.maxTrashcanContents) {
|
|
687
|
-
this.contents_.pop();
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
this.setMinOpenness_(HAS_BLOCKS_LID_ANGLE);
|
|
691
|
-
}
|
|
692
|
-
};
|
|
749
|
+
const OPACITY_MAX = 0.8;
|
|
693
750
|
|
|
694
751
|
/**
|
|
695
|
-
*
|
|
696
|
-
*
|
|
697
|
-
* @param {!blocks.State} json A JSON representation of
|
|
698
|
-
* a block's state.
|
|
699
|
-
* @return {string} Text representing the JSON, cleaned of all unnecessary
|
|
700
|
-
* attributes.
|
|
701
|
-
* @private
|
|
752
|
+
* The maximum angle the trashcan lid can opens to. At the end of the open
|
|
753
|
+
* animation the lid will be open to this angle.
|
|
702
754
|
*/
|
|
703
|
-
|
|
704
|
-
// Create a deep copy.
|
|
705
|
-
json = /** @type {!blocks.State} */ (JSON.parse(JSON.stringify(json)));
|
|
706
|
-
|
|
707
|
-
/**
|
|
708
|
-
* Reshape JSON into a nicer format.
|
|
709
|
-
* @param {!blocks.State} json The JSON to clean.
|
|
710
|
-
*/
|
|
711
|
-
function cleanRec(json) {
|
|
712
|
-
if (!json) {
|
|
713
|
-
return;
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
delete json['id'];
|
|
717
|
-
delete json['x'];
|
|
718
|
-
delete json['y'];
|
|
719
|
-
delete json['enabled'];
|
|
720
|
-
|
|
721
|
-
if (json['icons'] && json['icons']['comment']) {
|
|
722
|
-
const comment = json['icons']['comment'];
|
|
723
|
-
delete comment['height'];
|
|
724
|
-
delete comment['width'];
|
|
725
|
-
delete comment['pinned'];
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
const inputs = json['inputs'];
|
|
729
|
-
for (const name in inputs) {
|
|
730
|
-
const input = inputs[name];
|
|
731
|
-
cleanRec(input['block']);
|
|
732
|
-
cleanRec(input['shadow']);
|
|
733
|
-
}
|
|
734
|
-
if (json['next']) {
|
|
735
|
-
const next = json['next'];
|
|
736
|
-
cleanRec(next['block']);
|
|
737
|
-
cleanRec(next['shadow']);
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
cleanRec(json);
|
|
742
|
-
json['kind'] = 'BLOCK';
|
|
743
|
-
return JSON.stringify(json);
|
|
744
|
-
};
|
|
755
|
+
const MAX_LID_ANGLE = 45;
|
|
745
756
|
|
|
746
757
|
exports.Trashcan = Trashcan;
|