@blockdia/scratch-blocks 0.1.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.
Files changed (243) hide show
  1. package/.nvmrc +1 -0
  2. package/LICENSE +674 -0
  3. package/README.md +57 -0
  4. package/TRADEMARK +1 -0
  5. package/blockly_compressed_horizontal.js +2268 -0
  6. package/blockly_compressed_vertical.js +2296 -0
  7. package/blockly_uncompressed_horizontal.js +1212 -0
  8. package/blockly_uncompressed_vertical.js +1212 -0
  9. package/blocks_common/colour.js +61 -0
  10. package/blocks_common/math.js +159 -0
  11. package/blocks_common/matrix.js +54 -0
  12. package/blocks_common/note.js +58 -0
  13. package/blocks_common/text.js +57 -0
  14. package/blocks_compressed.js +37 -0
  15. package/blocks_compressed_horizontal.js +68 -0
  16. package/blocks_compressed_vertical.js +216 -0
  17. package/blocks_horizontal/control.js +212 -0
  18. package/blocks_horizontal/default_toolbox.js +139 -0
  19. package/blocks_horizontal/event.js +190 -0
  20. package/blocks_horizontal/wedo.js +325 -0
  21. package/blocks_vertical/control.js +532 -0
  22. package/blocks_vertical/data.js +666 -0
  23. package/blocks_vertical/default_toolbox.js +571 -0
  24. package/blocks_vertical/event.js +329 -0
  25. package/blocks_vertical/extensions.js +294 -0
  26. package/blocks_vertical/looks.js +591 -0
  27. package/blocks_vertical/motion.js +587 -0
  28. package/blocks_vertical/operators.js +470 -0
  29. package/blocks_vertical/procedures.js +1051 -0
  30. package/blocks_vertical/sensing.js +555 -0
  31. package/blocks_vertical/sound.js +246 -0
  32. package/blocks_vertical/vertical_extensions.js +289 -0
  33. package/build/gen_blocks.js +1 -0
  34. package/build/test_expect.js +1 -0
  35. package/build/test_input.js +1 -0
  36. package/build.py +647 -0
  37. package/cleanup.sh +101 -0
  38. package/core/block.js +1837 -0
  39. package/core/block_animations.js +107 -0
  40. package/core/block_drag_surface.js +299 -0
  41. package/core/block_dragger.js +424 -0
  42. package/core/block_events.js +531 -0
  43. package/core/block_render_svg_horizontal.js +892 -0
  44. package/core/block_render_svg_vertical.js +1734 -0
  45. package/core/block_svg.js +1355 -0
  46. package/core/blockly.js +620 -0
  47. package/core/blocks.js +37 -0
  48. package/core/bubble.js +664 -0
  49. package/core/bubble_dragger.js +285 -0
  50. package/core/colours.js +177 -0
  51. package/core/comment.js +293 -0
  52. package/core/comment_events.js +539 -0
  53. package/core/connection.js +776 -0
  54. package/core/connection_db.js +300 -0
  55. package/core/constants.js +408 -0
  56. package/core/contextmenu.js +534 -0
  57. package/core/css.js +1354 -0
  58. package/core/data_category.js +490 -0
  59. package/core/dragged_connection_manager.js +260 -0
  60. package/core/dropdowndiv.js +408 -0
  61. package/core/events.js +429 -0
  62. package/core/events_abstract.js +113 -0
  63. package/core/extensions.js +450 -0
  64. package/core/field.js +810 -0
  65. package/core/field_angle.js +410 -0
  66. package/core/field_checkbox.js +133 -0
  67. package/core/field_colour.js +253 -0
  68. package/core/field_colour_slider.js +387 -0
  69. package/core/field_date.js +353 -0
  70. package/core/field_dropdown.js +447 -0
  71. package/core/field_iconmenu.js +309 -0
  72. package/core/field_image.js +200 -0
  73. package/core/field_label.js +136 -0
  74. package/core/field_label_serializable.js +125 -0
  75. package/core/field_matrix.js +566 -0
  76. package/core/field_note.js +850 -0
  77. package/core/field_number.js +366 -0
  78. package/core/field_numberdropdown.js +77 -0
  79. package/core/field_textdropdown.js +164 -0
  80. package/core/field_textinput.js +675 -0
  81. package/core/field_textinput_removable.js +105 -0
  82. package/core/field_variable.js +385 -0
  83. package/core/field_variable_getter.js +185 -0
  84. package/core/field_vertical_separator.js +161 -0
  85. package/core/flyout_base.js +935 -0
  86. package/core/flyout_button.js +324 -0
  87. package/core/flyout_dragger.js +83 -0
  88. package/core/flyout_extension_category_header.js +159 -0
  89. package/core/flyout_horizontal.js +475 -0
  90. package/core/flyout_vertical.js +770 -0
  91. package/core/generator.js +426 -0
  92. package/core/gesture.js +1010 -0
  93. package/core/grid.js +227 -0
  94. package/core/icon.js +205 -0
  95. package/core/inject.js +496 -0
  96. package/core/input.js +285 -0
  97. package/core/insertion_marker_manager.js +678 -0
  98. package/core/intersection_observer.js +102 -0
  99. package/core/msg.js +62 -0
  100. package/core/mutator.js +426 -0
  101. package/core/names.js +198 -0
  102. package/core/options.js +244 -0
  103. package/core/procedures.js +739 -0
  104. package/core/rendered_connection.js +417 -0
  105. package/core/scratch_block_comment.js +646 -0
  106. package/core/scratch_blocks_utils.js +246 -0
  107. package/core/scratch_bubble.js +699 -0
  108. package/core/scratch_events.js +131 -0
  109. package/core/scratch_msgs.js +85 -0
  110. package/core/scrollbar.js +875 -0
  111. package/core/toolbox.js +803 -0
  112. package/core/tooltip.js +337 -0
  113. package/core/touch.js +226 -0
  114. package/core/trashcan.js +343 -0
  115. package/core/ui_events.js +91 -0
  116. package/core/ui_menu_utils.js +68 -0
  117. package/core/utils.js +825 -0
  118. package/core/variable_events.js +259 -0
  119. package/core/variable_map.js +415 -0
  120. package/core/variable_model.js +116 -0
  121. package/core/variables.js +674 -0
  122. package/core/warning.js +199 -0
  123. package/core/widgetdiv.js +344 -0
  124. package/core/workspace.js +673 -0
  125. package/core/workspace_audio.js +170 -0
  126. package/core/workspace_comment.js +426 -0
  127. package/core/workspace_comment_render_svg.js +706 -0
  128. package/core/workspace_comment_svg.js +609 -0
  129. package/core/workspace_drag_surface_svg.js +195 -0
  130. package/core/workspace_dragger.js +132 -0
  131. package/core/workspace_svg.js +2382 -0
  132. package/core/xml.js +930 -0
  133. package/core/zoom_controls.js +301 -0
  134. package/dist/horizontal.js +222 -0
  135. package/dist/vertical.js +222 -0
  136. package/dist/web/horizontal.js +232 -0
  137. package/dist/web/vertical.js +232 -0
  138. package/i18n/common.py +234 -0
  139. package/i18n/create_messages.py +162 -0
  140. package/i18n/create_scratch_msgs.js +69 -0
  141. package/i18n/dedup_json.py +73 -0
  142. package/i18n/js_to_json.js +46 -0
  143. package/i18n/js_to_json.py +136 -0
  144. package/i18n/json_to_js.js +52 -0
  145. package/i18n/json_to_js.py +185 -0
  146. package/i18n/sync_tx_translations.js +111 -0
  147. package/i18n/test_scratch_msgs.js +87 -0
  148. package/i18n/tests.py +47 -0
  149. package/i18n/xliff_to_json.py +232 -0
  150. package/local_build.sh +70 -0
  151. package/media/click.mp3 +0 -0
  152. package/media/click.ogg +0 -0
  153. package/media/click.wav +0 -0
  154. package/media/comment-arrow-down.svg +10 -0
  155. package/media/comment-arrow-up.svg +10 -0
  156. package/media/delete-x.svg +10 -0
  157. package/media/delete.mp3 +0 -0
  158. package/media/delete.ogg +0 -0
  159. package/media/delete.wav +0 -0
  160. package/media/dropdown-arrow-dark.svg +1 -0
  161. package/media/dropdown-arrow.svg +1 -0
  162. package/media/extensions/microbit-block-icon.svg +130 -0
  163. package/media/extensions/music-block-icon.svg +17 -0
  164. package/media/extensions/pen-block-icon.svg +19 -0
  165. package/media/extensions/wedo2-block-icon.svg +36 -0
  166. package/media/eyedropper.svg +22 -0
  167. package/media/green-flag.svg +17 -0
  168. package/media/handclosed.cur +0 -0
  169. package/media/handdelete.cur +0 -0
  170. package/media/handopen.cur +0 -0
  171. package/media/icons/arrow.svg +12 -0
  172. package/media/icons/arrow_button.svg +1 -0
  173. package/media/icons/control_forever.svg +1 -0
  174. package/media/icons/control_repeat.svg +1 -0
  175. package/media/icons/control_stop.svg +1 -0
  176. package/media/icons/control_wait.svg +1 -0
  177. package/media/icons/event_broadcast_blue.svg +1 -0
  178. package/media/icons/event_broadcast_coral.svg +1 -0
  179. package/media/icons/event_broadcast_green.svg +1 -0
  180. package/media/icons/event_broadcast_magenta.svg +1 -0
  181. package/media/icons/event_broadcast_orange.svg +1 -0
  182. package/media/icons/event_broadcast_purple.svg +1 -0
  183. package/media/icons/event_when-broadcast-received_blue.svg +1 -0
  184. package/media/icons/event_when-broadcast-received_coral.svg +1 -0
  185. package/media/icons/event_when-broadcast-received_green.svg +1 -0
  186. package/media/icons/event_when-broadcast-received_magenta.svg +1 -0
  187. package/media/icons/event_when-broadcast-received_orange.svg +1 -0
  188. package/media/icons/event_when-broadcast-received_purple.svg +1 -0
  189. package/media/icons/event_whenflagclicked.svg +1 -0
  190. package/media/icons/remove.svg +19 -0
  191. package/media/icons/set-led_blue.svg +1 -0
  192. package/media/icons/set-led_coral.svg +1 -0
  193. package/media/icons/set-led_green.svg +1 -0
  194. package/media/icons/set-led_magenta.svg +1 -0
  195. package/media/icons/set-led_mystery.svg +1 -0
  196. package/media/icons/set-led_orange.svg +1 -0
  197. package/media/icons/set-led_purple.svg +1 -0
  198. package/media/icons/set-led_white.svg +1 -0
  199. package/media/icons/set-led_yellow.svg +1 -0
  200. package/media/icons/wedo_motor-clockwise.svg +1 -0
  201. package/media/icons/wedo_motor-counterclockwise.svg +1 -0
  202. package/media/icons/wedo_motor-speed_fast.svg +1 -0
  203. package/media/icons/wedo_motor-speed_med.svg +1 -0
  204. package/media/icons/wedo_motor-speed_slow.svg +1 -0
  205. package/media/icons/wedo_when-distance_close.svg +1 -0
  206. package/media/icons/wedo_when-tilt-backward.svg +1 -0
  207. package/media/icons/wedo_when-tilt-forward.svg +1 -0
  208. package/media/icons/wedo_when-tilt-left.svg +1 -0
  209. package/media/icons/wedo_when-tilt-right.svg +1 -0
  210. package/media/icons/wedo_when-tilt.svg +1 -0
  211. package/media/repeat.svg +18 -0
  212. package/media/rotate-left.svg +1 -0
  213. package/media/rotate-right.svg +1 -0
  214. package/media/sprites.png +0 -0
  215. package/media/status-not-ready.svg +13 -0
  216. package/media/status-ready.svg +13 -0
  217. package/media/zoom-in.svg +1 -0
  218. package/media/zoom-out.svg +1 -0
  219. package/media/zoom-reset.svg +1 -0
  220. package/msg/js/en.js +290 -0
  221. package/msg/json/en.json +285 -0
  222. package/msg/messages.js +359 -0
  223. package/msg/scratch_msgs.js +22969 -0
  224. package/package.json +60 -0
  225. package/pull_from_blockly.sh +151 -0
  226. package/renovate.json5 +15 -0
  227. package/shim/blockly_compressed_horizontal-blocks_compressed.js +1 -0
  228. package/shim/blockly_compressed_horizontal.Blockly.js +1 -0
  229. package/shim/blockly_compressed_horizontal.goog.js +1 -0
  230. package/shim/blockly_compressed_horizontal.js +1 -0
  231. package/shim/blockly_compressed_vertical-blocks_compressed.js +1 -0
  232. package/shim/blockly_compressed_vertical.Blockly.js +1 -0
  233. package/shim/blockly_compressed_vertical.goog.js +1 -0
  234. package/shim/blockly_compressed_vertical.js +1 -0
  235. package/shim/blocks_compressed_horizontal-blockly_compressed_horizontal-messages.js +1 -0
  236. package/shim/blocks_compressed_horizontal.js +1 -0
  237. package/shim/blocks_compressed_vertical-blockly_compressed_vertical-messages.js +1 -0
  238. package/shim/blocks_compressed_vertical.js +1 -0
  239. package/shim/gh-pages.js +1 -0
  240. package/shim/horizontal.js +1 -0
  241. package/shim/index.js +17 -0
  242. package/shim/vertical.js +1 -0
  243. package/universal-python.js +82 -0
