blockly 7.20211209.2 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. package/blockly.d.ts +18963 -18432
  2. package/blockly.min.js +852 -844
  3. package/blockly_compressed.js +669 -664
  4. package/blockly_compressed.js.map +1 -1
  5. package/blocks/blocks.js +47 -0
  6. package/blocks/colour.js +13 -3
  7. package/blocks/lists.js +22 -13
  8. package/blocks/logic.js +13 -3
  9. package/blocks/loops.js +24 -11
  10. package/blocks/math.js +12 -3
  11. package/blocks/procedures.js +41 -27
  12. package/blocks/text.js +22 -13
  13. package/blocks/variables.js +14 -3
  14. package/blocks/variables_dynamic.js +13 -3
  15. package/blocks_compressed.js +146 -141
  16. package/blocks_compressed.js.map +1 -1
  17. package/core/block.js +1869 -1814
  18. package/core/block_drag_surface.js +201 -200
  19. package/core/block_dragger.js +377 -373
  20. package/core/block_svg.js +1593 -1479
  21. package/core/blockly.js +8 -22
  22. package/core/blocks.js +9 -2
  23. package/core/browser_events.js +22 -5
  24. package/core/bubble.js +841 -797
  25. package/core/bubble_dragger.js +213 -206
  26. package/core/bump_objects.js +2 -2
  27. package/core/clipboard.js +9 -9
  28. package/core/comment.js +353 -332
  29. package/core/common.js +46 -17
  30. package/core/component_manager.js +181 -174
  31. package/core/config.js +87 -0
  32. package/core/connection.js +595 -584
  33. package/core/connection_checker.js +242 -244
  34. package/core/connection_db.js +235 -230
  35. package/core/contextmenu.js +9 -6
  36. package/core/contextmenu_items.js +1 -2
  37. package/core/contextmenu_registry.js +93 -89
  38. package/core/css.js +474 -474
  39. package/core/delete_area.js +45 -42
  40. package/core/drag_target.js +57 -56
  41. package/core/dropdowndiv.js +153 -163
  42. package/core/events/events.js +2 -2
  43. package/core/events/events_abstract.js +89 -77
  44. package/core/events/events_block_base.js +37 -36
  45. package/core/events/events_block_change.js +130 -124
  46. package/core/events/events_block_create.js +73 -71
  47. package/core/events/events_block_delete.js +84 -82
  48. package/core/events/events_block_drag.js +50 -49
  49. package/core/events/events_block_move.js +147 -140
  50. package/core/events/events_bubble_open.js +51 -50
  51. package/core/events/events_click.js +48 -44
  52. package/core/events/events_comment_base.js +72 -69
  53. package/core/events/events_comment_change.js +63 -61
  54. package/core/events/events_comment_create.js +44 -42
  55. package/core/events/events_comment_delete.js +42 -40
  56. package/core/events/events_comment_move.js +106 -104
  57. package/core/events/events_marker_move.js +65 -64
  58. package/core/events/events_selected.js +46 -45
  59. package/core/events/events_theme_change.js +36 -35
  60. package/core/events/events_toolbox_item_select.js +46 -45
  61. package/core/events/events_trashcan_open.js +37 -36
  62. package/core/events/events_ui.js +47 -46
  63. package/core/events/events_ui_base.js +30 -29
  64. package/core/events/events_var_base.js +37 -36
  65. package/core/events/events_var_create.js +50 -48
  66. package/core/events/events_var_delete.js +50 -48
  67. package/core/events/events_var_rename.js +51 -49
  68. package/core/events/events_viewport.js +66 -65
  69. package/core/events/utils.js +29 -14
  70. package/core/events/workspace_events.js +49 -55
  71. package/core/extensions.js +4 -3
  72. package/core/field.js +1061 -997
  73. package/core/field_angle.js +462 -442
  74. package/core/field_checkbox.js +194 -182
  75. package/core/field_colour.js +519 -505
  76. package/core/field_dropdown.js +617 -598
  77. package/core/field_image.js +229 -220
  78. package/core/field_label.js +102 -91
  79. package/core/field_label_serializable.js +42 -41
  80. package/core/field_multilineinput.js +372 -358
  81. package/core/field_number.js +272 -253
  82. package/core/field_textinput.js +499 -467
  83. package/core/field_variable.js +458 -420
  84. package/core/flyout_base.js +1005 -952
  85. package/core/flyout_button.js +277 -260
  86. package/core/flyout_horizontal.js +304 -302
  87. package/core/flyout_metrics_manager.js +64 -64
  88. package/core/flyout_vertical.js +306 -300
  89. package/core/generator.js +459 -446
  90. package/core/gesture.js +829 -813
  91. package/core/grid.js +166 -163
  92. package/core/icon.js +168 -159
  93. package/core/inject.js +7 -5
  94. package/core/input.js +257 -248
  95. package/core/insertion_marker_manager.js +655 -624
  96. package/core/internal_constants.js +0 -129
  97. package/core/keyboard_nav/ast_node.js +605 -596
  98. package/core/keyboard_nav/basic_cursor.js +166 -165
  99. package/core/keyboard_nav/cursor.js +99 -97
  100. package/core/keyboard_nav/marker.js +83 -79
  101. package/core/keyboard_nav/tab_navigate_cursor.js +18 -23
  102. package/core/marker_manager.js +153 -141
  103. package/core/menu.js +377 -372
  104. package/core/menuitem.js +223 -217
  105. package/core/metrics_manager.js +403 -390
  106. package/core/mutator.js +468 -437
  107. package/core/names.js +229 -188
  108. package/core/options.js +290 -284
  109. package/core/procedures.js +29 -17
  110. package/core/registry.js +19 -16
  111. package/core/rendered_connection.js +482 -463
  112. package/core/renderers/common/block_rendering.js +9 -3
  113. package/core/renderers/common/constants.js +1119 -1112
  114. package/core/renderers/common/debug.js +14 -0
  115. package/core/renderers/common/debugger.js +338 -316
  116. package/core/renderers/common/drawer.js +380 -370
  117. package/core/renderers/common/i_path_object.js +2 -2
  118. package/core/renderers/common/info.js +626 -618
  119. package/core/renderers/common/marker_svg.js +579 -541
  120. package/core/renderers/common/path_object.js +203 -200
  121. package/core/renderers/common/renderer.js +220 -218
  122. package/core/renderers/geras/constants.js +36 -36
  123. package/core/renderers/geras/drawer.js +155 -147
  124. package/core/renderers/geras/highlight_constants.js +244 -238
  125. package/core/renderers/geras/highlighter.js +231 -179
  126. package/core/renderers/geras/info.js +392 -369
  127. package/core/renderers/geras/measurables/inline_input.js +25 -19
  128. package/core/renderers/geras/measurables/statement_input.js +23 -17
  129. package/core/renderers/geras/path_object.js +106 -121
  130. package/core/renderers/geras/renderer.js +96 -98
  131. package/core/renderers/measurables/base.js +30 -18
  132. package/core/renderers/measurables/bottom_row.js +83 -80
  133. package/core/renderers/measurables/connection.js +22 -15
  134. package/core/renderers/measurables/external_value_input.js +35 -22
  135. package/core/renderers/measurables/field.js +35 -20
  136. package/core/renderers/measurables/hat.js +18 -13
  137. package/core/renderers/measurables/icon.js +24 -17
  138. package/core/renderers/measurables/in_row_spacer.js +15 -13
  139. package/core/renderers/measurables/inline_input.js +43 -33
  140. package/core/renderers/measurables/input_connection.js +41 -28
  141. package/core/renderers/measurables/input_row.js +50 -44
  142. package/core/renderers/measurables/jagged_edge.js +14 -12
  143. package/core/renderers/measurables/next_connection.js +16 -14
  144. package/core/renderers/measurables/output_connection.js +26 -20
  145. package/core/renderers/measurables/previous_connection.js +16 -15
  146. package/core/renderers/measurables/round_corner.js +20 -18
  147. package/core/renderers/measurables/row.js +184 -168
  148. package/core/renderers/measurables/spacer_row.js +38 -23
  149. package/core/renderers/measurables/square_corner.js +18 -16
  150. package/core/renderers/measurables/statement_input.js +23 -20
  151. package/core/renderers/measurables/top_row.js +88 -85
  152. package/core/renderers/minimalist/constants.js +8 -7
  153. package/core/renderers/minimalist/drawer.js +11 -10
  154. package/core/renderers/minimalist/info.js +18 -18
  155. package/core/renderers/minimalist/renderer.js +40 -39
  156. package/core/renderers/thrasos/info.js +258 -248
  157. package/core/renderers/thrasos/renderer.js +20 -20
  158. package/core/renderers/zelos/constants.js +898 -873
  159. package/core/renderers/zelos/drawer.js +186 -169
  160. package/core/renderers/zelos/info.js +502 -479
  161. package/core/renderers/zelos/marker_svg.js +129 -115
  162. package/core/renderers/zelos/measurables/bottom_row.js +31 -30
  163. package/core/renderers/zelos/measurables/inputs.js +22 -21
  164. package/core/renderers/zelos/measurables/row_elements.js +14 -13
  165. package/core/renderers/zelos/measurables/top_row.js +34 -33
  166. package/core/renderers/zelos/path_object.js +181 -180
  167. package/core/renderers/zelos/renderer.js +91 -92
  168. package/core/scrollbar.js +759 -713
  169. package/core/scrollbar_pair.js +250 -245
  170. package/core/serialization/blocks.js +19 -9
  171. package/core/serialization/workspaces.js +3 -2
  172. package/core/shortcut_registry.js +286 -277
  173. package/core/sprites.js +31 -0
  174. package/core/theme.js +135 -141
  175. package/core/theme_manager.js +147 -143
  176. package/core/toolbox/category.js +602 -576
  177. package/core/toolbox/collapsible_category.js +226 -227
  178. package/core/toolbox/separator.js +70 -61
  179. package/core/toolbox/toolbox.js +934 -927
  180. package/core/toolbox/toolbox_item.js +115 -99
  181. package/core/tooltip.js +108 -35
  182. package/core/touch.js +8 -3
  183. package/core/touch_gesture.js +254 -251
  184. package/core/trashcan.js +606 -595
  185. package/core/utils/coordinate.js +97 -95
  186. package/core/utils/dom.js +2 -2
  187. package/core/utils/global.js +2 -0
  188. package/core/utils/rect.js +41 -37
  189. package/core/utils/sentinel.js +25 -0
  190. package/core/utils/size.js +30 -27
  191. package/core/utils/svg.js +18 -16
  192. package/core/variable_map.js +325 -341
  193. package/core/variable_model.js +55 -54
  194. package/core/variables.js +9 -2
  195. package/core/variables_dynamic.js +3 -1
  196. package/core/warning.js +126 -120
  197. package/core/widgetdiv.js +4 -4
  198. package/core/workspace.js +685 -664
  199. package/core/workspace_audio.js +124 -118
  200. package/core/workspace_comment.js +308 -298
  201. package/core/workspace_comment_svg.js +1029 -951
  202. package/core/workspace_drag_surface_svg.js +147 -140
  203. package/core/workspace_dragger.js +70 -71
  204. package/core/workspace_svg.js +2322 -2297
  205. package/core/xml.js +30 -20
  206. package/core/zoom_controls.js +431 -439
  207. package/dart_compressed.js +40 -43
  208. package/dart_compressed.js.map +1 -1
  209. package/generators/dart/colour.js +56 -64
  210. package/generators/dart/lists.js +61 -50
  211. package/generators/dart/math.js +160 -148
  212. package/generators/dart/text.js +83 -61
  213. package/generators/javascript/colour.js +37 -34
  214. package/generators/javascript/lists.js +50 -43
  215. package/generators/javascript/math.js +123 -139
  216. package/generators/javascript/text.js +67 -81
  217. package/generators/lua/colour.js +25 -23
  218. package/generators/lua/lists.js +97 -69
  219. package/generators/lua/logic.js +1 -2
  220. package/generators/lua/math.js +182 -144
  221. package/generators/lua/text.js +116 -99
  222. package/generators/php/colour.js +38 -32
  223. package/generators/php/lists.js +109 -89
  224. package/generators/php/math.js +90 -81
  225. package/generators/php/text.js +63 -61
  226. package/generators/python/colour.js +18 -18
  227. package/generators/python/lists.js +38 -30
  228. package/generators/python/loops.js +12 -8
  229. package/generators/python/math.js +104 -106
  230. package/generators/python/text.js +34 -30
  231. package/javascript_compressed.js +37 -39
  232. package/javascript_compressed.js.map +1 -1
  233. package/lua_compressed.js +39 -42
  234. package/lua_compressed.js.map +1 -1
  235. package/msg/az.js +2 -2
  236. package/msg/be.js +4 -4
  237. package/msg/cs.js +15 -15
  238. package/msg/de.js +1 -1
  239. package/msg/diq.js +1 -1
  240. package/msg/eo.js +1 -1
  241. package/msg/es.js +1 -1
  242. package/msg/fa.js +1 -1
  243. package/msg/fr.js +4 -4
  244. package/msg/he.js +1 -1
  245. package/msg/hr.js +2 -2
  246. package/msg/hy.js +2 -2
  247. package/msg/id.js +12 -12
  248. package/msg/inh.js +14 -14
  249. package/msg/ja.js +7 -7
  250. package/msg/lv.js +29 -29
  251. package/msg/pa.js +3 -3
  252. package/msg/smn.js +436 -0
  253. package/msg/te.js +1 -1
  254. package/msg/yue.js +1 -1
  255. package/msg/zh-hans.js +3 -3
  256. package/msg/zh-hant.js +3 -3
  257. package/package.json +7 -6
  258. package/php_compressed.js +38 -42
  259. package/php_compressed.js.map +1 -1
  260. package/python_compressed.js +26 -25
  261. package/python_compressed.js.map +1 -1
  262. package/blocks/all.js +0 -23
