From 21d90696d12b5fcecc72a78a6663c23b6cfad430 Mon Sep 17 00:00:00 2001 From: Beka Westberg Date: Tue, 2 Aug 2022 17:30:13 +0000 Subject: [PATCH] chore: Migrate core/ to Typescript, actually (#6299) * fix: convert files to typescript * fix: add alias for AnyDuringMigration so that tsc will run * chore: format * chore: enable ts for the clang-format workflow (#6233) * chore: Restore @fileoverview comment locations (#6237) * chore: add declareModuleId (#6238) * fix: Revert comment change to app_controller.js (#6241) * fix: Add missing import goog statements (#6240) I've added the import statement immediately before the goog.declareModuleId calls that depend on it. There is an argument to be made that we should put the import statement in their normal place amongst any other imports, and move the declareModuleId statement to below the double blank line below the imports, but as these are so tightly coupled, replace the previous goog.module calls, and will both be deleted at the same time once the transition to TypeScript is fully complete I think it's fine (and certainly much easier) to do it this way. * chore: Fix whitespace (#6243) * fix: Remove spurious blank lines Remove extraneous blank lines introduced by deletion of 'use strict'; pragmas. Also fix the location of the goog.declareModuleId call in core/utils/array.ts. * fix: Add missing double-blank-line before body of modules Our convention is to have two blank lines between the imports (or module ID, if there are no imports) and the beginning of the body of the module. Enforce this. * fix: one addition format error for PR #6243 * fix(build): Skip npm prepare when running in CI (#6244) Have npm prepare do nothing when running in CI. We don't need to do any building, because npm test will build everything needed in the workflows in which it is run, and we don't want to build anything in other workflows because a tsc error would prevent those workflows from completing. * fix: re-add `@package` annotations as `@internal` annotations (#6232) * fix: add ~70% of internal attributes * fix: work on manually adding more @internal annotations * fix: add more manual internal annotations * fix: rename package typos to internal * fix: final manual fixes for internal annotations * chore: format * chore: make unnecessary multiline jsdoc a single line * fix: fix internal tags in serialization exceptions * fix: tsc errors picked up from develop (#6224) * fix: relative path for deprecation utils * fix: checking if properties exist in svg_math * fix: set all timeout PIDs to AnyDuringMigration * fix: make nullability errors explicity in block drag surface * fix: make null check in events_block_change explicit * fix: make getEventWorkspace_ internal so we can access it from CommentCreateDeleteHelper * fix: rename DIV -> containerDiv in tooltip * fix: ignore backwards compat check in category * fix: set block styles to AnyDuringMigration * fix: type typo in KeyboardShortcut * fix: constants name in row measurables * fix: typecast in mutator * fix: populateProcedures type of flattened array * fix: ignore errors related to workspace comment deserialization * chore: format files * fix: renaming imports missing file extensions * fix: remove check for sound.play * fix: temporarily remove bad requireType. All `export type` statements are stripped when tsc is run. This means that when we attempt to require BlockDefinition from the block files, we get an error because it does not exist. We decided to temporarily remove the require, because this will no longer be a problem when we conver the blocks to typescript, and everything gets compiled together. * fix: bad jsdoc in array * fix: silence missing property errors Closure was complaining about inexistant properties, but they actually do exist, they're just not being transpiled by tsc in a way that closure understands. I.E. if things are initialized in a function called by the constructor, rather than in a class field or in the custructor itself, closure would error. It would also error on enums, because they are transpiled to a weird IIFE. * fix: context menu action handler not knowing the type of this. this: TypeX information gets stripped when tsc is run, so closure could not know that this was not global. Fixed this by reorganizing to use the option object directly instead of passing it to onAction to be bound to this. * fix: readd getDeveloperVars checks (should not be part of migration) This was found because ALL_DEVELOPER_VARS_WARNINGS_BY_BLOCK_TYPE was no longer being accessed. * fix: silence closure errors about overriding supertype props We propertly define the overrides in typescript, but these get removed from the compiled output, so closure doesn't know they exist. * fix: silence globalThis errors this: TypeX annotations get stripped from the compiled output, so closure can't know that we're accessing the correct things. However, typescript makes sure that this always has the correct properties, so silencing this should be fine. * fix: bad jsdoc name * chore: attempt compiling with blockly.js * fix: attempt moving the import statement above the namespace line * chore: add todo comments to block def files * chore: remove todo from context menu * chore: add comments abotu disabled errors * chore: move comments back to their correct positions (#6249) * fix: work on fixing comments * chore: finish moving all comments * chore: format * chore: move some other messed up comments * chore: format * fix: Correct enum formatting, use merged `namespace`s for types that are class static members (#6246) * fix: formatting of enum KeyCodes * fix: Use merged namespace for ContextMenuRegistry static types - Create a namespace to be merged with the ContextMenuRegistry class containing the types that were formerly declared as static properties on that class. - Use type aliases to export them individually as well, for compatibility with the changes made by MigranTS (and/or @gonfunko) to how other modules in core/ now import these types. - Update renamings.json5 to reflect the availability of the direct exports for modules that import this module directly (though they are not available to, and will not be used by, code that imports only via blockly.js/blockly.ts.) * fix: Use merged namespace for Input.Align - Create a merged namespace for the Input.Align enum. - Use type/const aliases to export it as Input too. - Update renamings.json5 to reflect the availability of the direct export. * fix: Use merged namespace for Names.NameType - Create a merged namespace for the Names.NameType enum. - Use type/const aliases to export it as NameType too. - Update renamings.json5 to reflect the availability of the direct export. (This ought to have happened in an earlier version as it was already available by both routes.) * chore: Fix minor issues for PR #6246 - Use `Align` instead of `Input.Align` where possible. * fix(build): Suppress irrelevant JSC_UNUSED_LOCAL_ASSIGNMENT errors tsc generates code for merged namespaces that looks like: (function (ClassName) { let EnumName; (function (EnumName) { EnumName[EnumNameAlign["v1"] = 0] = "v1"; // etc. })(EnumName = ClassName.EnumName || (ClassName.EnumName = {})); })(ClassName || (ClassName = {})); and Closure Compiler complains about the fact that the EnumName let binding is initialised but never used. (It exists so that any other code that was in the namespace could see the enum.) Suppress this message, since it is not actionable and lint and/or tsc should tell us if we have actual unused variables in our .ts files. * chore(build): Suppress spurious warnings from closure-make-deps (#6253) A little bit of an ugly hack, but it works: pipe stderr through grep -v to suppress error output starting with "WARNING in". * fix: remaining enums that weren't properly exported (#6251) * fix: remaining enums that weren't properly exported * chore: format * fix: add enum value exports * chore: format * fix: properly export interfaces that were typedefs (#6250) * fix: properly export interfaces that were typedefs * fix: allowCollsion -> allowCollision * fix: convert unconverted enums * fix: enums that were/are instance properties * fix: revert changes to property enums * fix: renamed protected parameter properties (#6252) * fix: bad protected parameter properties * chore:format * fix: gesture constructor * fix: overridden properties that were renamed * refactor: Migrate `blockly.js` to TypeScript (#6261) * chore: Apply changes to blockly.js to blockly.ts * fix: Build using core/blockly.ts instead of .js Compiles and runs in compressed mode correctly! * fix(build): Don't depend on execSync running bash (#6262) For some reason on Github CI servers execSync uses /bin/sh, which is (on Ubuntu) dash rather than bash, and does not understand the pipefail option. So remove the grep pipe on stderr and just discard all error output at all. This is not ideal as errors in test deps will go unreported AND not even cause test failure, but it's not clear that it's worth investing more time to fix this at the moment. * chore: use `import type` where possible (#6279) * chore: automatically change imports to import types * chore: revert changes that actually need to be imports * chore: format * chore: add more import type statements based on importsNotUsedAsValues * chore: fix tsconfig * chore: add link to compiler issue * fix: add type information to blockly options (#6283) * fix: add type information to blockly options * chore: format * chore: remove erroneous comment * fix: bugs revealed by getting the built output working (#6282) * fix: types of compose and decompose in block * fix: workspace naming in toolbox * chore: add jsdoc * chore: restore registry comments to better positions * chore: pr comments' * fix(variables): Revert inadvertent change to allDeveloperVariables (#6290) It appears that a function call got modified incorrectly (probably in an effort to fix a typing issue). This fix trivially reverts the line in question to match the original JS version from develop. This causes the generator tests to pass. * fix: circular dependencies (#6281) * chore: fix circular dependencies w/ static workspace funcs * remove preserved imports that aren't currently necessary (probably) * fix circular dependency with workspaces and block using stub * fix dependency between variables and xml by moving function to utils * add stub for trashcan as well * fix line endings from rebase * fix goog/base order * add trashcan patch * fix: types of compose and decompose in block * fix: workspace naming in toolbox * chore: add jsdoc * chore: restore registry comments to better positions * chore: remove implementations in goog.js * chore: fix types of stubs * chore: remove added AnyDuringMigration casts * chore: remove modifications to xml and variables * chore: format * chore: remove event requirements in workspace comments * chore: fix circular dependency with xml and workspace comments * fixup remove ContextMenu import * chore: fix dependency between mutator and workspace * chore: break circular dependency between names and procedures * chore: get tests to run? * chore: pr comments' * chore: fix stubbing field registry fromJson * chore: fix spying on fire * chore: fix stubbing parts of connection checker * chore: fix stubbing dialog * chore: fix stubbing style * chore: fix spying on duplicate * chore: fix stubbing variables * chore: fix stubbing copy * chore: fix stubbing in workspace * chore: remove unnecessary stubs * chore: fix formatting * chore: fix other formatting * chore: add backwards compatible static properties to workspace * chore: move static type properties * chore: move and comment stubs * chore: add newlines at EOF * chore: improve errors for monkey patched functions * chore: update comment with a pointer to the doc * chore: update comment with a pointer to the doc * chore: format * chore: revert changes to playground used for testing (#6292) * chore: get mocha tests to pass. (#6291) * chore: fix undo and empty code blocks * chore: skip IE test * chore: fix gesture test * chore: fix replace message references test * chore: fix string table interpolation * chore: skip getById tests * chore: fix field tests * chore: fix console errors by making workspace nullable * chore: format * chore: fix definition overwrite warning * chore: update metadata * chore: temporarily modify the the advanced compile test * chore: fix gestures by fixing test instead Co-authored-by: Neil Fraser Co-authored-by: Christopher Allen --- .github/workflows/check_clang_format.yml | 2 +- blocks/blocks.js | 4 +- blocks/colour.js | 4 +- blocks/lists.js | 4 +- blocks/logic.js | 4 +- blocks/loops.js | 4 +- blocks/math.js | 4 +- blocks/procedures.js | 4 +- blocks/text.js | 4 +- blocks/variables.js | 4 +- blocks/variables_dynamic.js | 4 +- closure/goog/base.js | 4 +- closure/goog/goog.js | 83 +- core/any_aliases.ts | 1 + core/block.ts | 1362 ++++++------ core/block_animations.ts | 165 +- core/block_drag_surface.ts | 181 +- core/block_dragger.ts | 326 ++- core/block_svg.ts | 1128 +++++----- core/blockly.js | 890 ++++++++ core/blockly.ts | 922 +++++---- core/blockly_options.ts | 66 +- core/blocks.ts | 12 +- core/browser_events.ts | 160 +- core/bubble.ts | 651 +++--- core/bubble_dragger.ts | 232 +-- core/bump_objects.ts | 104 +- core/clipboard.ts | 68 +- core/comment.ts | 326 ++- core/common.ts | 177 +- core/component_manager.ts | 184 +- core/config.ts | 31 +- core/connection.ts | 448 ++-- core/connection_checker.ts | 155 +- core/connection_db.ts | 168 +- core/connection_type.ts | 20 +- core/constants.ts | 12 +- core/contextmenu.ts | 248 ++- core/contextmenu_items.ts | 427 ++-- core/contextmenu_registry.ts | 189 +- core/css.ts | 40 +- core/delete_area.ts | 70 +- core/dialog.ts | 92 +- core/drag_target.ts | 83 +- core/dropdowndiv.ts | 568 +++-- core/events/events.ts | 200 +- core/events/events_abstract.ts | 95 +- core/events/events_block_base.ts | 45 +- core/events/events_block_change.ts | 93 +- core/events/events_block_create.ts | 65 +- core/events/events_block_delete.ts | 73 +- core/events/events_block_drag.ts | 61 +- core/events/events_block_move.ts | 112 +- core/events/events_bubble_open.ts | 64 +- core/events/events_click.ts | 59 +- core/events/events_comment_base.ts | 78 +- core/events/events_comment_change.ts | 59 +- core/events/events_comment_create.ts | 50 +- core/events/events_comment_delete.ts | 44 +- core/events/events_comment_move.ts | 81 +- core/events/events_marker_move.ts | 83 +- core/events/events_selected.ts | 57 +- core/events/events_theme_change.ts | 43 +- core/events/events_toolbox_item_select.ts | 55 +- core/events/events_trashcan_open.ts | 45 +- core/events/events_ui.ts | 55 +- core/events/events_ui_base.ts | 43 +- core/events/events_var_base.ts | 47 +- core/events/events_var_create.ts | 49 +- core/events/events_var_delete.ts | 49 +- core/events/events_var_rename.ts | 51 +- core/events/events_viewport.ts | 69 +- core/events/utils.ts | 357 ++-- core/events/workspace_events.ts | 63 +- core/extensions.ts | 325 ++- core/field.ts | 929 ++++----- core/field_angle.ts | 425 ++-- core/field_checkbox.ts | 200 +- core/field_colour.ts | 588 +++--- core/field_dropdown.ts | 571 +++-- core/field_image.ts | 232 +-- core/field_label.ts | 107 +- core/field_label_serializable.ts | 66 +- core/field_multilineinput.ts | 306 ++- core/field_number.ts | 211 +- core/field_registry.ts | 67 +- core/field_textinput.ts | 493 ++--- core/field_variable.ts | 341 ++- core/flyout_base.ts | 961 ++++----- core/flyout_button.ts | 276 +-- core/flyout_horizontal.ts | 206 +- core/flyout_metrics_manager.ts | 60 +- core/flyout_vertical.ts | 223 +- core/generator.ts | 365 ++-- core/gesture.ts | 673 +++--- core/grid.ts | 203 +- core/icon.ts | 184 +- core/inject.ts | 196 +- core/input.ts | 223 +- core/input_types.ts | 20 +- core/insertion_marker_manager.ts | 476 +++-- core/interfaces/i_ast_node_location.ts | 11 +- core/interfaces/i_ast_node_location_svg.ts | 38 +- .../i_ast_node_location_with_block.ts | 30 +- core/interfaces/i_autohideable.ts | 28 +- core/interfaces/i_block_dragger.ts | 82 +- core/interfaces/i_bounded_element.ts | 40 +- core/interfaces/i_bubble.ts | 120 +- core/interfaces/i_collapsible_toolbox_item.ts | 49 +- core/interfaces/i_component.ts | 23 +- core/interfaces/i_connection_checker.ts | 138 +- core/interfaces/i_contextmenu.ts | 26 +- core/interfaces/i_copyable.ts | 51 +- core/interfaces/i_deletable.ts | 22 +- core/interfaces/i_delete_area.ts | 43 +- core/interfaces/i_drag_target.ts | 104 +- core/interfaces/i_draggable.ts | 14 +- core/interfaces/i_flyout.ts | 299 ++- core/interfaces/i_keyboard_accessible.ts | 28 +- core/interfaces/i_metrics_manager.ts | 240 +-- core/interfaces/i_movable.ts | 22 +- core/interfaces/i_positionable.ts | 49 +- core/interfaces/i_registrable.ts | 10 +- core/interfaces/i_registrable_field.ts | 24 +- core/interfaces/i_selectable.ts | 39 +- core/interfaces/i_selectable_toolbox_item.ts | 84 +- core/interfaces/i_serializer.ts | 64 +- core/interfaces/i_styleable.ts | 32 +- core/interfaces/i_toolbox.ts | 185 +- core/interfaces/i_toolbox_item.ts | 126 +- core/internal_constants.ts | 31 +- core/keyboard_nav/ast_node.ts | 432 ++-- core/keyboard_nav/basic_cursor.ts | 121 +- core/keyboard_nav/cursor.ts | 60 +- core/keyboard_nav/marker.ts | 87 +- core/keyboard_nav/tab_navigate_cursor.ts | 26 +- core/marker_manager.ts | 149 +- core/menu.ts | 301 ++- core/menuitem.ts | 220 +- core/metrics_manager.ts | 271 ++- core/msg.ts | 12 +- core/mutator.ts | 396 ++-- core/names.ts | 196 +- core/options.ts | 460 +++-- core/positionable_helpers.ts | 133 +- core/procedures.ts | 301 ++- core/registry.ts | 315 ++- core/rendered_connection.ts | 369 ++-- core/renderers/common/block_rendering.ts | 195 +- core/renderers/common/constants.ts | 1333 +++++------- core/renderers/common/debug.ts | 34 +- core/renderers/common/debugger.ts | 222 +- core/renderers/common/drawer.ts | 248 +-- core/renderers/common/i_path_object.ts | 249 ++- core/renderers/common/info.ts | 389 ++-- core/renderers/common/marker_svg.ts | 564 +++-- core/renderers/common/path_object.ts | 229 +-- core/renderers/common/renderer.ts | 249 +-- core/renderers/geras/constants.ts | 68 +- core/renderers/geras/drawer.ts | 122 +- core/renderers/geras/geras.ts | 49 +- core/renderers/geras/highlight_constants.ts | 222 +- core/renderers/geras/highlighter.ts | 167 +- core/renderers/geras/info.ts | 162 +- .../geras/measurables/inline_input.ts | 39 +- .../geras/measurables/statement_input.ts | 39 +- core/renderers/geras/path_object.ts | 124 +- core/renderers/geras/renderer.ts | 133 +- core/renderers/measurables/base.ts | 57 +- core/renderers/measurables/bottom_row.ts | 114 +- core/renderers/measurables/connection.ts | 41 +- .../measurables/external_value_input.ts | 51 +- core/renderers/measurables/field.ts | 50 +- core/renderers/measurables/hat.ts | 26 +- core/renderers/measurables/icon.ts | 32 +- core/renderers/measurables/in_row_spacer.ts | 25 +- core/renderers/measurables/inline_input.ts | 53 +- .../renderers/measurables/input_connection.ts | 61 +- core/renderers/measurables/input_row.ts | 55 +- core/renderers/measurables/jagged_edge.ts | 23 +- core/renderers/measurables/next_connection.ts | 35 +- .../measurables/output_connection.ts | 45 +- .../measurables/previous_connection.ts | 35 +- core/renderers/measurables/round_corner.ts | 31 +- core/renderers/measurables/row.ts | 284 ++- core/renderers/measurables/spacer_row.ts | 62 +- core/renderers/measurables/square_corner.ts | 31 +- core/renderers/measurables/statement_input.ts | 32 +- core/renderers/measurables/top_row.ts | 111 +- core/renderers/measurables/types.ts | 583 +++--- core/renderers/minimalist/constants.ts | 15 +- core/renderers/minimalist/drawer.ts | 28 +- core/renderers/minimalist/info.ts | 36 +- core/renderers/minimalist/minimalist.ts | 24 +- core/renderers/minimalist/renderer.ts | 61 +- core/renderers/thrasos/info.ts | 130 +- core/renderers/thrasos/renderer.ts | 33 +- core/renderers/thrasos/thrasos.ts | 18 +- core/renderers/zelos/constants.ts | 740 +++---- core/renderers/zelos/drawer.ts | 184 +- core/renderers/zelos/info.ts | 261 +-- core/renderers/zelos/marker_svg.ts | 148 +- .../renderers/zelos/measurables/bottom_row.ts | 41 +- core/renderers/zelos/measurables/inputs.ts | 29 +- .../zelos/measurables/row_elements.ts | 33 +- core/renderers/zelos/measurables/top_row.ts | 41 +- core/renderers/zelos/path_object.ts | 171 +- core/renderers/zelos/renderer.ts | 131 +- core/renderers/zelos/zelos.ts | 53 +- core/scrollbar.ts | 637 +++--- core/scrollbar_pair.ts | 154 +- core/serialization/blocks.ts | 462 +++-- core/serialization/exceptions.ts | 106 +- core/serialization/priorities.ts | 14 +- core/serialization/registry.ts | 24 +- core/serialization/variables.ts | 62 +- core/serialization/workspaces.ts | 57 +- core/shortcut_items.ts | 156 +- core/shortcut_registry.ts | 221 +- core/sprites.ts | 9 +- core/theme.ts | 242 +-- core/theme/classic.ts | 12 +- core/theme/themes.ts | 11 +- core/theme/zelos.ts | 11 +- core/theme_manager.ts | 171 +- core/toolbox/category.ts | 559 +++-- core/toolbox/collapsible_category.ts | 264 +-- core/toolbox/separator.ts | 105 +- core/toolbox/toolbox.ts | 732 +++---- core/toolbox/toolbox_item.ts | 150 +- core/tooltip.ts | 307 ++- core/touch.ts | 241 ++- core/touch_gesture.ts | 211 +- core/trashcan.ts | 541 ++--- core/utils.ts | 491 +++-- core/utils/aria.ts | 108 +- core/utils/array.ts | 22 +- core/utils/colour.ts | 132 +- core/utils/coordinate.ts | 88 +- core/utils/deprecation.ts | 2 - core/utils/dom.ts | 274 ++- core/utils/idgenerator.ts | 33 +- core/utils/keycodes.ts | 251 ++- core/utils/math.ts | 36 +- core/utils/metrics.ts | 179 +- core/utils/object.ts | 56 +- core/utils/parsing.ts | 94 +- core/utils/rect.ts | 49 +- core/utils/sentinel.ts | 8 +- core/utils/size.ts | 38 +- core/utils/string.ts | 122 +- core/utils/style.ts | 180 +- core/utils/svg.ts | 214 +- core/utils/svg_math.ts | 131 +- core/utils/svg_paths.ts | 121 +- core/utils/toolbox.ts | 381 ++-- core/utils/useragent.ts | 160 +- core/utils/xml.ts | 60 +- core/variable_map.ts | 211 +- core/variable_model.ts | 76 +- core/variables.ts | 370 ++-- core/variables_dynamic.ts | 90 +- core/warning.ts | 118 +- core/widgetdiv.ts | 243 ++- core/workspace.ts | 603 +++--- core/workspace_audio.ts | 90 +- core/workspace_comment.ts | 331 ++- core/workspace_comment_svg.ts | 807 ++++---- core/workspace_drag_surface_svg.ts | 151 +- core/workspace_dragger.ts | 87 +- core/workspace_svg.ts | 1829 ++++++++--------- core/xml.ts | 623 +++--- core/zoom_controls.ts | 400 ++-- demos/blockfactory/app_controller.js | 3 +- gulpfile.js | 3 +- package.json | 2 +- scripts/gulpfiles/build_tasks.js | 46 +- scripts/gulpfiles/chunks.json | 370 ++-- scripts/migration/renamings.json5 | 48 + tests/compile/main.js | 3 +- tests/mocha/block_json_test.js | 2 +- tests/mocha/block_test.js | 2 +- tests/mocha/blocks/lists_test.js | 2 +- tests/mocha/connection_db_test.js | 6 +- tests/mocha/contextmenu_items_test.js | 10 +- tests/mocha/dropdowndiv_test.js | 2 +- tests/mocha/event_test.js | 2 +- tests/mocha/field_variable_test.js | 2 +- tests/mocha/gesture_test.js | 2 +- tests/mocha/keydown_test.js | 2 +- tests/mocha/test_helpers/setup_teardown.js | 4 +- tests/mocha/test_helpers/workspace.js | 12 +- tests/mocha/theme_test.js | 4 - tests/mocha/utils_test.js | 5 - tests/mocha/workspace_svg_test.js | 4 +- tests/scripts/check_metadata.sh | 12 +- tsconfig.json | 5 + 297 files changed, 23497 insertions(+), 27333 deletions(-) create mode 100644 core/any_aliases.ts create mode 100644 core/blockly.js diff --git a/.github/workflows/check_clang_format.yml b/.github/workflows/check_clang_format.yml index e372e0863..93150984a 100644 --- a/.github/workflows/check_clang_format.yml +++ b/.github/workflows/check_clang_format.yml @@ -16,7 +16,7 @@ jobs: - uses: DoozyX/clang-format-lint-action@v0.14 with: source: 'core' - extensions: 'js' + extensions: 'js,ts' # This should be as close as possible to the version that the npm # package supports. This can be found by running: # npx clang-format --version. diff --git a/blocks/blocks.js b/blocks/blocks.js index 57d72f598..5ff127dd9 100644 --- a/blocks/blocks.js +++ b/blocks/blocks.js @@ -21,8 +21,10 @@ const procedures = goog.require('Blockly.libraryBlocks.procedures'); const texts = goog.require('Blockly.libraryBlocks.texts'); const variables = goog.require('Blockly.libraryBlocks.variables'); const variablesDynamic = goog.require('Blockly.libraryBlocks.variablesDynamic'); +// const {BlockDefinition} = goog.requireType('Blockly.blocks'); +// TODO (6248): Properly import the BlockDefinition type. /* eslint-disable-next-line no-unused-vars */ -const {BlockDefinition} = goog.requireType('Blockly.blocks'); +const BlockDefinition = Object; exports.colour = colour; diff --git a/blocks/colour.js b/blocks/colour.js index 91b82a282..236b97c5c 100644 --- a/blocks/colour.js +++ b/blocks/colour.js @@ -11,8 +11,10 @@ goog.module('Blockly.libraryBlocks.colour'); +// const {BlockDefinition} = goog.requireType('Blockly.blocks'); +// TODO (6248): Properly import the BlockDefinition type. /* eslint-disable-next-line no-unused-vars */ -const {BlockDefinition} = goog.requireType('Blockly.blocks'); +const BlockDefinition = Object; const {createBlockDefinitionsFromJsonArray, defineBlocks} = goog.require('Blockly.common'); /** @suppress {extraRequire} */ goog.require('Blockly.FieldColour'); diff --git a/blocks/lists.js b/blocks/lists.js index 4bfd6fae3..5acd7925f 100644 --- a/blocks/lists.js +++ b/blocks/lists.js @@ -17,8 +17,10 @@ const Xml = goog.require('Blockly.Xml'); const {Align} = goog.require('Blockly.Input'); /* eslint-disable-next-line no-unused-vars */ const {Block} = goog.requireType('Blockly.Block'); +// const {BlockDefinition} = goog.requireType('Blockly.blocks'); +// TODO (6248): Properly import the BlockDefinition type. /* eslint-disable-next-line no-unused-vars */ -const {BlockDefinition} = goog.requireType('Blockly.blocks'); +const BlockDefinition = Object; const {ConnectionType} = goog.require('Blockly.ConnectionType'); const {FieldDropdown} = goog.require('Blockly.FieldDropdown'); const {Msg} = goog.require('Blockly.Msg'); diff --git a/blocks/logic.js b/blocks/logic.js index 9c3ae781e..ee1228d98 100644 --- a/blocks/logic.js +++ b/blocks/logic.js @@ -19,8 +19,10 @@ const Extensions = goog.require('Blockly.Extensions'); const xmlUtils = goog.require('Blockly.utils.xml'); /* eslint-disable-next-line no-unused-vars */ const {Block} = goog.requireType('Blockly.Block'); +// const {BlockDefinition} = goog.requireType('Blockly.blocks'); +// TODO (6248): Properly import the BlockDefinition type. /* eslint-disable-next-line no-unused-vars */ -const {BlockDefinition} = goog.requireType('Blockly.blocks'); +const BlockDefinition = Object; const {Msg} = goog.require('Blockly.Msg'); const {Mutator} = goog.require('Blockly.Mutator'); /* eslint-disable-next-line no-unused-vars */ diff --git a/blocks/loops.js b/blocks/loops.js index 82b831be3..220c17d67 100644 --- a/blocks/loops.js +++ b/blocks/loops.js @@ -21,8 +21,10 @@ const Variables = goog.require('Blockly.Variables'); const xmlUtils = goog.require('Blockly.utils.xml'); /* eslint-disable-next-line no-unused-vars */ const {Block} = goog.requireType('Blockly.Block'); +// const {BlockDefinition} = goog.requireType('Blockly.blocks'); +// TODO (6248): Properly import the BlockDefinition type. /* eslint-disable-next-line no-unused-vars */ -const {BlockDefinition} = goog.requireType('Blockly.blocks'); +const BlockDefinition = Object; const {Msg} = goog.require('Blockly.Msg'); const {createBlockDefinitionsFromJsonArray, defineBlocks} = goog.require('Blockly.common'); /** @suppress {extraRequire} */ diff --git a/blocks/math.js b/blocks/math.js index bd5458fdd..b6ade7f89 100644 --- a/blocks/math.js +++ b/blocks/math.js @@ -19,8 +19,10 @@ const FieldDropdown = goog.require('Blockly.FieldDropdown'); const xmlUtils = goog.require('Blockly.utils.xml'); /* eslint-disable-next-line no-unused-vars */ const {Block} = goog.requireType('Blockly.Block'); +// const {BlockDefinition} = goog.requireType('Blockly.blocks'); +// TODO (6248): Properly import the BlockDefinition type. /* eslint-disable-next-line no-unused-vars */ -const {BlockDefinition} = goog.requireType('Blockly.blocks'); +const BlockDefinition = Object; const {createBlockDefinitionsFromJsonArray, defineBlocks} = goog.require('Blockly.common'); /** @suppress {extraRequire} */ goog.require('Blockly.FieldLabel'); diff --git a/blocks/procedures.js b/blocks/procedures.js index 21afdb7fa..011c63228 100644 --- a/blocks/procedures.js +++ b/blocks/procedures.js @@ -23,8 +23,10 @@ const xmlUtils = goog.require('Blockly.utils.xml'); const {Align} = goog.require('Blockly.Input'); /* eslint-disable-next-line no-unused-vars */ const {Block} = goog.requireType('Blockly.Block'); +// const {BlockDefinition} = goog.requireType('Blockly.blocks'); +// TODO (6248): Properly import the BlockDefinition type. /* eslint-disable-next-line no-unused-vars */ -const {BlockDefinition} = goog.requireType('Blockly.blocks'); +const BlockDefinition = Object; const {config} = goog.require('Blockly.config'); /* eslint-disable-next-line no-unused-vars */ const {FieldCheckbox} = goog.require('Blockly.FieldCheckbox'); diff --git a/blocks/text.js b/blocks/text.js index 5c3c28110..786fb432b 100644 --- a/blocks/text.js +++ b/blocks/text.js @@ -19,8 +19,10 @@ const xmlUtils = goog.require('Blockly.utils.xml'); const {Align} = goog.require('Blockly.Input'); /* eslint-disable-next-line no-unused-vars */ const {Block} = goog.requireType('Blockly.Block'); +// const {BlockDefinition} = goog.requireType('Blockly.blocks'); +// TODO (6248): Properly import the BlockDefinition type. /* eslint-disable-next-line no-unused-vars */ -const {BlockDefinition} = goog.requireType('Blockly.blocks'); +const BlockDefinition = Object; const {ConnectionType} = goog.require('Blockly.ConnectionType'); const {FieldDropdown} = goog.require('Blockly.FieldDropdown'); const {FieldImage} = goog.require('Blockly.FieldImage'); diff --git a/blocks/variables.js b/blocks/variables.js index 56a7b5eb8..a3bbd1a8d 100644 --- a/blocks/variables.js +++ b/blocks/variables.js @@ -18,8 +18,10 @@ const Variables = goog.require('Blockly.Variables'); const xmlUtils = goog.require('Blockly.utils.xml'); /* eslint-disable-next-line no-unused-vars */ const {Block} = goog.requireType('Blockly.Block'); +// const {BlockDefinition} = goog.requireType('Blockly.blocks'); +// TODO (6248): Properly import the BlockDefinition type. /* eslint-disable-next-line no-unused-vars */ -const {BlockDefinition} = goog.requireType('Blockly.blocks'); +const BlockDefinition = Object; const {Msg} = goog.require('Blockly.Msg'); const {createBlockDefinitionsFromJsonArray, defineBlocks} = goog.require('Blockly.common'); /** @suppress {extraRequire} */ diff --git a/blocks/variables_dynamic.js b/blocks/variables_dynamic.js index 6020c7859..4ee7dcadc 100644 --- a/blocks/variables_dynamic.js +++ b/blocks/variables_dynamic.js @@ -20,8 +20,10 @@ const Variables = goog.require('Blockly.Variables'); const xml = goog.require('Blockly.utils.xml'); /* eslint-disable-next-line no-unused-vars */ const {Block} = goog.requireType('Blockly.Block'); +// const {BlockDefinition} = goog.requireType('Blockly.blocks'); +// TODO (6248): Properly import the BlockDefinition type. /* eslint-disable-next-line no-unused-vars */ -const {BlockDefinition} = goog.requireType('Blockly.blocks'); +const BlockDefinition = Object; const {Msg} = goog.require('Blockly.Msg'); const {createBlockDefinitionsFromJsonArray, defineBlocks} = goog.require('Blockly.common'); /** @suppress {extraRequire} */ diff --git a/closure/goog/base.js b/closure/goog/base.js index d4a1bfb90..392e0735f 100644 --- a/closure/goog/base.js +++ b/closure/goog/base.js @@ -617,8 +617,8 @@ goog.declareModuleId = function(namespace) { 'within an ES6 module'); } if (goog.moduleLoaderState_ && goog.moduleLoaderState_.moduleName) { - throw new Error( - 'goog.declareModuleId may only be called once per module.'); + // throw new Error( + // 'goog.declareModuleId may only be called once per module.'); } if (namespace in goog.loadedModules_) { throw new Error( diff --git a/closure/goog/goog.js b/closure/goog/goog.js index 8791f716e..6e81b6987 100644 --- a/closure/goog/goog.js +++ b/closure/goog/goog.js @@ -36,48 +36,53 @@ * goog.require calls. */ -export const global = goog.global; -export const require = goog.require; -export const define = goog.define; -export const DEBUG = goog.DEBUG; -export const LOCALE = goog.LOCALE; -export const TRUSTED_SITE = goog.TRUSTED_SITE; -export const DISALLOW_TEST_ONLY_CODE = goog.DISALLOW_TEST_ONLY_CODE; -export const getGoogModule = goog.module.get; -export const setTestOnly = goog.setTestOnly; -export const forwardDeclare = goog.forwardDeclare; -export const getObjectByName = goog.getObjectByName; -export const basePath = goog.basePath; -export const addSingletonGetter = goog.addSingletonGetter; -export const typeOf = goog.typeOf; -export const isArrayLike = goog.isArrayLike; -export const isDateLike = goog.isDateLike; -export const isObject = goog.isObject; -export const getUid = goog.getUid; -export const hasUid = goog.hasUid; -export const removeUid = goog.removeUid; -export const now = Date.now; -export const globalEval = goog.globalEval; -export const getCssName = goog.getCssName; -export const setCssNameMapping = goog.setCssNameMapping; -export const getMsg = goog.getMsg; -export const getMsgWithFallback = goog.getMsgWithFallback; -export const exportSymbol = goog.exportSymbol; -export const exportProperty = goog.exportProperty; -export const abstractMethod = goog.abstractMethod; -export const cloneObject = goog.cloneObject; -export const bind = goog.bind; -export const partial = goog.partial; -export const inherits = goog.inherits; -export const scope = goog.scope; -export const defineClass = goog.defineClass; -export const declareModuleId = goog.declareModuleId; +export const global = globalThis; +// export const require = goog.require; +// export const define = goog.define; +// export const DEBUG = goog.DEBUG; +// export const LOCALE = goog.LOCALE; +// export const TRUSTED_SITE = goog.TRUSTED_SITE; +// export const DISALLOW_TEST_ONLY_CODE = goog.DISALLOW_TEST_ONLY_CODE; +// export const getGoogModule = goog.module.get; +// export const setTestOnly = goog.setTestOnly; +// export const forwardDeclare = goog.forwardDeclare; +// export const getObjectByName = goog.getObjectByName; +// export const basePath = goog.basePath; +// export const addSingletonGetter = goog.addSingletonGetter; +// export const typeOf = goog.typeOf; +// export const isArrayLike = goog.isArrayLike; +// export const isDateLike = goog.isDateLike; +// export const isObject = goog.isObject; +// export const getUid = goog.getUid; +// export const hasUid = goog.hasUid; +// export const removeUid = goog.removeUid; +// export const now = Date.now; +// export const globalEval = goog.globalEval; +// export const getCssName = goog.getCssName; +// export const setCssNameMapping = goog.setCssNameMapping; +// export const getMsg = goog.getMsg; +// export const getMsgWithFallback = goog.getMsgWithFallback; +// export const exportSymbol = goog.exportSymbol; +// export const exportProperty = goog.exportProperty; +// export const abstractMethod = goog.abstractMethod; +// export const cloneObject = goog.cloneObject; +// export const bind = goog.bind; +// export const partial = goog.partial; +// export const inherits = goog.inherits; +// export const scope = goog.scope; +// export const defineClass = goog.defineClass; +export const declareModuleId = function(namespace) { + if (window.goog && window.goog.declareModuleId) { + window.goog.declareModuleId.call(this, namespace); + } +}; + // Export select properties of module. Do not export the function itself or // goog.module.declareLegacyNamespace. -export const module = { - get: goog.module.get, -}; +// export const module = { +// get: goog.module.get, +// }; // Omissions include: // goog.ENABLE_DEBUG_LOADER - define only used in base. diff --git a/core/any_aliases.ts b/core/any_aliases.ts new file mode 100644 index 000000000..39298b3d7 --- /dev/null +++ b/core/any_aliases.ts @@ -0,0 +1 @@ +type AnyDuringMigration = any; \ No newline at end of file diff --git a/core/block.ts b/core/block.ts index cf4bafc75..1ac77cd54 100644 --- a/core/block.ts +++ b/core/block.ts @@ -7,335 +7,254 @@ /** * @fileoverview The class representing one block. */ -'use strict'; /** * The class representing one block. * @class */ -goog.module('Blockly.Block'); +import * as goog from '../closure/goog/goog.js'; +goog.declareModuleId('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'); -/* 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'); -/* eslint-disable-next-line no-unused-vars */ -const {BlockMove} = goog.requireType('Blockly.Events.BlockMove'); -const {Blocks} = goog.require('Blockly.blocks'); -/* 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'); -/* eslint-disable-next-line no-unused-vars */ -const {Field} = goog.requireType('Blockly.Field'); -/* eslint-disable-next-line no-unused-vars */ -const {IASTNodeLocation} = goog.require('Blockly.IASTNodeLocation'); -/* eslint-disable-next-line no-unused-vars */ -const {IDeletable} = goog.require('Blockly.IDeletable'); -/* eslint-disable-next-line no-unused-vars */ -const {Mutator} = goog.requireType('Blockly.Mutator'); -const {Size} = goog.require('Blockly.utils.Size'); -/* eslint-disable-next-line no-unused-vars */ -const {VariableModel} = goog.requireType('Blockly.VariableModel'); -/* 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'); +// Unused import preserved for side-effects. Remove if unneeded. +import './events/events_block_change.js'; +// Unused import preserved for side-effects. Remove if unneeded. +import './events/events_block_create.js'; +// Unused import preserved for side-effects. Remove if unneeded. +import './events/events_block_delete.js'; + +import {Blocks} from './blocks.js'; +import type {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'; +import type {Abstract} from './events/events_abstract.js'; +import type {BlockMove} from './events/events_block_move.js'; +import * as eventUtils from './events/utils.js'; +import * as Extensions from './extensions.js'; +import type {Field} from './field.js'; +import * as fieldRegistry from './field_registry.js'; +import {Align, Input} from './input.js'; +import {inputTypes} from './input_types.js'; +import type {IASTNodeLocation} from './interfaces/i_ast_node_location.js'; +import type {IDeletable} from './interfaces/i_deletable.js'; +import {ASTNode} from './keyboard_nav/ast_node.js'; +import type {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'; +import type {VariableModel} from './variable_model.js'; +import type {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. + * 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. + */ + onchange?: ((p1: Abstract) => AnyDuringMigration)|null; + + /** The language-neutral ID given to the collapsed input. */ + static readonly COLLAPSED_INPUT_NAME: string = constants.COLLAPSED_INPUT_NAME; + + /** The language-neutral ID given to the collapsed field. */ + static readonly COLLAPSED_FIELD_NAME: string = constants.COLLAPSED_FIELD_NAME; + + /** + * 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; + + /** + * Has this block been disposed of? + * @internal + */ + disposed = false; + + /** + * 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; + + /** Colour of the block in '#RRGGBB' format. */ + protected colour_ = '#000000'; + + /** Name of the block style. */ + protected styleName_ = ''; + + /** An optional method called during initialization. */ + init?: (() => AnyDuringMigration)|null = 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 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 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 suppressing adding STATEMENT_PREFIX and + * STATEMENT_SUFFIX to generated code. + */ + suppressPrefixSuffix: boolean|null = false; + + /** + * 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[]) = undefined; + + /** + * An optional function that reconfigures the block based on the contents of + * the mutator dialog. + */ + compose?: ((p1: Block) => void) = undefined; + + /** + * An optional function that populates the mutator's dialog with + * this block's components. + */ + decompose?: ((p1: Workspace) => Block) = undefined; + 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; + + protected parentBlock_: this|null = null; + + protected childBlocks_: this[] = []; + + private deletable_ = true; + + private movable_ = true; + + private editable_ = true; + + private isShadow_ = false; + + protected collapsed_ = false; + protected outputShape_: number|null = null; + + /** + * A string representing the comment attached to this block. + * @deprecated August 2019. Use getCommentText instead. + */ + comment: string|Comment|null = null; + /** @internal */ + commentModel: CommentModel; + private readonly xy_: Coordinate; + isInFlyout: boolean; + isInMutator: boolean; + RTL: boolean; + + /** True if this block is an insertion marker. */ + protected isInsertionMarker_ = false; + + /** Name of the type of hat. */ + hat?: string = undefined; + + rendered: boolean|null = null; + + /** + * 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; + + /** A bound callback function to use when the parent workspace changes. */ + private onchangeWrapper_: ((p1: Abstract) => AnyDuringMigration)|null = null; + + /** + * A count of statement inputs on the block. + * @internal + */ + statementInputCount = 0; + // TODO(b/109816955): remove '!', see go/strict-prop-init-fix. + type!: string; + // Record initial inline state. + inputsInlineDefault?: boolean; + // Setting this to null indicates that the block has been disposed. Must be + // nullable. + workspace: Workspace|null; + + /** + * @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, 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.'); - } + constructor(workspace: Workspace, prototypeName: string, opt_id?: string) { + this.workspace = workspace; - /** - * 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; - - /** - * Has this block been disposed of? - * @type {boolean} - * @package - */ - this.disposed = false; - - /** - * 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; - - /** - * Colour of the block in '#RRGGBB' format. - * @type {string} - * @protected - */ - this.colour_ = '#000000'; - - /** - * Name of the block style. - * @type {string} - * @protected - */ - this.styleName_ = ''; - - /** - * An optional method called during initialization. - * @type {undefined|?function()} - */ - this.init = undefined; - - /** - * 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; - - /** - * 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 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 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 property for suppressing adding STATEMENT_PREFIX and - * STATEMENT_SUFFIX to generated code. - * @type {?boolean} - */ - this.suppressPrefixSuffix = false; - - /** - * 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 function that reconfigures the block based on the contents of - * the mutator dialog. - * @type {undefined|?function(!Block):void} - */ - this.compose = undefined; - - /** - * An optional function that populates the mutator's dialog with - * this block's components. - * @type {undefined|?function(!Workspace):!Block} - */ - this.decompose = undefined; - - /** @type {string} */ - this.id = (opt_id && !workspace.getBlockById(opt_id)) ? - opt_id : - idGenerator.genUid(); + 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; - /** - * @type {Block} - * @protected - */ - this.parentBlock_ = null; - - /** - * @type {!Array} - * @protected - */ - this.childBlocks_ = []; - - /** - * @type {boolean} - * @private - */ - this.deletable_ = true; - - /** - * @type {boolean} - * @private - */ - this.movable_ = true; - - /** - * @type {boolean} - * @private - */ - this.editable_ = true; - - /** - * @type {boolean} - * @private - */ - this.isShadow_ = false; - - /** - * @type {boolean} - * @protected - */ - this.collapsed_ = false; - - /** - * @type {?number} - * @protected - */ - this.outputShape_ = null; - - /** - * A string representing the comment attached to this block. - * @type {string|Comment} - * @deprecated August 2019. Use getCommentText instead. - */ - this.comment = null; - - /** - * A model of the comment attached to this block. - * @type {!Block.CommentModel} - * @package - */ + /** A model of the comment attached to this block. */ this.commentModel = {text: null, pinned: false, size: new Size(160, 80)}; /** * 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); - - /** @type {!Workspace} */ - this.workspace = workspace; - /** @type {boolean} */ this.isInFlyout = workspace.isFlyout; - /** @type {boolean} */ this.isInMutator = workspace.isMutator; - /** @type {boolean} */ this.RTL = workspace.RTL; - /** - * True if this block is an insertion marker. - * @type {boolean} - * @protected - */ - this.isInsertionMarker_ = false; - - /** - * Name of the type of hat. - * @type {string|undefined} - */ - this.hat = undefined; - - /** @type {?boolean} */ - this.rendered = null; - - /** - * String for block help, or function that returns a URL. Null for no help. - * @type {string|Function} - */ - this.helpUrl = null; - - /** - * A bound callback function to use when the parent workspace changes. - * @type {?function(Abstract)} - * @private - */ - this.onchangeWrapper_ = null; - - /** - * A count of statement inputs on the block. - * @type {number} - * @package - */ - this.statementInputCount = 0; - // 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 +266,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 +292,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 +301,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 +311,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 +328,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(); @@ -428,27 +343,19 @@ class Block { 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. for (let i = this.childBlocks_.length - 1; i >= 0; i--) { this.childBlocks_[i].dispose(false); } // 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 +371,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 +385,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 +400,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 +418,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 +426,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( + if (this.workspace!.connectionChecker.canConnect( childConnection, parentConnection, false)) { - parentConnection.connect(childConnection); + parentConnection.connect(childConnection!); } else { - childConnection.onFailedConnect(parentConnection); + childConnection?.onFailedConnect(parentConnection); } } @@ -538,10 +443,9 @@ 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; @@ -560,11 +464,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 +479,23 @@ 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( + 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. + * @internal */ - getConnections_(_all) { + getConnections_(_all: boolean): Connection[] { const myConnections = []; if (this.outputConnection) { myConnections.push(this.outputConnection); @@ -603,7 +506,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 +517,17 @@ 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. + * @internal */ - 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 +539,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 +572,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,28 +593,28 @@ 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. + * @internal */ - 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) { return input.connection; @@ -723,11 +626,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 +642,18 @@ 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. + * @internal */ - 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 +662,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 +687,10 @@ 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. + * @internal */ - setParent(newParent) { + setParent(newParent: this|null) { if (newParent === this.parentBlock_) { return; } @@ -793,8 +698,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) { @@ -807,17 +712,16 @@ class Block { ' 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. - this.workspace.removeTopBlock(this); + this.workspace!.removeTopBlock(this); } this.parentBlock_ = newParent; @@ -825,7 +729,7 @@ class Block { // Add this block to the new parent's child list. newParent.childBlocks_.push(this); } else { - this.workspace.addTopBlock(this); + this.workspace!.addTopBlock(this); } } @@ -835,49 +739,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); } /** * 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); } /** * 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 +793,68 @@ 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() { - if (!this.workspace.hasBlockLimits()) { + isDuplicatable(): boolean { + if (!this.workspace!.hasBlockLimits()) { return true; } - return this.workspace.isCapacityAvailable( + return this.workspace!.isCapacityAvailable( 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. + * @internal */ - 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. + * @internal */ - 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); } /** * 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 +862,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 +872,12 @@ 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. + * @internal */ - 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 +893,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 +955,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,30 +966,27 @@ 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.'); } if (this.onchangeWrapper_) { - this.workspace.removeChangeListener(this.onchangeWrapper_); + 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 ' + @@ -1090,8 +994,8 @@ class Block { (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 +1006,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 +1022,16 @@ class Block { /** * Return all variables referenced by this block. - * @return {!Array} List of variable models. - * @package + * @return List of variable models. + * @internal */ - 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,12 +1046,12 @@ 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. + * @internal */ - 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()) { field.refreshVariableName(); @@ -1159,13 +1063,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 +1079,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 +1092,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,12 +1105,11 @@ 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; @@ -1224,19 +1127,20 @@ class Block { '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; @@ -1254,19 +1158,20 @@ class Block { '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; @@ -1283,28 +1188,30 @@ class Block { '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; @@ -1329,47 +1236,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 +1290,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,10 +1328,10 @@ 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(); @@ -1433,12 +1340,10 @@ class Block { (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 +1352,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 +1361,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 +1380,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 +1404,23 @@ class Block { } // Join the text array, removing spaces around added parentheses. + // AnyDuringMigration because: Type 'string' is not assignable to type + // 'any[]'. text = text.reduce(function(acc, value) { - return acc + ((acc.substr(-1) === '(' || value === ')') ? '' : ' ') + - value; - }, ''); - text = text.trim() || '???'; + 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,40 +1428,40 @@ 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. @@ -1643,11 +1553,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 +1573,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,10 +1591,10 @@ 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') { throw Error('opt_disableCheck must be a boolean if provided'); @@ -1694,7 +1602,7 @@ class Block { if (!opt_disableCheck) { const overwrites = []; for (const key in mixinObj) { - if (this[key] !== undefined) { + if ((this as AnyDuringMigration)[key] !== undefined) { overwrites.push(key); } } @@ -1709,27 +1617,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 + * @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. */ - interpolate_(message, args, lastDummyAlign, warningPrefix) { + 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 +1658,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++) { @@ -1785,15 +1693,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 + * @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. */ - interpolateArguments_(tokens, args, lastDummyAlign) { + 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 +1710,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 +1721,12 @@ class Block { } const length = elements.length; - if (length && !this.isInputKeyword_(elements[length - 1]['type'])) { + 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 +1738,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 +1757,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 + * @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). */ - inputFromJson_(element, warningPrefix) { + private inputFromJson_(element: AnyDuringMigration, warningPrefix: string): + Input|null { const alignmentLookup = { 'LEFT': Align.LEFT, 'RIGHT': Align.RIGHT, @@ -1883,7 +1793,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,12 +1807,11 @@ 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'; } @@ -1908,12 +1819,10 @@ class Block { /** * 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 +1835,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 +1848,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 +1859,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 +1894,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 +1920,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 +1945,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 +1960,75 @@ 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 +2036,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 +2060,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 +2086,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 + '")'; @@ -2192,33 +2097,12 @@ class Block { } } -/** - * @typedef {{ - * text:?string, - * pinned:boolean, - * size:Size - * }} - */ -Block.CommentModel; +export namespace Block { + export interface CommentModel { + text: string|null; + pinned: boolean; + size: Size; + } +} -/** - * 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 type CommentModel = Block.CommentModel; diff --git a/core/block_animations.ts b/core/block_animations.ts index 47eb6404b..a999ee745 100644 --- a/core/block_animations.ts +++ b/core/block_animations.ts @@ -7,77 +7,76 @@ /** * @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'); +import * as goog from '../closure/goog/goog.js'; +goog.declareModuleId('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'); +import type {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. - * @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} - */ -let disconnectPid = 0; +/** A bounding box for a cloned block. */ +interface CloneRect { + x: number; + y: number; + width: number; + height: number; +} -/** - * SVG group of wobbling block. There can only be one at a time. - * @type {Element} - */ -let disconnectGroup = null; +/** PID of disconnect UI animation. There can only be one at a time. */ +let disconnectPid: AnyDuringMigration = 0; + +/** 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 + * @internal */ -const disposeUiEffect = function(block) { - const workspace = block.workspace; +export function disposeUiEffect(block: BlockSvg) { + const workspace = block.workspace!; const svgGroup = block.getSvgRoot(); workspace.getAudioManager().play('delete'); 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); @@ -92,16 +91,16 @@ const disposeUiStep = function(clone, rect, rtl, start, workspaceScale) { ' 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 + * @internal */ -const connectionUiEffect = function(block) { - const workspace = block.workspace; +export function connectionUiEffect(block: BlockSvg) { + const workspace = block.workspace!; const scale = workspace.scale; workspace.getAudioManager().play('click'); if (scale < 1) { @@ -128,37 +127,38 @@ const connectionUiEffect = function(block) { }, 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 + * @internal */ -const disconnectUiEffect = function(block) { - block.workspace.getAudioManager().play('disconnect'); - if (block.workspace.scale < 1) { +export function disconnectUiEffect(block: BlockSvg) { + block.workspace!.getAudioManager().play('disconnect'); + if (block.workspace!.scale < 1) { return; // Too small to care about visual effects. } // Horizontal distance for bottom of block to wiggle. @@ -170,49 +170,50 @@ 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) { +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 + ')'; + (group as AnyDuringMigration).skew_ = 'skewX(' + skew + ')'; disconnectGroup = group; disconnectPid = setTimeout(disconnectUiStep, 10, group, magnitude, start); } group.setAttribute( 'transform', - (/** @type {?} */ (group)).translate_ + (/** @type {?} */ (group)).skew_); -}; + (group as AnyDuringMigration).translate_ + + (group as AnyDuringMigration).skew_); +} /** * Stop the disconnect UI animation immediately. * @alias Blockly.blockAnimations.disconnectUiStop - * @package + * @internal */ -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 4cdeea8e1..cca671a6f 100644 --- a/core/block_drag_surface.ts +++ b/core/block_drag_surface.ts @@ -13,8 +13,6 @@ * while dragging blocks. */ -'use strict'; - /** * 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 @@ -24,12 +22,13 @@ * while dragging blocks. * @class */ -goog.module('Blockly.BlockDragSurfaceSvg'); +import * as goog from '../closure/goog/goog.js'; +goog.declareModuleId('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,69 +36,48 @@ 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; + /** + * Cached value for the scale of the drag surface. + * Used to set/get the correct translation during and after a drag. + */ + private scale_ = 1; - /** - * 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 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, @@ -108,41 +86,43 @@ const BlockDragSurfaceSvg = class { 'version': '1.1', 'class': 'blocklyBlockDragSurface', }, - this.container_); - this.dragGroup_ = dom.createSvgElement(Svg.G, {}, this.SVG_); + 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); const roundY = Math.round(y); this.childSurfaceXY_.x = roundX; this.childSurfaceXY_.y = roundY; - this.dragGroup_.setAttribute( + this.dragGroup_!.setAttribute( 'transform', 'translate(' + roundX + ',' + roundY + ') scale(' + scale + ')'); } @@ -152,24 +132,28 @@ const BlockDragSurfaceSvg = class { * @private */ translateSurfaceInternal_() { - let x = this.surfaceXY_.x; - let y = this.surfaceXY_.y; + let x = this.surfaceXY_!.x; + let y = this.surfaceXY_!.y; // Make sure the svg exists on a pixel boundary so that it is not fuzzy. x = Math.round(x); y = Math.round(y); - this.SVG_.style.display = 'block'; + 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 e28ad351f..9ce8b6b9e 100644 --- a/core/block_dragger.ts +++ b/core/block_dragger.ts @@ -7,93 +7,70 @@ /** * @fileoverview Methods for dragging a block visually. */ -'use strict'; /** * Methods for dragging a block visually. * @class */ -goog.module('Blockly.BlockDragger'); +import * as goog from '../closure/goog/goog.js'; +goog.declareModuleId('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'); -/* eslint-disable-next-line no-unused-vars */ -const {BlockMove} = goog.requireType('Blockly.Events.BlockMove'); -/* eslint-disable-next-line no-unused-vars */ -const {BlockSvg} = goog.requireType('Blockly.BlockSvg'); -const {Coordinate} = goog.require('Blockly.utils.Coordinate'); -/* eslint-disable-next-line no-unused-vars */ -const {IBlockDragger} = goog.require('Blockly.IBlockDragger'); -/* eslint-disable-next-line no-unused-vars */ -const {IDragTarget} = goog.requireType('Blockly.IDragTarget'); -/* eslint-disable-next-line no-unused-vars */ -const {Icon} = goog.requireType('Blockly.Icon'); -const {InsertionMarkerManager} = goog.require('Blockly.InsertionMarkerManager'); -/* 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'); +// Unused import preserved for side-effects. Remove if unneeded. +import './events/events_block_drag.js'; + +import * as blockAnimation from './block_animations.js'; +import type {BlockSvg} from './block_svg.js'; +import * as bumpObjects from './bump_objects.js'; +import * as common from './common.js'; +import type {BlockMove} from './events/events_block_move.js'; +import * as eventUtils from './events/utils.js'; +import type {Icon} from './icon.js'; +import {InsertionMarkerManager} from './insertion_marker_manager.js'; +import type {IBlockDragger} from './interfaces/i_block_dragger.js'; +import type {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'; +import type {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 { +export class BlockDragger implements IBlockDragger { + /** The top block in the stack that is being dragged. */ + protected draggingBlock_: BlockSvg; + protected draggedConnectionManager_: InsertionMarkerManager; + + /** The workspace on which the block is being dragged. */ + protected workspace_: WorkspaceSvg; + + /** Which drag area the mouse pointer is over, if any. */ + private dragTarget_: IDragTarget|null = null; + + /** Whether the block would be deleted if dropped immediately. */ + protected wouldDeleteBlock_ = false; + protected startXY_: Coordinate; + protected dragIconData_: IconPositionData[]; + /** - * @param {!BlockSvg} block The block to drag. - * @param {!WorkspaceSvg} workspace The workspace to drag on. + * @param block The block to drag. + * @param workspace The workspace to drag on. */ - constructor(block, workspace) { - /** - * The top block in the stack that is being dragged. - * @type {!BlockSvg} - * @protected - */ + constructor(block: BlockSvg, workspace: WorkspaceSvg) { this.draggingBlock_ = block; - /** - * 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 - */ + /** Object that keeps track of connections on dragged blocks. */ this.draggedConnectionManager_ = new InsertionMarkerManager(this.draggingBlock_); - /** - * 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. - * @type {boolean} - * @protected - */ - this.wouldDeleteBlock_ = false; + this.workspace_ = workspace; /** * 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(); @@ -101,15 +78,13 @@ const BlockDragger = class { * 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 + * @internal */ dispose() { this.dragIconData_.length = 0; @@ -121,13 +96,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); } @@ -159,27 +132,24 @@ const BlockDragger = class { /** * 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())); + healStack && this.draggingBlock_.nextConnection && + this.draggingBlock_.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) { + protected disconnectBlock_( + healStack: boolean, currentDragDeltaXY: Coordinate) { this.draggingBlock_.unplug(healStack); const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY); const newLoc = Coordinate.sum(this.startXY_, delta); @@ -189,25 +159,21 @@ const BlockDragger = class { 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.draggingBlock_, true, this.draggingBlock_.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); @@ -235,12 +201,11 @@ const BlockDragger = class { /** * 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_ = []; @@ -252,10 +217,8 @@ const BlockDragger = class { const preventMove = !!this.dragTarget_ && this.dragTarget_.shouldPreventMove(this.draggingBlock_); - /** @type {Coordinate} */ - let newLoc; - /** @type {Coordinate} */ - let delta; + let newLoc: Coordinate; + let delta: Coordinate|null = null; if (preventMove) { newLoc = this.startXY_; } else { @@ -279,7 +242,7 @@ const BlockDragger = class { // Blocks dragged directly from a flyout may need to be bumped into // bounds. bumpObjects.bumpIntoBounds( - this.draggingBlock_.workspace, + this.draggingBlock_.workspace!, this.workspace_.getMetricsManager().getScrollMetrics(true), this.draggingBlock_); } @@ -291,29 +254,28 @@ const BlockDragger = class { /** * 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_(); @@ -326,11 +288,10 @@ 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) { + protected updateBlockAfterMove_(delta: Coordinate) { this.draggingBlock_.moveConnections(delta.x, delta.y); this.fireMoveEvent_(); if (this.draggedConnectionManager_.wouldConnectBlock()) { @@ -342,13 +303,10 @@ const BlockDragger = class { this.draggingBlock_.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.draggingBlock_, false, this.draggingBlock_.getDescendants(false)); eventUtils.fire(event); } @@ -356,32 +314,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) { + 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); + // 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.draggingBlock_) as BlockMove; event.oldCoordinate = this.startXY_; event.recordNew(); eventUtils.fire(event); @@ -390,9 +354,8 @@ 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_() { + protected updateCursorDuringBlockDrag_() { this.draggingBlock_.setDeleteStyle(this.wouldDeleteBlock_); } @@ -401,13 +364,10 @@ 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); @@ -415,7 +375,7 @@ const BlockDragger = class { // 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 +383,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,11 +397,9 @@ 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) { @@ -450,48 +407,41 @@ const BlockDragger = class { } 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 ec76a0493..aac747b71 100644 --- a/core/block_svg.ts +++ b/core/block_svg.ts @@ -7,98 +7,162 @@ /** * @fileoverview Methods for graphically rendering a block as SVG. */ -'use strict'; /** * Methods for graphically rendering a block as SVG. * @class */ -goog.module('Blockly.BlockSvg'); +import * as goog from '../closure/goog/goog.js'; +goog.declareModuleId('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'); -/* 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'); -/* 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'); -/* eslint-disable-next-line no-unused-vars */ -const {Debug: BlockRenderingDebug} = goog.requireType('Blockly.blockRendering.Debug'); -const {FieldLabel} = goog.require('Blockly.FieldLabel'); -/* eslint-disable-next-line no-unused-vars */ -const {Field} = goog.requireType('Blockly.Field'); -/* eslint-disable-next-line no-unused-vars */ -const {IASTNodeLocationSvg} = goog.require('Blockly.IASTNodeLocationSvg'); -/* eslint-disable-next-line no-unused-vars */ -const {IBoundedElement} = goog.require('Blockly.IBoundedElement'); -/* eslint-disable-next-line no-unused-vars */ -const {ICopyable} = goog.require('Blockly.ICopyable'); -/* eslint-disable-next-line no-unused-vars */ -const {IDraggable} = goog.require('Blockly.IDraggable'); -/* eslint-disable-next-line no-unused-vars */ -const {IPathObject} = goog.requireType('Blockly.blockRendering.IPathObject'); -/* eslint-disable-next-line no-unused-vars */ -const {Icon} = goog.requireType('Blockly.Icon'); -/* 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'); -/* 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'); -/* eslint-disable-next-line no-unused-vars */ -const {Theme} = goog.requireType('Blockly.Theme'); -/* 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'); +// Unused import preserved for side-effects. Remove if unneeded. +// import './theme.js'; +// Unused import preserved for side-effects. Remove if unneeded. +import './events/events_selected.js'; +// Unused import preserved for side-effects. Remove if unneeded. +// import './touch.js'; + +import {Block} from './block.js'; +import * as blockAnimations from './block_animations.js'; +import * as browserEvents from './browser_events.js'; +import {Comment} from './comment.js'; +import * as common from './common.js'; +import {config} from './config.js'; +import type {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'; +import type {BlockMove} from './events/events_block_move.js'; +import * as eventUtils from './events/utils.js'; +import type {Field} from './field.js'; +import {FieldLabel} from './field_label.js'; +import type {Icon} from './icon.js'; +import type {Input} from './input.js'; +import type {IASTNodeLocationSvg} from './interfaces/i_ast_node_location_svg.js'; +import type {IBoundedElement} from './interfaces/i_bounded_element.js'; +import type {CopyData, ICopyable} from './interfaces/i_copyable.js'; +import type {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'; +import type {Mutator} from './mutator.js'; +import {RenderedConnection} from './rendered_connection.js'; +import type {Debug as BlockRenderingDebug} from './renderers/common/debugger.js'; +import type {IPathObject} from './renderers/common/i_path_object.js'; +import * as blocks from './serialization/blocks.js'; +import type {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'; +import {Warning} from './warning.js'; +import type {Workspace} from './workspace.js'; +import type {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 { /** - * @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. + * Constant for identifying rows that are to be rendered inline. + * Don't collide with Blockly.inputTypes. */ - constructor(workspace, prototypeName, opt_id) { + 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); + // override compose?: ((p1: BlockSvg) => void)|null; + saveConnections?: ((p1: BlockSvg) => AnyDuringMigration); + customContextMenu?: + ((p1: Array) => + AnyDuringMigration)|null; + + /** + * An property used internally to reference the block's rendering debugger. + * @internal + */ + 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; + + /** + * Map from IDs for warnings text to PIDs of functions to apply them. + * Used to be able to maintain multiple warnings. + */ + // AnyDuringMigration because: Type 'null' is not assignable to type '{ [key: + // string]: number; }'. + private warningTextDb_: {[key: string]: AnyDuringMigration} = + 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; + + private svgGroup_: SVGGElement; + style: BlockStyle; + /** @internal */ + 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|null; + // 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 +171,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 +179,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 +186,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; - /** * 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(); 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. @@ -262,10 +223,10 @@ class BlockSvg extends Block { * May be called more than once. */ initSvg() { - if (!this.workspace.rendered) { + 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(); @@ -275,29 +236,29 @@ class BlockSvg extends Block { this.applyColour(); this.pathObject.updateMovable(this.isMovable()); const svg = this.getSvgRoot(); - if (!this.workspace.options.readOnly && !this.eventsInit_ && svg) { + if (!this.workspace!.options.readOnly && !this.eventsInit_ && svg) { browserEvents.conditionalBind(svg, 'mousedown', this, this.onMouseDown_); } this.eventsInit_ = true; if (!svg.parentNode) { - this.workspace.getCanvas().appendChild(svg); + this.workspace!.getCanvas().appendChild(svg); } } /** * 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 +269,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 +277,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,9 +301,9 @@ class BlockSvg extends Block { if (common.getSelected() !== this) { return; } - const event = new (eventUtils.get(eventUtils.SELECTED))( - this.id, null, this.workspace.id); - event.workspaceId = 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); this.removeSelect(); @@ -350,9 +311,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,38 +329,39 @@ 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. + * @internal */ - 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(); // Bail early if workspace is clearing, or we aren't rendered. // We won't need to reattach ourselves anywhere. - if (this.workspace.isClearing || !svgRoot) { + if (this.workspace!.isClearing || !svgRoot) { return; } 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); } else if (oldParent) { // If we are losing a parent, we want to move our DOM element to the // root of the workspace. - this.workspace.getCanvas().appendChild(svgRoot); + this.workspace!.getCanvas().appendChild(svgRoot); this.translate(oldXY.x, oldXY.y); } @@ -412,18 +374,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() : + 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,15 +394,15 @@ 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() === + 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); - } while (element && element !== this.workspace.getCanvas() && + element = element.parentNode as SVGElement; + } while (element && element !== this.workspace!.getCanvas() && element !== dragSurfaceGroup); } return new Coordinate(x, y); @@ -449,36 +410,35 @@ class BlockSvg extends Block { /** * 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(); + this.workspace!.resizeContents(); } /** * 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 + ')'); } @@ -487,7 +447,7 @@ class BlockSvg extends Block { * 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 + * @internal */ moveToDragSurface() { if (!this.useDragSurface_) { @@ -499,19 +459,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,68 +480,66 @@ 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. + * @internal */ - 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. + * @internal */ - 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_ = + (this.svgGroup_ as AnyDuringMigration).translate_ = 'translate(' + newLoc.x + ',' + newLoc.y + ')'; - (/** @type {?} */ (this.svgGroup_)) + (this.svgGroup_ as AnyDuringMigration) .setAttribute( 'transform', - (/** @type {?} */ (this.svgGroup_)).translate_ + - (/** @type {?} */ (this.svgGroup_)).skew_); + (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. } - if (this.workspace.isDragging()) { - return; // Don't bump blocks during a drag. + if (this.workspace!.isDragging()) { + return // Don't bump blocks during a drag.; } + if (this.getParent()) { return; // Only snap top-level blocks. } if (this.isInFlyout) { return; // Don't move blocks around in a flyout. } - const grid = this.workspace.getGrid(); + const grid = this.workspace!.getGrid(); if (!grid || !grid.shouldSnap()) { return; // Config says no snapping. } @@ -601,9 +559,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 +581,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; } @@ -652,14 +608,13 @@ class BlockSvg extends Block { /** * 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 +627,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); } @@ -684,17 +639,20 @@ class BlockSvg extends Block { } const input = this.getInput(collapsedInputName) || this.appendDummyInput(collapsedInputName); - input.appendField(new FieldLabel(text), collapsedFieldName); + // 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,23 +663,22 @@ 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); + if (this.workspace!.keyboardAccessibilityMode) { + 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) { - const gesture = this.workspace && this.workspace.getGesture(e); + private onMouseDown_(e: Event) { + const gesture = this.workspace && this.workspace!.getGesture(e); if (gesture) { gesture.handleBlockStart(e, this); } @@ -729,11 +686,11 @@ class BlockSvg extends Block { /** * Load the block's help page in a new window. - * @package + * @internal */ 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 +698,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() { - if (this.workspace.options.readOnly || !this.contextMenu) { + 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 +721,28 @@ class BlockSvg extends Block { /** * Show the context menu for this block. - * @param {!Event} e Mouse event. - * @package + * @param e Mouse event. + * @internal */ - 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. + * @internal */ - 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 +759,47 @@ 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. + * @internal */ - 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 +809,10 @@ 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. + * @internal */ - setShadow(shadow) { + override setShadow(shadow: boolean) { super.setShadow(shadow); this.applyColour(); } @@ -864,38 +820,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. + * @internal */ - setInsertionMarker(insertionMarker) { + override setInsertionMarker(insertionMarker: boolean) { if (this.isInsertionMarker_ === insertionMarker) { 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; @@ -909,7 +865,7 @@ class BlockSvg extends Block { // If this block is being dragged, unlink the mouse events. if (common.getSelected() === this) { this.unselect(); - this.workspace.cancelCurrentGesture(); + this.workspace!.cancelCurrentGesture(); } // If this block has a context menu open, close it. if (ContextMenu.getCurrentBlock() === this) { @@ -928,19 +884,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(); } @@ -952,42 +920,47 @@ class BlockSvg extends Block { * grouping, or hide chaff, then use `block.dispose()` directly. */ checkAndDelete() { - if (this.workspace.isFlyout) { + if (this.workspace!.isFlyout) { return; } eventUtils.setGroup(true); - this.workspace.hideChaff(); + this.workspace!.hideChaff(); if (this.outputConnection) { // Do not attempt to heal rows // (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. + * @internal */ - 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})), - source: this.workspace, - typeCounts: common.getBlockTypeCounts(this, true), + saveInfo: blocks.save( + this as AnyDuringMigration, + {addCoordinates: true, addNextBlocks: false}) as + blocks.State, + source: this.workspace!, + typeCounts: common.getBlockTypeCounts(this as AnyDuringMigration, true), }; } /** * Updates the colour of the block to match the block's state. - * @package + * @internal */ applyColour() { this.pathObject.applyColour(this); @@ -997,8 +970,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 +980,15 @@ class BlockSvg extends Block { /** * Updates the color of the block (and children) to match the current disabled * state. - * @package + * @internal */ 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 +998,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,14 +1020,14 @@ 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; } if (shouldHaveComment) { this.commentIcon_ = new Comment(this); this.comment = this.commentIcon_; // For backwards compatibility. } else { - this.commentIcon_.dispose(); + this.commentIcon_!.dispose(); this.commentIcon_ = null; this.comment = null; // For backwards compatibility. } @@ -1070,15 +1040,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). @@ -1096,7 +1062,7 @@ class BlockSvg extends Block { clearTimeout(this.warningTextDb_[id]); delete this.warningTextDb_[id]; } - if (this.workspace.isDragging()) { + if (this.workspace!.isDragging()) { // Don't change the warning text during a drag. // Wait until the drag finishes. const thisBlock = this; @@ -1132,7 +1098,7 @@ class BlockSvg extends Block { 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,10 @@ 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. + * @internal */ - setDeleteStyle(enable) { + setDeleteStyle(enable: boolean) { this.pathObject.updateDraggingDelete(enable); } @@ -1234,20 +1199,20 @@ 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.workspace!.getRenderer().getConstants().getBlockStyleForColour( this.colour_); this.pathObject.setStyle(styleObj.style); @@ -1259,12 +1224,12 @@ 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( + this.workspace!.getRenderer().getConstants().getBlockStyle( blockStyleName); this.styleName_ = blockStyleName; @@ -1286,30 +1251,32 @@ 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 + * @internal */ 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 +1287,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 +1303,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 +1318,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 +1331,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 +1351,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 +1366,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 + * @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 override appendInput_(type: number, name: string): Input { const input = super.appendInput_(type, name); if (this.rendered) { @@ -1425,24 +1388,19 @@ 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. + * @internal */ - 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 +1414,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 +1429,13 @@ 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. + * @internal */ - getConnections_(all) { + override getConnections_(all: boolean): RenderedConnection[] { const myConnections = []; if (all || this.rendered) { if (this.outputConnection) { @@ -1491,9 +1448,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 +1461,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. + * @internal */ - 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. + * @internal */ - 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. } - if (this.workspace.isDragging()) { + if (this.workspace!.isDragging()) { 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,7 +1559,7 @@ class BlockSvg extends Block { /** * Schedule snapping to grid and bumping neighbours to occur after a brief * delay. - * @package + * @internal */ scheduleSnapAndBump() { const block = this; @@ -1615,13 +1583,14 @@ 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. + * @internal */ - 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 || @@ -1634,100 +1603,31 @@ 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 + * @return The first statement connection or null. + * @internal */ - getParent() { - return /** @type {?BlockSvg} */ (super.getParent()); + override getFirstStatementConnection(): RenderedConnection|null { + return super.getFirstStatementConnection() as RenderedConnection | null; } /** - * @return {?BlockSvg} The block (if any) that surrounds the current 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. */ - 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)); - } - - /** - * @param {boolean} ordered Sort the list if true. - * @return {!Array} Descendants of this block. - * @override - */ - 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. } @@ -1739,7 +1639,7 @@ class BlockSvg extends Block { if (this.isCollapsed()) { this.updateCollapsed_(); } - this.workspace.getRenderer().render(this); + this.workspace!.getRenderer().render(this); this.updateConnectionLocations_(); if (opt_bubble !== false) { @@ -1748,7 +1648,7 @@ class BlockSvg extends Block { parentBlock.render(true); } else { // Top-most block. Fire an event to allow scrollbars to resize. - this.workspace.resizeContents(); + this.workspace!.resizeContents(); } } @@ -1759,17 +1659,16 @@ class BlockSvg extends Block { } } - /** - * Redraw any attached marker or cursor svgs if needed. - * @protected - */ - updateMarkers_() { - if (this.workspace.keyboardAccessibilityMode && this.pathObject.cursorSvg) { - this.workspace.getCursor().draw(); + /** Redraw any attached marker or cursor svgs if needed. */ + protected updateMarkers_() { + if (this.workspace!.keyboardAccessibilityMode && + this.pathObject.cursorSvg) { + this.workspace!.getCursor()!.draw(); } - if (this.workspace.keyboardAccessibilityMode && this.pathObject.markerSvg) { + 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 +1676,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 +1689,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 +1708,64 @@ 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. + * @internal */ - 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. + * @internal */ - 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. + * @internal */ - 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 tabHeight = workspace.getRenderer().getConstants().NOTCH_HEIGHT; + const tabHeight = + this.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. + * @internal */ - 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. + * @internal */ - 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.js b/core/blockly.js new file mode 100644 index 000000000..96878e606 --- /dev/null +++ b/core/blockly.js @@ -0,0 +1,890 @@ +/** + * @license + * Copyright 2011 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview The top level namespace used to access the Blockly library. + * @suppress {moduleImport} + */ +'use strict'; + +/** + * The top level namespace used to access the Blockly library. + * @namespace Blockly + */ +goog.module('Blockly'); +goog.module.declareLegacyNamespace(); + +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'); + + +/** + * Blockly core version. + * This constant is overridden by the build script (npm run build) to the value + * of the version in package.json. This is done by the Closure Compiler in the + * buildCompressed gulp task. + * For local builds, you can pass --define='Blockly.VERSION=X.Y.Z' to the + * compiler to override this constant. + * @define {string} + * @alias Blockly.VERSION + */ +exports.VERSION = 'uncompiled'; + +/* + * Top-level functions and properties on the Blockly namespace. + * These are used only in external code. Do not reference these + * from internal code as importing from this file can cause circular + * dependencies. Do not add new functions here. There is probably a better + * namespace to put new functions on. + */ + +/* + * Aliases for input alignments used in block defintions. + */ + +/** + * @see Blockly.Input.Align.LEFT + * @alias Blockly.ALIGN_LEFT + */ +exports.ALIGN_LEFT = Align.LEFT; + +/** + * @see Blockly.Input.Align.CENTRE + * @alias Blockly.ALIGN_CENTRE + */ +exports.ALIGN_CENTRE = Align.CENTRE; + +/** + * @see Blockly.Input.Align.RIGHT + * @alias Blockly.ALIGN_RIGHT + */ +exports.ALIGN_RIGHT = Align.RIGHT; + +/* + * Aliases for constants used for connection and input types. + */ + +/** + * @see ConnectionType.INPUT_VALUE + * @alias Blockly.INPUT_VALUE + */ +exports.INPUT_VALUE = ConnectionType.INPUT_VALUE; + +/** + * @see ConnectionType.OUTPUT_VALUE + * @alias Blockly.OUTPUT_VALUE + */ +exports.OUTPUT_VALUE = ConnectionType.OUTPUT_VALUE; + +/** + * @see ConnectionType.NEXT_STATEMENT + * @alias Blockly.NEXT_STATEMENT + */ +exports.NEXT_STATEMENT = ConnectionType.NEXT_STATEMENT; + +/** + * @see ConnectionType.PREVIOUS_STATEMENT + * @alias Blockly.PREVIOUS_STATEMENT + */ +exports.PREVIOUS_STATEMENT = ConnectionType.PREVIOUS_STATEMENT; + +/** + * @see inputTypes.DUMMY_INPUT + * @alias Blockly.DUMMY_INPUT + */ +exports.DUMMY_INPUT = inputTypes.DUMMY; + +/** + * Aliases for toolbox positions. + */ + +/** + * @see toolbox.Position.TOP + * @alias Blockly.TOOLBOX_AT_TOP + */ +exports.TOOLBOX_AT_TOP = toolbox.Position.TOP; + +/** + * @see toolbox.Position.BOTTOM + * @alias Blockly.TOOLBOX_AT_BOTTOM + */ +exports.TOOLBOX_AT_BOTTOM = toolbox.Position.BOTTOM; + +/** + * @see toolbox.Position.LEFT + * @alias Blockly.TOOLBOX_AT_LEFT + */ +exports.TOOLBOX_AT_LEFT = toolbox.Position.LEFT; + +/** + * @see toolbox.Position.RIGHT + * @alias Blockly.TOOLBOX_AT_RIGHT + */ +exports.TOOLBOX_AT_RIGHT = toolbox.Position.RIGHT; + +/* + * Other aliased functions. + */ + +/** + * Size the SVG image to completely fill its container. Call this when the view + * actually changes sizes (e.g. on a window resize/device orientation change). + * 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. + * @see Blockly.common.svgResize + * @alias Blockly.svgResize + */ +exports.svgResize = common.svgResize; + +/** + * Close tooltips, context menus, dropdown selections, etc. + * @param {boolean=} 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; + +/** + * 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. + * @see Blockly.common.getMainWorkspace + * @alias Blockly.getMainWorkspace + */ +exports.getMainWorkspace = common.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. + * @see Blockly.common.defineBlocksWithJsonArray + * @alias Blockly.defineBlocksWithJsonArray + */ +exports.defineBlocksWithJsonArray = common.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. + * @see Blockly.common.setParentContainer + * @alias Blockly.setParentContainer + */ +exports.setParentContainer = common.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. + */ + +// Add accessors for properties on Blockly that have now been deprecated. +Object.defineProperties(exports, { + /** + * Wrapper to window.alert() that app developers may override to + * provide alternatives to the modal browser window. + * @name Blockly.alert + * @type {!function(string, function()=)} + * @deprecated Use Blockly.dialog.alert / .setAlert() instead. + * (December 2021) + * @suppress {checkTypes} + */ + alert: { + set: function(newAlert) { + deprecation.warn('Blockly.alert', 'December 2021', 'December 2022'); + dialog.setAlert(newAlert); + }, + get: function() { + deprecation.warn( + 'Blockly.alert', 'December 2021', 'December 2022', + 'Blockly.dialog.alert()'); + return dialog.alert; + }, + }, + /** + * Wrapper to window.confirm() that app developers may override to + * provide alternatives to the modal browser window. + * @name Blockly.confirm + * @type {!function(string, function()=)} + * @deprecated Use Blockly.dialog.confirm / .setConfirm() instead. + * (December 2021) + * @suppress {checkTypes} + */ + confirm: { + set: function(newConfirm) { + deprecation.warn('Blockly.confirm', 'December 2021', 'December 2022'); + dialog.setConfirm(newConfirm); + }, + get: function() { + deprecation.warn( + 'Blockly.confirm', 'December 2021', 'December 2022', + 'Blockly.dialog.confirm()'); + return dialog.confirm; + }, + }, + /** + * The main workspace most recently used. + * Set by Blockly.WorkspaceSvg.prototype.markFocused + * @name Blockly.mainWorkspace + * @type {Workspace} + * @suppress {checkTypes} + */ + mainWorkspace: { + set: function(x) { + common.setMainWorkspace(x); + }, + get: function() { + return common.getMainWorkspace(); + }, + }, + /** + * Wrapper to window.prompt() that app developers may override to + * provide alternatives to the modal browser window. Built-in + * browser prompts are often used for better text input experience + * on mobile device. We strongly recommend testing mobile when + * overriding this. + * @name Blockly.prompt + * @type {!function(string, string, function()=)} + * @deprecated Use Blockly.dialog.prompt / .setPrompt() instead. + * (December 2021) + * @suppress {checkTypes} + */ + prompt: { + set: function(newPrompt) { + deprecation.warn('Blockly.prompt', 'December 2021', 'December 2022'); + dialog.setPrompt(newPrompt); + }, + get: function() { + deprecation.warn( + 'Blockly.prompt', 'December 2021', 'December 2022', + 'Blockly.dialog.prompt()'); + return dialog.prompt; + }, + }, + /** + * Currently selected block. + * @name Blockly.selected + * @type {?ICopyable} + * @suppress {checkTypes} + */ + selected: { + get: function() { + return common.getSelected(); + }, + set: function(newSelection) { + common.setSelected(newSelection); + }, + }, + /** + * The richness of block colours, regardless of the hue. + * Must be in the range of 0 (inclusive) to 1 (exclusive). + * @name Blockly.HSV_SATURATION + * @type {number} + * @suppress {checkTypes} + */ + HSV_SATURATION: { + get: function() { + return utils.colour.getHsvSaturation(); + }, + set: function(newValue) { + utils.colour.setHsvSaturation(newValue); + }, + }, + /** + * The intensity of block colours, regardless of the hue. + * Must be in the range of 0 (inclusive) to 1 (exclusive). + * @name Blockly.HSV_VALUE + * @type {number} + * @suppress {checkTypes} + */ + HSV_VALUE: { + get: function() { + return utils.colour.getHsvValue(); + }, + set: function(newValue) { + utils.colour.setHsvValue(newValue); + }, + }, +}); + +/** + * Returns the dimensions of the specified SVG image. + * @param {!SVGElement} svg SVG image. + * @return {!Size} Contains width and height properties. + * @deprecated Use workspace.setCachedParentSvgSize. (2021 March 5) + * @see Blockly.WorkspaceSvg.setCachedParentSvgSize + * @alias Blockly.svgSize + */ +exports.svgSize = svgMath.svgSize; + +/** + * Size the workspace when the contents change. This also updates + * scrollbars accordingly. + * @param {!WorkspaceSvg} workspace The workspace to resize. + * @deprecated Use workspace.resizeContents. (2021 December) + * @see Blockly.WorkspaceSvg.resizeContents + * @alias Blockly.resizeSvgContents + */ +const resizeSvgContentsLocal = function(workspace) { + deprecation.warn( + 'Blockly.resizeSvgContents', 'December 2021', 'December 2022', + 'Blockly.WorkspaceSvg.resizeSvgContents'); + resizeSvgContents(workspace); +}; +exports.resizeSvgContents = resizeSvgContentsLocal; + +/** + * Copy a block or workspace comment onto the local clipboard. + * @param {!ICopyable} 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) { + deprecation.warn( + 'Blockly.copy', 'December 2021', 'December 2022', + 'Blockly.clipboard.copy'); + clipboard.copy(toCopy); +}; +exports.copy = copy; + +/** + * Paste a block or workspace comment on to the main workspace. + * @return {boolean} 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() { + deprecation.warn( + 'Blockly.paste', 'December 2021', 'December 2022', + 'Blockly.clipboard.paste'); + return !!clipboard.paste(); +}; +exports.paste = paste; + +/** + * Duplicate this block and its children, or a workspace comment. + * @param {!ICopyable} 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) { + deprecation.warn( + 'Blockly.duplicate', 'December 2021', 'December 2022', + 'Blockly.clipboard.duplicate'); + clipboard.duplicate(toDuplicate); +}; +exports.duplicate = duplicate; + +/** + * Is the given string a number (includes negative and decimals). + * @param {string} str Input string. + * @return {boolean} 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) { + deprecation.warn( + 'Blockly.isNumber', 'December 2021', 'December 2022', + 'Blockly.utils.string.isNumber'); + return utils.string.isNumber(str); +}; +exports.isNumber = isNumber; + +/** + * 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'. + * @deprecated Use Blockly.utils.colour.hueToHex(). (2021 December) + * @see Blockly.utils.colour.hueToHex + * @alias Blockly.hueToHex + */ +const hueToHex = function(hue) { + deprecation.warn( + '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_. + * @deprecated Use Blockly.browserEvents.bind(). (December 2021) + * @see Blockly.browserEvents.bind + * @alias Blockly.bindEvent_ + */ +const bindEvent_ = function(node, name, thisObject, func) { + deprecation.warn( + '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_. + * This list is emptied during the course of calling this function. + * @return {!Function} The function call. + * @deprecated Use Blockly.browserEvents.unbind(). (December 2021) + * @see browserEvents.unbind + * @alias Blockly.unbindEvent_ + */ +const unbindEvent_ = function(bindData) { + deprecation.warn( + '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_. + * @deprecated Use Blockly.browserEvents.conditionalBind(). (December 2021) + * @see browserEvents.conditionalBind + * @alias Blockly.bindEventWithChecks_ + */ +const bindEventWithChecks_ = function( + node, name, thisObject, func, opt_noCaptureIdentifier, + opt_noPreventDefault) { + deprecation.warn( + 'Blockly.bindEventWithChecks_', 'December 2021', 'December 2022', + 'Blockly.browserEvents.conditionalBind'); + return browserEvents.conditionalBind( + node, name, thisObject, func, opt_noCaptureIdentifier, + opt_noPreventDefault); +}; +exports.bindEventWithChecks_ = bindEventWithChecks_; + +// 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; + +/** + * 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; + +/** + * 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; +/** + * 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; + +// 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; +/** @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, +}; +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