package/core/bubble.js ADDED
@@ -0,0 +1,664 @@
1
+ /**
2
+ * @license
3
+ * Visual Blocks Editor
4
+ *
5
+ * Copyright 2012 Google Inc.
6
+ * https://developers.google.com/blockly/
7
+ *
8
+ * Licensed under the Apache License, Version 2.0 (the "License");
9
+ * you may not use this file except in compliance with the License.
10
+ * You may obtain a copy of the License at
11
+ *
12
+ * http://www.apache.org/licenses/LICENSE-2.0
13
+ *
14
+ * Unless required by applicable law or agreed to in writing, software
15
+ * distributed under the License is distributed on an "AS IS" BASIS,
16
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ * See the License for the specific language governing permissions and
18
+ * limitations under the License.
19
+ */
20
+
21
+ /**
22
+ * @fileoverview Object representing a UI bubble.
23
+ * @author fraser@google.com (Neil Fraser)
24
+ */
25
+ 'use strict';
26
+
27
+ goog.provide('Blockly.Bubble');
28
+
29
+ goog.require('Blockly.Touch');
30
+ goog.require('Blockly.Workspace');
31
+ goog.require('goog.dom');
32
+ goog.require('goog.math.Coordinate');
33
+ goog.require('goog.userAgent');
34
+
35
+
36
+ /**
37
+ * Class for UI bubble.
38
+ * @param {!Blockly.WorkspaceSvg} workspace The workspace on which to draw the
39
+ * bubble.
40
+ * @param {!Element} content SVG content for the bubble.
41
+ * @param {Element} shape SVG element to avoid eclipsing.
42
+ * @param {!goog.math.Coordinate} anchorXY Absolute position of bubble's anchor
43
+ * point.
44
+ * @param {?number} bubbleWidth Width of bubble, or null if not resizable.
45
+ * @param {?number} bubbleHeight Height of bubble, or null if not resizable.
46
+ * @constructor
47
+ */
48
+ Blockly.Bubble = function(workspace, content, shape, anchorXY,
49
+ bubbleWidth, bubbleHeight) {
50
+ this.workspace_ = workspace;
51
+ this.content_ = content;
52
+ this.shape_ = shape;
53
+
54
+ var angle = Blockly.Bubble.ARROW_ANGLE;
55
+ if (this.workspace_.RTL) {
56
+ angle = -angle;
57
+ }
58
+ this.arrow_radians_ = Blockly.utils.toRadians(angle);
59
+
60
+ var canvas = workspace.getBubbleCanvas();
61
+ canvas.appendChild(this.createDom_(content, !!(bubbleWidth && bubbleHeight)));
62
+
63
+ this.setAnchorLocation(anchorXY);
64
+ if (!bubbleWidth || !bubbleHeight) {
65
+ var bBox = /** @type {SVGLocatable} */ (this.content_).getBBox();
66
+ bubbleWidth = bBox.width + 2 * Blockly.Bubble.BORDER_WIDTH;
67
+ bubbleHeight = bBox.height + 2 * Blockly.Bubble.BORDER_WIDTH;
68
+ }
69
+ this.setBubbleSize(bubbleWidth, bubbleHeight);
70
+
71
+ // Render the bubble.
72
+ this.positionBubble_();
73
+ this.renderArrow_();
74
+ this.rendered_ = true;
75
+
76
+ if (!workspace.options.readOnly) {
77
+ Blockly.bindEventWithChecks_(
78
+ this.bubbleBack_, 'mousedown', this, this.bubbleMouseDown_);
79
+ if (this.resizeGroup_) {
80
+ Blockly.bindEventWithChecks_(
81
+ this.resizeGroup_, 'mousedown', this, this.resizeMouseDown_);
82
+ }
83
+ }
84
+ };
85
+
86
+ /**
87
+ * Width of the border around the bubble.
88
+ */
89
+ Blockly.Bubble.BORDER_WIDTH = 6;
90
+
91
+ /**
92
+ * Determines the thickness of the base of the arrow in relation to the size
93
+ * of the bubble. Higher numbers result in thinner arrows.
94
+ */
95
+ Blockly.Bubble.ARROW_THICKNESS = 5;
96
+
97
+ /**
98
+ * The number of degrees that the arrow bends counter-clockwise.
99
+ */
100
+ Blockly.Bubble.ARROW_ANGLE = 20;
101
+
102
+ /**
103
+ * The sharpness of the arrow's bend. Higher numbers result in smoother arrows.
104
+ */
105
+ Blockly.Bubble.ARROW_BEND = 4;
106
+
107
+ /**
108
+ * Distance between arrow point and anchor point.
109
+ */
110
+ Blockly.Bubble.ANCHOR_RADIUS = 8;
111
+
112
+ /**
113
+ * Wrapper function called when a mouseUp occurs during a drag operation.
114
+ * @type {Array.<!Array>}
115
+ * @private
116
+ */
117
+ Blockly.Bubble.onMouseUpWrapper_ = null;
118
+
119
+ /**
120
+ * Wrapper function called when a mouseMove occurs during a drag operation.
121
+ * @type {Array.<!Array>}
122
+ * @private
123
+ */
124
+ Blockly.Bubble.onMouseMoveWrapper_ = null;
125
+
126
+ /**
127
+ * Function to call on resize of bubble.
128
+ * @type {Function}
129
+ */
130
+ Blockly.Bubble.prototype.resizeCallback_ = null;
131
+
132
+ /**
133
+ * Stop binding to the global mouseup and mousemove events.
134
+ * @private
135
+ */
136
+ Blockly.Bubble.unbindDragEvents_ = function() {
137
+ if (Blockly.Bubble.onMouseUpWrapper_) {
138
+ Blockly.unbindEvent_(Blockly.Bubble.onMouseUpWrapper_);
139
+ Blockly.Bubble.onMouseUpWrapper_ = null;
140
+ }
141
+ if (Blockly.Bubble.onMouseMoveWrapper_) {
142
+ Blockly.unbindEvent_(Blockly.Bubble.onMouseMoveWrapper_);
143
+ Blockly.Bubble.onMouseMoveWrapper_ = null;
144
+ }
145
+ };
146
+
147
+ /*
148
+ * Handle a mouse-up event while dragging a bubble's border or resize handle.
149
+ * @param {!Event} e Mouse up event.
150
+ * @private
151
+ */
152
+ Blockly.Bubble.bubbleMouseUp_ = function(/*e*/) {
153
+ Blockly.Touch.clearTouchIdentifier();
154
+ Blockly.Bubble.unbindDragEvents_();
155
+ };
156
+
157
+ /**
158
+ * Flag to stop incremental rendering during construction.
159
+ * @private
160
+ */
161
+ Blockly.Bubble.prototype.rendered_ = false;
162
+
163
+ /**
164
+ * Absolute coordinate of anchor point, in workspace coordinates.
165
+ * @type {goog.math.Coordinate}
166
+ * @private
167
+ */
168
+ Blockly.Bubble.prototype.anchorXY_ = null;
169
+
170
+ /**
171
+ * Relative X coordinate of bubble with respect to the anchor's centre,
172
+ * in workspace units.
173
+ * In RTL mode the initial value is negated.
174
+ * @private
175
+ */
176
+ Blockly.Bubble.prototype.relativeLeft_ = 0;
177
+
178
+ /**
179
+ * Relative Y coordinate of bubble with respect to the anchor's centre.
180
+ * @private
181
+ */
182
+ Blockly.Bubble.prototype.relativeTop_ = 0;
183
+
184
+ /**
185
+ * Width of bubble.
186
+ * @private
187
+ */
188
+ Blockly.Bubble.prototype.width_ = 0;
189
+
190
+ /**
191
+ * Height of bubble.
192
+ * @private
193
+ */
194
+ Blockly.Bubble.prototype.height_ = 0;
195
+
196
+ /**
197
+ * Automatically position and reposition the bubble.
198
+ * @private
199
+ */
200
+ Blockly.Bubble.prototype.autoLayout_ = true;
201
+
202
+ /**
203
+ * Create the bubble's DOM.
204
+ * @param {!Element} content SVG content for the bubble.
205
+ * @param {boolean} hasResize Add diagonal resize gripper if true.
206
+ * @return {!Element} The bubble's SVG group.
207
+ * @private
208
+ */
209
+ Blockly.Bubble.prototype.createDom_ = function(content, hasResize) {
210
+ /* Create the bubble. Here's the markup that will be generated:
211
+ <g>
212
+ <g filter="url(#blocklyEmbossFilter837493)">
213
+ <path d="... Z" />
214
+ <rect class="blocklyDraggable" rx="8" ry="8" width="180" height="180"/>
215
+ </g>
216
+ <g transform="translate(165, 165)" class="blocklyResizeSE">
217
+ <polygon points="0,15 15,15 15,0"/>
218
+ <line class="blocklyResizeLine" x1="5" y1="14" x2="14" y2="5"/>
219
+ <line class="blocklyResizeLine" x1="10" y1="14" x2="14" y2="10"/>
220
+ </g>
221
+ [...content goes here...]
222
+ </g>
223
+ */
224
+ this.bubbleGroup_ = Blockly.utils.createSvgElement('g', {}, null);
225
+ var filter =
226
+ {'filter': 'url(#' + this.workspace_.options.embossFilterId + ')'};
227
+ if (goog.userAgent.getUserAgentString().indexOf('JavaFX') != -1) {
228
+ // Multiple reports that JavaFX can't handle filters. UserAgent:
229
+ // Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.44
230
+ // (KHTML, like Gecko) JavaFX/8.0 Safari/537.44
231
+ // https://github.com/google/blockly/issues/99
232
+ filter = {};
233
+ }
234
+ var bubbleEmboss = Blockly.utils.createSvgElement('g',
235
+ filter, this.bubbleGroup_);
236
+ this.bubbleArrow_ = Blockly.utils.createSvgElement('path', {}, bubbleEmboss);
237
+ this.bubbleBack_ = Blockly.utils.createSvgElement('rect',
238
+ {
239
+ 'class': 'blocklyDraggable',
240
+ 'x': 0,
241
+ 'y': 0,
242
+ 'rx': Blockly.Bubble.BORDER_WIDTH,
243
+ 'ry': Blockly.Bubble.BORDER_WIDTH
244
+ },
245
+ bubbleEmboss);
246
+ if (hasResize) {
247
+ this.resizeGroup_ = Blockly.utils.createSvgElement('g',
248
+ {'class': this.workspace_.RTL ?
249
+ 'blocklyResizeSW' : 'blocklyResizeSE'},
250
+ this.bubbleGroup_);
251
+ var resizeSize = 2 * Blockly.Bubble.BORDER_WIDTH;
252
+ Blockly.utils.createSvgElement('polygon',
253
+ {'points': '0,x x,x x,0'.replace(/x/g, resizeSize.toString())},
254
+ this.resizeGroup_);
255
+ Blockly.utils.createSvgElement('line',
256
+ {
257
+ 'class': 'blocklyResizeLine',
258
+ 'x1': resizeSize / 3, 'y1': resizeSize - 1,
259
+ 'x2': resizeSize - 1, 'y2': resizeSize / 3
260
+ }, this.resizeGroup_);
261
+ Blockly.utils.createSvgElement('line',
262
+ {
263
+ 'class': 'blocklyResizeLine',
264
+ 'x1': resizeSize * 2 / 3,
265
+ 'y1': resizeSize - 1,
266
+ 'x2': resizeSize - 1,
267
+ 'y2': resizeSize * 2 / 3
268
+ }, this.resizeGroup_);
269
+ } else {
270
+ this.resizeGroup_ = null;
271
+ }
272
+ this.bubbleGroup_.appendChild(content);
273
+ return this.bubbleGroup_;
274
+ };
275
+
276
+ /**
277
+ * Return the root node of the bubble's SVG group.
278
+ * @return {Element} The root SVG node of the bubble's group.
279
+ */
280
+ Blockly.Bubble.prototype.getSvgRoot = function() {
281
+ return this.bubbleGroup_;
282
+ };
283
+
284
+ /**
285
+ * Expose the block's ID on the bubble's top-level SVG group.
286
+ * @param {string} id ID of block.
287
+ */
288
+ Blockly.Bubble.prototype.setSvgId = function(id) {
289
+ if (this.bubbleGroup_.dataset) {
290
+ this.bubbleGroup_.dataset.blockId = id;
291
+ }
292
+ };
293
+
294
+ /**
295
+ * Handle a mouse-down on bubble's border.
296
+ * @param {!Event} e Mouse down event.
297
+ * @private
298
+ */
299
+ Blockly.Bubble.prototype.bubbleMouseDown_ = function(e) {
300
+ var gesture = this.workspace_.getGesture(e);
301
+ if (gesture) {
302
+ gesture.handleBubbleStart(e, this);
303
+ }
304
+ };
305
+
306
+ /**
307
+ * Show the context menu for this bubble.
308
+ * @param {!Event} _e Mouse event.
309
+ * @private
310
+ */
311
+ Blockly.Bubble.prototype.showContextMenu_ = function(_e) {
312
+ // NOP on bubbles, but used by the bubble dragger to pass events to
313
+ // workspace comments.
314
+ };
315
+
316
+ /**
317
+ * Get whether this bubble is deletable or not.
318
+ * @return {boolean} True if deletable.
319
+ * @package
320
+ */
321
+ Blockly.Bubble.prototype.isDeletable = function() {
322
+ return false;
323
+ };
324
+
325
+ /**
326
+ * Handle a mouse-down on bubble's resize corner.
327
+ * @param {!Event} e Mouse down event.
328
+ * @private
329
+ */
330
+ Blockly.Bubble.prototype.resizeMouseDown_ = function(e) {
331
+ this.promote_();
332
+ Blockly.Bubble.unbindDragEvents_();
333
+ if (Blockly.utils.isRightButton(e)) {
334
+ // No right-click.
335
+ e.stopPropagation();
336
+ return;
337
+ }
338
+ // Left-click (or middle click)
339
+ this.workspace_.startDrag(e, new goog.math.Coordinate(
340
+ this.workspace_.RTL ? -this.width_ : this.width_, this.height_));
341
+
342
+ Blockly.Bubble.onMouseUpWrapper_ = Blockly.bindEventWithChecks_(document,
343
+ 'mouseup', this, Blockly.Bubble.bubbleMouseUp_);
344
+ Blockly.Bubble.onMouseMoveWrapper_ = Blockly.bindEventWithChecks_(document,
345
+ 'mousemove', this, this.resizeMouseMove_);
346
+ Blockly.hideChaff();
347
+ // This event has been handled. No need to bubble up to the document.
348
+ e.stopPropagation();
349
+ };
350
+
351
+ /**
352
+ * Resize this bubble to follow the mouse.
353
+ * @param {!Event} e Mouse move event.
354
+ * @private
355
+ */
356
+ Blockly.Bubble.prototype.resizeMouseMove_ = function(e) {
357
+ this.autoLayout_ = false;
358
+ var newXY = this.workspace_.moveDrag(e);
359
+ this.setBubbleSize(this.workspace_.RTL ? -newXY.x : newXY.x, newXY.y);
360
+ if (this.workspace_.RTL) {
361
+ // RTL requires the bubble to move its left edge.
362
+ this.positionBubble_();
363
+ }
364
+ };
365
+
366
+ /**
367
+ * Register a function as a callback event for when the bubble is resized.
368
+ * @param {!Function} callback The function to call on resize.
369
+ */
370
+ Blockly.Bubble.prototype.registerResizeEvent = function(callback) {
371
+ this.resizeCallback_ = callback;
372
+ };
373
+
374
+ /**
375
+ * Move this bubble to the top of the stack.
376
+ * @return {!boolean} Whether or not the bubble has been moved.
377
+ * @private
378
+ */
379
+ Blockly.Bubble.prototype.promote_ = function() {
380
+ var svgGroup = this.bubbleGroup_.parentNode;
381
+ if (svgGroup.lastChild !== this.bubbleGroup_) {
382
+ svgGroup.appendChild(this.bubbleGroup_);
383
+ return true;
384
+ }
385
+ return false;
386
+ };
387
+
388
+ /**
389
+ * Notification that the anchor has moved.
390
+ * Update the arrow and bubble accordingly.
391
+ * @param {!goog.math.Coordinate} xy Absolute location.
392
+ */
393
+ Blockly.Bubble.prototype.setAnchorLocation = function(xy) {
394
+ this.anchorXY_ = xy;
395
+ if (this.rendered_) {
396
+ this.positionBubble_();
397
+ }
398
+ };
399
+
400
+ /**
401
+ * Position the bubble so that it does not fall off-screen.
402
+ * @private
403
+ */
404
+ Blockly.Bubble.prototype.layoutBubble_ = function() {
405
+ // Compute the preferred bubble location.
406
+ var relativeLeft = -this.width_ / 4;
407
+ var relativeTop = -this.height_ - Blockly.BlockSvg.MIN_BLOCK_Y;
408
+ // Prevent the bubble from being off-screen.
409
+ var metrics = this.workspace_.getMetrics();
410
+ metrics.viewWidth /= this.workspace_.scale;
411
+ metrics.viewLeft /= this.workspace_.scale;
412
+ var anchorX = this.anchorXY_.x;
413
+ if (this.workspace_.RTL) {
414
+ if (anchorX - metrics.viewLeft - relativeLeft - this.width_ <
415
+ Blockly.Scrollbar.scrollbarThickness) {
416
+ // Slide the bubble right until it is onscreen.
417
+ relativeLeft = anchorX - metrics.viewLeft - this.width_ -
418
+ Blockly.Scrollbar.scrollbarThickness;
419
+ } else if (anchorX - metrics.viewLeft - relativeLeft >
420
+ metrics.viewWidth) {
421
+ // Slide the bubble left until it is onscreen.
422
+ relativeLeft = anchorX - metrics.viewLeft - metrics.viewWidth;
423
+ }
424
+ } else {
425
+ if (anchorX + relativeLeft < metrics.viewLeft) {
426
+ // Slide the bubble right until it is onscreen.
427
+ relativeLeft = metrics.viewLeft - anchorX;
428
+ } else if (metrics.viewLeft + metrics.viewWidth <
429
+ anchorX + relativeLeft + this.width_ +
430
+ Blockly.BlockSvg.SEP_SPACE_X +
431
+ Blockly.Scrollbar.scrollbarThickness) {
432
+ // Slide the bubble left until it is onscreen.
433
+ relativeLeft = metrics.viewLeft + metrics.viewWidth - anchorX -
434
+ this.width_ - Blockly.Scrollbar.scrollbarThickness;
435
+ }
436
+ }
437
+ if (this.anchorXY_.y + relativeTop < metrics.viewTop) {
438
+ // Slide the bubble below the block.
439
+ var bBox = /** @type {SVGLocatable} */ (this.shape_).getBBox();
440
+ relativeTop = bBox.height;
441
+ }
442
+ this.relativeLeft_ = relativeLeft;
443
+ this.relativeTop_ = relativeTop;
444
+ };
445
+
446
+ /**
447
+ * Move the bubble to a location relative to the anchor's centre.
448
+ * @private
449
+ */
450
+ Blockly.Bubble.prototype.positionBubble_ = function() {
451
+ var left = this.anchorXY_.x;
452
+ if (this.workspace_.RTL) {
453
+ left -= this.relativeLeft_ ;
454
+ } else {
455
+ left += this.relativeLeft_;
456
+ }
457
+ var top = this.relativeTop_ + this.anchorXY_.y;
458
+ this.moveTo(left, top);
459
+ };
460
+
461
+ /**
462
+ * Move the bubble group to the specified location in workspace coordinates.
463
+ * @param {number} x The x position to move to.
464
+ * @param {number} y The y position to move to.
465
+ * @package
466
+ */
467
+ Blockly.Bubble.prototype.moveTo = function(x, y) {
468
+ this.bubbleGroup_.setAttribute('transform', 'translate(' + x + ',' + y + ')');
469
+ };
470
+
471
+ /**
472
+ * Get the dimensions of this bubble.
473
+ * @return {!Object} Object with width and height properties.
474
+ */
475
+ Blockly.Bubble.prototype.getBubbleSize = function() {
476
+ return {width: this.width_, height: this.height_};
477
+ };
478
+
479
+ /**
480
+ * Size this bubble.
481
+ * @param {number} width Width of the bubble.
482
+ * @param {number} height Height of the bubble.
483
+ */
484
+ Blockly.Bubble.prototype.setBubbleSize = function(width, height) {
485
+ var doubleBorderWidth = 2 * Blockly.Bubble.BORDER_WIDTH;
486
+ // Minimum size of a bubble.
487
+ width = Math.max(width, doubleBorderWidth + 45);
488
+ height = Math.max(height, doubleBorderWidth + 20);
489
+ this.width_ = width;
490
+ this.height_ = height;
491
+ this.bubbleBack_.setAttribute('width', width);
492
+ this.bubbleBack_.setAttribute('height', height);
493
+ if (this.resizeGroup_) {
494
+ if (this.workspace_.RTL) {
495
+ // Mirror the resize group.
496
+ var resizeSize = 2 * Blockly.Bubble.BORDER_WIDTH;
497
+ this.resizeGroup_.setAttribute('transform', 'translate(' +
498
+ resizeSize + ',' + (height - doubleBorderWidth) + ') scale(-1 1)');
499
+ } else {
500
+ this.resizeGroup_.setAttribute('transform', 'translate(' +
501
+ (width - doubleBorderWidth) + ',' +
502
+ (height - doubleBorderWidth) + ')');
503
+ }
504
+ }
505
+ if (this.rendered_) {
506
+ if (this.autoLayout_) {
507
+ this.layoutBubble_();
508
+ }
509
+ this.positionBubble_();
510
+ this.renderArrow_();
511
+ }
512
+ // Allow the contents to resize.
513
+ if (this.resizeCallback_) {
514
+ this.resizeCallback_();
515
+ }
516
+ };
517
+
518
+ /**
519
+ * Draw the arrow between the bubble and the origin.
520
+ * @private
521
+ */
522
+ Blockly.Bubble.prototype.renderArrow_ = function() {
523
+ var steps = [];
524
+ // Find the relative coordinates of the center of the bubble.
525
+ var relBubbleX = this.width_ / 2;
526
+ var relBubbleY = this.height_ / 2;
527
+ // Find the relative coordinates of the center of the anchor.
528
+ var relAnchorX = -this.relativeLeft_;
529
+ var relAnchorY = -this.relativeTop_;
530
+ if (relBubbleX == relAnchorX && relBubbleY == relAnchorY) {
531
+ // Null case. Bubble is directly on top of the anchor.
532
+ // Short circuit this rather than wade through divide by zeros.
533
+ steps.push('M ' + relBubbleX + ',' + relBubbleY);
534
+ } else {
535
+ // Compute the angle of the arrow's line.
536
+ var rise = relAnchorY - relBubbleY;
537
+ var run = relAnchorX - relBubbleX;
538
+ if (this.workspace_.RTL) {
539
+ run *= -1;
540
+ }
541
+ var hypotenuse = Math.sqrt(rise * rise + run * run);
542
+ var angle = Math.acos(run / hypotenuse);
543
+ if (rise < 0) {
544
+ angle = 2 * Math.PI - angle;
545
+ }
546
+ // Compute a line perpendicular to the arrow.
547
+ var rightAngle = angle + Math.PI / 2;
548
+ if (rightAngle > Math.PI * 2) {
549
+ rightAngle -= Math.PI * 2;
550
+ }
551
+ var rightRise = Math.sin(rightAngle);
552
+ var rightRun = Math.cos(rightAngle);
553
+
554
+ // Calculate the thickness of the base of the arrow.
555
+ var bubbleSize = this.getBubbleSize();
556
+ var thickness = (bubbleSize.width + bubbleSize.height) /
557
+ Blockly.Bubble.ARROW_THICKNESS;
558
+ thickness = Math.min(thickness, bubbleSize.width, bubbleSize.height) / 4;
559
+
560
+ // Back the tip of the arrow off of the anchor.
561
+ var backoffRatio = 1 - Blockly.Bubble.ANCHOR_RADIUS / hypotenuse;
562
+ relAnchorX = relBubbleX + backoffRatio * run;
563
+ relAnchorY = relBubbleY + backoffRatio * rise;
564
+
565
+ // Coordinates for the base of the arrow.
566
+ var baseX1 = relBubbleX + thickness * rightRun;
567
+ var baseY1 = relBubbleY + thickness * rightRise;
568
+ var baseX2 = relBubbleX - thickness * rightRun;
569
+ var baseY2 = relBubbleY - thickness * rightRise;
570
+
571
+ // Distortion to curve the arrow.
572
+ var swirlAngle = angle + this.arrow_radians_;
573
+ if (swirlAngle > Math.PI * 2) {
574
+ swirlAngle -= Math.PI * 2;
575
+ }
576
+ var swirlRise = Math.sin(swirlAngle) *
577
+ hypotenuse / Blockly.Bubble.ARROW_BEND;
578
+ var swirlRun = Math.cos(swirlAngle) *
579
+ hypotenuse / Blockly.Bubble.ARROW_BEND;
580
+
581
+ steps.push('M' + baseX1 + ',' + baseY1);
582
+ steps.push('C' + (baseX1 + swirlRun) + ',' + (baseY1 + swirlRise) +
583
+ ' ' + relAnchorX + ',' + relAnchorY +
584
+ ' ' + relAnchorX + ',' + relAnchorY);
585
+ steps.push('C' + relAnchorX + ',' + relAnchorY +
586
+ ' ' + (baseX2 + swirlRun) + ',' + (baseY2 + swirlRise) +
587
+ ' ' + baseX2 + ',' + baseY2);
588
+ }
589
+ steps.push('z');
590
+ this.bubbleArrow_.setAttribute('d', steps.join(' '));
591
+ };
592
+
593
+ /**
594
+ * Change the colour of a bubble.
595
+ * @param {string} hexColour Hex code of colour.
596
+ */
597
+ Blockly.Bubble.prototype.setColour = function(hexColour) {
598
+ this.bubbleBack_.setAttribute('fill', hexColour);
599
+ this.bubbleArrow_.setAttribute('fill', hexColour);
600
+ };
601
+
602
+ /**
603
+ * Dispose of this bubble.
604
+ */
605
+ Blockly.Bubble.prototype.dispose = function() {
606
+ Blockly.Bubble.unbindDragEvents_();
607
+ // Dispose of and unlink the bubble.
608
+ goog.dom.removeNode(this.bubbleGroup_);
609
+ this.bubbleGroup_ = null;
610
+ this.bubbleArrow_ = null;
611
+ this.bubbleBack_ = null;
612
+ this.resizeGroup_ = null;
613
+ this.workspace_ = null;
614
+ this.content_ = null;
615
+ this.shape_ = null;
616
+ };
617
+
618
+ /**
619
+ * Move this bubble during a drag, taking into account whether or not there is
620
+ * a drag surface.
621
+ * @param {?Blockly.BlockDragSurfaceSvg} dragSurface The surface that carries
622
+ * rendered items during a drag, or null if no drag surface is in use.
623
+ * @param {!goog.math.Coordinate} newLoc The location to translate to, in
624
+ * workspace coordinates.
625
+ * @package
626
+ */
627
+ Blockly.Bubble.prototype.moveDuringDrag = function(dragSurface, newLoc) {
628
+ if (dragSurface) {
629
+ dragSurface.translateSurface(newLoc.x, newLoc.y);
630
+ } else {
631
+ this.moveTo(newLoc.x, newLoc.y);
632
+ }
633
+ if (this.workspace_.RTL) {
634
+ this.relativeLeft_ = this.anchorXY_.x - newLoc.x - this.width_;
635
+ } else {
636
+ this.relativeLeft_ = newLoc.x - this.anchorXY_.x;
637
+ }
638
+ this.relativeTop_ = newLoc.y - this.anchorXY_.y;
639
+ this.renderArrow_();
640
+ };
641
+
642
+ /**
643
+ * Return the coordinates of the top corner of this bubble's starting edge (e.g.
644
+ * top left corner in LTR and top right corner in RTL) relative
645
+ * to the drawing surface's origin (0,0), in workspace units.
646
+ * @return {!goog.math.Coordinate} Object with .x and .y properties.
647
+ */
648
+ Blockly.Bubble.prototype.getRelativeToSurfaceXY = function() {
649
+ return new goog.math.Coordinate(
650
+ this.workspace_.RTL ? this.anchorXY_.x - this.relativeLeft_ : this.anchorXY_.x + this.relativeLeft_,
651
+ this.anchorXY_.y + this.relativeTop_);
652
+ };
653
+
654
+ /**
655
+ * Set whether auto-layout of this bubble is enabled. The first time a bubble
656
+ * is shown it positions itself to not cover any blocks. Once a user has
657
+ * dragged it to reposition, it renders where the user put it.
658
+ * @param {boolean} enable True if auto-layout should be enabled, false
659
+ * otherwise.
660
+ * @package
661
+ */
662
+ Blockly.Bubble.prototype.setAutoLayout = function(enable) {
663
+ this.autoLayout_ = enable;
664
+ };