@@ -19,16 +19,18 @@ const Variables = goog.require('Blockly.Variables');
19
19
  const Xml = goog.require('Blockly.Xml');
20
20
  const fieldRegistry = goog.require('Blockly.fieldRegistry');
21
21
  const internalConstants = goog.require('Blockly.internalConstants');
22
- const object = goog.require('Blockly.utils.object');
23
22
  const parsing = goog.require('Blockly.utils.parsing');
24
23
  /* eslint-disable-next-line no-unused-vars */
25
24
  const {Block} = goog.requireType('Blockly.Block');
25
+ const {Field} = goog.require('Blockly.Field');
26
26
  const {FieldDropdown} = goog.require('Blockly.FieldDropdown');
27
27
  /* eslint-disable-next-line no-unused-vars */
28
28
  const {MenuItem} = goog.requireType('Blockly.MenuItem');
29
29
  /* eslint-disable-next-line no-unused-vars */
30
30
  const {Menu} = goog.requireType('Blockly.Menu');
31
31
  const {Msg} = goog.require('Blockly.Msg');
32
+ /* eslint-disable-next-line no-unused-vars */
33
+ const {Sentinel} = goog.requireType('Blockly.utils.Sentinel');
32
34
  const {Size} = goog.require('Blockly.utils.Size');
