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.
Files changed (231) hide show
  1. package/blockly.d.ts +18963 -18432
  2. package/blockly.min.js +5 -4
  3. package/blockly_compressed.js +4 -3
  4. package/blockly_compressed.js.map +1 -1
  5. package/blocks/blocks.js +47 -0
  6. package/blocks/colour.js +13 -3
  7. package/blocks/lists.js +22 -13
  8. package/blocks/logic.js +13 -3
  9. package/blocks/loops.js +24 -11
  10. package/blocks/math.js +12 -3
  11. package/blocks/procedures.js +45 -32
  12. package/blocks/text.js +22 -13
  13. package/blocks/variables.js +14 -3
  14. package/blocks/variables_dynamic.js +13 -3
  15. package/blocks_compressed.js +1 -1
  16. package/blocks_compressed.js.map +1 -1
  17. package/core/block.js +1869 -1814
  18. package/core/block_drag_surface.js +201 -200
  19. package/core/block_dragger.js +377 -373
  20. package/core/block_svg.js +1593 -1479
  21. package/core/blockly.js +8 -22
  22. package/core/blocks.js +9 -2
  23. package/core/browser_events.js +22 -5
  24. package/core/bubble.js +841 -797
  25. package/core/bubble_dragger.js +213 -206
  26. package/core/bump_objects.js +2 -2
  27. package/core/clipboard.js +9 -9
  28. package/core/comment.js +353 -332
  29. package/core/common.js +46 -17
  30. package/core/component_manager.js +181 -174
  31. package/core/config.js +87 -0
  32. package/core/connection.js +595 -584
  33. package/core/connection_checker.js +242 -244
  34. package/core/connection_db.js +235 -230
  35. package/core/contextmenu.js +9 -6
  36. package/core/contextmenu_items.js +1 -2
  37. package/core/contextmenu_registry.js +93 -89
  38. package/core/css.js +474 -474
  39. package/core/delete_area.js +45 -42
  40. package/core/drag_target.js +57 -56
  41. package/core/dropdowndiv.js +153 -163
  42. package/core/events/events.js +2 -2
  43. package/core/events/events_abstract.js +89 -77
  44. package/core/events/events_block_base.js +37 -36
  45. package/core/events/events_block_change.js +130 -124
  46. package/core/events/events_block_create.js +73 -71
  47. package/core/events/events_block_delete.js +84 -82
  48. package/core/events/events_block_drag.js +50 -49
  49. package/core/events/events_block_move.js +147 -140
  50. package/core/events/events_bubble_open.js +51 -50
  51. package/core/events/events_click.js +48 -44
  52. package/core/events/events_comment_base.js +72 -69
  53. package/core/events/events_comment_change.js +63 -61
  54. package/core/events/events_comment_create.js +44 -42
  55. package/core/events/events_comment_delete.js +42 -40
  56. package/core/events/events_comment_move.js +106 -104
  57. package/core/events/events_marker_move.js +65 -64
  58. package/core/events/events_selected.js +46 -45
  59. package/core/events/events_theme_change.js +36 -35
  60. package/core/events/events_toolbox_item_select.js +46 -45
  61. package/core/events/events_trashcan_open.js +37 -36
  62. package/core/events/events_ui.js +47 -46
  63. package/core/events/events_ui_base.js +30 -29
  64. package/core/events/events_var_base.js +37 -36
  65. package/core/events/events_var_create.js +50 -48
  66. package/core/events/events_var_delete.js +50 -48
  67. package/core/events/events_var_rename.js +51 -49
  68. package/core/events/events_viewport.js +66 -65
  69. package/core/events/utils.js +29 -14
  70. package/core/events/workspace_events.js +49 -55
  71. package/core/extensions.js +4 -3
  72. package/core/field.js +1061 -997
  73. package/core/field_angle.js +462 -442
  74. package/core/field_checkbox.js +194 -182
  75. package/core/field_colour.js +519 -505
  76. package/core/field_dropdown.js +617 -598
  77. package/core/field_image.js +229 -220
  78. package/core/field_label.js +102 -91
  79. package/core/field_label_serializable.js +42 -41
  80. package/core/field_multilineinput.js +372 -358
  81. package/core/field_number.js +272 -253
  82. package/core/field_textinput.js +499 -467
  83. package/core/field_variable.js +458 -420
  84. package/core/flyout_base.js +1005 -952
  85. package/core/flyout_button.js +277 -260
  86. package/core/flyout_horizontal.js +304 -302
  87. package/core/flyout_metrics_manager.js +64 -64
  88. package/core/flyout_vertical.js +306 -300
  89. package/core/generator.js +459 -446
  90. package/core/gesture.js +829 -813
  91. package/core/grid.js +166 -163
  92. package/core/icon.js +168 -159
  93. package/core/inject.js +7 -5
  94. package/core/input.js +257 -248
  95. package/core/insertion_marker_manager.js +655 -624
  96. package/core/internal_constants.js +0 -129
  97. package/core/keyboard_nav/ast_node.js +605 -596
  98. package/core/keyboard_nav/basic_cursor.js +166 -165
  99. package/core/keyboard_nav/cursor.js +99 -97
  100. package/core/keyboard_nav/marker.js +83 -79
  101. package/core/keyboard_nav/tab_navigate_cursor.js +18 -23
  102. package/core/marker_manager.js +153 -141
  103. package/core/menu.js +377 -372
  104. package/core/menuitem.js +223 -217
  105. package/core/metrics_manager.js +403 -390
  106. package/core/mutator.js +468 -437
  107. package/core/names.js +229 -188
  108. package/core/options.js +290 -284
  109. package/core/procedures.js +29 -17
  110. package/core/registry.js +19 -16
  111. package/core/rendered_connection.js +482 -463
  112. package/core/renderers/common/block_rendering.js +9 -3
  113. package/core/renderers/common/constants.js +1119 -1112
  114. package/core/renderers/common/debug.js +14 -0
  115. package/core/renderers/common/debugger.js +338 -316
  116. package/core/renderers/common/drawer.js +380 -370
  117. package/core/renderers/common/i_path_object.js +2 -2
  118. package/core/renderers/common/info.js +626 -618
  119. package/core/renderers/common/marker_svg.js +579 -541
  120. package/core/renderers/common/path_object.js +203 -200
  121. package/core/renderers/common/renderer.js +220 -218
  122. package/core/renderers/geras/constants.js +36 -36
  123. package/core/renderers/geras/drawer.js +155 -147
  124. package/core/renderers/geras/highlight_constants.js +244 -238
  125. package/core/renderers/geras/highlighter.js +231 -179
  126. package/core/renderers/geras/info.js +392 -369
  127. package/core/renderers/geras/measurables/inline_input.js +25 -19
  128. package/core/renderers/geras/measurables/statement_input.js +23 -17
  129. package/core/renderers/geras/path_object.js +106 -121
  130. package/core/renderers/geras/renderer.js +96 -98
  131. package/core/renderers/measurables/base.js +30 -18
  132. package/core/renderers/measurables/bottom_row.js +83 -80
  133. package/core/renderers/measurables/connection.js +22 -15
  134. package/core/renderers/measurables/external_value_input.js +35 -22
  135. package/core/renderers/measurables/field.js +35 -20
  136. package/core/renderers/measurables/hat.js +18 -13
  137. package/core/renderers/measurables/icon.js +24 -17
  138. package/core/renderers/measurables/in_row_spacer.js +15 -13
  139. package/core/renderers/measurables/inline_input.js +43 -33
  140. package/core/renderers/measurables/input_connection.js +41 -28
  141. package/core/renderers/measurables/input_row.js +50 -44
  142. package/core/renderers/measurables/jagged_edge.js +14 -12
  143. package/core/renderers/measurables/next_connection.js +16 -14
  144. package/core/renderers/measurables/output_connection.js +26 -20
  145. package/core/renderers/measurables/previous_connection.js +16 -15
  146. package/core/renderers/measurables/round_corner.js +20 -18
  147. package/core/renderers/measurables/row.js +184 -168
  148. package/core/renderers/measurables/spacer_row.js +38 -23
  149. package/core/renderers/measurables/square_corner.js +18 -16
  150. package/core/renderers/measurables/statement_input.js +23 -20
  151. package/core/renderers/measurables/top_row.js +88 -85
  152. package/core/renderers/minimalist/constants.js +8 -7
  153. package/core/renderers/minimalist/drawer.js +11 -10
  154. package/core/renderers/minimalist/info.js +18 -18
  155. package/core/renderers/minimalist/renderer.js +40 -39
  156. package/core/renderers/thrasos/info.js +258 -248
  157. package/core/renderers/thrasos/renderer.js +20 -20
  158. package/core/renderers/zelos/constants.js +898 -873
  159. package/core/renderers/zelos/drawer.js +186 -169
  160. package/core/renderers/zelos/info.js +502 -479
  161. package/core/renderers/zelos/marker_svg.js +129 -115
  162. package/core/renderers/zelos/measurables/bottom_row.js +31 -30
  163. package/core/renderers/zelos/measurables/inputs.js +22 -21
  164. package/core/renderers/zelos/measurables/row_elements.js +14 -13
  165. package/core/renderers/zelos/measurables/top_row.js +34 -33
  166. package/core/renderers/zelos/path_object.js +181 -180
  167. package/core/renderers/zelos/renderer.js +91 -92
  168. package/core/scrollbar.js +759 -713
  169. package/core/scrollbar_pair.js +250 -245
  170. package/core/serialization/blocks.js +26 -10
  171. package/core/serialization/workspaces.js +3 -2
  172. package/core/shortcut_registry.js +286 -277
  173. package/core/sprites.js +31 -0
  174. package/core/theme.js +135 -141
  175. package/core/theme_manager.js +147 -143
  176. package/core/toolbox/category.js +602 -576
  177. package/core/toolbox/collapsible_category.js +226 -227
  178. package/core/toolbox/separator.js +70 -61
  179. package/core/toolbox/toolbox.js +934 -927
  180. package/core/toolbox/toolbox_item.js +115 -99
  181. package/core/tooltip.js +108 -35
  182. package/core/touch.js +8 -3
  183. package/core/touch_gesture.js +254 -251
  184. package/core/trashcan.js +606 -595
  185. package/core/utils/coordinate.js +97 -95
  186. package/core/utils/dom.js +2 -2
  187. package/core/utils/global.js +2 -0
  188. package/core/utils/rect.js +41 -37
  189. package/core/utils/sentinel.js +25 -0
  190. package/core/utils/size.js +30 -27
  191. package/core/utils/svg.js +18 -16
  192. package/core/variable_map.js +325 -341
  193. package/core/variable_model.js +55 -54
  194. package/core/variables.js +9 -2
  195. package/core/variables_dynamic.js +3 -1
  196. package/core/warning.js +126 -120
  197. package/core/widgetdiv.js +4 -4
  198. package/core/workspace.js +685 -664
  199. package/core/workspace_audio.js +124 -118
  200. package/core/workspace_comment.js +308 -298
  201. package/core/workspace_comment_svg.js +1029 -951
  202. package/core/workspace_drag_surface_svg.js +147 -140
  203. package/core/workspace_dragger.js +70 -71
  204. package/core/workspace_svg.js +2322 -2297
  205. package/core/xml.js +30 -20
  206. package/core/zoom_controls.js +431 -439
  207. package/generators/dart/colour.js +56 -64
  208. package/generators/dart/lists.js +61 -50
  209. package/generators/dart/math.js +160 -148
  210. package/generators/dart/text.js +83 -61
  211. package/generators/javascript/colour.js +37 -34
  212. package/generators/javascript/lists.js +50 -43
  213. package/generators/javascript/math.js +123 -139
  214. package/generators/javascript/text.js +67 -81
  215. package/generators/lua/colour.js +25 -23
  216. package/generators/lua/lists.js +97 -69
  217. package/generators/lua/logic.js +1 -2
  218. package/generators/lua/math.js +182 -144
  219. package/generators/lua/text.js +116 -99
  220. package/generators/php/colour.js +38 -32
  221. package/generators/php/lists.js +109 -89
  222. package/generators/php/math.js +90 -81
  223. package/generators/php/text.js +63 -61
  224. package/generators/python/colour.js +18 -18
  225. package/generators/python/lists.js +38 -30
  226. package/generators/python/loops.js +12 -8
  227. package/generators/python/math.js +104 -106
  228. package/generators/python/text.js +34 -30
  229. package/msg/smn.js +436 -0
  230. package/package.json +7 -6
  231. package/blocks/all.js +0 -23
