@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
@@ -0,0 +1,776 @@
1
+ /**
2
+ * @license
3
+ * Visual Blocks Editor
4
+ *
5
+ * Copyright 2011 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 Components for creating connections between blocks.
23
+ * @author fraser@google.com (Neil Fraser)
24
+ */
25
+ 'use strict';
26
+
27
+ goog.provide('Blockly.Connection');
28
+
29
+ goog.require('Blockly.Events.BlockMove');
30
+
31
+ goog.require('goog.asserts');
32
+ goog.require('goog.dom');
33
+
34
+
35
+ /**
36
+ * Class for a connection between blocks.
37
+ * @param {!Blockly.Block} source The block establishing this connection.
38
+ * @param {number} type The type of the connection.
39
+ * @constructor
40
+ */
41
+ Blockly.Connection = function(source, type) {
42
+ /**
43
+ * @type {!Blockly.Block}
44
+ * @protected
45
+ */
46
+ this.sourceBlock_ = source;
47
+ /** @type {number} */
48
+ this.type = type;
49
+ // Shortcut for the databases for this connection's workspace.
50
+ if (source.workspace.connectionDBList) {
51
+ this.db_ = source.workspace.connectionDBList[type];
52
+ this.dbOpposite_ =
53
+ source.workspace.connectionDBList[Blockly.OPPOSITE_TYPE[type]];
54
+ this.hidden_ = !this.db_;
55
+ }
56
+ };
57
+
58
+ /**
59
+ * Constants for checking whether two connections are compatible.
60
+ */
61
+ Blockly.Connection.CAN_CONNECT = 0;
62
+ Blockly.Connection.REASON_SELF_CONNECTION = 1;
63
+ Blockly.Connection.REASON_WRONG_TYPE = 2;
64
+ Blockly.Connection.REASON_TARGET_NULL = 3;
65
+ Blockly.Connection.REASON_CHECKS_FAILED = 4;
66
+ Blockly.Connection.REASON_DIFFERENT_WORKSPACES = 5;
67
+ Blockly.Connection.REASON_SHADOW_PARENT = 6;
68
+ // Fixes #1127, but may be the wrong solution.
69
+ Blockly.Connection.REASON_CUSTOM_PROCEDURE = 7;
70
+
71
+ /**
72
+ * Connection this connection connects to. Null if not connected.
73
+ * @type {Blockly.Connection}
74
+ */
75
+ Blockly.Connection.prototype.targetConnection = null;
76
+
77
+ /**
78
+ * List of compatible value types. Null if all types are compatible.
79
+ * @type {Array}
80
+ * @private
81
+ */
82
+ Blockly.Connection.prototype.check_ = null;
83
+
84
+ /**
85
+ * DOM representation of a shadow block, or null if none.
86
+ * @type {Element}
87
+ * @private
88
+ */
89
+ Blockly.Connection.prototype.shadowDom_ = null;
90
+
91
+ /**
92
+ * Horizontal location of this connection.
93
+ * @type {number}
94
+ * @protected
95
+ */
96
+ Blockly.Connection.prototype.x_ = 0;
97
+
98
+ /**
99
+ * Vertical location of this connection.
100
+ * @type {number}
101
+ * @protected
102
+ */
103
+ Blockly.Connection.prototype.y_ = 0;
104
+
105
+ /**
106
+ * Has this connection been added to the connection database?
107
+ * @type {boolean}
108
+ * @protected
109
+ */
110
+ Blockly.Connection.prototype.inDB_ = false;
111
+
112
+ /**
113
+ * Connection database for connections of this type on the current workspace.
114
+ * @type {Blockly.ConnectionDB}
115
+ * @protected
116
+ */
117
+ Blockly.Connection.prototype.db_ = null;
118
+
119
+ /**
120
+ * Connection database for connections compatible with this type on the
121
+ * current workspace.
122
+ * @type {Blockly.ConnectionDB}
123
+ * @protected
124
+ */
125
+ Blockly.Connection.prototype.dbOpposite_ = null;
126
+
127
+ /**
128
+ * Whether this connections is hidden (not tracked in a database) or not.
129
+ * @type {boolean}
130
+ * @protected
131
+ */
132
+ Blockly.Connection.prototype.hidden_ = null;
133
+
134
+ /**
135
+ * Connect two connections together. This is the connection on the superior
136
+ * block.
137
+ * @param {!Blockly.Connection} childConnection Connection on inferior block.
138
+ * @protected
139
+ */
140
+ Blockly.Connection.prototype.connect_ = function(childConnection) {
141
+ var parentConnection = this;
142
+ var parentBlock = parentConnection.getSourceBlock();
143
+ var childBlock = childConnection.getSourceBlock();
144
+ var isSurroundingC = false;
145
+ if (parentConnection == parentBlock.getFirstStatementConnection()) {
146
+ isSurroundingC = true;
147
+ }
148
+
149
+ if (Blockly.Events.isEnabled() && !childBlock.isInsertionMarker()) {
150
+ childBlock.workspace.procedureReturnsWillChange();
151
+ }
152
+
153
+ // Disconnect any existing parent on the child connection.
154
+ if (childConnection.isConnected()) {
155
+ // Scratch-specific behaviour:
156
+ // If we're using a c-shaped block to surround a stack, remember where the
157
+ // stack used to be connected.
158
+ if (isSurroundingC) {
159
+ var previousParentConnection = childConnection.targetConnection;
160
+ }
161
+ childConnection.disconnect();
162
+ }
163
+ if (parentConnection.isConnected()) {
164
+ // Other connection is already connected to something.
165
+ // Disconnect it and reattach it or bump it as needed.
166
+ var orphanBlock = parentConnection.targetBlock();
167
+ var shadowDom = parentConnection.getShadowDom();
168
+ // Temporarily set the shadow DOM to null so it does not respawn.
169
+ parentConnection.setShadowDom(null);
170
+ // Displaced shadow blocks dissolve rather than reattaching or bumping.
171
+ if (orphanBlock.isShadow()) {
172
+ // Save the shadow block so that field values are preserved.
173
+ shadowDom = Blockly.Xml.blockToDom(orphanBlock);
174
+ orphanBlock.dispose();
175
+ orphanBlock = null;
176
+ } else if (parentConnection.type == Blockly.NEXT_STATEMENT) {
177
+ // Statement connections.
178
+ // Statement blocks may be inserted into the middle of a stack.
179
+ // Split the stack.
180
+ if (!orphanBlock.previousConnection) {
181
+ throw 'Orphan block does not have a previous connection.';
182
+ }
183
+ // Attempt to reattach the orphan at the bottom of the newly inserted
184
+ // block. Since this block may be a stack, walk down to the end.
185
+ var newBlock = childBlock;
186
+ while (newBlock.nextConnection) {
187
+ var nextBlock = newBlock.getNextBlock();
188
+ if (nextBlock && !nextBlock.isShadow()) {
189
+ newBlock = nextBlock;
190
+ } else {
191
+ if (orphanBlock.previousConnection.checkType_(
192
+ newBlock.nextConnection)) {
193
+ newBlock.nextConnection.connect(orphanBlock.previousConnection);
194
+ orphanBlock = null;
195
+ }
196
+ break;
197
+ }
198
+ }
199
+ }
200
+ if (orphanBlock) {
201
+ // Unable to reattach orphan.
202
+ parentConnection.disconnect();
203
+ if (Blockly.Events.recordUndo) {
204
+ // Bump it off to the side after a moment.
205
+ var group = Blockly.Events.getGroup();
206
+ setTimeout(function() {
207
+ // Verify orphan hasn't been deleted or reconnected (user on meth).
208
+ if (orphanBlock.workspace && !orphanBlock.getParent()) {
209
+ Blockly.Events.setGroup(group);
210
+ if (orphanBlock.outputConnection) {
211
+ orphanBlock.outputConnection.bumpAwayFrom_(parentConnection);
212
+ } else if (orphanBlock.previousConnection) {
213
+ orphanBlock.previousConnection.bumpAwayFrom_(parentConnection);
214
+ }
215
+ Blockly.Events.setGroup(false);
216
+ }
217
+ }, Blockly.BUMP_DELAY);
218
+ }
219
+ }
220
+ // Restore the shadow DOM.
221
+ parentConnection.setShadowDom(shadowDom);
222
+ }
223
+
224
+ if (isSurroundingC && previousParentConnection) {
225
+ previousParentConnection.connect(parentBlock.previousConnection);
226
+ }
227
+
228
+ var event;
229
+ if (Blockly.Events.isEnabled()) {
230
+ event = new Blockly.Events.BlockMove(childBlock);
231
+ }
232
+ // Establish the connections.
233
+ Blockly.Connection.connectReciprocally_(parentConnection, childConnection);
234
+ // Demote the inferior block so that one is a child of the superior one.
235
+ childBlock.setParent(parentBlock);
236
+ if (event) {
237
+ event.recordNew();
238
+ Blockly.Events.fire(event);
239
+ }
240
+ };
241
+
242
+ /**
243
+ * Sever all links to this connection (not including from the source object).
244
+ */
245
+ Blockly.Connection.prototype.dispose = function() {
246
+ if (this.isConnected()) {
247
+ throw 'Disconnect connection before disposing of it.';
248
+ }
249
+ if (this.inDB_) {
250
+ this.db_.removeConnection_(this);
251
+ }
252
+ this.db_ = null;
253
+ this.dbOpposite_ = null;
254
+ };
255
+
256
+ /**
257
+ * @return {boolean} true if the connection is not connected or is connected to
258
+ * an insertion marker, false otherwise.
259
+ */
260
+ Blockly.Connection.prototype.isConnectedToNonInsertionMarker = function() {
261
+ return this.targetConnection && !this.targetBlock().isInsertionMarker();
262
+ };
263
+
264
+ /**
265
+ * Get the source block for this connection.
266
+ * @return {Blockly.Block} The source block, or null if there is none.
267
+ */
268
+ Blockly.Connection.prototype.getSourceBlock = function() {
269
+ return this.sourceBlock_;
270
+ };
271
+
272
+ /**
273
+ * Does the connection belong to a superior block (higher in the source stack)?
274
+ * @return {boolean} True if connection faces down or right.
275
+ */
276
+ Blockly.Connection.prototype.isSuperior = function() {
277
+ return this.type == Blockly.INPUT_VALUE ||
278
+ this.type == Blockly.NEXT_STATEMENT;
279
+ };
280
+
281
+ /**
282
+ * Is the connection connected?
283
+ * @return {boolean} True if connection is connected to another connection.
284
+ */
285
+ Blockly.Connection.prototype.isConnected = function() {
286
+ return !!this.targetConnection;
287
+ };
288
+
289
+ /**
290
+ * Checks whether the current connection can connect with the target
291
+ * connection.
292
+ * @param {Blockly.Connection} target Connection to check compatibility with.
293
+ * @return {number} Blockly.Connection.CAN_CONNECT if the connection is legal,
294
+ * an error code otherwise.
295
+ * @private
296
+ */
297
+ Blockly.Connection.prototype.canConnectWithReason_ = function(target) {
298
+ if (!target) {
299
+ return Blockly.Connection.REASON_TARGET_NULL;
300
+ }
301
+ if (this.isSuperior()) {
302
+ var blockA = this.sourceBlock_;
303
+ var blockB = target.getSourceBlock();
304
+ var superiorConn = this;
305
+ } else {
306
+ var blockB = this.sourceBlock_;
307
+ var blockA = target.getSourceBlock();
308
+ var superiorConn = target;
309
+ }
310
+ if (blockA && blockA == blockB) {
311
+ return Blockly.Connection.REASON_SELF_CONNECTION;
312
+ } else if (target.type != Blockly.OPPOSITE_TYPE[this.type]) {
313
+ return Blockly.Connection.REASON_WRONG_TYPE;
314
+ } else if (blockA && blockB && blockA.workspace !== blockB.workspace) {
315
+ return Blockly.Connection.REASON_DIFFERENT_WORKSPACES;
316
+ } else if (!this.checkType_(target)) {
317
+ return Blockly.Connection.REASON_CHECKS_FAILED;
318
+ } else if (blockA.isShadow() && !blockB.isShadow()) {
319
+ return Blockly.Connection.REASON_SHADOW_PARENT;
320
+ } else if ((blockA.type == Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE &&
321
+ blockB.type != Blockly.PROCEDURES_PROTOTYPE_BLOCK_TYPE &&
322
+ superiorConn == blockA.getInput('custom_block').connection) ||
323
+ (blockB.type == Blockly.PROCEDURES_PROTOTYPE_BLOCK_TYPE &&
324
+ blockA.type != Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE)) {
325
+ // Hack to fix #1127: Fail attempts to connect to the custom_block input
326
+ // on a defnoreturn block, unless the connecting block is a specific type.
327
+ // And hack to fix #1534: Fail attempts to connect anything but a
328
+ // defnoreturn block to a prototype block.
329
+ return Blockly.Connection.REASON_CUSTOM_PROCEDURE;
330
+ }
331
+ return Blockly.Connection.CAN_CONNECT;
332
+ };
333
+
334
+ /**
335
+ * Checks whether the current connection and target connection are compatible
336
+ * and throws an exception if they are not.
337
+ * @param {Blockly.Connection} target The connection to check compatibility
338
+ * with.
339
+ * @private
340
+ */
341
+ Blockly.Connection.prototype.checkConnection_ = function(target) {
342
+ switch (this.canConnectWithReason_(target)) {
343
+ case Blockly.Connection.CAN_CONNECT:
344
+ break;
345
+ case Blockly.Connection.REASON_SELF_CONNECTION:
346
+ throw 'Attempted to connect a block to itself.';
347
+ case Blockly.Connection.REASON_DIFFERENT_WORKSPACES:
348
+ // Usually this means one block has been deleted.
349
+ throw 'Blocks not on same workspace.';
350
+ case Blockly.Connection.REASON_WRONG_TYPE:
351
+ throw 'Attempt to connect incompatible types.';
352
+ case Blockly.Connection.REASON_TARGET_NULL:
353
+ throw 'Target connection is null.';
354
+ case Blockly.Connection.REASON_CHECKS_FAILED:
355
+ var msg = 'Connection checks failed. ';
356
+ msg += this + ' expected ' + this.check_ + ', found ' + target.check_;
357
+ throw msg;
358
+ case Blockly.Connection.REASON_SHADOW_PARENT:
359
+ throw 'Connecting non-shadow to shadow block.';
360
+ case Blockly.Connection.REASON_CUSTOM_PROCEDURE:
361
+ throw 'Trying to replace a shadow on a custom procedure definition.';
362
+ default:
363
+ throw 'Unknown connection failure: this should never happen!';
364
+ }
365
+ };
366
+
367
+ /**
368
+ * Check if the two connections can be dragged to connect to each other.
369
+ * This is used by the connection database when searching for the closest
370
+ * connection.
371
+ * @param {!Blockly.Connection} candidate A nearby connection to check, which
372
+ * must be a previous connection.
373
+ * @return {boolean} True if the connection is allowed, false otherwise.
374
+ */
375
+ Blockly.Connection.prototype.canConnectToPrevious_ = function(candidate) {
376
+ if (this.targetConnection) {
377
+ // This connection is already occupied.
378
+ // A next connection will never disconnect itself mid-drag.
379
+ return false;
380
+ }
381
+
382
+ // Don't let blocks try to connect to themselves or ones they nest.
383
+ if (Blockly.draggingConnections_.indexOf(candidate) != -1) {
384
+ return false;
385
+ }
386
+
387
+ var firstStatementConnection =
388
+ this.sourceBlock_.getFirstStatementConnection();
389
+ // Is it a C-shaped (e.g. repeat) or E-shaped (e.g. if-else) block?
390
+ var isComplexStatement = firstStatementConnection != null;
391
+ var isFirstStatementConnection = this == firstStatementConnection;
392
+ var isNextConnection = this == this.sourceBlock_.nextConnection;
393
+
394
+ // Scratch-specific behaviour: can connect to the first statement input of a
395
+ // C-shaped or E-shaped block, or to the next connection of any statement
396
+ // block, but not to the second statement input of an E-shaped block.
397
+ if (isComplexStatement && !isFirstStatementConnection && !isNextConnection) {
398
+ return false;
399
+ }
400
+
401
+ // Complex blocks with no previous connection will not be allowed to connect
402
+ // mid-stack.
403
+ var sourceHasPreviousConn = this.sourceBlock_.previousConnection != null;
404
+
405
+ if (isFirstStatementConnection && sourceHasPreviousConn) {
406
+ return true;
407
+ }
408
+
409
+ if (isNextConnection ||
410
+ (isFirstStatementConnection && !sourceHasPreviousConn)) {
411
+ // If the candidate is the first connection in a stack, we can connect.
412
+ if (!candidate.targetConnection) {
413
+ return true;
414
+ }
415
+
416
+ var targetBlock = candidate.targetBlock();
417
+ // If it is connected a real block, game over.
418
+ if (!targetBlock.isInsertionMarker()) {
419
+ return false;
420
+ }
421
+ // If it's connected to an insertion marker but that insertion marker
422
+ // is the first block in a stack, it's still fine. If that insertion
423
+ // marker is in the middle of a stack, it won't work.
424
+ return !targetBlock.getPreviousBlock();
425
+ }
426
+ };
427
+
428
+ /**
429
+ * Check if the two connections can be dragged to connect to each other.
430
+ * This is used by the connection database when searching for the closest
431
+ * connection.
432
+ * @param {!Blockly.Connection} candidate A nearby connection to check.
433
+ * @return {boolean} True if the connection is allowed, false otherwise.
434
+ */
435
+ Blockly.Connection.prototype.isConnectionAllowed = function(candidate) {
436
+
437
+ // Don't consider insertion markers.
438
+ if (candidate.sourceBlock_.isInsertionMarker()) {
439
+ return false;
440
+ }
441
+
442
+ // Type checking.
443
+ var canConnect = this.canConnectWithReason_(candidate);
444
+ if (canConnect != Blockly.Connection.CAN_CONNECT) {
445
+ return false;
446
+ }
447
+
448
+ var firstStatementConnection =
449
+ this.sourceBlock_.getFirstStatementConnection();
450
+ switch (candidate.type) {
451
+ case Blockly.PREVIOUS_STATEMENT:
452
+ return this.canConnectToPrevious_(candidate);
453
+ case Blockly.OUTPUT_VALUE: {
454
+ // Can't drag an input to an output--you have to move the inferior block.
455
+ return false;
456
+ }
457
+ case Blockly.INPUT_VALUE: {
458
+ // Offering to connect the left (male) of a value block to an already
459
+ // connected value pair is ok, we'll splice it in.
460
+ // However, don't offer to splice into an unmovable block.
461
+ if (candidate.targetConnection &&
462
+ !candidate.targetBlock().isMovable() &&
463
+ !candidate.targetBlock().isShadow()) {
464
+ return false;
465
+ }
466
+ break;
467
+ }
468
+ case Blockly.NEXT_STATEMENT: {
469
+ // Scratch-specific behaviour:
470
+ // If this is a c-block, we can't connect this block's
471
+ // previous connection unless we're connecting to the end of the last
472
+ // block on a stack or there's already a block connected inside the c.
473
+ if (firstStatementConnection &&
474
+ this == this.sourceBlock_.previousConnection &&
475
+ candidate.isConnectedToNonInsertionMarker() &&
476
+ !firstStatementConnection.targetConnection) {
477
+ return false;
478
+ }
479
+ // Don't let a block with no next connection bump other blocks out of the
480
+ // stack. But covering up a shadow block or stack of shadow blocks is
481
+ // fine. Similarly, replacing a terminal statement with another terminal
482
+ // statement is allowed.
483
+ if (candidate.isConnectedToNonInsertionMarker() &&
484
+ !this.sourceBlock_.nextConnection &&
485
+ !candidate.targetBlock().isShadow() &&
486
+ candidate.targetBlock().nextConnection) {
487
+ return false;
488
+ }
489
+ break;
490
+ }
491
+ default:
492
+ throw 'Unknown connection type in isConnectionAllowed';
493
+ }
494
+
495
+ // Don't let blocks try to connect to themselves or ones they nest.
496
+ if (Blockly.draggingConnections_.indexOf(candidate) != -1) {
497
+ return false;
498
+ }
499
+
500
+ return true;
501
+ };
502
+
503
+ /**
504
+ * Connect this connection to another connection.
505
+ * @param {!Blockly.Connection} otherConnection Connection to connect to.
506
+ */
507
+ Blockly.Connection.prototype.connect = function(otherConnection) {
508
+ if (this.targetConnection == otherConnection) {
509
+ // Already connected together. NOP.
510
+ return;
511
+ }
512
+ this.checkConnection_(otherConnection);
513
+ // Determine which block is superior (higher in the source stack).
514
+ if (this.isSuperior()) {
515
+ // Superior block.
516
+ this.connect_(otherConnection);
517
+ } else {
518
+ // Inferior block.
519
+ otherConnection.connect_(this);
520
+ }
521
+ };
522
+
523
+ /**
524
+ * Update two connections to target each other.
525
+ * @param {Blockly.Connection} first The first connection to update.
526
+ * @param {Blockly.Connection} second The second connection to update.
527
+ * @private
528
+ */
529
+ Blockly.Connection.connectReciprocally_ = function(first, second) {
530
+ goog.asserts.assert(first && second, 'Cannot connect null connections.');
531
+ first.targetConnection = second;
532
+ second.targetConnection = first;
533
+ };
534
+
535
+ /**
536
+ * Does the given block have one and only one connection point that will accept
537
+ * an orphaned block?
538
+ * @param {!Blockly.Block} block The superior block.
539
+ * @param {!Blockly.Block} orphanBlock The inferior block.
540
+ * @return {Blockly.Connection} The suitable connection point on 'block',
541
+ * or null.
542
+ * @private
543
+ */
544
+ Blockly.Connection.singleConnection_ = function(block, orphanBlock) {
545
+ var connection = false;
546
+ for (var i = 0; i < block.inputList.length; i++) {
547
+ var thisConnection = block.inputList[i].connection;
548
+ if (thisConnection && thisConnection.type == Blockly.INPUT_VALUE &&
549
+ orphanBlock.outputConnection.checkType_(thisConnection)) {
550
+ if (connection) {
551
+ return null; // More than one connection.
552
+ }
553
+ connection = thisConnection;
554
+ }
555
+ }
556
+ return connection;
557
+ };
558
+
559
+ /**
560
+ * Disconnect this connection.
561
+ */
562
+ Blockly.Connection.prototype.disconnect = function() {
563
+ var otherConnection = this.targetConnection;
564
+ goog.asserts.assert(otherConnection, 'Source connection not connected.');
565
+ goog.asserts.assert(otherConnection.targetConnection == this,
566
+ 'Target connection not connected to source connection.');
567
+
568
+ var parentBlock, childBlock, parentConnection;
569
+ if (this.isSuperior()) {
570
+ // Superior block.
571
+ parentBlock = this.sourceBlock_;
572
+ childBlock = otherConnection.getSourceBlock();
573
+ parentConnection = this;
574
+ } else {
575
+ // Inferior block.
576
+ parentBlock = otherConnection.getSourceBlock();
577
+ childBlock = this.sourceBlock_;
578
+ parentConnection = otherConnection;
579
+ }
580
+ this.disconnectInternal_(parentBlock, childBlock);
581
+ parentConnection.respawnShadow_();
582
+ };
583
+
584
+ /**
585
+ * Disconnect two blocks that are connected by this connection.
586
+ * @param {!Blockly.Block} parentBlock The superior block.
587
+ * @param {!Blockly.Block} childBlock The inferior block.
588
+ * @protected
589
+ */
590
+ Blockly.Connection.prototype.disconnectInternal_ = function(parentBlock,
591
+ childBlock) {
592
+ if (Blockly.Events.isEnabled() && !childBlock.isInsertionMarker()) {
593
+ childBlock.workspace.procedureReturnsWillChange();
594
+ }
595
+
596
+ var event;
597
+ if (Blockly.Events.isEnabled()) {
598
+ event = new Blockly.Events.BlockMove(childBlock);
599
+ }
600
+
601
+ var otherConnection = this.targetConnection;
602
+ otherConnection.targetConnection = null;
603
+ this.targetConnection = null;
604
+ childBlock.setParent(null);
605
+ if (event) {
606
+ event.recordNew();
607
+ Blockly.Events.fire(event);
608
+ }
609
+ };
610
+
611
+ /**
612
+ * Respawn the shadow block if there was one connected to the this connection.
613
+ * @protected
614
+ */
615
+ Blockly.Connection.prototype.respawnShadow_ = function() {
616
+ var parentBlock = this.getSourceBlock();
617
+ var shadow = this.getShadowDom();
618
+ if (parentBlock.workspace && shadow && Blockly.Events.recordUndo) {
619
+ var blockShadow =
620
+ Blockly.Xml.domToBlock(shadow, parentBlock.workspace);
621
+ if (blockShadow.outputConnection) {
622
+ this.connect(blockShadow.outputConnection);
623
+ } else if (blockShadow.previousConnection) {
624
+ this.connect(blockShadow.previousConnection);
625
+ } else {
626
+ throw 'Child block does not have output or previous statement.';
627
+ }
628
+ }
629
+ };
630
+
631
+ /**
632
+ * Returns the block that this connection connects to.
633
+ * @return {Blockly.Block} The connected block or null if none is connected.
634
+ */
635
+ Blockly.Connection.prototype.targetBlock = function() {
636
+ if (this.isConnected()) {
637
+ return this.targetConnection.getSourceBlock();
638
+ }
639
+ return null;
640
+ };
641
+
642
+ /**
643
+ * Is this connection compatible with another connection with respect to the
644
+ * value type system. E.g. square_root("Hello") is not compatible.
645
+ * @param {!Blockly.Connection} otherConnection Connection to compare against.
646
+ * @return {boolean} True if the connections share a type.
647
+ * @protected
648
+ */
649
+ Blockly.Connection.prototype.checkType_ = function(otherConnection) {
650
+ if (!this.check_ || !otherConnection.check_) {
651
+ // One or both sides are promiscuous enough that anything will fit.
652
+ return true;
653
+ }
654
+ // Find any intersection in the check lists.
655
+ for (var i = 0; i < this.check_.length; i++) {
656
+ if (otherConnection.check_.indexOf(this.check_[i]) != -1) {
657
+ return true;
658
+ }
659
+ }
660
+ // No intersection.
661
+ return false;
662
+ };
663
+
664
+ /**
665
+ * Function to be called when this connection's compatible types have changed.
666
+ * @private
667
+ */
668
+ Blockly.Connection.prototype.onCheckChanged_ = function() {
669
+ // The new value type may not be compatible with the existing connection.
670
+ if (this.isConnected() && !this.checkType_(this.targetConnection)) {
671
+ var child = this.isSuperior() ? this.targetBlock() : this.sourceBlock_;
672
+ child.unplug();
673
+ }
674
+ };
675
+
676
+ /**
677
+ * Change a connection's compatibility.
678
+ * @param {*} check Compatible value type or list of value types.
679
+ * Null if all types are compatible.
680
+ * @return {!Blockly.Connection} The connection being modified
681
+ * (to allow chaining).
682
+ */
683
+ Blockly.Connection.prototype.setCheck = function(check) {
684
+ if (check) {
685
+ // Ensure that check is in an array.
686
+ if (!goog.isArray(check)) {
687
+ check = [check];
688
+ }
689
+ this.check_ = check;
690
+ this.onCheckChanged_();
691
+ } else {
692
+ this.check_ = null;
693
+ }
694
+ return this;
695
+ };
696
+
697
+ /**
698
+ * Returns a shape enum for this connection.
699
+ * Used in scratch-blocks to draw unoccupied inputs.
700
+ * @return {number} Enum representing shape.
701
+ */
702
+ Blockly.Connection.prototype.getOutputShape = function() {
703
+ if (!this.check_) return Blockly.OUTPUT_SHAPE_ROUND;
704
+ if (this.check_.indexOf('Boolean') !== -1) {
705
+ return Blockly.OUTPUT_SHAPE_HEXAGONAL;
706
+ }
707
+ if (this.check_.indexOf('Number') !== -1) {
708
+ return Blockly.OUTPUT_SHAPE_ROUND;
709
+ }
710
+ if (this.check_.indexOf('String') !== -1) {
711
+ return Blockly.OUTPUT_SHAPE_SQUARE;
712
+ }
713
+ return Blockly.OUTPUT_SHAPE_ROUND;
714
+ };
715
+
716
+ /**
717
+ * Change a connection's shadow block.
718
+ * @param {Element} shadow DOM representation of a block or null.
719
+ */
720
+ Blockly.Connection.prototype.setShadowDom = function(shadow) {
721
+ this.shadowDom_ = shadow;
722
+ };
723
+
724
+ /**
725
+ * Return a connection's shadow block.
726
+ * @return {Element} shadow DOM representation of a block or null.
727
+ */
728
+ Blockly.Connection.prototype.getShadowDom = function() {
729
+ return this.shadowDom_;
730
+ };
731
+
732
+ /**
733
+ * Find all nearby compatible connections to this connection.
734
+ * Type checking does not apply, since this function is used for bumping.
735
+ *
736
+ * Headless configurations (the default) do not have neighboring connection,
737
+ * and always return an empty list (the default).
738
+ * {@link Blockly.RenderedConnection} overrides this behavior with a list
739
+ * computed from the rendered positioning.
740
+ * @param {number} maxLimit The maximum radius to another connection.
741
+ * @return {!Array.<!Blockly.Connection>} List of connections.
742
+ * @private
743
+ */
744
+ Blockly.Connection.prototype.neighbours_ = function(/* maxLimit */) {
745
+ return [];
746
+ };
747
+
748
+ /**
749
+ * This method returns a string describing this Connection in developer terms
750
+ * (English only). Intended to on be used in console logs and errors.
751
+ * @return {string} The description.
752
+ */
753
+ Blockly.Connection.prototype.toString = function() {
754
+ var msg;
755
+ var block = this.sourceBlock_;
756
+ if (!block) {
757
+ return 'Orphan Connection';
758
+ } else if (block.outputConnection == this) {
759
+ msg = 'Output Connection of ';
760
+ } else if (block.previousConnection == this) {
761
+ msg = 'Previous Connection of ';
762
+ } else if (block.nextConnection == this) {
763
+ msg = 'Next Connection of ';
764
+ } else {
765
+ var parentInput = goog.array.find(block.inputList, function(input) {
766
+ return input.connection == this;
767
+ }, this);
768
+ if (parentInput) {
769
+ msg = 'Input "' + parentInput.name + '" connection on ';
770
+ } else {
771
+ console.warn('Connection not actually connected to sourceBlock_');
772
+ return 'Orphan Connection';
773
+ }
774
+ }
775
+ return msg + block.toDevString();
776
+ };