33
35
  const {VariableModel} = goog.require('Blockly.VariableModel');
34
36
  /** @suppress {extraRequire} */
@@ -37,483 +39,519 @@ goog.require('Blockly.Events.BlockChange');
37
39
 
38
40
  /**
39
41
  * Class for a variable's dropdown field.
40
- * @param {?string} varName The default name for the variable. If null,
41
- * a unique variable name will be generated.
42
- * @param {Function=} opt_validator A function that is called to validate
43
- * changes to the field's value. Takes in a variable ID & returns a
44
- * validated variable ID, or null to abort the change.
45
- * @param {Array<string>=} opt_variableTypes A list of the types of variables
46
- * to include in the dropdown.
47
- * @param {string=} opt_defaultType The type of variable to create if this
48
- * field's value is not explicitly set. Defaults to ''.
49
- * @param {Object=} opt_config A map of options used to configure the field.
50
- * See the [field creation documentation]{@link
51
- * https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/variable#creation}
52
- * for a list of properties this parameter supports.
53
42
  * @extends {FieldDropdown}
54
- * @constructor
55
43
  * @alias Blockly.FieldVariable
56
44
  */
57
- const FieldVariable = function(
58
- varName, opt_validator, opt_variableTypes, opt_defaultType, opt_config) {
59
- // The FieldDropdown constructor expects the field's initial value to be
60
- // the first entry in the menu generator, which it may or may not be.
61
- // Just do the relevant parts of the constructor.
45
+ class FieldVariable extends FieldDropdown {
46
+ /**
47
+ * @param {?string|!Sentinel} varName The default name for the variable.
48
+ * If null, a unique variable name will be generated.
49
+ * Also accepts Field.SKIP_SETUP if you wish to skip setup (only used by
50
+ * subclasses that want to handle configuration and setting the field
51
+ * value after their own constructors have run).
52
+ * @param {Function=} opt_validator A function that is called to validate
53
+ * changes to the field's value. Takes in a variable ID & returns a
54
+ * validated variable ID, or null to abort the change.
55
+ * @param {Array<string>=} opt_variableTypes A list of the types of variables
56
+ * to include in the dropdown. Will only be used if opt_config is not
57
+ * provided.
58
+ * @param {string=} opt_defaultType The type of variable to create if this
59
+ * field's value is not explicitly set. Defaults to ''. Will only be used
60
+ * if opt_config is not provided.
61
+ * @param {Object=} opt_config A map of options used to configure the field.
62
+ * See the [field creation documentation]{@link
63
+ * https://developers.google.com/blockly/guides/create-custom-blocks/fields/built-in-fields/variable#creation}
64
+ * for a list of properties this parameter supports.
65
+ */
66
+ constructor(
67
+ varName, opt_validator, opt_variableTypes, opt_defaultType, opt_config) {
68
+ super(Field.SKIP_SETUP);
69
+
70
+ /**
71
+ * An array of options for a dropdown list,
72
+ * or a function which generates these options.
73
+ * @type {(!Array<!Array>|
74
+ * !function(this:FieldDropdown): !Array<!Array>)}
75
+ * @protected
76
+ */
77
+ this.menuGenerator_ = FieldVariable.dropdownCreate;
78
+
79
+ /**
80
+ * The initial variable name passed to this field's constructor, or an
81
+ * empty string if a name wasn't provided. Used to create the initial
82
+ * variable.
83
+ * @type {string}
84
+ */
85
+ this.defaultVariableName = typeof varName === 'string' ? varName : '';
86
+
87
+ /**
88
+ * The type of the default variable for this field.
89
+ * @type {string}
90
+ * @private
91
+ */
92
+ this.defaultType_ = '';
93
+
94
+ /**
95
+ * All of the types of variables that will be available in this field's
96
+ * dropdown.
97
+ * @type {?Array<string>}
98
+ */
99
+ this.variableTypes = [];
100
+
101
+ /**
102
+ * The size of the area rendered by the field.
103
+ * @type {Size}
104
+ * @protected
105
+ * @override
106
+ */
107
+ this.size_ = new Size(0, 0);
108
+
109
+ /**
110
+ * The variable model associated with this field.
111
+ * @type {?VariableModel}
112
+ * @private
113
+ */
114
+ this.variable_ = null;
115
+
116
+ /**
117
+ * Serializable fields are saved by the serializer, non-serializable fields
118
+ * are not. Editable fields should also be serializable.
119
+ * @type {boolean}
120
+ */
121
+ this.SERIALIZABLE = true;
122
+
123
+ if (varName === Field.SKIP_SETUP) return;
124
+
125
+ if (opt_config) {
126
+ this.configure_(opt_config);
127
+ } else {
128
+ this.setTypes_(opt_variableTypes, opt_defaultType);
129
+ }
130
+ if (opt_validator) this.setValidator(opt_validator);
131
+ }
62
132
 
63
133
  /**
64
- * An array of options for a dropdown list,
65
- * or a function which generates these options.
66
- * @type {(!Array<!Array>|
67
- * !function(this:FieldDropdown): !Array<!Array>)}
134
+ * Configure the field based on the given map of options.
135
+ * @param {!Object} config A map of options to configure the field based on.
68
136
  * @protected
69
137
  */
70
- this.menuGenerator_ = FieldVariable.dropdownCreate;
138
+ configure_(config) {
139
+ super.configure_(config);
140
+ this.setTypes_(config['variableTypes'], config['defaultType']);
141
+ }
71
142
 
72
143
  /**
73
- * The initial variable name passed to this field's constructor, or an
74
- * empty string if a name wasn't provided. Used to create the initial
75
- * variable.
76
- * @type {string}
144
+ * Initialize the model for this field if it has not already been initialized.
145
+ * If the value has not been set to a variable by the first render, we make up
146
+ * a variable rather than let the value be invalid.
147
+ * @package
77
148
  */
78
- this.defaultVariableName = typeof varName === 'string' ? varName : '';
149
+ initModel() {
150
+ if (this.variable_) {
151
+ return; // Initialization already happened.
152
+ }
153
+ const variable = Variables.getOrCreateVariablePackage(
154
+ this.sourceBlock_.workspace, null, this.defaultVariableName,
155
+ this.defaultType_);
156
+
157
+ // Don't call setValue because we don't want to cause a rerender.
158
+ this.doValueUpdate_(variable.getId());
159
+ }
79
160
 
80
161
  /**
81
- * The size of the area rendered by the field.
82
- * @type {Size}
83
- * @protected
84
162
  * @override
85
163
  */
86
- this.size_ = new Size(0, 0);
87
-
88
- opt_config && this.configure_(opt_config);
89
- opt_validator && this.setValidator(opt_validator);
90
-
91
- if (!opt_config) { // Only do one kind of configuration or the other.
92
- this.setTypes_(opt_variableTypes, opt_defaultType);
164
+ shouldAddBorderRect_() {
165
+ return super.shouldAddBorderRect_() &&
166
+ (!this.getConstants().FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW ||
167
+ this.sourceBlock_.type !== 'variables_get');
93
168
  }
94
- };
95
- object.inherits(FieldVariable, FieldDropdown);
96
-
97
- /**
98
- * Construct a FieldVariable from a JSON arg object,
99
- * dereferencing any string table references.
100
- * @param {!Object} options A JSON object with options (variable,
101
- * variableTypes, and defaultType).
102
- * @return {!FieldVariable} The new field instance.
103
- * @package
104
- * @nocollapse
105
- */
106
- FieldVariable.fromJson = function(options) {
107
- const varName = parsing.replaceMessageReferences(options['variable']);
108
- // `this` might be a subclass of FieldVariable if that class doesn't override
109
- // the static fromJson method.
110
- return new this(varName, undefined, undefined, undefined, options);
111
- };
112
169
 
113
- /**
114
- * Serializable fields are saved by the XML renderer, non-serializable fields
115
- * are not. Editable fields should also be serializable.
116
- * @type {boolean}
117
- */
118
- FieldVariable.prototype.SERIALIZABLE = true;
119
-
120
- /**
121
- * Configure the field based on the given map of options.
122
- * @param {!Object} config A map of options to configure the field based on.
123
- * @protected
124
- */
125
- FieldVariable.prototype.configure_ = function(config) {
126
- FieldVariable.superClass_.configure_.call(this, config);
127
- this.setTypes_(config['variableTypes'], config['defaultType']);
128
- };
170
+ /**
171
+ * Initialize this field based on the given XML.
172
+ * @param {!Element} fieldElement The element containing information about the
173
+ * variable field's state.
174
+ */
175
+ fromXml(fieldElement) {
176
+ const id = fieldElement.getAttribute('id');
177
+ const variableName = fieldElement.textContent;
178
+ // 'variabletype' should be lowercase, but until July 2019 it was sometimes
179
+ // recorded as 'variableType'. Thus we need to check for both.
180
+ const variableType = fieldElement.getAttribute('variabletype') ||
181
+ fieldElement.getAttribute('variableType') || '';
182
+
183
+ const variable = Variables.getOrCreateVariablePackage(
184
+ this.sourceBlock_.workspace, id, variableName, variableType);
185
+
186
+ // This should never happen :)
187
+ if (variableType !== null && variableType !== variable.type) {
188
+ throw Error(
189
+ 'Serialized variable type with id \'' + variable.getId() +
190
+ '\' had type ' + variable.type + ', and ' +
191
+ 'does not match variable field that references it: ' +
192
+ Xml.domToText(fieldElement) + '.');
193
+ }
129
194
 
130
- /**
131
- * Initialize the model for this field if it has not already been initialized.
132
- * If the value has not been set to a variable by the first render, we make up a
133
- * variable rather than let the value be invalid.
134
- * @package
135
- */
136
- FieldVariable.prototype.initModel = function() {
137
- if (this.variable_) {
138
- return; // Initialization already happened.
195
+ this.setValue(variable.getId());
139
196
  }
140
- const variable = Variables.getOrCreateVariablePackage(
141
- this.sourceBlock_.workspace, null, this.defaultVariableName,
142
- this.defaultType_);
143
-
144
- // Don't call setValue because we don't want to cause a rerender.
145
- this.doValueUpdate_(variable.getId());
146
- };
147
-
148
- /**
149
- * @override
150
- */
151
- FieldVariable.prototype.shouldAddBorderRect_ = function() {
152
- return FieldVariable.superClass_.shouldAddBorderRect_.call(this) &&
153
- (!this.getConstants().FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW ||
154
- this.sourceBlock_.type !== 'variables_get');
155
- };
156
197
 
157
- /**
158
- * Initialize this field based on the given XML.
159
- * @param {!Element} fieldElement The element containing information about the
160
- * variable field's state.
161
- */
162
- FieldVariable.prototype.fromXml = function(fieldElement) {
163
- const id = fieldElement.getAttribute('id');
164
- const variableName = fieldElement.textContent;
165
- // 'variabletype' should be lowercase, but until July 2019 it was sometimes
166
- // recorded as 'variableType'. Thus we need to check for both.
167
- const variableType = fieldElement.getAttribute('variabletype') ||
168
- fieldElement.getAttribute('variableType') || '';
169
-
170
- const variable = Variables.getOrCreateVariablePackage(
171
- this.sourceBlock_.workspace, id, variableName, variableType);
172
-
173
- // This should never happen :)
174
- if (variableType !== null && variableType !== variable.type) {
175
- throw Error(
176
- 'Serialized variable type with id \'' + variable.getId() +
177
- '\' had type ' + variable.type + ', and ' +
178
- 'does not match variable field that references it: ' +
179
- Xml.domToText(fieldElement) + '.');
198
+ /**
199
+ * Serialize this field to XML.
200
+ * @param {!Element} fieldElement The element to populate with info about the
201
+ * field's state.
202
+ * @return {!Element} The element containing info about the field's state.
203
+ */
204
+ toXml(fieldElement) {
205
+ // Make sure the variable is initialized.
206
+ this.initModel();
207
+
208
+ fieldElement.id = this.variable_.getId();
209
+ fieldElement.textContent = this.variable_.name;
210
+ if (this.variable_.type) {
211
+ fieldElement.setAttribute('variabletype', this.variable_.type);
212
+ }
213
+ return fieldElement;
180
214
  }
181
215
 
182
- this.setValue(variable.getId());
183
- };
184
-
185
- /**
186
- * Serialize this field to XML.
187
- * @param {!Element} fieldElement The element to populate with info about the
188
- * field's state.
189
- * @return {!Element} The element containing info about the field's state.
190
- */
191
- FieldVariable.prototype.toXml = function(fieldElement) {
192
- // Make sure the variable is initialized.
193
- this.initModel();
194
-
195
- fieldElement.id = this.variable_.getId();
196
- fieldElement.textContent = this.variable_.name;
197
- if (this.variable_.type) {
198
- fieldElement.setAttribute('variabletype', this.variable_.type);
216
+ /**
217
+ * Saves this field's value.
218
+ * @param {boolean=} doFullSerialization If true, the variable field will
219
+ * serialize the full state of the field being referenced (ie ID, name,
220
+ * and type) rather than just a reference to it (ie ID).
221
+ * @return {*} The state of the variable field.
222
+ * @override
223
+ * @package
224
+ */
225
+ saveState(doFullSerialization) {
226
+ const legacyState = this.saveLegacyState(FieldVariable);
227
+ if (legacyState !== null) {
228
+ return legacyState;
229
+ }
230
+ // Make sure the variable is initialized.
231
+ this.initModel();
232
+ const state = {'id': this.variable_.getId()};
233
+ if (doFullSerialization) {
234
+ state['name'] = this.variable_.name;
235
+ state['type'] = this.variable_.type;
236
+ }
237
+ return state;
199
238
  }
200
- return fieldElement;
201
- };
202
239
 
203
- /**
204
- * Saves this field's value.
205
- * @param {boolean=} doFullSerialization If true, the variable field will
206
- * serialize the full state of the field being referenced (ie ID, name,
207
- * and type) rather than just a reference to it (ie ID).
208
- * @return {*} The state of the variable field.
209
- * @override
210
- * @package
211
- */
212
- FieldVariable.prototype.saveState = function(doFullSerialization) {
213
- const legacyState = this.saveLegacyState(FieldVariable);
214
- if (legacyState !== null) {
215
- return legacyState;
216
- }
217
- // Make sure the variable is initialized.
218
- this.initModel();
219
- const state = {'id': this.variable_.getId()};
220
- if (doFullSerialization) {
221
- state['name'] = this.variable_.name;
222
- state['type'] = this.variable_.type;
240
+ /**
241
+ * Sets the field's value based on the given state.
242
+ * @param {*} state The state of the variable to assign to this variable
243
+ * field.
244
+ * @override
245
+ * @package
246
+ */
247
+ loadState(state) {
248
+ if (this.loadLegacyState(FieldVariable, state)) {
249
+ return;
250
+ }
251
+ // This is necessary so that blocks in the flyout can have custom var names.
252
+ const variable = Variables.getOrCreateVariablePackage(
253
+ this.sourceBlock_.workspace, state['id'] || null, state['name'],
254
+ state['type'] || '');
255
+ this.setValue(variable.getId());
223
256
  }
224
- return state;
225
- };
226
257
 
227
- /**
228
- * Sets the field's value based on the given state.
229
- * @param {*} state The state of the variable to assign to this variable field.
230
- * @override
231
- * @package
232
- */
233
- FieldVariable.prototype.loadState = function(state) {
234
- if (this.loadLegacyState(FieldVariable, state)) {
235
- return;
258
+ /**
259
+ * Attach this field to a block.
260
+ * @param {!Block} block The block containing this field.
261
+ */
262
+ setSourceBlock(block) {
263
+ if (block.isShadow()) {
264
+ throw Error('Variable fields are not allowed to exist on shadow blocks.');
265
+ }
266
+ super.setSourceBlock(block);
236
267
  }
237
- // This is necessary so that blocks in the flyout can have custom var names.
238
- const variable = Variables.getOrCreateVariablePackage(
239
- this.sourceBlock_.workspace, state['id'] || null, state['name'],
240
- state['type'] || '');
241
- this.setValue(variable.getId());
242
- };
243
268
 
244
- /**
245
- * Attach this field to a block.
246
- * @param {!Block} block The block containing this field.
247
- */
248
- FieldVariable.prototype.setSourceBlock = function(block) {
249
- if (block.isShadow()) {
250
- throw Error('Variable fields are not allowed to exist on shadow blocks.');
269
+ /**
270
+ * Get the variable's ID.
271
+ * @return {?string} Current variable's ID.
272
+ */
273
+ getValue() {
274
+ return this.variable_ ? this.variable_.getId() : null;
251
275
  }
252
- FieldVariable.superClass_.setSourceBlock.call(this, block);
253
- };
254
-
255
- /**
256
- * Get the variable's ID.
257
- * @return {string} Current variable's ID.
258
- */
259
- FieldVariable.prototype.getValue = function() {
260
- return this.variable_ ? this.variable_.getId() : null;
261
- };
262
-
263
- /**
264
- * Get the text from this field, which is the selected variable's name.
265
- * @return {string} The selected variable's name, or the empty string if no
266
- * variable is selected.
267
- */
268
- FieldVariable.prototype.getText = function() {
269
- return this.variable_ ? this.variable_.name : '';
270
- };
271
-
272
- /**
273
- * Get the variable model for the selected variable.
274
- * Not guaranteed to be in the variable map on the workspace (e.g. if accessed
275
- * after the variable has been deleted).
276
- * @return {?VariableModel} The selected variable, or null if none was
277
- * selected.
278
- * @package
279
- */
280
- FieldVariable.prototype.getVariable = function() {
281
- return this.variable_;
282
- };
283
276
 
284
- /**
285
- * Gets the validation function for this field, or null if not set.
286
- * Returns null if the variable is not set, because validators should not
287
- * run on the initial setValue call, because the field won't be attached to
288
- * a block and workspace at that point.
289
- * @return {?Function} Validation function, or null.
290
- */
291
- FieldVariable.prototype.getValidator = function() {
292
- // Validators shouldn't operate on the initial setValue call.
293
- // Normally this is achieved by calling setValidator after setValue, but
294
- // this is not a possibility with variable fields.
295
- if (this.variable_) {
296
- return this.validator_;
277
+ /**
278
+ * Get the text from this field, which is the selected variable's name.
279
+ * @return {string} The selected variable's name, or the empty string if no
280
+ * variable is selected.
281
+ */
282
+ getText() {
283
+ return this.variable_ ? this.variable_.name : '';
297
284
  }
298
- return null;
299
- };
300
285
 
301
- /**
302
- * Ensure that the ID belongs to a valid variable of an allowed type.
303
- * @param {*=} opt_newValue The ID of the new variable to set.
304
- * @return {?string} The validated ID, or null if invalid.
305
- * @protected
306
- */
307
- FieldVariable.prototype.doClassValidation_ = function(opt_newValue) {
308
- if (opt_newValue === null) {
309
- return null;
310
- }
311
- const newId = /** @type {string} */ (opt_newValue);
312
- const variable = Variables.getVariable(this.sourceBlock_.workspace, newId);
313
- if (!variable) {
314
- console.warn(
315
- 'Variable id doesn\'t point to a real variable! ' +
316
- 'ID was ' + newId);
317
- return null;
286
+ /**
287
+ * Get the variable model for the selected variable.
288
+ * Not guaranteed to be in the variable map on the workspace (e.g. if accessed
289
+ * after the variable has been deleted).
290
+ * @return {?VariableModel} The selected variable, or null if none was
291
+ * selected.
292
+ * @package
293
+ */
294
+ getVariable() {
295
+ return this.variable_;
318
296
  }
319
- // Type Checks.
320
- const type = variable.type;
321
- if (!this.typeIsAllowed_(type)) {
322
- console.warn('Variable type doesn\'t match this field! Type was ' + type);
297
+
298
+ /**
299
+ * Gets the validation function for this field, or null if not set.
300
+ * Returns null if the variable is not set, because validators should not
301
+ * run on the initial setValue call, because the field won't be attached to
302
+ * a block and workspace at that point.
303
+ * @return {?Function} Validation function, or null.
304
+ */
305
+ getValidator() {
306
+ // Validators shouldn't operate on the initial setValue call.
307
+ // Normally this is achieved by calling setValidator after setValue, but
308
+ // this is not a possibility with variable fields.
309
+ if (this.variable_) {
310
+ return this.validator_;
311
+ }
323
312
  return null;
324
313
  }
325
- return newId;
326
- };
327
314
 
328
- /**
329
- * Update the value of this variable field, as well as its variable and text.
330
- *
331
- * The variable ID should be valid at this point, but if a variable field
332
- * validator returns a bad ID, this could break.
333
- * @param {*} newId The value to be saved.
334
- * @protected
335
- */
336
- FieldVariable.prototype.doValueUpdate_ = function(newId) {
337
- this.variable_ = Variables.getVariable(
338
- this.sourceBlock_.workspace, /** @type {string} */ (newId));
339
- FieldVariable.superClass_.doValueUpdate_.call(this, newId);
340
- };
341
-
342
- /**
343
- * Check whether the given variable type is allowed on this field.
344
- * @param {string} type The type to check.
345
- * @return {boolean} True if the type is in the list of allowed types.
346
- * @private
347
- */
348
- FieldVariable.prototype.typeIsAllowed_ = function(type) {
349
- const typeList = this.getVariableTypes_();
350
- if (!typeList) {
351
- return true; // If it's null, all types are valid.
352
- }
353
- for (let i = 0; i < typeList.length; i++) {
354
- if (type === typeList[i]) {
355
- return true;
315
+ /**
316
+ * Ensure that the ID belongs to a valid variable of an allowed type.
317
+ * @param {*=} opt_newValue The ID of the new variable to set.
318
+ * @return {?string} The validated ID, or null if invalid.
319
+ * @protected
320
+ */
321
+ doClassValidation_(opt_newValue) {
322
+ if (opt_newValue === null) {
323
+ return null;
324
+ }
325
+ const newId = /** @type {string} */ (opt_newValue);
326
+ const variable = Variables.getVariable(this.sourceBlock_.workspace, newId);
327
+ if (!variable) {
328
+ console.warn(
329
+ 'Variable id doesn\'t point to a real variable! ' +
330
+ 'ID was ' + newId);
331
+ return null;
356
332
  }
333
+ // Type Checks.
334
+ const type = variable.type;
335
+ if (!this.typeIsAllowed_(type)) {
336
+ console.warn(
337
+ 'Variable type doesn\'t match this field! Type was ' + type);
338
+ return null;
339
+ }
340
+ return newId;
357
341
  }
358
- return false;
359
- };
360
342
 
361
- /**
362
- * Return a list of variable types to include in the dropdown.
363
- * @return {!Array<string>} Array of variable types.
364
- * @throws {Error} if variableTypes is an empty array.
365
- * @private
366
- */
367
- FieldVariable.prototype.getVariableTypes_ = function() {
368
- // TODO (#1513): Try to avoid calling this every time the field is edited.
369
- let variableTypes = this.variableTypes;
370
- if (variableTypes === null) {
371
- // If variableTypes is null, return all variable types.
372
- if (this.sourceBlock_ && this.sourceBlock_.workspace) {
373
- return this.sourceBlock_.workspace.getVariableTypes();
374
- }
343
+ /**
344
+ * Update the value of this variable field, as well as its variable and text.
345
+ *
346
+ * The variable ID should be valid at this point, but if a variable field
347
+ * validator returns a bad ID, this could break.
348
+ * @param {*} newId The value to be saved.
349
+ * @protected
350
+ */
351
+ doValueUpdate_(newId) {
352
+ this.variable_ = Variables.getVariable(
353
+ this.sourceBlock_.workspace, /** @type {string} */ (newId));
354
+ super.doValueUpdate_(newId);
375
355
  }
376
- variableTypes = variableTypes || [''];
377
- if (variableTypes.length === 0) {
378
- // Throw an error if variableTypes is an empty list.
379
- const name = this.getText();
380
- throw Error(
381
- '\'variableTypes\' of field variable ' + name + ' was an empty list');
356
+
357
+ /**
358
+ * Check whether the given variable type is allowed on this field.
359
+ * @param {string} type The type to check.
360
+ * @return {boolean} True if the type is in the list of allowed types.
361
+ * @private
362
+ */
363
+ typeIsAllowed_(type) {
364
+ const typeList = this.getVariableTypes_();
365
+ if (!typeList) {
366
+ return true; // If it's null, all types are valid.
367
+ }
368
+ for (let i = 0; i < typeList.length; i++) {
369
+ if (type === typeList[i]) {
370
+ return true;
371
+ }
372
+ }
373
+ return false;
382
374
  }
383
- return variableTypes;
384
- };
385
375
 
386
- /**
387
- * Parse the optional arguments representing the allowed variable types and the
388
- * default variable type.
389
- * @param {Array<string>=} opt_variableTypes A list of the types of variables
390
- * to include in the dropdown. If null or undefined, variables of all types
391
- * will be displayed in the dropdown.
392
- * @param {string=} opt_defaultType The type of the variable to create if this
393
- * field's value is not explicitly set. Defaults to ''.
394
- * @private
395
- */
396
- FieldVariable.prototype.setTypes_ = function(
397
- opt_variableTypes, opt_defaultType) {
398
- // If you expected that the default type would be the same as the only entry
399
- // in the variable types array, tell the Blockly team by commenting on #1499.
400
- const defaultType = opt_defaultType || '';
401
- let variableTypes;
402
- // Set the allowable variable types. Null means all types on the workspace.
403
- if (opt_variableTypes === null || opt_variableTypes === undefined) {
404
- variableTypes = null;
405
- } else if (Array.isArray(opt_variableTypes)) {
406
- variableTypes = opt_variableTypes;
407
- // Make sure the default type is valid.
408
- let isInArray = false;
409
- for (let i = 0; i < variableTypes.length; i++) {
410
- if (variableTypes[i] === defaultType) {
411
- isInArray = true;
376
+ /**
377
+ * Return a list of variable types to include in the dropdown.
378
+ * @return {!Array<string>} Array of variable types.
379
+ * @throws {Error} if variableTypes is an empty array.
380
+ * @private
381
+ */
382
+ getVariableTypes_() {
383
+ // TODO (#1513): Try to avoid calling this every time the field is edited.
384
+ let variableTypes = this.variableTypes;
385
+ if (variableTypes === null) {
386
+ // If variableTypes is null, return all variable types.
387
+ if (this.sourceBlock_ && this.sourceBlock_.workspace) {
388
+ return this.sourceBlock_.workspace.getVariableTypes();
412
389
  }
413
390
  }
414
- if (!isInArray) {
391
+ variableTypes = variableTypes || [''];
392
+ if (variableTypes.length === 0) {
393
+ // Throw an error if variableTypes is an empty list.
394
+ const name = this.getText();
415
395
  throw Error(
416
- 'Invalid default type \'' + defaultType + '\' in ' +
417
- 'the definition of a FieldVariable');
396
+ '\'variableTypes\' of field variable ' + name + ' was an empty list');
418
397
  }
419
- } else {
420
- throw Error(
421
- '\'variableTypes\' was not an array in the definition of ' +
422
- 'a FieldVariable');
398
+ return variableTypes;
423
399
  }
424
- // Only update the field once all checks pass.
425
- this.defaultType_ = defaultType;
426
- this.variableTypes = variableTypes;
427
- };
428
400
 
429
- /**
430
- * Refreshes the name of the variable by grabbing the name of the model.
431
- * Used when a variable gets renamed, but the ID stays the same. Should only
432
- * be called by the block.
433
- * @package
434
- */
435
- FieldVariable.prototype.refreshVariableName = function() {
436
- this.forceRerender();
437
- };
401
+ /**
402
+ * Parse the optional arguments representing the allowed variable types and
403
+ * the default variable type.
404
+ * @param {Array<string>=} opt_variableTypes A list of the types of variables
405
+ * to include in the dropdown. If null or undefined, variables of all
406
+ * types will be displayed in the dropdown.
407
+ * @param {string=} opt_defaultType The type of the variable to create if this
408
+ * field's value is not explicitly set. Defaults to ''.
409
+ * @private
410
+ */
411
+ setTypes_(opt_variableTypes, opt_defaultType) {
412
+ // If you expected that the default type would be the same as the only entry
413
+ // in the variable types array, tell the Blockly team by commenting on
414
+ // #1499.
415
+ const defaultType = opt_defaultType || '';
416
+ let variableTypes;
417
+ // Set the allowable variable types. Null means all types on the workspace.
418
+ if (opt_variableTypes === null || opt_variableTypes === undefined) {
419
+ variableTypes = null;
420
+ } else if (Array.isArray(opt_variableTypes)) {
421
+ variableTypes = opt_variableTypes;
422
+ // Make sure the default type is valid.
423
+ let isInArray = false;
424
+ for (let i = 0; i < variableTypes.length; i++) {
425
+ if (variableTypes[i] === defaultType) {
426
+ isInArray = true;
427
+ }
428
+ }
429
+ if (!isInArray) {
430
+ throw Error(
431
+ 'Invalid default type \'' + defaultType + '\' in ' +
432
+ 'the definition of a FieldVariable');
433
+ }
434
+ } else {
435
+ throw Error(
436
+ '\'variableTypes\' was not an array in the definition of ' +
437
+ 'a FieldVariable');
438
+ }
439
+ // Only update the field once all checks pass.
440
+ this.defaultType_ = defaultType;
441
+ this.variableTypes = variableTypes;
442
+ }
438
443
 
439
- /**
440
- * Return a sorted list of variable names for variable dropdown menus.
441
- * Include a special option at the end for creating a new variable name.
442
- * @return {!Array<!Array>} Array of variable names/id tuples.
443
- * @this {FieldVariable}
444
- */
445
- FieldVariable.dropdownCreate = function() {
446
- if (!this.variable_) {
447
- throw Error(
448
- 'Tried to call dropdownCreate on a variable field with no' +
449
- ' variable selected.');
444
+ /**
445
+ * Refreshes the name of the variable by grabbing the name of the model.
446
+ * Used when a variable gets renamed, but the ID stays the same. Should only
447
+ * be called by the block.
448
+ * @override
449
+ * @package
450
+ */
451
+ refreshVariableName() {
452
+ this.forceRerender();
450
453
  }
451
- const name = this.getText();
452
- let variableModelList = [];
453
- if (this.sourceBlock_ && this.sourceBlock_.workspace) {
454
- const variableTypes = this.getVariableTypes_();
455
- // Get a copy of the list, so that adding rename and new variable options
456
- // doesn't modify the workspace's list.
457
- for (let i = 0; i < variableTypes.length; i++) {
458
- const variableType = variableTypes[i];
459
- const variables =
460
- this.sourceBlock_.workspace.getVariablesOfType(variableType);
461
- variableModelList = variableModelList.concat(variables);
454
+
455
+ /**
456
+ * Handle the selection of an item in the variable dropdown menu.
457
+ * Special case the 'Rename variable...' and 'Delete variable...' options.
458
+ * In the rename case, prompt the user for a new name.
459
+ * @param {!Menu} menu The Menu component clicked.
460
+ * @param {!MenuItem} menuItem The MenuItem selected within menu.
461
+ * @protected
462
+ */
463
+ onItemSelected_(menu, menuItem) {
464
+ const id = menuItem.getValue();
465
+ // Handle special cases.
466
+ if (this.sourceBlock_ && this.sourceBlock_.workspace) {
467
+ if (id === internalConstants.RENAME_VARIABLE_ID) {
468
+ // Rename variable.
469
+ Variables.renameVariable(
470
+ this.sourceBlock_.workspace,
471
+ /** @type {!VariableModel} */ (this.variable_));
472
+ return;
473
+ } else if (id === internalConstants.DELETE_VARIABLE_ID) {
474
+ // Delete variable.
475
+ this.sourceBlock_.workspace.deleteVariableById(this.variable_.getId());
476
+ return;
477
+ }
462
478
  }
479
+ // Handle unspecial case.
480
+ this.setValue(id);
463
481
  }
464
- variableModelList.sort(VariableModel.compareByName);
465
482
 
466
- const options = [];
467
- for (let i = 0; i < variableModelList.length; i++) {
468
- // Set the UUID as the internal representation of the variable.
469
- options[i] = [variableModelList[i].name, variableModelList[i].getId()];
483
+ /**
484
+ * Overrides referencesVariables(), indicating this field refers to a
485
+ * variable.
486
+ * @return {boolean} True.
487
+ * @package
488
+ * @override
489
+ */
490
+ referencesVariables() {
491
+ return true;
470
492
  }
471
- options.push([Msg['RENAME_VARIABLE'], internalConstants.RENAME_VARIABLE_ID]);
472
- if (Msg['DELETE_VARIABLE']) {
473
- options.push([
474
- Msg['DELETE_VARIABLE'].replace('%1', name),
475
- internalConstants.DELETE_VARIABLE_ID,
476
- ]);
493
+
494
+ /**
495
+ * Construct a FieldVariable from a JSON arg object,
496
+ * dereferencing any string table references.
497
+ * @param {!Object} options A JSON object with options (variable,
498
+ * variableTypes, and defaultType).
499
+ * @return {!FieldVariable} The new field instance.
500
+ * @package
501
+ * @nocollapse
502
+ * @override
503
+ */
504
+ static fromJson(options) {
505
+ const varName = parsing.replaceMessageReferences(options['variable']);
506
+ // `this` might be a subclass of FieldVariable if that class doesn't
507
+ // override the static fromJson method.
508
+ return new this(varName, undefined, undefined, undefined, options);
477
509
  }
478
510
 
479
- return options;
480
- };
511
+ /**
512
+ * Return a sorted list of variable names for variable dropdown menus.
513
+ * Include a special option at the end for creating a new variable name.
514
+ * @return {!Array<!Array>} Array of variable names/id tuples.
515
+ * @this {FieldVariable}
516
+ */
517
+ static dropdownCreate() {
518
+ if (!this.variable_) {
519
+ throw Error(
520
+ 'Tried to call dropdownCreate on a variable field with no' +
521
+ ' variable selected.');
522
+ }
523
+ const name = this.getText();
524
+ let variableModelList = [];
525
+ if (this.sourceBlock_ && this.sourceBlock_.workspace) {
526
+ const variableTypes = this.getVariableTypes_();
527
+ // Get a copy of the list, so that adding rename and new variable options
528
+ // doesn't modify the workspace's list.
529
+ for (let i = 0; i < variableTypes.length; i++) {
530
+ const variableType = variableTypes[i];
531
+ const variables =
532
+ this.sourceBlock_.workspace.getVariablesOfType(variableType);
533
+ variableModelList = variableModelList.concat(variables);
534
+ }
535
+ }
536
+ variableModelList.sort(VariableModel.compareByName);
481
537
 
482
- /**
483
- * Handle the selection of an item in the variable dropdown menu.
484
- * Special case the 'Rename variable...' and 'Delete variable...' options.
485
- * In the rename case, prompt the user for a new name.
486
- * @param {!Menu} menu The Menu component clicked.
487
- * @param {!MenuItem} menuItem The MenuItem selected within menu.
488
- * @protected
489
- */
490
- FieldVariable.prototype.onItemSelected_ = function(menu, menuItem) {
491
- const id = menuItem.getValue();
492
- // Handle special cases.
493
- if (this.sourceBlock_ && this.sourceBlock_.workspace) {
494
- if (id === internalConstants.RENAME_VARIABLE_ID) {
495
- // Rename variable.
496
- Variables.renameVariable(this.sourceBlock_.workspace, this.variable_);
497
- return;
498
- } else if (id === internalConstants.DELETE_VARIABLE_ID) {
499
- // Delete variable.
500
- this.sourceBlock_.workspace.deleteVariableById(this.variable_.getId());
501
- return;
538
+ const options = [];
539
+ for (let i = 0; i < variableModelList.length; i++) {
540
+ // Set the UUID as the internal representation of the variable.
541
+ options[i] = [variableModelList[i].name, variableModelList[i].getId()];
542
+ }
543
+ options.push(
544
+ [Msg['RENAME_VARIABLE'], internalConstants.RENAME_VARIABLE_ID]);
545
+ if (Msg['DELETE_VARIABLE']) {
546
+ options.push([
547
+ Msg['DELETE_VARIABLE'].replace('%1', name),
548
+ internalConstants.DELETE_VARIABLE_ID,
549
+ ]);
502
550
  }
503
- }
504
- // Handle unspecial case.
505
- this.setValue(id);
506
- };
507
551
 
508
- /**
509
- * Overrides referencesVariables(), indicating this field refers to a variable.
510
- * @return {boolean} True.
511
- * @package
512
- * @override
513
- */
514
- FieldVariable.prototype.referencesVariables = function() {
515
- return true;
516
- };
552
+ return options;
553
+ }
554
+ }
517
555
 
518
556
  fieldRegistry.register('field_variable', FieldVariable);
519
557