@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/field.js ADDED
@@ -0,0 +1,810 @@
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 Field. Used for editable titles, variables, etc.
23
+ * This is an abstract class that defines the UI on the block. Actual
24
+ * instances would be Blockly.FieldTextInput, Blockly.FieldDropdown, etc.
25
+ * @author fraser@google.com (Neil Fraser)
26
+ */
27
+ 'use strict';
28
+
29
+ goog.provide('Blockly.Field');
30
+
31
+ goog.require('Blockly.Events.BlockChange');
32
+ goog.require('Blockly.Gesture');
33
+
34
+ goog.require('goog.asserts');
35
+ goog.require('goog.dom');
36
+ goog.require('goog.math.Size');
37
+ goog.require('goog.style');
38
+ goog.require('goog.userAgent');
39
+
40
+
41
+ /**
42
+ * Abstract class for an editable field.
43
+ * @param {string} text The initial content of the field.
44
+ * @param {Function=} opt_validator An optional function that is called
45
+ * to validate any constraints on what the user entered. Takes the new
46
+ * text as an argument and returns either the accepted text, a replacement
47
+ * text, or null to abort the change.
48
+ * @constructor
49
+ */
50
+ Blockly.Field = function(text, opt_validator) {
51
+ this.size_ = new goog.math.Size(
52
+ Blockly.BlockSvg.FIELD_WIDTH,
53
+ Blockly.BlockSvg.FIELD_HEIGHT);
54
+ this.setValue(text);
55
+ this.setValidator(opt_validator);
56
+
57
+ /**
58
+ * Maximum characters of text to display before adding an ellipsis.
59
+ * Same for strings and numbers.
60
+ * @type {number}
61
+ */
62
+ this.maxDisplayLength = Blockly.BlockSvg.MAX_DISPLAY_LENGTH;
63
+ };
64
+
65
+
66
+ /**
67
+ * The set of all registered fields, keyed by field type as used in the JSON
68
+ * definition of a block.
69
+ * @type {!Object<string, !{fromJson: Function}>}
70
+ * @private
71
+ */
72
+ Blockly.Field.TYPE_MAP_ = {};
73
+
74
+ /**
75
+ * Registers a field type. May also override an existing field type.
76
+ * Blockly.Field.fromJson uses this registry to find the appropriate field.
77
+ * @param {!string} type The field type name as used in the JSON definition.
78
+ * @param {!{fromJson: Function}} fieldClass The field class containing a
79
+ * fromJson function that can construct an instance of the field.
80
+ * @throws {Error} if the type name is empty, or the fieldClass is not an
81
+ * object containing a fromJson function.
82
+ */
83
+ Blockly.Field.register = function(type, fieldClass) {
84
+ if (!goog.isString(type) || goog.string.isEmptyOrWhitespace(type)) {
85
+ throw new Error('Invalid field type "' + type + '"');
86
+ }
87
+ if (!goog.isObject(fieldClass) || !goog.isFunction(fieldClass.fromJson)) {
88
+ throw new Error('Field "' + fieldClass +
89
+ '" must have a fromJson function');
90
+ }
91
+ Blockly.Field.TYPE_MAP_[type] = fieldClass;
92
+ };
93
+
94
+ /**
95
+ * Construct a Field from a JSON arg object.
96
+ * Finds the appropriate registered field by the type name as registered using
97
+ * Blockly.Field.register.
98
+ * @param {!Object} options A JSON object with a type and options specific
99
+ * to the field type.
100
+ * @returns {?Blockly.Field} The new field instance or null if a field wasn't
101
+ * found with the given type name
102
+ * @package
103
+ */
104
+ Blockly.Field.fromJson = function(options) {
105
+ var fieldClass = Blockly.Field.TYPE_MAP_[options['type']];
106
+ if (fieldClass) {
107
+ return fieldClass.fromJson(options);
108
+ }
109
+ return null;
110
+ };
111
+
112
+ /**
113
+ * Temporary cache of text widths.
114
+ * @type {Object}
115
+ * @private
116
+ */
117
+ Blockly.Field.cacheWidths_ = null;
118
+
119
+ /**
120
+ * Number of current references to cache.
121
+ * @type {number}
122
+ * @private
123
+ */
124
+ Blockly.Field.cacheReference_ = 0;
125
+
126
+
127
+ /**
128
+ * Name of field. Unique within each block.
129
+ * Static labels are usually unnamed.
130
+ * @type {string|undefined}
131
+ */
132
+ Blockly.Field.prototype.name = undefined;
133
+
134
+ /**
135
+ * CSS class name for the text element.
136
+ * @type {string}
137
+ * @package
138
+ */
139
+ Blockly.Field.prototype.className_ = 'blocklyText';
140
+
141
+ /**
142
+ * Visible text to display.
143
+ * @type {string}
144
+ * @private
145
+ */
146
+ Blockly.Field.prototype.text_ = '';
147
+
148
+ /**
149
+ * Block this field is attached to. Starts as null, then in set in init.
150
+ * @type {Blockly.Block}
151
+ * @private
152
+ */
153
+ Blockly.Field.prototype.sourceBlock_ = null;
154
+
155
+ /**
156
+ * Is the field visible, or hidden due to the block being collapsed?
157
+ * @type {boolean}
158
+ * @private
159
+ */
160
+ Blockly.Field.prototype.visible_ = true;
161
+
162
+ /**
163
+ * Null, or an array of the field's argTypes (for styling).
164
+ * @type {Array}
165
+ * @private
166
+ */
167
+ Blockly.Field.prototype.argType_ = null;
168
+
169
+ /**
170
+ * Validation function called when user edits an editable field.
171
+ * @type {Function}
172
+ * @private
173
+ */
174
+ Blockly.Field.prototype.validator_ = null;
175
+
176
+ /**
177
+ * Whether to assume user is using a touch device for interactions.
178
+ * Used to show different UI for touch interactions, e.g.
179
+ * @type {boolean}
180
+ * @private
181
+ */
182
+ Blockly.Field.prototype.useTouchInteraction_ = false;
183
+
184
+ /**
185
+ * Non-breaking space.
186
+ * @const
187
+ */
188
+ Blockly.Field.NBSP = '\u00A0';
189
+
190
+ /**
191
+ * Text offset used for IE/Edge.
192
+ * @const
193
+ */
194
+ Blockly.Field.IE_TEXT_OFFSET = '0.3em';
195
+
196
+ /**
197
+ * Editable fields usually show some sort of UI for the user to change them.
198
+ * @type {boolean}
199
+ * @public
200
+ */
201
+ Blockly.Field.prototype.EDITABLE = true;
202
+
203
+ /**
204
+ * Serializable fields are saved by the XML renderer, non-serializable fields
205
+ * are not. Editable fields should be serialized.
206
+ * @type {boolean}
207
+ * @public
208
+ */
209
+ Blockly.Field.prototype.SERIALIZABLE = true;
210
+
211
+ /**
212
+ * Attach this field to a block.
213
+ * @param {!Blockly.Block} block The block containing this field.
214
+ */
215
+ Blockly.Field.prototype.setSourceBlock = function(block) {
216
+ goog.asserts.assert(!this.sourceBlock_, 'Field already bound to a block.');
217
+ this.sourceBlock_ = block;
218
+ };
219
+
220
+ /**
221
+ * Install this field on a block.
222
+ */
223
+ Blockly.Field.prototype.init = function() {
224
+ if (this.fieldGroup_) {
225
+ // Field has already been initialized once.
226
+ return;
227
+ }
228
+ // Build the DOM.
229
+ this.fieldGroup_ = Blockly.utils.createSvgElement('g', {}, null);
230
+ if (!this.visible_) {
231
+ this.fieldGroup_.style.display = 'none';
232
+ }
233
+ // Add an attribute to cassify the type of field.
234
+ if (this.getArgTypes() !== null) {
235
+ if (this.sourceBlock_.isShadow()) {
236
+ this.sourceBlock_.svgGroup_.setAttribute('data-argument-type',
237
+ this.getArgTypes());
238
+ } else {
239
+ // Fields without a shadow wrapper, like square dropdowns.
240
+ this.fieldGroup_.setAttribute('data-argument-type', this.getArgTypes());
241
+ }
242
+ }
243
+ // Adjust X to be flipped for RTL. Position is relative to horizontal start of source block.
244
+ var size = this.getSize();
245
+ var fieldX = (this.sourceBlock_.RTL) ? -size.width / 2 : size.width / 2;
246
+ /** @type {!Element} */
247
+ this.textElement_ = Blockly.utils.createSvgElement('text',
248
+ {
249
+ 'class': this.className_,
250
+ 'x': fieldX,
251
+ 'y': size.height / 2 + Blockly.BlockSvg.FIELD_TOP_PADDING,
252
+ 'dominant-baseline': 'middle',
253
+ 'dy': goog.userAgent.EDGE_OR_IE ? Blockly.Field.IE_TEXT_OFFSET : '0',
254
+ 'text-anchor': 'middle'
255
+ }, this.fieldGroup_);
256
+
257
+ this.updateEditable();
258
+ this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_);
259
+ // Force a render.
260
+ this.render_();
261
+ this.size_.width = 0;
262
+ this.mouseDownWrapper_ = Blockly.bindEventWithChecks_(
263
+ this.getClickTarget_(), 'mousedown', this, this.onMouseDown_);
264
+ };
265
+
266
+ /**
267
+ * Initializes the model of the field after it has been installed on a block.
268
+ * No-op by default.
269
+ */
270
+ Blockly.Field.prototype.initModel = function() {
271
+ };
272
+
273
+ /**
274
+ * Dispose of all DOM objects belonging to this editable field.
275
+ */
276
+ Blockly.Field.prototype.dispose = function() {
277
+ if (this.mouseDownWrapper_) {
278
+ Blockly.unbindEvent_(this.mouseDownWrapper_);
279
+ this.mouseDownWrapper_ = null;
280
+ }
281
+ this.sourceBlock_ = null;
282
+ goog.dom.removeNode(this.fieldGroup_);
283
+ this.fieldGroup_ = null;
284
+ this.textElement_ = null;
285
+ this.validator_ = null;
286
+ };
287
+
288
+ /**
289
+ * Add or remove the UI indicating if this field is editable or not.
290
+ */
291
+ Blockly.Field.prototype.updateEditable = function() {
292
+ var group = this.fieldGroup_;
293
+ if (!this.EDITABLE || !group) {
294
+ return;
295
+ }
296
+ if (this.sourceBlock_.isEditable()) {
297
+ Blockly.utils.addClass(group, 'blocklyEditableText');
298
+ Blockly.utils.removeClass(group, 'blocklyNonEditableText');
299
+ this.getClickTarget_().style.cursor = this.CURSOR;
300
+ } else {
301
+ Blockly.utils.addClass(group, 'blocklyNonEditableText');
302
+ Blockly.utils.removeClass(group, 'blocklyEditableText');
303
+ this.getClickTarget_().style.cursor = '';
304
+ }
305
+ };
306
+
307
+ /**
308
+ * Check whether this field is currently editable. Some fields are never
309
+ * editable (e.g. text labels). Those fields are not serialized to XML. Other
310
+ * fields may be editable, and therefore serialized, but may exist on
311
+ * non-editable blocks.
312
+ * @return {boolean} whether this field is editable and on an editable block
313
+ */
314
+ Blockly.Field.prototype.isCurrentlyEditable = function() {
315
+ return this.EDITABLE && !!this.sourceBlock_ && this.sourceBlock_.isEditable();
316
+ };
317
+
318
+ /**
319
+ * Gets whether this editable field is visible or not.
320
+ * @return {boolean} True if visible.
321
+ */
322
+ Blockly.Field.prototype.isVisible = function() {
323
+ return this.visible_;
324
+ };
325
+
326
+ /**
327
+ * Sets whether this editable field is visible or not.
328
+ * @param {boolean} visible True if visible.
329
+ */
330
+ Blockly.Field.prototype.setVisible = function(visible) {
331
+ if (this.visible_ == visible) {
332
+ return;
333
+ }
334
+ this.visible_ = visible;
335
+ var root = this.getSvgRoot();
336
+ if (root) {
337
+ root.style.display = visible ? 'block' : 'none';
338
+ this.render_();
339
+ }
340
+ };
341
+
342
+ /**
343
+ * Adds a string to the field's array of argTypes (used for styling).
344
+ * @param {string} argType New argType.
345
+ */
346
+ Blockly.Field.prototype.addArgType = function(argType) {
347
+ if (this.argType_ == null) {
348
+ this.argType_ = [];
349
+ }
350
+ this.argType_.push(argType);
351
+ };
352
+
353
+ /**
354
+ * Gets the field's argTypes joined as a string, or returns null (used for styling).
355
+ * @return {string} argType string, or null.
356
+ */
357
+ Blockly.Field.prototype.getArgTypes = function() {
358
+ if (this.argType_ === null || this.argType_.length === 0) {
359
+ return null;
360
+ } else {
361
+ return this.argType_.join(' ');
362
+ }
363
+ };
364
+
365
+ /**
366
+ * Sets a new validation function for editable fields.
367
+ * @param {Function} handler New validation function, or null.
368
+ */
369
+ Blockly.Field.prototype.setValidator = function(handler) {
370
+ this.validator_ = handler;
371
+ };
372
+
373
+ /**
374
+ * Gets the validation function for editable fields.
375
+ * @return {Function} Validation function, or null.
376
+ */
377
+ Blockly.Field.prototype.getValidator = function() {
378
+ return this.validator_;
379
+ };
380
+
381
+ /**
382
+ * Validates a change. Does nothing. Subclasses may override this.
383
+ * @param {string} text The user's text.
384
+ * @return {string} No change needed.
385
+ */
386
+ Blockly.Field.prototype.classValidator = function(text) {
387
+ return text;
388
+ };
389
+
390
+ /**
391
+ * Calls the validation function for this field, as well as all the validation
392
+ * function for the field's class and its parents.
393
+ * @param {string} text Proposed text.
394
+ * @return {?string} Revised text, or null if invalid.
395
+ */
396
+ Blockly.Field.prototype.callValidator = function(text) {
397
+ var classResult = this.classValidator(text);
398
+ if (classResult === null) {
399
+ // Class validator rejects value. Game over.
400
+ return null;
401
+ } else if (classResult !== undefined) {
402
+ text = classResult;
403
+ }
404
+ var userValidator = this.getValidator();
405
+ if (userValidator) {
406
+ var userResult = userValidator.call(this, text);
407
+ if (userResult === null) {
408
+ // User validator rejects value. Game over.
409
+ return null;
410
+ } else if (userResult !== undefined) {
411
+ text = userResult;
412
+ }
413
+ }
414
+ return text;
415
+ };
416
+
417
+ /**
418
+ * Gets the group element for this editable field.
419
+ * Used for measuring the size and for positioning.
420
+ * @return {!Element} The group element.
421
+ */
422
+ Blockly.Field.prototype.getSvgRoot = function() {
423
+ return /** @type {!Element} */ (this.fieldGroup_);
424
+ };
425
+
426
+ /**
427
+ * Draws the border with the correct width.
428
+ * Saves the computed width in a property.
429
+ * @private
430
+ */
431
+ Blockly.Field.prototype.render_ = function() {
432
+ if (this.visible_ && this.textElement_) {
433
+ // Replace the text.
434
+ this.textElement_.textContent = this.getDisplayText_();
435
+ this.updateWidth();
436
+
437
+ // Update text centering, based on newly calculated width.
438
+ var centerTextX = (this.size_.width - this.arrowWidth_) / 2;
439
+ if (this.sourceBlock_.RTL) {
440
+ centerTextX += this.arrowWidth_;
441
+ }
442
+
443
+ // In a text-editing shadow block's field,
444
+ // if half the text length is not at least center of
445
+ // visible field (FIELD_WIDTH), center it there instead,
446
+ // unless there is a drop-down arrow.
447
+ if (this.sourceBlock_.isShadow() && !this.positionArrow) {
448
+ var minOffset = Blockly.BlockSvg.FIELD_WIDTH / 2;
449
+ if (this.sourceBlock_.RTL) {
450
+ // X position starts at the left edge of the block, in both RTL and LTR.
451
+ // First offset by the width of the block to move to the right edge,
452
+ // and then subtract to move to the same position as LTR.
453
+ var minCenter = this.size_.width - minOffset;
454
+ centerTextX = Math.min(minCenter, centerTextX);
455
+ } else {
456
+ // (width / 2) should exceed Blockly.BlockSvg.FIELD_WIDTH / 2
457
+ // if the text is longer.
458
+ centerTextX = Math.max(minOffset, centerTextX);
459
+ }
460
+ }
461
+
462
+ // Apply new text element x position.
463
+ this.textElement_.setAttribute('x', centerTextX);
464
+ }
465
+
466
+ // Update any drawn box to the correct width and height.
467
+ if (this.box_) {
468
+ this.box_.setAttribute('width', this.size_.width);
469
+ this.box_.setAttribute('height', this.size_.height);
470
+ }
471
+ };
472
+
473
+ /**
474
+ * Updates the width of the field. This calls getCachedWidth which won't cache
475
+ * the approximated width on IE/Edge when `getComputedTextLength` fails. Once
476
+ * it eventually does succeed, the result will be cached.
477
+ **/
478
+ Blockly.Field.prototype.updateWidth = function() {
479
+ // Calculate width of field
480
+ var width = Blockly.Field.getCachedWidth(this.textElement_);
481
+
482
+ // Add padding to left and right of text.
483
+ if (this.EDITABLE) {
484
+ width += Blockly.BlockSvg.EDITABLE_FIELD_PADDING;
485
+ }
486
+
487
+ // Adjust width for drop-down arrows.
488
+ this.arrowWidth_ = 0;
489
+ if (this.positionArrow) {
490
+ this.arrowWidth_ = this.positionArrow(width);
491
+ width += this.arrowWidth_;
492
+ }
493
+
494
+ // Add padding to any drawn box.
495
+ if (this.box_) {
496
+ width += 2 * Blockly.BlockSvg.BOX_FIELD_PADDING;
497
+ }
498
+
499
+ // Set width of the field.
500
+ this.size_.width = width;
501
+ };
502
+
503
+ /**
504
+ * Gets the width of a text element, caching it in the process.
505
+ * @param {!Element} textElement An SVG 'text' element.
506
+ * @return {number} Width of element.
507
+ */
508
+ Blockly.Field.getCachedWidth = function(textElement) {
509
+ var key = textElement.textContent + '\n' + textElement.className.baseVal;
510
+ var width;
511
+
512
+ // Return the cached width if it exists.
513
+ if (Blockly.Field.cacheWidths_) {
514
+ width = Blockly.Field.cacheWidths_[key];
515
+ if (width) {
516
+ return width;
517
+ }
518
+ }
519
+
520
+ // Attempt to compute fetch the width of the SVG text element.
521
+ try {
522
+ if (goog.userAgent.IE || goog.userAgent.EDGE) {
523
+ width = textElement.getBBox().width;
524
+ } else {
525
+ width = textElement.getComputedTextLength();
526
+ }
527
+ } catch (e) {
528
+ // In other cases where we fail to geth the computed text. Instead, use an
529
+ // approximation and do not cache the result. At some later point in time
530
+ // when the block is inserted into the visible DOM, this method will be
531
+ // called again and, at that point in time, will not throw an exception.
532
+ return textElement.textContent.length * 8;
533
+ }
534
+
535
+ // Cache the computed width and return.
536
+ if (Blockly.Field.cacheWidths_) {
537
+ Blockly.Field.cacheWidths_[key] = width;
538
+ }
539
+ return width;
540
+ };
541
+
542
+ /**
543
+ * Start caching field widths. Every call to this function MUST also call
544
+ * stopCache. Caches must not survive between execution threads.
545
+ */
546
+ Blockly.Field.startCache = function() {
547
+ Blockly.Field.cacheReference_++;
548
+ if (!Blockly.Field.cacheWidths_) {
549
+ Blockly.Field.cacheWidths_ = {};
550
+ }
551
+ };
552
+
553
+ /**
554
+ * Stop caching field widths. Unless caching was already on when the
555
+ * corresponding call to startCache was made.
556
+ */
557
+ Blockly.Field.stopCache = function() {
558
+ Blockly.Field.cacheReference_--;
559
+ if (!Blockly.Field.cacheReference_) {
560
+ Blockly.Field.cacheWidths_ = null;
561
+ }
562
+ };
563
+
564
+ /**
565
+ * Returns the height and width of the field.
566
+ * @return {!goog.math.Size} Height and width.
567
+ */
568
+ Blockly.Field.prototype.getSize = function() {
569
+ if (!this.size_.width) {
570
+ this.render_();
571
+ }
572
+ return this.size_;
573
+ };
574
+
575
+ /**
576
+ * Returns the bounding box of the rendered field, accounting for workspace
577
+ * scaling.
578
+ * @return {!Object} An object with top, bottom, left, and right in pixels
579
+ * relative to the top left corner of the page (window coordinates).
580
+ * @private
581
+ */
582
+ Blockly.Field.prototype.getScaledBBox_ = function() {
583
+ var size = this.getSize();
584
+ var scaledHeight = size.height * this.sourceBlock_.workspace.scale;
585
+ var scaledWidth = size.width * this.sourceBlock_.workspace.scale;
586
+ var xy = this.getAbsoluteXY_();
587
+ return {
588
+ top: xy.y,
589
+ bottom: xy.y + scaledHeight,
590
+ left: xy.x,
591
+ right: xy.x + scaledWidth
592
+ };
593
+ };
594
+
595
+ /**
596
+ * Get the text from this field as displayed on screen. May differ from getText
597
+ * due to ellipsis, and other formatting.
598
+ * @return {string} Currently displayed text.
599
+ * @private
600
+ */
601
+ Blockly.Field.prototype.getDisplayText_ = function() {
602
+ var text = this.text_;
603
+ if (!text) {
604
+ // Prevent the field from disappearing if empty.
605
+ return Blockly.Field.NBSP;
606
+ }
607
+ if (text.length > this.maxDisplayLength) {
608
+ // Truncate displayed string and add an ellipsis ('...').
609
+ text = text.substring(0, this.maxDisplayLength - 2) + '\u2026';
610
+ }
611
+ // Replace whitespace with non-breaking spaces so the text doesn't collapse.
612
+ text = text.replace(/\s/g, Blockly.Field.NBSP);
613
+ if (this.sourceBlock_.RTL) {
614
+ // The SVG is LTR, force text to be RTL unless a number.
615
+ if (this.sourceBlock_.editable_ && this.sourceBlock_.type === 'math_number') {
616
+ text = '\u202A' + text + '\u202C';
617
+ } else {
618
+ text = '\u202B' + text + '\u202C';
619
+ }
620
+ }
621
+ return text;
622
+ };
623
+
624
+ /**
625
+ * Get the text from this field.
626
+ * @return {string} Current text.
627
+ */
628
+ Blockly.Field.prototype.getText = function() {
629
+ return this.text_;
630
+ };
631
+
632
+ /**
633
+ * Set the text in this field. Trigger a rerender of the source block.
634
+ * @param {*} newText New text.
635
+ */
636
+ Blockly.Field.prototype.setText = function(newText) {
637
+ if (newText === null) {
638
+ // No change if null.
639
+ return;
640
+ }
641
+ newText = String(newText);
642
+ if (newText === this.text_) {
643
+ // No change.
644
+ return;
645
+ }
646
+ this.text_ = newText;
647
+ this.forceRerender();
648
+ };
649
+
650
+ /**
651
+ * Force a rerender of the block that this field is installed on, which will
652
+ * rerender this field and adjust for any sizing changes.
653
+ * Other fields on the same block will not rerender, because their sizes have
654
+ * already been recorded.
655
+ * @package
656
+ */
657
+ Blockly.Field.prototype.forceRerender = function() {
658
+ // Set width to 0 to force a rerender of this field.
659
+ this.size_.width = 0;
660
+
661
+ if (this.sourceBlock_ && this.sourceBlock_.rendered) {
662
+ this.sourceBlock_.render();
663
+ this.sourceBlock_.bumpNeighbours_();
664
+ }
665
+ };
666
+
667
+ /**
668
+ * Update the text node of this field to display the current text.
669
+ * @private
670
+ */
671
+ Blockly.Field.prototype.updateTextNode_ = function() {
672
+ if (!this.textElement_) {
673
+ // Not rendered yet.
674
+ return;
675
+ }
676
+ var text = this.text_;
677
+ if (text.length > this.maxDisplayLength) {
678
+ // Truncate displayed string and add an ellipsis ('...').
679
+ text = text.substring(0, this.maxDisplayLength - 2) + '\u2026';
680
+ // Add special class for sizing font when truncated
681
+ this.textElement_.setAttribute('class', this.className_ + ' blocklyTextTruncated');
682
+ } else {
683
+ this.textElement_.setAttribute('class', this.className_);
684
+ }
685
+ // Empty the text element.
686
+ goog.dom.removeChildren(/** @type {!Element} */ (this.textElement_));
687
+ // Replace whitespace with non-breaking spaces so the text doesn't collapse.
688
+ text = text.replace(/\s/g, Blockly.Field.NBSP);
689
+ if (this.sourceBlock_.RTL && text) {
690
+ // The SVG is LTR, force text to be RTL.
691
+ if (this.sourceBlock_.editable_ && this.sourceBlock_.type === 'math_number') {
692
+ text = '\u202A' + text + '\u202C';
693
+ } else {
694
+ text = '\u202B' + text + '\u202C';
695
+ }
696
+ }
697
+ if (!text) {
698
+ // Prevent the field from disappearing if empty.
699
+ text = Blockly.Field.NBSP;
700
+ }
701
+ var textNode = document.createTextNode(text);
702
+ this.textElement_.appendChild(textNode);
703
+
704
+ // Cached width is obsolete. Clear it.
705
+ this.size_.width = 0;
706
+ };
707
+
708
+ /**
709
+ * By default there is no difference between the human-readable text and
710
+ * the language-neutral values. Subclasses (such as dropdown) may define this.
711
+ * @return {string} Current value.
712
+ */
713
+ Blockly.Field.prototype.getValue = function() {
714
+ return this.getText();
715
+ };
716
+
717
+ /**
718
+ * By default there is no difference between the human-readable text and
719
+ * the language-neutral values. Subclasses (such as dropdown) may define this.
720
+ * @param {string} newValue New value.
721
+ */
722
+ Blockly.Field.prototype.setValue = function(newValue) {
723
+ if (newValue === null) {
724
+ // No change if null.
725
+ return;
726
+ }
727
+ var oldValue = this.getValue();
728
+ if (oldValue == newValue) {
729
+ return;
730
+ }
731
+ if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
732
+ Blockly.Events.fire(new Blockly.Events.BlockChange(
733
+ this.sourceBlock_, 'field', this.name, oldValue, newValue));
734
+ }
735
+ this.setText(newValue);
736
+ };
737
+
738
+ /**
739
+ * Handle a mouse down event on a field.
740
+ * @param {!Event} e Mouse down event.
741
+ * @private
742
+ */
743
+ Blockly.Field.prototype.onMouseDown_ = function(e) {
744
+ if (!this.sourceBlock_ || !this.sourceBlock_.workspace) {
745
+ return;
746
+ }
747
+ if (this.sourceBlock_.workspace.isDragging()) {
748
+ return;
749
+ }
750
+ var gesture = this.sourceBlock_.workspace.getGesture(e);
751
+ if (gesture) {
752
+ gesture.setStartField(this);
753
+ }
754
+ this.useTouchInteraction_ = Blockly.Touch.getTouchIdentifierFromEvent(e) !== 'mouse';
755
+ };
756
+
757
+ /**
758
+ * Change the tooltip text for this field.
759
+ * @param {string|!Element} _newTip Text for tooltip or a parent element to
760
+ * link to for its tooltip.
761
+ * @abstract
762
+ */
763
+ Blockly.Field.prototype.setTooltip = function(_newTip) {
764
+ // Non-abstract sub-classes may wish to implement this. See FieldLabel.
765
+ };
766
+
767
+ /**
768
+ * Select the element to bind the click handler to. When this element is
769
+ * clicked on an editable field, the editor will open.
770
+ *
771
+ * If the block has only one field and no output connection, we handle clicks
772
+ * over the whole block. Otherwise, handle clicks over the the group containing
773
+ * the field.
774
+ *
775
+ * @return {!Element} Element to bind click handler to.
776
+ * @private
777
+ */
778
+ Blockly.Field.prototype.getClickTarget_ = function() {
779
+ var nFields = 0;
780
+
781
+ for (var i = 0, input; input = this.sourceBlock_.inputList[i]; i++) {
782
+ nFields += input.fieldRow.length;
783
+ }
784
+ if (nFields <= 1 && this.sourceBlock_.outputConnection) {
785
+ return this.sourceBlock_.getSvgRoot();
786
+ } else {
787
+ return this.getSvgRoot();
788
+ }
789
+ };
790
+
791
+ /**
792
+ * Return the absolute coordinates of the top-left corner of this field.
793
+ * The origin (0,0) is the top-left corner of the page body.
794
+ * @return {!goog.math.Coordinate} Object with .x and .y properties.
795
+ * @private
796
+ */
797
+ Blockly.Field.prototype.getAbsoluteXY_ = function() {
798
+ return goog.style.getPageOffset(this.getClickTarget_());
799
+ };
800
+
801
+ /**
802
+ * Whether this field references any Blockly variables. If true it may need to
803
+ * be handled differently during serialization and deserialization. Subclasses
804
+ * may override this.
805
+ * @return {boolean} True if this field has any variable references.
806
+ * @package
807
+ */
808
+ Blockly.Field.prototype.referencesVariables = function() {
809
+ return false;
810
+ };