@@ -22,10 +22,11 @@ const browserEvents = goog.require('Blockly.browserEvents');
22
22
  const common = goog.require('Blockly.common');
23
23
  const dom = goog.require('Blockly.utils.dom');
24
24
  const eventUtils = goog.require('Blockly.Events.utils');
25
- const object = goog.require('Blockly.utils.object');
26
25
  const svgMath = goog.require('Blockly.utils.svgMath');
27
26
  /* eslint-disable-next-line no-unused-vars */
28
27
  const {BlockDragSurfaceSvg} = goog.requireType('Blockly.BlockDragSurfaceSvg');
28
+ /* eslint-disable-next-line no-unused-vars */
29
+ const {CommentMove} = goog.require('Blockly.Events.CommentMove');
29
30
  const {Coordinate} = goog.require('Blockly.utils.Coordinate');
30
31
  /* eslint-disable-next-line no-unused-vars */
31
32
  const {IBoundedElement} = goog.require('Blockly.IBoundedElement');
@@ -43,8 +44,6 @@ goog.require('Blockly.Events.CommentCreate');
43
44
  /** @suppress {extraRequire} */
44
45
  goog.require('Blockly.Events.CommentDelete');
45
46
  /** @suppress {extraRequire} */
46
- goog.require('Blockly.Events.CommentMove');
47
- /** @suppress {extraRequire} */
48
47
  goog.require('Blockly.Events.Selected');
49
48
 
50
49
 
@@ -71,1076 +70,1155 @@ const TEXTAREA_OFFSET = 2;
71
70
 
72
71
  /**
73
72
  * Class for a workspace comment's SVG representation.
74
- * @param {!WorkspaceSvg} workspace The block's workspace.
75
- * @param {string} content The content of this workspace comment.
76
- * @param {number} height Height of the comment.
77
- * @param {number} width Width of the comment.
78
- * @param {string=} opt_id Optional ID. Use this ID if provided, otherwise
79
- * create a new ID.
80
73
  * @extends {WorkspaceComment}
81
74
  * @implements {IBoundedElement}
82
75
  * @implements {IBubble}
83
76
  * @implements {ICopyable}
84
- * @constructor
85
77
  * @alias Blockly.WorkspaceCommentSvg
86
78
  */
