From f1b2bcc4f39340e38ae31f28c5de1e38be4f7305 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Tue, 7 Jun 2022 15:05:11 +0000 Subject: [PATCH 01/27] fix: convert files to typescript --- core/block.ts | 1391 ++++++------ core/block_animations.ts | 198 +- core/block_drag_surface.ts | 197 +- core/block_dragger.ts | 399 ++-- core/block_svg.ts | 1129 +++++----- core/blockly.ts | 928 ++++---- core/blockly_options.ts | 14 +- core/blocks.ts | 15 +- core/browser_events.ts | 181 +- core/bubble.ts | 800 ++++--- core/bubble_dragger.ts | 225 +- core/bump_objects.ts | 131 +- core/clipboard.ts | 52 +- core/comment.ts | 384 ++-- core/common.ts | 161 +- core/component_manager.ts | 207 +- core/config.ts | 42 +- core/connection.ts | 470 ++--- core/connection_checker.ts | 186 +- core/connection_db.ts | 177 +- core/connection_type.ts | 22 +- core/constants.ts | 15 +- core/contextmenu.ts | 278 ++- core/contextmenu_items.ts | 454 ++-- core/contextmenu_registry.ts | 169 +- core/css.ts | 47 +- core/delete_area.ts | 74 +- core/dialog.ts | 84 +- core/drag_target.ts | 86 +- core/dropdowndiv.ts | 633 +++--- core/events/events.ts | 201 +- core/events/events_abstract.ts | 101 +- core/events/events_block_base.ts | 47 +- core/events/events_block_change.ts | 93 +- core/events/events_block_create.ts | 73 +- core/events/events_block_delete.ts | 83 +- core/events/events_block_drag.ts | 63 +- core/events/events_block_move.ts | 123 +- core/events/events_bubble_open.ts | 64 +- core/events/events_click.ts | 59 +- core/events/events_comment_base.ts | 80 +- core/events/events_comment_change.ts | 71 +- core/events/events_comment_create.ts | 54 +- core/events/events_comment_delete.ts | 52 +- core/events/events_comment_move.ts | 95 +- core/events/events_marker_move.ts | 82 +- core/events/events_selected.ts | 60 +- core/events/events_theme_change.ts | 46 +- core/events/events_toolbox_item_select.ts | 62 +- core/events/events_trashcan_open.ts | 48 +- core/events/events_ui.ts | 59 +- core/events/events_ui_base.ts | 51 +- core/events/events_var_base.ts | 49 +- core/events/events_var_create.ts | 51 +- core/events/events_var_delete.ts | 51 +- core/events/events_var_rename.ts | 53 +- core/events/events_viewport.ts | 74 +- core/events/utils.ts | 354 ++-- core/events/workspace_events.ts | 67 +- core/extensions.ts | 387 ++-- core/field.ts | 924 ++++---- core/field_angle.ts | 494 ++--- core/field_checkbox.ts | 204 +- core/field_colour.ts | 611 +++--- core/field_dropdown.ts | 630 +++--- core/field_image.ts | 265 +-- core/field_label.ts | 118 +- core/field_label_serializable.ts | 74 +- core/field_multilineinput.ts | 334 ++- core/field_number.ts | 219 +- core/field_registry.ts | 70 +- core/field_textinput.ts | 509 ++--- core/field_variable.ts | 376 ++-- core/flyout_base.ts | 1039 +++++---- core/flyout_button.ts | 333 ++- core/flyout_horizontal.ts | 240 +-- core/flyout_metrics_manager.ts | 65 +- core/flyout_vertical.ts | 251 +-- core/generator.ts | 401 ++-- core/gesture.ts | 696 +++--- core/grid.ts | 217 +- core/icon.ts | 187 +- core/inject.ts | 310 +-- core/input.ts | 213 +- core/input_types.ts | 22 +- core/insertion_marker_manager.ts | 525 +++-- core/interfaces/i_ast_node_location.ts | 12 +- core/interfaces/i_ast_node_location_svg.ts | 42 +- .../i_ast_node_location_with_block.ts | 35 +- core/interfaces/i_autohideable.ts | 33 +- core/interfaces/i_block_dragger.ts | 92 +- core/interfaces/i_bounded_element.ts | 44 +- core/interfaces/i_bubble.ts | 120 +- core/interfaces/i_collapsible_toolbox_item.ts | 55 +- core/interfaces/i_component.ts | 29 +- core/interfaces/i_connection_checker.ts | 145 +- core/interfaces/i_contextmenu.ts | 28 +- core/interfaces/i_copyable.ts | 54 +- core/interfaces/i_deletable.ts | 24 +- core/interfaces/i_delete_area.ts | 49 +- core/interfaces/i_drag_target.ts | 110 +- core/interfaces/i_draggable.ts | 15 +- core/interfaces/i_flyout.ts | 349 ++- core/interfaces/i_keyboard_accessible.ts | 30 +- core/interfaces/i_metrics_manager.ts | 259 ++- core/interfaces/i_movable.ts | 24 +- core/interfaces/i_positionable.ts | 53 +- core/interfaces/i_registrable.ts | 16 +- core/interfaces/i_registrable_field.ts | 24 +- core/interfaces/i_selectable.ts | 39 +- core/interfaces/i_selectable_toolbox_item.ts | 93 +- core/interfaces/i_serializer.ts | 69 +- core/interfaces/i_styleable.ts | 36 +- core/interfaces/i_toolbox.ts | 216 +- core/interfaces/i_toolbox_item.ts | 145 +- core/internal_constants.ts | 37 +- core/keyboard_nav/ast_node.ts | 439 ++-- core/keyboard_nav/basic_cursor.ts | 134 +- core/keyboard_nav/cursor.ts | 76 +- core/keyboard_nav/marker.ts | 100 +- core/keyboard_nav/tab_navigate_cursor.ts | 34 +- core/marker_manager.ts | 157 +- core/menu.ts | 324 ++- core/menuitem.ts | 227 +- core/metrics_manager.ts | 328 ++- core/msg.ts | 15 +- core/mutator.ts | 441 ++-- core/names.ts | 178 +- core/options.ts | 463 ++-- core/positionable_helpers.ts | 151 +- core/procedures.ts | 314 ++- core/registry.ts | 314 ++- core/rendered_connection.ts | 398 ++-- core/renderers/common/block_rendering.ts | 211 +- core/renderers/common/constants.ts | 1500 ++++++------- core/renderers/common/debug.ts | 46 +- core/renderers/common/debugger.ts | 417 ++-- core/renderers/common/drawer.ts | 290 ++- core/renderers/common/i_path_object.ts | 274 ++- core/renderers/common/info.ts | 437 ++-- core/renderers/common/marker_svg.ts | 656 +++--- core/renderers/common/path_object.ts | 211 +- core/renderers/common/renderer.ts | 237 +-- core/renderers/geras/constants.ts | 74 +- core/renderers/geras/drawer.ts | 137 +- core/renderers/geras/geras.ts | 45 +- core/renderers/geras/highlight_constants.ts | 298 ++- core/renderers/geras/highlighter.ts | 217 +- core/renderers/geras/info.ts | 199 +- .../geras/measurables/inline_input.ts | 45 +- .../geras/measurables/statement_input.ts | 42 +- core/renderers/geras/path_object.ts | 124 +- core/renderers/geras/renderer.ts | 130 +- core/renderers/measurables/base.ts | 53 +- core/renderers/measurables/bottom_row.ts | 109 +- core/renderers/measurables/connection.ts | 48 +- .../measurables/external_value_input.ts | 61 +- core/renderers/measurables/field.ts | 55 +- core/renderers/measurables/hat.ts | 37 +- core/renderers/measurables/icon.ts | 38 +- core/renderers/measurables/in_row_spacer.ts | 32 +- core/renderers/measurables/inline_input.ts | 67 +- .../renderers/measurables/input_connection.ts | 62 +- core/renderers/measurables/input_row.ts | 63 +- core/renderers/measurables/jagged_edge.ts | 34 +- core/renderers/measurables/next_connection.ts | 41 +- .../measurables/output_connection.ts | 72 +- .../measurables/previous_connection.ts | 40 +- core/renderers/measurables/round_corner.ts | 42 +- core/renderers/measurables/row.ts | 268 +-- core/renderers/measurables/spacer_row.ts | 65 +- core/renderers/measurables/square_corner.ts | 42 +- core/renderers/measurables/statement_input.ts | 43 +- core/renderers/measurables/top_row.ts | 114 +- core/renderers/measurables/types.ts | 585 +++-- core/renderers/minimalist/constants.ts | 21 +- core/renderers/minimalist/drawer.ts | 29 +- core/renderers/minimalist/info.ts | 36 +- core/renderers/minimalist/minimalist.ts | 20 +- core/renderers/minimalist/renderer.ts | 64 +- core/renderers/thrasos/info.ts | 149 +- core/renderers/thrasos/renderer.ts | 37 +- core/renderers/thrasos/thrasos.ts | 14 +- core/renderers/zelos/constants.ts | 976 ++++----- core/renderers/zelos/drawer.ts | 209 +- core/renderers/zelos/info.ts | 345 ++- core/renderers/zelos/marker_svg.ts | 155 +- .../renderers/zelos/measurables/bottom_row.ts | 46 +- core/renderers/zelos/measurables/inputs.ts | 34 +- .../zelos/measurables/row_elements.ts | 40 +- core/renderers/zelos/measurables/top_row.ts | 51 +- core/renderers/zelos/path_object.ts | 194 +- core/renderers/zelos/renderer.ts | 135 +- core/renderers/zelos/zelos.ts | 49 +- core/scrollbar.ts | 671 +++--- core/scrollbar_pair.ts | 182 +- core/serialization/blocks.ts | 500 ++--- core/serialization/exceptions.ts | 128 +- core/serialization/priorities.ts | 22 +- core/serialization/registry.ts | 31 +- core/serialization/variables.ts | 69 +- core/serialization/workspaces.ts | 63 +- core/shortcut_items.ts | 162 +- core/shortcut_registry.ts | 238 +-- core/sprites.ts | 14 +- core/theme.ts | 238 +-- core/theme/classic.ts | 56 +- core/theme/themes.ts | 15 +- core/theme/zelos.ts | 32 +- core/theme_manager.ts | 169 +- core/toolbox/category.ts | 608 +++--- core/toolbox/collapsible_category.ts | 273 +-- core/toolbox/separator.ts | 111 +- core/toolbox/toolbox.ts | 781 +++---- core/toolbox/toolbox_item.ts | 146 +- core/tooltip.ts | 329 ++- core/touch.ts | 232 +- core/touch_gesture.ts | 221 +- core/trashcan.ts | 586 +++-- core/utils.ts | 440 ++-- core/utils/aria.ts | 117 +- core/utils/array.ts | 24 +- core/utils/colour.ts | 130 +- core/utils/coordinate.ts | 96 +- core/utils/deprecation.ts | 15 +- core/utils/dom.ts | 288 ++- core/utils/idgenerator.ts | 38 +- core/utils/keycodes.ts | 294 +-- core/utils/math.ts | 45 +- core/utils/metrics.ts | 182 +- core/utils/object.ts | 60 +- core/utils/parsing.ts | 262 ++- core/utils/rect.ts | 64 +- core/utils/sentinel.ts | 11 +- core/utils/size.ts | 47 +- core/utils/string.ts | 143 +- core/utils/style.ts | 183 +- core/utils/svg.ts | 199 +- core/utils/svg_math.ts | 145 +- core/utils/svg_paths.ts | 118 +- core/utils/toolbox.ts | 381 ++-- core/utils/useragent.ts | 240 +-- core/utils/xml.ts | 70 +- core/variable_map.ts | 229 +- core/variable_model.ts | 79 +- core/variables.ts | 408 ++-- core/variables_dynamic.ts | 102 +- core/warning.ts | 155 +- core/widgetdiv.ts | 251 ++- core/workspace.ts | 589 +++--- core/workspace_audio.ts | 102 +- core/workspace_comment.ts | 331 ++- core/workspace_comment_svg.ts | 931 ++++---- core/workspace_drag_surface_svg.ts | 173 +- core/workspace_dragger.ts | 89 +- core/workspace_svg.ts | 1878 ++++++++--------- core/xml.ts | 678 +++--- core/zoom_controls.ts | 572 +++-- 258 files changed, 23982 insertions(+), 29818 deletions(-) diff --git a/core/block.ts b/core/block.ts index cf4bafc7579..9b0987773d2 100644 --- a/core/block.ts +++ b/core/block.ts @@ -1,341 +1,257 @@ +/** @fileoverview The class representing one block. */ + /** * @license * Copyright 2011 Google LLC * SPDX-License-Identifier: Apache-2.0 */ -/** - * @fileoverview The class representing one block. - */ -'use strict'; /** * The class representing one block. * @class */ -goog.module('Blockly.Block'); - -const Extensions = goog.require('Blockly.Extensions'); -const Tooltip = goog.require('Blockly.Tooltip'); -const arrayUtils = goog.require('Blockly.utils.array'); -const common = goog.require('Blockly.common'); -const constants = goog.require('Blockly.constants'); -const eventUtils = goog.require('Blockly.Events.utils'); -const fieldRegistry = goog.require('Blockly.fieldRegistry'); -const idGenerator = goog.require('Blockly.utils.idGenerator'); -const parsing = goog.require('Blockly.utils.parsing'); + +// Unused import preserved for side-effects. Remove if unneeded. +import './events/events_block_change'; +// Unused import preserved for side-effects. Remove if unneeded. +import './events/events_block_create'; +// Unused import preserved for side-effects. Remove if unneeded. +import './events/events_block_delete'; + +import { Blocks } from './blocks.js'; /* eslint-disable-next-line no-unused-vars */ -const {Abstract} = goog.requireType('Blockly.Events.Abstract'); -const {Align, Input} = goog.require('Blockly.Input'); -const {ASTNode} = goog.require('Blockly.ASTNode'); +import { Comment } from './comment.js'; +import * as common from './common.js'; +import { Connection } from './connection.js'; +import { ConnectionType } from './connection_type.js'; +import * as constants from './constants.js'; /* eslint-disable-next-line no-unused-vars */ -const {BlockMove} = goog.requireType('Blockly.Events.BlockMove'); -const {Blocks} = goog.require('Blockly.blocks'); +import { Abstract } from './events/events_abstract.js'; /* eslint-disable-next-line no-unused-vars */ -const {Comment} = goog.requireType('Blockly.Comment'); -const {ConnectionType} = goog.require('Blockly.ConnectionType'); -const {Connection} = goog.require('Blockly.Connection'); -const {Coordinate} = goog.require('Blockly.utils.Coordinate'); +import { BlockMove } from './events/events_block_move.js'; +import * as eventUtils from './events/utils.js'; +import * as Extensions from './extensions.js'; /* eslint-disable-next-line no-unused-vars */ -const {Field} = goog.requireType('Blockly.Field'); +import { Field } from './field.js'; +import * as fieldRegistry from './field_registry.js'; +import { Align, Input } from './input.js'; +import { inputTypes } from './input_types.js'; /* eslint-disable-next-line no-unused-vars */ -const {IASTNodeLocation} = goog.require('Blockly.IASTNodeLocation'); +import { IASTNodeLocation } from './interfaces/i_ast_node_location.js'; /* eslint-disable-next-line no-unused-vars */ -const {IDeletable} = goog.require('Blockly.IDeletable'); +import { IDeletable } from './interfaces/i_deletable.js'; +import { ASTNode } from './keyboard_nav/ast_node.js'; /* eslint-disable-next-line no-unused-vars */ -const {Mutator} = goog.requireType('Blockly.Mutator'); -const {Size} = goog.require('Blockly.utils.Size'); +import { Mutator } from './mutator.js'; +import * as Tooltip from './tooltip.js'; +import * as arrayUtils from './utils/array.js'; +import { Coordinate } from './utils/coordinate.js'; +import * as idGenerator from './utils/idgenerator.js'; +import * as parsing from './utils/parsing.js'; +import { Size } from './utils/size.js'; /* eslint-disable-next-line no-unused-vars */ -const {VariableModel} = goog.requireType('Blockly.VariableModel'); +import { VariableModel } from './variable_model.js'; /* eslint-disable-next-line no-unused-vars */ -const {Workspace} = goog.requireType('Blockly.Workspace'); -const {inputTypes} = goog.require('Blockly.inputTypes'); -/** @suppress {extraRequire} */ -goog.require('Blockly.Events.BlockChange'); -/** @suppress {extraRequire} */ -goog.require('Blockly.Events.BlockCreate'); -/** @suppress {extraRequire} */ -goog.require('Blockly.Events.BlockDelete'); -/** @suppress {extraRequire} */ -goog.require('Blockly.Events.BlockMove'); +import { Workspace } from './workspace.js'; /** * Class for one block. * Not normally called directly, workspace.newBlock() is preferred. - * @implements {IASTNodeLocation} - * @implements {IDeletable} * @unrestricted * @alias Blockly.Block */ -class Block { +export class Block implements IASTNodeLocation, IDeletable { /** - * @param {!Workspace} workspace The block's workspace. - * @param {!string} prototypeName Name of the language object containing - * type-specific functions for this block. - * @param {string=} opt_id Optional ID. Use this ID if provided, otherwise - * create a new ID. - * @throws When the prototypeName is not valid or not allowed. + * An optional callback method to use whenever the block's parent workspace + * changes. This is usually only called from the constructor, the block type + * initializer function, or an extension initializer function. */ - constructor(workspace, prototypeName, opt_id) { - const {Generator} = goog.module.get('Blockly.Generator'); - if (Generator && - typeof Generator.prototype[prototypeName] !== 'undefined') { - // Occluding Generator class members is not allowed. - throw Error( - 'Block prototypeName "' + prototypeName + - '" conflicts with Blockly.Generator members.'); - } + onchange?: ((p1: Abstract) => AnyDuringMigration) | null; - /** - * Optional text data that round-trips between blocks and XML. - * Has no effect. May be used by 3rd parties for meta information. - * @type {?string} - */ - this.data = null; + /** The language-neutral ID given to the collapsed input. */ + static readonly COLLAPSED_INPUT_NAME: string = constants.COLLAPSED_INPUT_NAME; - /** - * Has this block been disposed of? - * @type {boolean} - * @package - */ - this.disposed = false; + /** The language-neutral ID given to the collapsed field. */ + static readonly COLLAPSED_FIELD_NAME: string = constants.COLLAPSED_FIELD_NAME; - /** - * Colour of the block as HSV hue value (0-360) - * This may be null if the block colour was not set via a hue number. - * @type {?number} - * @private - */ - this.hue_ = null; + /** + * Optional text data that round-trips between blocks and XML. + * Has no effect. May be used by 3rd parties for meta information. + */ + data: string | null = null; - /** - * Colour of the block in '#RRGGBB' format. - * @type {string} - * @protected - */ - this.colour_ = '#000000'; + /** Has this block been disposed of? */ + disposed = false; - /** - * Name of the block style. - * @type {string} - * @protected - */ - this.styleName_ = ''; + /** + * Colour of the block as HSV hue value (0-360) + * This may be null if the block colour was not set via a hue number. + */ + private hue_: number | null = null; - /** - * An optional method called during initialization. - * @type {undefined|?function()} - */ - this.init = undefined; + /** Colour of the block in '#RRGGBB' format. */ + protected colour_ = '#000000'; - /** - * An optional serialization method for defining how to serialize the - * mutation state to XML. This must be coupled with defining - * `domToMutation`. - * @type {undefined|?function(...):!Element} - */ - this.mutationToDom = undefined; + /** Name of the block style. */ + protected styleName_ = ''; - /** - * An optional deserialization method for defining how to deserialize the - * mutation state from XML. This must be coupled with defining - * `mutationToDom`. - * @type {undefined|?function(!Element)} - */ - this.domToMutation = undefined; + /** An optional method called during initialization. */ + init?: (() => AnyDuringMigration) | null = undefined; - /** - * An optional serialization method for defining how to serialize the - * block's extra state (eg mutation state) to something JSON compatible. - * This must be coupled with defining `loadExtraState`. - * @type {undefined|?function(): *} - */ - this.saveExtraState = undefined; + /** + * An optional serialization method for defining how to serialize the + * mutation state to XML. This must be coupled with defining + * `domToMutation`. + */ + mutationToDom?: ((...p1: AnyDuringMigration[]) => Element) | null = undefined; - /** - * An optional serialization method for defining how to deserialize the - * block's extra state (eg mutation state) from something JSON compatible. - * This must be coupled with defining `saveExtraState`. - * @type {undefined|?function(*)} - */ - this.loadExtraState = undefined; + /** + * An optional deserialization method for defining how to deserialize the + * mutation state from XML. This must be coupled with defining + * `mutationToDom`. + */ + domToMutation?: ((p1: Element) => AnyDuringMigration) | null = undefined; + /** + * An optional serialization method for defining how to serialize the + * block's extra state (eg mutation state) to something JSON compatible. + * This must be coupled with defining `loadExtraState`. + */ + saveExtraState?: (() => AnyDuringMigration) | null = undefined; - /** - * An optional property for suppressing adding STATEMENT_PREFIX and - * STATEMENT_SUFFIX to generated code. - * @type {?boolean} - */ - this.suppressPrefixSuffix = false; + /** + * An optional serialization method for defining how to deserialize the + * block's extra state (eg mutation state) from something JSON compatible. + * This must be coupled with defining `saveExtraState`. + */ + loadExtraState?: + ((p1: AnyDuringMigration) => AnyDuringMigration) | null = undefined; - /** - * An optional property for declaring developer variables. Return a list of - * variable names for use by generators. Developer variables are never - * shown to the user, but are declared as global variables in the generated - * code. - * @type {undefined|?function():!Array} - */ - this.getDeveloperVariables = undefined; + /** + * An optional property for suppressing adding STATEMENT_PREFIX and + * STATEMENT_SUFFIX to generated code. + */ + suppressPrefixSuffix: boolean | null = false; - /** - * An optional function that reconfigures the block based on the contents of - * the mutator dialog. - * @type {undefined|?function(!Block):void} - */ - this.compose = undefined; + /** + * An optional property for declaring developer variables. Return a list of + * variable names for use by generators. Developer variables are never + * shown to the user, but are declared as global variables in the generated + * code. + */ + getDeveloperVariables?: (() => string[]) | null = undefined; - /** - * An optional function that populates the mutator's dialog with - * this block's components. - * @type {undefined|?function(!Workspace):!Block} - */ - this.decompose = undefined; + /** + * An optional function that reconfigures the block based on the contents of + * the mutator dialog. + */ + compose?: ((p1: Block) => void) | null = null; - /** @type {string} */ - this.id = (opt_id && !workspace.getBlockById(opt_id)) ? - opt_id : - idGenerator.genUid(); - workspace.setBlockById(this.id, this); - /** @type {Connection} */ - this.outputConnection = null; - /** @type {Connection} */ - this.nextConnection = null; - /** @type {Connection} */ - this.previousConnection = null; - /** @type {!Array} */ - this.inputList = []; - /** @type {boolean|undefined} */ - this.inputsInline = undefined; - /** - * @type {boolean} - * @private - */ - this.disabled = false; - /** @type {!Tooltip.TipInfo} */ - this.tooltip = ''; - /** @type {boolean} */ - this.contextMenu = true; + /** + * An optional function that populates the mutator's dialog with + * this block's components. + */ + decompose?: ((p1: Workspace) => Block) | null = null; + id: string; + // AnyDuringMigration because: Type 'null' is not assignable to type + // 'Connection'. + outputConnection: Connection = null as AnyDuringMigration; + // AnyDuringMigration because: Type 'null' is not assignable to type + // 'Connection'. + nextConnection: Connection = null as AnyDuringMigration; + // AnyDuringMigration because: Type 'null' is not assignable to type + // 'Connection'. + previousConnection: Connection = null as AnyDuringMigration; + inputList: Input[] = []; + inputsInline?: boolean = undefined; + private disabled = false; + tooltip: Tooltip.TipInfo = ''; + contextMenu = true; - /** - * @type {Block} - * @protected - */ - this.parentBlock_ = null; + protected parentBlock_: this | null = null; - /** - * @type {!Array} - * @protected - */ - this.childBlocks_ = []; + protected childBlocks_: this[] = []; - /** - * @type {boolean} - * @private - */ - this.deletable_ = true; + private deletable_ = true; - /** - * @type {boolean} - * @private - */ - this.movable_ = true; + private movable_ = true; - /** - * @type {boolean} - * @private - */ - this.editable_ = true; + private editable_ = true; - /** - * @type {boolean} - * @private - */ - this.isShadow_ = false; + private isShadow_ = false; - /** - * @type {boolean} - * @protected - */ - this.collapsed_ = false; + protected collapsed_ = false; + protected outputShape_: number | null = null; - /** - * @type {?number} - * @protected - */ - this.outputShape_ = null; + /** + * A string representing the comment attached to this block. + * @deprecated August 2019. Use getCommentText instead. + */ + comment: string | Comment | null = null; + commentModel: CommentModel; + private readonly xy_: Coordinate; + isInFlyout: boolean; + isInMutator: boolean; + RTL: boolean; - /** - * A string representing the comment attached to this block. - * @type {string|Comment} - * @deprecated August 2019. Use getCommentText instead. - */ - this.comment = null; + /** True if this block is an insertion marker. */ + protected isInsertionMarker_ = false; - /** - * A model of the comment attached to this block. - * @type {!Block.CommentModel} - * @package - */ - this.commentModel = {text: null, pinned: false, size: new Size(160, 80)}; + /** Name of the type of hat. */ + hat?: string = undefined; - /** - * The block's position in workspace units. (0, 0) is at the workspace's - * origin; scale does not change this value. - * @type {!Coordinate} - * @private - */ - this.xy_ = new Coordinate(0, 0); + rendered: boolean | null = null; - /** @type {!Workspace} */ - this.workspace = workspace; - /** @type {boolean} */ - this.isInFlyout = workspace.isFlyout; - /** @type {boolean} */ - this.isInMutator = workspace.isMutator; + /** + * String for block help, or function that returns a URL. Null for no help. + */ + // AnyDuringMigration because: Type 'null' is not assignable to type 'string + // | Function'. + helpUrl: string | Function = null as AnyDuringMigration; - /** @type {boolean} */ - this.RTL = workspace.RTL; + /** A bound callback function to use when the parent workspace changes. */ + private onchangeWrapper_: ((p1: Abstract) => AnyDuringMigration) | null = null; - /** - * True if this block is an insertion marker. - * @type {boolean} - * @protected - */ - this.isInsertionMarker_ = false; + /** A count of statement inputs on the block. */ + statementInputCount = 0; + // TODO(b/109816955): remove '!', see go/strict-prop-init-fix. + type!: string; + // Record initial inline state. + inputsInlineDefault?: boolean; + workspace: Workspace; - /** - * Name of the type of hat. - * @type {string|undefined} - */ - this.hat = undefined; + /** + * @param workspace The block's workspace. + * @param prototypeName Name of the language object containing type-specific + * functions for this block. + * @param opt_id Optional ID. Use this ID if provided, otherwise create a new + * ID. + * @throws When the prototypeName is not valid or not allowed. + */ + constructor(workspace: Workspace, prototypeName: string, opt_id?: string) { + this.workspace = workspace; - /** @type {?boolean} */ - this.rendered = null; + this.id = opt_id && !workspace.getBlockById(opt_id) ? opt_id : + idGenerator.genUid(); + workspace.setBlockById(this.id, this); - /** - * String for block help, or function that returns a URL. Null for no help. - * @type {string|Function} - */ - this.helpUrl = null; + /** A model of the comment attached to this block. */ + this.commentModel = { text: null, pinned: false, size: new Size(160, 80) }; /** - * A bound callback function to use when the parent workspace changes. - * @type {?function(Abstract)} - * @private + * The block's position in workspace units. (0, 0) is at the workspace's + * origin; scale does not change this value. */ - this.onchangeWrapper_ = null; + this.xy_ = new Coordinate(0, 0); + this.isInFlyout = workspace.isFlyout; + this.isInMutator = workspace.isMutator; - /** - * A count of statement inputs on the block. - * @type {number} - * @package - */ - this.statementInputCount = 0; + this.RTL = workspace.RTL; // Copy the type-specific functions and data from the prototype. if (prototypeName) { - /** @type {string} */ this.type = prototypeName; const prototype = Blocks[prototypeName]; if (!prototype || typeof prototype !== 'object') { @@ -347,14 +263,13 @@ class Block { workspace.addTopBlock(this); workspace.addTypedBlock(this); - if (new.target === Block) this.doInit_(); + if (new.target === Block) { + this.doInit_(); + } } - /** - * Calls the init() function and handles associated event firing, etc. - * @protected - */ - doInit_() { + /** Calls the init() function and handles associated event firing, etc. */ + protected doInit_() { // All events fired should be part of the same group. // Any events fired during init should not be undoable, // so that block creation is atomic. @@ -374,7 +289,7 @@ class Block { // Fire a create event. if (eventUtils.isEnabled()) { - eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CREATE))(this)); + eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CREATE))!(this)); } } finally { if (!existingGroup) { @@ -383,9 +298,6 @@ class Block { // In case init threw, recordUndo flag should still be reset. eventUtils.setRecordUndo(initialUndoFlag); } - - // Record initial inline state. - /** @type {boolean|undefined} */ this.inputsInlineDefault = this.inputsInline; // Bind an onchange function, if it exists. @@ -396,12 +308,12 @@ class Block { /** * Dispose of this block. - * @param {boolean} healStack If true, then try to heal any gap by connecting - * the next statement with the previous statement. Otherwise, dispose of - * all children of this block. + * @param healStack If true, then try to heal any gap by connecting the next + * statement with the previous statement. Otherwise, dispose of all + * children of this block. * @suppress {checkTypes} */ - dispose(healStack) { + dispose(healStack: boolean) { if (!this.workspace) { // Already deleted. return; @@ -413,7 +325,7 @@ class Block { this.unplug(healStack); if (eventUtils.isEnabled()) { - eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_DELETE))(this)); + eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_DELETE))!(this)); } eventUtils.disable(); @@ -425,15 +337,6 @@ class Block { this.workspace.removeTypedBlock(this); // Remove from block database. this.workspace.removeBlockById(this.id); - this.workspace = null; - } - - // Just deleting this block from the DOM would result in a memory leak as - // well as corruption of the connection database. Therefore we must - // methodically step through the blocks and carefully disassemble them. - - if (common.getSelected() === this) { - common.setSelected(null); } // First, dispose of all my children. @@ -442,13 +345,13 @@ class Block { } // Then dispose of myself. // Dispose of all inputs and their fields. - for (let i = 0, input; (input = this.inputList[i]); i++) { + for (let i = 0, input; input = this.inputList[i]; i++) { input.dispose(); } this.inputList.length = 0; // Dispose of any remaining connections (next/previous/output). const connections = this.getConnections_(true); - for (let i = 0, connection; (connection = connections[i]); i++) { + for (let i = 0, connection; connection = connections[i]; i++) { connection.dispose(); } } finally { @@ -464,11 +367,10 @@ class Block { * before the first interaction with it. Interactions include UI actions * (e.g. clicking and dragging) and firing events (e.g. create, delete, and * change). - * @public */ initModel() { - for (let i = 0, input; (input = this.inputList[i]); i++) { - for (let j = 0, field; (field = input.fieldRow[j]); j++) { + for (let i = 0, input; input = this.inputList[i]; i++) { + for (let j = 0, field; field = input.fieldRow[j]; j++) { if (field.initModel) { field.initModel(); } @@ -479,10 +381,10 @@ class Block { /** * Unplug this block from its superior block. If this block is a statement, * optionally reconnect the block underneath with the block on top. - * @param {boolean=} opt_healStack Disconnect child statement and reconnect - * stack. Defaults to false. + * @param opt_healStack Disconnect child statement and reconnect stack. + * Defaults to false. */ - unplug(opt_healStack) { + unplug(opt_healStack?: boolean) { if (this.outputConnection) { this.unplugFromRow_(opt_healStack); } @@ -494,11 +396,10 @@ class Block { /** * Unplug this block's output from an input on another block. Optionally * reconnect the block's parent to the only child block, if possible. - * @param {boolean=} opt_healStack Disconnect right-side block and connect to - * left-side block. Defaults to false. - * @private + * @param opt_healStack Disconnect right-side block and connect to left-side + * block. Defaults to false. */ - unplugFromRow_(opt_healStack) { + private unplugFromRow_(opt_healStack?: boolean) { let parentConnection = null; if (this.outputConnection.isConnected()) { parentConnection = this.outputConnection.targetConnection; @@ -513,7 +414,7 @@ class Block { const thisConnection = this.getOnlyValueConnection_(); if (!thisConnection || !thisConnection.isConnected() || - thisConnection.targetBlock().isShadow()) { + thisConnection.targetBlock()!.isShadow()) { // Too many or too few possible connections on this block, or there's // nothing on the other side of this connection. return; @@ -521,13 +422,13 @@ class Block { const childConnection = thisConnection.targetConnection; // Disconnect the child block. - childConnection.disconnect(); + childConnection?.disconnect(); // Connect child to the parent if possible, otherwise bump away. if (this.workspace.connectionChecker.canConnect( - childConnection, parentConnection, false)) { - parentConnection.connect(childConnection); + childConnection, parentConnection, false)) { + parentConnection.connect(childConnection!); } else { - childConnection.onFailedConnect(parentConnection); + childConnection?.onFailedConnect(parentConnection); } } @@ -538,19 +439,19 @@ class Block { * Since only one block can be displaced and attached to the insertion marker * this should only ever return one connection. * - * @return {?Connection} The connection on the value input, or null. - * @private + * @return The connection on the value input, or null. */ - getOnlyValueConnection_() { + private getOnlyValueConnection_(): Connection | null { let connection = null; for (let i = 0; i < this.inputList.length; i++) { const thisConnection = this.inputList[i].connection; if (thisConnection && - thisConnection.type === ConnectionType.INPUT_VALUE && - thisConnection.targetConnection) { + thisConnection.type === ConnectionType.INPUT_VALUE && + thisConnection.targetConnection) { if (connection) { - return null; // More than one value input found. + return null; } + // More than one value input found. connection = thisConnection; } } @@ -560,11 +461,10 @@ class Block { /** * Unplug this statement block from its superior block. Optionally reconnect * the block underneath with the block on top. - * @param {boolean=} opt_healStack Disconnect child statement and reconnect - * stack. Defaults to false. - * @private + * @param opt_healStack Disconnect child statement and reconnect stack. + * Defaults to false. */ - unplugFromStack_(opt_healStack) { + private unplugFromStack_(opt_healStack?: boolean) { let previousTarget = null; if (this.previousConnection.isConnected()) { // Remember the connection that any next statements need to connect to. @@ -576,23 +476,22 @@ class Block { if (opt_healStack && nextBlock && !nextBlock.isShadow()) { // Disconnect the next statement. const nextTarget = this.nextConnection.targetConnection; - nextTarget.disconnect(); + nextTarget?.disconnect(); if (previousTarget && - this.workspace.connectionChecker.canConnect( - previousTarget, nextTarget, false)) { + this.workspace.connectionChecker.canConnect( + previousTarget, nextTarget, false)) { // Attach the next statement to the previous statement. - previousTarget.connect(nextTarget); + previousTarget.connect(nextTarget!); } } } /** * Returns all connections originating from this block. - * @param {boolean} _all If true, return all connections even hidden ones. - * @return {!Array} Array of connections. - * @package + * @param _all If true, return all connections even hidden ones. + * @return Array of connections. */ - getConnections_(_all) { + getConnections_(_all: boolean): Connection[] { const myConnections = []; if (this.outputConnection) { myConnections.push(this.outputConnection); @@ -603,7 +502,7 @@ class Block { if (this.nextConnection) { myConnections.push(this.nextConnection); } - for (let i = 0, input; (input = this.inputList[i]); i++) { + for (let i = 0, input; input = this.inputList[i]; i++) { if (input.connection) { myConnections.push(input.connection); } @@ -614,17 +513,16 @@ class Block { /** * Walks down a stack of blocks and finds the last next connection on the * stack. - * @param {boolean} ignoreShadows If true,the last connection on a non-shadow - * block will be returned. If false, this will follow shadows to find the - * last connection. - * @return {?Connection} The last next connection on the stack, or null. - * @package + * @param ignoreShadows If true,the last connection on a non-shadow block will + * be returned. If false, this will follow shadows to find the last + * connection. + * @return The last next connection on the stack, or null. */ - lastConnectionInStack(ignoreShadows) { + lastConnectionInStack(ignoreShadows: boolean): Connection | null { let nextConnection = this.nextConnection; while (nextConnection) { const nextBlock = nextConnection.targetBlock(); - if (!nextBlock || (ignoreShadows && nextBlock.isShadow())) { + if (!nextBlock || ignoreShadows && nextBlock.isShadow()) { return nextConnection; } nextConnection = nextBlock.nextConnection; @@ -636,29 +534,27 @@ class Block { * Bump unconnected blocks out of alignment. Two blocks which aren't actually * connected should not coincidentally line up on screen. */ - bumpNeighbours() { - // noop. - } + bumpNeighbours() {} + // noop. /** * Return the parent block or null if this block is at the top level. The * parent block is either the block connected to the previous connection (for * a statement block) or the block connected to the output connection (for a * value block). - * @return {?Block} The block (if any) that holds the current block. + * @return The block (if any) that holds the current block. */ - getParent() { + getParent(): this | null { return this.parentBlock_; } /** * Return the input that connects to the specified block. - * @param {!Block} block A block connected to an input on this block. - * @return {?Input} The input (if any) that connects to the specified - * block. + * @param block A block connected to an input on this block. + * @return The input (if any) that connects to the specified block. */ - getInputWithBlock(block) { - for (let i = 0, input; (input = this.inputList[i]); i++) { + getInputWithBlock(block: Block): Input | null { + for (let i = 0, input; input = this.inputList[i]; i++) { if (input.connection && input.connection.targetBlock() === block) { return input; } @@ -671,14 +567,16 @@ class Block { * block has no surrounding block. A parent block might just be the previous * statement, whereas the surrounding block is an if statement, while loop, * etc. - * @return {?Block} The block (if any) that surrounds the current block. + * @return The block (if any) that surrounds the current block. */ - getSurroundParent() { + getSurroundParent(): this | null { let block = this; let prevBlock; do { prevBlock = block; - block = block.getParent(); + // AnyDuringMigration because: Type 'Block | null' is not assignable to + // type 'this'. + block = block.getParent() as AnyDuringMigration; if (!block) { // Ran off the top. return null; @@ -690,30 +588,29 @@ class Block { /** * Return the next statement block directly connected to this block. - * @return {?Block} The next statement block or null. + * @return The next statement block or null. */ - getNextBlock() { + getNextBlock(): Block | null { return this.nextConnection && this.nextConnection.targetBlock(); } /** * Returns the block connected to the previous connection. - * @return {?Block} The previous statement block or null. + * @return The previous statement block or null. */ - getPreviousBlock() { + getPreviousBlock(): Block | null { return this.previousConnection && this.previousConnection.targetBlock(); } /** * Return the connection on the first statement input on this block, or null * if there are none. - * @return {?Connection} The first statement connection or null. - * @package + * @return The first statement connection or null. */ - getFirstStatementConnection() { - for (let i = 0, input; (input = this.inputList[i]); i++) { + getFirstStatementConnection(): Connection | null { + for (let i = 0, input; input = this.inputList[i]; i++) { if (input.connection && - input.connection.type === ConnectionType.NEXT_STATEMENT) { + input.connection.type === ConnectionType.NEXT_STATEMENT) { return input.connection; } } @@ -723,11 +620,11 @@ class Block { /** * Return the top-most block in this block's tree. * This will return itself if this block is at the top level. - * @return {!Block} The root block. + * @return The root block. */ - getRootBlock() { - let rootBlock; - let block = this; + getRootBlock(): this { + let rootBlock: this; + let block: this | null = this; do { rootBlock = block; block = rootBlock.parentBlock_; @@ -739,16 +636,17 @@ class Block { * Walk up from the given block up through the stack of blocks to find * the top block of the sub stack. If we are nested in a statement input only * find the top-most nested block. Do not go all the way to the root block. - * @return {!Block} The top block in a stack. - * @package + * @return The top block in a stack. */ - getTopStackBlock() { + getTopStackBlock(): this { let block = this; let previous; do { previous = block.getPreviousBlock(); + // AnyDuringMigration because: Type 'Block' is not assignable to type + // 'this'. } while (previous && previous.getNextBlock() === block && - (block = previous)); + (block = previous as AnyDuringMigration)); return block; } @@ -757,15 +655,15 @@ class Block { * Includes value and statement inputs, as well as any following statement. * Excludes any connection on an output tab or any preceding statement. * Blocks are optionally sorted by position; top to bottom. - * @param {boolean} ordered Sort the list if true. - * @return {!Array} Array of blocks. + * @param ordered Sort the list if true. + * @return Array of blocks. */ - getChildren(ordered) { + getChildren(ordered: boolean): Block[] { if (!ordered) { return this.childBlocks_; } const blocks = []; - for (let i = 0, input; (input = this.inputList[i]); i++) { + for (let i = 0, input; input = this.inputList[i]; i++) { if (input.connection) { const child = input.connection.targetBlock(); if (child) { @@ -782,10 +680,9 @@ class Block { /** * Set parent of this block to be a new block or null. - * @param {Block} newParent New parent block. - * @package + * @param newParent New parent block. */ - setParent(newParent) { + setParent(newParent: this | null) { if (newParent === this.parentBlock_) { return; } @@ -793,8 +690,8 @@ class Block { // Check that block is connected to new parent if new parent is not null and // that block is not connected to superior one if new parent is null. const targetBlock = - (this.previousConnection && this.previousConnection.targetBlock()) || - (this.outputConnection && this.outputConnection.targetBlock()); + this.previousConnection && this.previousConnection.targetBlock() || + this.outputConnection && this.outputConnection.targetBlock(); const isConnected = !!targetBlock; if (isConnected && newParent && targetBlock !== newParent) { @@ -803,17 +700,17 @@ class Block { throw Error('Block not connected to new parent.'); } else if (isConnected && !newParent) { throw Error( - 'Cannot set parent to null while block is still connected to' + - ' superior block.'); + 'Cannot set parent to null while block is still connected to' + + ' superior block.'); } + + // This block hasn't actually moved on-screen, so there's no need to + // update + // its connection locations. if (this.parentBlock_) { // Remove this block from the old parent's child list. arrayUtils.removeElem(this.parentBlock_.childBlocks_, this); - - // This block hasn't actually moved on-screen, so there's no need to - // update - // its connection locations. } else { // New parent must be non-null so remove this block from the workspace's // list of top-most blocks. @@ -835,49 +732,52 @@ class Block { * Includes value and statement inputs, as well as any following statements. * Excludes any connection on an output tab or any preceding statements. * Blocks are optionally sorted by position; top to bottom. - * @param {boolean} ordered Sort the list if true. - * @return {!Array} Flattened array of blocks. + * @param ordered Sort the list if true. + * @return Flattened array of blocks. */ - getDescendants(ordered) { + getDescendants(ordered: boolean): this[] { const blocks = [this]; const childBlocks = this.getChildren(ordered); - for (let child, i = 0; (child = childBlocks[i]); i++) { - blocks.push.apply(blocks, child.getDescendants(ordered)); + for (let child, i = 0; child = childBlocks[i]; i++) { + // AnyDuringMigration because: Argument of type 'Block[]' is not + // assignable to parameter of type 'this[]'. + blocks.push.apply( + blocks, child.getDescendants(ordered) as AnyDuringMigration); } return blocks; } /** * Get whether this block is deletable or not. - * @return {boolean} True if deletable. + * @return True if deletable. */ - isDeletable() { + isDeletable(): boolean { return this.deletable_ && !this.isShadow_ && - !(this.workspace && this.workspace.options.readOnly); + !(this.workspace && this.workspace.options.readOnly); } /** * Set whether this block is deletable or not. - * @param {boolean} deletable True if deletable. + * @param deletable True if deletable. */ - setDeletable(deletable) { + setDeletable(deletable: boolean) { this.deletable_ = deletable; } /** * Get whether this block is movable or not. - * @return {boolean} True if movable. + * @return True if movable. */ - isMovable() { + isMovable(): boolean { return this.movable_ && !this.isShadow_ && - !(this.workspace && this.workspace.options.readOnly); + !(this.workspace && this.workspace.options.readOnly); } /** * Set whether this block is movable or not. - * @param {boolean} movable True if movable. + * @param movable True if movable. */ - setMovable(movable) { + setMovable(movable: boolean) { this.movable_ = movable; } @@ -886,68 +786,66 @@ class Block { * descendants will put this block over the workspace's capacity this block is * not duplicatable. If duplicating this block and descendants will put any * type over their maxInstances this block is not duplicatable. - * @return {boolean} True if duplicatable. + * @return True if duplicatable. */ - isDuplicatable() { + isDuplicatable(): boolean { if (!this.workspace.hasBlockLimits()) { return true; } return this.workspace.isCapacityAvailable( - common.getBlockTypeCounts(this, true)); + common.getBlockTypeCounts(this, true)); } /** * Get whether this block is a shadow block or not. - * @return {boolean} True if a shadow. + * @return True if a shadow. */ - isShadow() { + isShadow(): boolean { return this.isShadow_; } /** * Set whether this block is a shadow block or not. - * @param {boolean} shadow True if a shadow. - * @package + * @param shadow True if a shadow. */ - setShadow(shadow) { + setShadow(shadow: boolean) { this.isShadow_ = shadow; } /** * Get whether this block is an insertion marker block or not. - * @return {boolean} True if an insertion marker. + * @return True if an insertion marker. */ - isInsertionMarker() { + isInsertionMarker(): boolean { return this.isInsertionMarker_; } /** * Set whether this block is an insertion marker block or not. * Once set this cannot be unset. - * @param {boolean} insertionMarker True if an insertion marker. - * @package + * @param insertionMarker True if an insertion marker. */ - setInsertionMarker(insertionMarker) { + setInsertionMarker(insertionMarker: boolean) { this.isInsertionMarker_ = insertionMarker; } /** * Get whether this block is editable or not. - * @return {boolean} True if editable. + * @return True if editable. */ - isEditable() { + isEditable(): boolean { return this.editable_ && - !(this.workspace && this.workspace.options.readOnly); + !(this.workspace && this.workspace.options.readOnly); } /** * Set whether this block is editable or not. - * @param {boolean} editable True if editable. + * @param editable True if editable. */ - setEditable(editable) { + setEditable(editable: boolean) { this.editable_ = editable; - for (let i = 0, input; (input = this.inputList[i]); i++) { - for (let j = 0, field; (field = input.fieldRow[j]); j++) { + for (let i = 0, input; input = this.inputList[i]; i++) { + for (let j = 0, field; field = input.fieldRow[j]; j++) { field.updateEditable(); } } @@ -955,9 +853,9 @@ class Block { /** * Returns if this block has been disposed of / deleted. - * @return {boolean} True if this block has been disposed of / deleted. + * @return True if this block has been disposed of / deleted. */ - isDisposed() { + isDisposed(): boolean { return this.disposed; } @@ -965,12 +863,11 @@ class Block { * Find the connection on this block that corresponds to the given connection * on the other block. * Used to match connections between a block and its insertion marker. - * @param {!Block} otherBlock The other block to match against. - * @param {!Connection} conn The other connection to match. - * @return {?Connection} The matching connection on this block, or null. - * @package + * @param otherBlock The other block to match against. + * @param conn The other connection to match. + * @return The matching connection on this block, or null. */ - getMatchingConnection(otherBlock, conn) { + getMatchingConnection(otherBlock: Block, conn: Connection): Connection | null { const connections = this.getConnections_(true); const otherConnections = otherBlock.getConnections_(true); if (connections.length !== otherConnections.length) { @@ -986,61 +883,61 @@ class Block { /** * Set the URL of this block's help page. - * @param {string|Function} url URL string for block help, or function that - * returns a URL. Null for no help. + * @param url URL string for block help, or function that returns a URL. Null + * for no help. */ - setHelpUrl(url) { + setHelpUrl(url: string | Function) { this.helpUrl = url; } /** * Sets the tooltip for this block. - * @param {!Tooltip.TipInfo} newTip The text for the tooltip, a function - * that returns the text for the tooltip, or a parent object whose tooltip - * will be used. To not display a tooltip pass the empty string. + * @param newTip The text for the tooltip, a function that returns the text + * for the tooltip, or a parent object whose tooltip will be used. To not + * display a tooltip pass the empty string. */ - setTooltip(newTip) { + setTooltip(newTip: Tooltip.TipInfo) { this.tooltip = newTip; } /** * Returns the tooltip text for this block. - * @return {!string} The tooltip text for this block. + * @return The tooltip text for this block. */ - getTooltip() { + getTooltip(): string { return Tooltip.getTooltipOfObject(this); } /** * Get the colour of a block. - * @return {string} #RRGGBB string. + * @return #RRGGBB string. */ - getColour() { + getColour(): string { return this.colour_; } /** * Get the name of the block style. - * @return {string} Name of the block style. + * @return Name of the block style. */ - getStyleName() { + getStyleName(): string { return this.styleName_; } /** * Get the HSV hue value of a block. Null if hue not set. - * @return {?number} Hue value (0-360). + * @return Hue value (0-360). */ - getHue() { + getHue(): number | null { return this.hue_; } /** * Change the colour of a block. - * @param {number|string} colour HSV hue value (0 to 360), #RRGGBB string, - * or a message reference string pointing to one of those two values. + * @param colour HSV hue value (0 to 360), #RRGGBB string, or a message + * reference string pointing to one of those two values. */ - setColour(colour) { + setColour(colour: number | string) { const parsed = parsing.parseBlockColour(colour); this.hue_ = parsed.hue; this.colour_ = parsed.hex; @@ -1048,9 +945,9 @@ class Block { /** * Set the style and colour values of a block. - * @param {string} blockStyleName Name of the block style. + * @param blockStyleName Name of the block style. */ - setStyle(blockStyleName) { + setStyle(blockStyleName: string) { this.styleName_ = blockStyleName; } @@ -1059,11 +956,10 @@ class Block { * changes, replacing any prior onchange handler. This is usually only called * from the constructor, the block type initializer function, or an extension * initializer function. - * @param {function(Abstract)} onchangeFn The callback to call - * when the block's workspace changes. + * @param onchangeFn The callback to call when the block's workspace changes. * @throws {Error} if onchangeFn is not falsey and not a function. */ - setOnChange(onchangeFn) { + setOnChange(onchangeFn: (p1: Abstract) => AnyDuringMigration) { if (onchangeFn && typeof onchangeFn !== 'function') { throw Error('onchange must be a function.'); } @@ -1071,27 +967,25 @@ class Block { this.workspace.removeChangeListener(this.onchangeWrapper_); } this.onchange = onchangeFn; - if (this.onchange) { - this.onchangeWrapper_ = onchangeFn.bind(this); - this.workspace.addChangeListener(this.onchangeWrapper_); - } + this.onchangeWrapper_ = onchangeFn.bind(this); + this.workspace.addChangeListener(this.onchangeWrapper_); } /** * Returns the named field from a block. - * @param {string} name The name of the field. - * @return {?Field} Named field, or null if field does not exist. + * @param name The name of the field. + * @return Named field, or null if field does not exist. */ - getField(name) { + getField(name: string): Field | null { if (typeof name !== 'string') { throw TypeError( - 'Block.prototype.getField expects a string ' + - 'with the field name but received ' + - (name === undefined ? 'nothing' : name + ' of type ' + typeof name) + - ' instead'); + 'Block.prototype.getField expects a string ' + + 'with the field name but received ' + + (name === undefined ? 'nothing' : name + ' of type ' + typeof name) + + ' instead'); } - for (let i = 0, input; (input = this.inputList[i]); i++) { - for (let j = 0, field; (field = input.fieldRow[j]); j++) { + for (let i = 0, input; input = this.inputList[i]; i++) { + for (let j = 0, field; field = input.fieldRow[j]; j++) { if (field.name === name) { return field; } @@ -1102,12 +996,12 @@ class Block { /** * Return all variables referenced by this block. - * @return {!Array} List of variable ids. + * @return List of variable ids. */ - getVars() { + getVars(): string[] { const vars = []; - for (let i = 0, input; (input = this.inputList[i]); i++) { - for (let j = 0, field; (field = input.fieldRow[j]); j++) { + for (let i = 0, input; input = this.inputList[i]; i++) { + for (let j = 0, field; field = input.fieldRow[j]; j++) { if (field.referencesVariables()) { vars.push(field.getValue()); } @@ -1118,16 +1012,15 @@ class Block { /** * Return all variables referenced by this block. - * @return {!Array} List of variable models. - * @package + * @return List of variable models. */ - getVarModels() { + getVarModels(): VariableModel[] { const vars = []; - for (let i = 0, input; (input = this.inputList[i]); i++) { - for (let j = 0, field; (field = input.fieldRow[j]); j++) { + for (let i = 0, input; input = this.inputList[i]; i++) { + for (let j = 0, field; field = input.fieldRow[j]; j++) { if (field.referencesVariables()) { - const model = this.workspace.getVariableById( - /** @type {string} */ (field.getValue())); + const model = + this.workspace.getVariableById(field.getValue() as string); // Check if the variable actually exists (and isn't just a potential // variable). if (model) { @@ -1142,14 +1035,13 @@ class Block { /** * Notification that a variable is renaming but keeping the same ID. If the * variable is in use on this block, rerender to show the new name. - * @param {!VariableModel} variable The variable being renamed. - * @package + * @param variable The variable being renamed. */ - updateVarName(variable) { - for (let i = 0, input; (input = this.inputList[i]); i++) { - for (let j = 0, field; (field = input.fieldRow[j]); j++) { + updateVarName(variable: VariableModel) { + for (let i = 0, input; input = this.inputList[i]; i++) { + for (let j = 0, field; field = input.fieldRow[j]; j++) { if (field.referencesVariables() && - variable.getId() === field.getValue()) { + variable.getId() === field.getValue()) { field.refreshVariableName(); } } @@ -1159,13 +1051,13 @@ class Block { /** * Notification that a variable is renaming. * If the ID matches one of this block's variables, rename it. - * @param {string} oldId ID of variable to rename. - * @param {string} newId ID of new variable. May be the same as oldId, but - * with an updated name. + * @param oldId ID of variable to rename. + * @param newId ID of new variable. May be the same as oldId, but with an + * updated name. */ - renameVarById(oldId, newId) { - for (let i = 0, input; (input = this.inputList[i]); i++) { - for (let j = 0, field; (field = input.fieldRow[j]); j++) { + renameVarById(oldId: string, newId: string) { + for (let i = 0, input; input = this.inputList[i]; i++) { + for (let j = 0, field; field = input.fieldRow[j]; j++) { if (field.referencesVariables() && oldId === field.getValue()) { field.setValue(newId); } @@ -1175,10 +1067,10 @@ class Block { /** * Returns the language-neutral value of the given field. - * @param {string} name The name of the field. - * @return {*} Value of the field or null if field does not exist. + * @param name The name of the field. + * @return Value of the field or null if field does not exist. */ - getFieldValue(name) { + getFieldValue(name: string): AnyDuringMigration { const field = this.getField(name); if (field) { return field.getValue(); @@ -1188,10 +1080,10 @@ class Block { /** * Sets the value of the given field for this block. - * @param {*} newValue The value to set. - * @param {string} name The name of the field to set the value of. + * @param newValue The value to set. + * @param name The name of the field to set the value of. */ - setFieldValue(newValue, name) { + setFieldValue(newValue: AnyDuringMigration, name: string) { const field = this.getField(name); if (!field) { throw Error('Field "' + name + '" not found.'); @@ -1201,110 +1093,113 @@ class Block { /** * Set whether this block can chain onto the bottom of another block. - * @param {boolean} newBoolean True if there can be a previous statement. - * @param {(string|Array|null)=} opt_check Statement type or - * list of statement types. Null/undefined if any type could be - * connected. + * @param newBoolean True if there can be a previous statement. + * @param opt_check Statement type or list of statement types. Null/undefined + * if any type could be connected. */ - setPreviousStatement(newBoolean, opt_check) { + setPreviousStatement(newBoolean: boolean, opt_check?: string | string[] | null) { if (newBoolean) { if (opt_check === undefined) { opt_check = null; } if (!this.previousConnection) { this.previousConnection = - this.makeConnection_(ConnectionType.PREVIOUS_STATEMENT); + this.makeConnection_(ConnectionType.PREVIOUS_STATEMENT); } this.previousConnection.setCheck(opt_check); } else { if (this.previousConnection) { if (this.previousConnection.isConnected()) { throw Error( - 'Must disconnect previous statement before removing ' + - 'connection.'); + 'Must disconnect previous statement before removing ' + + 'connection.'); } this.previousConnection.dispose(); - this.previousConnection = null; + // AnyDuringMigration because: Type 'null' is not assignable to type + // 'Connection'. + this.previousConnection = null as AnyDuringMigration; } } } /** * Set whether another block can chain onto the bottom of this block. - * @param {boolean} newBoolean True if there can be a next statement. - * @param {(string|Array|null)=} opt_check Statement type or - * list of statement types. Null/undefined if any type could be - * connected. + * @param newBoolean True if there can be a next statement. + * @param opt_check Statement type or list of statement types. Null/undefined + * if any type could be connected. */ - setNextStatement(newBoolean, opt_check) { + setNextStatement(newBoolean: boolean, opt_check?: string | string[] | null) { if (newBoolean) { if (opt_check === undefined) { opt_check = null; } if (!this.nextConnection) { this.nextConnection = - this.makeConnection_(ConnectionType.NEXT_STATEMENT); + this.makeConnection_(ConnectionType.NEXT_STATEMENT); } this.nextConnection.setCheck(opt_check); } else { if (this.nextConnection) { if (this.nextConnection.isConnected()) { throw Error( - 'Must disconnect next statement before removing ' + - 'connection.'); + 'Must disconnect next statement before removing ' + + 'connection.'); } this.nextConnection.dispose(); - this.nextConnection = null; + // AnyDuringMigration because: Type 'null' is not assignable to type + // 'Connection'. + this.nextConnection = null as AnyDuringMigration; } } } /** * Set whether this block returns a value. - * @param {boolean} newBoolean True if there is an output. - * @param {(string|Array|null)=} opt_check Returned type or list - * of returned types. Null or undefined if any type could be returned - * (e.g. variable get). + * @param newBoolean True if there is an output. + * @param opt_check Returned type or list of returned types. Null or + * undefined if any type could be returned (e.g. variable get). */ - setOutput(newBoolean, opt_check) { + setOutput(newBoolean: boolean, opt_check?: string | string[] | null) { if (newBoolean) { if (opt_check === undefined) { opt_check = null; } if (!this.outputConnection) { this.outputConnection = - this.makeConnection_(ConnectionType.OUTPUT_VALUE); + this.makeConnection_(ConnectionType.OUTPUT_VALUE); } this.outputConnection.setCheck(opt_check); } else { if (this.outputConnection) { if (this.outputConnection.isConnected()) { throw Error( - 'Must disconnect output value before removing connection.'); + 'Must disconnect output value before removing connection.'); } this.outputConnection.dispose(); - this.outputConnection = null; + // AnyDuringMigration because: Type 'null' is not assignable to type + // 'Connection'. + this.outputConnection = null as AnyDuringMigration; } } } /** * Set whether value inputs are arranged horizontally or vertically. - * @param {boolean} newBoolean True if inputs are horizontal. + * @param newBoolean True if inputs are horizontal. */ - setInputsInline(newBoolean) { + setInputsInline(newBoolean: boolean) { if (this.inputsInline !== newBoolean) { - eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CHANGE))( - this, 'inline', null, this.inputsInline, newBoolean)); + eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CHANGE))! + (this, 'inline', null, this.inputsInline, newBoolean)); this.inputsInline = newBoolean; } } /** * Get whether value inputs are arranged horizontally or vertically. - * @return {boolean} True if inputs are horizontal. + * @return True if inputs are horizontal. */ - getInputsInline() { + getInputsInline(): boolean { if (this.inputsInline !== undefined) { // Set explicitly. return this.inputsInline; @@ -1312,14 +1207,14 @@ class Block { // Not defined explicitly. Figure out what would look best. for (let i = 1; i < this.inputList.length; i++) { if (this.inputList[i - 1].type === inputTypes.DUMMY && - this.inputList[i].type === inputTypes.DUMMY) { + this.inputList[i].type === inputTypes.DUMMY) { // Two dummy inputs in a row. Don't inline them. return false; } } for (let i = 1; i < this.inputList.length; i++) { if (this.inputList[i - 1].type === inputTypes.VALUE && - this.inputList[i].type === inputTypes.DUMMY) { + this.inputList[i].type === inputTypes.DUMMY) { // Dummy input after a value input. Inline them. return true; } @@ -1329,47 +1224,47 @@ class Block { /** * Set the block's output shape. - * @param {?number} outputShape Value representing an output shape. + * @param outputShape Value representing an output shape. */ - setOutputShape(outputShape) { + setOutputShape(outputShape: number | null) { this.outputShape_ = outputShape; } /** * Get the block's output shape. - * @return {?number} Value representing output shape if one exists. + * @return Value representing output shape if one exists. */ - getOutputShape() { + getOutputShape(): number | null { return this.outputShape_; } /** * Get whether this block is enabled or not. - * @return {boolean} True if enabled. + * @return True if enabled. */ - isEnabled() { + isEnabled(): boolean { return !this.disabled; } /** * Set whether the block is enabled or not. - * @param {boolean} enabled True if enabled. + * @param enabled True if enabled. */ - setEnabled(enabled) { + setEnabled(enabled: boolean) { if (this.isEnabled() !== enabled) { const oldValue = this.disabled; this.disabled = !enabled; - eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CHANGE))( - this, 'disabled', null, oldValue, !enabled)); + eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CHANGE))! + (this, 'disabled', null, oldValue, !enabled)); } } /** * Get whether the block is disabled or not due to parents. * The block's own disabled property is not considered. - * @return {boolean} True if disabled. + * @return True if disabled. */ - getInheritedDisabled() { + getInheritedDisabled(): boolean { let ancestor = this.getSurroundParent(); while (ancestor) { if (ancestor.disabled) { @@ -1383,32 +1278,32 @@ class Block { /** * Get whether the block is collapsed or not. - * @return {boolean} True if collapsed. + * @return True if collapsed. */ - isCollapsed() { + isCollapsed(): boolean { return this.collapsed_; } /** * Set whether the block is collapsed or not. - * @param {boolean} collapsed True if collapsed. + * @param collapsed True if collapsed. */ - setCollapsed(collapsed) { + setCollapsed(collapsed: boolean) { if (this.collapsed_ !== collapsed) { - eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CHANGE))( - this, 'collapsed', null, this.collapsed_, collapsed)); + eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CHANGE))! + (this, 'collapsed', null, this.collapsed_, collapsed)); this.collapsed_ = collapsed; } } /** * Create a human-readable text representation of this block and any children. - * @param {number=} opt_maxLength Truncate the string to this length. - * @param {string=} opt_emptyToken The placeholder string used to denote an - * empty field. If not specified, '?' is used. - * @return {string} Text of block. + * @param opt_maxLength Truncate the string to this length. + * @param opt_emptyToken The placeholder string used to denote an empty field. + * If not specified, '?' is used. + * @return Text of block. */ - toString(opt_maxLength, opt_emptyToken) { + toString(opt_maxLength?: number, opt_emptyToken?: string): string { let text = []; const emptyFieldPlaceholder = opt_emptyToken || '?'; @@ -1421,24 +1316,22 @@ class Block { /** * Whether or not to add parentheses around an input. - * @param {!Connection} connection The connection. - * @return {boolean} True if we should add parentheses around the input. + * @param connection The connection. + * @return True if we should add parentheses around the input. */ - function shouldAddParentheses(connection) { + function shouldAddParentheses(connection: Connection): boolean { let checks = connection.getCheck(); if (!checks && connection.targetConnection) { checks = connection.targetConnection.getCheck(); } return !!checks && - (checks.indexOf('Boolean') !== -1 || checks.indexOf('Number') !== -1); + (checks.indexOf('Boolean') !== -1 || checks.indexOf('Number') !== -1); } - /** - * Check that we haven't circled back to the original root node. - */ + /** Check that we haven't circled back to the original root node. */ function checkRoot() { - if (node && node.getType() === rootNode.getType() && - node.getLocation() === rootNode.getLocation()) { + if (node && node.getType() === rootNode?.getType() && + node.getLocation() === rootNode?.getLocation()) { node = null; } } @@ -1447,7 +1340,7 @@ class Block { while (node) { switch (node.getType()) { case ASTNode.types.INPUT: { - const connection = /** @type {!Connection} */ (node.getLocation()); + const connection = node.getLocation() as Connection; if (!node.in()) { text.push(emptyFieldPlaceholder); } else if (shouldAddParentheses(connection)) { @@ -1456,7 +1349,7 @@ class Block { break; } case ASTNode.types.FIELD: { - const field = /** @type {Field} */ (node.getLocation()); + const field = node.getLocation() as Field; if (field.name !== constants.COLLAPSED_FIELD_NAME) { text.push(field.getText()); } @@ -1475,8 +1368,7 @@ class Block { checkRoot(); // If we hit an input on the way up, possibly close out parentheses. if (node && node.getType() === ASTNode.types.INPUT && - shouldAddParentheses( - /** @type {!Connection} */ (node.getLocation()))) { + shouldAddParentheses(node.getLocation() as Connection)) { text.push(')'); } } @@ -1500,17 +1392,23 @@ class Block { } // Join the text array, removing spaces around added parentheses. - text = text.reduce(function(acc, value) { - return acc + ((acc.substr(-1) === '(' || value === ')') ? '' : ' ') + - value; - }, ''); - text = text.trim() || '???'; + // AnyDuringMigration because: Type 'string' is not assignable to type + // 'any[]'. + text = text.reduce(function (acc, value) { + return acc + (acc.substr(-1) === '(' || value === ')' ? '' : ' ') + value; + }, '') as AnyDuringMigration; + // AnyDuringMigration because: Property 'trim' does not exist on type + // 'any[]'. + text = (text as AnyDuringMigration).trim() || '???'; if (opt_maxLength) { // TODO: Improve truncation so that text from this block is given // priority. E.g. "1+2+3+4+5+6+7+8+9=0" should be "...6+7+8+9=0", not // "1+2+3+4+5...". E.g. "1+2+3+4+5=6+7+8+9+0" should be "...4+5=6+7...". if (text.length > opt_maxLength) { - text = text.substring(0, opt_maxLength - 3) + '...'; + // AnyDuringMigration because: Type 'string' is not assignable to type + // 'any[]'. + text = (text.substring(0, opt_maxLength - 3) + '...') as + AnyDuringMigration; } } return text; @@ -1518,47 +1416,47 @@ class Block { /** * Shortcut for appending a value input row. - * @param {string} name Language-neutral identifier which may used to find - * this input again. Should be unique to this block. - * @return {!Input} The input object created. + * @param name Language-neutral identifier which may used to find this input + * again. Should be unique to this block. + * @return The input object created. */ - appendValueInput(name) { + appendValueInput(name: string): Input { return this.appendInput_(inputTypes.VALUE, name); } /** * Shortcut for appending a statement input row. - * @param {string} name Language-neutral identifier which may used to find - * this input again. Should be unique to this block. - * @return {!Input} The input object created. + * @param name Language-neutral identifier which may used to find this input + * again. Should be unique to this block. + * @return The input object created. */ - appendStatementInput(name) { + appendStatementInput(name: string): Input { return this.appendInput_(inputTypes.STATEMENT, name); } /** * Shortcut for appending a dummy input row. - * @param {string=} opt_name Language-neutral identifier which may used to - * find this input again. Should be unique to this block. - * @return {!Input} The input object created. + * @param opt_name Language-neutral identifier which may used to find this + * input again. Should be unique to this block. + * @return The input object created. */ - appendDummyInput(opt_name) { + appendDummyInput(opt_name?: string): Input { return this.appendInput_(inputTypes.DUMMY, opt_name || ''); } /** * Initialize this block using a cross-platform, internationalization-friendly * JSON description. - * @param {!Object} json Structured data describing the block. + * @param json Structured data describing the block. */ - jsonInit(json) { + jsonInit(json: AnyDuringMigration) { const warningPrefix = json['type'] ? 'Block "' + json['type'] + '": ' : ''; // Validate inputs. if (json['output'] && json['previousStatement']) { throw Error( - warningPrefix + - 'Must not have both an output and a previousStatement.'); + warningPrefix + + 'Must not have both an output and a previousStatement.'); } // Set basic properties of block. @@ -1582,8 +1480,8 @@ class Block { let i = 0; while (json['message' + i] !== undefined) { this.interpolate_( - json['message' + i], json['args' + i] || [], - json['lastDummyAlign' + i], warningPrefix); + json['message' + i], json['args' + i] || [], + json['lastDummyAlign' + i], warningPrefix); i++; } @@ -1621,12 +1519,13 @@ class Block { } if (typeof json['extensions'] === 'string') { console.warn( - warningPrefix + - 'JSON attribute \'extensions\' should be an array of' + - ' strings. Found raw string in JSON for \'' + json['type'] + - '\' block.'); - json['extensions'] = [json['extensions']]; // Correct and continue. + warningPrefix + + 'JSON attribute \'extensions\' should be an array of' + + ' strings. Found raw string in JSON for \'' + json['type'] + + '\' block.'); + json['extensions'] = [json['extensions']]; } + // Correct and continue. // Add the mutator to the block. if (json['mutator'] !== undefined) { @@ -1643,11 +1542,10 @@ class Block { /** * Initialize the colour of this block from the JSON description. - * @param {!Object} json Structured data describing the block. - * @param {string} warningPrefix Warning prefix string identifying block. - * @private + * @param json Structured data describing the block. + * @param warningPrefix Warning prefix string identifying block. */ - jsonInitColour_(json, warningPrefix) { + private jsonInitColour_(json: AnyDuringMigration, warningPrefix: string) { if ('colour' in json) { if (json['colour'] === undefined) { console.warn(warningPrefix + 'Undefined colour value.'); @@ -1664,11 +1562,10 @@ class Block { /** * Initialize the style of this block from the JSON description. - * @param {!Object} json Structured data describing the block. - * @param {string} warningPrefix Warning prefix string identifying block. - * @private + * @param json Structured data describing the block. + * @param warningPrefix Warning prefix string identifying block. */ - jsonInitStyle_(json, warningPrefix) { + private jsonInitStyle_(json: AnyDuringMigration, warningPrefix: string) { const blockStyleName = json['style']; try { this.setStyle(blockStyleName); @@ -1683,25 +1580,25 @@ class Block { * the block, including prototype values. This provides some insurance against * mixin / extension incompatibilities with future block features. This check * can be disabled by passing true as the second argument. - * @param {!Object} mixinObj The key/values pairs to add to this block object. - * @param {boolean=} opt_disableCheck Option flag to disable overwrite checks. + * @param mixinObj The key/values pairs to add to this block object. + * @param opt_disableCheck Option flag to disable overwrite checks. */ - mixin(mixinObj, opt_disableCheck) { + mixin(mixinObj: AnyDuringMigration, opt_disableCheck?: boolean) { if (opt_disableCheck !== undefined && - typeof opt_disableCheck !== 'boolean') { + typeof opt_disableCheck !== 'boolean') { throw Error('opt_disableCheck must be a boolean if provided'); } if (!opt_disableCheck) { const overwrites = []; for (const key in mixinObj) { - if (this[key] !== undefined) { + if ((this as AnyDuringMigration)[key] !== undefined) { overwrites.push(key); } } if (overwrites.length) { throw Error( - 'Mixin will overwrite block members: ' + - JSON.stringify(overwrites)); + 'Mixin will overwrite block members: ' + + JSON.stringify(overwrites)); } } Object.assign(this, mixinObj); @@ -1709,27 +1606,28 @@ class Block { /** * Interpolate a message description onto the block. - * @param {string} message Text contains interpolation tokens (%1, %2, ...) - * that match with fields or inputs defined in the args array. - * @param {!Array} args Array of arguments to be interpolated. - * @param {string|undefined} lastDummyAlign If a dummy input is added at the - * end, how should it be aligned? - * @param {string} warningPrefix Warning prefix string identifying block. - * @private - */ - interpolate_(message, args, lastDummyAlign, warningPrefix) { + * @param message Text contains interpolation tokens (%1, %2, ...) that match + * with fields or inputs defined in the args array. + * @param args Array of arguments to be interpolated. + * @param lastDummyAlign If a dummy input is added at the end, how should it + * be aligned? + * @param warningPrefix Warning prefix string identifying block. + */ + private interpolate_( + message: string, args: AnyDuringMigration[], + lastDummyAlign: string | undefined, warningPrefix: string) { const tokens = parsing.tokenizeInterpolation(message); this.validateTokens_(tokens, args.length); const elements = this.interpolateArguments_(tokens, args, lastDummyAlign); // An array of [field, fieldName] tuples. const fieldStack = []; - for (let i = 0, element; (element = elements[i]); i++) { + for (let i = 0, element; element = elements[i]; i++) { if (this.isInputKeyword_(element['type'])) { const input = this.inputFromJson_(element, warningPrefix); // Should never be null, but just in case. if (input) { - for (let j = 0, tuple; (tuple = fieldStack[j]); j++) { + for (let j = 0, tuple; tuple = fieldStack[j]; j++) { input.appendField(tuple[0], tuple[1]); } fieldStack.length = 0; @@ -1749,11 +1647,10 @@ class Block { * Validates that the tokens are within the correct bounds, with no * duplicates, and that all of the arguments are referred to. Throws errors if * any of these things are not true. - * @param {!Array} tokens An array of tokens to validate - * @param {number} argsCount The number of args that need to be referred to. - * @private + * @param tokens An array of tokens to validate + * @param argsCount The number of args that need to be referred to. */ - validateTokens_(tokens, argsCount) { + private validateTokens_(tokens: Array, argsCount: number) { const visitedArgsHash = []; let visitedArgsCount = 0; for (let i = 0; i < tokens.length; i++) { @@ -1763,21 +1660,21 @@ class Block { } if (token < 1 || token > argsCount) { throw Error( - 'Block "' + this.type + '": ' + - 'Message index %' + token + ' out of range.'); + 'Block "' + this.type + '": ' + + 'Message index %' + token + ' out of range.'); } if (visitedArgsHash[token]) { throw Error( - 'Block "' + this.type + '": ' + - 'Message index %' + token + ' duplicated.'); + 'Block "' + this.type + '": ' + + 'Message index %' + token + ' duplicated.'); } visitedArgsHash[token] = true; visitedArgsCount++; } if (visitedArgsCount !== argsCount) { throw Error( - 'Block "' + this.type + '": ' + - 'Message does not reference all ' + argsCount + ' arg(s).'); + 'Block "' + this.type + '": ' + + 'Message does not reference all ' + argsCount + ' arg(s).'); } } @@ -1785,15 +1682,15 @@ class Block { * Inserts args in place of numerical tokens. String args are converted to * JSON that defines a label field. If necessary an extra dummy input is added * to the end of the elements. - * @param {!Array} tokens The tokens to interpolate - * @param {!Array} args The arguments to insert. - * @param {string|undefined} lastDummyAlign The alignment the added dummy - * input should have, if we are required to add one. - * @return {!Array} The JSON definitions of field and inputs to add - * to the block. - * @private - */ - interpolateArguments_(tokens, args, lastDummyAlign) { + * @param tokens The tokens to interpolate + * @param args The arguments to insert. + * @param lastDummyAlign The alignment the added dummy input should have, if + * we are required to add one. + * @return The JSON definitions of field and inputs to add to the block. + */ + private interpolateArguments_( + tokens: Array, args: Array, + lastDummyAlign: string | undefined): AnyDuringMigration[] { const elements = []; for (let i = 0; i < tokens.length; i++) { let element = tokens[i]; @@ -1802,7 +1699,9 @@ class Block { } // Args can be strings, which is why this isn't elseif. if (typeof element === 'string') { - element = this.stringToFieldJson_(element); + // AnyDuringMigration because: Type '{ text: string; type: string; } | + // null' is not assignable to type 'string | number'. + element = this.stringToFieldJson_(element) as AnyDuringMigration; if (!element) { continue; } @@ -1811,10 +1710,12 @@ class Block { } const length = elements.length; - if (length && !this.isInputKeyword_(elements[length - 1]['type'])) { - const dummyInput = {'type': 'input_dummy'}; + if (length && + !this.isInputKeyword_( + (elements as AnyDuringMigration)[length - 1]['type'])) { + const dummyInput = { 'type': 'input_dummy' }; if (lastDummyAlign) { - dummyInput['align'] = lastDummyAlign; + (dummyInput as AnyDuringMigration)['align'] = lastDummyAlign; } elements.push(dummyInput); } @@ -1826,13 +1727,11 @@ class Block { * Creates a field from the JSON definition of a field. If a field with the * given type cannot be found, this attempts to create a different field using * the 'alt' property of the JSON definition (if it exists). - * @param {{alt:(string|undefined)}} element The element to try to turn into a - * field. - * @return {?Field} The field defined by the JSON, or null if one - * couldn't be created. - * @private + * @param element The element to try to turn into a field. + * @return The field defined by the JSON, or null if one couldn't be created. */ - fieldFromJson_(element) { + private fieldFromJson_(element: { alt?: string, type?: string, text?: string }): + Field | null { const field = fieldRegistry.fromJson(element); if (!field && element['alt']) { if (typeof element['alt'] === 'string') { @@ -1847,14 +1746,14 @@ class Block { /** * Creates an input from the JSON definition of an input. Sets the input's * check and alignment if they are provided. - * @param {!Object} element The JSON to turn into an input. - * @param {string} warningPrefix The prefix to add to warnings to help the - * developer debug. - * @return {?Input} The input that has been created, or null if one - * could not be created for some reason (should never happen). - * @private - */ - inputFromJson_(element, warningPrefix) { + * @param element The JSON to turn into an input. + * @param warningPrefix The prefix to add to warnings to help the developer + * debug. + * @return The input that has been created, or null if one could not be + * created for some reason (should never happen). + */ + private inputFromJson_(element: AnyDuringMigration, warningPrefix: string): + Input | null { const alignmentLookup = { 'LEFT': Align.LEFT, 'RIGHT': Align.RIGHT, @@ -1883,7 +1782,9 @@ class Block { input.setCheck(element['check']); } if (element['align']) { - const alignment = alignmentLookup[element['align'].toUpperCase()]; + const alignment = + (alignmentLookup as + AnyDuringMigration)[element['align'].toUpperCase()]; if (alignment === undefined) { console.warn(warningPrefix + 'Illegal align value: ', element['align']); } else { @@ -1895,25 +1796,22 @@ class Block { /** * Returns true if the given string matches one of the input keywords. - * @param {string} str The string to check. - * @return {boolean} True if the given string matches one of the input - * keywords, false otherwise. - * @private + * @param str The string to check. + * @return True if the given string matches one of the input keywords, false + * otherwise. */ - isInputKeyword_(str) { + private isInputKeyword_(str: string): boolean { return str === 'input_value' || str === 'input_statement' || - str === 'input_dummy'; + str === 'input_dummy'; } /** * Turns a string into the JSON definition of a label field. If the string * becomes an empty string when trimmed, this returns null. - * @param {string} str String to turn into the JSON definition of a label - * field. - * @return {?{text: string, type: string}} The JSON definition or null. - * @private + * @param str String to turn into the JSON definition of a label field. + * @return The JSON definition or null. */ - stringToFieldJson_(str) { + private stringToFieldJson_(str: string): { text: string, type: string } | null { str = str.trim(); if (str) { return { @@ -1926,13 +1824,12 @@ class Block { /** * Add a value input, statement input or local variable to this block. - * @param {number} type One of Blockly.inputTypes. - * @param {string} name Language-neutral identifier which may used to find - * this input again. Should be unique to this block. - * @return {!Input} The input object created. - * @protected + * @param type One of Blockly.inputTypes. + * @param name Language-neutral identifier which may used to find this input + * again. Should be unique to this block. + * @return The input object created. */ - appendInput_(type, name) { + protected appendInput_(type: number, name: string): Input { let connection = null; if (type === inputTypes.VALUE || type === inputTypes.STATEMENT) { connection = this.makeConnection_(type); @@ -1940,7 +1837,10 @@ class Block { if (type === inputTypes.STATEMENT) { this.statementInputCount++; } - const input = new Input(type, name, this, connection); + // AnyDuringMigration because: Argument of type 'Connection | null' is not + // assignable to parameter of type 'Connection'. + const input = + new Input(type, name, this, (connection as AnyDuringMigration)); // Append input to list. this.inputList.push(input); return input; @@ -1948,19 +1848,18 @@ class Block { /** * Move a named input to a different location on this block. - * @param {string} name The name of the input to move. - * @param {?string} refName Name of input that should be after the moved - * input, - * or null to be the input at the end. + * @param name The name of the input to move. + * @param refName Name of input that should be after the moved input, or null + * to be the input at the end. */ - moveInputBefore(name, refName) { + moveInputBefore(name: string, refName: string | null) { if (name === refName) { return; } // Find both inputs. let inputIndex = -1; let refIndex = refName ? -1 : this.inputList.length; - for (let i = 0, input; (input = this.inputList[i]); i++) { + for (let i = 0, input; input = this.inputList[i]; i++) { if (input.name === name) { inputIndex = i; if (refIndex !== -1) { @@ -1984,11 +1883,10 @@ class Block { /** * Move a numbered input to a different location on this block. - * @param {number} inputIndex Index of the input to move. - * @param {number} refIndex Index of input that should be after the moved - * input. + * @param inputIndex Index of the input to move. + * @param refIndex Index of input that should be after the moved input. */ - moveNumberedInputBefore(inputIndex, refIndex) { + moveNumberedInputBefore(inputIndex: number, refIndex: number) { // Validate arguments. if (inputIndex === refIndex) { throw Error('Can\'t move input to itself.'); @@ -2011,15 +1909,14 @@ class Block { /** * Remove an input from this block. - * @param {string} name The name of the input. - * @param {boolean=} opt_quiet True to prevent an error if input is not - * present. - * @return {boolean} True if operation succeeds, false if input is not present - * and opt_quiet is true. + * @param name The name of the input. + * @param opt_quiet True to prevent an error if input is not present. + * @return True if operation succeeds, false if input is not present and + * opt_quiet is true. * @throws {Error} if the input is not present and opt_quiet is not true. */ - removeInput(name, opt_quiet) { - for (let i = 0, input; (input = this.inputList[i]); i++) { + removeInput(name: string, opt_quiet?: boolean): boolean { + for (let i = 0, input; input = this.inputList[i]; i++) { if (input.name === name) { if (input.type === inputTypes.STATEMENT) { this.statementInputCount--; @@ -2037,11 +1934,11 @@ class Block { /** * Fetches the named input object. - * @param {string} name The name of the input. - * @return {?Input} The input object, or null if input does not exist. + * @param name The name of the input. + * @return The input object, or null if input does not exist. */ - getInput(name) { - for (let i = 0, input; (input = this.inputList[i]); i++) { + getInput(name: string): Input | null { + for (let i = 0, input; input = this.inputList[i]; i++) { if (input.name === name) { return input; } @@ -2052,76 +1949,76 @@ class Block { /** * Fetches the block attached to the named input. - * @param {string} name The name of the input. - * @return {?Block} The attached value block, or null if the input is - * either disconnected or if the input does not exist. + * @param name The name of the input. + * @return The attached value block, or null if the input is either + * disconnected or if the input does not exist. */ - getInputTargetBlock(name) { + getInputTargetBlock(name: string): Block | null { const input = this.getInput(name); return input && input.connection && input.connection.targetBlock(); } /** * Returns the comment on this block (or null if there is no comment). - * @return {?string} Block's comment. + * @return Block's comment. */ - getCommentText() { + getCommentText(): string | null { return this.commentModel.text; } /** * Set this block's comment text. - * @param {?string} text The text, or null to delete. + * @param text The text, or null to delete. */ - setCommentText(text) { + setCommentText(text: string | null) { if (this.commentModel.text === text) { return; } - eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CHANGE))( - this, 'comment', null, this.commentModel.text, text)); + eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CHANGE))! + (this, 'comment', null, this.commentModel.text, text)); this.commentModel.text = text; - this.comment = text; // For backwards compatibility. + // AnyDuringMigration because: Type 'string | null' is not assignable to + // type 'string | Comment'. + this.comment = text as AnyDuringMigration; } + // For backwards compatibility. /** * Set this block's warning text. - * @param {?string} _text The text, or null to delete. - * @param {string=} _opt_id An optional ID for the warning text to be able to - * maintain multiple warnings. + * @param _text The text, or null to delete. + * @param _opt_id An optional ID for the warning text to be able to maintain + * multiple warnings. */ - setWarningText(_text, _opt_id) { - // NOP. - } + setWarningText(_text: string | null, _opt_id?: string) {} + // NOP. /** * Give this block a mutator dialog. - * @param {Mutator} _mutator A mutator dialog instance or null to - * remove. + * @param _mutator A mutator dialog instance or null to remove. */ - setMutator(_mutator) { - // NOP. - } + setMutator(_mutator: Mutator) {} + // NOP. /** * Return the coordinates of the top-left corner of this block relative to the * drawing surface's origin (0,0), in workspace units. - * @return {!Coordinate} Object with .x and .y properties. + * @return Object with .x and .y properties. */ - getRelativeToSurfaceXY() { + getRelativeToSurfaceXY(): Coordinate { return this.xy_; } /** * Move a block by a relative offset. - * @param {number} dx Horizontal offset, in workspace units. - * @param {number} dy Vertical offset, in workspace units. + * @param dx Horizontal offset, in workspace units. + * @param dy Vertical offset, in workspace units. */ - moveBy(dx, dy) { + moveBy(dx: number, dy: number) { if (this.parentBlock_) { throw Error('Block has parent.'); } - const event = /** @type {!BlockMove} */ ( - new (eventUtils.get(eventUtils.BLOCK_MOVE))(this)); + const event = + new (eventUtils.get(eventUtils.BLOCK_MOVE))!(this) as BlockMove; this.xy_.translate(dx, dy); event.recordNew(); eventUtils.fire(event); @@ -2129,23 +2026,21 @@ class Block { /** * Create a connection of the specified type. - * @param {number} type The type of the connection to create. - * @return {!Connection} A new connection of the specified type. - * @protected + * @param type The type of the connection to create. + * @return A new connection of the specified type. */ - makeConnection_(type) { + protected makeConnection_(type: number): Connection { return new Connection(this, type); } /** * Recursively checks whether all statement and value inputs are filled with * blocks. Also checks all following statement blocks in this stack. - * @param {boolean=} opt_shadowBlocksAreFilled An optional argument - * controlling whether shadow blocks are counted as filled. Defaults to - * true. - * @return {boolean} True if all inputs are filled, false otherwise. + * @param opt_shadowBlocksAreFilled An optional argument controlling whether + * shadow blocks are counted as filled. Defaults to true. + * @return True if all inputs are filled, false otherwise. */ - allInputsFilled(opt_shadowBlocksAreFilled) { + allInputsFilled(opt_shadowBlocksAreFilled?: boolean): boolean { // Account for the shadow block filledness toggle. if (opt_shadowBlocksAreFilled === undefined) { opt_shadowBlocksAreFilled = true; @@ -2155,7 +2050,7 @@ class Block { } // Recursively check each input block of the current block. - for (let i = 0, input; (input = this.inputList[i]); i++) { + for (let i = 0, input; input = this.inputList[i]; i++) { if (!input.connection) { continue; } @@ -2181,9 +2076,9 @@ class Block { * Intended to on be used in console logs and errors. If you need a string * that uses the user's native language (including block text, field values, * and child blocks), use [toString()]{@link Block#toString}. - * @return {string} The description. + * @return The description. */ - toDevString() { + toDevString(): string { let msg = this.type ? '"' + this.type + '" block' : 'Block'; if (this.id) { msg += ' (id="' + this.id + '")'; @@ -2191,34 +2086,8 @@ class Block { return msg; } } - -/** - * @typedef {{ - * text:?string, - * pinned:boolean, - * size:Size - * }} - */ -Block.CommentModel; - -/** - * An optional callback method to use whenever the block's parent workspace - * changes. This is usually only called from the constructor, the block type - * initializer function, or an extension initializer function. - * @type {undefined|?function(Abstract)} - */ -Block.prototype.onchange; - -/** - * The language-neutral ID given to the collapsed input. - * @const {string} - */ -Block.COLLAPSED_INPUT_NAME = constants.COLLAPSED_INPUT_NAME; - -/** - * The language-neutral ID given to the collapsed field. - * @const {string} - */ -Block.COLLAPSED_FIELD_NAME = constants.COLLAPSED_FIELD_NAME; - -exports.Block = Block; +export interface CommentModel { + text: string | null; + pinned: boolean; + size: Size; +} diff --git a/core/block_animations.ts b/core/block_animations.ts index 47eb6404b91..5abd4f6e379 100644 --- a/core/block_animations.ts +++ b/core/block_animations.ts @@ -1,55 +1,43 @@ +/** @fileoverview Methods animating a block on connection and disconnection. */ + /** * @license * Copyright 2018 Google LLC * SPDX-License-Identifier: Apache-2.0 */ -/** - * @fileoverview Methods animating a block on connection and disconnection. - */ -'use strict'; /** * Methods animating a block on connection and disconnection. * @namespace Blockly.blockAnimations */ -goog.module('Blockly.blockAnimations'); -const dom = goog.require('Blockly.utils.dom'); /* eslint-disable-next-line no-unused-vars */ -const {BlockSvg} = goog.requireType('Blockly.BlockSvg'); -const {Svg} = goog.require('Blockly.utils.Svg'); - -/** - * A bounding box for a cloned block. - * @typedef {{ - * x: number, - * y: number, - * width: number, - * height: number - * }} - */ -let CloneRect; // eslint-disable-line no-unused-vars - -/** - * PID of disconnect UI animation. There can only be one at a time. - * @type {number} - */ +import { BlockSvg } from './block_svg.js'; +import * as dom from './utils/dom.js'; +import { Svg } from './utils/svg.js'; + +/** A bounding box for a cloned block. */ +interface CloneRect { + x: number; + y: number; + width: number; + height: number; +} // eslint-disable-line no-unused-vars + +/** PID of disconnect UI animation. There can only be one at a time. */ let disconnectPid = 0; -/** - * SVG group of wobbling block. There can only be one at a time. - * @type {Element} - */ -let disconnectGroup = null; +/** SVG group of wobbling block. There can only be one at a time. */ +// AnyDuringMigration because: Type 'null' is not assignable to type 'Element'. +let disconnectGroup: Element = null as AnyDuringMigration; /** * Play some UI effects (sound, animation) when disposing of a block. - * @param {!BlockSvg} block The block being disposed of. + * @param block The block being disposed of. * @alias Blockly.blockAnimations.disposeUiEffect - * @package */ -const disposeUiEffect = function(block) { +export function disposeUiEffect(block: BlockSvg) { const workspace = block.workspace; const svgGroup = block.getSvgRoot(); workspace.getAudioManager().play('delete'); @@ -57,56 +45,63 @@ const disposeUiEffect = function(block) { const xy = workspace.getSvgXY(svgGroup); // Deeply clone the current block. const clone = svgGroup.cloneNode(true); - clone.setAttribute('transform', 'translate(' + xy.x + ',' + xy.y + ')'); + // AnyDuringMigration because: Property 'setAttribute' does not exist on type + // 'Node'. + (clone as AnyDuringMigration) + .setAttribute('transform', 'translate(' + xy.x + ',' + xy.y + ')'); workspace.getParentSvg().appendChild(clone); const cloneRect = {'x': xy.x, 'y': xy.y, 'width': block.width, 'height': block.height}; // Start the animation. - disposeUiStep(clone, cloneRect, workspace.RTL, new Date, workspace.scale); -}; -exports.disposeUiEffect = disposeUiEffect; - + // AnyDuringMigration because: Argument of type 'Node' is not assignable to + // parameter of type 'Element'. + disposeUiStep( + clone as AnyDuringMigration, cloneRect, workspace.RTL, new Date(), + workspace.scale); +} /** * Animate a cloned block and eventually dispose of it. * This is a class method, not an instance method since the original block has * been destroyed and is no longer accessible. - * @param {!Element} clone SVG element to animate and dispose of. - * @param {!CloneRect} rect Starting rect of the clone. - * @param {boolean} rtl True if RTL, false if LTR. - * @param {!Date} start Date of animation's start. - * @param {number} workspaceScale Scale of workspace. + * @param clone SVG element to animate and dispose of. + * @param rect Starting rect of the clone. + * @param rtl True if RTL, false if LTR. + * @param start Date of animation's start. + * @param workspaceScale Scale of workspace. */ -const disposeUiStep = function(clone, rect, rtl, start, workspaceScale) { - const ms = (new Date).getTime() - start.getTime(); +function disposeUiStep( + clone: Element, rect: CloneRect, rtl: boolean, start: Date, + workspaceScale: number) { + const ms = new Date().getTime() - start.getTime(); const percent = ms / 150; if (percent > 1) { dom.removeNode(clone); } else { const x = - rect.x + (rtl ? -1 : 1) * rect.width * workspaceScale / 2 * percent; + rect.x + (rtl ? -1 : 1) * rect.width * workspaceScale / 2 * percent; const y = rect.y + rect.height * workspaceScale * percent; const scale = (1 - percent) * workspaceScale; clone.setAttribute( - 'transform', - 'translate(' + x + ',' + y + ')' + - ' scale(' + scale + ')'); + 'transform', + 'translate(' + x + ',' + y + ')' + + ' scale(' + scale + ')'); setTimeout(disposeUiStep, 10, clone, rect, rtl, start, workspaceScale); } -}; +} /** * Play some UI effects (sound, ripple) after a connection has been established. - * @param {!BlockSvg} block The block being connected. + * @param block The block being connected. * @alias Blockly.blockAnimations.connectionUiEffect - * @package */ -const connectionUiEffect = function(block) { +export function connectionUiEffect(block: BlockSvg) { const workspace = block.workspace; const scale = workspace.scale; workspace.getAudioManager().play('click'); if (scale < 1) { - return; // Too small to care about visual effects. + return; } + // Too small to care about visual effects. // Determine the absolute coordinates of the inferior block. const xy = workspace.getSvgXY(block.getSvgRoot()); // Offset the coordinates based on the two connection types, fix scale. @@ -118,49 +113,50 @@ const connectionUiEffect = function(block) { xy.y += 3 * scale; } const ripple = dom.createSvgElement( - Svg.CIRCLE, { - 'cx': xy.x, - 'cy': xy.y, - 'r': 0, - 'fill': 'none', - 'stroke': '#888', - 'stroke-width': 10, - }, - workspace.getParentSvg()); + Svg.CIRCLE, { + 'cx': xy.x, + 'cy': xy.y, + 'r': 0, + 'fill': 'none', + 'stroke': '#888', + 'stroke-width': 10, + }, + workspace.getParentSvg()); // Start the animation. - connectionUiStep(ripple, new Date, scale); -}; -exports.connectionUiEffect = connectionUiEffect; + connectionUiStep(ripple, new Date(), scale); +} /** * Expand a ripple around a connection. - * @param {!SVGElement} ripple Element to animate. - * @param {!Date} start Date of animation's start. - * @param {number} scale Scale of workspace. + * @param ripple Element to animate. + * @param start Date of animation's start. + * @param scale Scale of workspace. */ -const connectionUiStep = function(ripple, start, scale) { - const ms = (new Date).getTime() - start.getTime(); +function connectionUiStep(ripple: SVGElement, start: Date, scale: number) { + const ms = new Date().getTime() - start.getTime(); const percent = ms / 150; if (percent > 1) { dom.removeNode(ripple); } else { - ripple.setAttribute('r', percent * 25 * scale); - ripple.style.opacity = 1 - percent; + ripple.setAttribute('r', (percent * 25 * scale).toString()); + // AnyDuringMigration because: Type 'number' is not assignable to type + // 'string'. + ripple.style.opacity = (1 - percent) as AnyDuringMigration; disconnectPid = setTimeout(connectionUiStep, 10, ripple, start, scale); } -}; +} /** * Play some UI effects (sound, animation) when disconnecting a block. - * @param {!BlockSvg} block The block being disconnected. + * @param block The block being disconnected. * @alias Blockly.blockAnimations.disconnectUiEffect - * @package */ -const disconnectUiEffect = function(block) { +export function disconnectUiEffect(block: BlockSvg) { block.workspace.getAudioManager().play('disconnect'); if (block.workspace.scale < 1) { - return; // Too small to care about visual effects. + return; } + // Too small to care about visual effects. // Horizontal distance for bottom of block to wiggle. const DISPLACEMENT = 10; // Scale magnitude of skew to height of block. @@ -170,49 +166,51 @@ const disconnectUiEffect = function(block) { magnitude *= -1; } // Start the animation. - disconnectUiStep(block.getSvgRoot(), magnitude, new Date); -}; -exports.disconnectUiEffect = disconnectUiEffect; + disconnectUiStep(block.getSvgRoot(), magnitude, new Date()); +} /** * Animate a brief wiggle of a disconnected block. - * @param {!SVGElement} group SVG element to animate. - * @param {number} magnitude Maximum degrees skew (reversed for RTL). - * @param {!Date} start Date of animation's start. + * @param group SVG element to animate. + * @param magnitude Maximum degrees skew (reversed for RTL). + * @param start Date of animation's start. */ -const disconnectUiStep = function(group, magnitude, start) { - const DURATION = 200; // Milliseconds. - const WIGGLES = 3; // Half oscillations. +function disconnectUiStep(group: SVGElement, magnitude: number, start: Date) { + const DURATION = 200; + // Milliseconds. + const WIGGLES = 3; + // Half oscillations. - const ms = (new Date).getTime() - start.getTime(); + const ms = new Date().getTime() - start.getTime(); const percent = ms / DURATION; if (percent > 1) { - (/** @type {?} */ (group)).skew_ = ''; + (group as AnyDuringMigration).skew_ = ''; } else { const skew = Math.round( - Math.sin(percent * Math.PI * WIGGLES) * (1 - percent) * magnitude); - (/** @type {?} */ (group)).skew_ = 'skewX(' + skew + ')'; + Math.sin(percent * Math.PI * WIGGLES) * (1 - percent) * magnitude); + (group as AnyDuringMigration).skew_ = 'skewX(' + skew + ')'; disconnectGroup = group; disconnectPid = setTimeout(disconnectUiStep, 10, group, magnitude, start); } group.setAttribute( - 'transform', - (/** @type {?} */ (group)).translate_ + (/** @type {?} */ (group)).skew_); -}; + 'transform', + (group as AnyDuringMigration).translate_ + + (group as AnyDuringMigration).skew_); +} /** * Stop the disconnect UI animation immediately. * @alias Blockly.blockAnimations.disconnectUiStop - * @package */ -const disconnectUiStop = function() { +export function disconnectUiStop() { if (disconnectGroup) { clearTimeout(disconnectPid); const group = disconnectGroup; - (/** @type {?} */ (group)).skew_ = ''; - group.setAttribute('transform', (/** @type {?} */ (group)).translate_); - disconnectGroup = null; + (group as AnyDuringMigration).skew_ = ''; + group.setAttribute('transform', (group as AnyDuringMigration).translate_); + // AnyDuringMigration because: Type 'null' is not assignable to type + // 'Element'. + disconnectGroup = null as AnyDuringMigration; } -}; -exports.disconnectUiStop = disconnectUiStop; +} diff --git a/core/block_drag_surface.ts b/core/block_drag_surface.ts index 4cdeea8e105..229e9241429 100644 --- a/core/block_drag_surface.ts +++ b/core/block_drag_surface.ts @@ -1,9 +1,3 @@ -/** - * @license - * Copyright 2016 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - /** * @fileoverview A class that manages a surface for dragging blocks. When a * block drag is started, we move the block (and children) to a separate DOM @@ -13,7 +7,13 @@ * while dragging blocks. */ -'use strict'; +/** + * @license + * Copyright 2016 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + + /** * A class that manages a surface for dragging blocks. When a @@ -24,12 +24,11 @@ * while dragging blocks. * @class */ -goog.module('Blockly.BlockDragSurfaceSvg'); -const dom = goog.require('Blockly.utils.dom'); -const svgMath = goog.require('Blockly.utils.svgMath'); -const {Coordinate} = goog.require('Blockly.utils.Coordinate'); -const {Svg} = goog.require('Blockly.utils.Svg'); +import { Coordinate } from './utils/coordinate.js'; +import * as dom from './utils/dom.js'; +import { Svg } from './utils/svg.js'; +import * as svgMath from './utils/svg_math.js'; /** @@ -37,105 +36,86 @@ const {Svg} = goog.require('Blockly.utils.Svg'); * SVG that contains only the currently moving block, or nothing. * @alias Blockly.BlockDragSurfaceSvg */ -const BlockDragSurfaceSvg = class { +export class BlockDragSurfaceSvg { + /** The SVG drag surface. Set once by BlockDragSurfaceSvg.createDom. */ + private SVG_: SVGElement | null = null; + /** - * @param {!Element} container Containing element. + * This is where blocks live while they are being dragged if the drag + * surface is enabled. */ - constructor(container) { - /** - * The SVG drag surface. Set once by BlockDragSurfaceSvg.createDom. - * @type {?SVGElement} - * @private - */ - this.SVG_ = null; + private dragGroup_: SVGElement | null = null; - /** - * This is where blocks live while they are being dragged if the drag - * surface is enabled. - * @type {?SVGElement} - * @private - */ - this.dragGroup_ = null; - - /** - * Containing HTML element; parent of the workspace and the drag surface. - * @type {!Element} - * @private - */ - this.container_ = container; - - /** - * Cached value for the scale of the drag surface. - * Used to set/get the correct translation during and after a drag. - * @type {number} - * @private - */ - this.scale_ = 1; + /** + * Cached value for the scale of the drag surface. + * Used to set/get the correct translation during and after a drag. + */ + private scale_ = 1; - /** - * Cached value for the translation of the drag surface. - * This translation is in pixel units, because the scale is applied to the - * drag group rather than the top-level SVG. - * @type {?Coordinate} - * @private - */ - this.surfaceXY_ = null; + /** + * Cached value for the translation of the drag surface. + * This translation is in pixel units, because the scale is applied to the + * drag group rather than the top-level SVG. + */ + private surfaceXY_: Coordinate | null = null; + private readonly childSurfaceXY_: Coordinate; + /** @param container Containing element. */ + constructor(private readonly container: Element) { /** * Cached value for the translation of the child drag surface in pixel * units. Since the child drag surface tracks the translation of the * workspace this is ultimately the translation of the workspace. - * @type {!Coordinate} - * @private */ this.childSurfaceXY_ = new Coordinate(0, 0); this.createDom(); } - /** - * Create the drag surface and inject it into the container. - */ + /** Create the drag surface and inject it into the container. */ createDom() { if (this.SVG_) { - return; // Already created. + return; } + // Already created. this.SVG_ = dom.createSvgElement( - Svg.SVG, { - 'xmlns': dom.SVG_NS, - 'xmlns:html': dom.HTML_NS, - 'xmlns:xlink': dom.XLINK_NS, - 'version': '1.1', - 'class': 'blocklyBlockDragSurface', - }, - this.container_); - this.dragGroup_ = dom.createSvgElement(Svg.G, {}, this.SVG_); + Svg.SVG, { + 'xmlns': dom.SVG_NS, + 'xmlns:html': dom.HTML_NS, + 'xmlns:xlink': dom.XLINK_NS, + 'version': '1.1', + 'class': 'blocklyBlockDragSurface', + }, + this.container); + // AnyDuringMigration because: Argument of type 'SVGElement | null' is not + // assignable to parameter of type 'Element | undefined'. + this.dragGroup_ = + dom.createSvgElement(Svg.G, {}, this.SVG_ as AnyDuringMigration); } /** * Set the SVG blocks on the drag surface's group and show the surface. * Only one block group should be on the drag surface at a time. - * @param {!SVGElement} blocks Block or group of blocks to place on the drag - * surface. + * @param blocks Block or group of blocks to place on the drag surface. */ - setBlocksAndShow(blocks) { - if (this.dragGroup_.childNodes.length) { + setBlocksAndShow(blocks: SVGElement) { + if (this.dragGroup_!.childNodes.length) { throw Error('Already dragging a block.'); } // appendChild removes the blocks from the previous parent - this.dragGroup_.appendChild(blocks); - this.SVG_.style.display = 'block'; + this.dragGroup_!.appendChild(blocks); + this.SVG_!.style.display = 'block'; this.surfaceXY_ = new Coordinate(0, 0); } /** * Translate and scale the entire drag surface group to the given position, to * keep in sync with the workspace. - * @param {number} x X translation in pixel coordinates. - * @param {number} y Y translation in pixel coordinates. - * @param {number} scale Scale of the group. + * @param x X translation in pixel coordinates. + * @param y Y translation in pixel coordinates. + * @param scale Scale of the group. */ - translateAndScaleGroup(x, y, scale) { + translateAndScaleGroup(x: number, y: number, scale: number) { this.scale_ = scale; // Make sure the svg exists on a pixel boundary so that it is not fuzzy. const roundX = Math.round(x); @@ -159,17 +139,21 @@ const BlockDragSurfaceSvg = class { y = Math.round(y); this.SVG_.style.display = 'block'; - dom.setCssTransform(this.SVG_, 'translate3d(' + x + 'px, ' + y + 'px, 0)'); + // AnyDuringMigration because: Argument of type 'SVGElement | null' is not + // assignable to parameter of type 'Element'. + dom.setCssTransform( + this.SVG_ as AnyDuringMigration, + 'translate3d(' + x + 'px, ' + y + 'px, 0)'); } /** * Translates the entire surface by a relative offset. - * @param {number} deltaX Horizontal offset in pixel units. - * @param {number} deltaY Vertical offset in pixel units. + * @param deltaX Horizontal offset in pixel units. + * @param deltaY Vertical offset in pixel units. */ - translateBy(deltaX, deltaY) { - const x = this.surfaceXY_.x + deltaX; - const y = this.surfaceXY_.y + deltaY; + translateBy(deltaX: number, deltaY: number) { + const x = this.surfaceXY_!.x + deltaX; + const y = this.surfaceXY_!.y + deltaY; this.surfaceXY_ = new Coordinate(x, y); this.translateSurfaceInternal_(); } @@ -179,10 +163,10 @@ const BlockDragSurfaceSvg = class { * We translate the drag surface instead of the blocks inside the surface * so that the browser avoids repainting the SVG. * Because of this, the drag coordinates must be adjusted by scale. - * @param {number} x X translation for the entire surface. - * @param {number} y Y translation for the entire surface. + * @param x X translation for the entire surface. + * @param y Y translation for the entire surface. */ - translateSurface(x, y) { + translateSurface(x: number, y: number) { this.surfaceXY_ = new Coordinate(x * this.scale_, y * this.scale_); this.translateSurfaceInternal_(); } @@ -190,47 +174,46 @@ const BlockDragSurfaceSvg = class { /** * Reports the surface translation in scaled workspace coordinates. * Use this when finishing a drag to return blocks to the correct position. - * @return {!Coordinate} Current translation of the surface. + * @return Current translation of the surface. */ - getSurfaceTranslation() { - const xy = svgMath.getRelativeXY(/** @type {!SVGElement} */ (this.SVG_)); + getSurfaceTranslation(): Coordinate { + const xy = svgMath.getRelativeXY(this.SVG_ as SVGElement); return new Coordinate(xy.x / this.scale_, xy.y / this.scale_); } /** * Provide a reference to the drag group (primarily for * BlockSvg.getRelativeToSurfaceXY). - * @return {?SVGElement} Drag surface group element. + * @return Drag surface group element. */ - getGroup() { + getGroup(): SVGElement | null { return this.dragGroup_; } /** * Returns the SVG drag surface. - * @returns {?SVGElement} The SVG drag surface. + * @returns The SVG drag surface. */ - getSvgRoot() { + getSvgRoot(): SVGElement | null { return this.SVG_; } /** * Get the current blocks on the drag surface, if any (primarily * for BlockSvg.getRelativeToSurfaceXY). - * @return {?Element} Drag surface block DOM element, or null if no blocks - * exist. + * @return Drag surface block DOM element, or null if no blocks exist. */ - getCurrentBlock() { - return /** @type {Element} */ (this.dragGroup_.firstChild); + getCurrentBlock(): Element | null { + return this.dragGroup_!.firstChild as Element; } /** * Gets the translation of the child block surface * This surface is in charge of keeping track of how much the workspace has * moved. - * @return {!Coordinate} The amount the workspace has been moved. + * @return The amount the workspace has been moved. */ - getWsTranslation() { + getWsTranslation(): Coordinate { // Returning a copy so the coordinate can not be changed outside this class. return this.childSurfaceXY_.clone(); } @@ -240,26 +223,24 @@ const BlockDragSurfaceSvg = class { * element. * If the block is being deleted it doesn't need to go back to the original * surface, since it would be removed immediately during dispose. - * @param {Element=} opt_newSurface Surface the dragging blocks should be - * moved to, or null if the blocks should be removed from this surface - * without being moved to a different surface. + * @param opt_newSurface Surface the dragging blocks should be moved to, or + * null if the blocks should be removed from this surface without being + * moved to a different surface. */ - clearAndHide(opt_newSurface) { + clearAndHide(opt_newSurface?: Element) { const currentBlockElement = this.getCurrentBlock(); if (currentBlockElement) { if (opt_newSurface) { // appendChild removes the node from this.dragGroup_ opt_newSurface.appendChild(currentBlockElement); } else { - this.dragGroup_.removeChild(currentBlockElement); + this.dragGroup_!.removeChild(currentBlockElement); } } - this.SVG_.style.display = 'none'; - if (this.dragGroup_.childNodes.length) { + this.SVG_!.style.display = 'none'; + if (this.dragGroup_!.childNodes.length) { throw Error('Drag group was not cleared.'); } this.surfaceXY_ = null; } -}; - -exports.BlockDragSurfaceSvg = BlockDragSurfaceSvg; +} diff --git a/core/block_dragger.ts b/core/block_dragger.ts index e28ad351feb..1f52170ad3e 100644 --- a/core/block_dragger.ts +++ b/core/block_dragger.ts @@ -1,116 +1,83 @@ +/** @fileoverview Methods for dragging a block visually. */ + /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: Apache-2.0 */ -/** - * @fileoverview Methods for dragging a block visually. - */ -'use strict'; /** * Methods for dragging a block visually. * @class */ -goog.module('Blockly.BlockDragger'); - -const blockAnimation = goog.require('Blockly.blockAnimations'); -const bumpObjects = goog.require('Blockly.bumpObjects'); -const common = goog.require('Blockly.common'); -const dom = goog.require('Blockly.utils.dom'); -const eventUtils = goog.require('Blockly.Events.utils'); -const registry = goog.require('Blockly.registry'); + +// Unused import preserved for side-effects. Remove if unneeded. +import './events/events_block_drag'; + +import * as blockAnimation from './block_animations.js'; /* eslint-disable-next-line no-unused-vars */ -const {BlockMove} = goog.requireType('Blockly.Events.BlockMove'); +import { BlockSvg } from './block_svg.js'; +import * as bumpObjects from './bump_objects.js'; +import * as common from './common.js'; /* eslint-disable-next-line no-unused-vars */ -const {BlockSvg} = goog.requireType('Blockly.BlockSvg'); -const {Coordinate} = goog.require('Blockly.utils.Coordinate'); +import { BlockMove } from './events/events_block_move.js'; +import * as eventUtils from './events/utils.js'; /* eslint-disable-next-line no-unused-vars */ -const {IBlockDragger} = goog.require('Blockly.IBlockDragger'); +import { Icon } from './icon.js'; +import { InsertionMarkerManager } from './insertion_marker_manager.js'; /* eslint-disable-next-line no-unused-vars */ -const {IDragTarget} = goog.requireType('Blockly.IDragTarget'); +import { IBlockDragger } from './interfaces/i_block_dragger.js'; /* eslint-disable-next-line no-unused-vars */ -const {Icon} = goog.requireType('Blockly.Icon'); -const {InsertionMarkerManager} = goog.require('Blockly.InsertionMarkerManager'); +import { IDragTarget } from './interfaces/i_drag_target.js'; +import * as registry from './registry.js'; +import { Coordinate } from './utils/coordinate.js'; +import * as dom from './utils/dom.js'; /* eslint-disable-next-line no-unused-vars */ -const {WorkspaceSvg} = goog.requireType('Blockly.WorkspaceSvg'); -/** @suppress {extraRequire} */ -goog.require('Blockly.Events.BlockDrag'); -/** @suppress {extraRequire} */ -goog.require('Blockly.Events.BlockMove'); +import { WorkspaceSvg } from './workspace_svg.js'; /** * Class for a block dragger. It moves blocks around the workspace when they * are being dragged by a mouse or touch. - * @implements {IBlockDragger} * @alias Blockly.BlockDragger */ -const BlockDragger = class { - /** - * @param {!BlockSvg} block The block to drag. - * @param {!WorkspaceSvg} workspace The workspace to drag on. - */ - constructor(block, workspace) { - /** - * The top block in the stack that is being dragged. - * @type {!BlockSvg} - * @protected - */ - this.draggingBlock_ = block; +export class BlockDragger implements IBlockDragger { + protected draggedConnectionManager_: InsertionMarkerManager; - /** - * The workspace on which the block is being dragged. - * @type {!WorkspaceSvg} - * @protected - */ - this.workspace_ = workspace; - - /** - * Object that keeps track of connections on dragged blocks. - * @type {!InsertionMarkerManager} - * @protected - */ - this.draggedConnectionManager_ = - new InsertionMarkerManager(this.draggingBlock_); + /** Which drag area the mouse pointer is over, if any. */ + private dragTarget_: IDragTarget | null = null; - /** - * Which drag area the mouse pointer is over, if any. - * @type {?IDragTarget} - * @private - */ - this.dragTarget_ = null; + /** Whether the block would be deleted if dropped immediately. */ + protected wouldDeleteBlock_ = false; + protected startXY_: Coordinate; + protected dragIconData_: IconPositionData[]; - /** - * Whether the block would be deleted if dropped immediately. - * @type {boolean} - * @protected - */ - this.wouldDeleteBlock_ = false; + /** + * @param block The block to drag. + * @param workspace The workspace to drag on. + */ + constructor( + private readonly block: BlockSvg, + private readonly workspace: WorkspaceSvg) { + /** Object that keeps track of connections on dragged blocks. */ + this.draggedConnectionManager_ = new InsertionMarkerManager(this.block); /** * The location of the top left corner of the dragging block at the * beginning of the drag in workspace coordinates. - * @type {!Coordinate} - * @protected */ - this.startXY_ = this.draggingBlock_.getRelativeToSurfaceXY(); + this.startXY_ = this.block.getRelativeToSurfaceXY(); /** * A list of all of the icons (comment, warning, and mutator) that are * on this block and its descendants. Moving an icon moves the bubble that * extends from it if that bubble is open. - * @type {Array} - * @protected */ this.dragIconData_ = initIconData(block); } - /** - * Sever all links from this object. - * @package - */ + /** Sever all links from this object. */ dispose() { this.dragIconData_.length = 0; @@ -121,13 +88,11 @@ const BlockDragger = class { /** * Start dragging a block. This includes moving it to the drag surface. - * @param {!Coordinate} currentDragDeltaXY How far the pointer has - * moved from the position at mouse down, in pixel units. - * @param {boolean} healStack Whether or not to heal the stack after - * disconnecting. - * @public + * @param currentDragDeltaXY How far the pointer has moved from the position + * at mouse down, in pixel units. + * @param healStack Whether or not to heal the stack after disconnecting. */ - startDrag(currentDragDeltaXY, healStack) { + startDrag(currentDragDeltaXY: Coordinate, healStack: boolean) { if (!eventUtils.getGroup()) { eventUtils.setGroup(true); } @@ -137,84 +102,77 @@ const BlockDragger = class { // during a drag. They have to rely on the order of the blocks in the SVG. // For performance reasons that usually happens at the end of a drag, // but do it at the beginning for mutators. - if (this.workspace_.isMutator) { - this.draggingBlock_.bringToFront(); + if (this.workspace.isMutator) { + this.block.bringToFront(); } // During a drag there may be a lot of rerenders, but not field changes. // Turn the cache on so we don't do spurious remeasures during the drag. dom.startTextWidthCache(); - this.workspace_.setResizesEnabled(false); + this.workspace.setResizesEnabled(false); blockAnimation.disconnectUiStop(); if (this.shouldDisconnect_(healStack)) { this.disconnectBlock_(healStack, currentDragDeltaXY); } - this.draggingBlock_.setDragging(true); + this.block.setDragging(true); // For future consideration: we may be able to put moveToDragSurface inside // the block dragger, which would also let the block not track the block // drag surface. - this.draggingBlock_.moveToDragSurface(); + this.block.moveToDragSurface(); } /** * Whether or not we should disconnect the block when a drag is started. - * @param {boolean} healStack Whether or not to heal the stack after - * disconnecting. - * @return {boolean} True to disconnect the block, false otherwise. - * @protected + * @param healStack Whether or not to heal the stack after disconnecting. + * @return True to disconnect the block, false otherwise. */ - shouldDisconnect_(healStack) { + protected shouldDisconnect_(healStack: boolean): boolean { return !!( - this.draggingBlock_.getParent() || - (healStack && this.draggingBlock_.nextConnection && - this.draggingBlock_.nextConnection.targetBlock())); + this.block.getParent() || + healStack && this.block.nextConnection && + this.block.nextConnection.targetBlock()); } /** * Disconnects the block and moves it to a new location. - * @param {boolean} healStack Whether or not to heal the stack after - * disconnecting. - * @param {!Coordinate} currentDragDeltaXY How far the pointer has - * moved from the position at mouse down, in pixel units. - * @protected + * @param healStack Whether or not to heal the stack after disconnecting. + * @param currentDragDeltaXY How far the pointer has moved from the position + * at mouse down, in pixel units. */ - disconnectBlock_(healStack, currentDragDeltaXY) { - this.draggingBlock_.unplug(healStack); + protected disconnectBlock_( + healStack: boolean, currentDragDeltaXY: Coordinate) { + this.block.unplug(healStack); const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY); const newLoc = Coordinate.sum(this.startXY_, delta); - this.draggingBlock_.translate(newLoc.x, newLoc.y); - blockAnimation.disconnectUiEffect(this.draggingBlock_); + this.block.translate(newLoc.x, newLoc.y); + blockAnimation.disconnectUiEffect(this.block); this.draggedConnectionManager_.updateAvailableConnections(); } - /** - * Fire a UI event at the start of a block drag. - * @protected - */ - fireDragStartEvent_() { - const event = new (eventUtils.get(eventUtils.BLOCK_DRAG))( - this.draggingBlock_, true, this.draggingBlock_.getDescendants(false)); + /** Fire a UI event at the start of a block drag. */ + protected fireDragStartEvent_() { + const event = new (eventUtils.get(eventUtils.BLOCK_DRAG))! + (this.block, true, this.block.getDescendants(false)); eventUtils.fire(event); } /** * Execute a step of block dragging, based on the given event. Update the * display accordingly. - * @param {!Event} e The most recent move event. - * @param {!Coordinate} currentDragDeltaXY How far the pointer has - * moved from the position at the start of the drag, in pixel units. - * @public + * @param e The most recent move event. + * @param currentDragDeltaXY How far the pointer has moved from the position + * at the start of the drag, in pixel units. */ - drag(e, currentDragDeltaXY) { + drag(e: Event, currentDragDeltaXY: Coordinate) { const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY); const newLoc = Coordinate.sum(this.startXY_, delta); - this.draggingBlock_.moveDuringDrag(newLoc); + this.block.moveDuringDrag(newLoc); this.dragIcons_(delta); const oldDragTarget = this.dragTarget_; - this.dragTarget_ = this.workspace_.getDragTarget(e); + this.dragTarget_ = this.workspace.getDragTarget(e); this.draggedConnectionManager_.update(delta, this.dragTarget_); const oldWouldDeleteBlock = this.wouldDeleteBlock_; @@ -227,20 +185,19 @@ const BlockDragger = class { // Call drag enter/exit/over after wouldDeleteBlock is called in // InsertionMarkerManager.update. if (this.dragTarget_ !== oldDragTarget) { - oldDragTarget && oldDragTarget.onDragExit(this.draggingBlock_); - this.dragTarget_ && this.dragTarget_.onDragEnter(this.draggingBlock_); + oldDragTarget && oldDragTarget.onDragExit(this.block); + this.dragTarget_ && this.dragTarget_.onDragEnter(this.block); } - this.dragTarget_ && this.dragTarget_.onDragOver(this.draggingBlock_); + this.dragTarget_ && this.dragTarget_.onDragOver(this.block); } /** * Finish a block drag and put the block back on the workspace. - * @param {!Event} e The mouseup/touchend event. - * @param {!Coordinate} currentDragDeltaXY How far the pointer has - * moved from the position at the start of the drag, in pixel units. - * @public + * @param e The mouseup/touchend event. + * @param currentDragDeltaXY How far the pointer has moved from the position + * at the start of the drag, in pixel units. */ - endDrag(e, currentDragDeltaXY) { + endDrag(e: Event, currentDragDeltaXY: Coordinate) { // Make sure internal state is fresh. this.drag(e, currentDragDeltaXY); this.dragIconData_ = []; @@ -250,12 +207,10 @@ const BlockDragger = class { blockAnimation.disconnectUiStop(); - const preventMove = !!this.dragTarget_ && - this.dragTarget_.shouldPreventMove(this.draggingBlock_); - /** @type {Coordinate} */ - let newLoc; - /** @type {Coordinate} */ - let delta; + const preventMove = + !!this.dragTarget_ && this.dragTarget_.shouldPreventMove(this.block); + let newLoc: Coordinate; + let delta: Coordinate | null = null; if (preventMove) { newLoc = this.startXY_; } else { @@ -263,61 +218,59 @@ const BlockDragger = class { delta = newValues.delta; newLoc = newValues.newLocation; } - this.draggingBlock_.moveOffDragSurface(newLoc); + this.block.moveOffDragSurface(newLoc); if (this.dragTarget_) { - this.dragTarget_.onDrop(this.draggingBlock_); + this.dragTarget_.onDrop(this.block); } const deleted = this.maybeDeleteBlock_(); if (!deleted) { // These are expensive and don't need to be done if we're deleting. - this.draggingBlock_.setDragging(false); + this.block.setDragging(false); if (delta) { // !preventMove this.updateBlockAfterMove_(delta); } else { // Blocks dragged directly from a flyout may need to be bumped into // bounds. bumpObjects.bumpIntoBounds( - this.draggingBlock_.workspace, - this.workspace_.getMetricsManager().getScrollMetrics(true), - this.draggingBlock_); + this.block.workspace, + this.workspace.getMetricsManager().getScrollMetrics(true), + this.block); } } - this.workspace_.setResizesEnabled(true); + this.workspace.setResizesEnabled(true); eventUtils.setGroup(false); } /** * Calculates the drag delta and new location values after a block is dragged. - * @param {!Coordinate} currentDragDeltaXY How far the pointer has - * moved from the start of the drag, in pixel units. - * @return {{delta: !Coordinate, newLocation: - * !Coordinate}} New location after drag. delta is in - * workspace units. newLocation is the new coordinate where the block - * should end up. - * @protected + * @param currentDragDeltaXY How far the pointer has moved from the start of + * the drag, in pixel units. + * @return New location after drag. delta is in workspace units. newLocation + * is the new coordinate where the block should end up. */ - getNewLocationAfterDrag_(currentDragDeltaXY) { - const newValues = {}; - newValues.delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY); - newValues.newLocation = Coordinate.sum(this.startXY_, newValues.delta); - return newValues; + protected getNewLocationAfterDrag_(currentDragDeltaXY: Coordinate): { delta: Coordinate, newLocation: Coordinate } { + const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY); + const newLocation = Coordinate.sum(this.startXY_, delta); + return { + delta, + newLocation, + }; } /** * May delete the dragging block, if allowed. If `this.wouldDeleteBlock_` is * not true, the block will not be deleted. This should be called at the end * of a block drag. - * @return {boolean} True if the block was deleted. - * @protected + * @return True if the block was deleted. */ - maybeDeleteBlock_() { + protected maybeDeleteBlock_(): boolean { if (this.wouldDeleteBlock_) { // Fire a move event, so we know where to go back to for an undo. this.fireMoveEvent_(); - this.draggingBlock_.dispose(false, true); + this.block.dispose(false, true); common.draggingConnections.length = 0; return true; } @@ -326,29 +279,25 @@ const BlockDragger = class { /** * Updates the necessary information to place a block at a certain location. - * @param {!Coordinate} delta The change in location from where - * the block started the drag to where it ended the drag. - * @protected + * @param delta The change in location from where the block started the drag + * to where it ended the drag. */ - updateBlockAfterMove_(delta) { - this.draggingBlock_.moveConnections(delta.x, delta.y); + protected updateBlockAfterMove_(delta: Coordinate) { + this.block.moveConnections(delta.x, delta.y); this.fireMoveEvent_(); if (this.draggedConnectionManager_.wouldConnectBlock()) { // Applying connections also rerenders the relevant blocks. this.draggedConnectionManager_.applyConnections(); } else { - this.draggingBlock_.render(); + this.block.render(); } - this.draggingBlock_.scheduleSnapAndBump(); + this.block.scheduleSnapAndBump(); } - /** - * Fire a UI event at the end of a block drag. - * @protected - */ - fireDragEndEvent_() { - const event = new (eventUtils.get(eventUtils.BLOCK_DRAG))( - this.draggingBlock_, false, this.draggingBlock_.getDescendants(false)); + /** Fire a UI event at the end of a block drag. */ + protected fireDragEndEvent_() { + const event = new (eventUtils.get(eventUtils.BLOCK_DRAG))! + (this.block, false, this.block.getDescendants(false)); eventUtils.fire(event); } @@ -356,32 +305,38 @@ const BlockDragger = class { * Adds or removes the style of the cursor for the toolbox. * This is what changes the cursor to display an x when a deletable block is * held over the toolbox. - * @param {boolean} isEnd True if we are at the end of a drag, false - * otherwise. - * @protected + * @param isEnd True if we are at the end of a drag, false otherwise. */ - updateToolboxStyle_(isEnd) { - const toolbox = this.workspace_.getToolbox(); + protected updateToolboxStyle_(isEnd: boolean) { + const toolbox = this.workspace.getToolbox(); if (toolbox) { - const style = this.draggingBlock_.isDeletable() ? 'blocklyToolboxDelete' : - 'blocklyToolboxGrab'; - - if (isEnd && typeof toolbox.removeStyle === 'function') { - toolbox.removeStyle(style); - } else if (!isEnd && typeof toolbox.addStyle === 'function') { - toolbox.addStyle(style); + const style = this.block.isDeletable() ? 'blocklyToolboxDelete' : + 'blocklyToolboxGrab'; + + // AnyDuringMigration because: Property 'removeStyle' does not exist on + // type 'IToolbox'. + if (isEnd && + typeof (toolbox as AnyDuringMigration).removeStyle === 'function') { + // AnyDuringMigration because: Property 'removeStyle' does not exist on + // type 'IToolbox'. + (toolbox as AnyDuringMigration).removeStyle(style); + // AnyDuringMigration because: Property 'addStyle' does not exist on + // type 'IToolbox'. + } else if ( + !isEnd && + typeof (toolbox as AnyDuringMigration).addStyle === 'function') { + // AnyDuringMigration because: Property 'addStyle' does not exist on + // type 'IToolbox'. + (toolbox as AnyDuringMigration).addStyle(style); } } } - /** - * Fire a move event at the end of a block drag. - * @protected - */ - fireMoveEvent_() { - const event = /** @type {!BlockMove} */ - (new (eventUtils.get(eventUtils.BLOCK_MOVE))(this.draggingBlock_)); + /** Fire a move event at the end of a block drag. */ + protected fireMoveEvent_() { + const event = + new (eventUtils.get(eventUtils.BLOCK_MOVE))!(this.block) as BlockMove; event.oldCoordinate = this.startXY_; event.recordNew(); eventUtils.fire(event); @@ -390,10 +345,9 @@ const BlockDragger = class { /** * Update the cursor (and possibly the trash can lid) to reflect whether the * dragging block would be deleted if released immediately. - * @protected */ - updateCursorDuringBlockDrag_() { - this.draggingBlock_.setDeleteStyle(this.wouldDeleteBlock_); + protected updateCursorDuringBlockDrag_() { + this.block.setDeleteStyle(this.wouldDeleteBlock_); } /** @@ -401,21 +355,18 @@ const BlockDragger = class { * correction for mutator workspaces. * This function does not consider differing origins. It simply scales the * input's x and y values. - * @param {!Coordinate} pixelCoord A coordinate with x and y - * values in CSS pixel units. - * @return {!Coordinate} The input coordinate divided by the - * workspace scale. - * @protected + * @param pixelCoord A coordinate with x and y values in CSS pixel units. + * @return The input coordinate divided by the workspace scale. */ - pixelsToWorkspaceUnits_(pixelCoord) { + protected pixelsToWorkspaceUnits_(pixelCoord: Coordinate): Coordinate { const result = new Coordinate( - pixelCoord.x / this.workspace_.scale, - pixelCoord.y / this.workspace_.scale); - if (this.workspace_.isMutator) { + pixelCoord.x / this.workspace.scale, + pixelCoord.y / this.workspace.scale); + if (this.workspace.isMutator) { // If we're in a mutator, its scale is always 1, purely because of some // oddities in our rendering optimizations. The actual scale is the same // as the scale on the parent workspace. Fix that for dragging. - const mainScale = this.workspace_.options.parentWorkspace.scale; + const mainScale = this.workspace.options.parentWorkspace!.scale; result.scale(1 / mainScale); } return result; @@ -423,11 +374,10 @@ const BlockDragger = class { /** * Move all of the icons connected to this drag. - * @param {!Coordinate} dxy How far to move the icons from their - * original positions, in workspace units. - * @protected + * @param dxy How far to move the icons from their original positions, in + * workspace units. */ - dragIcons_(dxy) { + protected dragIcons_(dxy: Coordinate) { // Moving icons moves their associated bubbles. for (let i = 0; i < this.dragIconData_.length; i++) { const data = this.dragIconData_[i]; @@ -438,60 +388,51 @@ const BlockDragger = class { /** * Get a list of the insertion markers that currently exist. Drags have 0, 1, * or 2 insertion markers. - * @return {!Array} A possibly empty list of insertion - * marker blocks. - * @public + * @return A possibly empty list of insertion marker blocks. */ - getInsertionMarkers() { + getInsertionMarkers(): BlockSvg[] { // No insertion markers with the old style of dragged connection managers. if (this.draggedConnectionManager_ && - this.draggedConnectionManager_.getInsertionMarkers) { + this.draggedConnectionManager_.getInsertionMarkers) { return this.draggedConnectionManager_.getInsertionMarkers(); } return []; } -}; +} -/** - * Data about the position of a given icon. - * @typedef {{ - * location:!Coordinate, - * icon:!Icon, - * }} - */ -let IconPositionData; -exports.IconPositionData = IconPositionData; +/** Data about the position of a given icon. */ +export interface IconPositionData { + location: Coordinate; + icon: Icon; +} /** * Make a list of all of the icons (comment, warning, and mutator) that are * on this block and its descendants. Moving an icon moves the bubble that * extends from it if that bubble is open. - * @param {!BlockSvg} block The root block that is being dragged. - * @return {!Array} The list of all icons and their - * locations. + * @param block The root block that is being dragged. + * @return The list of all icons and their locations. */ -const initIconData = function(block) { +function initIconData(block: BlockSvg): IconPositionData[] { // Build a list of icons that need to be moved and where they started. const dragIconData = []; - const descendants = - /** @type {!Array} */ (block.getDescendants(false)); + const descendants = (block.getDescendants(false)); - for (let i = 0, descendant; (descendant = descendants[i]); i++) { + for (let i = 0, descendant; descendant = descendants[i]; i++) { const icons = descendant.getIcons(); for (let j = 0; j < icons.length; j++) { const data = { // Coordinate with x and y properties (workspace // coordinates). - location: icons[j].getIconLocation(), - // Blockly.Icon + location: icons[j].getIconLocation(), // Blockly.Icon icon: icons[j], }; dragIconData.push(data); } } - return dragIconData; -}; + // AnyDuringMigration because: Type '{ location: Coordinate | null; icon: + // Icon; }[]' is not assignable to type 'IconPositionData[]'. + return dragIconData as AnyDuringMigration; +} registry.register(registry.Type.BLOCK_DRAGGER, registry.DEFAULT, BlockDragger); - -exports.BlockDragger = BlockDragger; diff --git a/core/block_svg.ts b/core/block_svg.ts index ec76a04939f..60f1d5c70b3 100644 --- a/core/block_svg.ts +++ b/core/block_svg.ts @@ -1,104 +1,176 @@ +/** @fileoverview Methods for graphically rendering a block as SVG. */ + /** * @license * Copyright 2012 Google LLC * SPDX-License-Identifier: Apache-2.0 */ -/** - * @fileoverview Methods for graphically rendering a block as SVG. - */ -'use strict'; /** * Methods for graphically rendering a block as SVG. * @class */ -goog.module('Blockly.BlockSvg'); - -const ContextMenu = goog.require('Blockly.ContextMenu'); -const Tooltip = goog.require('Blockly.Tooltip'); -const blockAnimations = goog.require('Blockly.blockAnimations'); -const blocks = goog.require('Blockly.serialization.blocks'); -const browserEvents = goog.require('Blockly.browserEvents'); -const common = goog.require('Blockly.common'); -const constants = goog.require('Blockly.constants'); -const dom = goog.require('Blockly.utils.dom'); -const eventUtils = goog.require('Blockly.Events.utils'); -const internalConstants = goog.require('Blockly.internalConstants'); -const svgMath = goog.require('Blockly.utils.svgMath'); -const {ASTNode} = goog.require('Blockly.ASTNode'); -const {Block} = goog.require('Blockly.Block'); /* eslint-disable-next-line no-unused-vars */ -const {BlockMove} = goog.requireType('Blockly.Events.BlockMove'); +// Unused import preserved for side-effects. Remove if unneeded. +import './theme'; +// Unused import preserved for side-effects. Remove if unneeded. +import './events/events_selected'; +// Unused import preserved for side-effects. Remove if unneeded. +import './touch'; + +import { Block } from './block.js'; +import * as blockAnimations from './block_animations.js'; +import * as browserEvents from './browser_events.js'; /* eslint-disable-next-line no-unused-vars */ -const {Comment} = goog.requireType('Blockly.Comment'); -const {config} = goog.require('Blockly.config'); -const {ConnectionType} = goog.require('Blockly.ConnectionType'); +import { Comment } from './comment.js'; +import * as common from './common.js'; +import { config } from './config.js'; /* eslint-disable-next-line no-unused-vars */ -const {Connection} = goog.requireType('Blockly.Connection'); -const {ContextMenuRegistry} = goog.require('Blockly.ContextMenuRegistry'); -const {Coordinate} = goog.require('Blockly.utils.Coordinate'); +import { Connection } from './connection.js'; +import { ConnectionType } from './connection_type.js'; +import * as constants from './constants.js'; +import * as ContextMenu from './contextmenu.js'; +import { ContextMenuOption, ContextMenuRegistry, LegacyContextMenuOption } from './contextmenu_registry.js'; /* eslint-disable-next-line no-unused-vars */ -const {Debug: BlockRenderingDebug} = goog.requireType('Blockly.blockRendering.Debug'); -const {FieldLabel} = goog.require('Blockly.FieldLabel'); +import { BlockMove } from './events/events_block_move.js'; +import * as eventUtils from './events/utils.js'; /* eslint-disable-next-line no-unused-vars */ -const {Field} = goog.requireType('Blockly.Field'); +import { Field } from './field.js'; +import { FieldLabel } from './field_label.js'; /* eslint-disable-next-line no-unused-vars */ -const {IASTNodeLocationSvg} = goog.require('Blockly.IASTNodeLocationSvg'); +import { Icon } from './icon.js'; /* eslint-disable-next-line no-unused-vars */ -const {IBoundedElement} = goog.require('Blockly.IBoundedElement'); +import { Input } from './input.js'; /* eslint-disable-next-line no-unused-vars */ -const {ICopyable} = goog.require('Blockly.ICopyable'); +import { IASTNodeLocationSvg } from './interfaces/i_ast_node_location_svg.js'; /* eslint-disable-next-line no-unused-vars */ -const {IDraggable} = goog.require('Blockly.IDraggable'); +import { IBoundedElement } from './interfaces/i_bounded_element.js'; +import { CopyData, ICopyable } from './interfaces/i_copyable.js'; /* eslint-disable-next-line no-unused-vars */ -const {IPathObject} = goog.requireType('Blockly.blockRendering.IPathObject'); +import { IDraggable } from './interfaces/i_draggable.js'; +import * as internalConstants from './internal_constants.js'; +import { ASTNode } from './keyboard_nav/ast_node.js'; +import { TabNavigateCursor } from './keyboard_nav/tab_navigate_cursor.js'; +import { MarkerManager } from './marker_manager.js'; +import { Msg } from './msg.js'; /* eslint-disable-next-line no-unused-vars */ -const {Icon} = goog.requireType('Blockly.Icon'); +import { Mutator } from './mutator.js'; +import { RenderedConnection } from './rendered_connection.js'; /* eslint-disable-next-line no-unused-vars */ -const {Input} = goog.requireType('Blockly.Input'); -const {MarkerManager} = goog.require('Blockly.MarkerManager'); -const {Msg} = goog.require('Blockly.Msg'); +import { Debug as BlockRenderingDebug } from './renderers/common/debugger.js'; /* eslint-disable-next-line no-unused-vars */ -const {Mutator} = goog.requireType('Blockly.Mutator'); -const {Rect} = goog.require('Blockly.utils.Rect'); -const {RenderedConnection} = goog.require('Blockly.RenderedConnection'); -const {Svg} = goog.require('Blockly.utils.Svg'); -const {TabNavigateCursor} = goog.require('Blockly.TabNavigateCursor'); +import { IPathObject } from './renderers/common/i_path_object.js'; +import * as blocks from './serialization/blocks.js'; +import { BlockStyle } from './theme.js'; +import * as Tooltip from './tooltip.js'; +import { Coordinate } from './utils/coordinate.js'; +import * as dom from './utils/dom.js'; +import { Rect } from './utils/rect.js'; +import { Svg } from './utils/svg.js'; +import * as svgMath from './utils/svg_math.js'; /* eslint-disable-next-line no-unused-vars */ -const {Theme} = goog.requireType('Blockly.Theme'); +import { Warning } from './warning.js'; +import { Workspace } from './workspace.js'; /* eslint-disable-next-line no-unused-vars */ -const {Warning} = goog.requireType('Blockly.Warning'); -/* eslint-disable-next-line no-unused-vars */ -const {WorkspaceSvg} = goog.requireType('Blockly.WorkspaceSvg'); -/** @suppress {extraRequire} */ -goog.require('Blockly.Events.BlockMove'); -/** @suppress {extraRequire} */ -goog.require('Blockly.Events.Selected'); -/** @suppress {extraRequire} */ -goog.require('Blockly.Touch'); +import { WorkspaceSvg } from './workspace_svg.js'; /** * Class for a block's SVG representation. * Not normally called directly, workspace.newBlock() is preferred. - * @extends {Block} - * @implements {IASTNodeLocationSvg} - * @implements {IBoundedElement} - * @implements {ICopyable} - * @implements {IDraggable} * @alias Blockly.BlockSvg */ -class BlockSvg extends Block { +export class BlockSvg extends Block implements IASTNodeLocationSvg, + IBoundedElement, ICopyable, + IDraggable { + /** + * Constant for identifying rows that are to be rendered inline. + * Don't collide with Blockly.inputTypes. + */ + static readonly INLINE = -1; + + /** + * ID to give the "collapsed warnings" warning. Allows us to remove the + * "collapsed warnings" warning without removing any warnings that belong to + * the block. + */ + static readonly COLLAPSED_WARNING_ID = 'TEMP_COLLAPSED_WARNING_'; + override decompose?: ((p1: Workspace) => BlockSvg) | null; + // override compose?: ((p1: BlockSvg) => void)|null; + saveConnections?: ((p1: BlockSvg) => AnyDuringMigration) | null; + customContextMenu?: + ((p1: Array) => + AnyDuringMigration) | null; + + /** + * An property used internally to reference the block's rendering debugger. + */ + renderingDebugger: BlockRenderingDebug | null = null; + + /** + * Height of this block, not including any statement blocks above or below. + * Height is in workspace units. + */ + height = 0; + + /** + * Width of this block, including any connected value blocks. + * Width is in workspace units. + */ + width = 0; + /** - * @param {!WorkspaceSvg} workspace The block's workspace. - * @param {string} prototypeName Name of the language object containing - * type-specific functions for this block. - * @param {string=} opt_id Optional ID. Use this ID if provided, otherwise - * create a new ID. + * Map from IDs for warnings text to PIDs of functions to apply them. + * Used to be able to maintain multiple warnings. */ - constructor(workspace, prototypeName, opt_id) { + // AnyDuringMigration because: Type 'null' is not assignable to type '{ [key: + // string]: number; }'. + private warningTextDb_: { [key: string]: number } = null as AnyDuringMigration; + + /** Block's mutator icon (if any). */ + mutator: Mutator | null = null; + + /** Block's comment icon (if any). */ + private commentIcon_: Comment | null = null; + + /** Block's warning icon (if any). */ + warning: Warning | null = null; + + // Create core elements for the block. + private svgGroup_: SVGGElement; + style: BlockStyle; + pathObject: IPathObject; + override rendered = false; + + /** + * Is this block currently rendering? Used to stop recursive render calls + * from actually triggering a re-render. + */ + private renderIsInProgress_ = false; + + /** Whether mousedown events have been bound yet. */ + private eventsInit_ = false; + + override workspace: WorkspaceSvg; + // TODO(b/109816955): remove '!', see go/strict-prop-init-fix. + override outputConnection!: RenderedConnection; + // TODO(b/109816955): remove '!', see go/strict-prop-init-fix. + override nextConnection!: RenderedConnection; + // TODO(b/109816955): remove '!', see go/strict-prop-init-fix. + override previousConnection!: RenderedConnection; + private readonly useDragSurface_: boolean; + + /** + * @param workspace The block's workspace. + * @param prototypeName Name of the language object containing type-specific + * functions for this block. + * @param opt_id Optional ID. Use this ID if provided, otherwise create a new + * ID. + */ + constructor(workspace: WorkspaceSvg, prototypeName: string, opt_id?: string) { super(workspace, prototypeName, opt_id); + this.workspace = workspace; /** * An optional method called when a mutator dialog is first opened. @@ -107,7 +179,6 @@ class BlockSvg extends Block { * top-level block with any sub-blocks which are appropriate. This method * must also be coupled with defining a `compose` method for the default * mutation dialog button and UI to appear. - * @type {undefined|?function(WorkspaceSvg):!BlockSvg} */ this.decompose = this.decompose; @@ -116,7 +187,6 @@ class BlockSvg extends Block { * This function is called to modify the original block according to new * settings. This method must also be coupled with defining a `decompose` * method for the default mutation dialog button and UI to appear. - * @type {undefined|?function(!BlockSvg)} */ this.compose = this.compose; @@ -124,131 +194,30 @@ class BlockSvg extends Block { * An optional method called by the default mutator UI which gives the block * a chance to save information about what child blocks are connected to * what mutated connections. - * @type {undefined|?function(!BlockSvg)} */ this.saveConnections = this.saveConnections; - /** - * An optional method for defining custom block context menu items. - * @type {undefined|?function(!Array)} - */ + /** An optional method for defining custom block context menu items. */ this.customContextMenu = this.customContextMenu; + this.svgGroup_ = dom.createSvgElement(Svg.G, {}); + (this.svgGroup_ as AnyDuringMigration).translate_ = ''; - /** - * An property used internally to reference the block's rendering debugger. - * @type {?BlockRenderingDebug} - * @package - */ - this.renderingDebugger = null; - - /** - * Height of this block, not including any statement blocks above or below. - * Height is in workspace units. - * @type {number} - */ - this.height = 0; - - /** - * Width of this block, including any connected value blocks. - * Width is in workspace units. - * @type {number} - */ - this.width = 0; - - /** - * Map from IDs for warnings text to PIDs of functions to apply them. - * Used to be able to maintain multiple warnings. - * @type {Object} - * @private - */ - this.warningTextDb_ = null; - - /** - * Block's mutator icon (if any). - * @type {?Mutator} - */ - this.mutator = null; - - /** - * Block's comment icon (if any). - * @type {?Comment} - * @deprecated August 2019. Use getCommentIcon instead. - */ - this.comment = null; - - /** - * Block's comment icon (if any). - * @type {?Comment} - * @private - */ - this.commentIcon_ = null; - - /** - * Block's warning icon (if any). - * @type {?Warning} - */ - this.warning = null; - - // Create core elements for the block. - /** - * @type {!SVGGElement} - * @private - */ - this.svgGroup_ = dom.createSvgElement(Svg.G, {}, null); - (/** @type {?} */ (this.svgGroup_)).translate_ = ''; - - /** - * A block style object. - * @type {!Theme.BlockStyle} - */ + /** A block style object. */ this.style = workspace.getRenderer().getConstants().getBlockStyle(null); - /** - * The renderer's path object. - * @type {IPathObject} - * @package - */ + /** The renderer's path object. */ this.pathObject = - workspace.getRenderer().makePathObject(this.svgGroup_, this.style); - - /** @type {boolean} */ - this.rendered = false; - /** - * Is this block currently rendering? Used to stop recursive render calls - * from actually triggering a re-render. - * @type {boolean} - * @private - */ - this.renderIsInProgress_ = false; - - /** - * Whether mousedown events have been bound yet. - * @type {boolean} - * @private - */ - this.eventsInit_ = false; - - /** @type {!WorkspaceSvg} */ - this.workspace; - /** @type {RenderedConnection} */ - this.outputConnection; - /** @type {RenderedConnection} */ - this.nextConnection; - /** @type {RenderedConnection} */ - this.previousConnection; + workspace.getRenderer().makePathObject(this.svgGroup_, this.style); /** * Whether to move the block to the drag surface when it is dragged. * True if it should move, false if it should be translated directly. - * @type {boolean} - * @private */ this.useDragSurface_ = - svgMath.is3dSupported() && !!workspace.getBlockDragSurface(); + svgMath.is3dSupported() && !!workspace.getBlockDragSurface(); const svgPath = this.pathObject.svgPath; - (/** @type {?} */ (svgPath)).tooltip = this; + (svgPath as AnyDuringMigration).tooltip = this; Tooltip.bindMouseEvents(svgPath); // Expose this block's ID on its top-level SVG group. @@ -265,7 +234,7 @@ class BlockSvg extends Block { if (!this.workspace.rendered) { throw TypeError('Workspace is headless.'); } - for (let i = 0, input; (input = this.inputList[i]); i++) { + for (let i = 0, input; input = this.inputList[i]; i++) { input.init(); } const icons = this.getIcons(); @@ -287,17 +256,17 @@ class BlockSvg extends Block { /** * Get the secondary colour of a block. - * @return {?string} #RRGGBB string. + * @return #RRGGBB string. */ - getColourSecondary() { + getColourSecondary(): string | null { return this.style.colourSecondary; } /** * Get the tertiary colour of a block. - * @return {?string} #RRGGBB string. + * @return #RRGGBB string. */ - getColourTertiary() { + getColourTertiary(): string | null { return this.style.colourTertiary; } @@ -308,7 +277,7 @@ class BlockSvg extends Block { select() { if (this.isShadow() && this.getParent()) { // Shadow blocks should not be selected. - this.getParent().select(); + this.getParent()!.select(); return; } if (common.getSelected() === this) { @@ -316,17 +285,17 @@ class BlockSvg extends Block { } let oldId = null; if (common.getSelected()) { - oldId = common.getSelected().id; + oldId = common.getSelected()!.id; // Unselect any previously selected block. eventUtils.disable(); try { - common.getSelected().unselect(); + common.getSelected()!.unselect(); } finally { eventUtils.enable(); } } - const event = new (eventUtils.get(eventUtils.SELECTED))( - oldId, this.id, this.workspace.id); + const event = new (eventUtils.get(eventUtils.SELECTED))! + (oldId, this.id, this.workspace.id); eventUtils.fire(event); common.setSelected(this); this.addSelect(); @@ -340,8 +309,8 @@ class BlockSvg extends Block { if (common.getSelected() !== this) { return; } - const event = new (eventUtils.get(eventUtils.SELECTED))( - this.id, null, this.workspace.id); + const event = new (eventUtils.get(eventUtils.SELECTED))! + (this.id, null, this.workspace.id); event.workspaceId = this.workspace.id; eventUtils.fire(event); common.setSelected(null); @@ -350,9 +319,9 @@ class BlockSvg extends Block { /** * Returns a list of mutator, comment, and warning icons. - * @return {!Array} List of icons. + * @return List of icons. */ - getIcons() { + getIcons(): Icon[] { const icons = []; if (this.mutator) { icons.push(this.mutator); @@ -368,18 +337,18 @@ class BlockSvg extends Block { /** * Sets the parent of this block to be a new block or null. - * @param {?Block} newParent New parent block. - * @package - * @override + * @param newParent New parent block. */ - setParent(newParent) { + override setParent(newParent: this | null) { const oldParent = this.parentBlock_; if (newParent === oldParent) { return; } dom.startTextWidthCache(); - super.setParent(newParent); + // AnyDuringMigration because: Argument of type 'Block | null' is not + // assignable to parameter of type 'Block'. + super.setParent(newParent as AnyDuringMigration); dom.stopTextWidthCache(); const svgRoot = this.getSvgRoot(); @@ -392,7 +361,7 @@ class BlockSvg extends Block { const oldXY = this.getRelativeToSurfaceXY(); if (newParent) { - (/** @type {!BlockSvg} */ (newParent)).getSvgRoot().appendChild(svgRoot); + (newParent as BlockSvg).getSvgRoot().appendChild(svgRoot); const newXY = this.getRelativeToSurfaceXY(); // Move the connections to match the child's new position. this.moveConnections(newXY.x - oldXY.x, newXY.y - oldXY.y); @@ -412,18 +381,17 @@ class BlockSvg extends Block { * If the block is on the workspace, (0, 0) is the origin of the workspace * coordinate system. * This does not change with workspace scale. - * @return {!Coordinate} Object with .x and .y properties in - * workspace coordinates. + * @return Object with .x and .y properties in workspace coordinates. */ - getRelativeToSurfaceXY() { + override getRelativeToSurfaceXY(): Coordinate { let x = 0; let y = 0; const dragSurfaceGroup = this.useDragSurface_ ? - this.workspace.getBlockDragSurface().getGroup() : - null; + this.workspace.getBlockDragSurface()!.getGroup() : + null; - let element = this.getSvgRoot(); + let element: SVGElement = this.getSvgRoot(); if (element) { do { // Loop through this block and every parent. @@ -433,40 +401,39 @@ class BlockSvg extends Block { // If this element is the current element on the drag surface, include // the translation of the drag surface itself. if (this.useDragSurface_ && - this.workspace.getBlockDragSurface().getCurrentBlock() === - element) { + this.workspace.getBlockDragSurface()!.getCurrentBlock() === + element) { const surfaceTranslation = - this.workspace.getBlockDragSurface().getSurfaceTranslation(); + this.workspace.getBlockDragSurface()!.getSurfaceTranslation(); x += surfaceTranslation.x; y += surfaceTranslation.y; } - element = /** @type {!SVGElement} */ (element.parentNode); + element = element.parentNode as SVGElement; } while (element && element !== this.workspace.getCanvas() && - element !== dragSurfaceGroup); + element !== dragSurfaceGroup); } return new Coordinate(x, y); } /** * Move a block by a relative offset. - * @param {number} dx Horizontal offset in workspace units. - * @param {number} dy Vertical offset in workspace units. + * @param dx Horizontal offset in workspace units. + * @param dy Vertical offset in workspace units. */ - moveBy(dx, dy) { + override moveBy(dx: number, dy: number) { if (this.parentBlock_) { throw Error('Block has parent.'); } const eventsEnabled = eventUtils.isEnabled(); - let event; + let event: BlockMove | null = null; if (eventsEnabled) { - event = /** @type {!BlockMove} */ - (new (eventUtils.get(eventUtils.BLOCK_MOVE))(this)); + event = new (eventUtils.get(eventUtils.BLOCK_MOVE))!(this) as BlockMove; } const xy = this.getRelativeToSurfaceXY(); this.translate(xy.x + dx, xy.y + dy); this.moveConnections(dx, dy); - if (eventsEnabled) { - event.recordNew(); + if (eventsEnabled && event) { + event!.recordNew(); eventUtils.fire(event); } this.workspace.resizeContents(); @@ -475,19 +442,18 @@ class BlockSvg extends Block { /** * Transforms a block by setting the translation on the transform attribute * of the block's SVG. - * @param {number} x The x coordinate of the translation in workspace units. - * @param {number} y The y coordinate of the translation in workspace units. + * @param x The x coordinate of the translation in workspace units. + * @param y The y coordinate of the translation in workspace units. */ - translate(x, y) { + translate(x: number, y: number) { this.getSvgRoot().setAttribute( - 'transform', 'translate(' + x + ',' + y + ')'); + 'transform', 'translate(' + x + ',' + y + ')'); } /** * Move this block to its workspace's drag surface, accounting for * positioning. Generally should be called at the same time as * setDragging_(true). Does nothing if useDragSurface_ is false. - * @package */ moveToDragSurface() { if (!this.useDragSurface_) { @@ -499,19 +465,19 @@ class BlockSvg extends Block { // This is in workspace coordinates. const xy = this.getRelativeToSurfaceXY(); this.clearTransformAttributes_(); - this.workspace.getBlockDragSurface().translateSurface(xy.x, xy.y); + this.workspace.getBlockDragSurface()!.translateSurface(xy.x, xy.y); // Execute the move on the top-level SVG component const svg = this.getSvgRoot(); if (svg) { - this.workspace.getBlockDragSurface().setBlocksAndShow(svg); + this.workspace.getBlockDragSurface()!.setBlocksAndShow(svg); } } /** * Move a block to a position. - * @param {Coordinate} xy The position to move to in workspace units. + * @param xy The position to move to in workspace units. */ - moveTo(xy) { + moveTo(xy: Coordinate) { const curXY = this.getRelativeToSurfaceXY(); this.moveBy(xy.x - curXY.x, xy.y - curXY.y); } @@ -520,78 +486,78 @@ class BlockSvg extends Block { * Move this block back to the workspace block canvas. * Generally should be called at the same time as setDragging_(false). * Does nothing if useDragSurface_ is false. - * @param {!Coordinate} newXY The position the block should take on - * on the workspace canvas, in workspace coordinates. - * @package + * @param newXY The position the block should take on on the workspace canvas, + * in workspace coordinates. */ - moveOffDragSurface(newXY) { + moveOffDragSurface(newXY: Coordinate) { if (!this.useDragSurface_) { return; } // Translate to current position, turning off 3d. this.translate(newXY.x, newXY.y); - this.workspace.getBlockDragSurface().clearAndHide( - this.workspace.getCanvas()); + this.workspace.getBlockDragSurface()!.clearAndHide( + this.workspace.getCanvas()); } /** * Move this block during a drag, taking into account whether we are using a * drag surface to translate blocks. * This block must be a top-level block. - * @param {!Coordinate} newLoc The location to translate to, in - * workspace coordinates. - * @package + * @param newLoc The location to translate to, in workspace coordinates. */ - moveDuringDrag(newLoc) { + moveDuringDrag(newLoc: Coordinate) { if (this.useDragSurface_) { - this.workspace.getBlockDragSurface().translateSurface(newLoc.x, newLoc.y); + this.workspace.getBlockDragSurface()!.translateSurface( + newLoc.x, newLoc.y); } else { - (/** @type {?} */ (this.svgGroup_)).translate_ = - 'translate(' + newLoc.x + ',' + newLoc.y + ')'; - (/** @type {?} */ (this.svgGroup_)) - .setAttribute( - 'transform', - (/** @type {?} */ (this.svgGroup_)).translate_ + - (/** @type {?} */ (this.svgGroup_)).skew_); + (this.svgGroup_ as AnyDuringMigration).translate_ = + 'translate(' + newLoc.x + ',' + newLoc.y + ')'; + (this.svgGroup_ as AnyDuringMigration) + .setAttribute( + 'transform', + (this.svgGroup_ as AnyDuringMigration).translate_ + + (this.svgGroup_ as AnyDuringMigration).skew_); } } /** * Clear the block of transform="..." attributes. * Used when the block is switching from 3d to 2d transform or vice versa. - * @private */ - clearTransformAttributes_() { + private clearTransformAttributes_() { this.getSvgRoot().removeAttribute('transform'); } - /** - * Snap this block to the nearest grid point. - */ + /** Snap this block to the nearest grid point. */ snapToGrid() { if (!this.workspace) { - return; // Deleted block. + return; } + // Deleted block. if (this.workspace.isDragging()) { - return; // Don't bump blocks during a drag. + return; } + // Don't bump blocks during a drag. if (this.getParent()) { - return; // Only snap top-level blocks. + return; } + // Only snap top-level blocks. if (this.isInFlyout) { - return; // Don't move blocks around in a flyout. + return; } + // Don't move blocks around in a flyout. const grid = this.workspace.getGrid(); if (!grid || !grid.shouldSnap()) { - return; // Config says no snapping. + return; } + // Config says no snapping. const spacing = grid.getSpacing(); const half = spacing / 2; const xy = this.getRelativeToSurfaceXY(); const dx = - Math.round(Math.round((xy.x - half) / spacing) * spacing + half - xy.x); + Math.round(Math.round((xy.x - half) / spacing) * spacing + half - xy.x); const dy = - Math.round(Math.round((xy.y - half) / spacing) * spacing + half - xy.y); + Math.round(Math.round((xy.y - half) / spacing) * spacing + half - xy.y); if (dx || dy) { this.moveBy(dx, dy); } @@ -601,9 +567,9 @@ class BlockSvg extends Block { * Returns the coordinates of a bounding box describing the dimensions of this * block and any blocks stacked below it. * Coordinate system: workspace coordinates. - * @return {!Rect} Object with coordinates of the bounding box. + * @return Object with coordinates of the bounding box. */ - getBoundingRectangle() { + getBoundingRectangle(): Rect { const blockXY = this.getRelativeToSurfaceXY(); const blockBounds = this.getHeightWidth(); let left; @@ -623,19 +589,17 @@ class BlockSvg extends Block { * A dirty field is a field that needs to be re-rendered. */ markDirty() { - this.pathObject.constants = (/** @type {!WorkspaceSvg} */ (this.workspace)) - .getRenderer() - .getConstants(); - for (let i = 0, input; (input = this.inputList[i]); i++) { + this.pathObject.constants = (this.workspace).getRenderer().getConstants(); + for (let i = 0, input; input = this.inputList[i]; i++) { input.markDirty(); } } /** * Set whether the block is collapsed or not. - * @param {boolean} collapsed True if collapsed. + * @param collapsed True if collapsed. */ - setCollapsed(collapsed) { + override setCollapsed(collapsed: boolean) { if (this.collapsed_ === collapsed) { return; } @@ -644,22 +608,21 @@ class BlockSvg extends Block { this.updateCollapsed_(); } else if (this.rendered) { this.render(); - // Don't bump neighbours. Users like to store collapsed functions together - // and bumping makes them go out of alignment. } } + // Don't bump neighbours. Users like to store collapsed functions together + // and bumping makes them go out of alignment. /** * Makes sure that when the block is collapsed, it is rendered correctly * for that state. - * @private */ - updateCollapsed_() { + private updateCollapsed_() { const collapsed = this.isCollapsed(); const collapsedInputName = constants.COLLAPSED_INPUT_NAME; const collapsedFieldName = constants.COLLAPSED_FIELD_NAME; - for (let i = 0, input; (input = this.inputList[i]); i++) { + for (let i = 0, input; input = this.inputList[i]; i++) { if (input.name !== collapsedInputName) { input.setVisible(!collapsed); } @@ -672,7 +635,7 @@ class BlockSvg extends Block { } const icons = this.getIcons(); - for (let i = 0, icon; (icon = icons[i]); i++) { + for (let i = 0, icon; icon = icons[i]; i++) { icon.setVisible(false); } @@ -683,18 +646,21 @@ class BlockSvg extends Block { return; } const input = this.getInput(collapsedInputName) || - this.appendDummyInput(collapsedInputName); - input.appendField(new FieldLabel(text), collapsedFieldName); + this.appendDummyInput(collapsedInputName); + // AnyDuringMigration because: Argument of type 'FieldLabel' is not + // assignable to parameter of type 'string | Field'. + input.appendField( + new FieldLabel(text) as AnyDuringMigration, collapsedFieldName); } /** * Open the next (or previous) FieldTextInput. - * @param {!Field} start Current field. - * @param {boolean} forward If true go forward, otherwise backward. + * @param start Current field. + * @param forward If true go forward, otherwise backward. */ - tab(start, forward) { + tab(start: Field, forward: boolean) { const tabCursor = new TabNavigateCursor(); - tabCursor.setCurNode(ASTNode.createFieldNode(start)); + tabCursor.setCurNode(ASTNode.createFieldNode(start)!); const currentNode = tabCursor.getCurNode(); if (forward) { @@ -705,35 +671,31 @@ class BlockSvg extends Block { const nextNode = tabCursor.getCurNode(); if (nextNode && nextNode !== currentNode) { - const nextField = /** @type {!Field} */ (nextNode.getLocation()); + const nextField = nextNode.getLocation() as Field; nextField.showEditor(); // Also move the cursor if we're in keyboard nav mode. if (this.workspace.keyboardAccessibilityMode) { - this.workspace.getCursor().setCurNode(nextNode); + this.workspace.getCursor()!.setCurNode(nextNode); } } } /** * Handle a mouse-down on an SVG block. - * @param {!Event} e Mouse down event or touch start event. - * @private + * @param e Mouse down event or touch start event. */ - onMouseDown_(e) { + private onMouseDown_(e: Event) { const gesture = this.workspace && this.workspace.getGesture(e); if (gesture) { gesture.handleBlockStart(e, this); } } - /** - * Load the block's help page in a new window. - * @package - */ + /** Load the block's help page in a new window. */ showHelp() { const url = - (typeof this.helpUrl === 'function') ? this.helpUrl() : this.helpUrl; + typeof this.helpUrl === 'function' ? this.helpUrl() : this.helpUrl; if (url) { window.open(url); } @@ -741,17 +703,18 @@ class BlockSvg extends Block { /** * Generate the context menu for this block. - * @return {?Array} - * Context menu options or null if no menu. - * @protected + * @return Context menu options or null if no menu. */ - generateContextMenu() { + protected generateContextMenu(): + Array | null { if (this.workspace.options.readOnly || !this.contextMenu) { return null; } + // AnyDuringMigration because: Argument of type '{ block: this; }' is not + // assignable to parameter of type 'Scope'. const menuOptions = ContextMenuRegistry.registry.getContextMenuOptions( - ContextMenuRegistry.ScopeType.BLOCK, {block: this}); + ContextMenuRegistry.ScopeType.BLOCK, + { block: this } as AnyDuringMigration); // Allow the block to add or modify menuOptions. if (this.customContextMenu) { @@ -763,28 +726,26 @@ class BlockSvg extends Block { /** * Show the context menu for this block. - * @param {!Event} e Mouse event. - * @package + * @param e Mouse event. */ - showContextMenu(e) { + showContextMenu(e: Event) { const menuOptions = this.generateContextMenu(); if (menuOptions && menuOptions.length) { ContextMenu.show(e, menuOptions, this.RTL); - ContextMenu.setCurrentBlock(this); + // AnyDuringMigration because: Argument of type 'this' is not assignable + // to parameter of type 'Block | null'. + ContextMenu.setCurrentBlock(this as AnyDuringMigration); } } /** * Move the connections for this block and all blocks attached under it. * Also update any attached bubbles. - * @param {number} dx Horizontal offset from current location, in workspace - * units. - * @param {number} dy Vertical offset from current location, in workspace - * units. - * @package + * @param dx Horizontal offset from current location, in workspace units. + * @param dy Vertical offset from current location, in workspace units. */ - moveConnections(dx, dy) { + moveConnections(dx: number, dy: number) { if (!this.rendered) { // Rendering is required to lay out the blocks. // This is probably an invisible block attached to a collapsed block. @@ -801,49 +762,46 @@ class BlockSvg extends Block { // Recurse through all blocks attached under this one. for (let i = 0; i < this.childBlocks_.length; i++) { - (/** @type {!BlockSvg} */ (this.childBlocks_[i])).moveConnections(dx, dy); + (this.childBlocks_[i] as BlockSvg).moveConnections(dx, dy); } } /** * Recursively adds or removes the dragging class to this node and its * children. - * @param {boolean} adding True if adding, false if removing. - * @package + * @param adding True if adding, false if removing. */ - setDragging(adding) { + setDragging(adding: boolean) { if (adding) { const group = this.getSvgRoot(); - (/** @type {?} */ (group)).translate_ = ''; - (/** @type {?} */ (group)).skew_ = ''; + (group as AnyDuringMigration).translate_ = ''; + (group as AnyDuringMigration).skew_ = ''; common.draggingConnections.push(...this.getConnections_(true)); - dom.addClass( - /** @type {!Element} */ (this.svgGroup_), 'blocklyDragging'); + dom.addClass(this.svgGroup_ as Element, 'blocklyDragging'); } else { common.draggingConnections.length = 0; - dom.removeClass( - /** @type {!Element} */ (this.svgGroup_), 'blocklyDragging'); + dom.removeClass(this.svgGroup_ as Element, 'blocklyDragging'); } // Recurse through all blocks attached under this one. for (let i = 0; i < this.childBlocks_.length; i++) { - (/** @type {!BlockSvg} */ (this.childBlocks_[i])).setDragging(adding); + (this.childBlocks_[i] as BlockSvg).setDragging(adding); } } /** * Set whether this block is movable or not. - * @param {boolean} movable True if movable. + * @param movable True if movable. */ - setMovable(movable) { + override setMovable(movable: boolean) { super.setMovable(movable); this.pathObject.updateMovable(movable); } /** * Set whether this block is editable or not. - * @param {boolean} editable True if editable. + * @param editable True if editable. */ - setEditable(editable) { + override setEditable(editable: boolean) { super.setEditable(editable); const icons = this.getIcons(); for (let i = 0; i < icons.length; i++) { @@ -853,10 +811,9 @@ class BlockSvg extends Block { /** * Sets whether this block is a shadow block or not. - * @param {boolean} shadow True if a shadow. - * @package + * @param shadow True if a shadow. */ - setShadow(shadow) { + override setShadow(shadow: boolean) { super.setShadow(shadow); this.applyColour(); } @@ -864,38 +821,38 @@ class BlockSvg extends Block { /** * Set whether this block is an insertion marker block or not. * Once set this cannot be unset. - * @param {boolean} insertionMarker True if an insertion marker. - * @package + * @param insertionMarker True if an insertion marker. */ - setInsertionMarker(insertionMarker) { + override setInsertionMarker(insertionMarker: boolean) { if (this.isInsertionMarker_ === insertionMarker) { - return; // No change. + return; } + // No change. this.isInsertionMarker_ = insertionMarker; if (this.isInsertionMarker_) { this.setColour( - this.workspace.getRenderer().getConstants().INSERTION_MARKER_COLOUR); + this.workspace.getRenderer().getConstants().INSERTION_MARKER_COLOUR); this.pathObject.updateInsertionMarker(true); } } /** * Return the root node of the SVG or null if none exists. - * @return {!SVGGElement} The root SVG node (probably a group). + * @return The root SVG node (probably a group). */ - getSvgRoot() { + getSvgRoot(): SVGGElement { return this.svgGroup_; } /** * Dispose of this block. - * @param {boolean=} healStack If true, then try to heal any gap by connecting - * the next statement with the previous statement. Otherwise, dispose of - * all children of this block. - * @param {boolean=} animate If true, show a disposal animation and sound. + * @param healStack If true, then try to heal any gap by connecting the next + * statement with the previous statement. Otherwise, dispose of all + * children of this block. + * @param animate If true, show a disposal animation and sound. * @suppress {checkTypes} */ - dispose(healStack, animate) { + override dispose(healStack?: boolean, animate?: boolean) { if (!this.workspace) { // The block has already been deleted. return; @@ -928,19 +885,31 @@ class BlockSvg extends Block { for (const n in this.warningTextDb_) { clearTimeout(this.warningTextDb_[n]); } - this.warningTextDb_ = null; + // AnyDuringMigration because: Type 'null' is not assignable to type '{ + // [key: string]: number; }'. + this.warningTextDb_ = null as AnyDuringMigration; } const icons = this.getIcons(); for (let i = 0; i < icons.length; i++) { icons[i].dispose(); } + + // Just deleting this block from the DOM would result in a memory leak as + // well as corruption of the connection database. Therefore we must + // methodically step through the blocks and carefully disassemble them. + if (common.getSelected() === this) { + common.setSelected(null); + } + super.dispose(!!healStack); dom.removeNode(this.svgGroup_); blockWorkspace.resizeContents(); // Sever JavaScript to DOM connections. - this.svgGroup_ = null; + // AnyDuringMigration because: Type 'null' is not assignable to type + // 'SVGGElement'. + this.svgGroup_ = null as AnyDuringMigration; dom.stopTextWidthCache(); } @@ -962,33 +931,34 @@ class BlockSvg extends Block { // (https://github.com/google/blockly/issues/4832) this.dispose(false, true); } else { - this.dispose(/* heal */ true, true); + this.dispose(/* heal */ + true, true); } eventUtils.setGroup(false); } /** * Encode a block for copying. - * @return {?ICopyable.CopyData} Copy metadata, or null if the block is - * an insertion marker. - * @package + * @return Copy metadata, or null if the block is an insertion marker. */ - toCopyData() { + toCopyData(): CopyData | null { if (this.isInsertionMarker_) { return null; } + // AnyDuringMigration because: Argument of type 'this' is not assignable to + // parameter of type 'Block'. AnyDuringMigration because: Argument of type + // 'this' is not assignable to parameter of type 'Block'. return { - saveInfo: /** @type {!blocks.State} */ ( - blocks.save(this, {addCoordinates: true, addNextBlocks: false})), + saveInfo: blocks.save( + this as AnyDuringMigration, + { addCoordinates: true, addNextBlocks: false }) as + blocks.State, source: this.workspace, - typeCounts: common.getBlockTypeCounts(this, true), + typeCounts: common.getBlockTypeCounts(this as AnyDuringMigration, true), }; } - /** - * Updates the colour of the block to match the block's state. - * @package - */ + /** Updates the colour of the block to match the block's state. */ applyColour() { this.pathObject.applyColour(this); @@ -997,8 +967,8 @@ class BlockSvg extends Block { icons[i].applyColour(); } - for (let x = 0, input; (input = this.inputList[x]); x++) { - for (let y = 0, field; (field = input.fieldRow[y]); y++) { + for (let x = 0, input; input = this.inputList[x]; x++) { + for (let y = 0, field; field = input.fieldRow[y]; y++) { field.applyColour(); } } @@ -1007,16 +977,14 @@ class BlockSvg extends Block { /** * Updates the color of the block (and children) to match the current disabled * state. - * @package */ updateDisabled() { - const children = - /** @type {!Array} */ (this.getChildren(false)); + const children = (this.getChildren(false)); this.applyColour(); if (this.isCollapsed()) { return; } - for (let i = 0, child; (child = children[i]); i++) { + for (let i = 0, child; child = children[i]; i++) { if (child.rendered) { child.updateDisabled(); } @@ -1026,21 +994,19 @@ class BlockSvg extends Block { /** * Get the comment icon attached to this block, or null if the block has no * comment. - * @return {?Comment} The comment icon attached to this block, or null. + * @return The comment icon attached to this block, or null. */ - getCommentIcon() { + getCommentIcon(): Comment | null { return this.commentIcon_; } /** * Set this block's comment text. - * @param {?string} text The text, or null to delete. + * @param text The text, or null to delete. */ - setCommentText(text) { - const {Comment} = goog.module.get('Blockly.Comment'); - if (!Comment) { - throw Error('Missing require for Blockly.Comment'); - } + override setCommentText(text: string | null) { + // AnyDuringMigration because: Property 'get' does not exist on type + // '(name: string) => void'. if (this.commentModel.text === text) { return; } @@ -1050,17 +1016,20 @@ class BlockSvg extends Block { if (!!this.commentIcon_ === shouldHaveComment) { // If the comment's state of existence is correct, but the text is new // that means we're just updating a comment. - this.commentIcon_.updateText(); + this.commentIcon_!.updateText(); return; } + // For backwards compatibility. if (shouldHaveComment) { this.commentIcon_ = new Comment(this); - this.comment = this.commentIcon_; // For backwards compatibility. - } else { - this.commentIcon_.dispose(); + this.comment = this.commentIcon_; + } else // For backwards compatibility. + { + this.commentIcon_!.dispose(); this.commentIcon_ = null; - this.comment = null; // For backwards compatibility. + this.comment = null; } + // For backwards compatibility. if (this.rendered) { this.render(); // Adding or removing a comment icon will cause the block to change shape. @@ -1070,15 +1039,11 @@ class BlockSvg extends Block { /** * Set this block's warning text. - * @param {?string} text The text, or null to delete. - * @param {string=} opt_id An optional ID for the warning text to be able to - * maintain multiple warnings. + * @param text The text, or null to delete. + * @param opt_id An optional ID for the warning text to be able to maintain + * multiple warnings. */ - setWarningText(text, opt_id) { - const {Warning} = goog.module.get('Blockly.Warning'); - if (!Warning) { - throw Error('Missing require for Blockly.Warning'); - } + override setWarningText(text: string | null, opt_id?: string) { if (!this.warningTextDb_) { // Create a database of warning PIDs. // Only runs once per block (and only those with warnings). @@ -1100,8 +1065,9 @@ class BlockSvg extends Block { // Don't change the warning text during a drag. // Wait until the drag finishes. const thisBlock = this; - this.warningTextDb_[id] = setTimeout(function() { - if (thisBlock.workspace) { // Check block wasn't deleted. + this.warningTextDb_[id] = setTimeout(function () { + if (thisBlock.workspace) { + // Check block wasn't deleted. delete thisBlock.warningTextDb_[id]; thisBlock.setWarningText(text, id); } @@ -1125,14 +1091,14 @@ class BlockSvg extends Block { } if (collapsedParent) { collapsedParent.setWarningText( - Msg['COLLAPSED_WARNINGS_WARNING'], BlockSvg.COLLAPSED_WARNING_ID); + Msg['COLLAPSED_WARNINGS_WARNING'], BlockSvg.COLLAPSED_WARNING_ID); } if (!this.warning) { this.warning = new Warning(this); changedState = true; } - this.warning.setText(/** @type {string} */ (text), id); + this.warning!.setText((text), id); } else { // Dispose all warnings if no ID is given. if (this.warning && !id) { @@ -1157,9 +1123,9 @@ class BlockSvg extends Block { /** * Give this block a mutator dialog. - * @param {?Mutator} mutator A mutator dialog instance or null to remove. + * @param mutator A mutator dialog instance or null to remove. */ - setMutator(mutator) { + override setMutator(mutator: Mutator | null) { if (this.mutator && this.mutator !== mutator) { this.mutator.dispose(); } @@ -1177,9 +1143,9 @@ class BlockSvg extends Block { /** * Set whether the block is enabled or not. - * @param {boolean} enabled True if enabled. + * @param enabled True if enabled. */ - setEnabled(enabled) { + override setEnabled(enabled: boolean) { if (this.isEnabled() !== enabled) { super.setEnabled(enabled); if (this.rendered && !this.getInheritedDisabled()) { @@ -1191,9 +1157,9 @@ class BlockSvg extends Block { /** * Set whether the block is highlighted or not. Block highlighting is * often used to visually mark blocks currently being executed. - * @param {boolean} highlighted True if highlighted. + * @param highlighted True if highlighted. */ - setHighlighted(highlighted) { + setHighlighted(highlighted: boolean) { if (!this.rendered) { return; } @@ -1220,11 +1186,9 @@ class BlockSvg extends Block { /** * Update the cursor over this block by adding or removing a class. - * @param {boolean} enable True if the delete cursor should be shown, false - * otherwise. - * @package + * @param enable True if the delete cursor should be shown, false otherwise. */ - setDeleteStyle(enable) { + setDeleteStyle(enable: boolean) { this.pathObject.updateDraggingDelete(enable); } @@ -1234,21 +1198,21 @@ class BlockSvg extends Block { /** * Get the colour of a block. - * @return {string} #RRGGBB string. + * @return #RRGGBB string. */ - getColour() { + override getColour(): string { return this.style.colourPrimary; } /** * Change the colour of a block. - * @param {number|string} colour HSV hue value, or #RRGGBB string. + * @param colour HSV hue value, or #RRGGBB string. */ - setColour(colour) { + override setColour(colour: number | string) { super.setColour(colour); const styleObj = - this.workspace.getRenderer().getConstants().getBlockStyleForColour( - this.colour_); + this.workspace.getRenderer().getConstants().getBlockStyleForColour( + this.colour_); this.pathObject.setStyle(styleObj.style); this.style = styleObj.style; @@ -1259,13 +1223,13 @@ class BlockSvg extends Block { /** * Set the style and colour values of a block. - * @param {string} blockStyleName Name of the block style. + * @param blockStyleName Name of the block style. * @throws {Error} if the block style does not exist. */ - setStyle(blockStyleName) { + override setStyle(blockStyleName: string) { const blockStyle = - this.workspace.getRenderer().getConstants().getBlockStyle( - blockStyleName); + this.workspace.getRenderer().getConstants().getBlockStyle( + blockStyleName); this.styleName_ = blockStyleName; if (blockStyle) { @@ -1286,30 +1250,31 @@ class BlockSvg extends Block { * tags do not respect z-index so SVG renders them in the * order that they are in the DOM. By placing this block first within the * block group's , it will render on top of any other blocks. - * @package */ bringToFront() { let block = this; do { const root = block.getSvgRoot(); const parent = root.parentNode; - const childNodes = parent.childNodes; + const childNodes = parent!.childNodes; // Avoid moving the block if it's already at the bottom. if (childNodes[childNodes.length - 1] !== root) { - parent.appendChild(root); + parent!.appendChild(root); } - block = block.getParent(); + // AnyDuringMigration because: Type 'BlockSvg | null' is not assignable + // to type 'this'. + block = block.getParent() as AnyDuringMigration; } while (block); } /** * Set whether this block can chain onto the bottom of another block. - * @param {boolean} newBoolean True if there can be a previous statement. - * @param {(string|Array|null)=} opt_check Statement type or - * list of statement types. Null/undefined if any type could be - * connected. + * @param newBoolean True if there can be a previous statement. + * @param opt_check Statement type or list of statement types. Null/undefined + * if any type could be connected. */ - setPreviousStatement(newBoolean, opt_check) { + override setPreviousStatement( + newBoolean: boolean, opt_check?: string | string[] | null) { super.setPreviousStatement(newBoolean, opt_check); if (this.rendered) { @@ -1320,12 +1285,12 @@ class BlockSvg extends Block { /** * Set whether another block can chain onto the bottom of this block. - * @param {boolean} newBoolean True if there can be a next statement. - * @param {(string|Array|null)=} opt_check Statement type or - * list of statement types. Null/undefined if any type could be - * connected. + * @param newBoolean True if there can be a next statement. + * @param opt_check Statement type or list of statement types. Null/undefined + * if any type could be connected. */ - setNextStatement(newBoolean, opt_check) { + override setNextStatement( + newBoolean: boolean, opt_check?: string | string[] | null) { super.setNextStatement(newBoolean, opt_check); if (this.rendered) { @@ -1336,12 +1301,11 @@ class BlockSvg extends Block { /** * Set whether this block returns a value. - * @param {boolean} newBoolean True if there is an output. - * @param {(string|Array|null)=} opt_check Returned type or list - * of returned types. Null or undefined if any type could be returned - * (e.g. variable get). + * @param newBoolean True if there is an output. + * @param opt_check Returned type or list of returned types. Null or + * undefined if any type could be returned (e.g. variable get). */ - setOutput(newBoolean, opt_check) { + override setOutput(newBoolean: boolean, opt_check?: string | string[] | null) { super.setOutput(newBoolean, opt_check); if (this.rendered) { @@ -1352,9 +1316,9 @@ class BlockSvg extends Block { /** * Set whether value inputs are arranged horizontally or vertically. - * @param {boolean} newBoolean True if inputs are horizontal. + * @param newBoolean True if inputs are horizontal. */ - setInputsInline(newBoolean) { + override setInputsInline(newBoolean: boolean) { super.setInputsInline(newBoolean); if (this.rendered) { @@ -1365,13 +1329,13 @@ class BlockSvg extends Block { /** * Remove an input from this block. - * @param {string} name The name of the input. - * @param {boolean=} opt_quiet True to prevent error if input is not present. - * @return {boolean} True if operation succeeds, false if input is not present - * and opt_quiet is true + * @param name The name of the input. + * @param opt_quiet True to prevent error if input is not present. + * @return True if operation succeeds, false if input is not present and + * opt_quiet is true * @throws {Error} if the input is not present and opt_quiet is not true. */ - removeInput(name, opt_quiet) { + override removeInput(name: string, opt_quiet?: boolean): boolean { const removed = super.removeInput(name, opt_quiet); if (this.rendered) { @@ -1385,11 +1349,10 @@ class BlockSvg extends Block { /** * Move a numbered input to a different location on this block. - * @param {number} inputIndex Index of the input to move. - * @param {number} refIndex Index of input that should be after the moved - * input. + * @param inputIndex Index of the input to move. + * @param refIndex Index of input that should be after the moved input. */ - moveNumberedInputBefore(inputIndex, refIndex) { + override moveNumberedInputBefore(inputIndex: number, refIndex: number) { super.moveNumberedInputBefore(inputIndex, refIndex); if (this.rendered) { @@ -1401,14 +1364,12 @@ class BlockSvg extends Block { /** * Add a value input, statement input or local variable to this block. - * @param {number} type One of Blockly.inputTypes. - * @param {string} name Language-neutral identifier which may used to find - * this input again. Should be unique to this block. - * @return {!Input} The input object created. - * @protected - * @override - */ - appendInput_(type, name) { + * @param type One of Blockly.inputTypes. + * @param name Language-neutral identifier which may used to find this input + * again. Should be unique to this block. + * @return The input object created. + */ + protected override appendInput_(type: number, name: string): Input { const input = super.appendInput_(type, name); if (this.rendered) { @@ -1425,24 +1386,18 @@ class BlockSvg extends Block { * Used by the deserializer to be more efficient. Setting a connection's * tracked_ value to false keeps it from adding itself to the db when it * gets its first moveTo call, saving expensive ops for later. - * @param {boolean} track If true, start tracking. If false, stop tracking. - * @package + * @param track If true, start tracking. If false, stop tracking. */ - setConnectionTracking(track) { + setConnectionTracking(track: boolean) { if (this.previousConnection) { - /** @type {!RenderedConnection} */ (this.previousConnection) - .setTracking(track); + (this.previousConnection).setTracking(track); } if (this.outputConnection) { - /** @type {!RenderedConnection} */ (this.outputConnection) - .setTracking(track); + (this.outputConnection).setTracking(track); } if (this.nextConnection) { - /** @type {!RenderedConnection} */ (this.nextConnection) - .setTracking(track); - const child = - /** @type {!RenderedConnection} */ (this.nextConnection) - .targetBlock(); + (this.nextConnection).setTracking(track); + const child = (this.nextConnection).targetBlock(); if (child) { child.setConnectionTracking(track); } @@ -1456,8 +1411,7 @@ class BlockSvg extends Block { } for (let i = 0; i < this.inputList.length; i++) { - const conn = - /** @type {!RenderedConnection} */ (this.inputList[i].connection); + const conn = this.inputList[i].connection as RenderedConnection; if (conn) { conn.setTracking(track); @@ -1472,13 +1426,12 @@ class BlockSvg extends Block { /** * Returns connections originating from this block. - * @param {boolean} all If true, return all connections even hidden ones. + * @param all If true, return all connections even hidden ones. * Otherwise, for a non-rendered block return an empty list, and for a - * collapsed block don't return inputs connections. - * @return {!Array} Array of connections. - * @package + * collapsed block don't return inputs connections. + * @return Array of connections. */ - getConnections_(all) { + override getConnections_(all: boolean): RenderedConnection[] { const myConnections = []; if (all || this.rendered) { if (this.outputConnection) { @@ -1491,9 +1444,9 @@ class BlockSvg extends Block { myConnections.push(this.nextConnection); } if (all || !this.collapsed_) { - for (let i = 0, input; (input = this.inputList[i]); i++) { + for (let i = 0, input; input = this.inputList[i]; i++) { if (input.connection) { - myConnections.push(input.connection); + myConnections.push(input.connection as RenderedConnection); } } } @@ -1504,73 +1457,84 @@ class BlockSvg extends Block { /** * Walks down a stack of blocks and finds the last next connection on the * stack. - * @param {boolean} ignoreShadows If true,the last connection on a non-shadow - * block will be returned. If false, this will follow shadows to find the - * last connection. - * @return {?RenderedConnection} The last next connection on the stack, - * or null. - * @package - * @override + * @param ignoreShadows If true,the last connection on a non-shadow block will + * be returned. If false, this will follow shadows to find the last + * connection. + * @return The last next connection on the stack, or null. */ - lastConnectionInStack(ignoreShadows) { - return /** @type {RenderedConnection} */ ( - super.lastConnectionInStack(ignoreShadows)); + override lastConnectionInStack(ignoreShadows: boolean): RenderedConnection + | null { + return super.lastConnectionInStack(ignoreShadows) as RenderedConnection; } /** * Find the connection on this block that corresponds to the given connection * on the other block. * Used to match connections between a block and its insertion marker. - * @param {!Block} otherBlock The other block to match against. - * @param {!Connection} conn The other connection to match. - * @return {?RenderedConnection} The matching connection on this block, - * or null. - * @package - * @override + * @param otherBlock The other block to match against. + * @param conn The other connection to match. + * @return The matching connection on this block, or null. */ - getMatchingConnection(otherBlock, conn) { - return /** @type {RenderedConnection} */ ( - super.getMatchingConnection(otherBlock, conn)); + override getMatchingConnection(otherBlock: Block, conn: Connection): + RenderedConnection | null { + return super.getMatchingConnection(otherBlock, conn) as RenderedConnection; } /** * Create a connection of the specified type. - * @param {number} type The type of the connection to create. - * @return {!RenderedConnection} A new connection of the specified type. - * @protected + * @param type The type of the connection to create. + * @return A new connection of the specified type. */ - makeConnection_(type) { + protected override makeConnection_(type: number): RenderedConnection { return new RenderedConnection(this, type); } + /** + * Return the next statement block directly connected to this block. + * @return The next statement block or null. + */ + override getNextBlock(): BlockSvg | null { + return super.getNextBlock() as BlockSvg; + } + + /** + * Returns the block connected to the previous connection. + * @return The previous statement block or null. + */ + override getPreviousBlock(): BlockSvg | null { + return super.getPreviousBlock() as BlockSvg; + } + /** * Bump unconnected blocks out of alignment. Two blocks which aren't actually * connected should not coincidentally line up on screen. */ - bumpNeighbours() { + override bumpNeighbours() { if (!this.workspace) { - return; // Deleted block. + return; } + // Deleted block. if (this.workspace.isDragging()) { - return; // Don't bump blocks during a drag. + return; } + // Don't bump blocks during a drag. const rootBlock = this.getRootBlock(); if (rootBlock.isInFlyout) { - return; // Don't move blocks around in a flyout. + return; } + // Don't move blocks around in a flyout. // Loop through every connection on this block. const myConnections = this.getConnections_(false); - for (let i = 0, connection; (connection = myConnections[i]); i++) { - const renderedConn = /** @type {!RenderedConnection} */ (connection); + for (let i = 0, connection; connection = myConnections[i]; i++) { + const renderedConn = (connection); // Spider down from this block bumping all sub-blocks. if (renderedConn.isConnected() && renderedConn.isSuperior()) { - renderedConn.targetBlock().bumpNeighbours(); + renderedConn.targetBlock()!.bumpNeighbours(); } const neighbours = connection.neighbours(config.snapRadius); - for (let j = 0, otherConnection; (otherConnection = neighbours[j]); j++) { - const renderedOther = - /** @type {!RenderedConnection} */ (otherConnection); + for (let j = 0, otherConnection; otherConnection = neighbours[j]; j++) { + const renderedOther = otherConnection as RenderedConnection; // If both connections are connected, that's probably fine. But if // either one of them is unconnected, then there could be confusion. if (!renderedConn.isConnected() || !renderedOther.isConnected()) { @@ -1591,20 +1555,19 @@ class BlockSvg extends Block { /** * Schedule snapping to grid and bumping neighbours to occur after a brief * delay. - * @package */ scheduleSnapAndBump() { const block = this; // Ensure that any snap and bump are part of this move's event group. const group = eventUtils.getGroup(); - setTimeout(function() { + setTimeout(function () { eventUtils.setGroup(group); block.snapToGrid(); eventUtils.setGroup(false); }, config.bumpDelay / 2); - setTimeout(function() { + setTimeout(function () { eventUtils.setGroup(group); block.bumpNeighbours(); eventUtils.setGroup(false); @@ -1615,17 +1578,17 @@ class BlockSvg extends Block { * Position a block so that it doesn't move the target block when connected. * The block to position is usually either the first block in a dragged stack * or an insertion marker. - * @param {!RenderedConnection} sourceConnection The connection on the - * moving block's stack. - * @param {!RenderedConnection} targetConnection The connection that - * should stay stationary as this block is positioned. - * @package + * @param sourceConnection The connection on the moving block's stack. + * @param targetConnection The connection that should stay stationary as this + * block is positioned. */ - positionNearConnection(sourceConnection, targetConnection) { + positionNearConnection( + sourceConnection: RenderedConnection, + targetConnection: RenderedConnection) { // We only need to position the new block if it's before the existing one, // otherwise its position is set by the previous block. if (sourceConnection.type === ConnectionType.NEXT_STATEMENT || - sourceConnection.type === ConnectionType.INPUT_VALUE) { + sourceConnection.type === ConnectionType.INPUT_VALUE) { const dx = targetConnection.x - sourceConnection.x; const dy = targetConnection.y - sourceConnection.y; @@ -1633,104 +1596,33 @@ class BlockSvg extends Block { } } - /** - * Return the parent block or null if this block is at the top level. - * @return {?BlockSvg} The block (if any) that holds the current block. - * @override - */ - getParent() { - return /** @type {?BlockSvg} */ (super.getParent()); - } - - /** - * @return {?BlockSvg} The block (if any) that surrounds the current block. - * @override - */ - getSurroundParent() { - return /** @type {?BlockSvg} */ (super.getSurroundParent()); - } - - /** - * @return {?BlockSvg} The next statement block or null. - * @override - */ - getNextBlock() { - return /** @type {?BlockSvg} */ (super.getNextBlock()); - } - - /** - * @return {?BlockSvg} The previou statement block or null. - * @override - */ - getPreviousBlock() { - return /** @type {?BlockSvg} */ (super.getPreviousBlock()); - } - - /** - * @return {?RenderedConnection} The first statement connection or null. - * @package - * @override - */ - getFirstStatementConnection() { - return /** @type {?RenderedConnection} */ ( - super.getFirstStatementConnection()); - } - - /** - * @return {!BlockSvg} The top block in a stack. - * @override - */ - getTopStackBlock() { - return /** @type {!BlockSvg} */ (super.getTopStackBlock()); - } - - /** - * @param {boolean} ordered Sort the list if true. - * @return {!Array} Children of this block. - * @override - */ - getChildren(ordered) { - return /** @type {!Array} */ (super.getChildren(ordered)); + /** @return The first statement connection or null. */ + override getFirstStatementConnection(): RenderedConnection | null { + return super.getFirstStatementConnection() as RenderedConnection | null; } /** - * @param {boolean} ordered Sort the list if true. - * @return {!Array} Descendants of this block. - * @override + * Find all the blocks that are directly nested inside this one. + * Includes value and statement inputs, as well as any following statement. + * Excludes any connection on an output tab or any preceding statement. + * Blocks are optionally sorted by position; top to bottom. + * @param ordered Sort the list if true. + * @return Array of blocks. */ - getDescendants(ordered) { - return /** @type {!Array} */ (super.getDescendants(ordered)); - } - - /** - * @param {string} name The name of the input. - * @return {?BlockSvg} The attached value block, or null if the input is - * either disconnected or if the input does not exist. - * @override - */ - getInputTargetBlock(name) { - return /** @type {?BlockSvg} */ (super.getInputTargetBlock(name)); - } - - /** - * Return the top-most block in this block's tree. - * This will return itself if this block is at the top level. - * @return {!BlockSvg} The root block. - * @override - */ - getRootBlock() { - return /** @type {!BlockSvg} */ (super.getRootBlock()); + override getChildren(ordered: boolean): BlockSvg[] { + return super.getChildren(ordered) as BlockSvg[]; } /** * Lays out and reflows a block based on its contents and settings. - * @param {boolean=} opt_bubble If false, just render this block. + * @param opt_bubble If false, just render this block. * If true, also render block's parent, grandparent, etc. Defaults to true. */ - render(opt_bubble) { + render(opt_bubble?: boolean) { if (this.renderIsInProgress_) { - return; // Don't allow recursive renders. + return; } + // Don't allow recursive renders. this.renderIsInProgress_ = true; try { this.rendered = true; @@ -1759,17 +1651,14 @@ class BlockSvg extends Block { } } - /** - * Redraw any attached marker or cursor svgs if needed. - * @protected - */ - updateMarkers_() { + /** Redraw any attached marker or cursor svgs if needed. */ + protected updateMarkers_() { if (this.workspace.keyboardAccessibilityMode && this.pathObject.cursorSvg) { - this.workspace.getCursor().draw(); + this.workspace.getCursor()!.draw(); } if (this.workspace.keyboardAccessibilityMode && this.pathObject.markerSvg) { // TODO(#4592): Update all markers on the block. - this.workspace.getMarker(MarkerManager.LOCAL_MARKER).draw(); + this.workspace.getMarker(MarkerManager.LOCAL_MARKER)!.draw(); } } @@ -1777,9 +1666,8 @@ class BlockSvg extends Block { * Update all of the connections on this block with the new locations * calculated during rendering. Also move all of the connected blocks based * on the new connection locations. - * @private */ - updateConnectionLocations_() { + private updateConnectionLocations_() { const blockTL = this.getRelativeToSurfaceXY(); // Don't tighten previous or output connections because they are inferior // connections. @@ -1791,8 +1679,7 @@ class BlockSvg extends Block { } for (let i = 0; i < this.inputList.length; i++) { - const conn = - /** @type {!RenderedConnection} */ (this.inputList[i].connection); + const conn = this.inputList[i].connection as RenderedConnection; if (conn) { conn.moveToOffset(blockTL); if (conn.isConnected()) { @@ -1811,83 +1698,59 @@ class BlockSvg extends Block { /** * Add the cursor SVG to this block's SVG group. - * @param {SVGElement} cursorSvg The SVG root of the cursor to be added to the - * block SVG group. - * @package + * @param cursorSvg The SVG root of the cursor to be added to the block SVG + * group. */ - setCursorSvg(cursorSvg) { + setCursorSvg(cursorSvg: SVGElement) { this.pathObject.setCursorSvg(cursorSvg); } /** * Add the marker SVG to this block's SVG group. - * @param {SVGElement} markerSvg The SVG root of the marker to be added to the - * block SVG group. - * @package + * @param markerSvg The SVG root of the marker to be added to the block SVG + * group. */ - setMarkerSvg(markerSvg) { + setMarkerSvg(markerSvg: SVGElement) { this.pathObject.setMarkerSvg(markerSvg); } /** * Returns a bounding box describing the dimensions of this block * and any blocks stacked below it. - * @return {!{height: number, width: number}} Object with height and width - * properties in workspace units. - * @package + * @return Object with height and width properties in workspace units. */ - getHeightWidth() { + getHeightWidth(): { height: number, width: number } { let height = this.height; let width = this.width; // Recursively add size of subsequent blocks. const nextBlock = this.getNextBlock(); if (nextBlock) { const nextHeightWidth = nextBlock.getHeightWidth(); - const workspace = /** @type {!WorkspaceSvg} */ (this.workspace); + const workspace = (this.workspace); const tabHeight = workspace.getRenderer().getConstants().NOTCH_HEIGHT; height += nextHeightWidth.height - tabHeight; width = Math.max(width, nextHeightWidth.width); } - return {height: height, width: width}; + return { height, width }; } /** * Visual effect to show that if the dragging block is dropped, this block * will be replaced. If a shadow block, it will disappear. Otherwise it will * bump. - * @param {boolean} add True if highlighting should be added. - * @package + * @param add True if highlighting should be added. */ - fadeForReplacement(add) { + fadeForReplacement(add: boolean) { this.pathObject.updateReplacementFade(add); } /** * Visual effect to show that if the dragging block is dropped it will connect * to this input. - * @param {Connection} conn The connection on the input to highlight. - * @param {boolean} add True if highlighting should be added. - * @package + * @param conn The connection on the input to highlight. + * @param add True if highlighting should be added. */ - highlightShapeForInput(conn, add) { + highlightShapeForInput(conn: Connection, add: boolean) { this.pathObject.updateShapeForInputHighlight(conn, add); } } - -/** - * Constant for identifying rows that are to be rendered inline. - * Don't collide with Blockly.inputTypes. - * @const - */ -BlockSvg.INLINE = -1; - -/** - * ID to give the "collapsed warnings" warning. Allows us to remove the - * "collapsed warnings" warning without removing any warnings that belong to - * the block. - * @type {string} - * @const - */ -BlockSvg.COLLAPSED_WARNING_ID = 'TEMP_COLLAPSED_WARNING_'; - -exports.BlockSvg = BlockSvg; diff --git a/core/blockly.ts b/core/blockly.ts index 55fa06bdaa9..7f4bb2fa1ad 100644 --- a/core/blockly.ts +++ b/core/blockly.ts @@ -1,181 +1,178 @@ +/** @fileoverview The top level namespace used to access the Blockly library. */ + /** * @license * Copyright 2011 Google LLC * SPDX-License-Identifier: Apache-2.0 */ -/** - * @fileoverview The top level namespace used to access the Blockly library. - */ -'use strict'; /** * The top level namespace used to access the Blockly library. * @namespace Blockly */ -goog.module('Blockly'); - -const ContextMenu = goog.require('Blockly.ContextMenu'); -const ContextMenuItems = goog.require('Blockly.ContextMenuItems'); -const Css = goog.require('Blockly.Css'); -const Events = goog.require('Blockly.Events'); -const Extensions = goog.require('Blockly.Extensions'); -const Procedures = goog.require('Blockly.Procedures'); -const ShortcutItems = goog.require('Blockly.ShortcutItems'); -const Themes = goog.require('Blockly.Themes'); -const Tooltip = goog.require('Blockly.Tooltip'); -const Touch = goog.require('Blockly.Touch'); -const Variables = goog.require('Blockly.Variables'); -const VariablesDynamic = goog.require('Blockly.VariablesDynamic'); -const WidgetDiv = goog.require('Blockly.WidgetDiv'); -const Xml = goog.require('Blockly.Xml'); -const blockAnimations = goog.require('Blockly.blockAnimations'); -const blockRendering = goog.require('Blockly.blockRendering'); -const browserEvents = goog.require('Blockly.browserEvents'); -const bumpObjects = goog.require('Blockly.bumpObjects'); -const clipboard = goog.require('Blockly.clipboard'); -const colour = goog.require('Blockly.utils.colour'); -const common = goog.require('Blockly.common'); -const constants = goog.require('Blockly.constants'); -const deprecation = goog.require('Blockly.utils.deprecation'); -const dialog = goog.require('Blockly.dialog'); -const dropDownDiv = goog.require('Blockly.dropDownDiv'); -const fieldRegistry = goog.require('Blockly.fieldRegistry'); -const geras = goog.require('Blockly.geras'); -const internalConstants = goog.require('Blockly.internalConstants'); -const minimalist = goog.require('Blockly.minimalist'); -const registry = goog.require('Blockly.registry'); -const serializationBlocks = goog.require('Blockly.serialization.blocks'); -const serializationExceptions = goog.require('Blockly.serialization.exceptions'); -const serializationPriorities = goog.require('Blockly.serialization.priorities'); -const serializationRegistry = goog.require('Blockly.serialization.registry'); -const serializationVariables = goog.require('Blockly.serialization.variables'); -const serializationWorkspaces = goog.require('Blockly.serialization.workspaces'); -const svgMath = goog.require('Blockly.utils.svgMath'); -const thrasos = goog.require('Blockly.thrasos'); -const toolbox = goog.require('Blockly.utils.toolbox'); -const uiPosition = goog.require('Blockly.uiPosition'); -const utils = goog.require('Blockly.utils'); -const zelos = goog.require('Blockly.zelos'); -const {Align, Input} = goog.require('Blockly.Input'); -const {ASTNode} = goog.require('Blockly.ASTNode'); -const {BasicCursor} = goog.require('Blockly.BasicCursor'); -const {BlockDragSurfaceSvg} = goog.require('Blockly.BlockDragSurfaceSvg'); -const {BlockDragger} = goog.require('Blockly.BlockDragger'); -const {BlockSvg} = goog.require('Blockly.BlockSvg'); -const {BlocklyOptions} = goog.require('Blockly.BlocklyOptions'); -const {Blocks} = goog.require('Blockly.blocks'); -const {Block} = goog.require('Blockly.Block'); -const {BubbleDragger} = goog.require('Blockly.BubbleDragger'); -const {Bubble} = goog.require('Blockly.Bubble'); -const {CollapsibleToolboxCategory} = goog.require('Blockly.CollapsibleToolboxCategory'); -const {Comment} = goog.require('Blockly.Comment'); -const {ComponentManager} = goog.require('Blockly.ComponentManager'); -const {config} = goog.require('Blockly.config'); -const {ConnectionChecker} = goog.require('Blockly.ConnectionChecker'); -const {ConnectionDB} = goog.require('Blockly.ConnectionDB'); -const {ConnectionType} = goog.require('Blockly.ConnectionType'); -const {Connection} = goog.require('Blockly.Connection'); -const {ContextMenuRegistry} = goog.require('Blockly.ContextMenuRegistry'); -const {Cursor} = goog.require('Blockly.Cursor'); -const {DeleteArea} = goog.require('Blockly.DeleteArea'); -const {DragTarget} = goog.require('Blockly.DragTarget'); -const {FieldAngle} = goog.require('Blockly.FieldAngle'); -const {FieldCheckbox} = goog.require('Blockly.FieldCheckbox'); -const {FieldColour} = goog.require('Blockly.FieldColour'); -const {FieldDropdown} = goog.require('Blockly.FieldDropdown'); -const {FieldImage} = goog.require('Blockly.FieldImage'); -const {FieldLabelSerializable} = goog.require('Blockly.FieldLabelSerializable'); -const {FieldLabel} = goog.require('Blockly.FieldLabel'); -const {FieldMultilineInput} = goog.require('Blockly.FieldMultilineInput'); -const {FieldNumber} = goog.require('Blockly.FieldNumber'); -const {FieldTextInput} = goog.require('Blockly.FieldTextInput'); -const {FieldVariable} = goog.require('Blockly.FieldVariable'); -const {Field} = goog.require('Blockly.Field'); -const {FlyoutButton} = goog.require('Blockly.FlyoutButton'); -const {FlyoutMetricsManager} = goog.require('Blockly.FlyoutMetricsManager'); -const {Flyout} = goog.require('Blockly.Flyout'); -const {Generator} = goog.require('Blockly.Generator'); -const {Gesture} = goog.require('Blockly.Gesture'); -const {Grid} = goog.require('Blockly.Grid'); -const {HorizontalFlyout} = goog.require('Blockly.HorizontalFlyout'); -const {IASTNodeLocationSvg} = goog.require('Blockly.IASTNodeLocationSvg'); -const {IASTNodeLocationWithBlock} = goog.require('Blockly.IASTNodeLocationWithBlock'); -const {IASTNodeLocation} = goog.require('Blockly.IASTNodeLocation'); -const {IAutoHideable} = goog.require('Blockly.IAutoHideable'); -const {IBlockDragger} = goog.require('Blockly.IBlockDragger'); -const {IBoundedElement} = goog.require('Blockly.IBoundedElement'); -const {IBubble} = goog.require('Blockly.IBubble'); -const {ICollapsibleToolboxItem} = goog.require('Blockly.ICollapsibleToolboxItem'); -const {IComponent} = goog.require('Blockly.IComponent'); -const {IConnectionChecker} = goog.require('Blockly.IConnectionChecker'); -const {IContextMenu} = goog.require('Blockly.IContextMenu'); -const {ICopyable} = goog.require('Blockly.ICopyable'); -const {IDeletable} = goog.require('Blockly.IDeletable'); -const {IDeleteArea} = goog.require('Blockly.IDeleteArea'); -const {IDragTarget} = goog.require('Blockly.IDragTarget'); -const {IDraggable} = goog.require('Blockly.IDraggable'); -const {IFlyout} = goog.require('Blockly.IFlyout'); -const {IKeyboardAccessible} = goog.require('Blockly.IKeyboardAccessible'); -const {IMetricsManager} = goog.require('Blockly.IMetricsManager'); -const {IMovable} = goog.require('Blockly.IMovable'); -const {IPositionable} = goog.require('Blockly.IPositionable'); -const {IRegistrableField} = goog.require('Blockly.IRegistrableField'); -const {IRegistrable} = goog.require('Blockly.IRegistrable'); -const {ISelectableToolboxItem} = goog.require('Blockly.ISelectableToolboxItem'); -const {ISelectable} = goog.require('Blockly.ISelectable'); -const {ISerializer} = goog.require('Blockly.serialization.ISerializer'); -const {IStyleable} = goog.require('Blockly.IStyleable'); -const {IToolboxItem} = goog.require('Blockly.IToolboxItem'); -const {IToolbox} = goog.require('Blockly.IToolbox'); -const {Icon} = goog.require('Blockly.Icon'); -const {InsertionMarkerManager} = goog.require('Blockly.InsertionMarkerManager'); -const {Marker} = goog.require('Blockly.Marker'); -const {MarkerManager} = goog.require('Blockly.MarkerManager'); -const {MenuItem} = goog.require('Blockly.MenuItem'); -const {Menu} = goog.require('Blockly.Menu'); -const {MetricsManager} = goog.require('Blockly.MetricsManager'); -const {Mutator} = goog.require('Blockly.Mutator'); -const {Msg} = goog.require('Blockly.Msg'); -const {Names} = goog.require('Blockly.Names'); -const {Options} = goog.require('Blockly.Options'); -const {RenderedConnection} = goog.require('Blockly.RenderedConnection'); -const {ScrollbarPair} = goog.require('Blockly.ScrollbarPair'); -const {Scrollbar} = goog.require('Blockly.Scrollbar'); -const {ShortcutRegistry} = goog.require('Blockly.ShortcutRegistry'); -const {TabNavigateCursor} = goog.require('Blockly.TabNavigateCursor'); -const {ThemeManager} = goog.require('Blockly.ThemeManager'); -const {Theme} = goog.require('Blockly.Theme'); -const {ToolboxCategory} = goog.require('Blockly.ToolboxCategory'); -const {ToolboxItem} = goog.require('Blockly.ToolboxItem'); -const {ToolboxSeparator} = goog.require('Blockly.ToolboxSeparator'); -const {Toolbox} = goog.require('Blockly.Toolbox'); -const {TouchGesture} = goog.require('Blockly.TouchGesture'); -const {Trashcan} = goog.require('Blockly.Trashcan'); -const {VariableMap} = goog.require('Blockly.VariableMap'); -const {VariableModel} = goog.require('Blockly.VariableModel'); -const {VerticalFlyout} = goog.require('Blockly.VerticalFlyout'); -const {Warning} = goog.require('Blockly.Warning'); -const {WorkspaceAudio} = goog.require('Blockly.WorkspaceAudio'); -const {WorkspaceCommentSvg} = goog.require('Blockly.WorkspaceCommentSvg'); -const {WorkspaceComment} = goog.require('Blockly.WorkspaceComment'); -const {WorkspaceDragSurfaceSvg} = goog.require('Blockly.WorkspaceDragSurfaceSvg'); -const {WorkspaceDragger} = goog.require('Blockly.WorkspaceDragger'); -const {WorkspaceSvg, resizeSvgContents} = goog.require('Blockly.WorkspaceSvg'); -const {Workspace} = goog.require('Blockly.Workspace'); -const {ZoomControls} = goog.require('Blockly.ZoomControls'); -const {inject} = goog.require('Blockly.inject'); -const {inputTypes} = goog.require('Blockly.inputTypes'); -/** @suppress {extraRequire} */ -goog.require('Blockly.Events.BlockCreate'); -/** @suppress {extraRequire} */ -goog.require('Blockly.Events.FinishedLoading'); -/** @suppress {extraRequire} */ -goog.require('Blockly.Events.Ui'); -/** @suppress {extraRequire} */ -goog.require('Blockly.Events.UiBase'); -/** @suppress {extraRequire} */ -goog.require('Blockly.Events.VarCreate'); +// Unused import preserved for side-effects. Remove if unneeded. +import './events/events_block_create'; +// Unused import preserved for side-effects. Remove if unneeded. +import './events/workspace_events'; +// Unused import preserved for side-effects. Remove if unneeded. +import './events/events_ui'; +// Unused import preserved for side-effects. Remove if unneeded. +import './events/events_ui_base'; +// Unused import preserved for side-effects. Remove if unneeded. +import './events/events_var_create'; + +import { Block } from './block.js'; +import * as blockAnimations from './block_animations.js'; +import { BlockDragSurfaceSvg } from './block_drag_surface.js'; +import { BlockDragger } from './block_dragger.js'; +import { BlockSvg } from './block_svg.js'; +import { BlocklyOptions } from './blockly_options.js'; +import { Blocks } from './blocks.js'; +import * as browserEvents from './browser_events.js'; +import { Bubble } from './bubble.js'; +import { BubbleDragger } from './bubble_dragger.js'; +import * as bumpObjects from './bump_objects.js'; +import * as clipboard from './clipboard.js'; +import { Comment } from './comment.js'; +import * as common from './common.js'; +import { ComponentManager } from './component_manager.js'; +import { config } from './config.js'; +import { Connection } from './connection.js'; +import { ConnectionChecker } from './connection_checker.js'; +import { ConnectionDB } from './connection_db.js'; +import { ConnectionType } from './connection_type.js'; +import * as ContextMenu from './contextmenu.js'; +import * as ContextMenuItems from './contextmenu_items.js'; +import { ContextMenuRegistry } from './contextmenu_registry.js'; +import * as Css from './css.js'; +import { DeleteArea } from './delete_area.js'; +import * as dialog from './dialog.js'; +import { DragTarget } from './drag_target.js'; +import * as dropDownDiv from './dropdowndiv.js'; +import * as Events from './events/events.js'; +import * as Extensions from './extensions.js'; +import { Field } from './field.js'; +import { FieldAngle } from './field_angle.js'; +import { FieldCheckbox } from './field_checkbox.js'; +import { FieldColour } from './field_colour.js'; +import { FieldDropdown } from './field_dropdown.js'; +import { FieldImage } from './field_image.js'; +import { FieldLabel } from './field_label.js'; +import { FieldLabelSerializable } from './field_label_serializable.js'; +import { FieldMultilineInput } from './field_multilineinput.js'; +import { FieldNumber } from './field_number.js'; +import * as fieldRegistry from './field_registry.js'; +import { FieldTextInput } from './field_textinput.js'; +import { FieldVariable } from './field_variable.js'; +import { Flyout } from './flyout_base.js'; +import { FlyoutButton } from './flyout_button.js'; +import { HorizontalFlyout } from './flyout_horizontal.js'; +import { FlyoutMetricsManager } from './flyout_metrics_manager.js'; +import { VerticalFlyout } from './flyout_vertical.js'; +import { Generator } from './generator.js'; +import { Gesture } from './gesture.js'; +import { Grid } from './grid.js'; +import { Icon } from './icon.js'; +import { inject } from './inject.js'; +import { Align, Input } from './input.js'; +import { inputTypes } from './input_types.js'; +import { InsertionMarkerManager } from './insertion_marker_manager.js'; +import { IASTNodeLocation } from './interfaces/i_ast_node_location.js'; +import { IASTNodeLocationSvg } from './interfaces/i_ast_node_location_svg.js'; +import { IASTNodeLocationWithBlock } from './interfaces/i_ast_node_location_with_block.js'; +import { IAutoHideable } from './interfaces/i_autohideable.js'; +import { IBlockDragger } from './interfaces/i_block_dragger.js'; +import { IBoundedElement } from './interfaces/i_bounded_element.js'; +import { IBubble } from './interfaces/i_bubble.js'; +import { ICollapsibleToolboxItem } from './interfaces/i_collapsible_toolbox_item.js'; +import { IComponent } from './interfaces/i_component.js'; +import { IConnectionChecker } from './interfaces/i_connection_checker.js'; +import { IContextMenu } from './interfaces/i_contextmenu.js'; +import { ICopyable } from './interfaces/i_copyable.js'; +import { IDeletable } from './interfaces/i_deletable.js'; +import { IDeleteArea } from './interfaces/i_delete_area.js'; +import { IDragTarget } from './interfaces/i_drag_target.js'; +import { IDraggable } from './interfaces/i_draggable.js'; +import { IFlyout } from './interfaces/i_flyout.js'; +import { IKeyboardAccessible } from './interfaces/i_keyboard_accessible.js'; +import { IMetricsManager } from './interfaces/i_metrics_manager.js'; +import { IMovable } from './interfaces/i_movable.js'; +import { IPositionable } from './interfaces/i_positionable.js'; +import { IRegistrable } from './interfaces/i_registrable.js'; +import { IRegistrableField } from './interfaces/i_registrable_field.js'; +import { ISelectable } from './interfaces/i_selectable.js'; +import { ISelectableToolboxItem } from './interfaces/i_selectable_toolbox_item.js'; +import { ISerializer as SerializerInterface } from './interfaces/i_serializer.js'; +import { IStyleable } from './interfaces/i_styleable.js'; +import { IToolbox } from './interfaces/i_toolbox.js'; +import { IToolboxItem } from './interfaces/i_toolbox_item.js'; +import * as internalConstants from './internal_constants.js'; +import { ASTNode } from './keyboard_nav/ast_node.js'; +import { BasicCursor } from './keyboard_nav/basic_cursor.js'; +import { Cursor } from './keyboard_nav/cursor.js'; +import { Marker } from './keyboard_nav/marker.js'; +import { TabNavigateCursor } from './keyboard_nav/tab_navigate_cursor.js'; +import { MarkerManager } from './marker_manager.js'; +import { Menu } from './menu.js'; +import { MenuItem } from './menuitem.js'; +import { MetricsManager } from './metrics_manager.js'; +import { Msg } from './msg.js'; +import { Mutator } from './mutator.js'; +import { Names } from './names.js'; +import { Options } from './options.js'; +import * as uiPosition from './positionable_helpers.js'; +import * as Procedures from './procedures.js'; +import * as registry from './registry.js'; +import { RenderedConnection } from './rendered_connection.js'; +import * as blockRendering from './renderers/common/block_rendering.js'; +import * as constants from './renderers/common/constants.js'; +import * as geras from './renderers/geras/geras.js'; +import * as minimalist from './renderers/minimalist/minimalist.js'; +import * as thrasos from './renderers/thrasos/thrasos.js'; +import * as zelos from './renderers/zelos/zelos.js'; +import { Scrollbar } from './scrollbar.js'; +import { ScrollbarPair } from './scrollbar_pair.js'; +import * as serializationBlocks from './serialization/blocks.js'; +import * as serializationExceptions from './serialization/exceptions.js'; +import * as serializationPriorities from './serialization/priorities.js'; +import * as serializationRegistry from './serialization/registry.js'; +import * as serializationVariables from './serialization/variables.js'; +import * as serializationWorkspaces from './serialization/workspaces.js'; +import * as ShortcutItems from './shortcut_items.js'; +import { ShortcutRegistry } from './shortcut_registry.js'; +import { Theme } from './theme.js'; +import * as Themes from './theme/themes.js'; +import { ThemeManager } from './theme_manager.js'; +import { ToolboxCategory } from './toolbox/category.js'; +import { CollapsibleToolboxCategory } from './toolbox/collapsible_category.js'; +import { ToolboxSeparator } from './toolbox/separator.js'; +import { Toolbox } from './toolbox/toolbox.js'; +import { ToolboxItem } from './toolbox/toolbox_item.js'; +import * as Tooltip from './tooltip.js'; +import * as Touch from './touch.js'; +import { TouchGesture } from './touch_gesture.js'; +import { Trashcan } from './trashcan.js'; +import * as utils from './utils.js'; +import * as colour from './utils/colour.js'; +import * as deprecation from './utils/deprecation.js'; +import * as svgMath from './utils/svg_math.js'; +import * as toolbox from './utils/toolbox.js'; +import { VariableMap } from './variable_map.js'; +import { VariableModel } from './variable_model.js'; +import * as Variables from './variables.js'; +import * as VariablesDynamic from './variables_dynamic.js'; +import { Warning } from './warning.js'; +import * as WidgetDiv from './widgetdiv.js'; +import { Workspace } from './workspace.js'; +import { WorkspaceAudio } from './workspace_audio.js'; +import { WorkspaceComment } from './workspace_comment.js'; +import { WorkspaceCommentSvg } from './workspace_comment_svg.js'; +import { WorkspaceDragSurfaceSvg } from './workspace_drag_surface_svg.js'; +import { WorkspaceDragger } from './workspace_dragger.js'; +import { resizeSvgContents as realResizeSvgContents, WorkspaceSvg } from './workspace_svg.js'; +import * as Xml from './xml.js'; +import { ZoomControls } from './zoom_controls.js'; /** @@ -188,7 +185,7 @@ goog.require('Blockly.Events.VarCreate'); * @define {string} * @alias Blockly.VERSION */ -exports.VERSION = 'uncompiled'; +export const VERSION = 'uncompiled'; /* * Top-level functions and properties on the Blockly namespace. @@ -206,20 +203,19 @@ exports.VERSION = 'uncompiled'; * @see Blockly.Input.Align.LEFT * @alias Blockly.ALIGN_LEFT */ -exports.ALIGN_LEFT = Align.LEFT; +export const ALIGN_LEFT = Align.LEFT; /** * @see Blockly.Input.Align.CENTRE * @alias Blockly.ALIGN_CENTRE */ -exports.ALIGN_CENTRE = Align.CENTRE; +export const ALIGN_CENTRE = Align.CENTRE; /** * @see Blockly.Input.Align.RIGHT * @alias Blockly.ALIGN_RIGHT */ -exports.ALIGN_RIGHT = Align.RIGHT; - +export const ALIGN_RIGHT = Align.RIGHT; /* * Aliases for constants used for connection and input types. */ @@ -228,59 +224,57 @@ exports.ALIGN_RIGHT = Align.RIGHT; * @see ConnectionType.INPUT_VALUE * @alias Blockly.INPUT_VALUE */ -exports.INPUT_VALUE = ConnectionType.INPUT_VALUE; +export const INPUT_VALUE = ConnectionType.INPUT_VALUE; /** * @see ConnectionType.OUTPUT_VALUE * @alias Blockly.OUTPUT_VALUE */ -exports.OUTPUT_VALUE = ConnectionType.OUTPUT_VALUE; +export const OUTPUT_VALUE = ConnectionType.OUTPUT_VALUE; /** * @see ConnectionType.NEXT_STATEMENT * @alias Blockly.NEXT_STATEMENT */ -exports.NEXT_STATEMENT = ConnectionType.NEXT_STATEMENT; +export const NEXT_STATEMENT = ConnectionType.NEXT_STATEMENT; /** * @see ConnectionType.PREVIOUS_STATEMENT * @alias Blockly.PREVIOUS_STATEMENT */ -exports.PREVIOUS_STATEMENT = ConnectionType.PREVIOUS_STATEMENT; +export const PREVIOUS_STATEMENT = ConnectionType.PREVIOUS_STATEMENT; /** * @see inputTypes.DUMMY_INPUT * @alias Blockly.DUMMY_INPUT */ -exports.DUMMY_INPUT = inputTypes.DUMMY; +export const DUMMY_INPUT = inputTypes.DUMMY; -/** - * Aliases for toolbox positions. - */ +/** Aliases for toolbox positions. */ /** * @see toolbox.Position.TOP * @alias Blockly.TOOLBOX_AT_TOP */ -exports.TOOLBOX_AT_TOP = toolbox.Position.TOP; +export const TOOLBOX_AT_TOP = toolbox.Position.TOP; /** * @see toolbox.Position.BOTTOM * @alias Blockly.TOOLBOX_AT_BOTTOM */ -exports.TOOLBOX_AT_BOTTOM = toolbox.Position.BOTTOM; +export const TOOLBOX_AT_BOTTOM = toolbox.Position.BOTTOM; /** * @see toolbox.Position.LEFT * @alias Blockly.TOOLBOX_AT_LEFT */ -exports.TOOLBOX_AT_LEFT = toolbox.Position.LEFT; +export const TOOLBOX_AT_LEFT = toolbox.Position.LEFT; /** * @see toolbox.Position.RIGHT * @alias Blockly.TOOLBOX_AT_RIGHT */ -exports.TOOLBOX_AT_RIGHT = toolbox.Position.RIGHT; +export const TOOLBOX_AT_RIGHT = toolbox.Position.RIGHT; /* * Other aliased functions. @@ -292,422 +286,450 @@ exports.TOOLBOX_AT_RIGHT = toolbox.Position.RIGHT; * See workspace.resizeContents to resize the workspace when the contents * change (e.g. when a block is added or removed). * Record the height/width of the SVG image. - * @param {!WorkspaceSvg} workspace Any workspace in the SVG. + * @param workspace Any workspace in the SVG. * @see Blockly.common.svgResize * @alias Blockly.svgResize */ -exports.svgResize = common.svgResize; +// AnyDuringMigration because: Property 'svgResize' does not exist on type +// 'void'. +export const svgResize = (common as AnyDuringMigration).svgResize; /** * Close tooltips, context menus, dropdown selections, etc. - * @param {boolean=} opt_onlyClosePopups Whether only popups should be closed. + * @param opt_onlyClosePopups Whether only popups should be closed. * @see Blockly.WorkspaceSvg.hideChaff * @alias Blockly.hideChaff */ -const hideChaff = function(opt_onlyClosePopups) { - /** @type {!WorkspaceSvg} */ (common.getMainWorkspace()) - .hideChaff(opt_onlyClosePopups); -}; -exports.hideChaff = hideChaff; +export function hideChaff(opt_onlyClosePopups?: boolean) { + // AnyDuringMigration because: Property 'getMainWorkspace' does not exist on + // type 'void'. + ((common as AnyDuringMigration).getMainWorkspace() as WorkspaceSvg) + .hideChaff(opt_onlyClosePopups); +} /** * Returns the main workspace. Returns the last used main workspace (based on * focus). Try not to use this function, particularly if there are multiple * Blockly instances on a page. - * @return {!Workspace} The main workspace. + * @return The main workspace. * @see Blockly.common.getMainWorkspace * @alias Blockly.getMainWorkspace */ -exports.getMainWorkspace = common.getMainWorkspace; +// AnyDuringMigration because: Property 'getMainWorkspace' does not exist on +// type 'void'. +export const getMainWorkspace = (common as AnyDuringMigration).getMainWorkspace; /** * Define blocks from an array of JSON block definitions, as might be generated * by the Blockly Developer Tools. - * @param {!Array} jsonArray An array of JSON block definitions. + * @param jsonArray An array of JSON block definitions. * @see Blockly.common.defineBlocksWithJsonArray * @alias Blockly.defineBlocksWithJsonArray */ -exports.defineBlocksWithJsonArray = common.defineBlocksWithJsonArray; - +// AnyDuringMigration because: Property 'defineBlocksWithJsonArray' does not +// exist on type 'void'. +export const defineBlocksWithJsonArray = + (common as AnyDuringMigration).defineBlocksWithJsonArray; /** * Set the parent container. This is the container element that the WidgetDiv, * dropDownDiv, and Tooltip are rendered into the first time `Blockly.inject` * is called. * This method is a NOP if called after the first ``Blockly.inject``. - * @param {!Element} container The container element. + * @param container The container element. * @see Blockly.common.setParentContainer * @alias Blockly.setParentContainer */ -exports.setParentContainer = common.setParentContainer; +// AnyDuringMigration because: Property 'setParentContainer' does not exist on +// type 'void'. +export const setParentContainer = + (common as AnyDuringMigration).setParentContainer; -/* - * Aliased functions and properties that used to be on the Blockly namespace. - * Everything in this section is deprecated. Both external and internal code - * should avoid using these functions and use the designated replacements. - * Anything in this section may be removed in a future version of Blockly. - */ /** * Returns the dimensions of the specified SVG image. - * @param {!SVGElement} svg SVG image. - * @return {!Size} Contains width and height properties. + * @param svg SVG image. + * @return Contains width and height properties. * @deprecated Use workspace.setCachedParentSvgSize. (2021 March 5) * @see Blockly.WorkspaceSvg.setCachedParentSvgSize * @alias Blockly.svgSize */ -exports.svgSize = svgMath.svgSize; +export const svgSize = svgMath.svgSize; /** * Size the workspace when the contents change. This also updates * scrollbars accordingly. - * @param {!WorkspaceSvg} workspace The workspace to resize. + * @param workspace The workspace to resize. * @deprecated Use workspace.resizeContents. (2021 December) * @see Blockly.WorkspaceSvg.resizeContents * @alias Blockly.resizeSvgContents */ -const resizeSvgContentsLocal = function(workspace) { +function resizeSvgContentsLocal(workspace: WorkspaceSvg) { deprecation.warn( - 'Blockly.resizeSvgContents', 'December 2021', 'December 2022', - 'Blockly.WorkspaceSvg.resizeSvgContents'); - resizeSvgContents(workspace); -}; -exports.resizeSvgContents = resizeSvgContentsLocal; + 'Blockly.resizeSvgContents', 'December 2021', 'December 2022', + 'Blockly.WorkspaceSvg.resizeSvgContents'); + realResizeSvgContents(workspace); +} +export const resizeSvgContents = resizeSvgContentsLocal; /** * Copy a block or workspace comment onto the local clipboard. - * @param {!ICopyable} toCopy Block or Workspace Comment to be copied. + * @param toCopy Block or Workspace Comment to be copied. * @deprecated Use Blockly.clipboard.copy(). (2021 December) * @see Blockly.clipboard.copy * @alias Blockly.copy */ -const copy = function(toCopy) { +export function copy(toCopy: ICopyable) { deprecation.warn( - 'Blockly.copy', 'December 2021', 'December 2022', - 'Blockly.clipboard.copy'); - clipboard.copy(toCopy); -}; -exports.copy = copy; + 'Blockly.copy', 'December 2021', 'December 2022', + 'Blockly.clipboard.copy'); + // AnyDuringMigration because: Property 'copy' does not exist on type 'void'. + (clipboard as AnyDuringMigration).copy(toCopy); +} /** * Paste a block or workspace comment on to the main workspace. - * @return {boolean} True if the paste was successful, false otherwise. + * @return True if the paste was successful, false otherwise. * @deprecated Use Blockly.clipboard.paste(). (2021 December) * @see Blockly.clipboard.paste * @alias Blockly.paste */ -const paste = function() { +export function paste(): boolean { deprecation.warn( - 'Blockly.paste', 'December 2021', 'December 2022', - 'Blockly.clipboard.paste'); - return !!clipboard.paste(); -}; -exports.paste = paste; + 'Blockly.paste', 'December 2021', 'December 2022', + 'Blockly.clipboard.paste'); + // AnyDuringMigration because: Property 'paste' does not exist on type + // 'void'. + return !!(clipboard as AnyDuringMigration).paste(); +} /** * Duplicate this block and its children, or a workspace comment. - * @param {!ICopyable} toDuplicate Block or Workspace Comment to be - * copied. + * @param toDuplicate Block or Workspace Comment to be copied. * @deprecated Use Blockly.clipboard.duplicate(). (2021 December) * @see Blockly.clipboard.duplicate * @alias Blockly.duplicate */ -const duplicate = function(toDuplicate) { +export function duplicate(toDuplicate: ICopyable) { deprecation.warn( - 'Blockly.duplicate', 'December 2021', 'December 2022', - 'Blockly.clipboard.duplicate'); - clipboard.duplicate(toDuplicate); -}; -exports.duplicate = duplicate; + 'Blockly.duplicate', 'December 2021', 'December 2022', + 'Blockly.clipboard.duplicate'); + // AnyDuringMigration because: Property 'duplicate' does not exist on type + // 'void'. + (clipboard as AnyDuringMigration).duplicate(toDuplicate); +} /** * Is the given string a number (includes negative and decimals). - * @param {string} str Input string. - * @return {boolean} True if number, false otherwise. + * @param str Input string. + * @return True if number, false otherwise. * @deprecated Use Blockly.utils.string.isNumber(str). (2021 December) * @see Blockly.utils.string.isNumber * @alias Blockly.isNumber */ -const isNumber = function(str) { +export function isNumber(str: string): boolean { deprecation.warn( - 'Blockly.isNumber', 'December 2021', 'December 2022', - 'Blockly.utils.string.isNumber'); - return utils.string.isNumber(str); -}; -exports.isNumber = isNumber; + 'Blockly.isNumber', 'December 2021', 'December 2022', + 'Blockly.utils.string.isNumber'); + // AnyDuringMigration because: Property 'string' does not exist on type + // 'void'. + return (utils as AnyDuringMigration).string.isNumber(str); +} /** * Convert a hue (HSV model) into an RGB hex triplet. - * @param {number} hue Hue on a colour wheel (0-360). - * @return {string} RGB code, e.g. '#5ba65b'. + * @param hue Hue on a colour wheel (0-360). + * @return RGB code, e.g. '#5ba65b'. * @deprecated Use Blockly.utils.colour.hueToHex(). (2021 December) * @see Blockly.utils.colour.hueToHex * @alias Blockly.hueToHex */ -const hueToHex = function(hue) { +export function hueToHex(hue: number): string { deprecation.warn( - 'Blockly.hueToHex', 'December 2021', 'December 2022', - 'Blockly.utils.colour.hueToHex'); + 'Blockly.hueToHex', 'December 2021', 'December 2022', + 'Blockly.utils.colour.hueToHex'); return colour.hueToHex(hue); -}; -exports.hueToHex = hueToHex; +} /** * Bind an event handler that should be called regardless of whether it is part * of the active touch stream. * Use this for events that are not part of a multi-part gesture (e.g. * mouseover for tooltips). - * @param {!EventTarget} node Node upon which to listen. - * @param {string} name Event name to listen to (e.g. 'mousedown'). - * @param {?Object} thisObject The value of 'this' in the function. - * @param {!Function} func Function to call when event is triggered. - * @return {!browserEvents.Data} Opaque data that can be passed to - * unbindEvent_. + * @param node Node upon which to listen. + * @param name Event name to listen to (e.g. 'mousedown'). + * @param thisObject The value of 'this' in the function. + * @param func Function to call when event is triggered. + * @return Opaque data that can be passed to unbindEvent_. * @deprecated Use Blockly.browserEvents.bind(). (December 2021) * @see Blockly.browserEvents.bind * @alias Blockly.bindEvent_ */ -const bindEvent_ = function(node, name, thisObject, func) { +export function bindEvent_( + node: EventTarget, name: string, thisObject: AnyDuringMigration | null, + func: Function): browserEvents.Data { deprecation.warn( - 'Blockly.bindEvent_', 'December 2021', 'December 2022', - 'Blockly.browserEvents.bind'); + 'Blockly.bindEvent_', 'December 2021', 'December 2022', + 'Blockly.browserEvents.bind'); return browserEvents.bind(node, name, thisObject, func); -}; -exports.bindEvent_ = bindEvent_; +} /** * Unbind one or more events event from a function call. - * @param {!browserEvents.Data} bindData Opaque data from bindEvent_. + * @param bindData Opaque data from bindEvent_. * This list is emptied during the course of calling this function. - * @return {!Function} The function call. + * @return The function call. * @deprecated Use Blockly.browserEvents.unbind(). (December 2021) * @see browserEvents.unbind * @alias Blockly.unbindEvent_ */ -const unbindEvent_ = function(bindData) { +export function unbindEvent_(bindData: browserEvents.Data): Function { deprecation.warn( - 'Blockly.unbindEvent_', 'December 2021', 'December 2022', - 'Blockly.browserEvents.unbind'); + 'Blockly.unbindEvent_', 'December 2021', 'December 2022', + 'Blockly.browserEvents.unbind'); return browserEvents.unbind(bindData); -}; -exports.unbindEvent_ = unbindEvent_; +} /** * Bind an event handler that can be ignored if it is not part of the active * touch stream. * Use this for events that either start or continue a multi-part gesture (e.g. * mousedown or mousemove, which may be part of a drag or click). - * @param {!EventTarget} node Node upon which to listen. - * @param {string} name Event name to listen to (e.g. 'mousedown'). - * @param {?Object} thisObject The value of 'this' in the function. - * @param {!Function} func Function to call when event is triggered. - * @param {boolean=} opt_noCaptureIdentifier True if triggering on this event - * should not block execution of other event handlers on this touch or - * other simultaneous touches. False by default. - * @param {boolean=} opt_noPreventDefault True if triggering on this event - * should prevent the default handler. False by default. If - * opt_noPreventDefault is provided, opt_noCaptureIdentifier must also be - * provided. - * @return {!browserEvents.Data} Opaque data that can be passed to - * unbindEvent_. + * @param node Node upon which to listen. + * @param name Event name to listen to (e.g. 'mousedown'). + * @param thisObject The value of 'this' in the function. + * @param func Function to call when event is triggered. + * @param opt_noCaptureIdentifier True if triggering on this event should not + * block execution of other event handlers on this touch or other + * simultaneous touches. False by default. + * @param opt_noPreventDefault True if triggering on this event should prevent + * the default handler. False by default. If opt_noPreventDefault is + * provided, opt_noCaptureIdentifier must also be provided. + * @return Opaque data that can be passed to unbindEvent_. * @deprecated Use Blockly.browserEvents.conditionalBind(). (December 2021) * @see browserEvents.conditionalBind * @alias Blockly.bindEventWithChecks_ */ -const bindEventWithChecks_ = function( - node, name, thisObject, func, opt_noCaptureIdentifier, - opt_noPreventDefault) { +export function bindEventWithChecks_( + node: EventTarget, name: string, thisObject: AnyDuringMigration | null, + func: Function, opt_noCaptureIdentifier?: boolean, + opt_noPreventDefault?: boolean): browserEvents.Data { deprecation.warn( - 'Blockly.bindEventWithChecks_', 'December 2021', 'December 2022', - 'Blockly.browserEvents.conditionalBind'); + 'Blockly.bindEventWithChecks_', 'December 2021', 'December 2022', + 'Blockly.browserEvents.conditionalBind'); return browserEvents.conditionalBind( - node, name, thisObject, func, opt_noCaptureIdentifier, - opt_noPreventDefault); -}; -exports.bindEventWithChecks_ = bindEventWithChecks_; + node, name, thisObject, func, opt_noCaptureIdentifier, + opt_noPreventDefault); +} // Aliases to allow external code to access these values for legacy reasons. -exports.COLLAPSE_CHARS = internalConstants.COLLAPSE_CHARS; -exports.DRAG_STACK = internalConstants.DRAG_STACK; -exports.OPPOSITE_TYPE = internalConstants.OPPOSITE_TYPE; -exports.RENAME_VARIABLE_ID = internalConstants.RENAME_VARIABLE_ID; -exports.DELETE_VARIABLE_ID = internalConstants.DELETE_VARIABLE_ID; -exports.COLLAPSED_INPUT_NAME = constants.COLLAPSED_INPUT_NAME; -exports.COLLAPSED_FIELD_NAME = constants.COLLAPSED_FIELD_NAME; +export const COLLAPSE_CHARS = internalConstants.COLLAPSE_CHARS; +export const DRAG_STACK = internalConstants.DRAG_STACK; +export const OPPOSITE_TYPE = internalConstants.OPPOSITE_TYPE; +export const RENAME_VARIABLE_ID = internalConstants.RENAME_VARIABLE_ID; +export const DELETE_VARIABLE_ID = internalConstants.DELETE_VARIABLE_ID; +// AnyDuringMigration because: Property 'COLLAPSED_INPUT_NAME' does not exist +// on type 'void'. +export const COLLAPSED_INPUT_NAME = + (constants as AnyDuringMigration).COLLAPSED_INPUT_NAME; +// AnyDuringMigration because: Property 'COLLAPSED_FIELD_NAME' does not exist +// on type 'void'. +export const COLLAPSED_FIELD_NAME = + (constants as AnyDuringMigration).COLLAPSED_FIELD_NAME; /** * String for use in the "custom" attribute of a category in toolbox XML. * This string indicates that the category should be dynamically populated with * variable blocks. - * @const {string} * @alias Blockly.VARIABLE_CATEGORY_NAME */ -exports.VARIABLE_CATEGORY_NAME = Variables.CATEGORY_NAME; +// AnyDuringMigration because: Property 'CATEGORY_NAME' does not exist on type +// 'void'. +export const VARIABLE_CATEGORY_NAME: string = + (Variables as AnyDuringMigration).CATEGORY_NAME; /** * String for use in the "custom" attribute of a category in toolbox XML. * This string indicates that the category should be dynamically populated with * variable blocks. - * @const {string} * @alias Blockly.VARIABLE_DYNAMIC_CATEGORY_NAME */ -exports.VARIABLE_DYNAMIC_CATEGORY_NAME = VariablesDynamic.CATEGORY_NAME; +// AnyDuringMigration because: Property 'CATEGORY_NAME' does not exist on type +// 'void'. +export const VARIABLE_DYNAMIC_CATEGORY_NAME: string = + (VariablesDynamic as AnyDuringMigration).CATEGORY_NAME; /** * String for use in the "custom" attribute of a category in toolbox XML. * This string indicates that the category should be dynamically populated with * procedure blocks. - * @const {string} * @alias Blockly.PROCEDURE_CATEGORY_NAME */ -exports.PROCEDURE_CATEGORY_NAME = Procedures.CATEGORY_NAME; +// AnyDuringMigration because: Property 'CATEGORY_NAME' does not exist on type +// 'void'. +export const PROCEDURE_CATEGORY_NAME: string = + (Procedures as AnyDuringMigration).CATEGORY_NAME; // Re-export submodules that no longer declareLegacyNamespace. -exports.ASTNode = ASTNode; -exports.BasicCursor = BasicCursor; -exports.Block = Block; -exports.BlocklyOptions = BlocklyOptions; -exports.BlockDragger = BlockDragger; -exports.BlockDragSurfaceSvg = BlockDragSurfaceSvg; -exports.BlockSvg = BlockSvg; -exports.Blocks = Blocks; -exports.Bubble = Bubble; -exports.BubbleDragger = BubbleDragger; -exports.CollapsibleToolboxCategory = CollapsibleToolboxCategory; -exports.Comment = Comment; -exports.ComponentManager = ComponentManager; -exports.Connection = Connection; -exports.ConnectionType = ConnectionType; -exports.ConnectionChecker = ConnectionChecker; -exports.ConnectionDB = ConnectionDB; -exports.ContextMenu = ContextMenu; -exports.ContextMenuItems = ContextMenuItems; -exports.ContextMenuRegistry = ContextMenuRegistry; -exports.Css = Css; -exports.Cursor = Cursor; -exports.DeleteArea = DeleteArea; -exports.DragTarget = DragTarget; -exports.DropDownDiv = dropDownDiv; -exports.Events = Events; -exports.Extensions = Extensions; -exports.Field = Field; -exports.FieldAngle = FieldAngle; -exports.FieldCheckbox = FieldCheckbox; -exports.FieldColour = FieldColour; -exports.FieldDropdown = FieldDropdown; -exports.FieldImage = FieldImage; -exports.FieldLabel = FieldLabel; -exports.FieldLabelSerializable = FieldLabelSerializable; -exports.FieldMultilineInput = FieldMultilineInput; -exports.FieldNumber = FieldNumber; -exports.FieldTextInput = FieldTextInput; -exports.FieldVariable = FieldVariable; -exports.Flyout = Flyout; -exports.FlyoutButton = FlyoutButton; -exports.FlyoutMetricsManager = FlyoutMetricsManager; -exports.Generator = Generator; -exports.Gesture = Gesture; -exports.Grid = Grid; -exports.HorizontalFlyout = HorizontalFlyout; -exports.IASTNodeLocation = IASTNodeLocation; -exports.IASTNodeLocationSvg = IASTNodeLocationSvg; -exports.IASTNodeLocationWithBlock = IASTNodeLocationWithBlock; -exports.IAutoHideable = IAutoHideable; -exports.IBlockDragger = IBlockDragger; -exports.IBoundedElement = IBoundedElement; -exports.IBubble = IBubble; -exports.ICollapsibleToolboxItem = ICollapsibleToolboxItem; -exports.IComponent = IComponent; -exports.IConnectionChecker = IConnectionChecker; -exports.IContextMenu = IContextMenu; -exports.Icon = Icon; -exports.ICopyable = ICopyable; -exports.IDeletable = IDeletable; -exports.IDeleteArea = IDeleteArea; -exports.IDragTarget = IDragTarget; -exports.IDraggable = IDraggable; -exports.IFlyout = IFlyout; -exports.IKeyboardAccessible = IKeyboardAccessible; -exports.IMetricsManager = IMetricsManager; -exports.IMovable = IMovable; -exports.Input = Input; -exports.InsertionMarkerManager = InsertionMarkerManager; -exports.IPositionable = IPositionable; -exports.IRegistrable = IRegistrable; -exports.IRegistrableField = IRegistrableField; -exports.ISelectable = ISelectable; -exports.ISelectableToolboxItem = ISelectableToolboxItem; -exports.IStyleable = IStyleable; -exports.IToolbox = IToolbox; -exports.IToolboxItem = IToolboxItem; -exports.Marker = Marker; -exports.MarkerManager = MarkerManager; -exports.Menu = Menu; -exports.MenuItem = MenuItem; -exports.MetricsManager = MetricsManager; -exports.Mutator = Mutator; -exports.Msg = Msg; -exports.Names = Names; -exports.Options = Options; -exports.Procedures = Procedures; -exports.RenderedConnection = RenderedConnection; -exports.Scrollbar = Scrollbar; -exports.ScrollbarPair = ScrollbarPair; -exports.ShortcutItems = ShortcutItems; -exports.ShortcutRegistry = ShortcutRegistry; -exports.TabNavigateCursor = TabNavigateCursor; -exports.Theme = Theme; -exports.Themes = Themes; -exports.ThemeManager = ThemeManager; -exports.Toolbox = Toolbox; -exports.ToolboxCategory = ToolboxCategory; -exports.ToolboxItem = ToolboxItem; -exports.ToolboxSeparator = ToolboxSeparator; -exports.Tooltip = Tooltip; -exports.Touch = Touch; -exports.TouchGesture = TouchGesture; -exports.Trashcan = Trashcan; -exports.VariableMap = VariableMap; -exports.VariableModel = VariableModel; -exports.Variables = Variables; -exports.VariablesDynamic = VariablesDynamic; -exports.VerticalFlyout = VerticalFlyout; -exports.Warning = Warning; -exports.WidgetDiv = WidgetDiv; -exports.Workspace = Workspace; -exports.WorkspaceAudio = WorkspaceAudio; -exports.WorkspaceComment = WorkspaceComment; -exports.WorkspaceCommentSvg = WorkspaceCommentSvg; -exports.WorkspaceDragSurfaceSvg = WorkspaceDragSurfaceSvg; -exports.WorkspaceDragger = WorkspaceDragger; -exports.WorkspaceSvg = WorkspaceSvg; -exports.Xml = Xml; -exports.ZoomControls = ZoomControls; -exports.blockAnimations = blockAnimations; -exports.blockRendering = blockRendering; -exports.browserEvents = browserEvents; -exports.bumpObjects = bumpObjects; -exports.clipboard = clipboard; -exports.common = common; -exports.config = config; +export { browserEvents }; +export { ContextMenu }; +export { ContextMenuItems }; +export { Css }; +export { Events }; +export { Extensions }; +export { Procedures }; +export { ShortcutItems }; +export { Themes }; +export { Tooltip }; +export { Touch }; +export { Variables }; +export { VariablesDynamic }; +export { WidgetDiv }; +export { Xml }; +export { blockAnimations }; +export { blockRendering }; +export { bumpObjects }; +export { clipboard }; +export { common }; +export { constants }; +export { dialog }; +export { fieldRegistry }; +export { geras }; +export { minimalist }; +export { registry }; +export { thrasos }; +export { uiPosition }; +export { utils }; +export { zelos }; +export { ASTNode }; +export { BasicCursor }; +export { Block }; +export { BlocklyOptions }; +export { BlockDragger }; +export { BlockDragSurfaceSvg }; +export { BlockSvg }; +export { Blocks }; +export { Bubble }; +export { BubbleDragger }; +export { CollapsibleToolboxCategory }; +export { Comment }; +export { ComponentManager }; +export { Connection }; +export { ConnectionType }; +export { ConnectionChecker }; +export { ConnectionDB }; +export { ContextMenuRegistry }; +export { Cursor }; +export { DeleteArea }; +export { DragTarget }; +export const DropDownDiv = dropDownDiv; +export { Field }; +export { FieldAngle }; +export { FieldCheckbox }; +export { FieldColour }; +export { FieldDropdown }; +export { FieldImage }; +export { FieldLabel }; +export { FieldLabelSerializable }; +export { FieldMultilineInput }; +export { FieldNumber }; +export { FieldTextInput }; +export { FieldVariable }; +export { Flyout }; +export { FlyoutButton }; +export { FlyoutMetricsManager }; +export { Generator }; +export { Gesture }; +export { Grid }; +export { HorizontalFlyout }; +export { IASTNodeLocation }; +export { IASTNodeLocationSvg }; +export { IASTNodeLocationWithBlock }; +export { IAutoHideable }; +export { IBlockDragger }; +export { IBoundedElement }; +export { IBubble }; +export { ICollapsibleToolboxItem }; +export { IComponent }; +export { IConnectionChecker }; +export { IContextMenu }; +export { Icon }; +export { ICopyable }; +export { IDeletable }; +export { IDeleteArea }; +export { IDragTarget }; +export { IDraggable }; +export { IFlyout }; +export { IKeyboardAccessible }; +export { IMetricsManager }; +export { IMovable }; +export { Input }; +export { InsertionMarkerManager }; +export { IPositionable }; +export { IRegistrable }; +export { IRegistrableField }; +export { ISelectable }; +export { ISelectableToolboxItem }; +export { IStyleable }; +export { IToolbox }; +export { IToolboxItem }; +export { Marker }; +export { MarkerManager }; +export { Menu }; +export { MenuItem }; +export { MetricsManager }; +export { Mutator }; +export { Msg }; +export { Names }; +export { Options }; +export { RenderedConnection }; +export { Scrollbar }; +export { ScrollbarPair }; +export { ShortcutRegistry }; +export { TabNavigateCursor }; +export { Theme }; +export { ThemeManager }; +export { Toolbox }; +export { ToolboxCategory }; +export { ToolboxItem }; +export { ToolboxSeparator }; +export { TouchGesture }; +export { Trashcan }; +export { VariableMap }; +export { VariableModel }; +export { VerticalFlyout }; +export { Warning }; +export { Workspace }; +export { WorkspaceAudio }; +export { WorkspaceComment }; +export { WorkspaceCommentSvg }; +export { WorkspaceDragSurfaceSvg }; +export { WorkspaceDragger }; +export { WorkspaceSvg }; +export { ZoomControls }; +export { config }; /** @deprecated Use Blockly.ConnectionType instead. */ -exports.connectionTypes = ConnectionType; -exports.constants = constants; -exports.dialog = dialog; -exports.fieldRegistry = fieldRegistry; -exports.geras = geras; -exports.inject = inject; -exports.inputTypes = inputTypes; -exports.minimalist = minimalist; -exports.registry = registry; -exports.serialization = { - blocks: serializationBlocks, - exceptions: serializationExceptions, - priorities: serializationPriorities, - registry: serializationRegistry, - variables: serializationVariables, - workspaces: serializationWorkspaces, - ISerializer: ISerializer, +export const connectionTypes = ConnectionType; +export { inject }; +export { inputTypes }; +export namespace serialization { + export const blocks = serializationBlocks; + export const exceptions = serializationExceptions; + export const priorities = serializationPriorities; + export const registry = serializationRegistry; + export const variables = serializationVariables; + export const workspaces = serializationWorkspaces; + export type ISerializer = SerializerInterface; }; -exports.thrasos = thrasos; -exports.uiPosition = uiPosition; -exports.utils = utils; -exports.zelos = zelos; + +// If Blockly is compiled with ADVANCED_COMPILATION and/or loaded as a +// CJS or ES module there will not be a Blockly global variable +// created. This can cause problems because a very common way of +// loading translations is to use a