87
- const WorkspaceCommentSvg = function(
88
- workspace, content, height, width, opt_id) {
79
+ class WorkspaceCommentSvg extends WorkspaceComment {
89
80
  /**
90
- * @type {!WorkspaceSvg}
81
+ * @param {!WorkspaceSvg} workspace The block's workspace.
82
+ * @param {string} content The content of this workspace comment.
83
+ * @param {number} height Height of the comment.
84
+ * @param {number} width Width of the comment.
85
+ * @param {string=} opt_id Optional ID. Use this ID if provided, otherwise
86
+ * create a new ID.
91
87
  */
92
- this.workspace;
88
+ constructor(workspace, content, height, width, opt_id) {
89
+ super(workspace, content, height, width, opt_id);
90
+ /**
91
+ * @type {!WorkspaceSvg}
92
+ */
93
+ this.workspace;
94
+
95
+ /**
96
+ * Mouse up event data.
97
+ * @type {?browserEvents.Data}
98
+ * @private
99
+ */
100
+ this.onMouseUpWrapper_ = null;
93
101
 
94
- /**
95
- * Mouse up event data.
96
- * @type {?browserEvents.Data}
97
- * @private
98
- */
99
- this.onMouseUpWrapper_ = null;
102
+ /**
103
+ * Mouse move event data.
104
+ * @type {?browserEvents.Data}
105
+ * @private
106
+ */
107
+ this.onMouseMoveWrapper_ = null;
108
+
109
+ /**
110
+ * Whether event handlers have been initialized.
111
+ * @type {boolean}
112
+ * @private
113
+ */
114
+ this.eventsInit_ = false;
115
+
116
+ /**
117
+ * @type {?Element}
118
+ * @private
119
+ */
120
+ this.textarea_ = null;
121
+
122
+ /**
123
+ * @type {?SVGRectElement}
124
+ * @private
125
+ */
126
+ this.svgRectTarget_ = null;
127
+
128
+ /**
129
+ * @type {?SVGRectElement}
130
+ * @private
131
+ */
132
+ this.svgHandleTarget_ = null;
133
+
134
+ /**
135
+ * @type {?SVGForeignObjectElement}
136
+ * @private
137
+ */
138
+ this.foreignObject_ = null;
139
+
140
+ /**
141
+ * @type {?SVGGElement}
142
+ * @private
143
+ */
144
+ this.resizeGroup_ = null;
145
+
146
+ /**
147
+ * @type {?SVGGElement}
148
+ * @private
149
+ */
150
+ this.deleteGroup_ = null;
151
+
152
+ /**
153
+ * @type {?SVGCircleElement}
154
+ * @private
155
+ */
156
+ this.deleteIconBorder_ = null;
157
+
158
+ /**
159
+ * @type {boolean}
160
+ * @private
161
+ */
162
+ this.focused_ = false;
163
+
164
+ /**
165
+ * @type {boolean}
166
+ * @private
167
+ */
168
+ this.autoLayout_ = false;
169
+
170
+ // Create core elements for the block.
171
+ /**
172
+ * @type {!SVGElement}
173
+ * @private
174
+ */
175
+ this.svgGroup_ =
176
+ dom.createSvgElement(Svg.G, {'class': 'blocklyComment'}, null);
177
+ this.svgGroup_.translate_ = '';
178
+
179
+ this.svgRect_ = dom.createSvgElement(Svg.RECT, {
180
+ 'class': 'blocklyCommentRect',
181
+ 'x': 0,
182
+ 'y': 0,
183
+ 'rx': BORDER_RADIUS,
184
+ 'ry': BORDER_RADIUS,
185
+ });
186
+ this.svgGroup_.appendChild(this.svgRect_);
187
+
188
+ /**
189
+ * Whether the comment is rendered onscreen and is a part of the DOM.
190
+ * @type {boolean}
191
+ * @private
192
+ */
193
+ this.rendered_ = false;
194
+
195
+ /**
196
+ * Whether to move the comment to the drag surface when it is dragged.
197
+ * True if it should move, false if it should be translated directly.
198
+ * @type {boolean}
199
+ * @private
200
+ */
201
+ this.useDragSurface_ =
202
+ svgMath.is3dSupported() && !!workspace.getBlockDragSurface();
203
+
204
+ this.render();
205
+ }
100
206
 
101
207
  /**
102
- * Mouse move event data.
103
- * @type {?browserEvents.Data}
104
- * @private
208
+ * Dispose of this comment.
209
+ * @package
105
210
  */
106
- this.onMouseMoveWrapper_ = null;
211
+ dispose() {
212
+ if (this.disposed_) {
213
+ return;
214
+ }
215
+ // If this comment is being dragged, unlink the mouse events.
216
+ if (common.getSelected() === this) {
217
+ this.unselect();
218
+ this.workspace.cancelCurrentGesture();
219
+ }
220
+
221
+ if (eventUtils.isEnabled()) {
222
+ eventUtils.fire(new (eventUtils.get(eventUtils.COMMENT_DELETE))(this));
223
+ }
224
+
225
+ dom.removeNode(this.svgGroup_);
226
+ // Dispose of any rendered components
227
+ this.disposeInternal_();
228
+
229
+ eventUtils.disable();
230
+ super.dispose();
231
+ eventUtils.enable();
232
+ }
107
233
 
108
- // Create core elements for the block.
109
234
  /**
110
- * @type {!SVGElement}
111
- * @private
235
+ * Create and initialize the SVG representation of a workspace comment.
236
+ * May be called more than once.
237
+ *
238
+ * @param {boolean=} opt_noSelect Text inside text area will be selected if
239
+ * false
240
+ *
241
+ * @package
112
242
  */
113
- this.svgGroup_ =
114
- dom.createSvgElement(Svg.G, {'class': 'blocklyComment'}, null);
115
- this.svgGroup_.translate_ = '';
116
-
117
- this.svgRect_ = dom.createSvgElement(Svg.RECT, {
118
- 'class': 'blocklyCommentRect',
119
- 'x': 0,
120
- 'y': 0,
121
- 'rx': BORDER_RADIUS,
122
- 'ry': BORDER_RADIUS,
123
- });
124
- this.svgGroup_.appendChild(this.svgRect_);
243
+ initSvg(opt_noSelect) {
244
+ if (!this.workspace.rendered) {
245
+ throw TypeError('Workspace is headless.');
246
+ }
247
+ if (!this.workspace.options.readOnly && !this.eventsInit_) {
248
+ browserEvents.conditionalBind(
249
+ /** @type {!SVGRectElement} */ (this.svgRectTarget_), 'mousedown',
250
+ this, this.pathMouseDown_);
251
+ browserEvents.conditionalBind(
252
+ /** @type {!SVGRectElement} */ (this.svgHandleTarget_), 'mousedown',
253
+ this, this.pathMouseDown_);
254
+ }
255
+ this.eventsInit_ = true;
256
+
257
+ this.updateMovable();
258
+ if (!this.getSvgRoot().parentNode) {
259
+ this.workspace.getBubbleCanvas().appendChild(this.getSvgRoot());
260
+ }
261
+
262
+ if (!opt_noSelect && this.textarea_) {
263
+ this.textarea_.select();
264
+ }
265
+ }
125
266
 
126
267
  /**
127
- * Whether the comment is rendered onscreen and is a part of the DOM.
128
- * @type {boolean}
268
+ * Handle a mouse-down on an SVG comment.
269
+ * @param {!Event} e Mouse down event or touch start event.
129
270
  * @private
130
271
  */
131
- this.rendered_ = false;
272
+ pathMouseDown_(e) {
273
+ const gesture = this.workspace.getGesture(e);
274
+ if (gesture) {
275
+ gesture.handleBubbleStart(e, this);
276
+ }
277
+ }
132
278
 
133
279
  /**
134
- * Whether to move the comment to the drag surface when it is dragged.
135
- * True if it should move, false if it should be translated directly.
136
- * @type {boolean}
137
- * @private
280
+ * Show the context menu for this workspace comment.
281
+ * @param {!Event} e Mouse event.
282
+ * @package
138
283
  */
139
- this.useDragSurface_ =
140
- svgMath.is3dSupported() && !!workspace.getBlockDragSurface();
141
-
142
- WorkspaceCommentSvg.superClass_.constructor.call(
143
- this, workspace, content, height, width, opt_id);
144
-
145
- this.render();
146
- };
147
- object.inherits(WorkspaceCommentSvg, WorkspaceComment);
148
-
149
- /**
150
- * The width and height to use to size a workspace comment when it is first
151
- * added, before it has been edited by the user.
152
- * @type {number}
153
- * @package
154
- */
155
- WorkspaceCommentSvg.DEFAULT_SIZE = 100;
284
+ showContextMenu(e) {
285
+ if (this.workspace.options.readOnly) {
286
+ return;
287
+ }
288
+ // Save the current workspace comment in a variable for use in closures.
289
+ const comment = this;
290
+ const menuOptions = [];
156
291
 
157
- /**
158
- * Offset from the top to make room for a top bar.
159
- * @type {number}
160
- * @const
161
- * @private
162
- */
163
- WorkspaceCommentSvg.TOP_OFFSET = 10;
292
+ if (this.isDeletable() && this.isMovable()) {
293
+ menuOptions.push(ContextMenu.commentDuplicateOption(comment));
294
+ menuOptions.push(ContextMenu.commentDeleteOption(comment));
295
+ }
164
296
 
165
- /**
166
- * Dispose of this comment.
167
- * @package
168
- */
169
- WorkspaceCommentSvg.prototype.dispose = function() {
170
- if (this.disposed_) {
171
- return;
172
- }
173
- // If this comment is being dragged, unlink the mouse events.
174
- if (common.getSelected() === this) {
175
- this.unselect();
176
- this.workspace.cancelCurrentGesture();
297
+ ContextMenu.show(e, menuOptions, this.RTL);
177
298
  }
178
299
 
179
- if (eventUtils.isEnabled()) {
180
- eventUtils.fire(new (eventUtils.get(eventUtils.COMMENT_DELETE))(this));
300
+ /**
301
+ * Select this comment. Highlight it visually.
302
+ * @package
303
+ */
304
+ select() {
305
+ if (common.getSelected() === this) {
306
+ return;
307
+ }
308
+ let oldId = null;
309
+ if (common.getSelected()) {
310
+ oldId = common.getSelected().id;
311
+ // Unselect any previously selected block.
312
+ eventUtils.disable();
313
+ try {
314
+ common.getSelected().unselect();
315
+ } finally {
316
+ eventUtils.enable();
317
+ }
318
+ }
319
+ const event = new (eventUtils.get(eventUtils.SELECTED))(
320
+ oldId, this.id, this.workspace.id);
321
+ eventUtils.fire(event);
322
+ common.setSelected(this);
323
+ this.addSelect();
181
324
  }
182
325
 
183
- dom.removeNode(this.svgGroup_);
184
- // Dispose of any rendered components
185
- this.disposeInternal_();
186
-
187
- eventUtils.disable();
188
- WorkspaceCommentSvg.superClass_.dispose.call(this);
189
- eventUtils.enable();
190
- };
191
-
192
- /**
193
- * Create and initialize the SVG representation of a workspace comment.
194
- * May be called more than once.
195
- *
196
- * @param {boolean=} opt_noSelect Text inside text area will be selected if
197
- * false
198
- *
199
- * @package
200
- */
201
- WorkspaceCommentSvg.prototype.initSvg = function(opt_noSelect) {
202
- if (!this.workspace.rendered) {
203
- throw TypeError('Workspace is headless.');
204
- }
205
- if (!this.workspace.options.readOnly && !this.eventsInit_) {
206
- browserEvents.conditionalBind(
207
- this.svgRectTarget_, 'mousedown', this, this.pathMouseDown_);
208
- browserEvents.conditionalBind(
209
- this.svgHandleTarget_, 'mousedown', this, this.pathMouseDown_);
326
+ /**
327
+ * Unselect this comment. Remove its highlighting.
328
+ * @package
329
+ */
330
+ unselect() {
331
+ if (common.getSelected() !== this) {
332
+ return;
333
+ }
334
+ const event = new (eventUtils.get(eventUtils.SELECTED))(
335
+ this.id, null, this.workspace.id);
336
+ eventUtils.fire(event);
337
+ common.setSelected(null);
338
+ this.removeSelect();
339
+ this.blurFocus();
210
340
  }
211
- this.eventsInit_ = true;
212
341
 
213
- this.updateMovable();
214
- if (!this.getSvgRoot().parentNode) {
215
- this.workspace.getBubbleCanvas().appendChild(this.getSvgRoot());
342
+ /**
343
+ * Select this comment. Highlight it visually.
344
+ * @package
345
+ */
346
+ addSelect() {
347
+ dom.addClass(
348
+ /** @type {!Element} */ (this.svgGroup_), 'blocklySelected');
349
+ this.setFocus();
216
350
  }
217
351
 
218
- if (!opt_noSelect && this.textarea_) {
219
- this.textarea_.select();
352
+ /**
353
+ * Unselect this comment. Remove its highlighting.
354
+ * @package
355
+ */
356
+ removeSelect() {
357
+ dom.removeClass(
358
+ /** @type {!Element} */ (this.svgGroup_), 'blocklySelected');
359
+ this.blurFocus();
220
360
  }
221
- };
222
361
 
223
- /**
224
- * Handle a mouse-down on an SVG comment.
225
- * @param {!Event} e Mouse down event or touch start event.
226
- * @private
227
- */
228
- WorkspaceCommentSvg.prototype.pathMouseDown_ = function(e) {
229
- const gesture = this.workspace.getGesture(e);
230
- if (gesture) {
231
- gesture.handleBubbleStart(e, this);
362
+ /**
363
+ * Focus this comment. Highlight it visually.
364
+ * @package
365
+ */
366
+ addFocus() {
367
+ dom.addClass(
368
+ /** @type {!Element} */ (this.svgGroup_), 'blocklyFocused');
232
369
  }
233
- };
234
370
 
235
- /**
236
- * Show the context menu for this workspace comment.
237
- * @param {!Event} e Mouse event.
238
- * @package
239
- */
240
- WorkspaceCommentSvg.prototype.showContextMenu = function(e) {
241
- if (this.workspace.options.readOnly) {
242
- return;
371
+ /**
372
+ * Unfocus this comment. Remove its highlighting.
373
+ * @package
374
+ */
375
+ removeFocus() {
376
+ dom.removeClass(
377
+ /** @type {!Element} */ (this.svgGroup_), 'blocklyFocused');
243
378
  }
244
- // Save the current workspace comment in a variable for use in closures.
245
- const comment = this;
246
- const menuOptions = [];
247
379
 
248
- if (this.isDeletable() && this.isMovable()) {
249
- menuOptions.push(ContextMenu.commentDuplicateOption(comment));
250
- menuOptions.push(ContextMenu.commentDeleteOption(comment));
380
+ /**
381
+ * Return the coordinates of the top-left corner of this comment relative to
382
+ * the drawing surface's origin (0,0), in workspace units.
383
+ * If the comment is on the workspace, (0, 0) is the origin of the workspace
384
+ * coordinate system.
385
+ * This does not change with workspace scale.
386
+ * @return {!Coordinate} Object with .x and .y properties in
387
+ * workspace coordinates.
388
+ * @package
389
+ */
390
+ getRelativeToSurfaceXY() {
391
+ let x = 0;
392
+ let y = 0;
393
+
394
+ const dragSurfaceGroup = this.useDragSurface_ ?
395
+ this.workspace.getBlockDragSurface().getGroup() :
396
+ null;
397
+
398
+ let element = this.getSvgRoot();
399
+ if (element) {
400
+ do {
401
+ // Loop through this comment and every parent.
402
+ const xy = svgMath.getRelativeXY(/** @type {!Element} */ (element));
403
+ x += xy.x;
404
+ y += xy.y;
405
+ // If this element is the current element on the drag surface, include
406
+ // the translation of the drag surface itself.
407
+ if (this.useDragSurface_ &&
408
+ this.workspace.getBlockDragSurface().getCurrentBlock() ===
409
+ element) {
410
+ const surfaceTranslation =
411
+ this.workspace.getBlockDragSurface().getSurfaceTranslation();
412
+ x += surfaceTranslation.x;
413
+ y += surfaceTranslation.y;
414
+ }
415
+ element = element.parentNode;
416
+ } while (element && element !== this.workspace.getBubbleCanvas() &&
417
+ element !== dragSurfaceGroup);
418
+ }
419
+ this.xy_ = new Coordinate(x, y);
420
+ return this.xy_;
251
421
  }
252
422
 
253
- ContextMenu.show(e, menuOptions, this.RTL);
254
- };
423
+ /**
424
+ * Move a comment by a relative offset.
425
+ * @param {number} dx Horizontal offset, in workspace units.
426
+ * @param {number} dy Vertical offset, in workspace units.
427
+ * @package
428
+ */
429
+ moveBy(dx, dy) {
430
+ const event = /** @type {!CommentMove} */ (
431
+ new (eventUtils.get(eventUtils.COMMENT_MOVE))(this));
432
+ // TODO: Do I need to look up the relative to surface XY position here?
433
+ const xy = this.getRelativeToSurfaceXY();
434
+ this.translate(xy.x + dx, xy.y + dy);
435
+ this.xy_ = new Coordinate(xy.x + dx, xy.y + dy);
436
+ event.recordNew();
437
+ eventUtils.fire(event);
438
+ this.workspace.resizeContents();
439
+ }
255
440
 
256
- /**
257
- * Select this comment. Highlight it visually.
258
- * @package
259
- */
260
- WorkspaceCommentSvg.prototype.select = function() {
261
- if (common.getSelected() === this) {
262
- return;
441
+ /**
442
+ * Transforms a comment by setting the translation on the transform attribute
443
+ * of the block's SVG.
444
+ * @param {number} x The x coordinate of the translation in workspace units.
445
+ * @param {number} y The y coordinate of the translation in workspace units.
446
+ * @package
447
+ */
448
+ translate(x, y) {
449
+ this.xy_ = new Coordinate(x, y);
450
+ this.getSvgRoot().setAttribute(
451
+ 'transform', 'translate(' + x + ',' + y + ')');
263
452
  }
264
- let oldId = null;
265
- if (common.getSelected()) {
266
- oldId = common.getSelected().id;
267
- // Unselect any previously selected block.
268
- eventUtils.disable();
269
- try {
270
- common.getSelected().unselect();
271
- } finally {
272
- eventUtils.enable();
453
+
454
+ /**
455
+ * Move this comment to its workspace's drag surface, accounting for
456
+ * positioning. Generally should be called at the same time as
457
+ * setDragging(true). Does nothing if useDragSurface_ is false.
458
+ * @package
459
+ */
460
+ moveToDragSurface() {
461
+ if (!this.useDragSurface_) {
462
+ return;
273
463
  }
464
+ // The translation for drag surface blocks,
465
+ // is equal to the current relative-to-surface position,
466
+ // to keep the position in sync as it move on/off the surface.
467
+ // This is in workspace coordinates.
468
+ const xy = this.getRelativeToSurfaceXY();
469
+ this.clearTransformAttributes_();
470
+ this.workspace.getBlockDragSurface().translateSurface(xy.x, xy.y);
471
+ // Execute the move on the top-level SVG component
472
+ this.workspace.getBlockDragSurface().setBlocksAndShow(this.getSvgRoot());
274
473
  }
275
- const event = new (eventUtils.get(eventUtils.SELECTED))(
276
- oldId, this.id, this.workspace.id);
277
- eventUtils.fire(event);
278
- common.setSelected(this);
279
- this.addSelect();
280
- };
281
474
 
282
- /**
283
- * Unselect this comment. Remove its highlighting.
284
- * @package
285
- */
286
- WorkspaceCommentSvg.prototype.unselect = function() {
287
- if (common.getSelected() !== this) {
288
- return;
475
+ /**
476
+ * Move this comment during a drag, taking into account whether we are using a
477
+ * drag surface to translate blocks.
478
+ * @param {BlockDragSurfaceSvg} dragSurface The surface that carries
479
+ * rendered items during a drag, or null if no drag surface is in use.
480
+ * @param {!Coordinate} newLoc The location to translate to, in
481
+ * workspace coordinates.
482
+ * @package
483
+ */
484
+ moveDuringDrag(dragSurface, newLoc) {
485
+ if (dragSurface) {
486
+ dragSurface.translateSurface(newLoc.x, newLoc.y);
487
+ } else {
488
+ this.svgGroup_.translate_ =
489
+ 'translate(' + newLoc.x + ',' + newLoc.y + ')';
490
+ this.svgGroup_.setAttribute(
491
+ 'transform', this.svgGroup_.translate_ + this.svgGroup_.skew_);
492
+ }
289
493
  }
290
- const event = new (eventUtils.get(eventUtils.SELECTED))(
291
- this.id, null, this.workspace.id);
292
- eventUtils.fire(event);
293
- common.setSelected(null);
294
- this.removeSelect();
295
- this.blurFocus();
296
- };
297
494
 
298
- /**
299
- * Select this comment. Highlight it visually.
300
- * @package
301
- */
302
- WorkspaceCommentSvg.prototype.addSelect = function() {
303
- dom.addClass(
304
- /** @type {!Element} */ (this.svgGroup_), 'blocklySelected');
305
- this.setFocus();
306
- };
307
-
308
- /**
309
- * Unselect this comment. Remove its highlighting.
310
- * @package
311
- */
312
- WorkspaceCommentSvg.prototype.removeSelect = function() {
313
- dom.removeClass(
314
- /** @type {!Element} */ (this.svgGroup_), 'blocklySelected');
315
- this.blurFocus();
316
- };
317
-
318
- /**
319
- * Focus this comment. Highlight it visually.
320
- * @package
321
- */
322
- WorkspaceCommentSvg.prototype.addFocus = function() {
323
- dom.addClass(
324
- /** @type {!Element} */ (this.svgGroup_), 'blocklyFocused');
325
- };
326
-
327
- /**
328
- * Unfocus this comment. Remove its highlighting.
329
- * @package
330
- */
331
- WorkspaceCommentSvg.prototype.removeFocus = function() {
332
- dom.removeClass(
333
- /** @type {!Element} */ (this.svgGroup_), 'blocklyFocused');
334
- };
335
-
336
- /**
337
- * Return the coordinates of the top-left corner of this comment relative to
338
- * the drawing surface's origin (0,0), in workspace units.
339
- * If the comment is on the workspace, (0, 0) is the origin of the workspace
340
- * coordinate system.
341
- * This does not change with workspace scale.
342
- * @return {!Coordinate} Object with .x and .y properties in
343
- * workspace coordinates.
344
- * @package
345
- */
346
- WorkspaceCommentSvg.prototype.getRelativeToSurfaceXY = function() {
347
- let x = 0;
348
- let y = 0;
349
-
350
- const dragSurfaceGroup = this.useDragSurface_ ?
351
- this.workspace.getBlockDragSurface().getGroup() :
352
- null;
353
-
354
- let element = this.getSvgRoot();
355
- if (element) {
356
- do {
357
- // Loop through this comment and every parent.
358
- const xy = svgMath.getRelativeXY(/** @type {!Element} */ (element));
359
- x += xy.x;
360
- y += xy.y;
361
- // If this element is the current element on the drag surface, include
362
- // the translation of the drag surface itself.
363
- if (this.useDragSurface_ &&
364
- this.workspace.getBlockDragSurface().getCurrentBlock() === element) {
365
- const surfaceTranslation =
366
- this.workspace.getBlockDragSurface().getSurfaceTranslation();
367
- x += surfaceTranslation.x;
368
- y += surfaceTranslation.y;
369
- }
370
- element = element.parentNode;
371
- } while (element && element !== this.workspace.getBubbleCanvas() &&
372
- element !== dragSurfaceGroup);
495
+ /**
496
+ * Move the bubble group to the specified location in workspace coordinates.
497
+ * @param {number} x The x position to move to.
498
+ * @param {number} y The y position to move to.
499
+ * @package
500
+ */
501
+ moveTo(x, y) {
502
+ this.translate(x, y);
373
503
  }
374
- this.xy_ = new Coordinate(x, y);
375
- return this.xy_;
376
- };
377
-
378
- /**
379
- * Move a comment by a relative offset.
380
- * @param {number} dx Horizontal offset, in workspace units.
381
- * @param {number} dy Vertical offset, in workspace units.
382
- * @package
383
- */
384
- WorkspaceCommentSvg.prototype.moveBy = function(dx, dy) {
385
- const event = new (eventUtils.get(eventUtils.COMMENT_MOVE))(this);
386
- // TODO: Do I need to look up the relative to surface XY position here?
387
- const xy = this.getRelativeToSurfaceXY();
388
- this.translate(xy.x + dx, xy.y + dy);
389
- this.xy_ = new Coordinate(xy.x + dx, xy.y + dy);
390
- event.recordNew();
391
- eventUtils.fire(event);
392
- this.workspace.resizeContents();
393
- };
394
504
 
395
- /**
396
- * Transforms a comment by setting the translation on the transform attribute
397
- * of the block's SVG.
398
- * @param {number} x The x coordinate of the translation in workspace units.
399
- * @param {number} y The y coordinate of the translation in workspace units.
400
- * @package
401
- */
402
- WorkspaceCommentSvg.prototype.translate = function(x, y) {
403
- this.xy_ = new Coordinate(x, y);
404
- this.getSvgRoot().setAttribute('transform', 'translate(' + x + ',' + y + ')');
405
- };
406
-
407
- /**
408
- * Move this comment to its workspace's drag surface, accounting for
409
- * positioning. Generally should be called at the same time as
410
- * setDragging(true). Does nothing if useDragSurface_ is false.
411
- * @package
412
- */
413
- WorkspaceCommentSvg.prototype.moveToDragSurface = function() {
414
- if (!this.useDragSurface_) {
415
- return;
505
+ /**
506
+ * Clear the comment of transform="..." attributes.
507
+ * Used when the comment is switching from 3d to 2d transform or vice versa.
508
+ * @private
509
+ */
510
+ clearTransformAttributes_() {
511
+ this.getSvgRoot().removeAttribute('transform');
416
512
  }
417
- // The translation for drag surface blocks,
418
- // is equal to the current relative-to-surface position,
419
- // to keep the position in sync as it move on/off the surface.
420
- // This is in workspace coordinates.
421
- const xy = this.getRelativeToSurfaceXY();
422
- this.clearTransformAttributes_();
423
- this.workspace.getBlockDragSurface().translateSurface(xy.x, xy.y);
424
- // Execute the move on the top-level SVG component
425
- this.workspace.getBlockDragSurface().setBlocksAndShow(this.getSvgRoot());
426
- };
427
513
 
428
- /**
429
- * Move this comment during a drag, taking into account whether we are using a
430
- * drag surface to translate blocks.
431
- * @param {BlockDragSurfaceSvg} dragSurface The surface that carries
432
- * rendered items during a drag, or null if no drag surface is in use.
433
- * @param {!Coordinate} newLoc The location to translate to, in
434
- * workspace coordinates.
435
- * @package
436
- */
437
- WorkspaceCommentSvg.prototype.moveDuringDrag = function(dragSurface, newLoc) {
438
- if (dragSurface) {
439
- dragSurface.translateSurface(newLoc.x, newLoc.y);
440
- } else {
441
- this.svgGroup_.translate_ = 'translate(' + newLoc.x + ',' + newLoc.y + ')';
442
- this.svgGroup_.setAttribute(
443
- 'transform', this.svgGroup_.translate_ + this.svgGroup_.skew_);
514
+ /**
515
+ * Returns the coordinates of a bounding box describing the dimensions of this
516
+ * comment.
517
+ * Coordinate system: workspace coordinates.
518
+ * @return {!Rect} Object with coordinates of the bounding box.
519
+ * @package
520
+ */
521
+ getBoundingRectangle() {
522
+ const blockXY = this.getRelativeToSurfaceXY();
523
+ const commentBounds = this.getHeightWidth();
524
+ const top = blockXY.y;
525
+ const bottom = blockXY.y + commentBounds.height;
526
+ let left;
527
+ let right;
528
+ if (this.RTL) {
529
+ left = blockXY.x - commentBounds.width;
530
+ // Add the width of the tab/puzzle piece knob to the x coordinate
531
+ // since X is the corner of the rectangle, not the whole puzzle piece.
532
+ right = blockXY.x;
533
+ } else {
534
+ // Subtract the width of the tab/puzzle piece knob to the x coordinate
535
+ // since X is the corner of the rectangle, not the whole puzzle piece.
536
+ left = blockXY.x;
537
+ right = blockXY.x + commentBounds.width;
538
+ }
539
+ return new Rect(top, bottom, left, right);
444
540
  }
445
- };
446
-
447
- /**
448
- * Move the bubble group to the specified location in workspace coordinates.
449
- * @param {number} x The x position to move to.
450
- * @param {number} y The y position to move to.
451
- * @package
452
- */
453
- WorkspaceCommentSvg.prototype.moveTo = function(x, y) {
454
- this.translate(x, y);
455
- };
456
541
 
457
- /**
458
- * Clear the comment of transform="..." attributes.
459
- * Used when the comment is switching from 3d to 2d transform or vice versa.
460
- * @private
461
- */
462
- WorkspaceCommentSvg.prototype.clearTransformAttributes_ = function() {
463
- this.getSvgRoot().removeAttribute('transform');
464
- };
465
-
466
- /**
467
- * Returns the coordinates of a bounding box describing the dimensions of this
468
- * comment.
469
- * Coordinate system: workspace coordinates.
470
- * @return {!Rect} Object with coordinates of the bounding box.
471
- * @package
472
- */
473
- WorkspaceCommentSvg.prototype.getBoundingRectangle = function() {
474
- const blockXY = this.getRelativeToSurfaceXY();
475
- const commentBounds = this.getHeightWidth();
476
- const top = blockXY.y;
477
- const bottom = blockXY.y + commentBounds.height;
478
- let left;
479
- let right;
480
- if (this.RTL) {
481
- left = blockXY.x - commentBounds.width;
482
- // Add the width of the tab/puzzle piece knob to the x coordinate
483
- // since X is the corner of the rectangle, not the whole puzzle piece.
484
- right = blockXY.x;
485
- } else {
486
- // Subtract the width of the tab/puzzle piece knob to the x coordinate
487
- // since X is the corner of the rectangle, not the whole puzzle piece.
488
- left = blockXY.x;
489
- right = blockXY.x + commentBounds.width;
542
+ /**
543
+ * Add or remove the UI indicating if this comment is movable or not.
544
+ * @package
545
+ */
546
+ updateMovable() {
547
+ if (this.isMovable()) {
548
+ dom.addClass(
549
+ /** @type {!Element} */ (this.svgGroup_), 'blocklyDraggable');
550
+ } else {
551
+ dom.removeClass(
552
+ /** @type {!Element} */ (this.svgGroup_), 'blocklyDraggable');
553
+ }
490
554
  }
491
- return new Rect(top, bottom, left, right);
492
- };
493
555
 
494
- /**
495
- * Add or remove the UI indicating if this comment is movable or not.
496
- * @package
497
- */
498
- WorkspaceCommentSvg.prototype.updateMovable = function() {
499
- if (this.isMovable()) {
500
- dom.addClass(
501
- /** @type {!Element} */ (this.svgGroup_), 'blocklyDraggable');
502
- } else {
503
- dom.removeClass(
504
- /** @type {!Element} */ (this.svgGroup_), 'blocklyDraggable');
556
+ /**
557
+ * Set whether this comment is movable or not.
558
+ * @param {boolean} movable True if movable.
559
+ * @package
560
+ */
561
+ setMovable(movable) {
562
+ super.setMovable(movable);
563
+ this.updateMovable();
505
564
  }
506
- };
507
565
 
508
- /**
509
- * Set whether this comment is movable or not.
510
- * @param {boolean} movable True if movable.
511
- * @package
512
- */
513
- WorkspaceCommentSvg.prototype.setMovable = function(movable) {
514
- WorkspaceCommentSvg.superClass_.setMovable.call(this, movable);
515
- this.updateMovable();
516
- };
517
-
518
- /**
519
- * Set whether this comment is editable or not.
520
- * @param {boolean} editable True if editable.
521
- */
522
- WorkspaceCommentSvg.prototype.setEditable = function(editable) {
523
- WorkspaceCommentSvg.superClass_.setEditable.call(this, editable);
524
- if (this.textarea_) {
525
- this.textarea_.readOnly = !editable;
566
+ /**
567
+ * Set whether this comment is editable or not.
568
+ * @param {boolean} editable True if editable.
569
+ */
570
+ setEditable(editable) {
571
+ super.setEditable(editable);
572
+ if (this.textarea_) {
573
+ this.textarea_.readOnly = !editable;
574
+ }
526
575
  }
527
- };
528
576
 
529
- /**
530
- * Recursively adds or removes the dragging class to this node and its children.
531
- * @param {boolean} adding True if adding, false if removing.
532
- * @package
533
- */
534
- WorkspaceCommentSvg.prototype.setDragging = function(adding) {
535
- if (adding) {
536
- const group = this.getSvgRoot();
537
- group.translate_ = '';
538
- group.skew_ = '';
539
- dom.addClass(
540
- /** @type {!Element} */ (this.svgGroup_), 'blocklyDragging');
541
- } else {
542
- dom.removeClass(
543
- /** @type {!Element} */ (this.svgGroup_), 'blocklyDragging');
577
+ /**
578
+ * Recursively adds or removes the dragging class to this node and its
579
+ * children.
580
+ * @param {boolean} adding True if adding, false if removing.
581
+ * @package
582
+ */
583
+ setDragging(adding) {
584
+ if (adding) {
585
+ const group = this.getSvgRoot();
586
+ group.translate_ = '';
587
+ group.skew_ = '';
588
+ dom.addClass(
589
+ /** @type {!Element} */ (this.svgGroup_), 'blocklyDragging');
590
+ } else {
591
+ dom.removeClass(
592
+ /** @type {!Element} */ (this.svgGroup_), 'blocklyDragging');
593
+ }
544
594
  }
545
- };
546
595
 
547
- /**
548
- * Return the root node of the SVG or null if none exists.
549
- * @return {!SVGElement} The root SVG node (probably a group).
550
- * @package
551
- */
552
- WorkspaceCommentSvg.prototype.getSvgRoot = function() {
553
- return this.svgGroup_;
554
- };
596
+ /**
597
+ * Return the root node of the SVG or null if none exists.
598
+ * @return {!SVGElement} The root SVG node (probably a group).
599
+ * @package
600
+ */
601
+ getSvgRoot() {
602
+ return this.svgGroup_;
603
+ }
555
604
 
556
- /**
557
- * Returns this comment's text.
558
- * @return {string} Comment text.
559
- * @package
560
- */
561
- WorkspaceCommentSvg.prototype.getContent = function() {
562
- return this.textarea_ ? this.textarea_.value : this.content_;
563
- };
605
+ /**
606
+ * Returns this comment's text.
607
+ * @return {string} Comment text.
608
+ * @package
609
+ */
610
+ getContent() {
611
+ return this.textarea_ ? this.textarea_.value : this.content_;
612
+ }
564
613
 
565
- /**
566
- * Set this comment's content.
567
- * @param {string} content Comment content.
568
- * @package
569
- */
570
- WorkspaceCommentSvg.prototype.setContent = function(content) {
571
- WorkspaceCommentSvg.superClass_.setContent.call(this, content);
572
- if (this.textarea_) {
573
- this.textarea_.value = content;
614
+ /**
615
+ * Set this comment's content.
616
+ * @param {string} content Comment content.
617
+ * @package
618
+ */
619
+ setContent(content) {
620
+ super.setContent(content);
621
+ if (this.textarea_) {
622
+ this.textarea_.value = content;
623
+ }
574
624
  }
575
- };
576
625
 
577
- /**
578
- * Update the cursor over this comment by adding or removing a class.
579
- * @param {boolean} enable True if the delete cursor should be shown, false
580
- * otherwise.
581
- * @package
582
- */
583
- WorkspaceCommentSvg.prototype.setDeleteStyle = function(enable) {
584
- if (enable) {
585
- dom.addClass(
586
- /** @type {!Element} */ (this.svgGroup_), 'blocklyDraggingDelete');
587
- } else {
588
- dom.removeClass(
589
- /** @type {!Element} */ (this.svgGroup_), 'blocklyDraggingDelete');
626
+ /**
627
+ * Update the cursor over this comment by adding or removing a class.
628
+ * @param {boolean} enable True if the delete cursor should be shown, false
629
+ * otherwise.
630
+ * @package
631
+ */
632
+ setDeleteStyle(enable) {
633
+ if (enable) {
634
+ dom.addClass(
635
+ /** @type {!Element} */ (this.svgGroup_), 'blocklyDraggingDelete');
636
+ } else {
637
+ dom.removeClass(
638
+ /** @type {!Element} */ (this.svgGroup_), 'blocklyDraggingDelete');
639
+ }
590
640
  }
591
- };
592
641
 
593
- /**
594
- * Set whether auto-layout of this bubble is enabled. The first time a bubble
595
- * is shown it positions itself to not cover any blocks. Once a user has
596
- * dragged it to reposition, it renders where the user put it.
597
- * @param {boolean} _enable True if auto-layout should be enabled, false
598
- * otherwise.
599
- * @package
600
- */
601
- WorkspaceCommentSvg.prototype.setAutoLayout = function(_enable) {
602
- // NOP for compatibility with the bubble dragger.
603
- };
642
+ /**
643
+ * Set whether auto-layout of this bubble is enabled. The first time a bubble
644
+ * is shown it positions itself to not cover any blocks. Once a user has
645
+ * dragged it to reposition, it renders where the user put it.
646
+ * @param {boolean} _enable True if auto-layout should be enabled, false
647
+ * otherwise.
648
+ * @package
649
+ */
650
+ setAutoLayout(_enable) {
651
+ // NOP for compatibility with the bubble dragger.
652
+ }
604
653
 
605
- /**
606
- * Decode an XML comment tag and create a rendered comment on the workspace.
607
- * @param {!Element} xmlComment XML comment element.
608
- * @param {!WorkspaceSvg} workspace The workspace.
609
- * @param {number=} opt_wsWidth The width of the workspace, which is used to
610
- * position comments correctly in RTL.
611
- * @return {!WorkspaceCommentSvg} The created workspace comment.
612
- * @package
613
- */
614
- WorkspaceCommentSvg.fromXml = function(xmlComment, workspace, opt_wsWidth) {
615
- eventUtils.disable();
616
- let comment;
617
- try {
618
- const info = WorkspaceComment.parseAttributes(xmlComment);
619
-
620
- comment = new WorkspaceCommentSvg(
621
- workspace, info.content, info.h, info.w, info.id);
622
- if (workspace.rendered) {
623
- comment.initSvg(true);
624
- comment.render();
625
- }
626
- // Position the comment correctly, taking into account the width of a
627
- // rendered RTL workspace.
628
- if (!isNaN(info.x) && !isNaN(info.y)) {
629
- if (workspace.RTL) {
630
- const wsWidth = opt_wsWidth || workspace.getWidth();
631
- comment.moveBy(wsWidth - info.x, info.y);
632
- } else {
633
- comment.moveBy(info.x, info.y);
634
- }
654
+ /**
655
+ * Encode a comment subtree as XML with XY coordinates.
656
+ * @param {boolean=} opt_noId True if the encoder should skip the comment ID.
657
+ * @return {!Element} Tree of XML elements.
658
+ * @package
659
+ */
660
+ toXmlWithXY(opt_noId) {
661
+ let width; // Not used in LTR.
662
+ if (this.workspace.RTL) {
663
+ // Here be performance dragons: This calls getMetrics().
664
+ width = this.workspace.getWidth();
635
665
  }
636
- } finally {
637
- eventUtils.enable();
666
+ const element = this.toXml(opt_noId);
667
+ const xy = this.getRelativeToSurfaceXY();
668
+ element.setAttribute(
669
+ 'x', Math.round(this.workspace.RTL ? width - xy.x : xy.x));
670
+ element.setAttribute('y', Math.round(xy.y));
671
+ element.setAttribute('h', this.getHeight());
672
+ element.setAttribute('w', this.getWidth());
673
+ return element;
638
674
  }
639
675
 
640
- WorkspaceComment.fireCreateEvent(
641
- /** @type {!WorkspaceCommentSvg} */ (comment));
642
- return (/** @type {!WorkspaceCommentSvg} */ (comment));
643
- };
644
-
645
- /**
646
- * Encode a comment subtree as XML with XY coordinates.
647
- * @param {boolean=} opt_noId True if the encoder should skip the comment ID.
648
- * @return {!Element} Tree of XML elements.
649
- * @package
650
- */
651
- WorkspaceCommentSvg.prototype.toXmlWithXY = function(opt_noId) {
652
- let width; // Not used in LTR.
653
- if (this.workspace.RTL) {
654
- // Here be performance dragons: This calls getMetrics().
655
- width = this.workspace.getWidth();
676
+ /**
677
+ * Encode a comment for copying.
678
+ * @return {!ICopyable.CopyData} Copy metadata.
679
+ * @package
680
+ */
681
+ toCopyData() {
682
+ return {
683
+ saveInfo: this.toXmlWithXY(),
684
+ source: this.workspace,
685
+ typeCounts: null,
686
+ };
656
687
  }
657
- const element = this.toXml(opt_noId);
658
- const xy = this.getRelativeToSurfaceXY();
659
- element.setAttribute(
660
- 'x', Math.round(this.workspace.RTL ? width - xy.x : xy.x));
661
- element.setAttribute('y', Math.round(xy.y));
662
- element.setAttribute('h', this.getHeight());
663
- element.setAttribute('w', this.getWidth());
664
- return element;
665
- };
666
688
 
667
- /**
668
- * Encode a comment for copying.
669
- * @return {!ICopyable.CopyData} Copy metadata.
670
- * @package
671
- */
672
- WorkspaceCommentSvg.prototype.toCopyData = function() {
673
- return {
674
- saveInfo: this.toXmlWithXY(),
675
- source: this.workspace,
676
- typeCounts: null,
677
- };
678
- };
689
+ /**
690
+ * Returns a bounding box describing the dimensions of this comment.
691
+ * @return {!{height: number, width: number}} Object with height and width
692
+ * properties in workspace units.
693
+ * @package
694
+ */
695
+ getHeightWidth() {
696
+ return {width: this.getWidth(), height: this.getHeight()};
697
+ }
679
698
 
680
- /**
681
- * Returns a bounding box describing the dimensions of this comment.
682
- * @return {!{height: number, width: number}} Object with height and width
683
- * properties in workspace units.
684
- * @package
685
- */
686
- WorkspaceCommentSvg.prototype.getHeightWidth = function() {
687
- return {width: this.getWidth(), height: this.getHeight()};
688
- };
699
+ /**
700
+ * Renders the workspace comment.
701
+ * @package
702
+ */
703
+ render() {
704
+ if (this.rendered_) {
705
+ return;
706
+ }
689
707
 
690
- /**
691
- * Renders the workspace comment.
692
- * @package
693
- */
694
- WorkspaceCommentSvg.prototype.render = function() {
695
- if (this.rendered_) {
696
- return;
697
- }
708
+ const size = this.getHeightWidth();
709
+
710
+ // Add text area
711
+ this.createEditor_();
712
+ this.svgGroup_.appendChild(this.foreignObject_);
713
+
714
+ this.svgHandleTarget_ = dom.createSvgElement(
715
+ Svg.RECT, {'class': 'blocklyCommentHandleTarget', 'x': 0, 'y': 0});
716
+ this.svgGroup_.appendChild(this.svgHandleTarget_);
717
+ this.svgRectTarget_ = dom.createSvgElement(Svg.RECT, {
718
+ 'class': 'blocklyCommentTarget',
719
+ 'x': 0,
720
+ 'y': 0,
721
+ 'rx': BORDER_RADIUS,
722
+ 'ry': BORDER_RADIUS,
723
+ });
724
+ this.svgGroup_.appendChild(this.svgRectTarget_);
725
+
726
+ // Add the resize icon
727
+ this.addResizeDom_();
728
+ if (this.isDeletable()) {
729
+ // Add the delete icon
730
+ this.addDeleteDom_();
731
+ }
698
732
 
699
- const size = this.getHeightWidth();
700
-
701
- // Add text area
702
- this.createEditor_();
703
- this.svgGroup_.appendChild(this.foreignObject_);
704
-
705
- this.svgHandleTarget_ = dom.createSvgElement(
706
- Svg.RECT, {'class': 'blocklyCommentHandleTarget', 'x': 0, 'y': 0});
707
- this.svgGroup_.appendChild(this.svgHandleTarget_);
708
- this.svgRectTarget_ = dom.createSvgElement(Svg.RECT, {
709
- 'class': 'blocklyCommentTarget',
710
- 'x': 0,
711
- 'y': 0,
712
- 'rx': BORDER_RADIUS,
713
- 'ry': BORDER_RADIUS,
714
- });
715
- this.svgGroup_.appendChild(this.svgRectTarget_);
716
-
717
- // Add the resize icon
718
- this.addResizeDom_();
719
- if (this.isDeletable()) {
720
- // Add the delete icon
721
- this.addDeleteDom_();
722
- }
733
+ this.setSize_(size.width, size.height);
723
734
 
724
- this.setSize_(size.width, size.height);
735
+ // Set the content
736
+ this.textarea_.value = this.content_;
725
737
 
726
- // Set the content
727
- this.textarea_.value = this.content_;
738
+ this.rendered_ = true;
728
739
 
729
- this.rendered_ = true;
740
+ if (this.resizeGroup_) {
741
+ browserEvents.conditionalBind(
742
+ /** @type {!SVGGElement} */ (this.resizeGroup_), 'mousedown', this,
743
+ this.resizeMouseDown_);
744
+ }
730
745
 
731
- if (this.resizeGroup_) {
732
- browserEvents.conditionalBind(
733
- this.resizeGroup_, 'mousedown', this, this.resizeMouseDown_);
746
+ if (this.isDeletable()) {
747
+ browserEvents.conditionalBind(
748
+ /** @type {!SVGGElement} */ (this.deleteGroup_), 'mousedown', this,
749
+ this.deleteMouseDown_);
750
+ browserEvents.conditionalBind(
751
+ /** @type {!SVGGElement} */ (this.deleteGroup_), 'mouseout', this,
752
+ this.deleteMouseOut_);
753
+ browserEvents.conditionalBind(
754
+ /** @type {!SVGGElement} */ (this.deleteGroup_), 'mouseup', this,
755
+ this.deleteMouseUp_);
756
+ }
734
757
  }
735
758
 
736
- if (this.isDeletable()) {
737
- browserEvents.conditionalBind(
738
- this.deleteGroup_, 'mousedown', this, this.deleteMouseDown_);
739
- browserEvents.conditionalBind(
740
- this.deleteGroup_, 'mouseout', this, this.deleteMouseOut_);
759
+ /**
760
+ * Create the text area for the comment.
761
+ * @return {!Element} The top-level node of the editor.
762
+ * @private
763
+ */
764
+ createEditor_() {
765
+ /* Create the editor. Here's the markup that will be generated:
766
+ <foreignObject class="blocklyCommentForeignObject" x="0" y="10"
767
+ width="164" height="164"> <body xmlns="http://www.w3.org/1999/xhtml"
768
+ class="blocklyMinimalBody"> <textarea xmlns="http://www.w3.org/1999/xhtml"
769
+ class="blocklyCommentTextarea"
770
+ style="height: 164px; width: 164px;"></textarea>
771
+ </body>
772
+ </foreignObject>
773
+ */
774
+ this.foreignObject_ = dom.createSvgElement(
775
+ Svg.FOREIGNOBJECT, {
776
+ 'x': 0,
777
+ 'y': WorkspaceCommentSvg.TOP_OFFSET,
778
+ 'class': 'blocklyCommentForeignObject',
779
+ },
780
+ null);
781
+ const body = document.createElementNS(dom.HTML_NS, 'body');
782
+ body.setAttribute('xmlns', dom.HTML_NS);
783
+ body.className = 'blocklyMinimalBody';
784
+ const textarea = document.createElementNS(dom.HTML_NS, 'textarea');
785
+ textarea.className = 'blocklyCommentTextarea';
786
+ textarea.setAttribute('dir', this.RTL ? 'RTL' : 'LTR');
787
+ textarea.readOnly = !this.isEditable();
788
+ body.appendChild(textarea);
789
+ this.textarea_ = textarea;
790
+ this.foreignObject_.appendChild(body);
791
+ // Don't zoom with mousewheel.
792
+ browserEvents.conditionalBind(textarea, 'wheel', this, function(e) {
793
+ e.stopPropagation();
794
+ });
741
795
  browserEvents.conditionalBind(
742
- this.deleteGroup_, 'mouseup', this, this.deleteMouseUp_);
796
+ textarea, 'change', this,
797
+ /**
798
+ * @this {WorkspaceCommentSvg}
799
+ * @param {Event} e Unused event parameter
800
+ */
801
+ function(
802
+ /* eslint-disable no-unused-vars */ e
803
+ /* eslint-enable no-unused-vars */) {
804
+ this.setContent(textarea.value);
805
+ });
806
+ return this.foreignObject_;
743
807
  }
744
- };
745
-
746
- /**
747
- * Create the text area for the comment.
748
- * @return {!Element} The top-level node of the editor.
749
- * @private
750
- */
751
- WorkspaceCommentSvg.prototype.createEditor_ = function() {
752
- /* Create the editor. Here's the markup that will be generated:
753
- <foreignObject class="blocklyCommentForeignObject" x="0" y="10" width="164"
754
- height="164"> <body xmlns="http://www.w3.org/1999/xhtml"
755
- class="blocklyMinimalBody"> <textarea xmlns="http://www.w3.org/1999/xhtml"
756
- class="blocklyCommentTextarea"
757
- style="height: 164px; width: 164px;"></textarea>
758
- </body>
759
- </foreignObject>
760
- */
761
- this.foreignObject_ = dom.createSvgElement(
762
- Svg.FOREIGNOBJECT, {
763
- 'x': 0,
764
- 'y': WorkspaceCommentSvg.TOP_OFFSET,
765
- 'class': 'blocklyCommentForeignObject',
766
- },
767
- null);
768
- const body = document.createElementNS(dom.HTML_NS, 'body');
769
- body.setAttribute('xmlns', dom.HTML_NS);
770
- body.className = 'blocklyMinimalBody';
771
- const textarea = document.createElementNS(dom.HTML_NS, 'textarea');
772
- textarea.className = 'blocklyCommentTextarea';
773
- textarea.setAttribute('dir', this.RTL ? 'RTL' : 'LTR');
774
- textarea.readOnly = !this.isEditable();
775
- body.appendChild(textarea);
776
- this.textarea_ = textarea;
777
- this.foreignObject_.appendChild(body);
778
- // Don't zoom with mousewheel.
779
- browserEvents.conditionalBind(textarea, 'wheel', this, function(e) {
780
- e.stopPropagation();
781
- });
782
- browserEvents.conditionalBind(
783
- textarea, 'change', this,
784
- /**
785
- * @this {WorkspaceCommentSvg}
786
- * @param {Event} e Unused event parameter
787
- */
788
- function(
789
- /* eslint-disable no-unused-vars */ e
790
- /* eslint-enable no-unused-vars */) {
791
- this.setContent(textarea.value);
792
- });
793
- return this.foreignObject_;
794
- };
795
808
 
796
- /**
797
- * Add the resize icon to the DOM
798
- * @private
799
- */
800
- WorkspaceCommentSvg.prototype.addResizeDom_ = function() {
801
- this.resizeGroup_ = dom.createSvgElement(
802
- Svg.G, {'class': this.RTL ? 'blocklyResizeSW' : 'blocklyResizeSE'},
803
- this.svgGroup_);
804
- dom.createSvgElement(
805
- Svg.POLYGON,
806
- {'points': '0,x x,x x,0'.replace(/x/g, RESIZE_SIZE.toString())},
807
- this.resizeGroup_);
808
- dom.createSvgElement(
809
- Svg.LINE, {
810
- 'class': 'blocklyResizeLine',
811
- 'x1': RESIZE_SIZE / 3,
812
- 'y1': RESIZE_SIZE - 1,
813
- 'x2': RESIZE_SIZE - 1,
814
- 'y2': RESIZE_SIZE / 3,
815
- },
816
- this.resizeGroup_);
817
- dom.createSvgElement(
818
- Svg.LINE, {
819
- 'class': 'blocklyResizeLine',
820
- 'x1': RESIZE_SIZE * 2 / 3,
821
- 'y1': RESIZE_SIZE - 1,
822
- 'x2': RESIZE_SIZE - 1,
823
- 'y2': RESIZE_SIZE * 2 / 3,
824
- },
825
- this.resizeGroup_);
826
- };
809
+ /**
810
+ * Add the resize icon to the DOM
811
+ * @private
812
+ */
813
+ addResizeDom_() {
814
+ this.resizeGroup_ = dom.createSvgElement(
815
+ Svg.G, {'class': this.RTL ? 'blocklyResizeSW' : 'blocklyResizeSE'},
816
+ this.svgGroup_);
817
+ dom.createSvgElement(
818
+ Svg.POLYGON,
819
+ {'points': '0,x x,x x,0'.replace(/x/g, RESIZE_SIZE.toString())},
820
+ this.resizeGroup_);
821
+ dom.createSvgElement(
822
+ Svg.LINE, {
823
+ 'class': 'blocklyResizeLine',
824
+ 'x1': RESIZE_SIZE / 3,
825
+ 'y1': RESIZE_SIZE - 1,
826
+ 'x2': RESIZE_SIZE - 1,
827
+ 'y2': RESIZE_SIZE / 3,
828
+ },
829
+ this.resizeGroup_);
830
+ dom.createSvgElement(
831
+ Svg.LINE, {
832
+ 'class': 'blocklyResizeLine',
833
+ 'x1': RESIZE_SIZE * 2 / 3,
834
+ 'y1': RESIZE_SIZE - 1,
835
+ 'x2': RESIZE_SIZE - 1,
836
+ 'y2': RESIZE_SIZE * 2 / 3,
837
+ },
838
+ this.resizeGroup_);
839
+ }
827
840
 
828
- /**
829
- * Add the delete icon to the DOM
830
- * @private
831
- */
832
- WorkspaceCommentSvg.prototype.addDeleteDom_ = function() {
833
- this.deleteGroup_ = dom.createSvgElement(
834
- Svg.G, {'class': 'blocklyCommentDeleteIcon'}, this.svgGroup_);
835
- this.deleteIconBorder_ = dom.createSvgElement(
836
- Svg.CIRCLE,
837
- {'class': 'blocklyDeleteIconShape', 'r': '7', 'cx': '7.5', 'cy': '7.5'},
838
- this.deleteGroup_);
839
- // x icon.
840
- dom.createSvgElement(
841
- Svg.LINE, {
842
- 'x1': '5',
843
- 'y1': '10',
844
- 'x2': '10',
845
- 'y2': '5',
846
- 'stroke': '#fff',
847
- 'stroke-width': '2',
848
- },
849
- this.deleteGroup_);
850
- dom.createSvgElement(
851
- Svg.LINE, {
852
- 'x1': '5',
853
- 'y1': '5',
854
- 'x2': '10',
855
- 'y2': '10',
856
- 'stroke': '#fff',
857
- 'stroke-width': '2',
858
- },
859
- this.deleteGroup_);
860
- };
841
+ /**
842
+ * Add the delete icon to the DOM
843
+ * @private
844
+ */
845
+ addDeleteDom_() {
846
+ this.deleteGroup_ = dom.createSvgElement(
847
+ Svg.G, {'class': 'blocklyCommentDeleteIcon'}, this.svgGroup_);
848
+ this.deleteIconBorder_ = dom.createSvgElement(
849
+ Svg.CIRCLE,
850
+ {'class': 'blocklyDeleteIconShape', 'r': '7', 'cx': '7.5', 'cy': '7.5'},
851
+ this.deleteGroup_);
852
+ // x icon.
853
+ dom.createSvgElement(
854
+ Svg.LINE, {
855
+ 'x1': '5',
856
+ 'y1': '10',
857
+ 'x2': '10',
858
+ 'y2': '5',
859
+ 'stroke': '#fff',
860
+ 'stroke-width': '2',
861
+ },
862
+ this.deleteGroup_);
863
+ dom.createSvgElement(
864
+ Svg.LINE, {
865
+ 'x1': '5',
866
+ 'y1': '5',
867
+ 'x2': '10',
868
+ 'y2': '10',
869
+ 'stroke': '#fff',
870
+ 'stroke-width': '2',
871
+ },
872
+ this.deleteGroup_);
873
+ }
861
874
 
862
- /**
863
- * Handle a mouse-down on comment's resize corner.
864
- * @param {!Event} e Mouse down event.
865
- * @private
866
- */
867
- WorkspaceCommentSvg.prototype.resizeMouseDown_ = function(e) {
868
- this.unbindDragEvents_();
869
- if (browserEvents.isRightButton(e)) {
870
- // No right-click.
875
+ /**
876
+ * Handle a mouse-down on comment's resize corner.
877
+ * @param {!Event} e Mouse down event.
878
+ * @private
879
+ */
880
+ resizeMouseDown_(e) {
881
+ this.unbindDragEvents_();
882
+ if (browserEvents.isRightButton(e)) {
883
+ // No right-click.
884
+ e.stopPropagation();
885
+ return;
886
+ }
887
+ // Left-click (or middle click)
888
+ this.workspace.startDrag(
889
+ e,
890
+ new Coordinate(
891
+ this.workspace.RTL ? -this.width_ : this.width_, this.height_));
892
+
893
+ this.onMouseUpWrapper_ = browserEvents.conditionalBind(
894
+ document, 'mouseup', this, this.resizeMouseUp_);
895
+ this.onMouseMoveWrapper_ = browserEvents.conditionalBind(
896
+ document, 'mousemove', this, this.resizeMouseMove_);
897
+ this.workspace.hideChaff();
898
+ // This event has been handled. No need to bubble up to the document.
871
899
  e.stopPropagation();
872
- return;
873
900
  }
874
- // Left-click (or middle click)
875
- this.workspace.startDrag(
876
- e,
877
- new Coordinate(
878
- this.workspace.RTL ? -this.width_ : this.width_, this.height_));
879
-
880
- this.onMouseUpWrapper_ = browserEvents.conditionalBind(
881
- document, 'mouseup', this, this.resizeMouseUp_);
882
- this.onMouseMoveWrapper_ = browserEvents.conditionalBind(
883
- document, 'mousemove', this, this.resizeMouseMove_);
884
- this.workspace.hideChaff();
885
- // This event has been handled. No need to bubble up to the document.
886
- e.stopPropagation();
887
- };
888
-
889
- /**
890
- * Handle a mouse-down on comment's delete icon.
891
- * @param {!Event} e Mouse down event.
892
- * @private
893
- */
894
- WorkspaceCommentSvg.prototype.deleteMouseDown_ = function(e) {
895
- // Highlight the delete icon.
896
- dom.addClass(
897
- /** @type {!Element} */ (this.deleteIconBorder_),
898
- 'blocklyDeleteIconHighlighted');
899
- // This event has been handled. No need to bubble up to the document.
900
- e.stopPropagation();
901
- };
902
901
 
903
- /**
904
- * Handle a mouse-out on comment's delete icon.
905
- * @param {!Event} _e Mouse out event.
906
- * @private
907
- */
908
- WorkspaceCommentSvg.prototype.deleteMouseOut_ = function(_e) {
909
- // Restore highlight on the delete icon.
910
- dom.removeClass(
911
- /** @type {!Element} */ (this.deleteIconBorder_),
912
- 'blocklyDeleteIconHighlighted');
913
- };
914
-
915
- /**
916
- * Handle a mouse-up on comment's delete icon.
917
- * @param {!Event} e Mouse up event.
918
- * @private
919
- */
920
- WorkspaceCommentSvg.prototype.deleteMouseUp_ = function(e) {
921
- // Delete this comment.
922
- this.dispose();
923
- // This event has been handled. No need to bubble up to the document.
924
- e.stopPropagation();
925
- };
926
-
927
- /**
928
- * Stop binding to the global mouseup and mousemove events.
929
- * @private
930
- */
931
- WorkspaceCommentSvg.prototype.unbindDragEvents_ = function() {
932
- if (this.onMouseUpWrapper_) {
933
- browserEvents.unbind(this.onMouseUpWrapper_);
934
- this.onMouseUpWrapper_ = null;
902
+ /**
903
+ * Handle a mouse-down on comment's delete icon.
904
+ * @param {!Event} e Mouse down event.
905
+ * @private
906
+ */
907
+ deleteMouseDown_(e) {
908
+ // Highlight the delete icon.
909
+ dom.addClass(
910
+ /** @type {!Element} */ (this.deleteIconBorder_),
911
+ 'blocklyDeleteIconHighlighted');
912
+ // This event has been handled. No need to bubble up to the document.
913
+ e.stopPropagation();
935
914
  }
936
- if (this.onMouseMoveWrapper_) {
937
- browserEvents.unbind(this.onMouseMoveWrapper_);
938
- this.onMouseMoveWrapper_ = null;
915
+
916
+ /**
917
+ * Handle a mouse-out on comment's delete icon.
918
+ * @param {!Event} _e Mouse out event.
919
+ * @private
920
+ */
921
+ deleteMouseOut_(_e) {
922
+ // Restore highlight on the delete icon.
923
+ dom.removeClass(
924
+ /** @type {!Element} */ (this.deleteIconBorder_),
925
+ 'blocklyDeleteIconHighlighted');
939
926
  }
940
- };
941
927
 
942
- /**
943
- * Handle a mouse-up event while dragging a comment's border or resize handle.
944
- * @param {!Event} _e Mouse up event.
945
- * @private
946
- */
947
- WorkspaceCommentSvg.prototype.resizeMouseUp_ = function(_e) {
948
- Touch.clearTouchIdentifier();
949
- this.unbindDragEvents_();
950
- };
928
+ /**
929
+ * Handle a mouse-up on comment's delete icon.
930
+ * @param {!Event} e Mouse up event.
931
+ * @private
932
+ */
933
+ deleteMouseUp_(e) {
934
+ // Delete this comment.
935
+ this.dispose();
936
+ // This event has been handled. No need to bubble up to the document.
937
+ e.stopPropagation();
938
+ }
951
939
 
952
- /**
953
- * Resize this comment to follow the mouse.
954
- * @param {!Event} e Mouse move event.
955
- * @private
956
- */
957
- WorkspaceCommentSvg.prototype.resizeMouseMove_ = function(e) {
958
- this.autoLayout_ = false;
959
- const newXY = this.workspace.moveDrag(e);
960
- this.setSize_(this.RTL ? -newXY.x : newXY.x, newXY.y);
961
- };
940
+ /**
941
+ * Stop binding to the global mouseup and mousemove events.
942
+ * @private
943
+ */
944
+ unbindDragEvents_() {
945
+ if (this.onMouseUpWrapper_) {
946
+ browserEvents.unbind(this.onMouseUpWrapper_);
947
+ this.onMouseUpWrapper_ = null;
948
+ }
949
+ if (this.onMouseMoveWrapper_) {
950
+ browserEvents.unbind(this.onMouseMoveWrapper_);
951
+ this.onMouseMoveWrapper_ = null;
952
+ }
953
+ }
962
954
 
963
- /**
964
- * Callback function triggered when the comment has resized.
965
- * Resize the text area accordingly.
966
- * @private
967
- */
968
- WorkspaceCommentSvg.prototype.resizeComment_ = function() {
969
- const size = this.getHeightWidth();
970
- const topOffset = WorkspaceCommentSvg.TOP_OFFSET;
971
- const textOffset = TEXTAREA_OFFSET * 2;
972
-
973
- this.foreignObject_.setAttribute('width', size.width);
974
- this.foreignObject_.setAttribute('height', size.height - topOffset);
975
- if (this.RTL) {
976
- this.foreignObject_.setAttribute('x', -size.width);
955
+ /**
956
+ * Handle a mouse-up event while dragging a comment's border or resize handle.
957
+ * @param {!Event} _e Mouse up event.
958
+ * @private
959
+ */
960
+ resizeMouseUp_(_e) {
961
+ Touch.clearTouchIdentifier();
962
+ this.unbindDragEvents_();
977
963
  }
978
- this.textarea_.style.width = (size.width - textOffset) + 'px';
979
- this.textarea_.style.height = (size.height - textOffset - topOffset) + 'px';
980
- };
981
964
 
982
- /**
983
- * Set size
984
- * @param {number} width width of the container
985
- * @param {number} height height of the container
986
- * @private
987
- */
988
- WorkspaceCommentSvg.prototype.setSize_ = function(width, height) {
989
- // Minimum size of a comment.
990
- width = Math.max(width, 45);
991
- height = Math.max(height, 20 + WorkspaceCommentSvg.TOP_OFFSET);
992
- this.width_ = width;
993
- this.height_ = height;
994
- this.svgRect_.setAttribute('width', width);
995
- this.svgRect_.setAttribute('height', height);
996
- this.svgRectTarget_.setAttribute('width', width);
997
- this.svgRectTarget_.setAttribute('height', height);
998
- this.svgHandleTarget_.setAttribute('width', width);
999
- this.svgHandleTarget_.setAttribute('height', WorkspaceCommentSvg.TOP_OFFSET);
1000
- if (this.RTL) {
1001
- this.svgRect_.setAttribute('transform', 'scale(-1 1)');
1002
- this.svgRectTarget_.setAttribute('transform', 'scale(-1 1)');
965
+ /**
966
+ * Resize this comment to follow the mouse.
967
+ * @param {!Event} e Mouse move event.
968
+ * @private
969
+ */
970
+ resizeMouseMove_(e) {
971
+ this.autoLayout_ = false;
972
+ const newXY = this.workspace.moveDrag(e);
973
+ this.setSize_(this.RTL ? -newXY.x : newXY.x, newXY.y);
1003
974
  }
1004
975
 
1005
- if (this.resizeGroup_) {
976
+ /**
977
+ * Callback function triggered when the comment has resized.
978
+ * Resize the text area accordingly.
979
+ * @private
980
+ */
981
+ resizeComment_() {
982
+ const size = this.getHeightWidth();
983
+ const topOffset = WorkspaceCommentSvg.TOP_OFFSET;
984
+ const textOffset = TEXTAREA_OFFSET * 2;
985
+
986
+ this.foreignObject_.setAttribute('width', size.width);
987
+ this.foreignObject_.setAttribute('height', size.height - topOffset);
1006
988
  if (this.RTL) {
1007
- // Mirror the resize group.
1008
- this.resizeGroup_.setAttribute(
1009
- 'transform',
1010
- 'translate(' + (-width + RESIZE_SIZE) + ',' + (height - RESIZE_SIZE) +
1011
- ') scale(-1 1)');
1012
- this.deleteGroup_.setAttribute(
1013
- 'transform',
1014
- 'translate(' + (-width + RESIZE_SIZE) + ',' + (-RESIZE_SIZE) +
1015
- ') scale(-1 1)');
1016
- } else {
1017
- this.resizeGroup_.setAttribute(
1018
- 'transform',
1019
- 'translate(' + (width - RESIZE_SIZE) + ',' + (height - RESIZE_SIZE) +
1020
- ')');
1021
- this.deleteGroup_.setAttribute(
1022
- 'transform',
1023
- 'translate(' + (width - RESIZE_SIZE) + ',' + (-RESIZE_SIZE) + ')');
989
+ this.foreignObject_.setAttribute('x', -size.width);
1024
990
  }
991
+ this.textarea_.style.width = (size.width - textOffset) + 'px';
992
+ this.textarea_.style.height = (size.height - textOffset - topOffset) + 'px';
1025
993
  }
1026
994
 
1027
- // Allow the contents to resize.
1028
- this.resizeComment_();
1029
- };
1030
-
1031
- /**
1032
- * Dispose of any rendered comment components.
1033
- * @private
1034
- */
1035
- WorkspaceCommentSvg.prototype.disposeInternal_ = function() {
1036
- this.textarea_ = null;
1037
- this.foreignObject_ = null;
1038
- this.svgRectTarget_ = null;
1039
- this.svgHandleTarget_ = null;
1040
- this.disposed_ = true;
1041
- };
1042
-
1043
- /**
1044
- * Set the focus on the text area.
1045
- * @package
1046
- */
1047
- WorkspaceCommentSvg.prototype.setFocus = function() {
1048
- const comment = this;
1049
- this.focused_ = true;
1050
- // Defer CSS changes.
1051
- setTimeout(function() {
1052
- if (comment.disposed_) {
1053
- return;
995
+ /**
996
+ * Set size
997
+ * @param {number} width width of the container
998
+ * @param {number} height height of the container
999
+ * @private
1000
+ */
1001
+ setSize_(width, height) {
1002
+ // Minimum size of a comment.
1003
+ width = Math.max(width, 45);
1004
+ height = Math.max(height, 20 + WorkspaceCommentSvg.TOP_OFFSET);
1005
+ this.width_ = width;
1006
+ this.height_ = height;
1007
+ this.svgRect_.setAttribute('width', width);
1008
+ this.svgRect_.setAttribute('height', height);
1009
+ this.svgRectTarget_.setAttribute('width', width);
1010
+ this.svgRectTarget_.setAttribute('height', height);
1011
+ this.svgHandleTarget_.setAttribute('width', width);
1012
+ this.svgHandleTarget_.setAttribute(
1013
+ 'height', WorkspaceCommentSvg.TOP_OFFSET);
1014
+ if (this.RTL) {
1015
+ this.svgRect_.setAttribute('transform', 'scale(-1 1)');
1016
+ this.svgRectTarget_.setAttribute('transform', 'scale(-1 1)');
1054
1017
  }
1055
- comment.textarea_.focus();
1056
- comment.addFocus();
1057
- dom.addClass(comment.svgRectTarget_, 'blocklyCommentTargetFocused');
1058
- dom.addClass(comment.svgHandleTarget_, 'blocklyCommentHandleTargetFocused');
1059
- }, 0);
1060
- };
1061
1018
 
1062
- /**
1063
- * Remove focus from the text area.
1064
- * @package
1065
- */
1066
- WorkspaceCommentSvg.prototype.blurFocus = function() {
1067
- const comment = this;
1068
- this.focused_ = false;
1069
- // Defer CSS changes.
1070
- setTimeout(function() {
1071
- if (comment.disposed_) {
1072
- return;
1019
+ if (this.resizeGroup_) {
1020
+ if (this.RTL) {
1021
+ // Mirror the resize group.
1022
+ this.resizeGroup_.setAttribute(
1023
+ 'transform',
1024
+ 'translate(' + (-width + RESIZE_SIZE) + ',' +
1025
+ (height - RESIZE_SIZE) + ') scale(-1 1)');
1026
+ this.deleteGroup_.setAttribute(
1027
+ 'transform',
1028
+ 'translate(' + (-width + RESIZE_SIZE) + ',' + (-RESIZE_SIZE) +
1029
+ ') scale(-1 1)');
1030
+ } else {
1031
+ this.resizeGroup_.setAttribute(
1032
+ 'transform',
1033
+ 'translate(' + (width - RESIZE_SIZE) + ',' +
1034
+ (height - RESIZE_SIZE) + ')');
1035
+ this.deleteGroup_.setAttribute(
1036
+ 'transform',
1037
+ 'translate(' + (width - RESIZE_SIZE) + ',' + (-RESIZE_SIZE) + ')');
1038
+ }
1073
1039
  }
1074
1040
 
1075
- comment.textarea_.blur();
1076
- comment.removeFocus();
1077
- dom.removeClass(comment.svgRectTarget_, 'blocklyCommentTargetFocused');
1078
- dom.removeClass(
1079
- comment.svgHandleTarget_, 'blocklyCommentHandleTargetFocused');
1080
- }, 0);
1081
- };
1082
-
1083
- /**
1084
- * CSS for workspace comment. See css.js for use.
1085
- */
1086
- Css.register(`
1087
- .blocklyCommentForeignObject {
1088
- position: relative;
1089
- z-index: 0;
1090
- }
1091
-
1092
- .blocklyCommentRect {
1093
- fill: #E7DE8E;
1094
- stroke: #bcA903;
1095
- stroke-width: 1px;
1041
+ // Allow the contents to resize.
1042
+ this.resizeComment_();
1096
1043
  }
1097
1044
 
1098
- .blocklyCommentTarget {
1099
- fill: transparent;
1100
- stroke: #bcA903;
1045
+ /**
1046
+ * Dispose of any rendered comment components.
1047
+ * @private
1048
+ */
1049
+ disposeInternal_() {
1050
+ this.textarea_ = null;
1051
+ this.foreignObject_ = null;
1052
+ this.svgRectTarget_ = null;
1053
+ this.svgHandleTarget_ = null;
1054
+ this.disposed_ = true;
1101
1055
  }
1102
1056
 
1103
- .blocklyCommentTargetFocused {
1104
- fill: none;
1057
+ /**
1058
+ * Set the focus on the text area.
1059
+ * @package
1060
+ */
1061
+ setFocus() {
1062
+ const comment = this;
1063
+ this.focused_ = true;
1064
+ // Defer CSS changes.
1065
+ setTimeout(function() {
1066
+ if (comment.disposed_) {
1067
+ return;
1068
+ }
1069
+ comment.textarea_.focus();
1070
+ comment.addFocus();
1071
+ dom.addClass(
1072
+ /** @type {!SVGRectElement} */ (comment.svgRectTarget_),
1073
+ 'blocklyCommentTargetFocused');
1074
+ dom.addClass(
1075
+ /** @type {!SVGRectElement} */ (comment.svgHandleTarget_),
1076
+ 'blocklyCommentHandleTargetFocused');
1077
+ }, 0);
1105
1078
  }
1106
1079
 
1107
- .blocklyCommentHandleTarget {
1108
- fill: none;
1109
- }
1080
+ /**
1081
+ * Remove focus from the text area.
1082
+ * @package
1083
+ */
1084
+ blurFocus() {
1085
+ const comment = this;
1086
+ this.focused_ = false;
1087
+ // Defer CSS changes.
1088
+ setTimeout(function() {
1089
+ if (comment.disposed_) {
1090
+ return;
1091
+ }
1110
1092
 
1111
- .blocklyCommentHandleTargetFocused {
1112
- fill: transparent;
1093
+ comment.textarea_.blur();
1094
+ comment.removeFocus();
1095
+ dom.removeClass(
1096
+ /** @type {!SVGRectElement} */ (comment.svgRectTarget_),
1097
+ 'blocklyCommentTargetFocused');
1098
+ dom.removeClass(
1099
+ /** @type {!SVGRectElement} */ (comment.svgHandleTarget_),
1100
+ 'blocklyCommentHandleTargetFocused');
1101
+ }, 0);
1113
1102
  }
1114
1103
 
1115
- .blocklyFocused>.blocklyCommentRect {
1116
- fill: #B9B272;
1117
- stroke: #B9B272;
1118
- }
1104
+ /**
1105
+ * Decode an XML comment tag and create a rendered comment on the workspace.
1106
+ * @param {!Element} xmlComment XML comment element.
1107
+ * @param {!WorkspaceSvg} workspace The workspace.
1108
+ * @param {number=} opt_wsWidth The width of the workspace, which is used to
1109
+ * position comments correctly in RTL.
1110
+ * @return {!WorkspaceCommentSvg} The created workspace comment.
1111
+ * @package
1112
+ */
1113
+ static fromXmlRendered(xmlComment, workspace, opt_wsWidth) {
1114
+ eventUtils.disable();
1115
+ let comment;
1116
+ try {
1117
+ const info = WorkspaceComment.parseAttributes(xmlComment);
1119
1118
 
1120
- .blocklySelected>.blocklyCommentTarget {
1121
- stroke: #fc3;
1122
- stroke-width: 3px;
1123
- }
1119
+ comment = new WorkspaceCommentSvg(
1120
+ workspace, info.content, info.h, info.w, info.id);
1121
+ if (workspace.rendered) {
1122
+ comment.initSvg(true);
1123
+ comment.render();
1124
+ }
1125
+ // Position the comment correctly, taking into account the width of a
1126
+ // rendered RTL workspace.
1127
+ if (!isNaN(info.x) && !isNaN(info.y)) {
1128
+ if (workspace.RTL) {
1129
+ const wsWidth = opt_wsWidth || workspace.getWidth();
1130
+ comment.moveBy(wsWidth - info.x, info.y);
1131
+ } else {
1132
+ comment.moveBy(info.x, info.y);
1133
+ }
1134
+ }
1135
+ } finally {
1136
+ eventUtils.enable();
1137
+ }
1124
1138
 
1125
- .blocklyCommentDeleteIcon {
1126
- cursor: pointer;
1127
- fill: #000;
1128
- display: none;
1139
+ WorkspaceComment.fireCreateEvent(
1140
+ /** @type {!WorkspaceCommentSvg} */ (comment));
1141
+ return (/** @type {!WorkspaceCommentSvg} */ (comment));
1129
1142
  }
1143
+ }
1130
1144
 
1131
- .blocklySelected > .blocklyCommentDeleteIcon {
1132
- display: block;
1133
- }
1145
+ /**
1146
+ * The width and height to use to size a workspace comment when it is first
1147
+ * added, before it has been edited by the user.
1148
+ * @type {number}
1149
+ * @package
1150
+ */
1151
+ WorkspaceCommentSvg.DEFAULT_SIZE = 100;
1134
1152
 
1135
- .blocklyDeleteIconShape {
1136
- fill: #000;
1137
- stroke: #000;
1138
- stroke-width: 1px;
1139
- }
1153
+ /**
1154
+ * Offset from the top to make room for a top bar.
1155
+ * @type {number}
1156
+ * @const
1157
+ * @private
1158
+ */
1159
+ WorkspaceCommentSvg.TOP_OFFSET = 10;
1140
1160
 
1141
- .blocklyDeleteIconShape.blocklyDeleteIconHighlighted {
1142
- stroke: #fc3;
1143
- }
1161
+ /**
1162
+ * CSS for workspace comment. See css.js for use.
1163
+ */
1164
+ Css.register(`
1165
+ .blocklyCommentForeignObject {
1166
+ position: relative;
1167
+ z-index: 0;
1168
+ }
1169
+
1170
+ .blocklyCommentRect {
1171
+ fill: #E7DE8E;
1172
+ stroke: #bcA903;
1173
+ stroke-width: 1px;
1174
+ }
1175
+
1176
+ .blocklyCommentTarget {
1177
+ fill: transparent;
1178
+ stroke: #bcA903;
1179
+ }
1180
+
1181
+ .blocklyCommentTargetFocused {
1182
+ fill: none;
1183
+ }
1184
+
1185
+ .blocklyCommentHandleTarget {
1186
+ fill: none;
1187
+ }
1188
+
1189
+ .blocklyCommentHandleTargetFocused {
1190
+ fill: transparent;
1191
+ }
1192
+
1193
+ .blocklyFocused>.blocklyCommentRect {
1194
+ fill: #B9B272;
1195
+ stroke: #B9B272;
1196
+ }
1197
+
1198
+ .blocklySelected>.blocklyCommentTarget {
1199
+ stroke: #fc3;
1200
+ stroke-width: 3px;
1201
+ }
1202
+
1203
+ .blocklyCommentDeleteIcon {
1204
+ cursor: pointer;
1205
+ fill: #000;
1206
+ display: none;
1207
+ }
1208
+
1209
+ .blocklySelected > .blocklyCommentDeleteIcon {
1210
+ display: block;
1211
+ }
1212
+
1213
+ .blocklyDeleteIconShape {
1214
+ fill: #000;
1215
+ stroke: #000;
1216
+ stroke-width: 1px;
1217
+ }
1218
+
1219
+ .blocklyDeleteIconShape.blocklyDeleteIconHighlighted {
1220
+ stroke: #fc3;
1221
+ }
1144
1222
  `);
1145
1223
 
1146
1224
  exports.WorkspaceCommentSvg = WorkspaceCommentSvg;