diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index dc0ea8c44eb..634b59bfad4 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,6 +1,7 @@ # Contributing to Blockly Want to contribute? Great! + - First, read this page (including the small print at the end). - Second, please make pull requests against develop, not master. If your patch needs to go into master immediately, include a note in your PR. @@ -8,6 +9,7 @@ Want to contribute? Great! For more information on style guide and other details, head over to the [Blockly Developers site](https://developers.google.com/blockly/guides/modify/contributing). ### Before you contribute + Before we can use your code, you must sign the [Google Individual Contributor License Agreement](https://cla.developers.google.com/about/google-individual) (CLA), which you can do online. The CLA is necessary mainly because you own the @@ -19,22 +21,26 @@ the CLA until after you've submitted your code for review and a member has approved it, but you must do it before we can put your code into our codebase. ### Larger changes + Before you start working on a larger contribution, you should get in touch with us first through the issue tracker with your idea so that we can help out and possibly guide you. Coordinating up front makes it much easier to avoid frustration later on. ### Code reviews + All submissions, including submissions by project members, require review. We use Github pull requests for this purpose. ### Browser compatibility -We care strongly about making Blockly work on all browsers. As of 2022 we -support Edge, Chrome, Safari, and Firefox. We will not accept changes that only -work on a subset of those browsers. You can check [caniuse.com](https://caniuse.com/) + +We care strongly about making Blockly work on all browsers. As of 2022 we +support Edge, Chrome, Safari, and Firefox. We will not accept changes that only +work on a subset of those browsers. You can check [caniuse.com](https://caniuse.com/) for compatibility information. ### The small print + Contributions made by corporations are covered by a different agreement than the one above, the [Software Grant and Corporate Contributor License Agreement](https://cla.developers.google.com/about/google-corporate). diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index c0097d77065..ec2f89b57b4 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -30,7 +30,7 @@ body: value: | 1. 2. - 3. + 3. - type: textarea id: stack-trace attributes: diff --git a/.github/ISSUE_TEMPLATE/documentation.yaml b/.github/ISSUE_TEMPLATE/documentation.yaml index 93d7f501164..4ddae3b7b2e 100644 --- a/.github/ISSUE_TEMPLATE/documentation.yaml +++ b/.github/ISSUE_TEMPLATE/documentation.yaml @@ -1,4 +1,3 @@ - name: Documentation description: Report an issue with our documentation labels: 'issue: docs, issue: triage' diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index f6464078962..7c319982905 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -1,4 +1,3 @@ - name: Feature request description: Suggest an idea for this project labels: 'issue: feature request, issue: triage' diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f6e75614c6f..42f0d297aea 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,23 +5,23 @@ version: 2 updates: - - package-ecosystem: "npm" # See documentation for possible values - directory: "/" # Location of package manifests - target-branch: "develop" + - package-ecosystem: 'npm' # See documentation for possible values + directory: '/' # Location of package manifests + target-branch: 'develop' schedule: - interval: "weekly" + interval: 'weekly' commit-message: - prefix: "chore(deps)" + prefix: 'chore(deps)' labels: - - "PR: chore" - - "PR: dependencies" - - package-ecosystem: "github-actions" # See documentation for possible values - directory: "/" - target-branch: "develop" + - 'PR: chore' + - 'PR: dependencies' + - package-ecosystem: 'github-actions' # See documentation for possible values + directory: '/' + target-branch: 'develop' schedule: - interval: "weekly" + interval: 'weekly' commit-message: - prefix: "chore(deps)" + prefix: 'chore(deps)' labels: - - "PR: chore" - - "PR: dependencies" + - 'PR: chore' + - 'PR: dependencies' diff --git a/.github/release.yml b/.github/release.yml index 2c5b11486d8..a6b8dc3ef36 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -4,7 +4,7 @@ changelog: exclude: labels: - ignore-for-release - - "PR: chore" + - 'PR: chore' authors: - dependabot categories: @@ -16,17 +16,17 @@ changelog: - deprecation - title: New features ✨ labels: - - "PR: feature" + - 'PR: feature' - title: Bug fixes 🐛 labels: - - "PR: fix" + - 'PR: fix' - title: Cleanup ♻️ labels: - - "PR: docs" - - "PR: refactor" + - 'PR: docs' + - 'PR: refactor' - title: Reverted changes ⎌ labels: - - "PR: revert" + - 'PR: revert' - title: Other changes labels: - - "*" + - '*' diff --git a/.github/workflows/assign_reviewers.yml b/.github/workflows/assign_reviewers.yml index 8facfc743bb..765f0e655af 100644 --- a/.github/workflows/assign_reviewers.yml +++ b/.github/workflows/assign_reviewers.yml @@ -16,26 +16,26 @@ jobs: requested-reviewer: runs-on: ubuntu-latest steps: - - name: Assign requested reviewer - uses: actions/github-script@v6 - with: - script: | - try { - if (context.payload.pull_request === undefined) { - throw new Error("Can't get pull_request payload. " + - 'Check a request reviewer event was triggered.'); + - name: Assign requested reviewer + uses: actions/github-script@v6 + with: + script: | + try { + if (context.payload.pull_request === undefined) { + throw new Error("Can't get pull_request payload. " + + 'Check a request reviewer event was triggered.'); + } + const reviewers = context.payload.pull_request.requested_reviewers; + // Assignees takes in a list of logins rather than the + // reviewer object. + const reviewerNames = reviewers.map(reviewer => reviewer.login); + const {number:issue_number} = context.payload.pull_request; + github.rest.issues.addAssignees({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issue_number, + assignees: reviewerNames + }); + } catch (error) { + core.setFailed(error.message); } - const reviewers = context.payload.pull_request.requested_reviewers; - // Assignees takes in a list of logins rather than the - // reviewer object. - const reviewerNames = reviewers.map(reviewer => reviewer.login); - const {number:issue_number} = context.payload.pull_request; - github.rest.issues.addAssignees({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issue_number, - assignees: reviewerNames - }); - } catch (error) { - core.setFailed(error.message); - } diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b1b7062170a..484faf969a2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,67 +23,67 @@ jobs: # https://nodejs.org/en/about/releases/ steps: - - uses: actions/checkout@v3 - with: - persist-credentials: false + - uses: actions/checkout@v3 + with: + persist-credentials: false - - name: Reconfigure git to use HTTP authentication - run: > - git config --global url."https://github.com/".insteadOf - ssh://git@github.com/ + - name: Reconfigure git to use HTTP authentication + run: > + git config --global url."https://github.com/".insteadOf + ssh://git@github.com/ - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} - - name: Npm Install - run: npm install + - name: Npm Install + run: npm install - - name: Linux Test Setup - if: runner.os == 'Linux' - run: source ./tests/scripts/setup_linux_env.sh + - name: Linux Test Setup + if: runner.os == 'Linux' + run: source ./tests/scripts/setup_linux_env.sh - - name: MacOS Test Setup - if: runner.os == 'macOS' - run: source ./tests/scripts/setup_osx_env.sh + - name: MacOS Test Setup + if: runner.os == 'macOS' + run: source ./tests/scripts/setup_osx_env.sh - - name: Run - run: npm run test + - name: Run + run: npm run test - env: - CI: true + env: + CI: true lint: timeout-minutes: 5 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3 - - name: Use Node.js 20.x - uses: actions/setup-node@v3 - with: - node-version: 20.x + - name: Use Node.js 20.x + uses: actions/setup-node@v3 + with: + node-version: 20.x - - name: Npm Install - run: npm install + - name: Npm Install + run: npm install - - name: Lint - run: npm run lint + - name: Lint + run: npm run lint format: timeout-minutes: 5 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3 - - name: Use Node.js 20.x - uses: actions/setup-node@v3 - with: - node-version: 20.x + - name: Use Node.js 20.x + uses: actions/setup-node@v3 + with: + node-version: 20.x - - name: Npm Install - run: npm install + - name: Npm Install + run: npm install - - name: Check Format - run: npm run format:check + - name: Check Format + run: npm run format:check diff --git a/.github/workflows/conventional-label.yml b/.github/workflows/conventional-label.yml index a7ab2c18b2f..64289d98723 100644 --- a/.github/workflows/conventional-label.yml +++ b/.github/workflows/conventional-label.yml @@ -10,7 +10,8 @@ jobs: steps: - uses: bcoe/conventional-release-labels@v1 with: - type_labels: '{"feat": "PR: feature", "fix": "PR: fix", "breaking": "breaking + type_labels: + '{"feat": "PR: feature", "fix": "PR: fix", "breaking": "breaking change", "chore": "PR: chore", "docs": "PR: docs", "refactor": "PR: refactor", "revert": "PR: revert", "deprecate": "deprecation"}' ignored_types: '[]' diff --git a/.github/workflows/develop_freeze.yml b/.github/workflows/develop_freeze.yml index d049b9f5104..395a34434dd 100644 --- a/.github/workflows/develop_freeze.yml +++ b/.github/workflows/develop_freeze.yml @@ -23,4 +23,4 @@ jobs: uses: github-actions-up-and-running/pr-comment@f1f8ab2bf00dce6880a369ce08758a60c61d6c0b with: repo-token: ${{ secrets.GITHUB_TOKEN }} - message: "Thanks for the PR! The develop branch is currently frozen in preparation for the release so it may not be addressed until after release week." + message: 'Thanks for the PR! The develop branch is currently frozen in preparation for the release so it may not be addressed until after release week.' diff --git a/.github/workflows/tag_module_cleanup.yml b/.github/workflows/tag_module_cleanup.yml index d5555d962e3..e0186af6711 100644 --- a/.github/workflows/tag_module_cleanup.yml +++ b/.github/workflows/tag_module_cleanup.yml @@ -12,7 +12,6 @@ on: jobs: tag-module-cleanup: - # Add the type: cleanup label runs-on: ubuntu-latest steps: diff --git a/README.md b/README.md index a0ab5a8bf6b..45998fb37d1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Blockly -Google's Blockly is a library that adds a visual code editor to web and mobile apps. The Blockly editor uses interlocking, graphical blocks to represent code concepts like variables, logical expressions, loops, and more. It allows users to apply programming principles without having to worry about syntax or the intimidation of a blinking cursor on the command line. All code is free and open source. +Google's Blockly is a library that adds a visual code editor to web and mobile apps. The Blockly editor uses interlocking, graphical blocks to represent code concepts like variables, logical expressions, loops, and more. It allows users to apply programming principles without having to worry about syntax or the intimidation of a blinking cursor on the command line. All code is free and open source. ![](https://developers.google.com/blockly/images/sample.png) @@ -8,13 +8,13 @@ Google's Blockly is a library that adds a visual code editor to web and mobile a Blockly has many resources for learning how to use the library. Start at our [Google Developers Site](https://developers.google.com/blockly) to read the documentation on how to get started, configure Blockly, and integrate it into your application. The developers site also contains links to: -* [Getting Started article](https://developers.google.com/blockly/guides/get-started/web) -* [Getting Started codelab](https://blocklycodelabs.dev/codelabs/getting-started/index.html#0) -* [More codelabs](https://blocklycodelabs.dev/) -* [Demos and plugins](https://google.github.io/blockly-samples/) +- [Getting Started article](https://developers.google.com/blockly/guides/get-started/web) +- [Getting Started codelab](https://blocklycodelabs.dev/codelabs/getting-started/index.html#0) +- [More codelabs](https://blocklycodelabs.dev/) +- [Demos and plugins](https://google.github.io/blockly-samples/) Help us focus our development efforts by telling us [what you are doing with -Blockly](https://developers.google.com/blockly/registration). The questionnaire only takes +Blockly](https://developers.google.com/blockly/registration). The questionnaire only takes a few minutes and will help us better support the Blockly community. ### Installing Blockly @@ -28,8 +28,9 @@ npm install blockly For more information on installing and using Blockly, see the [Getting Started article](https://developers.google.com/blockly/guides/get-started/web). ### Getting Help -* [Report a bug](https://developers.google.com/blockly/guides/modify/contribute/write_a_good_issue) or file a feature request on GitHub -* Ask a question, or search others' questions, on our [developer forum](https://groups.google.com/forum/#!forum/blockly). You can also drop by to say hello and show us your prototypes; collectively we have a lot of experience and can offer hints which will save you time. We actively monitor the forums and typically respond to questions within 2 working days. + +- [Report a bug](https://developers.google.com/blockly/guides/modify/contribute/write_a_good_issue) or file a feature request on GitHub +- Ask a question, or search others' questions, on our [developer forum](https://groups.google.com/forum/#!forum/blockly). You can also drop by to say hello and show us your prototypes; collectively we have a lot of experience and can offer hints which will save you time. We actively monitor the forums and typically respond to questions within 2 working days. ### blockly-samples @@ -50,6 +51,7 @@ We now have a [beta release on npm](https://www.npmjs.com/package/blockly?active ```bash npm install blockly@beta ``` + As it is a beta channel, it may be less stable, and the APIs there are subject to change. ### Branches @@ -82,5 +84,5 @@ We typically triage all bugs within 2 working days, which includes adding any ap ## Good to Know -* Cross-browser Testing Platform and Open Source <3 Provided by [Sauce Labs](https://saucelabs.com) -* We test browsers using [BrowserStack](https://browserstack.com) +- Cross-browser Testing Platform and Open Source <3 Provided by [Sauce Labs](https://saucelabs.com) +- We test browsers using [BrowserStack](https://browserstack.com) diff --git a/api-extractor.json b/api-extractor.json index 649528e7e0f..6c599d255b4 100644 --- a/api-extractor.json +++ b/api-extractor.json @@ -109,7 +109,7 @@ /** * (REQUIRED) Whether to generate an API report. */ - "enabled": false, + "enabled": false /** * The filename for the API report files. It will be combined with "reportFolder" or "reportTempFolder" to produce @@ -195,7 +195,7 @@ * SUPPORTED TOKENS: , , * DEFAULT VALUE: "/dist/.d.ts" */ - "untrimmedFilePath": "/dist/_rollup.d.ts", + "untrimmedFilePath": "/dist/_rollup.d.ts" /** * Specifies the output path for a .d.ts rollup file to be generated with trimming for an "alpha" release. diff --git a/blocks/colour.ts b/blocks/colour.ts index 93298ef9c92..f48f47c4d36 100644 --- a/blocks/colour.ts +++ b/blocks/colour.ts @@ -17,7 +17,6 @@ import { } from '../core/common.js'; import '../core/field_colour.js'; - /** * A dictionary of the block definitions provided by this module. */ @@ -54,7 +53,7 @@ export const blocks = createBlockDefinitionsFromJsonArray([ { 'type': 'colour_rgb', 'message0': - '%{BKY_COLOUR_RGB_TITLE} %{BKY_COLOUR_RGB_RED} %1 %{BKY_COLOUR_RGB_GREEN} %2 %{BKY_COLOUR_RGB_BLUE} %3', + '%{BKY_COLOUR_RGB_TITLE} %{BKY_COLOUR_RGB_RED} %1 %{BKY_COLOUR_RGB_GREEN} %2 %{BKY_COLOUR_RGB_BLUE} %3', 'args0': [ { 'type': 'input_value', @@ -84,8 +83,9 @@ export const blocks = createBlockDefinitionsFromJsonArray([ // Block for blending two colours together. { 'type': 'colour_blend', - 'message0': '%{BKY_COLOUR_BLEND_TITLE} %{BKY_COLOUR_BLEND_COLOUR1} ' + - '%1 %{BKY_COLOUR_BLEND_COLOUR2} %2 %{BKY_COLOUR_BLEND_RATIO} %3', + 'message0': + '%{BKY_COLOUR_BLEND_TITLE} %{BKY_COLOUR_BLEND_COLOUR1} ' + + '%1 %{BKY_COLOUR_BLEND_COLOUR2} %2 %{BKY_COLOUR_BLEND_RATIO} %3', 'args0': [ { 'type': 'input_value', diff --git a/blocks/lists.ts b/blocks/lists.ts index b9b02701c4b..69663cef9d6 100644 --- a/blocks/lists.ts +++ b/blocks/lists.ts @@ -23,11 +23,13 @@ import type {FieldDropdown} from '../core/field_dropdown.js'; import {Msg} from '../core/msg.js'; import {Mutator} from '../core/mutator.js'; import type {Workspace} from '../core/workspace.js'; -import {createBlockDefinitionsFromJsonArray, defineBlocks} from '../core/common.js'; +import { + createBlockDefinitionsFromJsonArray, + defineBlocks, +} from '../core/common.js'; import '../core/field_dropdown.js'; import {ValueInput} from '../core/inputs/value_input.js'; - /** * A dictionary of the block definitions provided by this module. */ @@ -116,9 +118,8 @@ export const blocks = createBlockDefinitionsFromJsonArray([ }, ]); - /** Type of a 'lists_create_with' block. */ -type CreateWithBlock = Block&ListCreateWithMixin; +type CreateWithBlock = Block & ListCreateWithMixin; interface ListCreateWithMixin extends ListCreateWithMixinType { itemCount_: number; } @@ -128,22 +129,22 @@ const LISTS_CREATE_WITH = { /** * Block for creating a list with any number of elements of any type. */ - init: function(this: CreateWithBlock) { + init: function (this: CreateWithBlock) { this.setHelpUrl(Msg['LISTS_CREATE_WITH_HELPURL']); this.setStyle('list_blocks'); this.itemCount_ = 3; this.updateShape_(); this.setOutput(true, 'Array'); - this.setMutator(new Mutator( - ['lists_create_with_item'], - this as unknown as BlockSvg)); // BUG(#6905) + this.setMutator( + new Mutator(['lists_create_with_item'], this as unknown as BlockSvg) + ); // BUG(#6905) this.setTooltip(Msg['LISTS_CREATE_WITH_TOOLTIP']); }, /** * Create XML to represent list inputs. * Backwards compatible serialization implementation. */ - mutationToDom: function(this: CreateWithBlock): Element { + mutationToDom: function (this: CreateWithBlock): Element { const container = xmlUtils.createElement('mutation'); container.setAttribute('items', String(this.itemCount_)); return container; @@ -154,7 +155,7 @@ const LISTS_CREATE_WITH = { * * @param xmlElement XML storage element. */ - domToMutation: function(this: CreateWithBlock, xmlElement: Element) { + domToMutation: function (this: CreateWithBlock, xmlElement: Element) { const items = xmlElement.getAttribute('items'); if (!items) throw new TypeError('element did not have items'); this.itemCount_ = parseInt(items, 10); @@ -165,7 +166,7 @@ const LISTS_CREATE_WITH = { * * @return The state of this block, ie the item count. */ - saveExtraState: function(this: CreateWithBlock): {itemCount: number} { + saveExtraState: function (this: CreateWithBlock): {itemCount: number} { return { 'itemCount': this.itemCount_, }; @@ -175,7 +176,7 @@ const LISTS_CREATE_WITH = { * * @param state The state to apply to this block, ie the item count. */ - loadExtraState: function(this: CreateWithBlock, state: AnyDuringMigration) { + loadExtraState: function (this: CreateWithBlock, state: AnyDuringMigration) { this.itemCount_ = state['itemCount']; this.updateShape_(); }, @@ -185,32 +186,37 @@ const LISTS_CREATE_WITH = { * @param workspace Mutator's workspace. * @return Root block in mutator. */ - decompose: function(this: CreateWithBlock, workspace: Workspace): - ContainerBlock { - const containerBlock = - workspace.newBlock('lists_create_with_container') as ContainerBlock; - (containerBlock as BlockSvg).initSvg(); - let connection = containerBlock.getInput('STACK')!.connection; - for (let i = 0; i < this.itemCount_; i++) { - const itemBlock = - workspace.newBlock('lists_create_with_item') as ItemBlock; - (itemBlock as BlockSvg).initSvg(); - if (!itemBlock.previousConnection) { - throw new Error('itemBlock has no previousConnection'); - } - connection!.connect(itemBlock.previousConnection); - connection = itemBlock.nextConnection; - } - return containerBlock; - }, + decompose: function ( + this: CreateWithBlock, + workspace: Workspace + ): ContainerBlock { + const containerBlock = workspace.newBlock( + 'lists_create_with_container' + ) as ContainerBlock; + (containerBlock as BlockSvg).initSvg(); + let connection = containerBlock.getInput('STACK')!.connection; + for (let i = 0; i < this.itemCount_; i++) { + const itemBlock = workspace.newBlock( + 'lists_create_with_item' + ) as ItemBlock; + (itemBlock as BlockSvg).initSvg(); + if (!itemBlock.previousConnection) { + throw new Error('itemBlock has no previousConnection'); + } + connection!.connect(itemBlock.previousConnection); + connection = itemBlock.nextConnection; + } + return containerBlock; + }, /** * Reconfigure this block based on the mutator dialog's components. * * @param containerBlock Root block in mutator. */ - compose: function(this: CreateWithBlock, containerBlock: Block) { - let itemBlock: ItemBlock|null = - containerBlock.getInputTargetBlock('STACK') as ItemBlock; + compose: function (this: CreateWithBlock, containerBlock: Block) { + let itemBlock: ItemBlock | null = containerBlock.getInputTargetBlock( + 'STACK' + ) as ItemBlock; // Count number of inputs. const connections: Connection[] = []; while (itemBlock) { @@ -240,9 +246,10 @@ const LISTS_CREATE_WITH = { * * @param containerBlock Root block in mutator. */ - saveConnections: function(this: CreateWithBlock, containerBlock: Block) { - let itemBlock: ItemBlock|null = - containerBlock.getInputTargetBlock('STACK') as ItemBlock; + saveConnections: function (this: CreateWithBlock, containerBlock: Block) { + let itemBlock: ItemBlock | null = containerBlock.getInputTargetBlock( + 'STACK' + ) as ItemBlock; let i = 0; while (itemBlock) { if (itemBlock.isInsertionMarker()) { @@ -250,8 +257,8 @@ const LISTS_CREATE_WITH = { continue; } const input = this.getInput('ADD' + i); - itemBlock.valueConnection_ = - input?.connection!.targetConnection as Connection; + itemBlock.valueConnection_ = input?.connection! + .targetConnection as Connection; itemBlock = itemBlock.getNextBlock() as ItemBlock | null; i++; } @@ -259,12 +266,13 @@ const LISTS_CREATE_WITH = { /** * Modify this block to have the correct number of inputs. */ - updateShape_: function(this: CreateWithBlock) { + updateShape_: function (this: CreateWithBlock) { if (this.itemCount_ && this.getInput('EMPTY')) { this.removeInput('EMPTY'); } else if (!this.itemCount_ && !this.getInput('EMPTY')) { this.appendDummyInput('EMPTY').appendField( - Msg['LISTS_CREATE_EMPTY_TITLE']); + Msg['LISTS_CREATE_EMPTY_TITLE'] + ); } // Add new inputs. for (let i = 0; i < this.itemCount_; i++) { @@ -284,7 +292,7 @@ const LISTS_CREATE_WITH = { blocks['lists_create_with'] = LISTS_CREATE_WITH; /** Type for a 'lists_create_with_container' block. */ -type ContainerBlock = Block&ContainerMutator; +type ContainerBlock = Block & ContainerMutator; interface ContainerMutator extends ContainerMutatorType {} type ContainerMutatorType = typeof LISTS_CREATE_WITH_CONTAINER; @@ -292,10 +300,11 @@ const LISTS_CREATE_WITH_CONTAINER = { /** * Mutator block for list container. */ - init: function(this: ContainerBlock) { + init: function (this: ContainerBlock) { this.setStyle('list_blocks'); this.appendDummyInput().appendField( - Msg['LISTS_CREATE_WITH_CONTAINER_TITLE_ADD']); + Msg['LISTS_CREATE_WITH_CONTAINER_TITLE_ADD'] + ); this.appendStatementInput('STACK'); this.setTooltip(Msg['LISTS_CREATE_WITH_CONTAINER_TOOLTIP']); this.contextMenu = false; @@ -304,7 +313,7 @@ const LISTS_CREATE_WITH_CONTAINER = { blocks['lists_create_with_container'] = LISTS_CREATE_WITH_CONTAINER; /** Type for a 'lists_create_with_item' block. */ -type ItemBlock = Block&ItemMutator; +type ItemBlock = Block & ItemMutator; interface ItemMutator extends ItemMutatorType { valueConnection_?: Connection; } @@ -314,7 +323,7 @@ const LISTS_CREATE_WITH_ITEM = { /** * Mutator block for adding items. */ - init: function(this: ItemBlock) { + init: function (this: ItemBlock) { this.setStyle('list_blocks'); this.appendDummyInput().appendField(Msg['LISTS_CREATE_WITH_ITEM_TITLE']); this.setPreviousStatement(true); @@ -326,7 +335,7 @@ const LISTS_CREATE_WITH_ITEM = { blocks['lists_create_with_item'] = LISTS_CREATE_WITH_ITEM; /** Type for a 'lists_indexOf' block. */ -type IndexOfBlock = Block&IndexOfMutator; +type IndexOfBlock = Block & IndexOfMutator; interface IndexOfMutator extends IndexOfMutatorType {} type IndexOfMutatorType = typeof LISTS_INDEXOF; @@ -334,7 +343,7 @@ const LISTS_INDEXOF = { /** * Block for finding an item in the list. */ - init: function(this: IndexOfBlock) { + init: function (this: IndexOfBlock) { const OPERATORS = [ [Msg['LISTS_INDEX_OF_FIRST'], 'FIRST'], [Msg['LISTS_INDEX_OF_LAST'], 'LAST'], @@ -342,8 +351,9 @@ const LISTS_INDEXOF = { this.setHelpUrl(Msg['LISTS_INDEX_OF_HELPURL']); this.setStyle('list_blocks'); this.setOutput(true, 'Number'); - this.appendValueInput('VALUE').setCheck('Array').appendField( - Msg['LISTS_INDEX_OF_INPUT_IN_LIST']); + this.appendValueInput('VALUE') + .setCheck('Array') + .appendField(Msg['LISTS_INDEX_OF_INPUT_IN_LIST']); const operatorsDropdown = fieldRegistry.fromJson({ type: 'field_dropdown', options: OPERATORS, @@ -362,7 +372,7 @@ const LISTS_INDEXOF = { blocks['lists_indexOf'] = LISTS_INDEXOF; /** Type for a 'lists_getIndex' block. */ -type GetIndexBlock = Block&GetIndexMutator; +type GetIndexBlock = Block & GetIndexMutator; interface GetIndexMutator extends GetIndexMutatorType { WHERE_OPTIONS: Array<[string, string]>; } @@ -372,7 +382,7 @@ const LISTS_GETINDEX = { /** * Block for getting element at index. */ - init: function(this: GetIndexBlock) { + init: function (this: GetIndexBlock) { const MODE = [ [Msg['LISTS_GET_INDEX_GET'], 'GET'], [Msg['LISTS_GET_INDEX_GET_REMOVE'], 'GET_REMOVE'], @@ -392,18 +402,19 @@ const LISTS_GETINDEX = { options: MODE, }) as FieldDropdown; modeMenu.setValidator( - /** @param value The input value. */ - function(this: FieldDropdown, value: string) { - const isStatement = (value === 'REMOVE'); - (this.getSourceBlock() as GetIndexBlock) - .updateStatement_(isStatement); - return undefined; - }); - this.appendValueInput('VALUE').setCheck('Array').appendField( - Msg['LISTS_GET_INDEX_INPUT_IN_LIST']); + /** @param value The input value. */ + function (this: FieldDropdown, value: string) { + const isStatement = value === 'REMOVE'; + (this.getSourceBlock() as GetIndexBlock).updateStatement_(isStatement); + return undefined; + } + ); + this.appendValueInput('VALUE') + .setCheck('Array') + .appendField(Msg['LISTS_GET_INDEX_INPUT_IN_LIST']); this.appendDummyInput() - .appendField(modeMenu, 'MODE') - .appendField('', 'SPACE'); + .appendField(modeMenu, 'MODE') + .appendField('', 'SPACE'); this.appendDummyInput('AT'); if (Msg['LISTS_GET_INDEX_TAIL']) { this.appendDummyInput('TAIL').appendField(Msg['LISTS_GET_INDEX_TAIL']); @@ -474,7 +485,7 @@ const LISTS_GETINDEX = { * * @return XML storage element. */ - mutationToDom: function(this: GetIndexBlock): Element { + mutationToDom: function (this: GetIndexBlock): Element { const container = xmlUtils.createElement('mutation'); const isStatement = !this.outputConnection; container.setAttribute('statement', String(isStatement)); @@ -487,12 +498,12 @@ const LISTS_GETINDEX = { * * @param xmlElement XML storage element. */ - domToMutation: function(this: GetIndexBlock, xmlElement: Element) { + domToMutation: function (this: GetIndexBlock, xmlElement: Element) { // Note: Until January 2013 this block did not have mutations, // so 'statement' defaults to false and 'at' defaults to true. - const isStatement = (xmlElement.getAttribute('statement') === 'true'); + const isStatement = xmlElement.getAttribute('statement') === 'true'; this.updateStatement_(isStatement); - const isAt = (xmlElement.getAttribute('at') !== 'false'); + const isAt = xmlElement.getAttribute('at') !== 'false'; this.updateAt_(isAt); }, /** @@ -501,9 +512,9 @@ const LISTS_GETINDEX = { * * @return The state of this block, ie whether it's a statement. */ - saveExtraState: function(this: GetIndexBlock): ({ - isStatement: boolean - } | null) { + saveExtraState: function (this: GetIndexBlock): { + isStatement: boolean; + } | null { if (!this.outputConnection) { return { isStatement: true, @@ -518,7 +529,7 @@ const LISTS_GETINDEX = { * @param state The state to apply to this block, ie whether it's a * statement. */ - loadExtraState: function(this: GetIndexBlock, state: AnyDuringMigration) { + loadExtraState: function (this: GetIndexBlock, state: AnyDuringMigration) { if (state['isStatement']) { this.updateStatement_(true); } else if (typeof state === 'string') { @@ -533,7 +544,7 @@ const LISTS_GETINDEX = { * @param newStatement True if the block should be a statement. * False if the block should be a value. */ - updateStatement_: function(this: GetIndexBlock, newStatement: boolean) { + updateStatement_: function (this: GetIndexBlock, newStatement: boolean) { const oldStatement = !this.outputConnection; if (newStatement !== oldStatement) { // TODO(#6920): The .unplug only has one parameter. @@ -554,7 +565,7 @@ const LISTS_GETINDEX = { * * @param isAt True if the input should exist. */ - updateAt_: function(this: GetIndexBlock, isAt: boolean) { + updateAt_: function (this: GetIndexBlock, isAt: boolean) { // Destroy old 'AT' and 'ORDINAL' inputs. this.removeInput('AT'); this.removeInput('ORDINAL', true); @@ -563,7 +574,8 @@ const LISTS_GETINDEX = { this.appendValueInput('AT').setCheck('Number'); if (Msg['ORDINAL_NUMBER_SUFFIX']) { this.appendDummyInput('ORDINAL').appendField( - Msg['ORDINAL_NUMBER_SUFFIX']); + Msg['ORDINAL_NUMBER_SUFFIX'] + ); } } else { this.appendDummyInput('AT'); @@ -573,24 +585,25 @@ const LISTS_GETINDEX = { options: this.WHERE_OPTIONS, }) as FieldDropdown; menu.setValidator( - /** - * @param value The input value. - * @return Null if the field has been replaced; otherwise undefined. - */ - function(this: FieldDropdown, value: string) { - const newAt = (value === 'FROM_START') || (value === 'FROM_END'); - // The 'isAt' variable is available due to this function being a - // closure. - if (newAt !== isAt) { - const block = this.getSourceBlock() as GetIndexBlock; - block.updateAt_(newAt); - // This menu has been destroyed and replaced. Update the - // replacement. - block.setFieldValue(value, 'WHERE'); - return null; - } - return undefined; - }); + /** + * @param value The input value. + * @return Null if the field has been replaced; otherwise undefined. + */ + function (this: FieldDropdown, value: string) { + const newAt = value === 'FROM_START' || value === 'FROM_END'; + // The 'isAt' variable is available due to this function being a + // closure. + if (newAt !== isAt) { + const block = this.getSourceBlock() as GetIndexBlock; + block.updateAt_(newAt); + // This menu has been destroyed and replaced. Update the + // replacement. + block.setFieldValue(value, 'WHERE'); + return null; + } + return undefined; + } + ); this.getInput('AT')!.appendField(menu, 'WHERE'); if (Msg['LISTS_GET_INDEX_TAIL']) { this.moveInputBefore('TAIL', null); @@ -600,7 +613,7 @@ const LISTS_GETINDEX = { blocks['lists_getIndex'] = LISTS_GETINDEX; /** Type for a 'lists_setIndex' block. */ -type SetIndexBlock = Block&SetIndexMutator; +type SetIndexBlock = Block & SetIndexMutator; interface SetIndexMutator extends SetIndexMutatorType { WHERE_OPTIONS: Array<[string, string]>; } @@ -610,7 +623,7 @@ const LISTS_SETINDEX = { /** * Block for setting the element at index. */ - init: function(this: SetIndexBlock) { + init: function (this: SetIndexBlock) { const MODE = [ [Msg['LISTS_SET_INDEX_SET'], 'SET'], [Msg['LISTS_SET_INDEX_INSERT'], 'INSERT'], @@ -624,15 +637,16 @@ const LISTS_SETINDEX = { ]; this.setHelpUrl(Msg['LISTS_SET_INDEX_HELPURL']); this.setStyle('list_blocks'); - this.appendValueInput('LIST').setCheck('Array').appendField( - Msg['LISTS_SET_INDEX_INPUT_IN_LIST']); + this.appendValueInput('LIST') + .setCheck('Array') + .appendField(Msg['LISTS_SET_INDEX_INPUT_IN_LIST']); const operationDropdown = fieldRegistry.fromJson({ type: 'field_dropdown', options: MODE, }) as FieldDropdown; this.appendDummyInput() - .appendField(operationDropdown, 'MODE') - .appendField('', 'SPACE'); + .appendField(operationDropdown, 'MODE') + .appendField('', 'SPACE'); this.appendDummyInput('AT'); this.appendValueInput('TO').appendField(Msg['LISTS_SET_INDEX_INPUT_TO']); this.setInputsInline(true); @@ -688,7 +702,7 @@ const LISTS_SETINDEX = { * * @return XML storage element. */ - mutationToDom: function(this: SetIndexBlock): Element { + mutationToDom: function (this: SetIndexBlock): Element { const container = xmlUtils.createElement('mutation'); const isAt = this.getInput('AT') instanceof ValueInput; container.setAttribute('at', String(isAt)); @@ -699,10 +713,10 @@ const LISTS_SETINDEX = { * * @param xmlElement XML storage element. */ - domToMutation: function(this: SetIndexBlock, xmlElement: Element) { + domToMutation: function (this: SetIndexBlock, xmlElement: Element) { // Note: Until January 2013 this block did not have mutations, // so 'at' defaults to true. - const isAt = (xmlElement.getAttribute('at') !== 'false'); + const isAt = xmlElement.getAttribute('at') !== 'false'; this.updateAt_(isAt); }, @@ -714,7 +728,7 @@ const LISTS_SETINDEX = { * * @return The state of this block. */ - saveExtraState: function(this: SetIndexBlock): null { + saveExtraState: function (this: SetIndexBlock): null { return null; }, @@ -723,14 +737,14 @@ const LISTS_SETINDEX = { * No extra state is needed or expected as it is already encoded in the * dropdown values. */ - loadExtraState: function(this: SetIndexBlock) {}, + loadExtraState: function (this: SetIndexBlock) {}, /** * Create or delete an input for the numeric index. * * @param isAt True if the input should exist. */ - updateAt_: function(this: SetIndexBlock, isAt: boolean) { + updateAt_: function (this: SetIndexBlock, isAt: boolean) { // Destroy old 'AT' and 'ORDINAL' input. this.removeInput('AT'); this.removeInput('ORDINAL', true); @@ -739,7 +753,8 @@ const LISTS_SETINDEX = { this.appendValueInput('AT').setCheck('Number'); if (Msg['ORDINAL_NUMBER_SUFFIX']) { this.appendDummyInput('ORDINAL').appendField( - Msg['ORDINAL_NUMBER_SUFFIX']); + Msg['ORDINAL_NUMBER_SUFFIX'] + ); } } else { this.appendDummyInput('AT'); @@ -749,24 +764,25 @@ const LISTS_SETINDEX = { options: this.WHERE_OPTIONS, }) as FieldDropdown; menu.setValidator( - /** - * @param value The input value. - * @return Null if the field has been replaced; otherwise undefined. - */ - function(this: FieldDropdown, value: string) { - const newAt = (value === 'FROM_START') || (value === 'FROM_END'); - // The 'isAt' variable is available due to this function being a - // closure. - if (newAt !== isAt) { - const block = this.getSourceBlock() as SetIndexBlock; - block.updateAt_(newAt); - // This menu has been destroyed and replaced. Update the - // replacement. - block.setFieldValue(value, 'WHERE'); - return null; - } - return undefined; - }); + /** + * @param value The input value. + * @return Null if the field has been replaced; otherwise undefined. + */ + function (this: FieldDropdown, value: string) { + const newAt = value === 'FROM_START' || value === 'FROM_END'; + // The 'isAt' variable is available due to this function being a + // closure. + if (newAt !== isAt) { + const block = this.getSourceBlock() as SetIndexBlock; + block.updateAt_(newAt); + // This menu has been destroyed and replaced. Update the + // replacement. + block.setFieldValue(value, 'WHERE'); + return null; + } + return undefined; + } + ); this.moveInputBefore('AT', 'TO'); if (this.getInput('ORDINAL')) { this.moveInputBefore('ORDINAL', 'TO'); @@ -778,7 +794,7 @@ const LISTS_SETINDEX = { blocks['lists_setIndex'] = LISTS_SETINDEX; /** Type for a 'lists_getSublist' block. */ -type GetSublistBlock = Block&GetSublistMutator; +type GetSublistBlock = Block & GetSublistMutator; interface GetSublistMutator extends GetSublistMutatorType { WHERE_OPTIONS_1: Array<[string, string]>; WHERE_OPTIONS_2: Array<[string, string]>; @@ -789,7 +805,7 @@ const LISTS_GETSUBLIST = { /** * Block for getting sublist. */ - init: function(this: GetSublistBlock) { + init: function (this: GetSublistBlock) { this['WHERE_OPTIONS_1'] = [ [Msg['LISTS_GET_SUBLIST_START_FROM_START'], 'FROM_START'], [Msg['LISTS_GET_SUBLIST_START_FROM_END'], 'FROM_END'], @@ -802,8 +818,9 @@ const LISTS_GETSUBLIST = { ]; this.setHelpUrl(Msg['LISTS_GET_SUBLIST_HELPURL']); this.setStyle('list_blocks'); - this.appendValueInput('LIST').setCheck('Array').appendField( - Msg['LISTS_GET_SUBLIST_INPUT_IN_LIST']); + this.appendValueInput('LIST') + .setCheck('Array') + .appendField(Msg['LISTS_GET_SUBLIST_INPUT_IN_LIST']); this.appendDummyInput('AT1'); this.appendDummyInput('AT2'); if (Msg['LISTS_GET_SUBLIST_TAIL']) { @@ -820,7 +837,7 @@ const LISTS_GETSUBLIST = { * * @return XML storage element. */ - mutationToDom: function(this: GetSublistBlock): Element { + mutationToDom: function (this: GetSublistBlock): Element { const container = xmlUtils.createElement('mutation'); const isAt1 = this.getInput('AT1') instanceof ValueInput; container.setAttribute('at1', String(isAt1)); @@ -833,9 +850,9 @@ const LISTS_GETSUBLIST = { * * @param xmlElement XML storage element. */ - domToMutation: function(this: GetSublistBlock, xmlElement: Element) { - const isAt1 = (xmlElement.getAttribute('at1') === 'true'); - const isAt2 = (xmlElement.getAttribute('at2') === 'true'); + domToMutation: function (this: GetSublistBlock, xmlElement: Element) { + const isAt1 = xmlElement.getAttribute('at1') === 'true'; + const isAt2 = xmlElement.getAttribute('at2') === 'true'; this.updateAt_(1, isAt1); this.updateAt_(2, isAt2); }, @@ -848,7 +865,7 @@ const LISTS_GETSUBLIST = { * * @return The state of this block. */ - saveExtraState: function(this: GetSublistBlock): null { + saveExtraState: function (this: GetSublistBlock): null { return null; }, @@ -857,7 +874,7 @@ const LISTS_GETSUBLIST = { * No extra state is needed or expected as it is already encoded in the * dropdown values. */ - loadExtraState: function(this: GetSublistBlock) {}, + loadExtraState: function (this: GetSublistBlock) {}, /** * Create or delete an input for a numeric index. @@ -866,7 +883,7 @@ const LISTS_GETSUBLIST = { * @param n Specify first or second input (1 or 2). * @param isAt True if the input should exist. */ - updateAt_: function(this: GetSublistBlock, n: 1|2, isAt: boolean) { + updateAt_: function (this: GetSublistBlock, n: 1 | 2, isAt: boolean) { // Create or delete an input for the numeric index. // Destroy old 'AT' and 'ORDINAL' inputs. this.removeInput('AT' + n); @@ -875,8 +892,9 @@ const LISTS_GETSUBLIST = { if (isAt) { this.appendValueInput('AT' + n).setCheck('Number'); if (Msg['ORDINAL_NUMBER_SUFFIX']) { - this.appendDummyInput('ORDINAL' + n) - .appendField(Msg['ORDINAL_NUMBER_SUFFIX']); + this.appendDummyInput('ORDINAL' + n).appendField( + Msg['ORDINAL_NUMBER_SUFFIX'] + ); } } else { this.appendDummyInput('AT' + n); @@ -887,23 +905,24 @@ const LISTS_GETSUBLIST = { this[('WHERE_OPTIONS_' + n) as 'WHERE_OPTIONS_1' | 'WHERE_OPTIONS_2'], }) as FieldDropdown; menu.setValidator( - /** - * @param value The input value. - * @return Null if the field has been replaced; otherwise undefined. - */ - function(this: FieldDropdown, value: string) { - const newAt = (value === 'FROM_START') || (value === 'FROM_END'); - // The 'isAt' variable is available due to this function being a - // closure. - if (newAt !== isAt) { - const block = this.getSourceBlock() as GetSublistBlock; - block.updateAt_(n, newAt); - // This menu has been destroyed and replaced. - // Update the replacement. - block.setFieldValue(value, 'WHERE' + n); - return null; - } - }); + /** + * @param value The input value. + * @return Null if the field has been replaced; otherwise undefined. + */ + function (this: FieldDropdown, value: string) { + const newAt = value === 'FROM_START' || value === 'FROM_END'; + // The 'isAt' variable is available due to this function being a + // closure. + if (newAt !== isAt) { + const block = this.getSourceBlock() as GetSublistBlock; + block.updateAt_(n, newAt); + // This menu has been destroyed and replaced. + // Update the replacement. + block.setFieldValue(value, 'WHERE' + n); + return null; + } + } + ); this.getInput('AT' + n)!.appendField(menu, 'WHERE' + n); if (n === 1) { this.moveInputBefore('AT1', 'AT2'); @@ -918,13 +937,13 @@ const LISTS_GETSUBLIST = { }; blocks['lists_getSublist'] = LISTS_GETSUBLIST; -type SortBlock = Block|typeof blocks['lists_sort']; +type SortBlock = Block | (typeof blocks)['lists_sort']; blocks['lists_sort'] = { /** * Block for sorting a list. */ - init: function(this: SortBlock) { + init: function (this: SortBlock) { this.jsonInit({ 'message0': '%{BKY_LISTS_SORT_TITLE}', 'args0': [ @@ -959,7 +978,7 @@ blocks['lists_sort'] = { }, }; -type SplitBlock = Block|typeof blocks['lists_split']; +type SplitBlock = Block | (typeof blocks)['lists_split']; blocks['lists_split'] = { /** @@ -979,10 +998,12 @@ blocks['lists_split'] = { }); this.setHelpUrl(Msg['LISTS_SPLIT_HELPURL']); this.setStyle('list_blocks'); - this.appendValueInput('INPUT').setCheck('String').appendField( - dropdown, 'MODE'); - this.appendValueInput('DELIM').setCheck('String').appendField( - Msg['LISTS_SPLIT_WITH_DELIMITER']); + this.appendValueInput('INPUT') + .setCheck('String') + .appendField(dropdown, 'MODE'); + this.appendValueInput('DELIM') + .setCheck('String') + .appendField(Msg['LISTS_SPLIT_WITH_DELIMITER']); this.setInputsInline(true); this.setOutput(true, 'Array'); this.setTooltip(() => { @@ -1000,7 +1021,7 @@ blocks['lists_split'] = { * * @param newMode Either 'SPLIT' or 'JOIN'. */ - updateType_: function(this: SplitBlock, newMode: string) { + updateType_: function (this: SplitBlock, newMode: string) { const mode = this.getFieldValue('MODE'); if (mode !== newMode) { const inputConnection = this.getInput('INPUT')!.connection; @@ -1029,7 +1050,7 @@ blocks['lists_split'] = { * * @return XML storage element. */ - mutationToDom: function(this: SplitBlock): Element { + mutationToDom: function (this: SplitBlock): Element { const container = xmlUtils.createElement('mutation'); container.setAttribute('mode', this.getFieldValue('MODE')); return container; @@ -1039,7 +1060,7 @@ blocks['lists_split'] = { * * @param xmlElement XML storage element. */ - domToMutation: function(this: SplitBlock, xmlElement: Element) { + domToMutation: function (this: SplitBlock, xmlElement: Element) { this.updateType_(xmlElement.getAttribute('mode')); }, @@ -1051,7 +1072,7 @@ blocks['lists_split'] = { * * @return The state of this block. */ - saveExtraState: function(this: SplitBlock): null { + saveExtraState: function (this: SplitBlock): null { return null; }, @@ -1060,7 +1081,7 @@ blocks['lists_split'] = { * No extra state is needed or expected as it is already encoded in the * dropdown values. */ - loadExtraState: function(this: SplitBlock) {}, + loadExtraState: function (this: SplitBlock) {}, }; // Register provided blocks. diff --git a/blocks/loops.ts b/blocks/loops.ts index fa2670c73db..0495033c2ea 100644 --- a/blocks/loops.ts +++ b/blocks/loops.ts @@ -14,13 +14,19 @@ goog.declareModuleId('Blockly.libraryBlocks.loops'); import type {Abstract as AbstractEvent} from '../core/events/events_abstract.js'; import type {Block} from '../core/block.js'; import * as ContextMenu from '../core/contextmenu.js'; -import type {ContextMenuOption, LegacyContextMenuOption} from '../core/contextmenu_registry.js'; +import type { + ContextMenuOption, + LegacyContextMenuOption, +} from '../core/contextmenu_registry.js'; import * as Events from '../core/events/events.js'; import * as Extensions from '../core/extensions.js'; import * as Variables from '../core/variables.js'; import * as xmlUtils from '../core/utils/xml.js'; import {Msg} from '../core/msg.js'; -import {createBlockDefinitionsFromJsonArray, defineBlocks} from '../core/common.js'; +import { + createBlockDefinitionsFromJsonArray, + defineBlocks, +} from '../core/common.js'; import '../core/field_dropdown.js'; import '../core/field_label.js'; import '../core/field_number.js'; @@ -29,7 +35,6 @@ import '../core/warning.js'; import {FieldVariable} from '../core/field_variable.js'; import {WorkspaceSvg} from '../core/workspace_svg.js'; - /** * A dictionary of the block definitions provided by this module. */ @@ -38,16 +43,20 @@ export const blocks = createBlockDefinitionsFromJsonArray([ { 'type': 'controls_repeat_ext', 'message0': '%{BKY_CONTROLS_REPEAT_TITLE}', - 'args0': [{ - 'type': 'input_value', - 'name': 'TIMES', - 'check': 'Number', - }], + 'args0': [ + { + 'type': 'input_value', + 'name': 'TIMES', + 'check': 'Number', + }, + ], 'message1': '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1', - 'args1': [{ - 'type': 'input_statement', - 'name': 'DO', - }], + 'args1': [ + { + 'type': 'input_statement', + 'name': 'DO', + }, + ], 'previousStatement': null, 'nextStatement': null, 'style': 'loop_blocks', @@ -59,18 +68,22 @@ export const blocks = createBlockDefinitionsFromJsonArray([ { 'type': 'controls_repeat', 'message0': '%{BKY_CONTROLS_REPEAT_TITLE}', - 'args0': [{ - 'type': 'field_number', - 'name': 'TIMES', - 'value': 10, - 'min': 0, - 'precision': 1, - }], + 'args0': [ + { + 'type': 'field_number', + 'name': 'TIMES', + 'value': 10, + 'min': 0, + 'precision': 1, + }, + ], 'message1': '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1', - 'args1': [{ - 'type': 'input_statement', - 'name': 'DO', - }], + 'args1': [ + { + 'type': 'input_statement', + 'name': 'DO', + }, + ], 'previousStatement': null, 'nextStatement': null, 'style': 'loop_blocks', @@ -97,10 +110,12 @@ export const blocks = createBlockDefinitionsFromJsonArray([ }, ], 'message1': '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1', - 'args1': [{ - 'type': 'input_statement', - 'name': 'DO', - }], + 'args1': [ + { + 'type': 'input_statement', + 'name': 'DO', + }, + ], 'previousStatement': null, 'nextStatement': null, 'style': 'loop_blocks', @@ -137,19 +152,18 @@ export const blocks = createBlockDefinitionsFromJsonArray([ }, ], 'message1': '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1', - 'args1': [{ - 'type': 'input_statement', - 'name': 'DO', - }], + 'args1': [ + { + 'type': 'input_statement', + 'name': 'DO', + }, + ], 'inputsInline': true, 'previousStatement': null, 'nextStatement': null, 'style': 'loop_blocks', 'helpUrl': '%{BKY_CONTROLS_FOR_HELPURL}', - 'extensions': [ - 'contextMenu_newGetVariableBlock', - 'controls_for_tooltip', - ], + 'extensions': ['contextMenu_newGetVariableBlock', 'controls_for_tooltip'], }, // Block for 'for each' loop. { @@ -168,10 +182,12 @@ export const blocks = createBlockDefinitionsFromJsonArray([ }, ], 'message1': '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1', - 'args1': [{ - 'type': 'input_statement', - 'name': 'DO', - }], + 'args1': [ + { + 'type': 'input_statement', + 'name': 'DO', + }, + ], 'previousStatement': null, 'nextStatement': null, 'style': 'loop_blocks', @@ -185,22 +201,21 @@ export const blocks = createBlockDefinitionsFromJsonArray([ { 'type': 'controls_flow_statements', 'message0': '%1', - 'args0': [{ - 'type': 'field_dropdown', - 'name': 'FLOW', - 'options': [ - ['%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK}', 'BREAK'], - ['%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE}', 'CONTINUE'], - ], - }], + 'args0': [ + { + 'type': 'field_dropdown', + 'name': 'FLOW', + 'options': [ + ['%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK}', 'BREAK'], + ['%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE}', 'CONTINUE'], + ], + }, + ], 'previousStatement': null, 'style': 'loop_blocks', 'helpUrl': '%{BKY_CONTROLS_FLOW_STATEMENTS_HELPURL}', 'suppressPrefixSuffix': true, - 'extensions': [ - 'controls_flow_tooltip', - 'controls_flow_in_loop_check', - ], + 'extensions': ['controls_flow_tooltip', 'controls_flow_in_loop_check'], }, ]); @@ -215,8 +230,9 @@ const WHILE_UNTIL_TOOLTIPS = { }; Extensions.register( - 'controls_whileUntil_tooltip', - Extensions.buildTooltipForDropdown('MODE', WHILE_UNTIL_TOOLTIPS)); + 'controls_whileUntil_tooltip', + Extensions.buildTooltipForDropdown('MODE', WHILE_UNTIL_TOOLTIPS) +); /** * Tooltips for the 'controls_flow_statements' block, keyed by FLOW value. @@ -229,14 +245,15 @@ const BREAK_CONTINUE_TOOLTIPS = { }; Extensions.register( - 'controls_flow_tooltip', - Extensions.buildTooltipForDropdown('FLOW', BREAK_CONTINUE_TOOLTIPS)); + 'controls_flow_tooltip', + Extensions.buildTooltipForDropdown('FLOW', BREAK_CONTINUE_TOOLTIPS) +); /** Type of a block that has CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN */ -type CustomContextMenuBlock = Block&CustomContextMenuMixin; +type CustomContextMenuBlock = Block & CustomContextMenuMixin; interface CustomContextMenuMixin extends CustomContextMenuMixinType {} type CustomContextMenuMixinType = - typeof CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN; + typeof CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN; /** * Mixin to add a context menu item to create a 'variables_get' block. @@ -249,9 +266,10 @@ const CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN = { * * @param options List of menu options to add to. */ - customContextMenu: function( - this: CustomContextMenuBlock, - options: Array) { + customContextMenu: function ( + this: CustomContextMenuBlock, + options: Array + ) { if (this.isInFlyout) { return; } @@ -267,24 +285,26 @@ const CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN = { options.push({ enabled: true, text: Msg['VARIABLES_SET_CREATE_GET'].replace('%1', varName), - callback: ContextMenu.callbackFactory(this, xmlBlock) + callback: ContextMenu.callbackFactory(this, xmlBlock), }); } }, }; Extensions.registerMixin( - 'contextMenu_newGetVariableBlock', - CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN); + 'contextMenu_newGetVariableBlock', + CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN +); Extensions.register( - 'controls_for_tooltip', - Extensions.buildTooltipWithFieldText('%{BKY_CONTROLS_FOR_TOOLTIP}', 'VAR')); + 'controls_for_tooltip', + Extensions.buildTooltipWithFieldText('%{BKY_CONTROLS_FOR_TOOLTIP}', 'VAR') +); Extensions.register( - 'controls_forEach_tooltip', - Extensions.buildTooltipWithFieldText( - '%{BKY_CONTROLS_FOREACH_TOOLTIP}', 'VAR')); + 'controls_forEach_tooltip', + Extensions.buildTooltipWithFieldText('%{BKY_CONTROLS_FOREACH_TOOLTIP}', 'VAR') +); /** * List of block types that are loops and thus do not need warnings. @@ -310,7 +330,7 @@ export const loopTypes: Set = new Set([ ]); /** Type of a block that has CONTROL_FLOW_IN_LOOP_CHECK_MIXIN */ -type ControlFlowInLoopBlock = Block&ControlFlowInLoopMixin; +type ControlFlowInLoopBlock = Block & ControlFlowInLoopMixin; interface ControlFlowInLoopMixin extends ControlFlowInLoopMixinType {} type ControlFlowInLoopMixinType = typeof CONTROL_FLOW_IN_LOOP_CHECK_MIXIN; @@ -340,7 +360,7 @@ const CONTROL_FLOW_IN_LOOP_CHECK_MIXIN = { * Called whenever anything on the workspace changes. * Add warning if this flow block is not nested inside a loop. */ - onchange: function(this: ControlFlowInLoopBlock, e: AbstractEvent) { + onchange: function (this: ControlFlowInLoopBlock, e: AbstractEvent) { const ws = this.workspace as WorkspaceSvg; // Don't change state if: // * It's at the start of a drag. @@ -350,7 +370,8 @@ const CONTROL_FLOW_IN_LOOP_CHECK_MIXIN = { } const enabled = !!this.getSurroundLoop(); this.setWarningText( - enabled ? null : Msg['CONTROLS_FLOW_STATEMENTS_WARNING']); + enabled ? null : Msg['CONTROLS_FLOW_STATEMENTS_WARNING'] + ); if (!this.isInFlyout) { const group = Events.getGroup(); // Makes it so the move and the disable event get undone together. @@ -362,7 +383,9 @@ const CONTROL_FLOW_IN_LOOP_CHECK_MIXIN = { }; Extensions.registerMixin( - 'controls_flow_in_loop_check', CONTROL_FLOW_IN_LOOP_CHECK_MIXIN); + 'controls_flow_in_loop_check', + CONTROL_FLOW_IN_LOOP_CHECK_MIXIN +); // Register provided blocks. defineBlocks(blocks); diff --git a/blocks/math.ts b/blocks/math.ts index cf3494178ed..ff4fd028906 100644 --- a/blocks/math.ts +++ b/blocks/math.ts @@ -24,7 +24,6 @@ import '../core/field_label.js'; import '../core/field_number.js'; import '../core/field_variable.js'; - /** * A dictionary of the block definitions provided by this module. */ @@ -33,11 +32,13 @@ export const blocks = createBlockDefinitionsFromJsonArray([ { 'type': 'math_number', 'message0': '%1', - 'args0': [{ - 'type': 'field_number', - 'name': 'NUM', - 'value': 0, - }], + 'args0': [ + { + 'type': 'field_number', + 'name': 'NUM', + 'value': 0, + }, + ], 'output': 'Number', 'helpUrl': '%{BKY_MATH_NUMBER_HELPURL}', 'style': 'math_blocks', @@ -429,13 +430,13 @@ const TOOLTIPS_BY_OP = { }; Extensions.register( - 'math_op_tooltip', - Extensions.buildTooltipForDropdown('OP', TOOLTIPS_BY_OP)); + 'math_op_tooltip', + Extensions.buildTooltipForDropdown('OP', TOOLTIPS_BY_OP) +); /** Type of a block that has IS_DIVISBLEBY_MUTATOR_MIXIN */ -type DivisiblebyBlock = Block&DivisiblebyMixin; +type DivisiblebyBlock = Block & DivisiblebyMixin; interface DivisiblebyMixin extends DivisiblebyMixinType {} -; type DivisiblebyMixinType = typeof IS_DIVISIBLEBY_MUTATOR_MIXIN; /** @@ -453,9 +454,9 @@ const IS_DIVISIBLEBY_MUTATOR_MIXIN = { * * @returns XML storage element. */ - mutationToDom: function(this: DivisiblebyBlock): Element { + mutationToDom: function (this: DivisiblebyBlock): Element { const container = xmlUtils.createElement('mutation'); - const divisorInput = (this.getFieldValue('PROPERTY') === 'DIVISIBLE_BY'); + const divisorInput = this.getFieldValue('PROPERTY') === 'DIVISIBLE_BY'; container.setAttribute('divisor_input', String(divisorInput)); return container; }, @@ -465,8 +466,8 @@ const IS_DIVISIBLEBY_MUTATOR_MIXIN = { * * @param xmlElement XML storage element. */ - domToMutation: function(this: DivisiblebyBlock, xmlElement: Element) { - const divisorInput = (xmlElement.getAttribute('divisor_input') === 'true'); + domToMutation: function (this: DivisiblebyBlock, xmlElement: Element) { + const divisorInput = xmlElement.getAttribute('divisor_input') === 'true'; this.updateShape_(divisorInput); }, @@ -480,7 +481,7 @@ const IS_DIVISIBLEBY_MUTATOR_MIXIN = { * * @param divisorInput True if this block has a divisor input. */ - updateShape_: function(this: DivisiblebyBlock, divisorInput: boolean) { + updateShape_: function (this: DivisiblebyBlock, divisorInput: boolean) { // Add or remove a Value Input. const inputExists = this.getInput('DIVISOR'); if (divisorInput) { @@ -498,29 +499,32 @@ const IS_DIVISIBLEBY_MUTATOR_MIXIN = { * can update the block shape (add/remove divisor input) based on whether * property is "divisible by". */ -const IS_DIVISIBLE_MUTATOR_EXTENSION = function(this: DivisiblebyBlock) { +const IS_DIVISIBLE_MUTATOR_EXTENSION = function (this: DivisiblebyBlock) { this.getField('PROPERTY')!.setValidator( - /** @param option The selected dropdown option. */ - function(this: FieldDropdown, option: string) { - const divisorInput = (option === 'DIVISIBLE_BY'); - (this.getSourceBlock() as DivisiblebyBlock).updateShape_(divisorInput); - return undefined; // FieldValidators can't be void. Use option as-is. - }); + /** @param option The selected dropdown option. */ + function (this: FieldDropdown, option: string) { + const divisorInput = option === 'DIVISIBLE_BY'; + (this.getSourceBlock() as DivisiblebyBlock).updateShape_(divisorInput); + return undefined; // FieldValidators can't be void. Use option as-is. + } + ); }; Extensions.registerMutator( - 'math_is_divisibleby_mutator', IS_DIVISIBLEBY_MUTATOR_MIXIN, - IS_DIVISIBLE_MUTATOR_EXTENSION); + 'math_is_divisibleby_mutator', + IS_DIVISIBLEBY_MUTATOR_MIXIN, + IS_DIVISIBLE_MUTATOR_EXTENSION +); // Update the tooltip of 'math_change' block to reference the variable. Extensions.register( - 'math_change_tooltip', - Extensions.buildTooltipWithFieldText('%{BKY_MATH_CHANGE_TOOLTIP}', 'VAR')); + 'math_change_tooltip', + Extensions.buildTooltipWithFieldText('%{BKY_MATH_CHANGE_TOOLTIP}', 'VAR') +); /** Type of a block that has LIST_MODES_MUTATOR_MIXIN */ -type ListModesBlock = Block&ListModesMixin; +type ListModesBlock = Block & ListModesMixin; interface ListModesMixin extends ListModesMixinType {} -; type ListModesMixinType = typeof LIST_MODES_MUTATOR_MIXIN; /** @@ -533,7 +537,7 @@ const LIST_MODES_MUTATOR_MIXIN = { * * @param newOp Either 'MODE' or some op than returns a number. */ - updateType_: function(this: ListModesBlock, newOp: string) { + updateType_: function (this: ListModesBlock, newOp: string) { if (newOp === 'MODE') { this.outputConnection!.setCheck('Array'); } else { @@ -546,7 +550,7 @@ const LIST_MODES_MUTATOR_MIXIN = { * * @returns XML storage element. */ - mutationToDom: function(this: ListModesBlock): Element { + mutationToDom: function (this: ListModesBlock): Element { const container = xmlUtils.createElement('mutation'); container.setAttribute('op', this.getFieldValue('OP')); return container; @@ -557,7 +561,7 @@ const LIST_MODES_MUTATOR_MIXIN = { * * @param xmlElement XML storage element. */ - domToMutation: function(this: ListModesBlock, xmlElement: Element) { + domToMutation: function (this: ListModesBlock, xmlElement: Element) { const op = xmlElement.getAttribute('op'); if (op === null) throw new TypeError('xmlElement had no op attribute'); this.updateType_(op); @@ -573,17 +577,20 @@ const LIST_MODES_MUTATOR_MIXIN = { * Extension to 'math_on_list' blocks that allows support of * modes operation (outputs a list of numbers). */ -const LIST_MODES_MUTATOR_EXTENSION = function(this: ListModesBlock) { - this.getField( - 'OP')!.setValidator(function(this: ListModesBlock, newOp: string) { - this.updateType_(newOp); - return undefined; - }.bind(this)); +const LIST_MODES_MUTATOR_EXTENSION = function (this: ListModesBlock) { + this.getField('OP')!.setValidator( + function (this: ListModesBlock, newOp: string) { + this.updateType_(newOp); + return undefined; + }.bind(this) + ); }; Extensions.registerMutator( - 'math_modes_of_list_mutator', LIST_MODES_MUTATOR_MIXIN, - LIST_MODES_MUTATOR_EXTENSION); + 'math_modes_of_list_mutator', + LIST_MODES_MUTATOR_MIXIN, + LIST_MODES_MUTATOR_EXTENSION +); // Register provided blocks. defineBlocks(blocks); diff --git a/blocks/text.ts b/blocks/text.ts index 6e462b73e03..d721d4353cc 100644 --- a/blocks/text.ts +++ b/blocks/text.ts @@ -31,7 +31,7 @@ import { } from '../core/common.js'; import '../core/field_multilineinput.js'; import '../core/field_variable.js'; -import { ValueInput } from '../core/inputs/value_input.js'; +import {ValueInput} from '../core/inputs/value_input.js'; /** * A dictionary of the block definitions provided by this module. @@ -1009,10 +1009,7 @@ const CHARAT_EXTENSION = function (this: CharAtBlock) { if (msg) { tooltip += ' ' + - msg.replace( - '%1', - this.workspace.options.oneBasedIndex ? '#1' : '#0' - ); + msg.replace('%1', this.workspace.options.oneBasedIndex ? '#1' : '#0'); } } return tooltip; diff --git a/blocks/variables.ts b/blocks/variables.ts index 97c53fbb916..f50111b8f6c 100644 --- a/blocks/variables.ts +++ b/blocks/variables.ts @@ -17,14 +17,19 @@ import * as Extensions from '../core/extensions.js'; import * as Variables from '../core/variables.js'; import * as xmlUtils from '../core/utils/xml.js'; import type {Block} from '../core/block.js'; -import type {ContextMenuOption, LegacyContextMenuOption} from '../core/contextmenu_registry.js'; +import type { + ContextMenuOption, + LegacyContextMenuOption, +} from '../core/contextmenu_registry.js'; import {FieldVariable} from '../core/field_variable.js'; import {Msg} from '../core/msg.js'; import type {WorkspaceSvg} from '../core/workspace_svg.js'; -import {createBlockDefinitionsFromJsonArray, defineBlocks} from '../core/common.js'; +import { + createBlockDefinitionsFromJsonArray, + defineBlocks, +} from '../core/common.js'; import '../core/field_label.js'; - /** * A dictionary of the block definitions provided by this module. */ @@ -71,10 +76,10 @@ export const blocks = createBlockDefinitionsFromJsonArray([ ]); /** Type of a block that has CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN */ -type VariableBlock = Block&VariableMixin; +type VariableBlock = Block & VariableMixin; interface VariableMixin extends VariableMixinType {} type VariableMixinType = - typeof CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN; + typeof CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN; /** * Mixin to add context menu items to create getter/setter blocks for this @@ -87,9 +92,10 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { * * @param options List of menu options to add to. */ - customContextMenu: function( - this: VariableBlock, - options: Array) { + customContextMenu: function ( + this: VariableBlock, + options: Array + ) { if (!this.isInFlyout) { let oppositeType; let contextMenuMsg; @@ -113,12 +119,14 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { options.push({ enabled: this.workspace.remainingCapacity() > 0, text: contextMenuMsg.replace('%1', name), - callback: ContextMenu.callbackFactory(this, xmlBlock) + callback: ContextMenu.callbackFactory(this, xmlBlock), }); // Getter blocks have the option to rename or delete that variable. } else { - if (this.type === 'variables_get' || - this.type === 'variables_get_reporter') { + if ( + this.type === 'variables_get' || + this.type === 'variables_get_reporter' + ) { const renameOption = { text: Msg['RENAME_VARIABLE'], enabled: true, @@ -144,8 +152,10 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { * @param block The block with the variable to rename. * @returns A function that renames the variable. */ -const renameOptionCallbackFactory = function(block: VariableBlock): () => void { - return function() { +const renameOptionCallbackFactory = function ( + block: VariableBlock +): () => void { + return function () { const workspace = block.workspace; const variableField = block.getField('VAR') as FieldVariable; const variable = variableField.getVariable()!; @@ -160,8 +170,10 @@ const renameOptionCallbackFactory = function(block: VariableBlock): () => void { * @param block The block with the variable to delete. * @returns A function that deletes the variable. */ -const deleteOptionCallbackFactory = function(block: VariableBlock): () => void { - return function() { +const deleteOptionCallbackFactory = function ( + block: VariableBlock +): () => void { + return function () { const workspace = block.workspace; const variableField = block.getField('VAR') as FieldVariable; const variable = variableField.getVariable()!; @@ -171,8 +183,9 @@ const deleteOptionCallbackFactory = function(block: VariableBlock): () => void { }; Extensions.registerMixin( - 'contextMenu_variableSetterGetter', - CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN); + 'contextMenu_variableSetterGetter', + CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN +); // Register provided blocks. defineBlocks(blocks); diff --git a/blocks/variables_dynamic.ts b/blocks/variables_dynamic.ts index 69b630d171e..6b50853f30f 100644 --- a/blocks/variables_dynamic.ts +++ b/blocks/variables_dynamic.ts @@ -18,14 +18,19 @@ import * as Variables from '../core/variables.js'; import * as xml from '../core/utils/xml.js'; import {Abstract as AbstractEvent} from '../core/events/events_abstract.js'; import type {Block} from '../core/block.js'; -import type {ContextMenuOption, LegacyContextMenuOption} from '../core/contextmenu_registry.js'; +import type { + ContextMenuOption, + LegacyContextMenuOption, +} from '../core/contextmenu_registry.js'; import {FieldVariable} from '../core/field_variable.js'; import {Msg} from '../core/msg.js'; import type {WorkspaceSvg} from '../core/workspace_svg.js'; -import {createBlockDefinitionsFromJsonArray, defineBlocks} from '../core/common.js'; +import { + createBlockDefinitionsFromJsonArray, + defineBlocks, +} from '../core/common.js'; import '../core/field_label.js'; - /** * A dictionary of the block definitions provided by this module. */ @@ -34,11 +39,13 @@ export const blocks = createBlockDefinitionsFromJsonArray([ { 'type': 'variables_get_dynamic', 'message0': '%1', - 'args0': [{ - 'type': 'field_variable', - 'name': 'VAR', - 'variable': '%{BKY_VARIABLES_DEFAULT_NAME}', - }], + 'args0': [ + { + 'type': 'field_variable', + 'name': 'VAR', + 'variable': '%{BKY_VARIABLES_DEFAULT_NAME}', + }, + ], 'output': null, 'style': 'variable_dynamic_blocks', 'helpUrl': '%{BKY_VARIABLES_GET_HELPURL}', @@ -70,10 +77,10 @@ export const blocks = createBlockDefinitionsFromJsonArray([ ]); /** Type of a block that has CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN */ -type VariableBlock = Block&VariableMixin; +type VariableBlock = Block & VariableMixin; interface VariableMixin extends VariableMixinType {} type VariableMixinType = - typeof CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN; + typeof CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN; /** * Mixin to add context menu items to create getter/setter blocks for this @@ -86,9 +93,10 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { * * @param options List of menu options to add to. */ - customContextMenu: function( - this: VariableBlock, - options: Array) { + customContextMenu: function ( + this: VariableBlock, + options: Array + ) { // Getter blocks have the option to create a setter block, and vice versa. if (!this.isInFlyout) { let oppositeType; @@ -116,11 +124,13 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { options.push({ enabled: this.workspace.remainingCapacity() > 0, text: contextMenuMsg.replace('%1', name), - callback: ContextMenu.callbackFactory(this, xmlBlock) + callback: ContextMenu.callbackFactory(this, xmlBlock), }); } else { - if (this.type === 'variables_get_dynamic' || - this.type === 'variables_get_reporter_dynamic') { + if ( + this.type === 'variables_get_dynamic' || + this.type === 'variables_get_reporter_dynamic' + ) { const renameOption = { text: Msg['RENAME_VARIABLE'], enabled: true, @@ -143,7 +153,7 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { * * @param _e Change event. */ - onchange: function(this: VariableBlock, _e: AbstractEvent) { + onchange: function (this: VariableBlock, _e: AbstractEvent) { const id = this.getFieldValue('VAR'); const variableModel = Variables.getVariable(this.workspace, id)!; if (this.type === 'variables_get_dynamic') { @@ -161,8 +171,8 @@ const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { * @param block The block with the variable to rename. * @returns A function that renames the variable. */ -const renameOptionCallbackFactory = function(block: VariableBlock) { - return function() { +const renameOptionCallbackFactory = function (block: VariableBlock) { + return function () { const workspace = block.workspace; const variableField = block.getField('VAR') as FieldVariable; const variable = variableField.getVariable()!; @@ -177,8 +187,8 @@ const renameOptionCallbackFactory = function(block: VariableBlock) { * @param block The block with the variable to delete. * @returns A function that deletes the variable. */ -const deleteOptionCallbackFactory = function(block: VariableBlock) { - return function() { +const deleteOptionCallbackFactory = function (block: VariableBlock) { + return function () { const workspace = block.workspace; const variableField = block.getField('VAR') as FieldVariable; const variable = variableField.getVariable()!; @@ -188,8 +198,9 @@ const deleteOptionCallbackFactory = function(block: VariableBlock) { }; Extensions.registerMixin( - 'contextMenu_variableDynamicSetterGetter', - CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN); + 'contextMenu_variableDynamicSetterGetter', + CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN +); // Register provided blocks. defineBlocks(blocks); diff --git a/core/block.ts b/core/block.ts index c674501f928..541966ae1af 100644 --- a/core/block.ts +++ b/core/block.ts @@ -48,7 +48,6 @@ import {DummyInput} from './inputs/dummy_input.js'; import {ValueInput} from './inputs/value_input.js'; import {StatementInput} from './inputs/statement_input.js'; - /** * Class for one block. * Not normally called directly, workspace.newBlock() is preferred. @@ -59,7 +58,7 @@ export class Block implements IASTNodeLocation, IDeletable { * changes. This is usually only called from the constructor, the block type * initializer function, or an extension initializer function. */ - onchange?: ((p1: Abstract) => void)|null; + onchange?: ((p1: Abstract) => void) | null; /** The language-neutral ID given to the collapsed input. */ static readonly COLLAPSED_INPUT_NAME: string = constants.COLLAPSED_INPUT_NAME; @@ -71,7 +70,7 @@ export class Block implements IASTNodeLocation, IDeletable { * 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; + data: string | null = null; /** * Has this block been disposed of? @@ -84,7 +83,7 @@ export class Block implements IASTNodeLocation, IDeletable { * 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; + private hue_: number | null = null; /** Colour of the block in '#RRGGBB' format. */ protected colour_ = '#000000'; @@ -93,10 +92,10 @@ export class Block implements IASTNodeLocation, IDeletable { protected styleName_ = ''; /** An optional method called during initialization. */ - init?: (() => void); + init?: () => void; /** An optional method called during disposal. */ - destroy?: (() => void); + destroy?: () => void; /** * An optional serialization method for defining how to serialize the @@ -130,7 +129,7 @@ export class Block implements IASTNodeLocation, IDeletable { * An optional property for suppressing adding STATEMENT_PREFIX and * STATEMENT_SUFFIX to generated code. */ - suppressPrefixSuffix: boolean|null = false; + suppressPrefixSuffix: boolean | null = false; /** * An optional property for declaring developer variables. Return a list of @@ -152,16 +151,16 @@ export class Block implements IASTNodeLocation, IDeletable { */ decompose?: (p1: Workspace) => Block; id: string; - outputConnection: Connection|null = null; - nextConnection: Connection|null = null; - previousConnection: Connection|null = null; + outputConnection: Connection | null = null; + nextConnection: Connection | null = null; + previousConnection: Connection | null = null; inputList: Input[] = []; inputsInline?: boolean; private disabled = false; tooltip: Tooltip.TipInfo = ''; contextMenu = true; - protected parentBlock_: this|null = null; + protected parentBlock_: this | null = null; protected childBlocks_: this[] = []; @@ -174,7 +173,7 @@ export class Block implements IASTNodeLocation, IDeletable { private isShadow_ = false; protected collapsed_ = false; - protected outputShape_: number|null = null; + protected outputShape_: number | null = null; /** * Is the current block currently in the process of being disposed? @@ -186,7 +185,7 @@ export class Block implements IASTNodeLocation, IDeletable { * * @deprecated August 2019. Use getCommentText instead. */ - comment: string|Comment|null = null; + comment: string | Comment | null = null; /** @internal */ commentModel: CommentModel; private readonly xy_: Coordinate; @@ -200,15 +199,15 @@ export class Block implements IASTNodeLocation, IDeletable { /** Name of the type of hat. */ hat?: string; - rendered: boolean|null = null; + rendered: boolean | null = null; /** * String for block help, or function that returns a URL. Null for no help. */ - helpUrl: string|Function|null = null; + helpUrl: string | Function | null = null; /** A bound callback function to use when the parent workspace changes. */ - private onchangeWrapper_: ((p1: Abstract) => void)|null = null; + private onchangeWrapper_: ((p1: Abstract) => void) | null = null; /** * A count of statement inputs on the block. @@ -233,8 +232,8 @@ export class Block implements IASTNodeLocation, IDeletable { constructor(workspace: Workspace, prototypeName: string, opt_id?: string) { this.workspace = workspace; - 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); /** A model of the comment attached to this block. */ @@ -380,8 +379,8 @@ export class Block implements IASTNodeLocation, IDeletable { * change). */ 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(); } @@ -426,8 +425,11 @@ export class Block implements IASTNodeLocation, IDeletable { } const thisConnection = this.getOnlyValueConnection_(); - if (!thisConnection || !thisConnection.isConnected() || - thisConnection.targetBlock()!.isShadow()) { + if ( + !thisConnection || + !thisConnection.isConnected() || + 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; @@ -437,8 +439,13 @@ export class Block implements IASTNodeLocation, IDeletable { // Disconnect the child block. childConnection?.disconnect(); // Connect child to the parent if possible, otherwise bump away. - if (this.workspace.connectionChecker.canConnect( - childConnection, parentConnection, false)) { + if ( + this.workspace.connectionChecker.canConnect( + childConnection, + parentConnection, + false + ) + ) { parentConnection.connect(childConnection!); } else { childConnection?.onFailedConnect(parentConnection); @@ -454,15 +461,17 @@ export class Block implements IASTNodeLocation, IDeletable { * * @returns The connection on the value input, or null. */ - private getOnlyValueConnection_(): Connection|null { + private getOnlyValueConnection_(): Connection | null { let connection = null; for (let i = 0; i < this.inputList.length; i++) { const thisConnection = this.inputList[i].connection; - if (thisConnection && - thisConnection.type === ConnectionType.INPUT_VALUE && - thisConnection.targetConnection) { + if ( + thisConnection && + thisConnection.type === ConnectionType.INPUT_VALUE && + thisConnection.targetConnection + ) { if (connection) { - return null; // More than one value input found. + return null; // More than one value input found. } connection = thisConnection; } @@ -490,9 +499,14 @@ export class Block implements IASTNodeLocation, IDeletable { // Disconnect the next statement. const nextTarget = this.nextConnection?.targetConnection ?? null; nextTarget?.disconnect(); - if (previousTarget && - this.workspace.connectionChecker.canConnect( - previousTarget, nextTarget, false)) { + if ( + previousTarget && + this.workspace.connectionChecker.canConnect( + previousTarget, + nextTarget, + false + ) + ) { // Attach the next statement to the previous statement. previousTarget.connect(nextTarget!); } @@ -517,7 +531,7 @@ export class Block implements IASTNodeLocation, IDeletable { 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); } @@ -535,11 +549,11 @@ export class Block implements IASTNodeLocation, IDeletable { * @returns The last next connection on the stack, or null. * @internal */ - lastConnectionInStack(ignoreShadows: boolean): Connection|null { + 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; @@ -562,7 +576,7 @@ export class Block implements IASTNodeLocation, IDeletable { * * @returns The block (if any) that holds the current block. */ - getParent(): this|null { + getParent(): this | null { return this.parentBlock_; } @@ -572,8 +586,8 @@ export class Block implements IASTNodeLocation, IDeletable { * @param block A block connected to an input on this block. * @returns The input (if any) that connects to the specified block. */ - getInputWithBlock(block: Block): Input|null { - 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; } @@ -589,9 +603,9 @@ export class Block implements IASTNodeLocation, IDeletable { * * @returns The block (if any) that surrounds the current block. */ - getSurroundParent(): this|null { + getSurroundParent(): this | null { /* eslint-disable-next-line @typescript-eslint/no-this-alias */ - let block: this|null = this; + let block: this | null = this; let prevBlock; do { prevBlock = block; @@ -610,7 +624,7 @@ export class Block implements IASTNodeLocation, IDeletable { * * @returns The next statement block or null. */ - getNextBlock(): Block|null { + getNextBlock(): Block | null { return this.nextConnection && this.nextConnection.targetBlock(); } @@ -619,7 +633,7 @@ export class Block implements IASTNodeLocation, IDeletable { * * @returns The previous statement block or null. */ - getPreviousBlock(): Block|null { + getPreviousBlock(): Block | null { return this.previousConnection && this.previousConnection.targetBlock(); } @@ -630,10 +644,12 @@ export class Block implements IASTNodeLocation, IDeletable { * @returns The first statement connection or null. * @internal */ - getFirstStatementConnection(): Connection|null { - for (let i = 0, input; input = this.inputList[i]; i++) { - if (input.connection && - input.connection.type === ConnectionType.NEXT_STATEMENT) { + 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; } } @@ -649,7 +665,7 @@ export class Block implements IASTNodeLocation, IDeletable { getRootBlock(): this { let rootBlock: this; /* eslint-disable-next-line @typescript-eslint/no-this-alias */ - let block: this|null = this; + let block: this | null = this; do { rootBlock = block; block = rootBlock.parentBlock_; @@ -673,8 +689,11 @@ export class Block implements IASTNodeLocation, IDeletable { previous = block.getPreviousBlock(); // AnyDuringMigration because: Type 'Block' is not assignable to type // 'this'. - } while (previous && previous.getNextBlock() === block && - (block = previous as AnyDuringMigration)); + } while ( + previous && + previous.getNextBlock() === block && + (block = previous as AnyDuringMigration) + ); return block; } @@ -692,7 +711,7 @@ export class Block implements IASTNodeLocation, IDeletable { 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) { @@ -713,7 +732,7 @@ export class Block implements IASTNodeLocation, IDeletable { * @param newParent New parent block. * @internal */ - setParent(newParent: this|null) { + setParent(newParent: this | null) { if (newParent === this.parentBlock_) { return; } @@ -721,8 +740,8 @@ export class Block implements IASTNodeLocation, IDeletable { // 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) { @@ -731,8 +750,9 @@ export class Block implements IASTNodeLocation, IDeletable { throw Error('Block not connected to new parent.'); } else if (isConnected && !newParent) { throw Error( - 'Cannot set parent to null while block is still connected to' + - ' superior block.'); + 'Cannot set parent to null while block is still connected to' + + ' superior block.' + ); } // This block hasn't actually moved on-screen, so there's no need to @@ -769,10 +789,10 @@ export class Block implements IASTNodeLocation, IDeletable { getDescendants(ordered: boolean): this[] { const blocks = [this]; const childBlocks = this.getChildren(ordered); - for (let child, i = 0; child = childBlocks[i]; i++) { + 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(...child.getDescendants(ordered) as AnyDuringMigration); + blocks.push(...(child.getDescendants(ordered) as AnyDuringMigration)); } return blocks; } @@ -783,8 +803,12 @@ export class Block implements IASTNodeLocation, IDeletable { * @returns True if deletable. */ isDeletable(): boolean { - return this.deletable_ && !this.isShadow_ && !this.isDeadOrDying() && - !this.workspace.options.readOnly; + return ( + this.deletable_ && + !this.isShadow_ && + !this.isDeadOrDying() && + !this.workspace.options.readOnly + ); } /** @@ -812,8 +836,12 @@ export class Block implements IASTNodeLocation, IDeletable { * @internal */ isMovable(): boolean { - return this.movable_ && !this.isShadow_ && !this.isDeadOrDying() && - !this.workspace.options.readOnly; + return ( + this.movable_ && + !this.isShadow_ && + !this.isDeadOrDying() && + !this.workspace.options.readOnly + ); } /** @@ -848,7 +876,8 @@ export class Block implements IASTNodeLocation, IDeletable { return true; } return this.workspace.isCapacityAvailable( - common.getBlockTypeCounts(this, true)); + common.getBlockTypeCounts(this, true) + ); } /** @@ -897,8 +926,11 @@ export class Block implements IASTNodeLocation, IDeletable { * @internal */ isEditable(): boolean { - return this.editable_ && !this.isDeadOrDying() && - !this.workspace.options.readOnly; + return ( + this.editable_ && + !this.isDeadOrDying() && + !this.workspace.options.readOnly + ); } /** @@ -917,8 +949,8 @@ export class Block implements IASTNodeLocation, IDeletable { */ 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(); } } @@ -943,7 +975,10 @@ export class Block implements IASTNodeLocation, IDeletable { * @returns The matching connection on this block, or null. * @internal */ - getMatchingConnection(otherBlock: Block, conn: Connection): Connection|null { + getMatchingConnection( + otherBlock: Block, + conn: Connection + ): Connection | null { const connections = this.getConnections_(true); const otherConnections = otherBlock.getConnections_(true); if (connections.length !== otherConnections.length) { @@ -963,7 +998,7 @@ export class Block implements IASTNodeLocation, IDeletable { * @param url URL string for block help, or function that returns a URL. Null * for no help. */ - setHelpUrl(url: string|Function) { + setHelpUrl(url: string | Function) { this.helpUrl = url; } @@ -1010,7 +1045,7 @@ export class Block implements IASTNodeLocation, IDeletable { * * @returns Hue value (0-360). */ - getHue(): number|null { + getHue(): number | null { return this.hue_; } @@ -1020,7 +1055,7 @@ export class Block implements IASTNodeLocation, IDeletable { * @param colour HSV hue value (0 to 360), #RRGGBB string, or a message * reference string pointing to one of those two values. */ - setColour(colour: number|string) { + setColour(colour: number | string) { const parsed = parsing.parseBlockColour(colour); this.hue_ = parsed.hue; this.colour_ = parsed.hex; @@ -1062,16 +1097,17 @@ export class Block implements IASTNodeLocation, IDeletable { * @param name The name of the field. * @returns Named field, or null if field does not exist. */ - getField(name: string): Field|null { + getField(name: string): Field | null { if (typeof name !== 'string') { throw TypeError( - 'Block.prototype.getField expects a string ' + + 'Block.prototype.getField expects a string ' + 'with the field name but received ' + (name === undefined ? 'nothing' : name + ' of type ' + typeof name) + - ' instead'); + ' 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; } @@ -1087,8 +1123,8 @@ export class Block implements IASTNodeLocation, IDeletable { */ getVars(): string[] { const vars: string[] = []; - 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()) { // NOTE: This only applies to `FieldVariable`, a `Field` vars.push(field.getValue() as string); @@ -1106,11 +1142,12 @@ export class Block implements IASTNodeLocation, IDeletable { */ 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(field.getValue() as string); + const model = this.workspace.getVariableById( + field.getValue() as string + ); // Check if the variable actually exists (and isn't just a potential // variable). if (model) { @@ -1130,10 +1167,12 @@ export class Block implements IASTNodeLocation, IDeletable { * @internal */ 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()) { + 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(); } } @@ -1149,8 +1188,8 @@ export class Block implements IASTNodeLocation, IDeletable { * updated name. */ 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++) { + 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); } @@ -1193,22 +1232,27 @@ export class Block implements IASTNodeLocation, IDeletable { * @param opt_check Statement type or list of statement types. Null/undefined * if any type could be connected. */ - setPreviousStatement(newBoolean: boolean, opt_check?: string|string[]|null) { + setPreviousStatement( + newBoolean: boolean, + opt_check?: string | string[] | null + ) { if (newBoolean) { if (opt_check === undefined) { opt_check = null; } if (!this.previousConnection) { - this.previousConnection = - this.makeConnection_(ConnectionType.PREVIOUS_STATEMENT); + this.previousConnection = this.makeConnection_( + ConnectionType.PREVIOUS_STATEMENT + ); } this.previousConnection.setCheck(opt_check); } else { if (this.previousConnection) { if (this.previousConnection.isConnected()) { throw Error( - 'Must disconnect previous statement before removing ' + - 'connection.'); + 'Must disconnect previous statement before removing ' + + 'connection.' + ); } this.previousConnection.dispose(); this.previousConnection = null; @@ -1223,22 +1267,23 @@ export class Block implements IASTNodeLocation, IDeletable { * @param opt_check Statement type or list of statement types. Null/undefined * if any type could be connected. */ - setNextStatement(newBoolean: boolean, opt_check?: string|string[]|null) { + setNextStatement(newBoolean: boolean, opt_check?: string | string[] | null) { if (newBoolean) { if (opt_check === undefined) { opt_check = null; } if (!this.nextConnection) { - this.nextConnection = - this.makeConnection_(ConnectionType.NEXT_STATEMENT); + this.nextConnection = this.makeConnection_( + ConnectionType.NEXT_STATEMENT + ); } this.nextConnection.setCheck(opt_check); } else { if (this.nextConnection) { if (this.nextConnection.isConnected()) { throw Error( - 'Must disconnect next statement before removing ' + - 'connection.'); + 'Must disconnect next statement before removing ' + 'connection.' + ); } this.nextConnection.dispose(); this.nextConnection = null; @@ -1253,21 +1298,23 @@ export class Block implements IASTNodeLocation, IDeletable { * @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: boolean, opt_check?: string|string[]|null) { + setOutput(newBoolean: boolean, opt_check?: string | string[] | null) { if (newBoolean) { if (opt_check === undefined) { opt_check = null; } if (!this.outputConnection) { - this.outputConnection = - this.makeConnection_(ConnectionType.OUTPUT_VALUE); + this.outputConnection = this.makeConnection_( + ConnectionType.OUTPUT_VALUE + ); } this.outputConnection.setCheck(opt_check); } else { if (this.outputConnection) { if (this.outputConnection.isConnected()) { throw Error( - 'Must disconnect output value before removing connection.'); + 'Must disconnect output value before removing connection.' + ); } this.outputConnection.dispose(); this.outputConnection = null; @@ -1282,8 +1329,15 @@ export class Block implements IASTNodeLocation, IDeletable { */ 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; } } @@ -1300,15 +1354,19 @@ export class Block implements IASTNodeLocation, IDeletable { } // Not defined explicitly. Figure out what would look best. for (let i = 1; i < this.inputList.length; i++) { - if (this.inputList[i - 1] instanceof DummyInput && - this.inputList[i] instanceof DummyInput) { + if ( + this.inputList[i - 1] instanceof DummyInput && + this.inputList[i] instanceof DummyInput + ) { // Two dummy inputs in a row. Don't inline them. return false; } } for (let i = 1; i < this.inputList.length; i++) { - if (this.inputList[i - 1] instanceof ValueInput && - this.inputList[i] instanceof DummyInput) { + if ( + this.inputList[i - 1] instanceof ValueInput && + this.inputList[i] instanceof DummyInput + ) { // Dummy input after a value input. Inline them. return true; } @@ -1321,7 +1379,7 @@ export class Block implements IASTNodeLocation, IDeletable { * * @param outputShape Value representing an output shape. */ - setOutputShape(outputShape: number|null) { + setOutputShape(outputShape: number | null) { this.outputShape_ = outputShape; } @@ -1330,7 +1388,7 @@ export class Block implements IASTNodeLocation, IDeletable { * * @returns Value representing output shape if one exists. */ - getOutputShape(): number|null { + getOutputShape(): number | null { return this.outputShape_; } @@ -1352,8 +1410,15 @@ export class Block implements IASTNodeLocation, IDeletable { 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 + ) + ); } } @@ -1391,8 +1456,15 @@ export class Block implements IASTNodeLocation, IDeletable { */ 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; } } @@ -1421,7 +1493,7 @@ export class Block implements IASTNodeLocation, IDeletable { // Join the text array, removing the spaces around added parentheses. let prev = ''; let text: string = tokens.reduce((acc, curr) => { - const val = acc + ((prev === '(' || curr === ')') ? '' : ' ') + curr; + const val = acc + (prev === '(' || curr === ')' ? '' : ' ') + curr; prev = curr[curr.length - 1]; return val; }, ''); @@ -1458,8 +1530,10 @@ export class Block implements IASTNodeLocation, IDeletable { if (!checks && connection.targetConnection) { checks = connection.targetConnection.getCheck(); } - return !!checks && - (checks.indexOf('Boolean') !== -1 || checks.indexOf('Number') !== -1); + return ( + !!checks && + (checks.indexOf('Boolean') !== -1 || checks.indexOf('Number') !== -1) + ); } for (const input of this.inputList) { @@ -1492,8 +1566,13 @@ export class Block implements IASTNodeLocation, IDeletable { * @returns The input object created. */ appendValueInput(name: string): Input { - return this.appendInput(new ValueInput( - name, this, this.makeConnection_(ConnectionType.INPUT_VALUE))); + return this.appendInput( + new ValueInput( + name, + this, + this.makeConnection_(ConnectionType.INPUT_VALUE) + ) + ); } /** @@ -1505,8 +1584,13 @@ export class Block implements IASTNodeLocation, IDeletable { */ appendStatementInput(name: string): Input { this.statementInputCount++; - return this.appendInput(new StatementInput( - name, this, this.makeConnection_(ConnectionType.NEXT_STATEMENT))); + return this.appendInput( + new StatementInput( + name, + this, + this.makeConnection_(ConnectionType.NEXT_STATEMENT) + ) + ); } /** @@ -1539,9 +1623,12 @@ export class Block implements IASTNodeLocation, IDeletable { * @returns The constucted input, or null if there was no constructor * associated with the type. */ - private appendInputFromRegistry(type: string, name: string): Input|null { - const inputConstructor = - registry.getClass(registry.Type.INPUT, type, false); + private appendInputFromRegistry(type: string, name: string): Input | null { + const inputConstructor = registry.getClass( + registry.Type.INPUT, + type, + false + ); if (!inputConstructor) return null; return this.appendInput(new inputConstructor(name, this, null)); } @@ -1558,8 +1645,8 @@ export class Block implements IASTNodeLocation, IDeletable { // Validate inputs. if (json['output'] && json['previousStatement']) { throw Error( - warningPrefix + - 'Must not have both an output and a previousStatement.'); + warningPrefix + 'Must not have both an output and a previousStatement.' + ); } // Set basic properties of block. @@ -1583,8 +1670,11 @@ export class Block implements IASTNodeLocation, IDeletable { let i = 0; while (json['message' + i] !== undefined) { this.interpolate_( - json['message' + i], json['args' + i] || [], - json['lastDummyAlign' + i], warningPrefix); + json['message' + i], + json['args' + i] || [], + json['lastDummyAlign' + i], + warningPrefix + ); i++; } @@ -1622,11 +1712,13 @@ export class Block implements IASTNodeLocation, IDeletable { } if (typeof json['extensions'] === 'string') { console.warn( - warningPrefix + - 'JSON attribute \'extensions\' should be an array of' + - ' strings. Found raw string in JSON for \'' + json['type'] + - '\' block.'); - json['extensions'] = [json['extensions']]; // Correct and continue. + warningPrefix + + "JSON attribute 'extensions' should be an array of" + + " strings. Found raw string in JSON for '" + + json['type'] + + "' block." + ); + json['extensions'] = [json['extensions']]; // Correct and continue. } // Add the mutator to the block. @@ -1689,8 +1781,10 @@ export class Block implements IASTNodeLocation, IDeletable { * @param opt_disableCheck Option flag to disable overwrite checks. */ mixin(mixinObj: AnyDuringMigration, opt_disableCheck?: boolean) { - if (opt_disableCheck !== undefined && - typeof opt_disableCheck !== 'boolean') { + if ( + opt_disableCheck !== undefined && + typeof opt_disableCheck !== 'boolean' + ) { throw Error('opt_disableCheck must be a boolean if provided'); } if (!opt_disableCheck) { @@ -1702,8 +1796,8 @@ export class Block implements IASTNodeLocation, IDeletable { } if (overwrites.length) { throw Error( - 'Mixin will overwrite block members: ' + - JSON.stringify(overwrites)); + 'Mixin will overwrite block members: ' + JSON.stringify(overwrites) + ); } } Object.assign(this, mixinObj); @@ -1720,20 +1814,23 @@ export class Block implements IASTNodeLocation, IDeletable { * @param warningPrefix Warning prefix string identifying block. */ private interpolate_( - message: string, args: AnyDuringMigration[], - lastDummyAlign: string|undefined, warningPrefix: string) { + 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; @@ -1757,7 +1854,7 @@ export class Block implements IASTNodeLocation, IDeletable { * @param tokens An array of tokens to validate * @param argsCount The number of args that need to be referred to. */ - private validateTokens_(tokens: Array, argsCount: number) { + private validateTokens_(tokens: Array, argsCount: number) { const visitedArgsHash = []; let visitedArgsCount = 0; for (let i = 0; i < tokens.length; i++) { @@ -1767,21 +1864,36 @@ export class Block implements IASTNodeLocation, IDeletable { } if (token < 1 || token > argsCount) { throw Error( - 'Block "' + this.type + '": ' + - 'Message index %' + token + ' out of range.'); + 'Block "' + + this.type + + '": ' + + 'Message index %' + + token + + ' out of range.' + ); } if (visitedArgsHash[token]) { throw Error( - 'Block "' + this.type + '": ' + - 'Message index %' + token + ' duplicated.'); + 'Block "' + + this.type + + '": ' + + 'Message index %' + + token + + ' duplicated.' + ); } visitedArgsHash[token] = true; visitedArgsCount++; } if (visitedArgsCount !== argsCount) { throw Error( - 'Block "' + this.type + '": ' + - 'Message does not reference all ' + argsCount + ' arg(s).'); + 'Block "' + + this.type + + '": ' + + 'Message does not reference all ' + + argsCount + + ' arg(s).' + ); } } @@ -1797,8 +1909,10 @@ export class Block implements IASTNodeLocation, IDeletable { * @returns The JSON definitions of field and inputs to add to the block. */ private interpolateArguments_( - tokens: Array, args: Array, - lastDummyAlign: string|undefined): AnyDuringMigration[] { + tokens: Array, + args: Array, + lastDummyAlign: string | undefined + ): AnyDuringMigration[] { const elements = []; for (let i = 0; i < tokens.length; i++) { let element = tokens[i]; @@ -1818,9 +1932,12 @@ export class Block implements IASTNodeLocation, IDeletable { } const length = elements.length; - if (length && - !this.isInputKeyword_( - (elements as AnyDuringMigration)[length - 1]['type'])) { + if ( + length && + !this.isInputKeyword_( + (elements as AnyDuringMigration)[length - 1]['type'] + ) + ) { const dummyInput = {'type': 'input_dummy'}; if (lastDummyAlign) { (dummyInput as AnyDuringMigration)['align'] = lastDummyAlign; @@ -1839,8 +1956,11 @@ export class Block implements IASTNodeLocation, IDeletable { * @param element The element to try to turn into a field. * @returns The field defined by the JSON, or null if one couldn't be created. */ - private fieldFromJson_(element: {alt?: string, type: string, text?: string}): - Field|null { + 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') { @@ -1862,8 +1982,10 @@ export class Block implements IASTNodeLocation, IDeletable { * @returns The input that has been created, or null if one could not be * created for some reason (should never happen). */ - private inputFromJson_(element: AnyDuringMigration, warningPrefix: string): - Input|null { + private inputFromJson_( + element: AnyDuringMigration, + warningPrefix: string + ): Input | null { const alignmentLookup = { 'LEFT': Align.LEFT, 'RIGHT': Align.RIGHT, @@ -1896,9 +2018,9 @@ export class Block implements IASTNodeLocation, IDeletable { input.setCheck(element['check']); } if (element['align']) { - const alignment = - (alignmentLookup as - AnyDuringMigration)[element['align'].toUpperCase()]; + const alignment = (alignmentLookup as AnyDuringMigration)[ + element['align'].toUpperCase() + ]; if (alignment === undefined) { console.warn(warningPrefix + 'Illegal align value: ', element['align']); } else { @@ -1916,8 +2038,12 @@ export class Block implements IASTNodeLocation, IDeletable { * otherwise. */ private isInputKeyword_(str: string): boolean { - return str === 'input_value' || str === 'input_statement' || - str === 'input_dummy' || registry.hasItem(registry.Type.INPUT, str); + return ( + str === 'input_value' || + str === 'input_statement' || + str === 'input_dummy' || + registry.hasItem(registry.Type.INPUT, str) + ); } /** @@ -1927,7 +2053,7 @@ export class Block implements IASTNodeLocation, IDeletable { * @param str String to turn into the JSON definition of a label field. * @returns The JSON definition or null. */ - private stringToFieldJson_(str: string): {text: string, type: string}|null { + private stringToFieldJson_(str: string): {text: string; type: string} | null { str = str.trim(); if (str) { return { @@ -1945,14 +2071,14 @@ export class Block implements IASTNodeLocation, IDeletable { * @param refName Name of input that should be after the moved input, or null * to be the input at the end. */ - moveInputBefore(name: string, refName: string|null) { + 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) { @@ -1983,7 +2109,7 @@ export class Block implements IASTNodeLocation, IDeletable { moveNumberedInputBefore(inputIndex: number, refIndex: number) { // Validate arguments. if (inputIndex === refIndex) { - throw Error('Can\'t move input to itself.'); + throw Error("Can't move input to itself."); } if (inputIndex >= this.inputList.length) { throw RangeError('Input index ' + inputIndex + ' out of bounds.'); @@ -2011,7 +2137,7 @@ export class Block implements IASTNodeLocation, IDeletable { * @throws {Error} if the input is not present and opt_quiet is not true. */ removeInput(name: string, opt_quiet?: boolean): boolean { - for (let i = 0, input; input = this.inputList[i]; i++) { + for (let i = 0, input; (input = this.inputList[i]); i++) { if (input.name === name) { if (input instanceof StatementInput) this.statementInputCount--; input.dispose(); @@ -2031,8 +2157,8 @@ export class Block implements IASTNodeLocation, IDeletable { * @param name The name of the input. * @returns The input object, or null if input does not exist. */ - getInput(name: string): Input|null { - 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; } @@ -2048,7 +2174,7 @@ export class Block implements IASTNodeLocation, IDeletable { * @returns The attached value block, or null if the input is either * disconnected or if the input does not exist. */ - getInputTargetBlock(name: string): Block|null { + getInputTargetBlock(name: string): Block | null { const input = this.getInput(name); return input && input.connection && input.connection.targetBlock(); } @@ -2058,7 +2184,7 @@ export class Block implements IASTNodeLocation, IDeletable { * * @returns Block's comment. */ - getCommentText(): string|null { + getCommentText(): string | null { return this.commentModel.text; } @@ -2067,14 +2193,21 @@ export class Block implements IASTNodeLocation, IDeletable { * * @param text The text, or null to delete. */ - setCommentText(text: string|null) { + 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. + this.comment = text; // For backwards compatibility. } /** @@ -2084,7 +2217,7 @@ export class Block implements IASTNodeLocation, IDeletable { * @param _opt_id An optional ID for the warning text to be able to maintain * multiple warnings. */ - setWarningText(_text: string|null, _opt_id?: string) {} + setWarningText(_text: string | null, _opt_id?: string) {} // NOP. /** @@ -2116,8 +2249,9 @@ export class Block implements IASTNodeLocation, IDeletable { if (this.parentBlock_) { throw Error('Block has parent'); } - const event = - new (eventUtils.get(eventUtils.BLOCK_MOVE))(this) as BlockMove; + const event = new (eventUtils.get(eventUtils.BLOCK_MOVE))( + this + ) as BlockMove; reason && event.setReason(reason); this.xy_.translate(dx, dy); event.recordNew(); @@ -2152,7 +2286,7 @@ export class Block implements IASTNodeLocation, IDeletable { } // 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; } @@ -2192,7 +2326,7 @@ export class Block implements IASTNodeLocation, IDeletable { export namespace Block { export interface CommentModel { - text: string|null; + text: string | null; pinned: boolean; size: Size; } diff --git a/core/block_animations.ts b/core/block_animations.ts index 8f72bfa650a..9c9a52aa78f 100644 --- a/core/block_animations.ts +++ b/core/block_animations.ts @@ -11,7 +11,6 @@ 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. */ interface CloneRect { x: number; @@ -21,11 +20,10 @@ interface CloneRect { } /** PID of disconnect UI animation. There can only be one at a time. */ -let disconnectPid: ReturnType|null = null; +let disconnectPid: ReturnType | null = null; /** The wobbling block. There can only be one at a time. */ -let wobblingBlock: BlockSvg|null = null; - +let wobblingBlock: BlockSvg | null = null; /** * Play some UI effects (sound, animation) when disposing of a block. @@ -46,8 +44,12 @@ export function disposeUiEffect(block: BlockSvg) { const clone: SVGGElement = svgGroup.cloneNode(true) as SVGGElement; clone.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}; + const cloneRect = { + 'x': xy.x, + 'y': xy.y, + 'width': block.width, + 'height': block.height, + }; disposeUiStep(clone, cloneRect, workspace.RTL, new Date(), workspace.scale); } /** @@ -62,21 +64,25 @@ export function disposeUiEffect(block: BlockSvg) { * @param workspaceScale Scale of workspace. */ function disposeUiStep( - clone: Element, rect: CloneRect, rtl: boolean, start: Date, - workspaceScale: number) { + clone: Element, + rect: CloneRect, + rtl: boolean, + start: Date, + workspaceScale: number +) { const ms = new Date().getTime() - start.getTime(); const percent = ms / 150; if (percent > 1) { dom.removeNode(clone); } else { const x = - rect.x + (rtl ? -1 : 1) * rect.width * workspaceScale / 2 * percent; + rect.x + (((rtl ? -1 : 1) * rect.width * workspaceScale) / 2) * percent; const y = rect.y + rect.height * workspaceScale * percent; const scale = (1 - percent) * workspaceScale; clone.setAttribute( - 'transform', - 'translate(' + x + ',' + y + ')' + - ' scale(' + scale + ')'); + 'transform', + 'translate(' + x + ',' + y + ')' + ' scale(' + scale + ')' + ); setTimeout(disposeUiStep, 10, clone, rect, rtl, start, workspaceScale); } } @@ -92,7 +98,7 @@ export function connectionUiEffect(block: BlockSvg) { const scale = workspace.scale; workspace.getAudioManager().play('click'); if (scale < 1) { - return; // Too small to care about visual effects. + return; // Too small to care about visual effects. } // Determine the absolute coordinates of the inferior block. const xy = workspace.getSvgXY(block.getSvgRoot()); @@ -105,15 +111,17 @@ export function connectionUiEffect(block: BlockSvg) { xy.y += 3 * scale; } const ripple = dom.createSvgElement( - Svg.CIRCLE, { - 'cx': xy.x, - 'cy': xy.y, - 'r': 0, - 'fill': 'none', - 'stroke': '#888', - 'stroke-width': 10, - }, - workspace.getParentSvg()); + Svg.CIRCLE, + { + 'cx': xy.x, + 'cy': xy.y, + 'r': 0, + 'fill': 'none', + 'stroke': '#888', + 'stroke-width': 10, + }, + workspace.getParentSvg() + ); // Start the animation. connectionUiStep(ripple, new Date(), scale); } @@ -147,13 +155,13 @@ export function disconnectUiEffect(block: BlockSvg) { disconnectUiStop(); block.workspace.getAudioManager().play('disconnect'); if (block.workspace.scale < 1) { - return; // Too small to care about visual effects. + return; // Too small to care about visual effects. } // Horizontal distance for bottom of block to wiggle. const DISPLACEMENT = 10; // Scale magnitude of skew to height of block. const height = block.getHeightWidth().height; - let magnitude = Math.atan(DISPLACEMENT / height) / Math.PI * 180; + let magnitude = (Math.atan(DISPLACEMENT / height) / Math.PI) * 180; if (!block.RTL) { magnitude *= -1; } @@ -170,8 +178,8 @@ export function disconnectUiEffect(block: BlockSvg) { * @param start Date of animation's start. */ function disconnectUiStep(block: BlockSvg, magnitude: number, start: Date) { - const DURATION = 200; // Milliseconds. - const WIGGLES = 3; // Half oscillations. + const DURATION = 200; // Milliseconds. + const WIGGLES = 3; // Half oscillations. const ms = new Date().getTime() - start.getTime(); const percent = ms / DURATION; @@ -179,13 +187,15 @@ function disconnectUiStep(block: BlockSvg, magnitude: number, start: Date) { let skew = ''; if (percent <= 1) { const val = Math.round( - Math.sin(percent * Math.PI * WIGGLES) * (1 - percent) * magnitude); + Math.sin(percent * Math.PI * WIGGLES) * (1 - percent) * magnitude + ); skew = `skewX(${val})`; disconnectPid = setTimeout(disconnectUiStep, 10, block, magnitude, start); } - block.getSvgRoot().setAttribute( - 'transform', `${block.getTranslation()} ${skew}`); + block + .getSvgRoot() + .setAttribute('transform', `${block.getTranslation()} ${skew}`); } /** @@ -199,7 +209,8 @@ export function disconnectUiStop() { clearTimeout(disconnectPid); disconnectPid = null; } - wobblingBlock.getSvgRoot().setAttribute( - 'transform', wobblingBlock.getTranslation()); + wobblingBlock + .getSvgRoot() + .setAttribute('transform', wobblingBlock.getTranslation()); wobblingBlock = null; } diff --git a/core/block_drag_surface.ts b/core/block_drag_surface.ts index 909aa76bf22..c249458cb44 100644 --- a/core/block_drag_surface.ts +++ b/core/block_drag_surface.ts @@ -23,7 +23,6 @@ import * as dom from './utils/dom.js'; import {Svg} from './utils/svg.js'; import * as svgMath from './utils/svg_math.js'; - /** * Class for a drag surface for the currently dragged block. This is a separate * SVG that contains only the currently moving block, or nothing. @@ -65,14 +64,16 @@ export class BlockDragSurfaceSvg { /** @param container Containing element. */ constructor(private readonly container: Element) { this.svg = dom.createSvgElement( - Svg.SVG, { - 'xmlns': dom.SVG_NS, - 'xmlns:html': dom.HTML_NS, - 'xmlns:xlink': dom.XLINK_NS, - 'version': '1.1', - 'class': 'blocklyBlockDragSurface', - }, - this.container); + Svg.SVG, + { + 'xmlns': dom.SVG_NS, + 'xmlns:html': dom.HTML_NS, + 'xmlns:xlink': dom.XLINK_NS, + 'version': '1.1', + 'class': 'blocklyBlockDragSurface', + }, + this.container + ); this.dragGroup = dom.createSvgElement(Svg.G, {}, this.svg); } @@ -120,8 +121,9 @@ export class BlockDragSurfaceSvg { this.childSurfaceXY.x = roundX; this.childSurfaceXY.y = roundY; this.dragGroup.setAttribute( - 'transform', - 'translate(' + roundX + ',' + roundY + ') scale(' + scale + ')'); + 'transform', + 'translate(' + roundX + ',' + roundY + ') scale(' + scale + ')' + ); } /** @@ -200,7 +202,7 @@ export class BlockDragSurfaceSvg { * * @returns Drag surface block DOM element, or null if no blocks exist. */ - getCurrentBlock(): Element|null { + getCurrentBlock(): Element | null { return this.dragGroup.firstChild as Element; } diff --git a/core/block_dragger.ts b/core/block_dragger.ts index 3291c5c74c7..11f03e2927b 100644 --- a/core/block_dragger.ts +++ b/core/block_dragger.ts @@ -30,7 +30,6 @@ 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. @@ -44,7 +43,7 @@ export class BlockDragger implements IBlockDragger { protected workspace_: WorkspaceSvg; /** Which drag area the mouse pointer is over, if any. */ - private dragTarget_: IDragTarget|null = null; + private dragTarget_: IDragTarget | null = null; /** Whether the block would be deleted if dropped immediately. */ protected wouldDeleteBlock_ = false; @@ -59,8 +58,9 @@ export class BlockDragger implements IBlockDragger { this.draggingBlock_ = block; /** Object that keeps track of connections on dragged blocks. */ - this.draggedConnectionManager_ = - new InsertionMarkerManager(this.draggingBlock_); + this.draggedConnectionManager_ = new InsertionMarkerManager( + this.draggingBlock_ + ); this.workspace_ = workspace; @@ -136,9 +136,11 @@ export class BlockDragger implements IBlockDragger { */ protected shouldDisconnect_(healStack: boolean): boolean { return !!( - this.draggingBlock_.getParent() || - healStack && this.draggingBlock_.nextConnection && - this.draggingBlock_.nextConnection.targetBlock()); + this.draggingBlock_.getParent() || + (healStack && + this.draggingBlock_.nextConnection && + this.draggingBlock_.nextConnection.targetBlock()) + ); } /** @@ -149,7 +151,9 @@ export class BlockDragger implements IBlockDragger { * at mouse down, in pixel units. */ protected disconnectBlock_( - healStack: boolean, currentDragDeltaXY: Coordinate) { + healStack: boolean, + currentDragDeltaXY: Coordinate + ) { this.draggingBlock_.unplug(healStack); const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY); const newLoc = Coordinate.sum(this.startXY_, delta); @@ -162,7 +166,10 @@ export class BlockDragger implements IBlockDragger { /** 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)); + this.draggingBlock_, + true, + this.draggingBlock_.getDescendants(false) + ); eventUtils.fire(event); } @@ -217,10 +224,11 @@ export class BlockDragger implements IBlockDragger { blockAnimation.disconnectUiStop(); - const preventMove = !!this.dragTarget_ && - this.dragTarget_.shouldPreventMove(this.draggingBlock_); + const preventMove = + !!this.dragTarget_ && + this.dragTarget_.shouldPreventMove(this.draggingBlock_); let newLoc: Coordinate; - let delta: Coordinate|null = null; + let delta: Coordinate | null = null; if (preventMove) { newLoc = this.startXY_; } else { @@ -238,15 +246,17 @@ export class BlockDragger implements IBlockDragger { if (!deleted) { // These are expensive and don't need to be done if we're deleting. this.draggingBlock_.setDragging(false); - if (delta) { // !preventMove + if (delta) { + // !preventMove this.updateBlockAfterMove_(); } else { // Blocks dragged directly from a flyout may need to be bumped into // bounds. bumpObjects.bumpIntoBounds( - this.draggingBlock_.workspace, - this.workspace_.getMetricsManager().getScrollMetrics(true), - this.draggingBlock_); + this.draggingBlock_.workspace, + this.workspace_.getMetricsManager().getScrollMetrics(true), + this.draggingBlock_ + ); } } this.workspace_.setResizesEnabled(true); @@ -262,8 +272,10 @@ export class BlockDragger implements IBlockDragger { * @returns New location after drag. delta is in workspace units. newLocation * is the new coordinate where the block should end up. */ - protected getNewLocationAfterDrag_(currentDragDeltaXY: Coordinate): - {delta: Coordinate, newLocation: Coordinate} { + protected getNewLocationAfterDrag_(currentDragDeltaXY: Coordinate): { + delta: Coordinate; + newLocation: Coordinate; + } { const delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY); const newLocation = Coordinate.sum(this.startXY_, delta); return { @@ -307,7 +319,10 @@ export class BlockDragger implements IBlockDragger { /** 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)); + this.draggingBlock_, + false, + this.draggingBlock_.getDescendants(false) + ); eventUtils.fire(event); } @@ -322,21 +337,25 @@ export class BlockDragger implements IBlockDragger { const toolbox = this.workspace_.getToolbox(); if (toolbox) { - const style = this.draggingBlock_.isDeletable() ? 'blocklyToolboxDelete' : - 'blocklyToolboxGrab'; + const style = this.draggingBlock_.isDeletable() + ? 'blocklyToolboxDelete' + : 'blocklyToolboxGrab'; // AnyDuringMigration because: Property 'removeStyle' does not exist on // type 'IToolbox'. - if (isEnd && - typeof (toolbox as AnyDuringMigration).removeStyle === 'function') { + 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') { + !isEnd && + typeof (toolbox as AnyDuringMigration).addStyle === 'function' + ) { // AnyDuringMigration because: Property 'addStyle' does not exist on // type 'IToolbox'. (toolbox as AnyDuringMigration).addStyle(style); @@ -348,7 +367,8 @@ export class BlockDragger implements IBlockDragger { protected fireMoveEvent_() { if (this.draggingBlock_.isDeadOrDying()) return; const event = new (eventUtils.get(eventUtils.BLOCK_MOVE))( - this.draggingBlock_) as BlockMove; + this.draggingBlock_ + ) as BlockMove; event.setReason(['drag']); event.oldCoordinate = this.startXY_; event.recordNew(); @@ -374,8 +394,9 @@ export class BlockDragger implements IBlockDragger { */ protected pixelsToWorkspaceUnits_(pixelCoord: Coordinate): Coordinate { const result = new Coordinate( - pixelCoord.x / this.workspace_.scale, - pixelCoord.y / this.workspace_.scale); + pixelCoord.x / this.workspace_.scale, + pixelCoord.y / this.workspace_.scale + ); if (this.workspace_.isMutator) { // If we're in a mutator, its scale is always 1, purely because of some // oddities in our rendering optimizations. The actual scale is the same @@ -408,8 +429,10 @@ export class BlockDragger implements IBlockDragger { */ getInsertionMarkers(): BlockSvg[] { // No insertion markers with the old style of dragged connection managers. - if (this.draggedConnectionManager_ && - this.draggedConnectionManager_.getInsertionMarkers) { + if ( + this.draggedConnectionManager_ && + this.draggedConnectionManager_.getInsertionMarkers + ) { return this.draggedConnectionManager_.getInsertionMarkers(); } return []; @@ -433,15 +456,15 @@ export interface IconPositionData { function initIconData(block: BlockSvg): IconPositionData[] { // Build a list of icons that need to be moved and where they started. const dragIconData = []; - const descendants = (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); diff --git a/core/block_svg.ts b/core/block_svg.ts index 29035819648..cd8fce5ef83 100644 --- a/core/block_svg.ts +++ b/core/block_svg.ts @@ -25,7 +25,11 @@ 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 { + 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'; @@ -58,14 +62,14 @@ import type {Workspace} from './workspace.js'; import type {WorkspaceSvg} from './workspace_svg.js'; import {queueRender} from './render_management.js'; - /** * Class for a block's SVG representation. * Not normally called directly, workspace.newBlock() is preferred. */ -export class BlockSvg extends Block implements IASTNodeLocationSvg, - IBoundedElement, ICopyable, - IDraggable { +export class BlockSvg + extends Block + implements IASTNodeLocationSvg, IBoundedElement, ICopyable, IDraggable +{ /** * Constant for identifying rows that are to be rendered inline. * Don't collide with Blockly.inputTypes. @@ -81,15 +85,16 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, override decompose?: (p1: Workspace) => BlockSvg; // override compose?: ((p1: BlockSvg) => void)|null; saveConnections?: (p1: BlockSvg) => void; - customContextMenu?: - (p1: Array) => void; + customContextMenu?: ( + p1: Array + ) => void; /** * An property used internally to reference the block's rendering debugger. * * @internal */ - renderingDebugger: BlockRenderingDebug|null = null; + renderingDebugger: BlockRenderingDebug | null = null; /** * Height of this block, not including any statement blocks above or below. @@ -110,13 +115,13 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, private warningTextDb = new Map>(); /** Block's mutator icon (if any). */ - mutator: Mutator|null = null; + mutator: Mutator | null = null; /** Block's comment icon (if any). */ - private commentIcon_: Comment|null = null; + private commentIcon_: Comment | null = null; /** Block's warning icon (if any). */ - warning: Warning|null = null; + warning: Warning | null = null; private svgGroup_: SVGGElement; style: BlockStyle; @@ -160,7 +165,6 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, */ relativeCoords = new Coordinate(0, 0); - /** * @param workspace The block's workspace. * @param prototypeName Name of the language object containing type-specific @@ -177,8 +181,9 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, this.style = workspace.getRenderer().getConstants().getBlockStyle(null); /** The renderer's path object. */ - this.pathObject = - workspace.getRenderer().makePathObject(this.svgGroup_, this.style); + this.pathObject = workspace + .getRenderer() + .makePathObject(this.svgGroup_, this.style); /** * Whether to move the block to the drag surface when it is dragged. @@ -204,7 +209,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, 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(); @@ -216,7 +221,11 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, const svg = this.getSvgRoot(); if (!this.workspace.options.readOnly && !this.eventsInit_ && svg) { browserEvents.conditionalBind( - svg, 'pointerdown', this, this.onMouseDown_); + svg, + 'pointerdown', + this, + this.onMouseDown_ + ); } this.eventsInit_ = true; @@ -230,7 +239,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, * * @returns #RRGGBB string. */ - getColourSecondary(): string|undefined { + getColourSecondary(): string | undefined { return this.style.colourSecondary; } @@ -239,7 +248,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, * * @returns #RRGGBB string. */ - getColourTertiary(): string|undefined { + getColourTertiary(): string | undefined { return this.style.colourTertiary; } @@ -268,7 +277,10 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, } } const event = new (eventUtils.get(eventUtils.SELECTED))( - oldId, this.id, this.workspace.id); + oldId, + this.id, + this.workspace.id + ); eventUtils.fire(event); common.setSelected(this); this.addSelect(); @@ -283,7 +295,10 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, return; } const event = new (eventUtils.get(eventUtils.SELECTED))( - this.id, null, this.workspace.id); + this.id, + null, + this.workspace.id + ); event.workspaceId = this.workspace.id; eventUtils.fire(event); common.setSelected(null); @@ -315,7 +330,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, * @param newParent New parent block. * @internal */ - override setParent(newParent: this|null) { + override setParent(newParent: this | null) { const oldParent = this.parentBlock_; if (newParent === oldParent) { return; @@ -359,9 +374,9 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, let x = 0; let y = 0; - const dragSurfaceGroup = this.useDragSurface_ ? - this.workspace.getBlockDragSurface()!.getGroup() : - null; + const dragSurfaceGroup = this.useDragSurface_ + ? this.workspace.getBlockDragSurface()!.getGroup() + : null; let element: SVGElement = this.getSvgRoot(); if (element) { @@ -372,17 +387,22 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, y += xy.y; // If this element is the current element on the drag surface, include // the translation of the drag surface itself. - if (this.useDragSurface_ && - this.workspace.getBlockDragSurface()!.getCurrentBlock() === - element) { - const surfaceTranslation = - this.workspace.getBlockDragSurface()!.getSurfaceTranslation(); + if ( + this.useDragSurface_ && + this.workspace.getBlockDragSurface()!.getCurrentBlock() === element + ) { + const surfaceTranslation = this.workspace + .getBlockDragSurface()! + .getSurfaceTranslation(); x += surfaceTranslation.x; y += surfaceTranslation.y; } element = element.parentNode as SVGElement; - } while (element && element !== this.workspace.getCanvas() && - element !== dragSurfaceGroup); + } while ( + element && + element !== this.workspace.getCanvas() && + element !== dragSurfaceGroup + ); } return new Coordinate(x, y); } @@ -399,9 +419,9 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, throw Error('Block has parent'); } const eventsEnabled = eventUtils.isEnabled(); - let event: BlockMove|null = null; + let event: BlockMove | null = null; if (eventsEnabled) { - event = new (eventUtils.get(eventUtils.BLOCK_MOVE))!(this) as BlockMove; + event = new (eventUtils.get(eventUtils.BLOCK_MOVE)!)(this) as BlockMove; reason && event.setReason(reason); } const xy = this.getRelativeToSurfaceXY(); @@ -487,8 +507,9 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, } // 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()); } /** @@ -501,8 +522,9 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, */ moveDuringDrag(newLoc: Coordinate) { if (this.useDragSurface_) { - this.workspace.getBlockDragSurface()!.translateSurface( - newLoc.x, newLoc.y); + this.workspace + .getBlockDragSurface()! + .translateSurface(newLoc.x, newLoc.y); } else { this.translate(newLoc.x, newLoc.y); this.getSvgRoot().setAttribute('transform', this.getTranslation()); @@ -520,29 +542,31 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, /** Snap this block to the nearest grid point. */ snapToGrid() { if (this.isDeadOrDying()) { - return; // Deleted block. + return; // Deleted block. } if (this.workspace.isDragging()) { - return; // Don't bump blocks during a drag.; + return; // Don't bump blocks during a drag.; } if (this.getParent()) { - return; // Only snap top-level blocks. + return; // Only snap top-level blocks. } if (this.isInFlyout) { - return; // Don't move blocks around in a flyout. + return; // Don't move blocks around in a flyout. } const grid = this.workspace.getGrid(); if (!grid || !grid.shouldSnap()) { - return; // Config says no snapping. + return; // Config says no snapping. } const spacing = grid.getSpacing(); const half = spacing / 2; const xy = this.getRelativeToSurfaceXY(); - const dx = - Math.round(Math.round((xy.x - half) / spacing) * spacing + half - xy.x); - const dy = - Math.round(Math.round((xy.y - half) / spacing) * spacing + half - xy.y); + const dx = Math.round( + Math.round((xy.x - half) / spacing) * spacing + half - xy.x + ); + const dy = Math.round( + Math.round((xy.y - half) / spacing) * spacing + half - xy.y + ); if (dx || dy) { this.moveBy(dx, dy, ['snap']); } @@ -576,7 +600,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, */ markDirty() { this.pathObject.constants = this.workspace.getRenderer().getConstants(); - for (let i = 0, input; input = this.inputList[i]; i++) { + for (let i = 0, input; (input = this.inputList[i]); i++) { input.markDirty(); } } @@ -603,7 +627,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, 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); } @@ -616,7 +640,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, } 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); } @@ -626,8 +650,9 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, field.setValue(text); return; } - const input = this.getInput(collapsedInputName) || - this.appendDummyInput(collapsedInputName); + const input = + this.getInput(collapsedInputName) || + this.appendDummyInput(collapsedInputName); input.appendField(new FieldLabel(text), collapsedFieldName); } @@ -679,7 +704,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, */ showHelp() { const url = - typeof this.helpUrl === 'function' ? this.helpUrl() : this.helpUrl; + typeof this.helpUrl === 'function' ? this.helpUrl() : this.helpUrl; if (url) { window.open(url); } @@ -690,13 +715,16 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, * * @returns Context menu options or null if no menu. */ - protected generateContextMenu(): - Array|null { + protected generateContextMenu(): Array< + ContextMenuOption | LegacyContextMenuOption + > | null { if (this.workspace.options.readOnly || !this.contextMenu) { return null; } const menuOptions = ContextMenuRegistry.registry.getContextMenuOptions( - ContextMenuRegistry.ScopeType.BLOCK, {block: this}); + ContextMenuRegistry.ScopeType.BLOCK, + {block: this} + ); // Allow the block to add or modify menuOptions. if (this.customContextMenu) { @@ -815,12 +843,13 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, */ override setInsertionMarker(insertionMarker: boolean) { if (this.isInsertionMarker_ === insertionMarker) { - return; // No change. + return; // No change. } this.isInsertionMarker_ = insertionMarker; if (this.isInsertionMarker_) { this.setColour( - this.workspace.getRenderer().getConstants().INSERTION_MARKER_COLOUR); + this.workspace.getRenderer().getConstants().INSERTION_MARKER_COLOUR + ); this.pathObject.updateInsertionMarker(true); } } @@ -897,8 +926,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, // (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); } @@ -909,14 +937,15 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, * @returns Copy metadata, or null if the block is an insertion marker. * @internal */ - toCopyData(): CopyData|null { + toCopyData(): CopyData | null { if (this.isInsertionMarker_) { return null; } return { - saveInfo: - blocks.save(this, {addCoordinates: true, addNextBlocks: false}) as - blocks.State, + saveInfo: blocks.save(this, { + addCoordinates: true, + addNextBlocks: false, + }) as blocks.State, source: this.workspace, typeCounts: common.getBlockTypeCounts(this, true), }; @@ -935,8 +964,8 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, 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(); } } @@ -969,7 +998,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, * * @returns The comment icon attached to this block, or null. */ - getCommentIcon(): Comment|null { + getCommentIcon(): Comment | null { return this.commentIcon_; } @@ -978,7 +1007,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, * * @param text The text, or null to delete. */ - override setCommentText(text: string|null) { + override setCommentText(text: string | null) { if (this.commentModel.text === text) { return; } @@ -993,11 +1022,11 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, } if (shouldHaveComment) { this.commentIcon_ = new Comment(this); - this.comment = this.commentIcon_; // For backwards compatibility. + this.comment = this.commentIcon_; // For backwards compatibility. } else { this.commentIcon_!.dispose(); this.commentIcon_ = null; - this.comment = null; // For backwards compatibility. + this.comment = null; // For backwards compatibility. } if (this.rendered) { // Icons must force an immediate render so that bubbles can be opened @@ -1015,7 +1044,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, * @param opt_id An optional ID for the warning text to be able to maintain * multiple warnings. */ - override setWarningText(text: string|null, opt_id?: string) { + override setWarningText(text: string | null, opt_id?: string) { const id = opt_id || ''; if (!id) { // Kill all previous pending processes, this edit supersedes them all. @@ -1031,12 +1060,15 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, if (this.workspace.isDragging()) { // Don't change the warning text during a drag. // Wait until the drag finishes. - this.warningTextDb.set(id, setTimeout(() => { - if (!this.isDeadOrDying()) { - this.warningTextDb.delete(id); - this.setWarningText(text, id); - } - }, 100)); + this.warningTextDb.set( + id, + setTimeout(() => { + if (!this.isDeadOrDying()) { + this.warningTextDb.delete(id); + this.setWarningText(text, id); + } + }, 100) + ); return; } if (this.isInFlyout) { @@ -1056,14 +1088,16 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, } if (collapsedParent) { collapsedParent.setWarningText( - Msg['COLLAPSED_WARNINGS_WARNING'], BlockSvg.COLLAPSED_WARNING_ID); + Msg['COLLAPSED_WARNINGS_WARNING'], + BlockSvg.COLLAPSED_WARNING_ID + ); } if (!this.warning) { this.warning = new Warning(this); changedState = true; } - this.warning!.setText((text), id); + this.warning!.setText(text, id); } else { // Dispose all warnings if no ID is given. if (this.warning && !id) { @@ -1093,7 +1127,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, * * @param mutator A mutator dialog instance or null to remove. */ - override setMutator(mutator: Mutator|null) { + override setMutator(mutator: Mutator | null) { if (this.mutator && this.mutator !== mutator) { this.mutator.dispose(); } @@ -1186,11 +1220,12 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, * * @param colour HSV hue value, or #RRGGBB string. */ - override setColour(colour: number|string) { + override setColour(colour: number | string) { super.setColour(colour); - const styleObj = - this.workspace.getRenderer().getConstants().getBlockStyleForColour( - this.colour_); + const styleObj = this.workspace + .getRenderer() + .getConstants() + .getBlockStyleForColour(this.colour_); this.pathObject.setStyle(styleObj.style); this.style = styleObj.style; @@ -1206,9 +1241,10 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, * @throws {Error} if the block style does not exist. */ override setStyle(blockStyleName: string) { - const blockStyle = - this.workspace.getRenderer().getConstants().getBlockStyle( - blockStyleName); + const blockStyle = this.workspace + .getRenderer() + .getConstants() + .getBlockStyle(blockStyleName); this.styleName_ = blockStyleName; if (blockStyle) { @@ -1234,7 +1270,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, */ bringToFront() { /* eslint-disable-next-line @typescript-eslint/no-this-alias */ - let block: this|null = this; + let block: this | null = this; do { const root = block.getSvgRoot(); const parent = root.parentNode; @@ -1255,7 +1291,9 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, * if any type could be connected. */ override setPreviousStatement( - newBoolean: boolean, opt_check?: string|string[]|null) { + newBoolean: boolean, + opt_check?: string | string[] | null + ) { super.setPreviousStatement(newBoolean, opt_check); if (this.rendered) { @@ -1272,7 +1310,9 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, * if any type could be connected. */ override setNextStatement( - newBoolean: boolean, opt_check?: string|string[]|null) { + newBoolean: boolean, + opt_check?: string | string[] | null + ) { super.setNextStatement(newBoolean, opt_check); if (this.rendered) { @@ -1288,7 +1328,10 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, * @param opt_check Returned type or list of returned types. Null or * undefined if any type could be returned (e.g. variable get). */ - override setOutput(newBoolean: boolean, opt_check?: string|string[]|null) { + override setOutput( + newBoolean: boolean, + opt_check?: string | string[] | null + ) { super.setOutput(newBoolean, opt_check); if (this.rendered) { @@ -1372,14 +1415,14 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, */ setConnectionTracking(track: boolean) { if (this.previousConnection) { - (this.previousConnection).setTracking(track); + this.previousConnection.setTracking(track); } if (this.outputConnection) { - (this.outputConnection).setTracking(track); + this.outputConnection.setTracking(track); } if (this.nextConnection) { - (this.nextConnection).setTracking(track); - const child = (this.nextConnection).targetBlock(); + this.nextConnection.setTracking(track); + const child = this.nextConnection.targetBlock(); if (child) { child.setConnectionTracking(track); } @@ -1428,7 +1471,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, 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 as RenderedConnection); } @@ -1448,8 +1491,9 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, * @returns The last next connection on the stack, or null. * @internal */ - override lastConnectionInStack(ignoreShadows: boolean): RenderedConnection - |null { + override lastConnectionInStack( + ignoreShadows: boolean + ): RenderedConnection | null { return super.lastConnectionInStack(ignoreShadows) as RenderedConnection; } @@ -1463,8 +1507,10 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, * @returns The matching connection on this block, or null. * @internal */ - override getMatchingConnection(otherBlock: Block, conn: Connection): - RenderedConnection|null { + override getMatchingConnection( + otherBlock: Block, + conn: Connection + ): RenderedConnection | null { return super.getMatchingConnection(otherBlock, conn) as RenderedConnection; } @@ -1483,7 +1529,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, * * @returns The next statement block or null. */ - override getNextBlock(): BlockSvg|null { + override getNextBlock(): BlockSvg | null { return super.getNextBlock() as BlockSvg; } @@ -1492,7 +1538,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, * * @returns The previous statement block or null. */ - override getPreviousBlock(): BlockSvg|null { + override getPreviousBlock(): BlockSvg | null { return super.getPreviousBlock() as BlockSvg; } @@ -1520,8 +1566,11 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, */ private bumpNeighboursInternal() { const root = this.getRootBlock(); - if (this.isDeadOrDying() || this.workspace.isDragging() || - root.isInFlyout) { + if ( + this.isDeadOrDying() || + this.workspace.isDragging() || + root.isInFlyout + ) { return; } @@ -1581,12 +1630,15 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, * @internal */ positionNearConnection( - sourceConnection: RenderedConnection, - targetConnection: RenderedConnection) { + sourceConnection: RenderedConnection, + targetConnection: RenderedConnection + ) { // We only need to position the new block if it's before the existing one, // otherwise its position is set by the previous block. - if (sourceConnection.type === ConnectionType.NEXT_STATEMENT || - sourceConnection.type === ConnectionType.INPUT_VALUE) { + if ( + sourceConnection.type === ConnectionType.NEXT_STATEMENT || + sourceConnection.type === ConnectionType.INPUT_VALUE + ) { const dx = targetConnection.x - sourceConnection.x; const dy = targetConnection.y - sourceConnection.y; @@ -1598,7 +1650,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, * @returns The first statement connection or null. * @internal */ - override getFirstStatementConnection(): RenderedConnection|null { + override getFirstStatementConnection(): RenderedConnection | null { return super.getFirstStatementConnection() as RenderedConnection | null; } @@ -1636,7 +1688,7 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, */ render(opt_bubble?: boolean) { if (this.renderIsInProgress_) { - return; // Don't allow recursive renders. + return; // Don't allow recursive renders. } this.renderIsInProgress_ = true; try { @@ -1784,15 +1836,16 @@ export class BlockSvg extends Block implements IASTNodeLocationSvg, * @returns Object with height and width properties in workspace units. * @internal */ - getHeightWidth(): {height: number, width: number} { + 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 tabHeight = - this.workspace.getRenderer().getConstants().NOTCH_HEIGHT; + const tabHeight = this.workspace + .getRenderer() + .getConstants().NOTCH_HEIGHT; height += nextHeightWidth.height - tabHeight; width = Math.max(width, nextHeightWidth.width); } diff --git a/core/blockly.ts b/core/blockly.ts index 975d64941a3..3d8e3ad320a 100644 --- a/core/blockly.ts +++ b/core/blockly.ts @@ -48,19 +48,75 @@ import {DragTarget} from './drag_target.js'; import * as dropDownDiv from './dropdowndiv.js'; import * as Events from './events/events.js'; import * as Extensions from './extensions.js'; -import {Field, FieldConfig, FieldValidator, UnattachedFieldError} from './field.js'; -import {FieldAngle, FieldAngleConfig, FieldAngleFromJsonConfig, FieldAngleValidator} from './field_angle.js'; -import {FieldCheckbox, FieldCheckboxConfig, FieldCheckboxFromJsonConfig, FieldCheckboxValidator} from './field_checkbox.js'; -import {FieldColour, FieldColourConfig, FieldColourFromJsonConfig, FieldColourValidator} from './field_colour.js'; -import {FieldDropdown, FieldDropdownConfig, FieldDropdownFromJsonConfig, FieldDropdownValidator, MenuGenerator, MenuGeneratorFunction, MenuOption} from './field_dropdown.js'; -import {FieldImage, FieldImageConfig, FieldImageFromJsonConfig} from './field_image.js'; -import {FieldLabel, FieldLabelConfig, FieldLabelFromJsonConfig} from './field_label.js'; +import { + Field, + FieldConfig, + FieldValidator, + UnattachedFieldError, +} from './field.js'; +import { + FieldAngle, + FieldAngleConfig, + FieldAngleFromJsonConfig, + FieldAngleValidator, +} from './field_angle.js'; +import { + FieldCheckbox, + FieldCheckboxConfig, + FieldCheckboxFromJsonConfig, + FieldCheckboxValidator, +} from './field_checkbox.js'; +import { + FieldColour, + FieldColourConfig, + FieldColourFromJsonConfig, + FieldColourValidator, +} from './field_colour.js'; +import { + FieldDropdown, + FieldDropdownConfig, + FieldDropdownFromJsonConfig, + FieldDropdownValidator, + MenuGenerator, + MenuGeneratorFunction, + MenuOption, +} from './field_dropdown.js'; +import { + FieldImage, + FieldImageConfig, + FieldImageFromJsonConfig, +} from './field_image.js'; +import { + FieldLabel, + FieldLabelConfig, + FieldLabelFromJsonConfig, +} from './field_label.js'; import {FieldLabelSerializable} from './field_label_serializable.js'; -import {FieldMultilineInput, FieldMultilineInputConfig, FieldMultilineInputFromJsonConfig, FieldMultilineInputValidator} from './field_multilineinput.js'; -import {FieldNumber, FieldNumberConfig, FieldNumberFromJsonConfig, FieldNumberValidator} from './field_number.js'; +import { + FieldMultilineInput, + FieldMultilineInputConfig, + FieldMultilineInputFromJsonConfig, + FieldMultilineInputValidator, +} from './field_multilineinput.js'; +import { + FieldNumber, + FieldNumberConfig, + FieldNumberFromJsonConfig, + FieldNumberValidator, +} from './field_number.js'; import * as fieldRegistry from './field_registry.js'; -import {FieldTextInput, FieldTextInputConfig, FieldTextInputFromJsonConfig, FieldTextInputValidator} from './field_textinput.js'; -import {FieldVariable, FieldVariableConfig, FieldVariableFromJsonConfig, FieldVariableValidator} from './field_variable.js'; +import { + FieldTextInput, + FieldTextInputConfig, + FieldTextInputFromJsonConfig, + FieldTextInputValidator, +} from './field_textinput.js'; +import { + FieldVariable, + FieldVariableConfig, + FieldVariableFromJsonConfig, + FieldVariableValidator, +} from './field_variable.js'; import {Flyout} from './flyout_base.js'; import {FlyoutButton} from './flyout_button.js'; import {HorizontalFlyout} from './flyout_horizontal.js'; @@ -105,7 +161,10 @@ import {ISelectableToolboxItem} from './interfaces/i_selectable_toolbox_item.js' import {IStyleable} from './interfaces/i_styleable.js'; import {IToolbox} from './interfaces/i_toolbox.js'; import {IToolboxItem} from './interfaces/i_toolbox_item.js'; -import {IVariableBackedParameterModel, isVariableBackedParameterModel} from './interfaces/i_variable_backed_parameter_model.js'; +import { + IVariableBackedParameterModel, + isVariableBackedParameterModel, +} from './interfaces/i_variable_backed_parameter_model.js'; import * as internalConstants from './internal_constants.js'; import {ASTNode} from './keyboard_nav/ast_node.js'; import {BasicCursor} from './keyboard_nav/basic_cursor.js'; @@ -163,11 +222,13 @@ import {WorkspaceComment} from './workspace_comment.js'; import {WorkspaceCommentSvg} from './workspace_comment_svg.js'; import {WorkspaceDragSurfaceSvg} from './workspace_drag_surface_svg.js'; import {WorkspaceDragger} from './workspace_dragger.js'; -import {resizeSvgContents as realResizeSvgContents, WorkspaceSvg} from './workspace_svg.js'; +import { + resizeSvgContents as realResizeSvgContents, + WorkspaceSvg, +} from './workspace_svg.js'; import * as Xml from './xml.js'; import {ZoomControls} from './zoom_controls.js'; - /** * Blockly core version. * This constant is overridden by the build script (npm run build) to the value @@ -327,8 +388,11 @@ export const setParentContainer = common.setParentContainer; */ function resizeSvgContentsLocal(workspace: WorkspaceSvg) { deprecation.warn( - 'Blockly.resizeSvgContents', 'December 2021', 'December 2022', - 'Blockly.WorkspaceSvg.resizeSvgContents'); + 'Blockly.resizeSvgContents', + 'December 2021', + 'December 2022', + 'Blockly.WorkspaceSvg.resizeSvgContents' + ); realResizeSvgContents(workspace); } export const resizeSvgContents = resizeSvgContentsLocal; @@ -342,8 +406,11 @@ export const resizeSvgContents = resizeSvgContentsLocal; */ export function copy(toCopy: ICopyable) { deprecation.warn( - 'Blockly.copy', 'December 2021', 'December 2022', - 'Blockly.clipboard.copy'); + 'Blockly.copy', + 'December 2021', + 'December 2022', + 'Blockly.clipboard.copy' + ); clipboard.copy(toCopy); } @@ -356,8 +423,11 @@ export function copy(toCopy: ICopyable) { */ export function paste(): boolean { deprecation.warn( - 'Blockly.paste', 'December 2021', 'December 2022', - 'Blockly.clipboard.paste'); + 'Blockly.paste', + 'December 2021', + 'December 2022', + 'Blockly.clipboard.paste' + ); return !!clipboard.paste(); } @@ -370,8 +440,11 @@ export function paste(): boolean { */ export function duplicate(toDuplicate: ICopyable) { deprecation.warn( - 'Blockly.duplicate', 'December 2021', 'December 2022', - 'Blockly.clipboard.duplicate'); + 'Blockly.duplicate', + 'December 2021', + 'December 2022', + 'Blockly.clipboard.duplicate' + ); clipboard.duplicate(toDuplicate); } @@ -385,8 +458,11 @@ export function duplicate(toDuplicate: ICopyable) { */ export function isNumber(str: string): boolean { deprecation.warn( - 'Blockly.isNumber', 'December 2021', 'December 2022', - 'Blockly.utils.string.isNumber'); + 'Blockly.isNumber', + 'December 2021', + 'December 2022', + 'Blockly.utils.string.isNumber' + ); return utils.string.isNumber(str); } @@ -400,8 +476,11 @@ export function isNumber(str: string): boolean { */ export function hueToHex(hue: number): string { deprecation.warn( - 'Blockly.hueToHex', 'December 2021', 'December 2022', - 'Blockly.utils.colour.hueToHex'); + 'Blockly.hueToHex', + 'December 2021', + 'December 2022', + 'Blockly.utils.colour.hueToHex' + ); return colour.hueToHex(hue); } @@ -420,11 +499,17 @@ export function hueToHex(hue: number): string { * @see Blockly.browserEvents.bind */ export function bindEvent_( - node: EventTarget, name: string, thisObject: Object|null, - func: Function): browserEvents.Data { + node: EventTarget, + name: string, + thisObject: Object | null, + func: Function +): browserEvents.Data { deprecation.warn( - 'Blockly.bindEvent_', 'December 2021', 'December 2022', - 'Blockly.browserEvents.bind'); + 'Blockly.bindEvent_', + 'December 2021', + 'December 2022', + 'Blockly.browserEvents.bind' + ); return browserEvents.bind(node, name, thisObject, func); } @@ -439,8 +524,11 @@ export function bindEvent_( */ export function unbindEvent_(bindData: browserEvents.Data): Function { deprecation.warn( - 'Blockly.unbindEvent_', 'December 2021', 'December 2022', - 'Blockly.browserEvents.unbind'); + 'Blockly.unbindEvent_', + 'December 2021', + 'December 2022', + 'Blockly.browserEvents.unbind' + ); return browserEvents.unbind(bindData); } @@ -463,14 +551,26 @@ export function unbindEvent_(bindData: browserEvents.Data): Function { * @see browserEvents.conditionalBind */ export function bindEventWithChecks_( - node: EventTarget, name: string, thisObject: Object|null, func: Function, - opt_noCaptureIdentifier?: boolean, - _opt_noPreventDefault?: boolean): browserEvents.Data { + node: EventTarget, + name: string, + thisObject: Object | null, + func: Function, + opt_noCaptureIdentifier?: boolean, + _opt_noPreventDefault?: boolean +): browserEvents.Data { deprecation.warn( - 'Blockly.bindEventWithChecks_', 'December 2021', 'December 2022', - 'Blockly.browserEvents.conditionalBind'); + 'Blockly.bindEventWithChecks_', + 'December 2021', + 'December 2022', + 'Blockly.browserEvents.conditionalBind' + ); return browserEvents.conditionalBind( - node, name, thisObject, func, opt_noCaptureIdentifier); + node, + name, + thisObject, + func, + opt_noCaptureIdentifier + ); } // Aliases to allow external code to access these values for legacy reasons. @@ -495,7 +595,7 @@ export const VARIABLE_CATEGORY_NAME: string = Variables.CATEGORY_NAME; * variable blocks. */ export const VARIABLE_DYNAMIC_CATEGORY_NAME: string = - VariablesDynamic.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 @@ -503,57 +603,61 @@ export const VARIABLE_DYNAMIC_CATEGORY_NAME: string = */ export const PROCEDURE_CATEGORY_NAME: string = Procedures.CATEGORY_NAME; - // Context for why we need to monkey-patch in these functions (internal): // https://docs.google.com/document/d/1MbO0LEA-pAyx1ErGLJnyUqTLrcYTo-5zga9qplnxeXo/edit?usp=sharing&resourcekey=0-5h_32-i-dHwHjf_9KYEVKg // clang-format off -Workspace.prototype.newBlock = - function(prototypeName: string, opt_id?: string): Block { - return new Block(this, prototypeName, opt_id); - }; +Workspace.prototype.newBlock = function ( + prototypeName: string, + opt_id?: string +): Block { + return new Block(this, prototypeName, opt_id); +}; -WorkspaceSvg.prototype.newBlock = - function(prototypeName: string, opt_id?: string): BlockSvg { - return new BlockSvg(this, prototypeName, opt_id); - }; +WorkspaceSvg.prototype.newBlock = function ( + prototypeName: string, + opt_id?: string +): BlockSvg { + return new BlockSvg(this, prototypeName, opt_id); +}; -WorkspaceSvg.newTrashcan = function(workspace: WorkspaceSvg): Trashcan { +WorkspaceSvg.newTrashcan = function (workspace: WorkspaceSvg): Trashcan { return new Trashcan(workspace); }; -WorkspaceCommentSvg.prototype.showContextMenu = - function(this: WorkspaceCommentSvg, e: Event) { - if (this.workspace.options.readOnly) { - return; - } - const menuOptions = []; - - if (this.isDeletable() && this.isMovable()) { - menuOptions.push(ContextMenu.commentDuplicateOption(this)); - menuOptions.push(ContextMenu.commentDeleteOption(this)); - } - - ContextMenu.show(e, menuOptions, this.RTL); - }; - -Mutator.prototype.newWorkspaceSvg = - function(options: Options): WorkspaceSvg { - return new WorkspaceSvg(options); - }; - -Names.prototype.populateProcedures = - function(this: Names, workspace: Workspace) { - const procedures = Procedures.allProcedures(workspace); - // Flatten the return vs no-return procedure lists. - const flattenedProcedures = - procedures[0].concat(procedures[1]); - for (let i = 0; i < flattenedProcedures.length; i++) { - this.getName(flattenedProcedures[i][0], Names.NameType.PROCEDURE); - } - }; -// clang-format on +WorkspaceCommentSvg.prototype.showContextMenu = function ( + this: WorkspaceCommentSvg, + e: Event +) { + if (this.workspace.options.readOnly) { + return; + } + const menuOptions = []; + + if (this.isDeletable() && this.isMovable()) { + menuOptions.push(ContextMenu.commentDuplicateOption(this)); + menuOptions.push(ContextMenu.commentDeleteOption(this)); + } + + ContextMenu.show(e, menuOptions, this.RTL); +}; + +Mutator.prototype.newWorkspaceSvg = function (options: Options): WorkspaceSvg { + return new WorkspaceSvg(options); +}; +Names.prototype.populateProcedures = function ( + this: Names, + workspace: Workspace +) { + const procedures = Procedures.allProcedures(workspace); + // Flatten the return vs no-return procedure lists. + const flattenedProcedures = procedures[0].concat(procedures[1]); + for (let i = 0; i < flattenedProcedures.length; i++) { + this.getName(flattenedProcedures[i][0], Names.NameType.PROCEDURE); + } +}; +// clang-format on // Re-export submodules that no longer declareLegacyNamespace. export {browserEvents}; @@ -668,9 +772,9 @@ export {Flyout}; export {FlyoutButton}; export {FlyoutMetricsManager}; export {CodeGenerator}; -export {CodeGenerator as Generator}; // Deprecated name, October 2022. +export {CodeGenerator as Generator}; // Deprecated name, October 2022. export {Gesture}; -export {Gesture as TouchGesture}; // Remove in v10. +export {Gesture as TouchGesture}; // Remove in v10. export {Grid}; export {HorizontalFlyout}; export {IASTNodeLocation}; diff --git a/core/blockly_options.ts b/core/blockly_options.ts index deabe1f9693..2c8ccea5558 100644 --- a/core/blockly_options.ts +++ b/core/blockly_options.ts @@ -11,7 +11,6 @@ import type {Theme, ITheme} from './theme.js'; import type {WorkspaceSvg} from './workspace_svg.js'; import type {ToolboxDefinition} from './utils/toolbox.js'; - /** * Blockly options. */ @@ -32,14 +31,14 @@ export interface BlocklyOptions { renderer?: string; rendererOverrides?: {[rendererConstant: string]: any}; rtl?: boolean; - scrollbars?: ScrollbarOptions|boolean; + scrollbars?: ScrollbarOptions | boolean; sounds?: boolean; - theme?: Theme|string|ITheme; - toolbox?: string|ToolboxDefinition|Element; + theme?: Theme | string | ITheme; + toolbox?: string | ToolboxDefinition | Element; toolboxPosition?: string; trashcan?: boolean; maxTrashcanContents?: number; - plugins?: {[key: string]: (new(...p1: any[]) => any)|string}; + plugins?: {[key: string]: (new (...p1: any[]) => any) | string}; zoom?: ZoomOptions; parentWorkspace?: WorkspaceSvg; } @@ -53,7 +52,7 @@ export interface GridOptions { export interface MoveOptions { drag?: boolean; - scrollbars?: boolean|ScrollbarOptions; + scrollbars?: boolean | ScrollbarOptions; wheel?: boolean; } diff --git a/core/blocks.ts b/core/blocks.ts index 0b381fa8987..f0c1f183d8f 100644 --- a/core/blocks.ts +++ b/core/blocks.ts @@ -7,7 +7,6 @@ import * as goog from '../closure/goog/goog.js'; goog.declareModuleId('Blockly.blocks'); - /** * A block definition. For now this very loose, but it can potentially * be refined e.g. by replacing this typedef with a class definition. diff --git a/core/browser_events.ts b/core/browser_events.ts index 6723aa7e5a1..84f4eb22977 100644 --- a/core/browser_events.ts +++ b/core/browser_events.ts @@ -11,7 +11,6 @@ import * as Touch from './touch.js'; import * as deprecation from './utils/deprecation.js'; import * as userAgent from './utils/useragent.js'; - /** * Blockly opaque event data used to unbind events when using * `bind` and `conditionalBind`. @@ -49,12 +48,19 @@ const PAGE_MODE_MULTIPLIER = 125; * @returns Opaque data that can be passed to unbindEvent_. */ export function conditionalBind( - node: EventTarget, name: string, thisObject: Object|null, func: Function, - opt_noCaptureIdentifier?: boolean, opt_noPreventDefault?: boolean): Data { + node: EventTarget, + name: string, + thisObject: Object | null, + func: Function, + opt_noCaptureIdentifier?: boolean, + opt_noPreventDefault?: boolean +): Data { if (opt_noPreventDefault !== undefined) { deprecation.warn( - 'The opt_noPreventDefault argument of conditionalBind', 'version 9', - 'version 10'); + 'The opt_noPreventDefault argument of conditionalBind', + 'version 9', + 'version 10' + ); } /** * @@ -99,8 +105,11 @@ export function conditionalBind( * @returns Opaque data that can be passed to unbindEvent_. */ export function bind( - node: EventTarget, name: string, thisObject: Object|null, - func: Function): Data { + node: EventTarget, + name: string, + thisObject: Object | null, + func: Function +): Data { /** * * @param e @@ -154,17 +163,24 @@ export function unbind(bindData: Data): (e: Event) => void { */ export function isTargetInput(e: Event): boolean { if (e.target instanceof HTMLElement) { - if (e.target.isContentEditable || - e.target.getAttribute('data-is-text-input') === 'true') { + if ( + e.target.isContentEditable || + e.target.getAttribute('data-is-text-input') === 'true' + ) { return true; } if (e.target instanceof HTMLInputElement) { const target = e.target; - return target.type === 'text' || target.type === 'number' || - target.type === 'email' || target.type === 'password' || - target.type === 'search' || target.type === 'tel' || - target.type === 'url'; + return ( + target.type === 'text' || + target.type === 'number' || + target.type === 'email' || + target.type === 'password' || + target.type === 'search' || + target.type === 'tel' || + target.type === 'url' + ); } if (e.target instanceof HTMLTextAreaElement) { @@ -200,7 +216,10 @@ export function isRightButton(e: MouseEvent): boolean { * @returns Object with .x and .y properties. */ export function mouseToSvg( - e: MouseEvent, svg: SVGSVGElement, matrix: SVGMatrix|null): SVGPoint { + e: MouseEvent, + svg: SVGSVGElement, + matrix: SVGMatrix | null +): SVGPoint { const svgPoint = svg.createSVGPoint(); svgPoint.x = e.clientX; svgPoint.y = e.clientY; @@ -217,17 +236,17 @@ export function mouseToSvg( * @param e Mouse event. * @returns Scroll delta object with .x and .y properties. */ -export function getScrollDeltaPixels(e: WheelEvent): {x: number, y: number} { +export function getScrollDeltaPixels(e: WheelEvent): {x: number; y: number} { switch (e.deltaMode) { - case 0x00: // Pixel mode. + case 0x00: // Pixel mode. default: return {x: e.deltaX, y: e.deltaY}; - case 0x01: // Line mode. + case 0x01: // Line mode. return { x: e.deltaX * LINE_MODE_MULTIPLIER, y: e.deltaY * LINE_MODE_MULTIPLIER, }; - case 0x02: // Page mode. + case 0x02: // Page mode. return { x: e.deltaX * PAGE_MODE_MULTIPLIER, y: e.deltaY * PAGE_MODE_MULTIPLIER, diff --git a/core/bubble.ts b/core/bubble.ts index c8fa2b0f333..ac8d34925f7 100644 --- a/core/bubble.ts +++ b/core/bubble.ts @@ -27,7 +27,6 @@ import {Svg} from './utils/svg.js'; import * as userAgent from './utils/useragent.js'; import type {WorkspaceSvg} from './workspace_svg.js'; - /** * Class for UI bubble. */ @@ -54,10 +53,10 @@ export class Bubble implements IBubble { static ANCHOR_RADIUS = 8; /** Mouse up event data. */ - private static onMouseUpWrapper: browserEvents.Data|null = null; + private static onMouseUpWrapper: browserEvents.Data | null = null; /** Mouse move event data. */ - private static onMouseMoveWrapper: browserEvents.Data|null = null; + private static onMouseMoveWrapper: browserEvents.Data | null = null; workspace_: WorkspaceSvg; content_: SVGElement; @@ -67,18 +66,18 @@ export class Bubble implements IBubble { private readonly rendered: boolean; /** The SVG group containing all parts of the bubble. */ - private bubbleGroup: SVGGElement|null = null; + private bubbleGroup: SVGGElement | null = null; /** * The SVG path for the arrow from the bubble to the icon on the block. */ - private bubbleArrow: SVGPathElement|null = null; + private bubbleArrow: SVGPathElement | null = null; /** The SVG rect for the main body of the bubble. */ - private bubbleBack: SVGRectElement|null = null; + private bubbleBack: SVGRectElement | null = null; /** The SVG group for the resize hash marks on some bubbles. */ - private resizeGroup: SVGGElement|null = null; + private resizeGroup: SVGGElement | null = null; /** Absolute coordinate of anchor point, in workspace coordinates. */ private anchorXY!: Coordinate; @@ -106,16 +105,16 @@ export class Bubble implements IBubble { private autoLayout = true; /** Method to call on resize of bubble. */ - private resizeCallback: (() => void)|null = null; + private resizeCallback: (() => void) | null = null; /** Method to call on move of bubble. */ - private moveCallback: (() => void)|null = null; + private moveCallback: (() => void) | null = null; /** Mouse down on bubbleBack event data. */ - private onMouseDownBubbleWrapper: browserEvents.Data|null = null; + private onMouseDownBubbleWrapper: browserEvents.Data | null = null; /** Mouse down on resizeGroup event data. */ - private onMouseDownResizeWrapper: browserEvents.Data|null = null; + private onMouseDownResizeWrapper: browserEvents.Data | null = null; /** * Describes whether this bubble has been disposed of (nodes and event @@ -135,9 +134,13 @@ export class Bubble implements IBubble { * @param bubbleHeight Height of bubble, or null if not resizable. */ constructor( - workspace: WorkspaceSvg, content: SVGElement, shape: SVGElement, - anchorXY: Coordinate, bubbleWidth: number|null, - bubbleHeight: number|null) { + workspace: WorkspaceSvg, + content: SVGElement, + shape: SVGElement, + anchorXY: Coordinate, + bubbleWidth: number | null, + bubbleHeight: number | null + ) { this.rendered = false; this.workspace_ = workspace; this.content_ = content; @@ -151,7 +154,8 @@ export class Bubble implements IBubble { const canvas = workspace.getBubbleCanvas(); canvas.appendChild( - this.createDom(content, !!(bubbleWidth && bubbleHeight))); + this.createDom(content, !!(bubbleWidth && bubbleHeight)) + ); this.setAnchorLocation(anchorXY); if (!bubbleWidth || !bubbleHeight) { @@ -192,8 +196,10 @@ export class Bubble implements IBubble { */ this.bubbleGroup = dom.createSvgElement(Svg.G, {}); let filter: {filter?: string} = { - 'filter': 'url(#' + - this.workspace_.getRenderer().getConstants().embossFilterId + ')', + 'filter': + 'url(#' + + this.workspace_.getRenderer().getConstants().embossFilterId + + ')', }; if (userAgent.JavaFx) { // Multiple reports that JavaFX can't handle filters. @@ -203,53 +209,70 @@ export class Bubble implements IBubble { const bubbleEmboss = dom.createSvgElement(Svg.G, filter, this.bubbleGroup); this.bubbleArrow = dom.createSvgElement(Svg.PATH, {}, bubbleEmboss); this.bubbleBack = dom.createSvgElement( - Svg.RECT, { - 'class': 'blocklyDraggable', - 'x': 0, - 'y': 0, - 'rx': Bubble.BORDER_WIDTH, - 'ry': Bubble.BORDER_WIDTH, - }, - bubbleEmboss); + Svg.RECT, + { + 'class': 'blocklyDraggable', + 'x': 0, + 'y': 0, + 'rx': Bubble.BORDER_WIDTH, + 'ry': Bubble.BORDER_WIDTH, + }, + bubbleEmboss + ); if (hasResize) { this.resizeGroup = dom.createSvgElement( - Svg.G, { - 'class': this.workspace_.RTL ? 'blocklyResizeSW' : - 'blocklyResizeSE', - }, - this.bubbleGroup); + Svg.G, + { + 'class': this.workspace_.RTL ? 'blocklyResizeSW' : 'blocklyResizeSE', + }, + this.bubbleGroup + ); const size = 2 * Bubble.BORDER_WIDTH; dom.createSvgElement( - Svg.POLYGON, {'points': `0,${size} ${size},${size} ${size},0`}, - this.resizeGroup); + Svg.POLYGON, + {'points': `0,${size} ${size},${size} ${size},0`}, + this.resizeGroup + ); dom.createSvgElement( - Svg.LINE, { - 'class': 'blocklyResizeLine', - 'x1': size / 3, - 'y1': size - 1, - 'x2': size - 1, - 'y2': size / 3, - }, - this.resizeGroup); + Svg.LINE, + { + 'class': 'blocklyResizeLine', + 'x1': size / 3, + 'y1': size - 1, + 'x2': size - 1, + 'y2': size / 3, + }, + this.resizeGroup + ); dom.createSvgElement( - Svg.LINE, { - 'class': 'blocklyResizeLine', - 'x1': size * 2 / 3, - 'y1': size - 1, - 'x2': size - 1, - 'y2': size * 2 / 3, - }, - this.resizeGroup); + Svg.LINE, + { + 'class': 'blocklyResizeLine', + 'x1': (size * 2) / 3, + 'y1': size - 1, + 'x2': size - 1, + 'y2': (size * 2) / 3, + }, + this.resizeGroup + ); } else { this.resizeGroup = null; } if (!this.workspace_.options.readOnly) { this.onMouseDownBubbleWrapper = browserEvents.conditionalBind( - this.bubbleBack, 'pointerdown', this, this.bubbleMouseDown); + this.bubbleBack, + 'pointerdown', + this, + this.bubbleMouseDown + ); if (this.resizeGroup) { this.onMouseDownResizeWrapper = browserEvents.conditionalBind( - this.resizeGroup, 'pointerdown', this, this.resizeMouseDown); + this.resizeGroup, + 'pointerdown', + this, + this.resizeMouseDown + ); } } this.bubbleGroup.appendChild(content); @@ -329,14 +352,25 @@ export class Bubble implements IBubble { } // Left-click (or middle click) this.workspace_.startDrag( - e, - new Coordinate( - this.workspace_.RTL ? -this.width : this.width, this.height)); + e, + new Coordinate( + this.workspace_.RTL ? -this.width : this.width, + this.height + ) + ); Bubble.onMouseUpWrapper = browserEvents.conditionalBind( - document, 'pointerup', this, Bubble.bubbleMouseUp); + document, + 'pointerup', + this, + Bubble.bubbleMouseUp + ); Bubble.onMouseMoveWrapper = browserEvents.conditionalBind( - document, 'pointermove', this, this.resizeMouseMove); + document, + 'pointermove', + this, + this.resizeMouseMove + ); this.workspace_.hideChaff(); // This event has been handled. No need to bubble up to the document. e.stopPropagation(); @@ -406,8 +440,9 @@ export class Bubble implements IBubble { /** Position the bubble so that it does not fall off-screen. */ private layoutBubble() { // Get the metrics in workspace units. - const viewMetrics = - this.workspace_.getMetricsManager().getViewMetrics(true); + const viewMetrics = this.workspace_ + .getMetricsManager() + .getViewMetrics(true); const optimalLeft = this.getOptimalRelativeLeft(viewMetrics); const optimalTop = this.getOptimalRelativeTop(viewMetrics); @@ -415,30 +450,35 @@ export class Bubble implements IBubble { const topPosition = { x: optimalLeft, - y: -this.height - - this.workspace_.getRenderer().getConstants().MIN_BLOCK_HEIGHT as - number, + y: (-this.height - + this.workspace_.getRenderer().getConstants() + .MIN_BLOCK_HEIGHT) as number, }; const startPosition = {x: -this.width - 30, y: optimalTop}; const endPosition = {x: bbox.width, y: optimalTop}; const bottomPosition = {x: optimalLeft, y: bbox.height}; const closerPosition = - bbox.width < bbox.height ? endPosition : bottomPosition; + bbox.width < bbox.height ? endPosition : bottomPosition; const fartherPosition = - bbox.width < bbox.height ? bottomPosition : endPosition; + bbox.width < bbox.height ? bottomPosition : endPosition; const topPositionOverlap = this.getOverlap(topPosition, viewMetrics); const startPositionOverlap = this.getOverlap(startPosition, viewMetrics); const closerPositionOverlap = this.getOverlap(closerPosition, viewMetrics); - const fartherPositionOverlap = - this.getOverlap(fartherPosition, viewMetrics); + const fartherPositionOverlap = this.getOverlap( + fartherPosition, + viewMetrics + ); // Set the position to whichever position shows the most of the bubble, // with tiebreaks going in the order: top > start > close > far. const mostOverlap = Math.max( - topPositionOverlap, startPositionOverlap, closerPositionOverlap, - fartherPositionOverlap); + topPositionOverlap, + startPositionOverlap, + closerPositionOverlap, + fartherPositionOverlap + ); if (topPositionOverlap === mostOverlap) { this.relativeLeft = topPosition.x; this.relativeTop = topPosition.y; @@ -472,12 +512,14 @@ export class Bubble implements IBubble { * @returns The percentage of the bubble that is visible. */ private getOverlap( - relativeMin: {x: number, y: number}, - viewMetrics: ContainerRegion): number { + relativeMin: {x: number; y: number}, + viewMetrics: ContainerRegion + ): number { // The position of the top-left corner of the bubble in workspace units. const bubbleMin = { - x: this.workspace_.RTL ? this.anchorXY.x - relativeMin.x - this.width : - relativeMin.x + this.anchorXY.x, + x: this.workspace_.RTL + ? this.anchorXY.x - relativeMin.x - this.width + : relativeMin.x + this.anchorXY.x, y: relativeMin.y + this.anchorXY.y, }; // The position of the bottom-right corner of the bubble in workspace units. @@ -499,13 +541,16 @@ export class Bubble implements IBubble { y: viewMetrics.top + viewMetrics.height, }; - const overlapWidth = Math.min(bubbleMax.x, workspaceMax.x) - - Math.max(bubbleMin.x, workspaceMin.x); - const overlapHeight = Math.min(bubbleMax.y, workspaceMax.y) - - Math.max(bubbleMin.y, workspaceMin.y); + const overlapWidth = + Math.min(bubbleMax.x, workspaceMax.x) - + Math.max(bubbleMin.x, workspaceMin.x); + const overlapHeight = + Math.min(bubbleMax.y, workspaceMax.y) - + Math.max(bubbleMin.y, workspaceMin.y); return Math.max( - 0, - Math.min(1, overlapWidth * overlapHeight / (this.width * this.height))); + 0, + Math.min(1, (overlapWidth * overlapHeight) / (this.width * this.height)) + ); } /** @@ -532,9 +577,10 @@ export class Bubble implements IBubble { const bubbleLeft = bubbleRight - this.width; const workspaceRight = viewMetrics.left + viewMetrics.width; - const workspaceLeft = viewMetrics.left + - // Thickness in workspace units. - Scrollbar.scrollbarThickness / this.workspace_.scale; + const workspaceLeft = + viewMetrics.left + + // Thickness in workspace units. + Scrollbar.scrollbarThickness / this.workspace_.scale; if (bubbleLeft < workspaceLeft) { // Slide the bubble right until it is onscreen. @@ -548,9 +594,11 @@ export class Bubble implements IBubble { const bubbleRight = bubbleLeft + this.width; const workspaceLeft = viewMetrics.left; - const workspaceRight = viewMetrics.left + viewMetrics.width - - // Thickness in workspace units. - Scrollbar.scrollbarThickness / this.workspace_.scale; + const workspaceRight = + viewMetrics.left + + viewMetrics.width - + // Thickness in workspace units. + Scrollbar.scrollbarThickness / this.workspace_.scale; if (bubbleLeft < workspaceLeft) { // Slide the bubble right until it is onscreen. @@ -585,9 +633,10 @@ export class Bubble implements IBubble { const bubbleTop = this.anchorXY.y + relativeTop; const bubbleBottom = bubbleTop + this.height; const workspaceTop = viewMetrics.top; - const workspaceBottom = viewMetrics.top + - viewMetrics.height - // Thickness in workspace units. - Scrollbar.scrollbarThickness / this.workspace_.scale; + const workspaceBottom = + viewMetrics.top + + viewMetrics.height - // Thickness in workspace units. + Scrollbar.scrollbarThickness / this.workspace_.scale; const anchorY = this.anchorXY.y; if (bubbleTop < workspaceTop) { @@ -622,7 +671,9 @@ export class Bubble implements IBubble { */ moveTo(x: number, y: number) { this.bubbleGroup?.setAttribute( - 'transform', 'translate(' + x + ',' + y + ')'); + 'transform', + 'translate(' + x + ',' + y + ')' + ); } /** @@ -666,14 +717,22 @@ export class Bubble implements IBubble { // Mirror the resize group. const resizeSize = 2 * Bubble.BORDER_WIDTH; this.resizeGroup.setAttribute( - 'transform', - 'translate(' + resizeSize + ',' + (height - doubleBorderWidth) + - ') scale(-1 1)'); + 'transform', + 'translate(' + + resizeSize + + ',' + + (height - doubleBorderWidth) + + ') scale(-1 1)' + ); } else { this.resizeGroup.setAttribute( - 'transform', - 'translate(' + (width - doubleBorderWidth) + ',' + - (height - doubleBorderWidth) + ')'); + 'transform', + 'translate(' + + (width - doubleBorderWidth) + + ',' + + (height - doubleBorderWidth) + + ')' + ); } } if (this.autoLayout) { @@ -724,7 +783,7 @@ export class Bubble implements IBubble { // Calculate the thickness of the base of the arrow. const bubbleSize = this.getBubbleSize(); let thickness = - (bubbleSize.width + bubbleSize.height) / Bubble.ARROW_THICKNESS; + (bubbleSize.width + bubbleSize.height) / Bubble.ARROW_THICKNESS; thickness = Math.min(thickness, bubbleSize.width, bubbleSize.height) / 4; // Back the tip of the arrow off of the anchor. @@ -743,16 +802,38 @@ export class Bubble implements IBubble { if (swirlAngle > Math.PI * 2) { swirlAngle -= Math.PI * 2; } - const swirlRise = Math.sin(swirlAngle) * hypotenuse / Bubble.ARROW_BEND; - const swirlRun = Math.cos(swirlAngle) * hypotenuse / Bubble.ARROW_BEND; + const swirlRise = (Math.sin(swirlAngle) * hypotenuse) / Bubble.ARROW_BEND; + const swirlRun = (Math.cos(swirlAngle) * hypotenuse) / Bubble.ARROW_BEND; steps.push('M' + baseX1 + ',' + baseY1); steps.push( - 'C' + (baseX1 + swirlRun) + ',' + (baseY1 + swirlRise) + ' ' + - relAnchorX + ',' + relAnchorY + ' ' + relAnchorX + ',' + relAnchorY); + 'C' + + (baseX1 + swirlRun) + + ',' + + (baseY1 + swirlRise) + + ' ' + + relAnchorX + + ',' + + relAnchorY + + ' ' + + relAnchorX + + ',' + + relAnchorY + ); steps.push( - 'C' + relAnchorX + ',' + relAnchorY + ' ' + (baseX2 + swirlRun) + - ',' + (baseY2 + swirlRise) + ' ' + baseX2 + ',' + baseY2); + 'C' + + relAnchorX + + ',' + + relAnchorY + + ' ' + + (baseX2 + swirlRun) + + ',' + + (baseY2 + swirlRise) + + ' ' + + baseX2 + + ',' + + baseY2 + ); } steps.push('z'); this.bubbleArrow?.setAttribute('d', steps.join(' ')); @@ -813,10 +894,11 @@ export class Bubble implements IBubble { */ getRelativeToSurfaceXY(): Coordinate { return new Coordinate( - this.workspace_.RTL ? - -this.relativeLeft + this.anchorXY.x - this.width : - this.anchorXY.x + this.relativeLeft, - this.anchorXY.y + this.relativeTop); + this.workspace_.RTL + ? -this.relativeLeft + this.anchorXY.x - this.width + : this.anchorXY.x + this.relativeLeft, + this.anchorXY.y + this.relativeTop + ); } /** @@ -868,7 +950,10 @@ export class Bubble implements IBubble { const lines = text.split('\n'); for (let i = 0; i < lines.length; i++) { const tspanElement = dom.createSvgElement( - Svg.TSPAN, {'dy': '1em', 'x': Bubble.BORDER_WIDTH}, paragraph); + Svg.TSPAN, + {'dy': '1em', 'x': Bubble.BORDER_WIDTH}, + paragraph + ); const textNode = document.createTextNode(lines[i]); tspanElement.appendChild(textNode); } @@ -885,20 +970,29 @@ export class Bubble implements IBubble { * @internal */ static createNonEditableBubble( - paragraphElement: SVGTextElement, block: BlockSvg, - iconXY: Coordinate): Bubble { + paragraphElement: SVGTextElement, + block: BlockSvg, + iconXY: Coordinate + ): Bubble { const bubble = new Bubble( - block.workspace!, paragraphElement, block.pathObject.svgPath, (iconXY), - null, null); + block.workspace!, + paragraphElement, + block.pathObject.svgPath, + iconXY, + null, + null + ); // Expose this bubble's block's ID on its top-level SVG group. bubble.setSvgId(block.id); if (block.RTL) { // Right-align the paragraph. // This cannot be done until the bubble is rendered on screen. const maxWidth = paragraphElement.getBBox().width; - for (let i = 0, textElement; - textElement = paragraphElement.childNodes[i] as SVGTSpanElement; - i++) { + for ( + let i = 0, textElement; + (textElement = paragraphElement.childNodes[i] as SVGTSpanElement); + i++ + ) { textElement.setAttribute('text-anchor', 'end'); textElement.setAttribute('x', String(maxWidth + Bubble.BORDER_WIDTH)); } diff --git a/core/bubble_dragger.ts b/core/bubble_dragger.ts index f960e399f21..2d3d8582c62 100644 --- a/core/bubble_dragger.ts +++ b/core/bubble_dragger.ts @@ -23,7 +23,6 @@ import {Coordinate} from './utils/coordinate.js'; import {WorkspaceCommentSvg} from './workspace_comment_svg.js'; import type {WorkspaceSvg} from './workspace_svg.js'; - /** * Class for a bubble dragger. It moves things on the bubble canvas around the * workspace when they are being dragged by a mouse or touch. These can be @@ -31,12 +30,12 @@ import type {WorkspaceSvg} from './workspace_svg.js'; */ export class BubbleDragger { /** Which drag target the mouse pointer is over, if any. */ - private dragTarget_: IDragTarget|null = null; + private dragTarget_: IDragTarget | null = null; /** Whether the bubble would be deleted if dropped immediately. */ private wouldDeleteBubble_ = false; private readonly startXY_: Coordinate; - private dragSurface_: BlockDragSurfaceSvg|null; + private dragSurface_: BlockDragSurfaceSvg | null; /** * @param bubble The item on the bubble canvas to drag. @@ -116,11 +115,13 @@ export class BubbleDragger { * @param dragTarget The drag target that the bubblee is currently over. * @returns Whether dropping the bubble immediately would delete the block. */ - private shouldDelete_(dragTarget: IDragTarget|null): boolean { + private shouldDelete_(dragTarget: IDragTarget | null): boolean { if (dragTarget) { const componentManager = this.workspace.getComponentManager(); const isDeleteArea = componentManager.hasCapability( - dragTarget.id, ComponentManager.Capability.DELETE_AREA); + dragTarget.id, + ComponentManager.Capability.DELETE_AREA + ); if (isDeleteArea) { return (dragTarget as IDeleteArea).wouldDelete(this.bubble, false); } @@ -149,7 +150,7 @@ export class BubbleDragger { this.dragBubble(e, currentDragDeltaXY); const preventMove = - this.dragTarget_ && this.dragTarget_.shouldPreventMove(this.bubble); + this.dragTarget_ && this.dragTarget_.shouldPreventMove(this.bubble); let newLoc; if (preventMove) { newLoc = this.startXY_; @@ -187,7 +188,8 @@ export class BubbleDragger { private fireMoveEvent_() { if (this.bubble instanceof WorkspaceCommentSvg) { const event = new (eventUtils.get(eventUtils.COMMENT_MOVE))( - this.bubble) as CommentMove; + this.bubble + ) as CommentMove; event.setOldCoordinate(this.startXY_); event.recordNew(); eventUtils.fire(event); @@ -207,8 +209,9 @@ export class BubbleDragger { */ private pixelsToWorkspaceUnits_(pixelCoord: Coordinate): Coordinate { const result = new Coordinate( - pixelCoord.x / this.workspace.scale, - pixelCoord.y / this.workspace.scale); + pixelCoord.x / this.workspace.scale, + pixelCoord.y / this.workspace.scale + ); if (this.workspace.isMutator) { // If we're in a mutator, its scale is always 1, purely because of some // oddities in our rendering optimizations. The actual scale is the same diff --git a/core/bump_objects.ts b/core/bump_objects.ts index cf17e2777dc..7f48167911b 100644 --- a/core/bump_objects.ts +++ b/core/bump_objects.ts @@ -21,7 +21,6 @@ import * as mathUtils from './utils/math.js'; import type {WorkspaceCommentSvg} from './workspace_comment_svg.js'; import type {WorkspaceSvg} from './workspace_svg.js'; - /** * Bumps the given object that has passed out of bounds. * @@ -34,8 +33,10 @@ import type {WorkspaceSvg} from './workspace_svg.js'; * @returns True if object was bumped. */ function bumpObjectIntoBounds( - workspace: WorkspaceSvg, bounds: ContainerRegion, - object: IBoundedElement): boolean { + workspace: WorkspaceSvg, + bounds: ContainerRegion, + object: IBoundedElement +): boolean { // Compute new top/left position for object. const objectMetrics = object.getBoundingRectangle(); const height = objectMetrics.bottom - objectMetrics.top; @@ -46,8 +47,11 @@ function bumpObjectIntoBounds( const bottomClamp = boundsBottom - height; // If the object is taller than the workspace we want to // top-align the block - const newYPosition = - mathUtils.clamp(topClamp, objectMetrics.top, bottomClamp); + const newYPosition = mathUtils.clamp( + topClamp, + objectMetrics.top, + bottomClamp + ); const deltaY = newYPosition - objectMetrics.top; // Note: Even in RTL mode the "anchor" of the object is the @@ -66,8 +70,11 @@ function bumpObjectIntoBounds( // the right clamp to match. rightClamp = Math.max(leftClamp, rightClamp); } - const newXPosition = - mathUtils.clamp(leftClamp, objectMetrics.left, rightClamp); + const newXPosition = mathUtils.clamp( + leftClamp, + objectMetrics.left, + rightClamp + ); const deltaX = newXPosition - objectMetrics.left; if (deltaX || deltaY) { @@ -84,8 +91,9 @@ export const bumpIntoBounds = bumpObjectIntoBounds; * @param workspace The workspace to handle. * @returns The event handler. */ -export function bumpIntoBoundsHandler(workspace: WorkspaceSvg): - (p1: Abstract) => void { +export function bumpIntoBoundsHandler( + workspace: WorkspaceSvg +): (p1: Abstract) => void { return (e) => { const metricsManager = workspace.getMetricsManager(); if (!metricsManager.hasFixedEdges() || workspace.isDragging()) { @@ -96,8 +104,10 @@ export function bumpIntoBoundsHandler(workspace: WorkspaceSvg): const scrollMetricsInWsCoords = metricsManager.getScrollMetrics(true); // Triggered by move/create event - const object = - extractObjectFromEvent(workspace, e as eventUtils.BumpEvent); + const object = extractObjectFromEvent( + workspace, + e as eventUtils.BumpEvent + ); if (!object) { return; } @@ -106,18 +116,25 @@ export function bumpIntoBoundsHandler(workspace: WorkspaceSvg): eventUtils.setGroup(e.group); const wasBumped = bumpObjectIntoBounds( - workspace, scrollMetricsInWsCoords, (object as IBoundedElement)); + workspace, + scrollMetricsInWsCoords, + object as IBoundedElement + ); if (wasBumped && !e.group) { console.warn( - 'Moved object in bounds but there was no' + - ' event group. This may break undo.'); + 'Moved object in bounds but there was no' + + ' event group. This may break undo.' + ); } eventUtils.setGroup(existingGroup); } else if (e.type === eventUtils.VIEWPORT_CHANGE) { - const viewportEvent = (e as ViewportChange); - if (viewportEvent.scale && viewportEvent.oldScale && - viewportEvent.scale > viewportEvent.oldScale) { + const viewportEvent = e as ViewportChange; + if ( + viewportEvent.scale && + viewportEvent.oldScale && + viewportEvent.scale > viewportEvent.oldScale + ) { bumpTopObjectsIntoBounds(workspace); } } @@ -134,8 +151,9 @@ export function bumpIntoBoundsHandler(workspace: WorkspaceSvg): * object. */ function extractObjectFromEvent( - workspace: WorkspaceSvg, e: eventUtils.BumpEvent): BlockSvg|null| - WorkspaceCommentSvg { + workspace: WorkspaceSvg, + e: eventUtils.BumpEvent +): BlockSvg | null | WorkspaceCommentSvg { let object = null; switch (e.type) { case eventUtils.BLOCK_CREATE: @@ -147,10 +165,9 @@ function extractObjectFromEvent( break; case eventUtils.COMMENT_CREATE: case eventUtils.COMMENT_MOVE: - object = - workspace.getCommentById((e as CommentCreate | CommentMove).commentId! - ) as WorkspaceCommentSvg | - null; + object = workspace.getCommentById( + (e as CommentCreate | CommentMove).commentId! + ) as WorkspaceCommentSvg | null; break; } return object; @@ -169,7 +186,7 @@ export function bumpTopObjectsIntoBounds(workspace: WorkspaceSvg) { const scrollMetricsInWsCoords = metricsManager.getScrollMetrics(true); const topBlocks = workspace.getTopBoundedElements(); - for (let i = 0, block; block = topBlocks[i]; i++) { + for (let i = 0, block; (block = topBlocks[i]); i++) { bumpObjectIntoBounds(workspace, scrollMetricsInWsCoords, block); } } diff --git a/core/clipboard.ts b/core/clipboard.ts index 6b402a5e985..662a1c9738a 100644 --- a/core/clipboard.ts +++ b/core/clipboard.ts @@ -9,9 +9,8 @@ goog.declareModuleId('Blockly.clipboard'); import type {CopyData, ICopyable} from './interfaces/i_copyable.js'; - /** Metadata about the object that is currently on the clipboard. */ -let copyData: CopyData|null = null; +let copyData: CopyData | null = null; /** * Copy a block or workspace comment onto the local clipboard. @@ -36,7 +35,7 @@ function copyInternal(toCopy: ICopyable) { * @returns The pasted thing if the paste was successful, null otherwise. * @internal */ -export function paste(): ICopyable|null { +export function paste(): ICopyable | null { if (!copyData) { return null; } @@ -46,8 +45,10 @@ export function paste(): ICopyable|null { if (workspace.isFlyout) { workspace = workspace.targetWorkspace!; } - if (copyData.typeCounts && - workspace.isCapacityAvailable(copyData.typeCounts)) { + if ( + copyData.typeCounts && + workspace.isCapacityAvailable(copyData.typeCounts) + ) { return workspace.paste(copyData.saveInfo); } return null; @@ -61,18 +62,18 @@ export function paste(): ICopyable|null { * duplication failed. * @internal */ -export function duplicate(toDuplicate: ICopyable): ICopyable|null { +export function duplicate(toDuplicate: ICopyable): ICopyable | null { return TEST_ONLY.duplicateInternal(toDuplicate); } /** * Private version of duplicate for stubbing in tests. */ -function duplicateInternal(toDuplicate: ICopyable): ICopyable|null { +function duplicateInternal(toDuplicate: ICopyable): ICopyable | null { const oldCopyData = copyData; copy(toDuplicate); const pastedThing = - toDuplicate.toCopyData()?.source?.paste(copyData!.saveInfo) ?? null; + toDuplicate.toCopyData()?.source?.paste(copyData!.saveInfo) ?? null; copyData = oldCopyData; return pastedThing; } diff --git a/core/comment.ts b/core/comment.ts index 7468383b3b0..3d2cb26e3f7 100644 --- a/core/comment.ts +++ b/core/comment.ts @@ -29,7 +29,6 @@ import * as dom from './utils/dom.js'; import type {Size} from './utils/size.js'; import {Svg} from './utils/svg.js'; - /** * Class for a comment. */ @@ -40,7 +39,7 @@ export class Comment extends Icon { * The model's text value at the start of an edit. * Used to tell if an event should be fired at the end of an edit. */ - private cachedText: string|null = ''; + private cachedText: string | null = ''; /** * Array holding info needed to unbind events. @@ -52,13 +51,13 @@ export class Comment extends Icon { /** * The SVG element that contains the text edit area, or null if not created. */ - private foreignObject: SVGForeignObjectElement|null = null; + private foreignObject: SVGForeignObjectElement | null = null; /** The editable text area, or null if not created. */ - private textarea_: HTMLTextAreaElement|null = null; + private textarea_: HTMLTextAreaElement | null = null; /** The top-level node of the comment text, or null if not created. */ - private paragraphElement_: SVGTextElement|null = null; + private paragraphElement_: SVGTextElement | null = null; /** @param block The block associated with this comment. */ constructor(block: BlockSvg) { @@ -81,28 +80,35 @@ export class Comment extends Icon { protected override drawIcon_(group: Element) { // Circle. dom.createSvgElement( - Svg.CIRCLE, - {'class': 'blocklyIconShape', 'r': '8', 'cx': '8', 'cy': '8'}, group); + Svg.CIRCLE, + {'class': 'blocklyIconShape', 'r': '8', 'cx': '8', 'cy': '8'}, + group + ); // Can't use a real '?' text character since different browsers and // operating systems render it differently. Body of question mark. dom.createSvgElement( - Svg.PATH, { - 'class': 'blocklyIconSymbol', - 'd': 'm6.8,10h2c0.003,-0.617 0.271,-0.962 0.633,-1.266 2.875,-2.405' + - '0.607,-5.534 -3.765,-3.874v1.7c3.12,-1.657 3.698,0.118 2.336,1.25' + - '-1.201,0.998 -1.201,1.528 -1.204,2.19z', - }, - group); + Svg.PATH, + { + 'class': 'blocklyIconSymbol', + 'd': + 'm6.8,10h2c0.003,-0.617 0.271,-0.962 0.633,-1.266 2.875,-2.405' + + '0.607,-5.534 -3.765,-3.874v1.7c3.12,-1.657 3.698,0.118 2.336,1.25' + + '-1.201,0.998 -1.201,1.528 -1.204,2.19z', + }, + group + ); // Dot of question mark. dom.createSvgElement( - Svg.RECT, { - 'class': 'blocklyIconSymbol', - 'x': '6.8', - 'y': '10.78', - 'height': '2', - 'width': '2', - }, - group); + Svg.RECT, + { + 'class': 'blocklyIconSymbol', + 'x': '6.8', + 'y': '10.78', + 'height': '2', + 'width': '2', + }, + group + ); } /** @@ -123,16 +129,19 @@ export class Comment extends Icon { * For non-editable mode see Warning.textToDom_. */ - this.foreignObject = dom.createSvgElement( - Svg.FOREIGNOBJECT, - {'x': Bubble.BORDER_WIDTH, 'y': Bubble.BORDER_WIDTH}); + this.foreignObject = dom.createSvgElement(Svg.FOREIGNOBJECT, { + 'x': Bubble.BORDER_WIDTH, + 'y': Bubble.BORDER_WIDTH, + }); const body = document.createElementNS(dom.HTML_NS, 'body'); body.setAttribute('xmlns', dom.HTML_NS); body.className = 'blocklyMinimalBody'; - this.textarea_ = document.createElementNS(dom.HTML_NS, 'textarea') as - HTMLTextAreaElement; + this.textarea_ = document.createElementNS( + dom.HTML_NS, + 'textarea' + ) as HTMLTextAreaElement; const textarea = this.textarea_; textarea.className = 'blocklyCommentTextarea'; textarea.setAttribute('dir', this.getBlock().RTL ? 'RTL' : 'LTR'); @@ -142,33 +151,62 @@ export class Comment extends Icon { body.appendChild(textarea); this.foreignObject!.appendChild(body); - this.boundEvents.push(browserEvents.conditionalBind( - textarea, 'focus', this, this.startEdit, true)); + this.boundEvents.push( + browserEvents.conditionalBind( + textarea, + 'focus', + this, + this.startEdit, + true + ) + ); // Don't zoom with mousewheel. - this.boundEvents.push(browserEvents.conditionalBind( - textarea, 'wheel', this, function(e: Event) { + this.boundEvents.push( + browserEvents.conditionalBind( + textarea, + 'wheel', + this, + function (e: Event) { e.stopPropagation(); - })); - this.boundEvents.push(browserEvents.conditionalBind( - textarea, 'change', this, + } + ) + ); + this.boundEvents.push( + browserEvents.conditionalBind( + textarea, + 'change', + this, /** * @param _e Unused event parameter. */ - function(this: Comment, _e: Event) { + function (this: Comment, _e: Event) { if (this.cachedText !== this.model.text) { - eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CHANGE))( - this.getBlock(), 'comment', null, this.cachedText, - this.model.text)); + eventUtils.fire( + new (eventUtils.get(eventUtils.BLOCK_CHANGE))( + this.getBlock(), + 'comment', + null, + this.cachedText, + this.model.text + ) + ); } - })); - this.boundEvents.push(browserEvents.conditionalBind( - textarea, 'input', this, + } + ) + ); + this.boundEvents.push( + browserEvents.conditionalBind( + textarea, + 'input', + this, /** * @param _e Unused event parameter. */ - function(this: Comment, _e: Event) { + function (this: Comment, _e: Event) { this.model.text = textarea.value; - })); + } + ) + ); setTimeout(textarea.focus.bind(textarea), 0); @@ -225,8 +263,13 @@ export class Comment extends Icon { if (visible === this.isVisible()) { return; } - eventUtils.fire(new (eventUtils.get(eventUtils.BUBBLE_OPEN))( - this.getBlock(), visible, 'comment')); + eventUtils.fire( + new (eventUtils.get(eventUtils.BUBBLE_OPEN))( + this.getBlock(), + visible, + 'comment' + ) + ); this.model.pinned = visible; if (visible) { this.createBubble(); @@ -248,9 +291,13 @@ export class Comment extends Icon { private createEditableBubble() { const block = this.getBlock(); this.bubble_ = new Bubble( - block.workspace, this.createEditor(), block.pathObject.svgPath, - (this.iconXY_ as Coordinate), this.model.size.width, - this.model.size.height); + block.workspace, + this.createEditor(), + block.pathObject.svgPath, + this.iconXY_ as Coordinate, + this.model.size.width, + this.model.size.height + ); // Expose this comment's block's ID on its top-level SVG group. this.bubble_.setSvgId(block.id); this.bubble_.registerResizeEvent(this.onBubbleResize.bind(this)); @@ -264,7 +311,10 @@ export class Comment extends Icon { // TODO (#2917): It would be great if the comment could support line breaks. this.paragraphElement_ = Bubble.textToDom(this.model.text ?? ''); this.bubble_ = Bubble.createNonEditableBubble( - this.paragraphElement_, this.getBlock(), this.iconXY_ as Coordinate); + this.paragraphElement_, + this.getBlock(), + this.iconXY_ as Coordinate + ); this.applyColour(); } diff --git a/core/common.ts b/core/common.ts index b50eb7b6843..ec10d222d20 100644 --- a/core/common.ts +++ b/core/common.ts @@ -15,18 +15,16 @@ import type {ICopyable} from './interfaces/i_copyable.js'; import type {Workspace} from './workspace.js'; import type {WorkspaceSvg} from './workspace_svg.js'; - /** Database of all workspaces. */ const WorkspaceDB_ = Object.create(null); - /** * Find the workspace with the specified ID. * * @param id ID of workspace to find. * @returns The sought after workspace or null if not found. */ -export function getWorkspaceById(id: string): Workspace|null { +export function getWorkspaceById(id: string): Workspace | null { return WorkspaceDB_[id] || null; } @@ -90,12 +88,12 @@ export function setMainWorkspace(workspace: Workspace) { /** * Currently selected copyable object. */ -let selected: ICopyable|null = null; +let selected: ICopyable | null = null; /** * Returns the currently selected copyable object. */ -export function getSelected(): ICopyable|null { +export function getSelected(): ICopyable | null { return selected; } @@ -107,14 +105,14 @@ export function getSelected(): ICopyable|null { * @param newSelection The newly selected block. * @internal */ -export function setSelected(newSelection: ICopyable|null) { +export function setSelected(newSelection: ICopyable | null) { selected = newSelection; } /** * Container element in which to render the WidgetDiv, DropDownDiv and Tooltip. */ -let parentContainer: Element|null; +let parentContainer: Element | null; /** * Get the container element in which to render the WidgetDiv, DropDownDiv and @@ -122,7 +120,7 @@ let parentContainer: Element|null; * * @returns The parent container. */ -export function getParentContainer(): Element|null { +export function getParentContainer(): Element | null { return parentContainer; } @@ -189,7 +187,9 @@ export const draggingConnections: Connection[] = []; * @returns Map of types to type counts for descendants of the bock. */ export function getBlockTypeCounts( - block: Block, opt_stripFollowing?: boolean): {[key: string]: number} { + block: Block, + opt_stripFollowing?: boolean +): {[key: string]: number} { const typeCountsMap = Object.create(null); const descendants = block.getDescendants(true); if (opt_stripFollowing) { @@ -199,7 +199,7 @@ export function getBlockTypeCounts( descendants.splice(index, descendants.length - index); } } - for (let i = 0, checkBlock; checkBlock = descendants[i]; i++) { + for (let i = 0, checkBlock; (checkBlock = descendants[i]); i++) { if (typeCountsMap[checkBlock.type]) { typeCountsMap[checkBlock.type]++; } else { @@ -218,7 +218,7 @@ export function getBlockTypeCounts( * of jsonDef. */ function jsonInitFactory(jsonDef: AnyDuringMigration): () => void { - return function(this: Block) { + return function (this: Block) { this.jsonInit(jsonDef); }; } @@ -249,7 +249,8 @@ function defineBlocksWithJsonArrayInternal(jsonArray: AnyDuringMigration[]) { * definitions created. */ export function createBlockDefinitionsFromJsonArray( - jsonArray: AnyDuringMigration[]): {[key: string]: BlockDefinition} { + jsonArray: AnyDuringMigration[] +): {[key: string]: BlockDefinition} { const blocks: {[key: string]: BlockDefinition} = {}; for (let i = 0; i < jsonArray.length; i++) { const elem = jsonArray[i]; @@ -260,8 +261,9 @@ export function createBlockDefinitionsFromJsonArray( const type = elem['type']; if (!type) { console.warn( - `Block definition #${i} in JSON array is missing a type attribute. ` + - 'Skipping.'); + `Block definition #${i} in JSON array is missing a type attribute. ` + + 'Skipping.' + ); continue; } blocks[type] = {init: jsonInitFactory(elem)}; diff --git a/core/component_manager.ts b/core/component_manager.ts index 6b014baa424..4eed1d2a4c2 100644 --- a/core/component_manager.ts +++ b/core/component_manager.ts @@ -19,7 +19,6 @@ import type {IDragTarget} from './interfaces/i_drag_target.js'; import type {IPositionable} from './interfaces/i_positionable.js'; import * as arrayUtils from './utils/array.js'; - class Capability<_T> { static POSITIONABLE = new Capability('positionable'); static DRAG_TARGET = new Capability('drag_target'); @@ -67,8 +66,12 @@ export class ComponentManager { const id = componentInfo.component.id; if (!opt_allowOverrides && this.componentData.has(id)) { throw Error( - 'Plugin "' + id + '" with capabilities "' + - this.componentData.get(id)?.capabilities + '" already added.'); + 'Plugin "' + + id + + '" with capabilities "' + + this.componentData.get(id)?.capabilities + + '" already added.' + ); } this.componentData.set(id, componentInfo); const stringCapabilities = []; @@ -107,15 +110,20 @@ export class ComponentManager { * @param id The ID of the component to add the capability to. * @param capability The capability to add. */ - addCapability(id: string, capability: string|Capability) { + addCapability(id: string, capability: string | Capability) { if (!this.getComponent(id)) { throw Error( - 'Cannot add capability, "' + capability + '". Plugin "' + id + - '" has not been added to the ComponentManager'); + 'Cannot add capability, "' + + capability + + '". Plugin "' + + id + + '" has not been added to the ComponentManager' + ); } if (this.hasCapability(id, capability)) { console.warn( - 'Plugin "' + id + 'already has capability "' + capability + '"'); + 'Plugin "' + id + 'already has capability "' + capability + '"' + ); return; } capability = `${capability}`.toLowerCase(); @@ -129,16 +137,24 @@ export class ComponentManager { * @param id The ID of the component to remove the capability from. * @param capability The capability to remove. */ - removeCapability(id: string, capability: string|Capability) { + removeCapability(id: string, capability: string | Capability) { if (!this.getComponent(id)) { throw Error( - 'Cannot remove capability, "' + capability + '". Plugin "' + id + - '" has not been added to the ComponentManager'); + 'Cannot remove capability, "' + + capability + + '". Plugin "' + + id + + '" has not been added to the ComponentManager' + ); } if (!this.hasCapability(id, capability)) { console.warn( - 'Plugin "' + id + 'doesn\'t have capability "' + capability + - '" to remove'); + 'Plugin "' + + id + + 'doesn\'t have capability "' + + capability + + '" to remove' + ); return; } capability = `${capability}`.toLowerCase(); @@ -153,10 +169,12 @@ export class ComponentManager { * @param capability The capability to check for. * @returns Whether the component has the capability. */ - hasCapability(id: string, capability: string|Capability): boolean { + hasCapability(id: string, capability: string | Capability): boolean { capability = `${capability}`.toLowerCase(); - return this.componentData.has(id) && - this.componentData.get(id)!.capabilities.indexOf(capability) !== -1; + return ( + this.componentData.has(id) && + this.componentData.get(id)!.capabilities.indexOf(capability) !== -1 + ); } /** @@ -165,7 +183,7 @@ export class ComponentManager { * @param id The ID of the component to get. * @returns The component with the given name or undefined if not found. */ - getComponent(id: string): IComponent|undefined { + getComponent(id: string): IComponent | undefined { return this.componentData.get(id)?.component; } @@ -177,7 +195,9 @@ export class ComponentManager { * @returns The components that match the specified capability. */ getComponents( - capability: string|Capability, sorted: boolean): T[] { + capability: string | Capability, + sorted: boolean + ): T[] { capability = `${capability}`.toLowerCase(); const componentIds = this.capabilityToComponentIds.get(capability); if (!componentIds) { @@ -189,10 +209,10 @@ export class ComponentManager { componentIds.forEach((id) => { componentDataList.push(this.componentData.get(id)!); }); - componentDataList.sort(function(a, b) { + componentDataList.sort(function (a, b) { return a.weight - b.weight; }); - componentDataList.forEach(function(componentDatum) { + componentDataList.forEach(function (componentDatum) { components.push(componentDatum.component as T); }); } else { @@ -208,7 +228,7 @@ export namespace ComponentManager { /** An object storing component information. */ export interface ComponentDatum { component: IComponent; - capabilities: Array>; + capabilities: Array>; weight: number; } } diff --git a/core/config.ts b/core/config.ts index 549a6aaf707..b1b05063bb6 100644 --- a/core/config.ts +++ b/core/config.ts @@ -7,7 +7,6 @@ import * as goog from '../closure/goog/goog.js'; goog.declareModuleId('Blockly.config'); - /** * All the values that we expect developers to be able to change * before injecting Blockly. diff --git a/core/connection.ts b/core/connection.ts index 5b4d86588d6..9eb4edacaaf 100644 --- a/core/connection.ts +++ b/core/connection.ts @@ -22,7 +22,6 @@ import type {IConnectionChecker} from './interfaces/i_connection_checker.js'; import * as blocks from './serialization/blocks.js'; import * as Xml from './xml.js'; - /** * Class for a connection between blocks. */ @@ -41,7 +40,7 @@ export class Connection implements IASTNodeLocationWithBlock { protected sourceBlock_: Block; /** Connection this connection connects to. Null if not connected. */ - targetConnection: Connection|null = null; + targetConnection: Connection | null = null; /** * Has this connection been disposed of? @@ -51,10 +50,10 @@ export class Connection implements IASTNodeLocationWithBlock { disposed = false; /** List of compatible value types. Null if all types are compatible. */ - private check: string[]|null = null; + private check: string[] | null = null; /** DOM representation of a shadow block, or null if none. */ - private shadowDom: Element|null = null; + private shadowDom: Element | null = null; /** * Horizontal location of this connection. @@ -70,7 +69,7 @@ export class Connection implements IASTNodeLocationWithBlock { */ y = 0; - private shadowState: blocks.State|null = null; + private shadowState: blocks.State | null = null; /** * @param source The block establishing this connection. @@ -113,8 +112,9 @@ export class Connection implements IASTNodeLocationWithBlock { // Connect the new connection to the parent. let event; if (eventUtils.isEnabled()) { - event = - new (eventUtils.get(eventUtils.BLOCK_MOVE))(childBlock) as BlockMove; + event = new (eventUtils.get(eventUtils.BLOCK_MOVE))( + childBlock + ) as BlockMove; event.setReason(['connect']); } connectReciprocally(this, childConnection); @@ -126,11 +126,15 @@ export class Connection implements IASTNodeLocationWithBlock { // Deal with the orphan if it exists. if (orphan) { - const orphanConnection = this.type === INPUT ? orphan.outputConnection : - orphan.previousConnection; + const orphanConnection = + this.type === INPUT + ? orphan.outputConnection + : orphan.previousConnection; if (!orphanConnection) return; const connection = Connection.getConnectionForOrphanedConnection( - childBlock, orphanConnection); + childBlock, + orphanConnection + ); if (connection) { orphanConnection.connect(connection); } else { @@ -176,8 +180,10 @@ export class Connection implements IASTNodeLocationWithBlock { * @returns True if connection faces down or right. */ isSuperior(): boolean { - return this.type === ConnectionType.INPUT_VALUE || - this.type === ConnectionType.NEXT_STATEMENT; + return ( + this.type === ConnectionType.INPUT_VALUE || + this.type === ConnectionType.NEXT_STATEMENT + ); } /** @@ -259,7 +265,7 @@ export class Connection implements IASTNodeLocationWithBlock { */ protected disconnectInternal(setParent = true) { const {parentConnection, childConnection} = - this.getParentAndChildConnections(); + this.getParentAndChildConnections(); if (!parentConnection || !childConnection) { throw Error('Source connection not connected.'); } @@ -272,7 +278,8 @@ export class Connection implements IASTNodeLocationWithBlock { let event; if (eventUtils.isEnabled()) { event = new (eventUtils.get(eventUtils.BLOCK_MOVE))( - childConnection.getSourceBlock()) as BlockMove; + childConnection.getSourceBlock() + ) as BlockMove; event.setReason(['disconnect']); } const otherConnection = this.targetConnection; @@ -301,8 +308,10 @@ export class Connection implements IASTNodeLocationWithBlock { * @returns The parent connection and child connection, given this connection * and the connection it is connected to. */ - protected getParentAndChildConnections(): - {parentConnection?: Connection, childConnection?: Connection} { + protected getParentAndChildConnections(): { + parentConnection?: Connection; + childConnection?: Connection; + } { if (!this.targetConnection) return {}; if (this.isSuperior()) { return { @@ -329,7 +338,7 @@ export class Connection implements IASTNodeLocationWithBlock { * * @returns The connected block or null if none is connected. */ - targetBlock(): Block|null { + targetBlock(): Block | null { if (this.isConnected()) { return this.targetConnection?.getSourceBlock() ?? null; } @@ -341,10 +350,15 @@ export class Connection implements IASTNodeLocationWithBlock { */ protected onCheckChanged_() { // The new value type may not be compatible with the existing connection. - if (this.isConnected() && - (!this.targetConnection || - !this.getConnectionChecker().canConnect( - this, this.targetConnection, false))) { + if ( + this.isConnected() && + (!this.targetConnection || + !this.getConnectionChecker().canConnect( + this, + this.targetConnection, + false + )) + ) { const child = this.isSuperior() ? this.targetBlock() : this.sourceBlock_; child!.unplug(); } @@ -357,7 +371,7 @@ export class Connection implements IASTNodeLocationWithBlock { * types are compatible. * @returns The connection being modified (to allow chaining). */ - setCheck(check: string|string[]|null): Connection { + setCheck(check: string | string[] | null): Connection { if (check) { if (!Array.isArray(check)) { check = [check]; @@ -376,7 +390,7 @@ export class Connection implements IASTNodeLocationWithBlock { * @returns List of compatible value types. * Null if all types are compatible. */ - getCheck(): string[]|null { + getCheck(): string[] | null { return this.check; } @@ -385,7 +399,7 @@ export class Connection implements IASTNodeLocationWithBlock { * * @param shadowDom DOM representation of a block or null. */ - setShadowDom(shadowDom: Element|null) { + setShadowDom(shadowDom: Element | null) { this.setShadowStateInternal({shadowDom}); } @@ -398,10 +412,10 @@ export class Connection implements IASTNodeLocationWithBlock { * just returned. * @returns Shadow DOM representation of a block or null. */ - getShadowDom(returnCurrent?: boolean): Element|null { - return returnCurrent && this.targetBlock()!.isShadow() ? - Xml.blockToDom((this.targetBlock() as Block)) as Element : - this.shadowDom; + getShadowDom(returnCurrent?: boolean): Element | null { + return returnCurrent && this.targetBlock()!.isShadow() + ? (Xml.blockToDom(this.targetBlock() as Block) as Element) + : this.shadowDom; } /** @@ -409,7 +423,7 @@ export class Connection implements IASTNodeLocationWithBlock { * * @param shadowState An state represetation of the block or null. */ - setShadowState(shadowState: blocks.State|null) { + setShadowState(shadowState: blocks.State | null) { this.setShadowStateInternal({shadowState}); } @@ -423,7 +437,7 @@ export class Connection implements IASTNodeLocationWithBlock { * returned. * @returns Serialized object representation of the block, or null. */ - getShadowState(returnCurrent?: boolean): blocks.State|null { + getShadowState(returnCurrent?: boolean): blocks.State | null { if (returnCurrent && this.targetBlock() && this.targetBlock()!.isShadow()) { return blocks.save(this.targetBlock() as Block); } @@ -454,7 +468,7 @@ export class Connection implements IASTNodeLocationWithBlock { * exists. * @internal */ - getParentInput(): Input|null { + getParentInput(): Input | null { let parentInput = null; const inputs = this.sourceBlock_.inputList; for (let i = 0; i < inputs.length; i++) { @@ -486,7 +500,7 @@ export class Connection implements IASTNodeLocationWithBlock { msg = 'Next Connection of '; } else { let parentInput = null; - for (let i = 0, input; input = block.inputList[i]; i++) { + for (let i = 0, input; (input = block.inputList[i]); i++) { if (input.connection === this) { parentInput = input; break; @@ -508,8 +522,10 @@ export class Connection implements IASTNodeLocationWithBlock { * * @returns The state of both the shadowDom_ and shadowState_ properties. */ - private stashShadowState(): - {shadowDom: Element|null, shadowState: blocks.State|null} { + private stashShadowState(): { + shadowDom: Element | null; + shadowState: blocks.State | null; + } { const shadowDom = this.getShadowDom(true); const shadowState = this.getShadowState(true); // Set to null so it doesn't respawn. @@ -524,9 +540,12 @@ export class Connection implements IASTNodeLocationWithBlock { * @param param0 The state to reapply to the shadowDom_ and shadowState_ * properties. */ - private applyShadowState({shadowDom, shadowState}: { - shadowDom: Element|null, - shadowState: blocks.State|null + private applyShadowState({ + shadowDom, + shadowState, + }: { + shadowDom: Element | null; + shadowState: blocks.State | null; }) { this.shadowDom = shadowDom; this.shadowState = shadowState; @@ -537,9 +556,12 @@ export class Connection implements IASTNodeLocationWithBlock { * * @param param0 The state to set the shadow of this connection to. */ - private setShadowStateInternal({shadowDom = null, shadowState = null}: { - shadowDom?: Element|null, - shadowState?: blocks.State|null + private setShadowStateInternal({ + shadowDom = null, + shadowState = null, + }: { + shadowDom?: Element | null; + shadowState?: blocks.State | null; } = {}) { // One or both of these should always be null. // If neither is null, the shadowState will get priority. @@ -577,11 +599,11 @@ export class Connection implements IASTNodeLocationWithBlock { * @returns The shadow block that was created, or null if both the * shadowState_ and shadowDom_ are null. */ - private createShadowBlock(attemptToConnect: boolean): Block|null { + private createShadowBlock(attemptToConnect: boolean): Block | null { const parentBlock = this.getSourceBlock(); const shadowState = this.getShadowState(); const shadowDom = this.getShadowDom(); - if (parentBlock.isDeadOrDying() || !shadowState && !shadowDom) { + if (parentBlock.isDeadOrDying() || (!shadowState && !shadowDom)) { return null; } @@ -614,7 +636,8 @@ export class Connection implements IASTNodeLocationWithBlock { } } else { throw new Error( - 'Cannot connect a shadow block to a previous/output connection'); + 'Cannot connect a shadow block to a previous/output connection' + ); } } return blockShadow; @@ -628,7 +651,7 @@ export class Connection implements IASTNodeLocationWithBlock { * * @param shadow The shadow to serialize, or null. */ - private serializeShadow(shadow: Block|null) { + private serializeShadow(shadow: Block | null) { if (!shadow) { return; } @@ -646,10 +669,14 @@ export class Connection implements IASTNodeLocationWithBlock { * @returns The suitable connection point on the chain of blocks, or null. */ static getConnectionForOrphanedConnection( - startBlock: Block, orphanConnection: Connection): Connection|null { + startBlock: Block, + orphanConnection: Connection + ): Connection | null { if (orphanConnection.type === ConnectionType.OUTPUT_VALUE) { return getConnectionForOrphanedOutput( - startBlock, orphanConnection.getSourceBlock()); + startBlock, + orphanConnection.getSourceBlock() + ); } // Otherwise we're dealing with a stack. const connection = startBlock.lastConnectionInStack(true); @@ -684,17 +711,19 @@ function connectReciprocally(first: Connection, second: Connection) { * @param orphanBlock The inferior block. * @returns The suitable connection point on 'block', or null. */ -function getSingleConnection(block: Block, orphanBlock: Block): Connection| - null { +function getSingleConnection( + block: Block, + orphanBlock: Block +): Connection | null { let foundConnection = null; const output = orphanBlock.outputConnection; const typeChecker = output?.getConnectionChecker(); - for (let i = 0, input; input = block.inputList[i]; i++) { + for (let i = 0, input; (input = block.inputList[i]); i++) { const connection = input.connection; if (connection && typeChecker?.canConnect(output, connection, false)) { if (foundConnection) { - return null; // More than one connection. + return null; // More than one connection. } foundConnection = connection; } @@ -714,10 +743,12 @@ function getSingleConnection(block: Block, orphanBlock: Block): Connection| * @returns The suitable connection point on the chain of blocks, or null. */ function getConnectionForOrphanedOutput( - startBlock: Block, orphanBlock: Block): Connection|null { - let newBlock: Block|null = startBlock; + startBlock: Block, + orphanBlock: Block +): Connection | null { + let newBlock: Block | null = startBlock; let connection; - while (connection = getSingleConnection(newBlock, orphanBlock)) { + while ((connection = getSingleConnection(newBlock, orphanBlock))) { newBlock = connection.targetBlock(); if (!newBlock || newBlock.isShadow()) { return connection; diff --git a/core/connection_checker.ts b/core/connection_checker.ts index 662037a9358..25cf8713028 100644 --- a/core/connection_checker.ts +++ b/core/connection_checker.ts @@ -21,7 +21,6 @@ import * as internalConstants from './internal_constants.js'; import * as registry from './registry.js'; import type {RenderedConnection} from './rendered_connection.js'; - /** * Class for connection type checking logic. */ @@ -38,10 +37,15 @@ export class ConnectionChecker implements IConnectionChecker { * @returns Whether the connection is legal. */ canConnect( - a: Connection|null, b: Connection|null, isDragging: boolean, - opt_distance?: number): boolean { - return this.canConnectWithReason(a, b, isDragging, opt_distance) === - Connection.CAN_CONNECT; + a: Connection | null, + b: Connection | null, + isDragging: boolean, + opt_distance?: number + ): boolean { + return ( + this.canConnectWithReason(a, b, isDragging, opt_distance) === + Connection.CAN_CONNECT + ); } /** @@ -57,8 +61,11 @@ export class ConnectionChecker implements IConnectionChecker { * otherwise. */ canConnectWithReason( - a: Connection|null, b: Connection|null, isDragging: boolean, - opt_distance?: number): number { + a: Connection | null, + b: Connection | null, + isDragging: boolean, + opt_distance?: number + ): number { const safety = this.doSafetyChecks(a, b); if (safety !== Connection.CAN_CONNECT) { return safety; @@ -71,10 +78,14 @@ export class ConnectionChecker implements IConnectionChecker { return Connection.REASON_CHECKS_FAILED; } - if (isDragging && - !this.doDragChecks( - a as RenderedConnection, b as RenderedConnection, - opt_distance || 0)) { + if ( + isDragging && + !this.doDragChecks( + a as RenderedConnection, + b as RenderedConnection, + opt_distance || 0 + ) + ) { return Connection.REASON_DRAG_CHECKS_FAILED; } @@ -89,8 +100,11 @@ export class ConnectionChecker implements IConnectionChecker { * @param b The second of the two connections being checked. * @returns A developer-readable error string. */ - getErrorMessage(errorCode: number, a: Connection|null, b: Connection|null): - string { + getErrorMessage( + errorCode: number, + a: Connection | null, + b: Connection | null + ): string { switch (errorCode) { case Connection.REASON_SELF_CONNECTION: return 'Attempted to connect a block to itself.'; @@ -105,8 +119,12 @@ export class ConnectionChecker implements IConnectionChecker { const connOne = a!; const connTwo = b!; let msg = 'Connection checks failed. '; - msg += connOne + ' expected ' + connOne.getCheck() + ', found ' + - connTwo.getCheck(); + msg += + connOne + + ' expected ' + + connOne.getCheck() + + ', found ' + + connTwo.getCheck(); return msg; } case Connection.REASON_SHADOW_PARENT: @@ -128,7 +146,7 @@ export class ConnectionChecker implements IConnectionChecker { * @param b The second of the connections to check. * @returns An enum with the reason this connection is safe or unsafe. */ - doSafetyChecks(a: Connection|null, b: Connection|null): number { + doSafetyChecks(a: Connection | null, b: Connection | null): number { if (!a || !b) { return Connection.REASON_TARGET_NULL; } @@ -150,22 +168,25 @@ export class ConnectionChecker implements IConnectionChecker { if (superiorBlock === inferiorBlock) { return Connection.REASON_SELF_CONNECTION; } else if ( - inferiorConnection.type !== - internalConstants.OPPOSITE_TYPE[superiorConnection.type]) { + inferiorConnection.type !== + internalConstants.OPPOSITE_TYPE[superiorConnection.type] + ) { return Connection.REASON_WRONG_TYPE; } else if (superiorBlock.workspace !== inferiorBlock.workspace) { return Connection.REASON_DIFFERENT_WORKSPACES; } else if (superiorBlock.isShadow() && !inferiorBlock.isShadow()) { return Connection.REASON_SHADOW_PARENT; } else if ( - inferiorConnection.type === ConnectionType.OUTPUT_VALUE && - inferiorBlock.previousConnection && - inferiorBlock.previousConnection.isConnected()) { + inferiorConnection.type === ConnectionType.OUTPUT_VALUE && + inferiorBlock.previousConnection && + inferiorBlock.previousConnection.isConnected() + ) { return Connection.REASON_PREVIOUS_AND_OUTPUT; } else if ( - inferiorConnection.type === ConnectionType.PREVIOUS_STATEMENT && - inferiorBlock.outputConnection && - inferiorBlock.outputConnection.isConnected()) { + inferiorConnection.type === ConnectionType.PREVIOUS_STATEMENT && + inferiorBlock.outputConnection && + inferiorBlock.outputConnection.isConnected() + ) { return Connection.REASON_PREVIOUS_AND_OUTPUT; } return Connection.CAN_CONNECT; @@ -206,8 +227,11 @@ export class ConnectionChecker implements IConnectionChecker { * @param distance The maximum allowable distance between connections. * @returns True if the connection is allowed during a drag. */ - doDragChecks(a: RenderedConnection, b: RenderedConnection, distance: number): - boolean { + doDragChecks( + a: RenderedConnection, + b: RenderedConnection, + distance: number + ): boolean { if (a.distanceFrom(b) > distance) { return false; } @@ -223,8 +247,10 @@ export class ConnectionChecker implements IConnectionChecker { case ConnectionType.OUTPUT_VALUE: { // Don't offer to connect an already connected left (male) value plug to // an available right (female) value plug. - if (b.isConnected() && !b.targetBlock()!.isInsertionMarker() || - a.isConnected()) { + if ( + (b.isConnected() && !b.targetBlock()!.isInsertionMarker()) || + a.isConnected() + ) { return false; } break; @@ -233,8 +259,11 @@ export class ConnectionChecker implements IConnectionChecker { // Offering to connect the left (male) of a value block to an already // connected value pair is ok, we'll splice it in. // However, don't offer to splice into an immovable block. - if (b.isConnected() && !b.targetBlock()!.isMovable() && - !b.targetBlock()!.isShadow()) { + if ( + b.isConnected() && + !b.targetBlock()!.isMovable() && + !b.targetBlock()!.isShadow() + ) { return false; } break; @@ -244,15 +273,22 @@ export class ConnectionChecker implements IConnectionChecker { // the stack. But covering up a shadow block or stack of shadow blocks // is fine. Similarly, replacing a terminal statement with another // terminal statement is allowed. - if (b.isConnected() && !a.getSourceBlock().nextConnection && - !b.targetBlock()!.isShadow() && b.targetBlock()!.nextConnection) { + if ( + b.isConnected() && + !a.getSourceBlock().nextConnection && + !b.targetBlock()!.isShadow() && + b.targetBlock()!.nextConnection + ) { return false; } // Don't offer to splice into a stack where the connected block is // immovable, unless the block is a shadow block. - if (b.targetBlock() && !b.targetBlock()!.isMovable() && - !b.targetBlock()!.isShadow()) { + if ( + b.targetBlock() && + !b.targetBlock()!.isMovable() && + !b.targetBlock()!.isShadow() + ) { return false; } break; @@ -307,4 +343,7 @@ export class ConnectionChecker implements IConnectionChecker { } registry.register( - registry.Type.CONNECTION_CHECKER, registry.DEFAULT, ConnectionChecker); + registry.Type.CONNECTION_CHECKER, + registry.DEFAULT, + ConnectionChecker +); diff --git a/core/connection_db.ts b/core/connection_db.ts index 23dd82983f9..1cf8900b7c1 100644 --- a/core/connection_db.ts +++ b/core/connection_db.ts @@ -19,7 +19,6 @@ import type {IConnectionChecker} from './interfaces/i_connection_checker.js'; import type {RenderedConnection} from './rendered_connection.js'; import type {Coordinate} from './utils/coordinate.js'; - /** * Database of connections. * Connections are stored in order of their vertical component. This way @@ -58,8 +57,10 @@ export class ConnectionDB { * @returns The index of the connection, or -1 if the connection was not * found. */ - private findIndexOfConnection(conn: RenderedConnection, yPos: number): - number { + private findIndexOfConnection( + conn: RenderedConnection, + yPos: number + ): number { if (!this.connections.length) { return -1; } @@ -81,8 +82,10 @@ export class ConnectionDB { } pointer = bestGuess; - while (pointer < this.connections.length && - this.connections[pointer].y === yPos) { + while ( + pointer < this.connections.length && + this.connections[pointer].y === yPos + ) { if (this.connections[pointer] === conn) { return pointer; } @@ -140,8 +143,10 @@ export class ConnectionDB { * @param maxRadius The maximum radius to another connection. * @returns List of connections. */ - getNeighbours(connection: RenderedConnection, maxRadius: number): - RenderedConnection[] { + getNeighbours( + connection: RenderedConnection, + maxRadius: number + ): RenderedConnection[] { const db = this.connections; const currentX = connection.x; const currentY = connection.y; @@ -218,8 +223,10 @@ export class ConnectionDB { * connection or null, and 'radius' which is the distance. */ searchForClosest( - conn: RenderedConnection, maxRadius: number, - dxy: Coordinate): {connection: RenderedConnection|null, radius: number} { + conn: RenderedConnection, + maxRadius: number, + dxy: Coordinate + ): {connection: RenderedConnection | null; radius: number} { if (!this.connections.length) { // Don't bother. return {connection: null, radius: maxRadius}; @@ -253,8 +260,10 @@ export class ConnectionDB { } let pointerMax = closestIndex; - while (pointerMax < this.connections.length && - this.isInYRange(pointerMax, conn.y, maxRadius)) { + while ( + pointerMax < this.connections.length && + this.isInYRange(pointerMax, conn.y, maxRadius) + ) { temp = this.connections[pointerMax]; if (this.connectionChecker.canConnect(conn, temp, true, bestRadius)) { bestConnection = temp; diff --git a/core/connection_type.ts b/core/connection_type.ts index 5fe77b12b82..2e6a13e9ddc 100644 --- a/core/connection_type.ts +++ b/core/connection_type.ts @@ -7,7 +7,6 @@ import * as goog from '../closure/goog/goog.js'; goog.declareModuleId('Blockly.ConnectionType'); - /** * Enum for the type of a connection or input. */ @@ -19,5 +18,5 @@ export enum ConnectionType { // A down-facing block stack. E.g. 'if-do' or 'else'. NEXT_STATEMENT, // An up-facing block stack. E.g. 'break out of loop'. - PREVIOUS_STATEMENT + PREVIOUS_STATEMENT, } diff --git a/core/constants.ts b/core/constants.ts index ace571ff196..8d222640bbb 100644 --- a/core/constants.ts +++ b/core/constants.ts @@ -7,7 +7,6 @@ import * as goog from '../closure/goog/goog.js'; goog.declareModuleId('Blockly.constants'); - /** * The language-neutral ID given to the collapsed input. */ diff --git a/core/contextmenu.ts b/core/contextmenu.ts index a9cae417a6a..e4d0e91a0cb 100644 --- a/core/contextmenu.ts +++ b/core/contextmenu.ts @@ -13,7 +13,10 @@ import * as browserEvents from './browser_events.js'; import * as clipboard from './clipboard.js'; import {config} from './config.js'; import * as dom from './utils/dom.js'; -import type {ContextMenuOption, LegacyContextMenuOption} from './contextmenu_registry.js'; +import type { + ContextMenuOption, + LegacyContextMenuOption, +} from './contextmenu_registry.js'; import * as eventUtils from './events/utils.js'; import {Menu} from './menu.js'; import {MenuItem} from './menuitem.js'; @@ -27,11 +30,10 @@ import {WorkspaceCommentSvg} from './workspace_comment_svg.js'; import type {WorkspaceSvg} from './workspace_svg.js'; import * as Xml from './xml.js'; - /** * Which block is the context menu attached to? */ -let currentBlock: Block|null = null; +let currentBlock: Block | null = null; const dummyOwner = {}; @@ -40,7 +42,7 @@ const dummyOwner = {}; * * @returns The block the context menu is attached to. */ -export function getCurrentBlock(): Block|null { +export function getCurrentBlock(): Block | null { return currentBlock; } @@ -49,14 +51,14 @@ export function getCurrentBlock(): Block|null { * * @param block The block the context menu is attached to. */ -export function setCurrentBlock(block: Block|null) { +export function setCurrentBlock(block: Block | null) { currentBlock = block; } /** * Menu object. */ -let menu_: Menu|null = null; +let menu_: Menu | null = null; /** * Construct the menu based on the list of options and show the menu. @@ -66,8 +68,10 @@ let menu_: Menu|null = null; * @param rtl True if RTL, false if LTR. */ export function show( - e: Event, options: (ContextMenuOption|LegacyContextMenuOption)[], - rtl: boolean) { + e: Event, + options: (ContextMenuOption | LegacyContextMenuOption)[], + rtl: boolean +) { WidgetDiv.show(dummyOwner, rtl, dispose); if (!options.length) { hide(); @@ -79,10 +83,10 @@ export function show( position_(menu, e, rtl); // 1ms delay is required for focusing on context menus because some other // mouse event is still waiting in the queue and clears focus. - setTimeout(function() { + setTimeout(function () { menu.focus(); }, 1); - currentBlock = null; // May be set by Blockly.Block. + currentBlock = null; // May be set by Blockly.Block. } /** @@ -93,8 +97,9 @@ export function show( * @returns The menu that will be shown on right click. */ function populate_( - options: (ContextMenuOption|LegacyContextMenuOption)[], - rtl: boolean): Menu { + options: (ContextMenuOption | LegacyContextMenuOption)[], + rtl: boolean +): Menu { /* Here's what one option object looks like: {text: 'Make It So', enabled: true, @@ -110,7 +115,7 @@ function populate_( menu.addChild(menuItem); menuItem.setEnabled(option.enabled); if (option.enabled) { - const actionHandler = function() { + const actionHandler = function () { hide(); requestAnimationFrame(() => { setTimeout(() => { @@ -143,10 +148,11 @@ function position_(menu: Menu, e: Event, rtl: boolean) { // This one is just a point, but we'll pretend that it's a rect so we can use // some helper functions. const anchorBBox = new Rect( - mouseEvent.clientY + viewportBBox.top, - mouseEvent.clientY + viewportBBox.top, - mouseEvent.clientX + viewportBBox.left, - mouseEvent.clientX + viewportBBox.left); + mouseEvent.clientY + viewportBBox.top, + mouseEvent.clientY + viewportBBox.top, + mouseEvent.clientX + viewportBBox.left, + mouseEvent.clientX + viewportBBox.left + ); createWidget_(menu); const menuSize = menu.getSize(); @@ -179,7 +185,11 @@ function createWidget_(menu: Menu) { dom.addClass(menuDom, 'blocklyContextMenu'); // Prevent system context menu when right-clicking a Blockly context menu. browserEvents.conditionalBind( - (menuDom as EventTarget), 'contextmenu', null, haltPropagation); + menuDom as EventTarget, + 'contextmenu', + null, + haltPropagation + ); // Focus only after the initial render to avoid issue #1329. menu.focus(); } @@ -256,12 +266,13 @@ export function callbackFactory(block: Block, xml: Element): () => void { * containing text, enabled, and a callback. * @internal */ -export function commentDeleteOption(comment: WorkspaceCommentSvg): - LegacyContextMenuOption { +export function commentDeleteOption( + comment: WorkspaceCommentSvg +): LegacyContextMenuOption { const deleteOption = { text: Msg['REMOVE_COMMENT'], enabled: true, - callback: function() { + callback: function () { eventUtils.setGroup(true); comment.dispose(); eventUtils.setGroup(false); @@ -279,12 +290,13 @@ export function commentDeleteOption(comment: WorkspaceCommentSvg): * containing text, enabled, and a callback. * @internal */ -export function commentDuplicateOption(comment: WorkspaceCommentSvg): - LegacyContextMenuOption { +export function commentDuplicateOption( + comment: WorkspaceCommentSvg +): LegacyContextMenuOption { const duplicateOption = { text: Msg['DUPLICATE_COMMENT'], enabled: true, - callback: function() { + callback: function () { clipboard.duplicate(comment); }, }; @@ -303,15 +315,20 @@ export function commentDuplicateOption(comment: WorkspaceCommentSvg): * @internal */ export function workspaceCommentOption( - ws: WorkspaceSvg, e: Event): ContextMenuOption { + ws: WorkspaceSvg, + e: Event +): ContextMenuOption { /** * Helper function to create and position a comment correctly based on the * location of the mouse event. */ function addWsComment() { const comment = new WorkspaceCommentSvg( - ws, Msg['WORKSPACE_COMMENT_DEFAULT_TEXT'], - WorkspaceCommentSvg.DEFAULT_SIZE, WorkspaceCommentSvg.DEFAULT_SIZE); + ws, + Msg['WORKSPACE_COMMENT_DEFAULT_TEXT'], + WorkspaceCommentSvg.DEFAULT_SIZE, + WorkspaceCommentSvg.DEFAULT_SIZE + ); const injectionDiv = ws.getInjectionDiv(); // Bounding rect coordinates are in client coordinates, meaning that they @@ -322,8 +339,9 @@ export function workspaceCommentOption( // The client coordinates offset by the injection div's upper left corner. const mouseEvent = e as MouseEvent; const clientOffsetPixels = new Coordinate( - mouseEvent.clientX - boundingRect.left, - mouseEvent.clientY - boundingRect.top); + mouseEvent.clientX - boundingRect.left, + mouseEvent.clientY - boundingRect.top + ); // The offset in pixels between the main workspace's origin and the upper // left corner of the injection div. @@ -331,8 +349,10 @@ export function workspaceCommentOption( // The position of the new comment in pixels relative to the origin of the // main workspace. - const finalOffset = - Coordinate.difference(clientOffsetPixels, mainOffsetPixels); + const finalOffset = Coordinate.difference( + clientOffsetPixels, + mainOffsetPixels + ); // The position of the new comment in main workspace coordinates. finalOffset.scale(1 / ws.scale); @@ -350,7 +370,7 @@ export function workspaceCommentOption( enabled: true, } as ContextMenuOption; wsCommentOption.text = Msg['ADD_COMMENT']; - wsCommentOption.callback = function() { + wsCommentOption.callback = function () { addWsComment(); }; return wsCommentOption; diff --git a/core/contextmenu_items.ts b/core/contextmenu_items.ts index 9b15d71cfbf..77ab48433e6 100644 --- a/core/contextmenu_items.ts +++ b/core/contextmenu_items.ts @@ -9,7 +9,11 @@ goog.declareModuleId('Blockly.ContextMenuItems'); import type {BlockSvg} from './block_svg.js'; import * as clipboard from './clipboard.js'; -import {ContextMenuRegistry, RegistryItem, Scope} from './contextmenu_registry.js'; +import { + ContextMenuRegistry, + RegistryItem, + Scope, +} from './contextmenu_registry.js'; import * as dialog from './dialog.js'; import * as Events from './events/events.js'; import * as eventUtils from './events/utils.js'; @@ -17,7 +21,6 @@ import {Msg} from './msg.js'; import {StatementInput} from './renderers/zelos/zelos.js'; import type {WorkspaceSvg} from './workspace_svg.js'; - /** * Option to undo previous action. */ @@ -111,7 +114,7 @@ function toggleOption_(shouldCollapse: boolean, topBlocks: BlockSvg[]) { } Events.setGroup(true); for (let i = 0; i < topBlocks.length; i++) { - let block: BlockSvg|null = topBlocks[i]; + let block: BlockSvg | null = topBlocks[i]; while (block) { timeoutCounter++; setTimeout(timeoutFn.bind(null, block), ms); @@ -133,7 +136,7 @@ export function registerCollapse() { if (scope.workspace!.options.collapse) { const topBlocks = scope.workspace!.getTopBlocks(false); for (let i = 0; i < topBlocks.length; i++) { - let block: BlockSvg|null = topBlocks[i]; + let block: BlockSvg | null = topBlocks[i]; while (block) { if (!block.isCollapsed()) { return 'enabled'; @@ -167,7 +170,7 @@ export function registerExpand() { if (scope.workspace!.options.collapse) { const topBlocks = scope.workspace!.getTopBlocks(false); for (let i = 0; i < topBlocks.length; i++) { - let block: BlockSvg|null = topBlocks[i]; + let block: BlockSvg | null = topBlocks[i]; while (block) { if (block.isCollapsed()) { return 'enabled'; @@ -280,13 +283,16 @@ export function registerDeleteAll() { deleteNext_(deletableBlocks); } else { dialog.confirm( - Msg['DELETE_ALL_BLOCKS'].replace( - '%1', String(deletableBlocks.length)), - function(ok) { - if (ok) { - deleteNext_(deletableBlocks); - } - }); + Msg['DELETE_ALL_BLOCKS'].replace( + '%1', + String(deletableBlocks.length) + ), + function (ok) { + if (ok) { + deleteNext_(deletableBlocks); + } + } + ); } }, scopeType: ContextMenuRegistry.ScopeType.WORKSPACE, @@ -350,8 +356,12 @@ export function registerComment() { }, preconditionFn(scope: Scope) { const block = scope.block; - if (!block!.isInFlyout && block!.workspace.options.comments && - !block!.isCollapsed() && block!.isEditable()) { + if ( + !block!.isInFlyout && + block!.workspace.options.comments && + !block!.isCollapsed() && + block!.isEditable() + ) { return 'enabled'; } return 'hidden'; @@ -377,8 +387,9 @@ export function registerComment() { export function registerInline() { const inlineOption: RegistryItem = { displayText(scope: Scope) { - return scope.block!.getInputsInline() ? Msg['EXTERNAL_INPUTS'] : - Msg['INLINE_INPUTS']; + return scope.block!.getInputsInline() + ? Msg['EXTERNAL_INPUTS'] + : Msg['INLINE_INPUTS']; }, preconditionFn(scope: Scope) { const block = scope.block; @@ -386,8 +397,10 @@ export function registerInline() { for (let i = 1; i < block!.inputList.length; i++) { // Only display this option if there are two value or dummy inputs // next to each other. - if (!(block!.inputList[i - 1] instanceof StatementInput) && - !(block!.inputList[i] instanceof StatementInput)) { + if ( + !(block!.inputList[i - 1] instanceof StatementInput) && + !(block!.inputList[i] instanceof StatementInput) + ) { return 'enabled'; } } @@ -410,13 +423,17 @@ export function registerInline() { export function registerCollapseExpandBlock() { const collapseExpandOption: RegistryItem = { displayText(scope: Scope) { - return scope.block!.isCollapsed() ? Msg['EXPAND_BLOCK'] : - Msg['COLLAPSE_BLOCK']; + return scope.block!.isCollapsed() + ? Msg['EXPAND_BLOCK'] + : Msg['COLLAPSE_BLOCK']; }, preconditionFn(scope: Scope) { const block = scope.block; - if (!block!.isInFlyout && block!.isMovable() && - block!.workspace.options.collapse) { + if ( + !block!.isInFlyout && + block!.isMovable() && + block!.workspace.options.collapse + ) { return 'enabled'; } return 'hidden'; @@ -437,13 +454,17 @@ export function registerCollapseExpandBlock() { export function registerDisable() { const disableOption: RegistryItem = { displayText(scope: Scope) { - return scope.block!.isEnabled() ? Msg['DISABLE_BLOCK'] : - Msg['ENABLE_BLOCK']; + return scope.block!.isEnabled() + ? Msg['DISABLE_BLOCK'] + : Msg['ENABLE_BLOCK']; }, preconditionFn(scope: Scope) { const block = scope.block; - if (!block!.isInFlyout && block!.workspace.options.disable && - block!.isEditable()) { + if ( + !block!.isInFlyout && + block!.workspace.options.disable && + block!.isEditable() + ) { if (block!.getInheritedDisabled()) { return 'disabled'; } @@ -481,9 +502,9 @@ export function registerDelete() { // Blocks in the current stack would survive this block's deletion. descendantCount -= nextBlock.getDescendants(false).length; } - return descendantCount === 1 ? - Msg['DELETE_BLOCK'] : - Msg['DELETE_X_BLOCKS'].replace('%1', `${descendantCount}`); + return descendantCount === 1 + ? Msg['DELETE_BLOCK'] + : Msg['DELETE_X_BLOCKS'].replace('%1', `${descendantCount}`); }, preconditionFn(scope: Scope) { if (!scope.block!.isInFlyout && scope.block!.isDeletable()) { @@ -513,8 +534,10 @@ export function registerHelp() { }, preconditionFn(scope: Scope) { const block = scope.block; - const url = typeof block!.helpUrl === 'function' ? block!.helpUrl() : - block!.helpUrl; + const url = + typeof block!.helpUrl === 'function' + ? block!.helpUrl() + : block!.helpUrl; if (url) { return 'enabled'; } diff --git a/core/contextmenu_registry.ts b/core/contextmenu_registry.ts index 6f3eb5e1c3e..cd7534a6370 100644 --- a/core/contextmenu_registry.ts +++ b/core/contextmenu_registry.ts @@ -15,7 +15,6 @@ goog.declareModuleId('Blockly.ContextMenuRegistry'); import type {BlockSvg} from './block_svg.js'; import type {WorkspaceSvg} from './workspace_svg.js'; - /** * Class for the registry of context menu items. This is intended to be a * singleton. You should not create a new instance, and only access this class @@ -66,7 +65,7 @@ export class ContextMenuRegistry { * @param id The ID of the RegistryItem to get. * @returns RegistryItem or null if not found */ - getItem(id: string): RegistryItem|null { + getItem(id: string): RegistryItem | null { return this.registry_.get(id) ?? null; } @@ -81,16 +80,19 @@ export class ContextMenuRegistry { * block being clicked on) * @returns the list of ContextMenuOptions */ - getContextMenuOptions(scopeType: ScopeType, scope: Scope): - ContextMenuOption[] { + getContextMenuOptions( + scopeType: ScopeType, + scope: Scope + ): ContextMenuOption[] { const menuOptions: ContextMenuOption[] = []; for (const item of this.registry_.values()) { if (scopeType === item.scopeType) { const precondition = item.preconditionFn(scope); if (precondition !== 'hidden') { - const displayText = typeof item.displayText === 'function' ? - item.displayText(scope) : - item.displayText; + const displayText = + typeof item.displayText === 'function' + ? item.displayText(scope) + : item.displayText; const menuOption: ContextMenuOption = { text: displayText, enabled: precondition === 'enabled', @@ -102,7 +104,7 @@ export class ContextMenuRegistry { } } } - menuOptions.sort(function(a, b) { + menuOptions.sort(function (a, b) { return a.weight - b.weight; }); return menuOptions; @@ -135,7 +137,7 @@ export namespace ContextMenuRegistry { export interface RegistryItem { callback: (p1: Scope) => void; scopeType: ScopeType; - displayText: ((p1: Scope) => string)|string; + displayText: ((p1: Scope) => string) | string; preconditionFn: (p1: Scope) => string; weight: number; id: string; @@ -175,4 +177,4 @@ export type Scope = ContextMenuRegistry.Scope; export type RegistryItem = ContextMenuRegistry.RegistryItem; export type ContextMenuOption = ContextMenuRegistry.ContextMenuOption; export type LegacyContextMenuOption = - ContextMenuRegistry.LegacyContextMenuOption; + ContextMenuRegistry.LegacyContextMenuOption; diff --git a/core/css.ts b/core/css.ts index f473aacf739..6095bbba18a 100644 --- a/core/css.ts +++ b/core/css.ts @@ -7,7 +7,6 @@ import * as goog from '../closure/goog/goog.js'; goog.declareModuleId('Blockly.Css'); - /** Has CSS already been injected? */ let injected = false; diff --git a/core/delete_area.ts b/core/delete_area.ts index 576d2475a7e..90fcb62bca0 100644 --- a/core/delete_area.ts +++ b/core/delete_area.ts @@ -18,7 +18,6 @@ import {DragTarget} from './drag_target.js'; import type {IDeleteArea} from './interfaces/i_delete_area.js'; import type {IDraggable} from './interfaces/i_draggable.js'; - /** * Abstract class for a component that can delete a block or bubble that is * dropped on top of it. @@ -59,7 +58,7 @@ export class DeleteArea extends DragTarget implements IDeleteArea { */ wouldDelete(element: IDraggable, couldConnect: boolean): boolean { if (element instanceof BlockSvg) { - const block = (element); + const block = element; const couldDeleteBlock = !block.getParent() && block.isDeletable(); this.updateWouldDelete_(couldDeleteBlock && !couldConnect); } else { diff --git a/core/dialog.ts b/core/dialog.ts index 4c5d17852c7..046b40c13a7 100644 --- a/core/dialog.ts +++ b/core/dialog.ts @@ -7,22 +7,28 @@ import * as goog from '../closure/goog/goog.js'; goog.declareModuleId('Blockly.dialog'); - -let alertImplementation = function(message: string, opt_callback?: () => void) { +let alertImplementation = function ( + message: string, + opt_callback?: () => void +) { window.alert(message); if (opt_callback) { opt_callback(); } }; -let confirmImplementation = function( - message: string, callback: (result: boolean) => void) { +let confirmImplementation = function ( + message: string, + callback: (result: boolean) => void +) { callback(window.confirm(message)); }; -let promptImplementation = function( - message: string, defaultValue: string, - callback: (result: string|null) => void) { +let promptImplementation = function ( + message: string, + defaultValue: string, + callback: (result: string | null) => void +) { callback(window.prompt(message, defaultValue)); }; @@ -65,7 +71,6 @@ function confirmInternal(message: string, callback: (p1: boolean) => void) { confirmImplementation(message, callback); } - /** * Sets the function to be run when Blockly.dialog.confirm() is called. * @@ -73,7 +78,8 @@ function confirmInternal(message: string, callback: (p1: boolean) => void) { * @see Blockly.dialog.confirm */ export function setConfirm( - confirmFunction: (p1: string, p2: (p1: boolean) => void) => void) { + confirmFunction: (p1: string, p2: (p1: boolean) => void) => void +) { confirmImplementation = confirmFunction; } @@ -88,8 +94,10 @@ export function setConfirm( * @param callback The callback for handling user response. */ export function prompt( - message: string, defaultValue: string, - callback: (p1: string|null) => void) { + message: string, + defaultValue: string, + callback: (p1: string | null) => void +) { promptImplementation(message, defaultValue, callback); } @@ -100,8 +108,12 @@ export function prompt( * @see Blockly.dialog.prompt */ export function setPrompt( - promptFunction: (p1: string, p2: string, p3: (p1: string|null) => void) => - void) { + promptFunction: ( + p1: string, + p2: string, + p3: (p1: string | null) => void + ) => void +) { promptImplementation = promptFunction; } diff --git a/core/drag_target.ts b/core/drag_target.ts index 5ef9d82ccb4..b6c2faa9f76 100644 --- a/core/drag_target.ts +++ b/core/drag_target.ts @@ -17,7 +17,6 @@ import type {IDragTarget} from './interfaces/i_drag_target.js'; import type {IDraggable} from './interfaces/i_draggable.js'; import type {Rect} from './utils/rect.js'; - /** * Abstract class for a component with custom behaviour when a block or bubble * is dragged over or dropped on top of it. @@ -76,7 +75,7 @@ export class DragTarget implements IDragTarget { * @returns The component's bounding box. Null if drag target area should be * ignored. */ - getClientRect(): Rect|null { + getClientRect(): Rect | null { return null; } diff --git a/core/dropdowndiv.ts b/core/dropdowndiv.ts index a1a254d03bf..f4b69b00aa9 100644 --- a/core/dropdowndiv.ts +++ b/core/dropdowndiv.ts @@ -23,7 +23,6 @@ import type {Size} from './utils/size.js'; import * as style from './utils/style.js'; import type {WorkspaceSvg} from './workspace_svg.js'; - /** * Arrow size in px. Should match the value in CSS * (need to position pre-render). @@ -52,10 +51,10 @@ export const ANIMATION_TIME = 0.25; * Timer for animation out, to be cleared if we need to immediately hide * without disrupting new shows. */ -let animateOutTimer: ReturnType|null = null; +let animateOutTimer: ReturnType | null = null; /** Callback for when the drop-down is hidden. */ -let onHide: Function|null = null; +let onHide: Function | null = null; /** A class name representing the current owner's workspace renderer. */ let renderedClassName = ''; @@ -76,13 +75,13 @@ let arrow: HTMLDivElement; * Drop-downs will appear within the bounds of this element if possible. * Set in setBoundsElement. */ -let boundsElement: Element|null = null; +let boundsElement: Element | null = null; /** The object currently using the drop-down. */ -let owner: Field|null = null; +let owner: Field | null = null; /** Whether the dropdown was positioned to a field or the source block. */ -let positionToField: boolean|null = null; +let positionToField: boolean | null = null; /** * Dropdown bounds info object used to encapsulate sizing information about a @@ -103,9 +102,9 @@ export interface PositionMetrics { initialY: number; finalX: number; finalY: number; - arrowX: number|null; - arrowY: number|null; - arrowAtTop: boolean|null; + arrowX: number | null; + arrowY: number | null; + arrowAtTop: boolean | null; arrowVisible: boolean; } @@ -116,7 +115,7 @@ export interface PositionMetrics { */ export function createDom() { if (div) { - return; // Already created. + return; // Already created. } div = document.createElement('div'); div.className = 'blocklyDropDownDiv'; @@ -133,15 +132,15 @@ export function createDom() { div.style.opacity = '0'; // Transition animation for transform: translate() and opacity. - div.style.transition = 'transform ' + ANIMATION_TIME + 's, ' + - 'opacity ' + ANIMATION_TIME + 's'; + div.style.transition = + 'transform ' + ANIMATION_TIME + 's, ' + 'opacity ' + ANIMATION_TIME + 's'; // Handle focusin/out events to add a visual indicator when // a child is focused or blurred. - div.addEventListener('focusin', function() { + div.addEventListener('focusin', function () { dom.addClass(div, 'blocklyFocused'); }); - div.addEventListener('focusout', function() { + div.addEventListener('focusout', function () { dom.removeClass(div, 'blocklyFocused'); }); } @@ -152,14 +151,14 @@ export function createDom() { * * @param boundsElem Element to bind drop-down to. */ -export function setBoundsElement(boundsElem: Element|null) { +export function setBoundsElement(boundsElem: Element | null) { boundsElement = boundsElem; } /** * @returns The field that currently owns this, or null. */ -export function getOwner(): Field|null { +export function getOwner(): Field | null { return owner; } @@ -202,11 +201,17 @@ export function setColour(backgroundColour: string, borderColour: string) { * @returns True if the menu rendered below block; false if above. */ export function showPositionedByBlock( - field: Field, block: BlockSvg, opt_onHide?: Function, - opt_secondaryYOffset?: number): boolean { + field: Field, + block: BlockSvg, + opt_onHide?: Function, + opt_secondaryYOffset?: number +): boolean { return showPositionedByRect( - getScaledBboxOfBlock(block), field as Field, opt_onHide, - opt_secondaryYOffset); + getScaledBboxOfBlock(block), + field as Field, + opt_onHide, + opt_secondaryYOffset + ); } /** @@ -221,12 +226,17 @@ export function showPositionedByBlock( * @returns True if the menu rendered below block; false if above. */ export function showPositionedByField( - field: Field, opt_onHide?: Function, - opt_secondaryYOffset?: number): boolean { + field: Field, + opt_onHide?: Function, + opt_secondaryYOffset?: number +): boolean { positionToField = true; return showPositionedByRect( - getScaledBboxOfField(field as Field), field as Field, opt_onHide, - opt_secondaryYOffset); + getScaledBboxOfField(field as Field), + field as Field, + opt_onHide, + opt_secondaryYOffset + ); } /** * Get the scaled bounding box of a block. @@ -267,8 +277,11 @@ function getScaledBboxOfField(field: Field): Rect { * @returns True if the menu rendered below block; false if above. */ function showPositionedByRect( - bBox: Rect, field: Field, opt_onHide?: Function, - opt_secondaryYOffset?: number): boolean { + bBox: Rect, + field: Field, + opt_onHide?: Function, + opt_secondaryYOffset?: number +): boolean { // If we can fit it, render below the block. const primaryX = bBox.left + (bBox.right - bBox.left) / 2; const primaryY = bBox.bottom; @@ -286,8 +299,14 @@ function showPositionedByRect( } setBoundsElement(workspace.getParentSvg().parentNode as Element | null); return show( - field, sourceBlock.RTL, primaryX, primaryY, secondaryX, secondaryY, - opt_onHide); + field, + sourceBlock.RTL, + primaryX, + primaryY, + secondaryX, + secondaryY, + opt_onHide + ); } /** @@ -310,8 +329,14 @@ function showPositionedByRect( * @internal */ export function show( - newOwner: Field, rtl: boolean, primaryX: number, primaryY: number, - secondaryX: number, secondaryY: number, opt_onHide?: Function): boolean { + newOwner: Field, + rtl: boolean, + primaryX: number, + primaryY: number, + secondaryX: number, + secondaryY: number, + opt_onHide?: Function +): boolean { owner = newOwner as Field; onHide = opt_onHide || null; // Set direction. @@ -345,7 +370,7 @@ const internal = { * @returns An object containing size information about the bounding element * (bounding box and width/height). */ - getBoundsInfo: function(): BoundsInfo { + getBoundsInfo: function (): BoundsInfo { const boundPosition = style.getPageOffset(boundsElement as Element); const boundSize = style.getSize(boundsElement as Element); @@ -370,9 +395,12 @@ const internal = { * @returns Various final metrics, including rendered positions for drop-down * and arrow. */ - getPositionMetrics: function( - primaryX: number, primaryY: number, secondaryX: number, - secondaryY: number): PositionMetrics { + getPositionMetrics: function ( + primaryX: number, + primaryY: number, + secondaryX: number, + secondaryY: number + ): PositionMetrics { const boundsInfo = internal.getBoundsInfo(); const divSize = style.getSize(div as Element); @@ -383,7 +411,11 @@ const internal = { // Can we fit in-bounds above the target? if (secondaryY - divSize.height > boundsInfo.top) { return getPositionAboveMetrics( - secondaryX, secondaryY, boundsInfo, divSize); + secondaryX, + secondaryY, + boundsInfo, + divSize + ); } // Can we fit outside the workspace bounds (but inside the window) // below? @@ -394,7 +426,11 @@ const internal = { // above? if (secondaryY - divSize.height > document.documentElement.clientTop) { return getPositionAboveMetrics( - secondaryX, secondaryY, boundsInfo, divSize); + secondaryX, + secondaryY, + boundsInfo, + divSize + ); } // Last resort, render at top of page. @@ -415,10 +451,17 @@ const internal = { * and arrow. */ function getPositionBelowMetrics( - primaryX: number, primaryY: number, boundsInfo: BoundsInfo, - divSize: Size): PositionMetrics { - const xCoords = - getPositionX(primaryX, boundsInfo.left, boundsInfo.right, divSize.width); + primaryX: number, + primaryY: number, + boundsInfo: BoundsInfo, + divSize: Size +): PositionMetrics { + const xCoords = getPositionX( + primaryX, + boundsInfo.left, + boundsInfo.right, + divSize.width + ); const arrowY = -(ARROW_SIZE / 2 + BORDER_SIZE); const finalY = primaryY + PADDING_Y; @@ -426,7 +469,7 @@ function getPositionBelowMetrics( return { initialX: xCoords.divX, initialY: primaryY, - finalX: xCoords.divX, // X position remains constant during animation. + finalX: xCoords.divX, // X position remains constant during animation. finalY, arrowX: xCoords.arrowX, arrowY, @@ -448,19 +491,26 @@ function getPositionBelowMetrics( * and arrow. */ function getPositionAboveMetrics( - secondaryX: number, secondaryY: number, boundsInfo: BoundsInfo, - divSize: Size): PositionMetrics { + secondaryX: number, + secondaryY: number, + boundsInfo: BoundsInfo, + divSize: Size +): PositionMetrics { const xCoords = getPositionX( - secondaryX, boundsInfo.left, boundsInfo.right, divSize.width); + secondaryX, + boundsInfo.left, + boundsInfo.right, + divSize.width + ); const arrowY = divSize.height - BORDER_SIZE * 2 - ARROW_SIZE / 2; const finalY = secondaryY - divSize.height - PADDING_Y; - const initialY = secondaryY - divSize.height; // No padding on Y. + const initialY = secondaryY - divSize.height; // No padding on Y. return { initialX: xCoords.divX, initialY, - finalX: xCoords.divX, // X position remains constant during animation. + finalX: xCoords.divX, // X position remains constant during animation. finalY, arrowX: xCoords.arrowX, arrowY, @@ -481,16 +531,23 @@ function getPositionAboveMetrics( * and arrow. */ function getPositionTopOfPageMetrics( - sourceX: number, boundsInfo: BoundsInfo, divSize: Size): PositionMetrics { - const xCoords = - getPositionX(sourceX, boundsInfo.left, boundsInfo.right, divSize.width); + sourceX: number, + boundsInfo: BoundsInfo, + divSize: Size +): PositionMetrics { + const xCoords = getPositionX( + sourceX, + boundsInfo.left, + boundsInfo.right, + divSize.width + ); // No need to provide arrow-specific information because it won't be visible. return { initialX: xCoords.divX, initialY: 0, - finalX: xCoords.divX, // X position remains constant during animation. - finalY: 0, // Y position remains constant during animation. + finalX: xCoords.divX, // X position remains constant during animation. + finalY: 0, // Y position remains constant during animation. arrowAtTop: null, arrowX: null, arrowY: null, @@ -511,8 +568,11 @@ function getPositionTopOfPageMetrics( * @internal */ export function getPositionX( - sourceX: number, boundsLeft: number, boundsRight: number, - divWidth: number): {divX: number, arrowX: number} { + sourceX: number, + boundsLeft: number, + boundsRight: number, + divWidth: number +): {divX: number; arrowX: number} { let divX = sourceX; // Offset the topLeft coord so that the dropdowndiv is centered. divX -= divWidth / 2; @@ -527,7 +587,10 @@ export function getPositionX( const horizPadding = ARROW_HORIZONTAL_PADDING; // Clamp the arrow position so that it stays attached to the dropdowndiv. relativeArrowX = math.clamp( - horizPadding, relativeArrowX, divWidth - horizPadding - ARROW_SIZE); + horizPadding, + relativeArrowX, + divWidth - horizPadding - ARROW_SIZE + ); return {arrowX: relativeArrowX, divX}; } @@ -550,7 +613,9 @@ export function isVisible(): boolean { * @returns True if hidden. */ export function hideIfOwner( - divOwner: Field, opt_withoutAnimation?: boolean): boolean { + divOwner: Field, + opt_withoutAnimation?: boolean +): boolean { if (owner === divOwner) { if (opt_withoutAnimation) { hideWithoutAnimation(); @@ -569,7 +634,7 @@ export function hide() { div.style.transform = 'translate(0, 0)'; div.style.opacity = '0'; // Finish animation - reset all values to default. - animateOutTimer = setTimeout(function() { + animateOutTimer = setTimeout(function () { hideWithoutAnimation(); }, ANIMATION_TIME * 1000); if (onHide) { @@ -625,20 +690,33 @@ export function hideWithoutAnimation() { * @returns True if the menu rendered at the primary origin point. */ function positionInternal( - primaryX: number, primaryY: number, secondaryX: number, - secondaryY: number): boolean { - const metrics = - internal.getPositionMetrics(primaryX, primaryY, secondaryX, secondaryY); + primaryX: number, + primaryY: number, + secondaryX: number, + secondaryY: number +): boolean { + const metrics = internal.getPositionMetrics( + primaryX, + primaryY, + secondaryX, + secondaryY + ); // Update arrow CSS. if (metrics.arrowVisible) { arrow.style.display = ''; - arrow.style.transform = 'translate(' + metrics.arrowX + 'px,' + - metrics.arrowY + 'px) rotate(45deg)'; + arrow.style.transform = + 'translate(' + + metrics.arrowX + + 'px,' + + metrics.arrowY + + 'px) rotate(45deg)'; arrow.setAttribute( - 'class', - metrics.arrowAtTop ? 'blocklyDropDownArrow blocklyArrowTop' : - 'blocklyDropDownArrow blocklyArrowBottom'); + 'class', + metrics.arrowAtTop + ? 'blocklyDropDownArrow blocklyArrowTop' + : 'blocklyDropDownArrow blocklyArrowBottom' + ); } else { arrow.style.display = 'none'; } @@ -679,8 +757,9 @@ export function repositionForWindowResize() { // it. if (owner) { const block = owner.getSourceBlock() as BlockSvg; - const bBox = positionToField ? getScaledBboxOfField(owner) : - getScaledBboxOfBlock(block); + const bBox = positionToField + ? getScaledBboxOfField(owner) + : getScaledBboxOfBlock(block); // If we can fit it, render below the block. const primaryX = bBox.left + (bBox.right - bBox.left) / 2; const primaryY = bBox.bottom; diff --git a/core/events/events.ts b/core/events/events.ts index edea5a6fffc..6f3a70fd62e 100644 --- a/core/events/events.ts +++ b/core/events/events.ts @@ -7,7 +7,6 @@ import * as goog from '../../closure/goog/goog.js'; goog.declareModuleId('Blockly.Events'); - import {Abstract, AbstractEventJson} from './events_abstract.js'; import {BlockBase, BlockBaseJson} from './events_block_base.js'; import {BlockChange, BlockChangeJson} from './events_block_change.js'; @@ -25,7 +24,10 @@ import {CommentMove, CommentMoveJson} from './events_comment_move.js'; import {MarkerMove, MarkerMoveJson} from './events_marker_move.js'; import {Selected, SelectedJson} from './events_selected.js'; import {ThemeChange, ThemeChangeJson} from './events_theme_change.js'; -import {ToolboxItemSelect, ToolboxItemSelectJson} from './events_toolbox_item_select.js'; +import { + ToolboxItemSelect, + ToolboxItemSelectJson, +} from './events_toolbox_item_select.js'; import {TrashcanOpen, TrashcanOpenJson} from './events_trashcan_open.js'; import {Ui} from './events_ui.js'; import {UiBase} from './events_ui_base.js'; @@ -37,7 +39,6 @@ import {ViewportChange, ViewportChangeJson} from './events_viewport.js'; import * as eventUtils from './utils.js'; import {FinishedLoading, FinishedLoadingJson} from './workspace_events.js'; - // Events. export {Abstract}; export {AbstractEventJson}; diff --git a/core/events/events_abstract.ts b/core/events/events_abstract.ts index a20d8c6b013..d46c275cc1f 100644 --- a/core/events/events_abstract.ts +++ b/core/events/events_abstract.ts @@ -19,7 +19,6 @@ import type {Workspace} from '../workspace.js'; import * as eventUtils from './utils.js'; - /** * Abstract class for an event. */ @@ -74,8 +73,11 @@ export abstract class Abstract { */ fromJson(json: AbstractEventJson) { deprecation.warn( - 'Blockly.Events.Abstract.prototype.fromJson', 'version 9', 'version 10', - 'Blockly.Events.fromJson'); + 'Blockly.Events.Abstract.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); this.isBlank = false; this.group = json['group'] || ''; } @@ -89,8 +91,11 @@ export abstract class Abstract { * supertypes of parameters to static methods in superclasses. * @internal */ - static fromJson(json: AbstractEventJson, workspace: Workspace, event: any): - Abstract { + static fromJson( + json: AbstractEventJson, + workspace: Workspace, + event: any + ): Abstract { event.isBlank = false; event.group = json['group'] || ''; event.workspaceId = workspace.id; @@ -130,8 +135,9 @@ export abstract class Abstract { } if (!workspace) { throw Error( - 'Workspace is null. Event must have been generated from real' + - ' Blockly events.'); + 'Workspace is null. Event must have been generated from real' + + ' Blockly events.' + ); } return workspace; } diff --git a/core/events/events_block_base.ts b/core/events/events_block_base.ts index b279af43b02..348934d282d 100644 --- a/core/events/events_block_base.ts +++ b/core/events/events_block_base.ts @@ -16,8 +16,10 @@ import type {Block} from '../block.js'; import * as deprecation from '../utils/deprecation.js'; import type {Workspace} from '../workspace.js'; -import {Abstract as AbstractEvent, AbstractEventJson} from './events_abstract.js'; - +import { + Abstract as AbstractEvent, + AbstractEventJson, +} from './events_abstract.js'; /** * Abstract class for any event related to blocks. @@ -51,8 +53,9 @@ export class BlockBase extends AbstractEvent { const json = super.toJson() as BlockBaseJson; if (!this.blockId) { throw new Error( - 'The block ID is undefined. Either pass a block to ' + - 'the constructor, or call fromJson'); + 'The block ID is undefined. Either pass a block to ' + + 'the constructor, or call fromJson' + ); } json['blockId'] = this.blockId; return json; @@ -65,8 +68,11 @@ export class BlockBase extends AbstractEvent { */ override fromJson(json: BlockBaseJson) { deprecation.warn( - 'Blockly.Events.BlockBase.prototype.fromJson', 'version 9', - 'version 10', 'Blockly.Events.fromJson'); + 'Blockly.Events.BlockBase.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); this.blockId = json['blockId']; } @@ -80,10 +86,16 @@ export class BlockBase extends AbstractEvent { * static methods in superclasses. * @internal */ - static fromJson(json: BlockBaseJson, workspace: Workspace, event?: any): - BlockBase { - const newEvent = - super.fromJson(json, workspace, event ?? new BlockBase()) as BlockBase; + static fromJson( + json: BlockBaseJson, + workspace: Workspace, + event?: any + ): BlockBase { + const newEvent = super.fromJson( + json, + workspace, + event ?? new BlockBase() + ) as BlockBase; newEvent.blockId = json['blockId']; return newEvent; } diff --git a/core/events/events_block_change.ts b/core/events/events_block_change.ts index 7218ea5d00f..2042adfee63 100644 --- a/core/events/events_block_change.ts +++ b/core/events/events_block_change.ts @@ -23,7 +23,6 @@ import * as Xml from '../xml.js'; import {BlockBase, BlockBaseJson} from './events_block_base.js'; import * as eventUtils from './utils.js'; - /** * Notifies listeners when some element of a block has changed (e.g. * field values, comments, etc). @@ -53,12 +52,16 @@ export class BlockChange extends BlockBase { * @param opt_newValue New value of element. */ constructor( - opt_block?: Block, opt_element?: string, opt_name?: string|null, - opt_oldValue?: unknown, opt_newValue?: unknown) { + opt_block?: Block, + opt_element?: string, + opt_name?: string | null, + opt_oldValue?: unknown, + opt_newValue?: unknown + ) { super(opt_block); if (!opt_block) { - return; // Blank event to be populated by fromJson. + return; // Blank event to be populated by fromJson. } this.element = opt_element; this.name = opt_name || undefined; @@ -75,8 +78,9 @@ export class BlockChange extends BlockBase { const json = super.toJson() as BlockChangeJson; if (!this.element) { throw new Error( - 'The changed element is undefined. Either pass an ' + - 'element to the constructor, or call fromJson'); + 'The changed element is undefined. Either pass an ' + + 'element to the constructor, or call fromJson' + ); } json['element'] = this.element; json['name'] = this.name; @@ -92,8 +96,11 @@ export class BlockChange extends BlockBase { */ override fromJson(json: BlockChangeJson) { deprecation.warn( - 'Blockly.Events.BlockChange.prototype.fromJson', 'version 9', - 'version 10', 'Blockly.Events.fromJson'); + 'Blockly.Events.BlockChange.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); this.element = json['element']; this.name = json['name']; @@ -110,11 +117,16 @@ export class BlockChange extends BlockBase { * parameters to static methods in superclasses. * @internal */ - static fromJson(json: BlockChangeJson, workspace: Workspace, event?: any): - BlockChange { - const newEvent = - super.fromJson(json, workspace, event ?? new BlockChange()) as - BlockChange; + static fromJson( + json: BlockChangeJson, + workspace: Workspace, + event?: any + ): BlockChange { + const newEvent = super.fromJson( + json, + workspace, + event ?? new BlockChange() + ) as BlockChange; newEvent.element = json['element']; newEvent.name = json['name']; newEvent.oldValue = json['oldValue']; @@ -140,14 +152,16 @@ export class BlockChange extends BlockBase { const workspace = this.getEventWorkspace_(); if (!this.blockId) { throw new Error( - 'The block ID is undefined. Either pass a block to ' + - 'the constructor, or call fromJson'); + 'The block ID is undefined. Either pass a block to ' + + 'the constructor, or call fromJson' + ); } const block = workspace.getBlockById(this.blockId); if (!block) { throw new Error( - 'The associated block is undefined. Either pass a ' + - 'block to the constructor, or call fromJson'); + 'The associated block is undefined. Either pass a ' + + 'block to the constructor, or call fromJson' + ); } // Assume the block is rendered so that then we can check. const blockSvg = block as BlockSvg; @@ -162,12 +176,12 @@ export class BlockChange extends BlockBase { if (field) { field.setValue(value); } else { - console.warn('Can\'t set non-existent field: ' + this.name); + console.warn("Can't set non-existent field: " + this.name); } break; } case 'comment': - block.setCommentText(value as string || null); + block.setCommentText((value as string) || null); break; case 'collapsed': block.setCollapsed(!!value); @@ -181,13 +195,15 @@ export class BlockChange extends BlockBase { case 'mutation': { const oldState = BlockChange.getExtraBlockState_(block as BlockSvg); if (block.loadExtraState) { - block.loadExtraState(JSON.parse(value as string || '{}')); + block.loadExtraState(JSON.parse((value as string) || '{}')); } else if (block.domToMutation) { block.domToMutation( - utilsXml.textToDom(value as string || '')); + utilsXml.textToDom((value as string) || '') + ); } eventUtils.fire( - new BlockChange(block, 'mutation', null, oldState, value)); + new BlockChange(block, 'mutation', null, oldState, value) + ); break; } default: diff --git a/core/events/events_block_create.ts b/core/events/events_block_create.ts index 7e09ed58109..c9065a80380 100644 --- a/core/events/events_block_create.ts +++ b/core/events/events_block_create.ts @@ -23,7 +23,6 @@ import {BlockBase, BlockBaseJson} from './events_block_base.js'; import * as eventUtils from './utils.js'; import {Workspace} from '../workspace.js'; - /** * Notifies listeners when a block (or connected stack of blocks) is * created. @@ -32,7 +31,7 @@ export class BlockCreate extends BlockBase { override type = eventUtils.BLOCK_CREATE; /** The XML representation of the created block(s). */ - xml?: Element|DocumentFragment; + xml?: Element | DocumentFragment; /** The JSON respresentation of the created block(s). */ json?: blocks.State; @@ -45,7 +44,7 @@ export class BlockCreate extends BlockBase { super(opt_block); if (!opt_block) { - return; // Blank event to be populated by fromJson. + return; // Blank event to be populated by fromJson. } if (opt_block.isShadow()) { @@ -68,18 +67,21 @@ export class BlockCreate extends BlockBase { const json = super.toJson() as BlockCreateJson; if (!this.xml) { throw new Error( - 'The block XML is undefined. Either pass a block to ' + - 'the constructor, or call fromJson'); + 'The block XML is undefined. Either pass a block to ' + + 'the constructor, or call fromJson' + ); } if (!this.ids) { throw new Error( - 'The block IDs are undefined. Either pass a block to ' + - 'the constructor, or call fromJson'); + 'The block IDs are undefined. Either pass a block to ' + + 'the constructor, or call fromJson' + ); } if (!this.json) { throw new Error( - 'The block JSON is undefined. Either pass a block to ' + - 'the constructor, or call fromJson'); + 'The block JSON is undefined. Either pass a block to ' + + 'the constructor, or call fromJson' + ); } json['xml'] = Xml.domToText(this.xml); json['ids'] = this.ids; @@ -97,8 +99,11 @@ export class BlockCreate extends BlockBase { */ override fromJson(json: BlockCreateJson) { deprecation.warn( - 'Blockly.Events.BlockCreate.prototype.fromJson', 'version 9', - 'version 10', 'Blockly.Events.fromJson'); + 'Blockly.Events.BlockCreate.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); this.xml = utilsXml.textToDom(json['xml']); this.ids = json['ids']; @@ -117,11 +122,16 @@ export class BlockCreate extends BlockBase { * parameters to static methods in superclasses. * @internal */ - static fromJson(json: BlockCreateJson, workspace: Workspace, event?: any): - BlockCreate { - const newEvent = - super.fromJson(json, workspace, event ?? new BlockCreate()) as - BlockCreate; + static fromJson( + json: BlockCreateJson, + workspace: Workspace, + event?: any + ): BlockCreate { + const newEvent = super.fromJson( + json, + workspace, + event ?? new BlockCreate() + ) as BlockCreate; newEvent.xml = utilsXml.textToDom(json['xml']); newEvent.ids = json['ids']; newEvent.json = json['json'] as blocks.State; @@ -140,13 +150,15 @@ export class BlockCreate extends BlockBase { const workspace = this.getEventWorkspace_(); if (!this.json) { throw new Error( - 'The block JSON is undefined. Either pass a block to ' + - 'the constructor, or call fromJson'); + 'The block JSON is undefined. Either pass a block to ' + + 'the constructor, or call fromJson' + ); } if (!this.ids) { throw new Error( - 'The block IDs are undefined. Either pass a block to ' + - 'the constructor, or call fromJson'); + 'The block IDs are undefined. Either pass a block to ' + + 'the constructor, or call fromJson' + ); } if (forward) { blocks.append(this.json, workspace); @@ -158,7 +170,7 @@ export class BlockCreate extends BlockBase { block.dispose(false); } else if (id === this.blockId) { // Only complain about root-level block. - console.warn('Can\'t uncreate non-existent block: ' + id); + console.warn("Can't uncreate non-existent block: " + id); } } } diff --git a/core/events/events_block_delete.ts b/core/events/events_block_delete.ts index 2331041fa75..106e64576cc 100644 --- a/core/events/events_block_delete.ts +++ b/core/events/events_block_delete.ts @@ -23,14 +23,13 @@ import {BlockBase, BlockBaseJson} from './events_block_base.js'; import * as eventUtils from './utils.js'; import {Workspace} from '../workspace.js'; - /** * Notifies listeners when a block (or connected stack of blocks) is * deleted. */ export class BlockDelete extends BlockBase { /** The XML representation of the deleted block(s). */ - oldXml?: Element|DocumentFragment; + oldXml?: Element | DocumentFragment; /** The JSON respresentation of the deleted block(s). */ oldJson?: blocks.State; @@ -48,7 +47,7 @@ export class BlockDelete extends BlockBase { super(opt_block); if (!opt_block) { - return; // Blank event to be populated by fromJson. + return; // Blank event to be populated by fromJson. } if (opt_block.getParent()) { @@ -62,8 +61,9 @@ export class BlockDelete extends BlockBase { this.oldXml = Xml.blockToDomWithXY(opt_block); this.ids = eventUtils.getDescendantIds(opt_block); this.wasShadow = opt_block.isShadow(); - this.oldJson = - blocks.save(opt_block, {addCoordinates: true}) as blocks.State; + this.oldJson = blocks.save(opt_block, { + addCoordinates: true, + }) as blocks.State; } /** @@ -75,23 +75,27 @@ export class BlockDelete extends BlockBase { const json = super.toJson() as BlockDeleteJson; if (!this.oldXml) { throw new Error( - 'The old block XML is undefined. Either pass a block ' + - 'to the constructor, or call fromJson'); + 'The old block XML is undefined. Either pass a block ' + + 'to the constructor, or call fromJson' + ); } if (!this.ids) { throw new Error( - 'The block IDs are undefined. Either pass a block to ' + - 'the constructor, or call fromJson'); + 'The block IDs are undefined. Either pass a block to ' + + 'the constructor, or call fromJson' + ); } if (this.wasShadow === undefined) { throw new Error( - 'Whether the block was a shadow is undefined. Either ' + - 'pass a block to the constructor, or call fromJson'); + 'Whether the block was a shadow is undefined. Either ' + + 'pass a block to the constructor, or call fromJson' + ); } if (!this.oldJson) { throw new Error( - 'The old block JSON is undefined. Either pass a block ' + - 'to the constructor, or call fromJson'); + 'The old block JSON is undefined. Either pass a block ' + + 'to the constructor, or call fromJson' + ); } json['oldXml'] = Xml.domToText(this.oldXml); json['ids'] = this.ids; @@ -110,13 +114,16 @@ export class BlockDelete extends BlockBase { */ override fromJson(json: BlockDeleteJson) { deprecation.warn( - 'Blockly.Events.BlockDelete.prototype.fromJson', 'version 9', - 'version 10', 'Blockly.Events.fromJson'); + 'Blockly.Events.BlockDelete.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); this.oldXml = utilsXml.textToDom(json['oldXml']); this.ids = json['ids']; this.wasShadow = - json['wasShadow'] || this.oldXml.tagName.toLowerCase() === 'shadow'; + json['wasShadow'] || this.oldXml.tagName.toLowerCase() === 'shadow'; this.oldJson = json['oldJson']; if (json['recordUndo'] !== undefined) { this.recordUndo = json['recordUndo']; @@ -132,15 +139,20 @@ export class BlockDelete extends BlockBase { * parameters to static methods in superclasses. * @internal */ - static fromJson(json: BlockDeleteJson, workspace: Workspace, event?: any): - BlockDelete { - const newEvent = - super.fromJson(json, workspace, event ?? new BlockDelete()) as - BlockDelete; + static fromJson( + json: BlockDeleteJson, + workspace: Workspace, + event?: any + ): BlockDelete { + const newEvent = super.fromJson( + json, + workspace, + event ?? new BlockDelete() + ) as BlockDelete; newEvent.oldXml = utilsXml.textToDom(json['oldXml']); newEvent.ids = json['ids']; newEvent.wasShadow = - json['wasShadow'] || newEvent.oldXml.tagName.toLowerCase() === 'shadow'; + json['wasShadow'] || newEvent.oldXml.tagName.toLowerCase() === 'shadow'; newEvent.oldJson = json['oldJson']; if (json['recordUndo'] !== undefined) { newEvent.recordUndo = json['recordUndo']; @@ -157,13 +169,15 @@ export class BlockDelete extends BlockBase { const workspace = this.getEventWorkspace_(); if (!this.ids) { throw new Error( - 'The block IDs are undefined. Either pass a block to ' + - 'the constructor, or call fromJson'); + 'The block IDs are undefined. Either pass a block to ' + + 'the constructor, or call fromJson' + ); } if (!this.oldJson) { throw new Error( - 'The old block JSON is undefined. Either pass a block ' + - 'to the constructor, or call fromJson'); + 'The old block JSON is undefined. Either pass a block ' + + 'to the constructor, or call fromJson' + ); } if (forward) { for (let i = 0; i < this.ids.length; i++) { @@ -173,7 +187,7 @@ export class BlockDelete extends BlockBase { block.dispose(false); } else if (id === this.blockId) { // Only complain about root-level block. - console.warn('Can\'t delete non-existent block: ' + id); + console.warn("Can't delete non-existent block: " + id); } } } else { diff --git a/core/events/events_block_drag.ts b/core/events/events_block_drag.ts index 4163da90719..bec60282351 100644 --- a/core/events/events_block_drag.ts +++ b/core/events/events_block_drag.ts @@ -20,7 +20,6 @@ import {UiBase} from './events_ui_base.js'; import * as eventUtils from './utils.js'; import {Workspace} from '../workspace.js'; - /** * Notifies listeners when a block is being manually dragged/dropped. */ @@ -66,13 +65,15 @@ export class BlockDrag extends UiBase { const json = super.toJson() as BlockDragJson; if (this.isStart === undefined) { throw new Error( - 'Whether this event is the start of a drag is undefined. ' + - 'Either pass the value to the constructor, or call fromJson'); + 'Whether this event is the start of a drag is undefined. ' + + 'Either pass the value to the constructor, or call fromJson' + ); } if (this.blockId === undefined) { throw new Error( - 'The block ID is undefined. Either pass a block to ' + - 'the constructor, or call fromJson'); + 'The block ID is undefined. Either pass a block to ' + + 'the constructor, or call fromJson' + ); } json['isStart'] = this.isStart; json['blockId'] = this.blockId; @@ -89,8 +90,11 @@ export class BlockDrag extends UiBase { */ override fromJson(json: BlockDragJson) { deprecation.warn( - 'Blockly.Events.BlockDrag.prototype.fromJson', 'version 9', - 'version 10', 'Blockly.Events.fromJson'); + 'Blockly.Events.BlockDrag.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); this.isStart = json['isStart']; this.blockId = json['blockId']; @@ -106,10 +110,16 @@ export class BlockDrag extends UiBase { * static methods in superclasses.. * @internal */ - static fromJson(json: BlockDragJson, workspace: Workspace, event?: any): - BlockDrag { - const newEvent = - super.fromJson(json, workspace, event ?? new BlockDrag()) as BlockDrag; + static fromJson( + json: BlockDragJson, + workspace: Workspace, + event?: any + ): BlockDrag { + const newEvent = super.fromJson( + json, + workspace, + event ?? new BlockDrag() + ) as BlockDrag; newEvent.isStart = json['isStart']; newEvent.blockId = json['blockId']; newEvent.blocks = json['blocks']; diff --git a/core/events/events_block_move.ts b/core/events/events_block_move.ts index 27f71570afb..8fe3e46afc7 100644 --- a/core/events/events_block_move.ts +++ b/core/events/events_block_move.ts @@ -22,7 +22,6 @@ import {BlockBase, BlockBaseJson} from './events_block_base.js'; import * as eventUtils from './utils.js'; import type {Workspace} from '../workspace.js'; - interface BlockLocation { parentId?: string; inputName?: string; @@ -109,14 +108,16 @@ export class BlockMove extends BlockBase { json['oldParentId'] = this.oldParentId; json['oldInputName'] = this.oldInputName; if (this.oldCoordinate) { - json['oldCoordinate'] = `${Math.round(this.oldCoordinate.x)}, ` + - `${Math.round(this.oldCoordinate.y)}`; + json['oldCoordinate'] = + `${Math.round(this.oldCoordinate.x)}, ` + + `${Math.round(this.oldCoordinate.y)}`; } json['newParentId'] = this.newParentId; json['newInputName'] = this.newInputName; if (this.newCoordinate) { - json['newCoordinate'] = `${Math.round(this.newCoordinate.x)}, ` + - `${Math.round(this.newCoordinate.y)}`; + json['newCoordinate'] = + `${Math.round(this.newCoordinate.x)}, ` + + `${Math.round(this.newCoordinate.y)}`; } if (this.reason) { json['reason'] = this.reason; @@ -134,8 +135,11 @@ export class BlockMove extends BlockBase { */ override fromJson(json: BlockMoveJson) { deprecation.warn( - 'Blockly.Events.BlockMove.prototype.fromJson', 'version 9', - 'version 10', 'Blockly.Events.fromJson'); + 'Blockly.Events.BlockMove.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); this.oldParentId = json['oldParentId']; this.oldInputName = json['oldInputName']; @@ -166,10 +170,16 @@ export class BlockMove extends BlockBase { * static methods in superclasses. * @internal */ - static fromJson(json: BlockMoveJson, workspace: Workspace, event?: any): - BlockMove { - const newEvent = - super.fromJson(json, workspace, event ?? new BlockMove()) as BlockMove; + static fromJson( + json: BlockMoveJson, + workspace: Workspace, + event?: any + ): BlockMove { + const newEvent = super.fromJson( + json, + workspace, + event ?? new BlockMove() + ) as BlockMove; newEvent.oldParentId = json['oldParentId']; newEvent.oldInputName = json['oldInputName']; if (json['oldCoordinate']) { @@ -218,14 +228,15 @@ export class BlockMove extends BlockBase { const workspace = this.getEventWorkspace_(); if (!this.blockId) { throw new Error( - 'The block ID is undefined. Either pass a block to ' + - 'the constructor, or call fromJson'); + 'The block ID is undefined. Either pass a block to ' + + 'the constructor, or call fromJson' + ); } const block = workspace.getBlockById(this.blockId); if (!block) { throw new Error( - 'The block associated with the block move event ' + - 'could not be found'); + 'The block associated with the block move event ' + 'could not be found' + ); } const location = {} as BlockLocation; const parent = block.getParent(); @@ -247,9 +258,11 @@ export class BlockMove extends BlockBase { * @returns False if something changed. */ override isNull(): boolean { - return this.oldParentId === this.newParentId && - this.oldInputName === this.newInputName && - Coordinate.equals(this.oldCoordinate, this.newCoordinate); + return ( + this.oldParentId === this.newParentId && + this.oldInputName === this.newInputName && + Coordinate.equals(this.oldCoordinate, this.newCoordinate) + ); } /** @@ -261,22 +274,23 @@ export class BlockMove extends BlockBase { const workspace = this.getEventWorkspace_(); if (!this.blockId) { throw new Error( - 'The block ID is undefined. Either pass a block to ' + - 'the constructor, or call fromJson'); + 'The block ID is undefined. Either pass a block to ' + + 'the constructor, or call fromJson' + ); } const block = workspace.getBlockById(this.blockId); if (!block) { - console.warn('Can\'t move non-existent block: ' + this.blockId); + console.warn("Can't move non-existent block: " + this.blockId); return; } const parentId = forward ? this.newParentId : this.oldParentId; const inputName = forward ? this.newInputName : this.oldInputName; const coordinate = forward ? this.newCoordinate : this.oldCoordinate; - let parentBlock: Block|null; + let parentBlock: Block | null; if (parentId) { parentBlock = workspace.getBlockById(parentId); if (!parentBlock) { - console.warn('Can\'t connect to non-existent block: ' + parentId); + console.warn("Can't connect to non-existent block: " + parentId); return; } } @@ -288,8 +302,10 @@ export class BlockMove extends BlockBase { block.moveBy(coordinate.x - xy.x, coordinate.y - xy.y, this.reason); } else { let blockConnection = block.outputConnection; - if (!blockConnection || - block.previousConnection && block.previousConnection.isConnected()) { + if ( + !blockConnection || + (block.previousConnection && block.previousConnection.isConnected()) + ) { blockConnection = block.previousConnection; } let parentConnection; @@ -305,7 +321,7 @@ export class BlockMove extends BlockBase { if (parentConnection && blockConnection) { blockConnection.connect(parentConnection); } else { - console.warn('Can\'t connect to non-existent input: ' + inputName); + console.warn("Can't connect to non-existent input: " + inputName); } } } diff --git a/core/events/events_bubble_open.ts b/core/events/events_bubble_open.ts index 8697d22c019..738dba449dd 100644 --- a/core/events/events_bubble_open.ts +++ b/core/events/events_bubble_open.ts @@ -20,7 +20,6 @@ import {UiBase} from './events_ui_base.js'; import * as eventUtils from './utils.js'; import type {Workspace} from '../workspace.js'; - /** * Class for a bubble open event. */ @@ -44,7 +43,10 @@ export class BubbleOpen extends UiBase { * 'warning'. Undefined for a blank event. */ constructor( - opt_block?: BlockSvg, opt_isOpen?: boolean, opt_bubbleType?: BubbleType) { + opt_block?: BlockSvg, + opt_isOpen?: boolean, + opt_bubbleType?: BubbleType + ) { const workspaceId = opt_block ? opt_block.workspace.id : undefined; super(workspaceId); if (!opt_block) return; @@ -63,13 +65,15 @@ export class BubbleOpen extends UiBase { const json = super.toJson() as BubbleOpenJson; if (this.isOpen === undefined) { throw new Error( - 'Whether this event is for opening the bubble is undefined. ' + - 'Either pass the value to the constructor, or call fromJson'); + 'Whether this event is for opening the bubble is undefined. ' + + 'Either pass the value to the constructor, or call fromJson' + ); } if (!this.bubbleType) { throw new Error( - 'The type of bubble is undefined. Either pass the ' + - 'value to the constructor, or call fromJson'); + 'The type of bubble is undefined. Either pass the ' + + 'value to the constructor, or call fromJson' + ); } json['isOpen'] = this.isOpen; json['bubbleType'] = this.bubbleType; @@ -84,8 +88,11 @@ export class BubbleOpen extends UiBase { */ override fromJson(json: BubbleOpenJson) { deprecation.warn( - 'Blockly.Events.BubbleOpen.prototype.fromJson', 'version 9', - 'version 10', 'Blockly.Events.fromJson'); + 'Blockly.Events.BubbleOpen.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); this.isOpen = json['isOpen']; this.bubbleType = json['bubbleType']; @@ -101,11 +108,16 @@ export class BubbleOpen extends UiBase { * parameters to static methods in superclasses. * @internal */ - static fromJson(json: BubbleOpenJson, workspace: Workspace, event?: any): - BubbleOpen { - const newEvent = - super.fromJson(json, workspace, event ?? new BubbleOpen()) as - BubbleOpen; + static fromJson( + json: BubbleOpenJson, + workspace: Workspace, + event?: any + ): BubbleOpen { + const newEvent = super.fromJson( + json, + workspace, + event ?? new BubbleOpen() + ) as BubbleOpen; newEvent.isOpen = json['isOpen']; newEvent.bubbleType = json['bubbleType']; newEvent.blockId = json['blockId']; diff --git a/core/events/events_click.ts b/core/events/events_click.ts index 66915762ae1..3cb9c221c8f 100644 --- a/core/events/events_click.ts +++ b/core/events/events_click.ts @@ -21,7 +21,6 @@ import {UiBase} from './events_ui_base.js'; import * as eventUtils from './utils.js'; import {Workspace} from '../workspace.js'; - /** * Notifies listeners that ome blockly element was clicked. */ @@ -46,8 +45,10 @@ export class Click extends UiBase { * Undefined for a blank event. */ constructor( - opt_block?: Block|null, opt_workspaceId?: string|null, - opt_targetType?: ClickTarget) { + opt_block?: Block | null, + opt_workspaceId?: string | null, + opt_targetType?: ClickTarget + ) { let workspaceId = opt_block ? opt_block.workspace.id : opt_workspaceId; if (workspaceId === null) { workspaceId = undefined; @@ -67,8 +68,9 @@ export class Click extends UiBase { const json = super.toJson() as ClickJson; if (!this.targetType) { throw new Error( - 'The click target type is undefined. Either pass a block to ' + - 'the constructor, or call fromJson'); + 'The click target type is undefined. Either pass a block to ' + + 'the constructor, or call fromJson' + ); } json['targetType'] = this.targetType; json['blockId'] = this.blockId; @@ -82,8 +84,11 @@ export class Click extends UiBase { */ override fromJson(json: ClickJson) { deprecation.warn( - 'Blockly.Events.Click.prototype.fromJson', 'version 9', 'version 10', - 'Blockly.Events.fromJson'); + 'Blockly.Events.Click.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); this.targetType = json['targetType']; this.blockId = json['blockId']; @@ -99,8 +104,11 @@ export class Click extends UiBase { * @internal */ static fromJson(json: ClickJson, workspace: Workspace, event?: any): Click { - const newEvent = - super.fromJson(json, workspace, event ?? new Click()) as Click; + const newEvent = super.fromJson( + json, + workspace, + event ?? new Click() + ) as Click; newEvent.targetType = json['targetType']; newEvent.blockId = json['blockId']; return newEvent; diff --git a/core/events/events_comment_base.ts b/core/events/events_comment_base.ts index bee9608862e..4567eda2c05 100644 --- a/core/events/events_comment_base.ts +++ b/core/events/events_comment_base.ts @@ -17,13 +17,15 @@ import * as utilsXml from '../utils/xml.js'; import type {WorkspaceComment} from '../workspace_comment.js'; import * as Xml from '../xml.js'; -import {Abstract as AbstractEvent, AbstractEventJson} from './events_abstract.js'; +import { + Abstract as AbstractEvent, + AbstractEventJson, +} from './events_abstract.js'; import type {CommentCreate} from './events_comment_create.js'; import type {CommentDelete} from './events_comment_delete.js'; import * as eventUtils from './utils.js'; import type {Workspace} from '../workspace.js'; - /** * Abstract class for a comment event. */ @@ -59,8 +61,9 @@ export class CommentBase extends AbstractEvent { const json = super.toJson() as CommentBaseJson; if (!this.commentId) { throw new Error( - 'The comment ID is undefined. Either pass a comment to ' + - 'the constructor, or call fromJson'); + 'The comment ID is undefined. Either pass a comment to ' + + 'the constructor, or call fromJson' + ); } json['commentId'] = this.commentId; return json; @@ -73,8 +76,11 @@ export class CommentBase extends AbstractEvent { */ override fromJson(json: CommentBaseJson) { deprecation.warn( - 'Blockly.Events.CommentBase.prototype.fromJson', 'version 9', - 'version 10', 'Blockly.Events.fromJson'); + 'Blockly.Events.CommentBase.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); this.commentId = json['commentId']; } @@ -88,11 +94,16 @@ export class CommentBase extends AbstractEvent { * parameters to static methods in superclasses. * @internal */ - static fromJson(json: CommentBaseJson, workspace: Workspace, event?: any): - CommentBase { - const newEvent = - super.fromJson(json, workspace, event ?? new CommentBase()) as - CommentBase; + static fromJson( + json: CommentBaseJson, + workspace: Workspace, + event?: any + ): CommentBase { + const newEvent = super.fromJson( + json, + workspace, + event ?? new CommentBase() + ) as CommentBase; newEvent.commentId = json['commentId']; return newEvent; } @@ -104,7 +115,9 @@ export class CommentBase extends AbstractEvent { * @param create if True then Create, if False then Delete */ static CommentCreateDeleteHelper( - event: CommentCreate|CommentDelete, create: boolean) { + event: CommentCreate | CommentDelete, + create: boolean + ) { const workspace = event.getEventWorkspace_(); if (create) { const xmlElement = utilsXml.createElement('xml'); @@ -116,16 +129,16 @@ export class CommentBase extends AbstractEvent { } else { if (!event.commentId) { throw new Error( - 'The comment ID is undefined. Either pass a comment to ' + - 'the constructor, or call fromJson'); + 'The comment ID is undefined. Either pass a comment to ' + + 'the constructor, or call fromJson' + ); } const comment = workspace.getCommentById(event.commentId); if (comment) { comment.dispose(); } else { // Only complain about root-level block. - console.warn( - 'Can\'t uncreate non-existent comment: ' + event.commentId); + console.warn("Can't uncreate non-existent comment: " + event.commentId); } } } diff --git a/core/events/events_comment_change.ts b/core/events/events_comment_change.ts index d154f64256e..2892473ff4d 100644 --- a/core/events/events_comment_change.ts +++ b/core/events/events_comment_change.ts @@ -20,7 +20,6 @@ import {CommentBase, CommentBaseJson} from './events_comment_base.js'; import * as eventUtils from './utils.js'; import type {Workspace} from '../workspace.js'; - /** * Notifies listeners that the contents of a workspace comment has changed. */ @@ -41,18 +40,20 @@ export class CommentChange extends CommentBase { * @param opt_newContents New contents of the comment. */ constructor( - opt_comment?: WorkspaceComment, opt_oldContents?: string, - opt_newContents?: string) { + opt_comment?: WorkspaceComment, + opt_oldContents?: string, + opt_newContents?: string + ) { super(opt_comment); if (!opt_comment) { - return; // Blank event to be populated by fromJson. + return; // Blank event to be populated by fromJson. } this.oldContents_ = - typeof opt_oldContents === 'undefined' ? '' : opt_oldContents; + typeof opt_oldContents === 'undefined' ? '' : opt_oldContents; this.newContents_ = - typeof opt_newContents === 'undefined' ? '' : opt_newContents; + typeof opt_newContents === 'undefined' ? '' : opt_newContents; } /** @@ -64,13 +65,15 @@ export class CommentChange extends CommentBase { const json = super.toJson() as CommentChangeJson; if (!this.oldContents_) { throw new Error( - 'The old contents is undefined. Either pass a value to ' + - 'the constructor, or call fromJson'); + 'The old contents is undefined. Either pass a value to ' + + 'the constructor, or call fromJson' + ); } if (!this.newContents_) { throw new Error( - 'The new contents is undefined. Either pass a value to ' + - 'the constructor, or call fromJson'); + 'The new contents is undefined. Either pass a value to ' + + 'the constructor, or call fromJson' + ); } json['oldContents'] = this.oldContents_; json['newContents'] = this.newContents_; @@ -84,8 +87,11 @@ export class CommentChange extends CommentBase { */ override fromJson(json: CommentChangeJson) { deprecation.warn( - 'Blockly.Events.CommentChange.prototype.fromJson', 'version 9', - 'version 10', 'Blockly.Events.fromJson'); + 'Blockly.Events.CommentChange.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); this.oldContents_ = json['oldContents']; this.newContents_ = json['newContents']; @@ -100,11 +106,16 @@ export class CommentChange extends CommentBase { * parameters to static methods in superclasses. * @internal */ - static fromJson(json: CommentChangeJson, workspace: Workspace, event?: any): - CommentChange { - const newEvent = - super.fromJson(json, workspace, event ?? new CommentChange()) as - CommentChange; + static fromJson( + json: CommentChangeJson, + workspace: Workspace, + event?: any + ): CommentChange { + const newEvent = super.fromJson( + json, + workspace, + event ?? new CommentChange() + ) as CommentChange; newEvent.oldContents_ = json['oldContents']; newEvent.newContents_ = json['newContents']; return newEvent; @@ -128,24 +139,27 @@ export class CommentChange extends CommentBase { const workspace = this.getEventWorkspace_(); if (!this.commentId) { throw new Error( - 'The comment ID is undefined. Either pass a comment to ' + - 'the constructor, or call fromJson'); + 'The comment ID is undefined. Either pass a comment to ' + + 'the constructor, or call fromJson' + ); } const comment = workspace.getCommentById(this.commentId); if (!comment) { - console.warn('Can\'t change non-existent comment: ' + this.commentId); + console.warn("Can't change non-existent comment: " + this.commentId); return; } const contents = forward ? this.newContents_ : this.oldContents_; if (!contents) { if (forward) { throw new Error( - 'The new contents is undefined. Either pass a value to ' + - 'the constructor, or call fromJson'); + 'The new contents is undefined. Either pass a value to ' + + 'the constructor, or call fromJson' + ); } throw new Error( - 'The old contents is undefined. Either pass a value to ' + - 'the constructor, or call fromJson'); + 'The old contents is undefined. Either pass a value to ' + + 'the constructor, or call fromJson' + ); } comment.setContent(contents); } @@ -157,4 +171,7 @@ export interface CommentChangeJson extends CommentBaseJson { } registry.register( - registry.Type.EVENT, eventUtils.COMMENT_CHANGE, CommentChange); + registry.Type.EVENT, + eventUtils.COMMENT_CHANGE, + CommentChange +); diff --git a/core/events/events_comment_create.ts b/core/events/events_comment_create.ts index 261a6c00ff1..8a21de77e68 100644 --- a/core/events/events_comment_create.ts +++ b/core/events/events_comment_create.ts @@ -22,7 +22,6 @@ import {CommentBase, CommentBaseJson} from './events_comment_base.js'; import * as eventUtils from './utils.js'; import type {Workspace} from '../workspace.js'; - /** * Notifies listeners that a workspace comment was created. */ @@ -30,7 +29,7 @@ export class CommentCreate extends CommentBase { override type = eventUtils.COMMENT_CREATE; /** The XML representation of the created workspace comment. */ - xml?: Element|DocumentFragment; + xml?: Element | DocumentFragment; /** * @param opt_comment The created comment. @@ -56,8 +55,9 @@ export class CommentCreate extends CommentBase { const json = super.toJson() as CommentCreateJson; if (!this.xml) { throw new Error( - 'The comment XML is undefined. Either pass a comment to ' + - 'the constructor, or call fromJson'); + 'The comment XML is undefined. Either pass a comment to ' + + 'the constructor, or call fromJson' + ); } json['xml'] = Xml.domToText(this.xml); return json; @@ -70,8 +70,11 @@ export class CommentCreate extends CommentBase { */ override fromJson(json: CommentCreateJson) { deprecation.warn( - 'Blockly.Events.CommentCreate.prototype.fromJson', 'version 9', - 'version 10', 'Blockly.Events.fromJson'); + 'Blockly.Events.CommentCreate.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); this.xml = utilsXml.textToDom(json['xml']); } @@ -85,11 +88,16 @@ export class CommentCreate extends CommentBase { * parameters to static methods in superclasses. * @internal */ - static fromJson(json: CommentCreateJson, workspace: Workspace, event?: any): - CommentCreate { - const newEvent = - super.fromJson(json, workspace, event ?? new CommentCreate()) as - CommentCreate; + static fromJson( + json: CommentCreateJson, + workspace: Workspace, + event?: any + ): CommentCreate { + const newEvent = super.fromJson( + json, + workspace, + event ?? new CommentCreate() + ) as CommentCreate; newEvent.xml = utilsXml.textToDom(json['xml']); return newEvent; } @@ -109,4 +117,7 @@ export interface CommentCreateJson extends CommentBaseJson { } registry.register( - registry.Type.EVENT, eventUtils.COMMENT_CREATE, CommentCreate); + registry.Type.EVENT, + eventUtils.COMMENT_CREATE, + CommentCreate +); diff --git a/core/events/events_comment_delete.ts b/core/events/events_comment_delete.ts index 736ef622359..8b418735de8 100644 --- a/core/events/events_comment_delete.ts +++ b/core/events/events_comment_delete.ts @@ -21,7 +21,6 @@ import * as utilsXml from '../utils/xml.js'; import * as Xml from '../xml.js'; import type {Workspace} from '../workspace.js'; - /** * Notifies listeners that a workspace comment has been deleted. */ @@ -39,7 +38,7 @@ export class CommentDelete extends CommentBase { super(opt_comment); if (!opt_comment) { - return; // Blank event to be populated by fromJson. + return; // Blank event to be populated by fromJson. } this.xml = opt_comment.toXmlWithXY(); @@ -63,8 +62,9 @@ export class CommentDelete extends CommentBase { const json = super.toJson() as CommentDeleteJson; if (!this.xml) { throw new Error( - 'The comment XML is undefined. Either pass a comment to ' + - 'the constructor, or call fromJson'); + 'The comment XML is undefined. Either pass a comment to ' + + 'the constructor, or call fromJson' + ); } json['xml'] = Xml.domToText(this.xml); return json; @@ -79,11 +79,16 @@ export class CommentDelete extends CommentBase { * parameters to static methods in superclasses. * @internal */ - static fromJson(json: CommentDeleteJson, workspace: Workspace, event?: any): - CommentDelete { - const newEvent = - super.fromJson(json, workspace, event ?? new CommentDelete()) as - CommentDelete; + static fromJson( + json: CommentDeleteJson, + workspace: Workspace, + event?: any + ): CommentDelete { + const newEvent = super.fromJson( + json, + workspace, + event ?? new CommentDelete() + ) as CommentDelete; newEvent.xml = utilsXml.textToDom(json['xml']); return newEvent; } @@ -94,4 +99,7 @@ export interface CommentDeleteJson extends CommentBaseJson { } registry.register( - registry.Type.EVENT, eventUtils.COMMENT_DELETE, CommentDelete); + registry.Type.EVENT, + eventUtils.COMMENT_DELETE, + CommentDelete +); diff --git a/core/events/events_comment_move.ts b/core/events/events_comment_move.ts index 31a893c0a0b..bf878d0ee3e 100644 --- a/core/events/events_comment_move.ts +++ b/core/events/events_comment_move.ts @@ -21,7 +21,6 @@ import {CommentBase, CommentBaseJson} from './events_comment_base.js'; import * as eventUtils from './utils.js'; import type {Workspace} from '../workspace.js'; - /** * Notifies listeners that a workspace comment has moved. */ @@ -46,7 +45,7 @@ export class CommentMove extends CommentBase { super(opt_comment); if (!opt_comment) { - return; // Blank event to be populated by fromJson. + return; // Blank event to be populated by fromJson. } this.comment_ = opt_comment; @@ -60,13 +59,15 @@ export class CommentMove extends CommentBase { recordNew() { if (this.newCoordinate_) { throw Error( - 'Tried to record the new position of a comment on the ' + - 'same event twice.'); + 'Tried to record the new position of a comment on the ' + + 'same event twice.' + ); } if (!this.comment_) { throw new Error( - 'The comment is undefined. Pass a comment to ' + - 'the constructor if you want to use the record functionality'); + 'The comment is undefined. Pass a comment to ' + + 'the constructor if you want to use the record functionality' + ); } this.newCoordinate_ = this.comment_.getRelativeToSurfaceXY(); } @@ -91,18 +92,23 @@ export class CommentMove extends CommentBase { const json = super.toJson() as CommentMoveJson; if (!this.oldCoordinate_) { throw new Error( - 'The old comment position is undefined. Either pass a comment to ' + - 'the constructor, or call fromJson'); + 'The old comment position is undefined. Either pass a comment to ' + + 'the constructor, or call fromJson' + ); } if (!this.newCoordinate_) { throw new Error( - 'The new comment position is undefined. Either call recordNew, or ' + - 'call fromJson'); + 'The new comment position is undefined. Either call recordNew, or ' + + 'call fromJson' + ); } - json['oldCoordinate'] = `${Math.round(this.oldCoordinate_.x)}, ` + - `${Math.round(this.oldCoordinate_.y)}`; - json['newCoordinate'] = Math.round(this.newCoordinate_.x) + ',' + - Math.round(this.newCoordinate_.y); + json['oldCoordinate'] = + `${Math.round(this.oldCoordinate_.x)}, ` + + `${Math.round(this.oldCoordinate_.y)}`; + json['newCoordinate'] = + Math.round(this.newCoordinate_.x) + + ',' + + Math.round(this.newCoordinate_.y); return json; } @@ -113,8 +119,11 @@ export class CommentMove extends CommentBase { */ override fromJson(json: CommentMoveJson) { deprecation.warn( - 'Blockly.Events.CommentMove.prototype.fromJson', 'version 9', - 'version 10', 'Blockly.Events.fromJson'); + 'Blockly.Events.CommentMove.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); let xy = json['oldCoordinate'].split(','); this.oldCoordinate_ = new Coordinate(Number(xy[0]), Number(xy[1])); @@ -131,11 +140,16 @@ export class CommentMove extends CommentBase { * parameters to static methods in superclasses. * @internal */ - static fromJson(json: CommentMoveJson, workspace: Workspace, event?: any): - CommentMove { - const newEvent = - super.fromJson(json, workspace, event ?? new CommentMove()) as - CommentMove; + static fromJson( + json: CommentMoveJson, + workspace: Workspace, + event?: any + ): CommentMove { + const newEvent = super.fromJson( + json, + workspace, + event ?? new CommentMove() + ) as CommentMove; let xy = json['oldCoordinate'].split(','); newEvent.oldCoordinate_ = new Coordinate(Number(xy[0]), Number(xy[1])); xy = json['newCoordinate'].split(','); @@ -161,21 +175,23 @@ export class CommentMove extends CommentBase { const workspace = this.getEventWorkspace_(); if (!this.commentId) { throw new Error( - 'The comment ID is undefined. Either pass a comment to ' + - 'the constructor, or call fromJson'); + 'The comment ID is undefined. Either pass a comment to ' + + 'the constructor, or call fromJson' + ); } const comment = workspace.getCommentById(this.commentId); if (!comment) { - console.warn('Can\'t move non-existent comment: ' + this.commentId); + console.warn("Can't move non-existent comment: " + this.commentId); return; } const target = forward ? this.newCoordinate_ : this.oldCoordinate_; if (!target) { throw new Error( - 'Either oldCoordinate_ or newCoordinate_ is undefined. ' + + 'Either oldCoordinate_ or newCoordinate_ is undefined. ' + 'Either pass a comment to the constructor and call recordNew, ' + - 'or call fromJson'); + 'or call fromJson' + ); } // TODO: Check if the comment is being dragged, and give up if so. const current = comment.getRelativeToSurfaceXY(); diff --git a/core/events/events_marker_move.ts b/core/events/events_marker_move.ts index dcb4fe78444..4aa24186a8f 100644 --- a/core/events/events_marker_move.ts +++ b/core/events/events_marker_move.ts @@ -22,7 +22,6 @@ import {AbstractEventJson} from './events_abstract.js'; import {UiBase} from './events_ui_base.js'; import * as eventUtils from './utils.js'; - /** * Notifies listeners that a marker (used for keyboard navigation) has * moved. @@ -57,8 +56,11 @@ export class MarkerMove extends UiBase { * Undefined for a blank event. */ constructor( - opt_block?: Block|null, isCursor?: boolean, opt_oldNode?: ASTNode|null, - opt_newNode?: ASTNode) { + opt_block?: Block | null, + isCursor?: boolean, + opt_oldNode?: ASTNode | null, + opt_newNode?: ASTNode + ) { let workspaceId = opt_block ? opt_block.workspace.id : undefined; if (opt_newNode && opt_newNode.getType() === ASTNode.types.WORKSPACE) { workspaceId = (opt_newNode.getLocation() as Workspace).id; @@ -80,13 +82,15 @@ export class MarkerMove extends UiBase { const json = super.toJson() as MarkerMoveJson; if (this.isCursor === undefined) { throw new Error( - 'Whether this is a cursor event or not is undefined. Either pass ' + - 'a value to the constructor, or call fromJson'); + 'Whether this is a cursor event or not is undefined. Either pass ' + + 'a value to the constructor, or call fromJson' + ); } if (!this.newNode) { throw new Error( - 'The new node is undefined. Either pass a node to ' + - 'the constructor, or call fromJson'); + 'The new node is undefined. Either pass a node to ' + + 'the constructor, or call fromJson' + ); } json['isCursor'] = this.isCursor; json['blockId'] = this.blockId; @@ -102,8 +106,11 @@ export class MarkerMove extends UiBase { */ override fromJson(json: MarkerMoveJson) { deprecation.warn( - 'Blockly.Events.MarkerMove.prototype.fromJson', 'version 9', - 'version 10', 'Blockly.Events.fromJson'); + 'Blockly.Events.MarkerMove.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); this.isCursor = json['isCursor']; this.blockId = json['blockId']; @@ -120,11 +127,16 @@ export class MarkerMove extends UiBase { * parameters to static methods in superclasses. * @internal */ - static fromJson(json: MarkerMoveJson, workspace: Workspace, event?: any): - MarkerMove { - const newEvent = - super.fromJson(json, workspace, event ?? new MarkerMove()) as - MarkerMove; + static fromJson( + json: MarkerMoveJson, + workspace: Workspace, + event?: any + ): MarkerMove { + const newEvent = super.fromJson( + json, + workspace, + event ?? new MarkerMove() + ) as MarkerMove; newEvent.isCursor = json['isCursor']; newEvent.blockId = json['blockId']; newEvent.oldNode = json['oldNode']; diff --git a/core/events/events_selected.ts b/core/events/events_selected.ts index fc1d6ce3fb2..484dcce99cf 100644 --- a/core/events/events_selected.ts +++ b/core/events/events_selected.ts @@ -20,7 +20,6 @@ import {UiBase} from './events_ui_base.js'; import * as eventUtils from './utils.js'; import type {Workspace} from '../workspace.js'; - /** * Class for a selected event. * Notifies listeners that a new element has been selected. @@ -46,8 +45,10 @@ export class Selected extends UiBase { * Null if no element previously selected. Undefined for a blank event. */ constructor( - opt_oldElementId?: string|null, opt_newElementId?: string|null, - opt_workspaceId?: string) { + opt_oldElementId?: string | null, + opt_newElementId?: string | null, + opt_workspaceId?: string + ) { super(opt_workspaceId); this.oldElementId = opt_oldElementId ?? undefined; @@ -73,8 +74,11 @@ export class Selected extends UiBase { */ override fromJson(json: SelectedJson) { deprecation.warn( - 'Blockly.Events.Selected.prototype.fromJson', 'version 9', 'version 10', - 'Blockly.Events.fromJson'); + 'Blockly.Events.Selected.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); this.oldElementId = json['oldElementId']; this.newElementId = json['newElementId']; @@ -89,10 +93,16 @@ export class Selected extends UiBase { * static methods in superclasses. * @internal */ - static fromJson(json: SelectedJson, workspace: Workspace, event?: any): - Selected { - const newEvent = - super.fromJson(json, workspace, event ?? new Selected()) as Selected; + static fromJson( + json: SelectedJson, + workspace: Workspace, + event?: any + ): Selected { + const newEvent = super.fromJson( + json, + workspace, + event ?? new Selected() + ) as Selected; newEvent.oldElementId = json['oldElementId']; newEvent.newElementId = json['newElementId']; return newEvent; diff --git a/core/events/events_theme_change.ts b/core/events/events_theme_change.ts index d7ebef5bf58..991937270fa 100644 --- a/core/events/events_theme_change.ts +++ b/core/events/events_theme_change.ts @@ -19,7 +19,6 @@ import {UiBase} from './events_ui_base.js'; import * as eventUtils from './utils.js'; import type {Workspace} from '../workspace.js'; - /** * Notifies listeners that the workspace theme has changed. */ @@ -48,8 +47,9 @@ export class ThemeChange extends UiBase { const json = super.toJson() as ThemeChangeJson; if (!this.themeName) { throw new Error( - 'The theme name is undefined. Either pass a theme name to ' + - 'the constructor, or call fromJson'); + 'The theme name is undefined. Either pass a theme name to ' + + 'the constructor, or call fromJson' + ); } json['themeName'] = this.themeName; return json; @@ -62,8 +62,11 @@ export class ThemeChange extends UiBase { */ override fromJson(json: ThemeChangeJson) { deprecation.warn( - 'Blockly.Events.ThemeChange.prototype.fromJson', 'version 9', - 'version 10', 'Blockly.Events.fromJson'); + 'Blockly.Events.ThemeChange.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); this.themeName = json['themeName']; } @@ -77,11 +80,16 @@ export class ThemeChange extends UiBase { * parameters to static methods in superclasses. * @internal */ - static fromJson(json: ThemeChangeJson, workspace: Workspace, event?: any): - ThemeChange { - const newEvent = - super.fromJson(json, workspace, event ?? new ThemeChange()) as - ThemeChange; + static fromJson( + json: ThemeChangeJson, + workspace: Workspace, + event?: any + ): ThemeChange { + const newEvent = super.fromJson( + json, + workspace, + event ?? new ThemeChange() + ) as ThemeChange; newEvent.themeName = json['themeName']; return newEvent; } diff --git a/core/events/events_toolbox_item_select.ts b/core/events/events_toolbox_item_select.ts index 4eca80b4d29..4d659c6ac8b 100644 --- a/core/events/events_toolbox_item_select.ts +++ b/core/events/events_toolbox_item_select.ts @@ -19,7 +19,6 @@ import {UiBase} from './events_ui_base.js'; import * as eventUtils from './utils.js'; import type {Workspace} from '../workspace.js'; - /** * Notifies listeners that a toolbox item has been selected. */ @@ -41,8 +40,10 @@ export class ToolboxItemSelect extends UiBase { * Undefined for a blank event. */ constructor( - opt_oldItem?: string|null, opt_newItem?: string|null, - opt_workspaceId?: string) { + opt_oldItem?: string | null, + opt_newItem?: string | null, + opt_workspaceId?: string + ) { super(opt_workspaceId); this.oldItem = opt_oldItem ?? undefined; this.newItem = opt_newItem ?? undefined; @@ -67,8 +68,11 @@ export class ToolboxItemSelect extends UiBase { */ override fromJson(json: ToolboxItemSelectJson) { deprecation.warn( - 'Blockly.Events.ToolboxItemSelect.prototype.fromJson', 'version 9', - 'version 10', 'Blockly.Events.fromJson'); + 'Blockly.Events.ToolboxItemSelect.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); this.oldItem = json['oldItem']; this.newItem = json['newItem']; @@ -84,11 +88,15 @@ export class ToolboxItemSelect extends UiBase { * @internal */ static fromJson( - json: ToolboxItemSelectJson, workspace: Workspace, - event?: any): ToolboxItemSelect { - const newEvent = - super.fromJson(json, workspace, event ?? new ToolboxItemSelect()) as - ToolboxItemSelect; + json: ToolboxItemSelectJson, + workspace: Workspace, + event?: any + ): ToolboxItemSelect { + const newEvent = super.fromJson( + json, + workspace, + event ?? new ToolboxItemSelect() + ) as ToolboxItemSelect; newEvent.oldItem = json['oldItem']; newEvent.newItem = json['newItem']; return newEvent; @@ -101,4 +109,7 @@ export interface ToolboxItemSelectJson extends AbstractEventJson { } registry.register( - registry.Type.EVENT, eventUtils.TOOLBOX_ITEM_SELECT, ToolboxItemSelect); + registry.Type.EVENT, + eventUtils.TOOLBOX_ITEM_SELECT, + ToolboxItemSelect +); diff --git a/core/events/events_trashcan_open.ts b/core/events/events_trashcan_open.ts index fd03b2da6a4..ea10cccf2e9 100644 --- a/core/events/events_trashcan_open.ts +++ b/core/events/events_trashcan_open.ts @@ -20,7 +20,6 @@ import {UiBase} from './events_ui_base.js'; import * as eventUtils from './utils.js'; import type {Workspace} from '../workspace.js'; - /** * Notifies listeners when the trashcan is opening or closing. */ @@ -52,8 +51,9 @@ export class TrashcanOpen extends UiBase { const json = super.toJson() as TrashcanOpenJson; if (this.isOpen === undefined) { throw new Error( - 'Whether this is already open or not is undefined. Either pass ' + - 'a value to the constructor, or call fromJson'); + 'Whether this is already open or not is undefined. Either pass ' + + 'a value to the constructor, or call fromJson' + ); } json['isOpen'] = this.isOpen; return json; @@ -66,8 +66,11 @@ export class TrashcanOpen extends UiBase { */ override fromJson(json: TrashcanOpenJson) { deprecation.warn( - 'Blockly.Events.TrashcanOpen.prototype.fromJson', 'version 9', - 'version 10', 'Blockly.Events.fromJson'); + 'Blockly.Events.TrashcanOpen.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); this.isOpen = json['isOpen']; } @@ -81,11 +84,16 @@ export class TrashcanOpen extends UiBase { * parameters to static methods in superclasses. * @internal */ - static fromJson(json: TrashcanOpenJson, workspace: Workspace, event?: any): - TrashcanOpen { - const newEvent = - super.fromJson(json, workspace, event ?? new TrashcanOpen()) as - TrashcanOpen; + static fromJson( + json: TrashcanOpenJson, + workspace: Workspace, + event?: any + ): TrashcanOpen { + const newEvent = super.fromJson( + json, + workspace, + event ?? new TrashcanOpen() + ) as TrashcanOpen; newEvent.isOpen = json['isOpen']; return newEvent; } diff --git a/core/events/events_ui.ts b/core/events/events_ui.ts index f743f14d92d..aaeccb43efa 100644 --- a/core/events/events_ui.ts +++ b/core/events/events_ui.ts @@ -18,7 +18,6 @@ import * as registry from '../registry.js'; import {UiBase} from './events_ui_base.js'; import * as eventUtils from './utils.js'; - /** * Class for a UI event. * @@ -39,8 +38,11 @@ export class Ui extends UiBase { * @param opt_newValue New value of element. */ constructor( - opt_block?: Block|null, opt_element?: string, - opt_oldValue?: AnyDuringMigration, opt_newValue?: AnyDuringMigration) { + opt_block?: Block | null, + opt_element?: string, + opt_oldValue?: AnyDuringMigration, + opt_newValue?: AnyDuringMigration + ) { const workspaceId = opt_block ? opt_block.workspace.id : undefined; super(workspaceId); diff --git a/core/events/events_ui_base.ts b/core/events/events_ui_base.ts index be5a60d59c2..60c7b5e45be 100644 --- a/core/events/events_ui_base.ts +++ b/core/events/events_ui_base.ts @@ -15,7 +15,6 @@ goog.declareModuleId('Blockly.Events.UiBase'); import {Abstract as AbstractEvent} from './events_abstract.js'; - /** * Base class for a UI event. * UI events are events that don't need to be sent over the wire for multi-user diff --git a/core/events/events_var_base.ts b/core/events/events_var_base.ts index f25628888c8..4b4356e70fe 100644 --- a/core/events/events_var_base.ts +++ b/core/events/events_var_base.ts @@ -15,10 +15,12 @@ goog.declareModuleId('Blockly.Events.VarBase'); import * as deprecation from '../utils/deprecation.js'; import type {VariableModel} from '../variable_model.js'; -import {Abstract as AbstractEvent, AbstractEventJson} from './events_abstract.js'; +import { + Abstract as AbstractEvent, + AbstractEventJson, +} from './events_abstract.js'; import type {Workspace} from '../workspace.js'; - /** * Abstract class for a variable event. */ @@ -49,8 +51,9 @@ export class VarBase extends AbstractEvent { const json = super.toJson() as VarBaseJson; if (!this.varId) { throw new Error( - 'The var ID is undefined. Either pass a variable to ' + - 'the constructor, or call fromJson'); + 'The var ID is undefined. Either pass a variable to ' + + 'the constructor, or call fromJson' + ); } json['varId'] = this.varId; return json; @@ -63,8 +66,11 @@ export class VarBase extends AbstractEvent { */ override fromJson(json: VarBaseJson) { deprecation.warn( - 'Blockly.Events.VarBase.prototype.fromJson', 'version 9', 'version 10', - 'Blockly.Events.fromJson'); + 'Blockly.Events.VarBase.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); this.varId = json['varId']; } @@ -78,10 +84,16 @@ export class VarBase extends AbstractEvent { * static methods in superclasses. * @internal */ - static fromJson(json: VarBaseJson, workspace: Workspace, event?: any): - VarBase { - const newEvent = - super.fromJson(json, workspace, event ?? new VarBase()) as VarBase; + static fromJson( + json: VarBaseJson, + workspace: Workspace, + event?: any + ): VarBase { + const newEvent = super.fromJson( + json, + workspace, + event ?? new VarBase() + ) as VarBase; newEvent.varId = json['varId']; return newEvent; } diff --git a/core/events/events_var_create.ts b/core/events/events_var_create.ts index eb8ccfe9ffd..ca400c46a1e 100644 --- a/core/events/events_var_create.ts +++ b/core/events/events_var_create.ts @@ -20,7 +20,6 @@ import {VarBase, VarBaseJson} from './events_var_base.js'; import * as eventUtils from './utils.js'; import type {Workspace} from '../workspace.js'; - /** * Notifies listeners that a variable model has been created. */ @@ -40,7 +39,7 @@ export class VarCreate extends VarBase { super(opt_variable); if (!opt_variable) { - return; // Blank event to be populated by fromJson. + return; // Blank event to be populated by fromJson. } this.varType = opt_variable.type; this.varName = opt_variable.name; @@ -55,13 +54,15 @@ export class VarCreate extends VarBase { const json = super.toJson() as VarCreateJson; if (this.varType === undefined) { throw new Error( - 'The var type is undefined. Either pass a variable to ' + - 'the constructor, or call fromJson'); + 'The var type is undefined. Either pass a variable to ' + + 'the constructor, or call fromJson' + ); } if (!this.varName) { throw new Error( - 'The var name is undefined. Either pass a variable to ' + - 'the constructor, or call fromJson'); + 'The var name is undefined. Either pass a variable to ' + + 'the constructor, or call fromJson' + ); } json['varType'] = this.varType; json['varName'] = this.varName; @@ -75,8 +76,11 @@ export class VarCreate extends VarBase { */ override fromJson(json: VarCreateJson) { deprecation.warn( - 'Blockly.Events.VarCreate.prototype.fromJson', 'version 9', - 'version 10', 'Blockly.Events.fromJson'); + 'Blockly.Events.VarCreate.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); this.varType = json['varType']; this.varName = json['varName']; @@ -91,10 +95,16 @@ export class VarCreate extends VarBase { * static methods in superclasses. * @internal */ - static fromJson(json: VarCreateJson, workspace: Workspace, event?: any): - VarCreate { - const newEvent = - super.fromJson(json, workspace, event ?? new VarCreate()) as VarCreate; + static fromJson( + json: VarCreateJson, + workspace: Workspace, + event?: any + ): VarCreate { + const newEvent = super.fromJson( + json, + workspace, + event ?? new VarCreate() + ) as VarCreate; newEvent.varType = json['varType']; newEvent.varName = json['varName']; return newEvent; @@ -109,13 +119,15 @@ export class VarCreate extends VarBase { const workspace = this.getEventWorkspace_(); if (!this.varId) { throw new Error( - 'The var ID is undefined. Either pass a variable to ' + - 'the constructor, or call fromJson'); + 'The var ID is undefined. Either pass a variable to ' + + 'the constructor, or call fromJson' + ); } if (!this.varName) { throw new Error( - 'The var name is undefined. Either pass a variable to ' + - 'the constructor, or call fromJson'); + 'The var name is undefined. Either pass a variable to ' + + 'the constructor, or call fromJson' + ); } if (forward) { workspace.createVariable(this.varName, this.varType, this.varId); diff --git a/core/events/events_var_delete.ts b/core/events/events_var_delete.ts index be0fec4fea3..a19a547d136 100644 --- a/core/events/events_var_delete.ts +++ b/core/events/events_var_delete.ts @@ -15,7 +15,6 @@ import {VarBase, VarBaseJson} from './events_var_base.js'; import * as eventUtils from './utils.js'; import type {Workspace} from '../workspace.js'; - /** * Notifies listeners that a variable model has been deleted. * @@ -35,7 +34,7 @@ export class VarDelete extends VarBase { super(opt_variable); if (!opt_variable) { - return; // Blank event to be populated by fromJson. + return; // Blank event to be populated by fromJson. } this.varType = opt_variable.type; this.varName = opt_variable.name; @@ -50,13 +49,15 @@ export class VarDelete extends VarBase { const json = super.toJson() as VarDeleteJson; if (this.varType === undefined) { throw new Error( - 'The var type is undefined. Either pass a variable to ' + - 'the constructor, or call fromJson'); + 'The var type is undefined. Either pass a variable to ' + + 'the constructor, or call fromJson' + ); } if (!this.varName) { throw new Error( - 'The var name is undefined. Either pass a variable to ' + - 'the constructor, or call fromJson'); + 'The var name is undefined. Either pass a variable to ' + + 'the constructor, or call fromJson' + ); } json['varType'] = this.varType; json['varName'] = this.varName; @@ -70,8 +71,11 @@ export class VarDelete extends VarBase { */ override fromJson(json: VarDeleteJson) { deprecation.warn( - 'Blockly.Events.VarDelete.prototype.fromJson', 'version 9', - 'version 10', 'Blockly.Events.fromJson'); + 'Blockly.Events.VarDelete.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); this.varType = json['varType']; this.varName = json['varName']; @@ -86,10 +90,16 @@ export class VarDelete extends VarBase { * static methods in superclasses. * @internal */ - static fromJson(json: VarDeleteJson, workspace: Workspace, event?: any): - VarDelete { - const newEvent = - super.fromJson(json, workspace, event ?? new VarDelete()) as VarDelete; + static fromJson( + json: VarDeleteJson, + workspace: Workspace, + event?: any + ): VarDelete { + const newEvent = super.fromJson( + json, + workspace, + event ?? new VarDelete() + ) as VarDelete; newEvent.varType = json['varType']; newEvent.varName = json['varName']; return newEvent; @@ -104,13 +114,15 @@ export class VarDelete extends VarBase { const workspace = this.getEventWorkspace_(); if (!this.varId) { throw new Error( - 'The var ID is undefined. Either pass a variable to ' + - 'the constructor, or call fromJson'); + 'The var ID is undefined. Either pass a variable to ' + + 'the constructor, or call fromJson' + ); } if (!this.varName) { throw new Error( - 'The var name is undefined. Either pass a variable to ' + - 'the constructor, or call fromJson'); + 'The var name is undefined. Either pass a variable to ' + + 'the constructor, or call fromJson' + ); } if (forward) { workspace.deleteVariableById(this.varId); diff --git a/core/events/events_var_rename.ts b/core/events/events_var_rename.ts index bed95cf6270..5150698529a 100644 --- a/core/events/events_var_rename.ts +++ b/core/events/events_var_rename.ts @@ -15,7 +15,6 @@ import {VarBase, VarBaseJson} from './events_var_base.js'; import * as eventUtils from './utils.js'; import type {Workspace} from '../workspace.js'; - /** * Notifies listeners that a variable model was renamed. * @@ -38,7 +37,7 @@ export class VarRename extends VarBase { super(opt_variable); if (!opt_variable) { - return; // Blank event to be populated by fromJson. + return; // Blank event to be populated by fromJson. } this.oldName = opt_variable.name; this.newName = typeof newName === 'undefined' ? '' : newName; @@ -53,13 +52,15 @@ export class VarRename extends VarBase { const json = super.toJson() as VarRenameJson; if (!this.oldName) { throw new Error( - 'The old var name is undefined. Either pass a variable to ' + - 'the constructor, or call fromJson'); + 'The old var name is undefined. Either pass a variable to ' + + 'the constructor, or call fromJson' + ); } if (!this.newName) { throw new Error( - 'The new var name is undefined. Either pass a value to ' + - 'the constructor, or call fromJson'); + 'The new var name is undefined. Either pass a value to ' + + 'the constructor, or call fromJson' + ); } json['oldName'] = this.oldName; json['newName'] = this.newName; @@ -73,8 +74,11 @@ export class VarRename extends VarBase { */ override fromJson(json: VarRenameJson) { deprecation.warn( - 'Blockly.Events.VarRename.prototype.fromJson', 'version 9', - 'version 10', 'Blockly.Events.fromJson'); + 'Blockly.Events.VarRename.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); this.oldName = json['oldName']; this.newName = json['newName']; @@ -89,10 +93,16 @@ export class VarRename extends VarBase { * static methods in superclasses. * @internal */ - static fromJson(json: VarRenameJson, workspace: Workspace, event?: any): - VarRename { - const newEvent = - super.fromJson(json, workspace, event ?? new VarRename()) as VarRename; + static fromJson( + json: VarRenameJson, + workspace: Workspace, + event?: any + ): VarRename { + const newEvent = super.fromJson( + json, + workspace, + event ?? new VarRename() + ) as VarRename; newEvent.oldName = json['oldName']; newEvent.newName = json['newName']; return newEvent; @@ -107,18 +117,21 @@ export class VarRename extends VarBase { const workspace = this.getEventWorkspace_(); if (!this.varId) { throw new Error( - 'The var ID is undefined. Either pass a variable to ' + - 'the constructor, or call fromJson'); + 'The var ID is undefined. Either pass a variable to ' + + 'the constructor, or call fromJson' + ); } if (!this.oldName) { throw new Error( - 'The old var name is undefined. Either pass a variable to ' + - 'the constructor, or call fromJson'); + 'The old var name is undefined. Either pass a variable to ' + + 'the constructor, or call fromJson' + ); } if (!this.newName) { throw new Error( - 'The new var name is undefined. Either pass a value to ' + - 'the constructor, or call fromJson'); + 'The new var name is undefined. Either pass a value to ' + + 'the constructor, or call fromJson' + ); } if (forward) { workspace.renameVariableById(this.varId, this.newName); diff --git a/core/events/events_viewport.ts b/core/events/events_viewport.ts index f0287a3997f..c8cbfb6cc3d 100644 --- a/core/events/events_viewport.ts +++ b/core/events/events_viewport.ts @@ -19,7 +19,6 @@ import {UiBase} from './events_ui_base.js'; import * as eventUtils from './utils.js'; import type {Workspace} from '../workspace.js'; - /** * Notifies listeners that the workspace surface's position or scale has * changed. @@ -59,8 +58,12 @@ export class ViewportChange extends UiBase { * event. */ constructor( - opt_top?: number, opt_left?: number, opt_scale?: number, - opt_workspaceId?: string, opt_oldScale?: number) { + opt_top?: number, + opt_left?: number, + opt_scale?: number, + opt_workspaceId?: string, + opt_oldScale?: number + ) { super(opt_workspaceId); this.viewTop = opt_top; @@ -78,23 +81,27 @@ export class ViewportChange extends UiBase { const json = super.toJson() as ViewportChangeJson; if (this.viewTop === undefined) { throw new Error( - 'The view top is undefined. Either pass a value to ' + - 'the constructor, or call fromJson'); + 'The view top is undefined. Either pass a value to ' + + 'the constructor, or call fromJson' + ); } if (this.viewLeft === undefined) { throw new Error( - 'The view left is undefined. Either pass a value to ' + - 'the constructor, or call fromJson'); + 'The view left is undefined. Either pass a value to ' + + 'the constructor, or call fromJson' + ); } if (this.scale === undefined) { throw new Error( - 'The scale is undefined. Either pass a value to ' + - 'the constructor, or call fromJson'); + 'The scale is undefined. Either pass a value to ' + + 'the constructor, or call fromJson' + ); } if (this.oldScale === undefined) { throw new Error( - 'The old scale is undefined. Either pass a value to ' + - 'the constructor, or call fromJson'); + 'The old scale is undefined. Either pass a value to ' + + 'the constructor, or call fromJson' + ); } json['viewTop'] = this.viewTop; json['viewLeft'] = this.viewLeft; @@ -110,8 +117,11 @@ export class ViewportChange extends UiBase { */ override fromJson(json: ViewportChangeJson) { deprecation.warn( - 'Blockly.Events.Viewport.prototype.fromJson', 'version 9', 'version 10', - 'Blockly.Events.fromJson'); + 'Blockly.Events.Viewport.prototype.fromJson', + 'version 9', + 'version 10', + 'Blockly.Events.fromJson' + ); super.fromJson(json); this.viewTop = json['viewTop']; this.viewLeft = json['viewLeft']; @@ -128,11 +138,16 @@ export class ViewportChange extends UiBase { * static methods in superclasses. * @internal */ - static fromJson(json: ViewportChangeJson, workspace: Workspace, event?: any): - ViewportChange { - const newEvent = - super.fromJson(json, workspace, event ?? new ViewportChange()) as - ViewportChange; + static fromJson( + json: ViewportChangeJson, + workspace: Workspace, + event?: any + ): ViewportChange { + const newEvent = super.fromJson( + json, + workspace, + event ?? new ViewportChange() + ) as ViewportChange; newEvent.viewTop = json['viewTop']; newEvent.viewLeft = json['viewLeft']; newEvent.scale = json['scale']; @@ -149,4 +164,7 @@ export interface ViewportChangeJson extends AbstractEventJson { } registry.register( - registry.Type.EVENT, eventUtils.VIEWPORT_CHANGE, ViewportChange); + registry.Type.EVENT, + eventUtils.VIEWPORT_CHANGE, + ViewportChange +); diff --git a/core/events/utils.ts b/core/events/utils.ts index 573f241e276..b55ddea9c4b 100644 --- a/core/events/utils.ts +++ b/core/events/utils.ts @@ -22,7 +22,6 @@ import type {CommentCreate} from './events_comment_create.js'; import type {CommentMove} from './events_comment_move.js'; import type {ViewportChange} from './events_viewport.js'; - /** Group ID for new events. Grouped events are indivisible. */ let group = ''; @@ -187,7 +186,7 @@ export const FINISHED_LOADING = 'finished_loading'; * Not to be confused with bumping so that disconnected connections do not * appear connected. */ -export type BumpEvent = BlockCreate|BlockMove|CommentCreate|CommentMove; +export type BumpEvent = BlockCreate | BlockMove | CommentCreate | CommentMove; /** * List of events that cause objects to be bumped back into the visible @@ -196,8 +195,12 @@ export type BumpEvent = BlockCreate|BlockMove|CommentCreate|CommentMove; * Not to be confused with bumping so that disconnected connections do not * appear connected. */ -export const BUMP_EVENTS: string[] = - [BLOCK_CREATE, BLOCK_MOVE, COMMENT_CREATE, COMMENT_MOVE]; +export const BUMP_EVENTS: string[] = [ + BLOCK_CREATE, + BLOCK_MOVE, + COMMENT_CREATE, + COMMENT_MOVE, +]; /** List of events queued for firing. */ const FIRE_QUEUE: Abstract[] = []; @@ -235,12 +238,11 @@ function fireInternal(event: Abstract) { FIRE_QUEUE.push(event); } - /** Fire all queued events. */ function fireNow() { const queue = filter(FIRE_QUEUE, true); FIRE_QUEUE.length = 0; - for (let i = 0, event; event = queue[i]; i++) { + for (let i = 0, event; (event = queue[i]); i++) { if (!event.workspaceId) { continue; } @@ -268,7 +270,7 @@ export function filter(queueIn: Abstract[], forward: boolean): Abstract[] { const mergedQueue = []; const hash = Object.create(null); // Merge duplicates. - for (let i = 0, event; event = queue[i]; i++) { + for (let i = 0, event; (event = queue[i]); i++) { if (!event.isNull()) { // Treat all UI events as the same type in hash table. const eventType = event.isUiEvent ? UI : event.type; @@ -293,8 +295,9 @@ export function filter(queueIn: Abstract[], forward: boolean): Abstract[] { if (moveEvent.reason) { if (lastEvent.reason) { // Concatenate reasons without duplicates. - const reasonSet = - new Set(moveEvent.reason.concat(lastEvent.reason)); + const reasonSet = new Set( + moveEvent.reason.concat(lastEvent.reason) + ); lastEvent.reason = Array.from(reasonSet); } else { lastEvent.reason = moveEvent.reason; @@ -302,9 +305,10 @@ export function filter(queueIn: Abstract[], forward: boolean): Abstract[] { } lastEntry.index = i; } else if ( - event.type === CHANGE && - (event as BlockChange).element === lastEvent.element && - (event as BlockChange).name === lastEvent.name) { + event.type === CHANGE && + (event as BlockChange).element === lastEvent.element && + (event as BlockChange).name === lastEvent.name + ) { const changeEvent = event as BlockChange; // Merge change events. lastEvent.newValue = changeEvent.newValue; @@ -326,7 +330,7 @@ export function filter(queueIn: Abstract[], forward: boolean): Abstract[] { } } // Filter out any events that have become null due to merging. - queue = mergedQueue.filter(function(e) { + queue = mergedQueue.filter(function (e) { return !e.isNull(); }); if (!forward) { @@ -335,11 +339,13 @@ export function filter(queueIn: Abstract[], forward: boolean): Abstract[] { } // Move mutation events to the top of the queue. // Intentionally skip first event. - for (let i = 1, event; event = queue[i]; i++) { + for (let i = 1, event; (event = queue[i]); i++) { // AnyDuringMigration because: Property 'element' does not exist on type // 'Abstract'. - if (event.type === CHANGE && - (event as AnyDuringMigration).element === 'mutation') { + if ( + event.type === CHANGE && + (event as AnyDuringMigration).element === 'mutation' + ) { queue.unshift(queue.splice(i, 1)[0]); } } @@ -351,7 +357,7 @@ export function filter(queueIn: Abstract[], forward: boolean): Abstract[] { * in the undo stack. Called by Workspace.clearUndo. */ export function clearPendingUndo() { - for (let i = 0, event; event = FIRE_QUEUE[i]; i++) { + for (let i = 0, event; (event = FIRE_QUEUE[i]); i++) { event.recordUndo = false; } } @@ -395,14 +401,14 @@ export function getGroup(): string { * @param state True to start new group, false to end group. * String to set group explicitly. */ -export function setGroup(state: boolean|string) { +export function setGroup(state: boolean | string) { TEST_ONLY.setGroupInternal(state); } /** * Private version of setGroup for stubbing in tests. */ -function setGroupInternal(state: boolean|string) { +function setGroupInternal(state: boolean | string) { if (typeof state === 'boolean') { group = state ? idGenerator.genUid() : ''; } else { @@ -420,7 +426,7 @@ function setGroupInternal(state: boolean|string) { export function getDescendantIds(block: Block): string[] { const ids = []; const descendants = block.getDescendants(false); - for (let i = 0, descendant; descendant = descendants[i]; i++) { + for (let i = 0, descendant; (descendant = descendants[i]); i++) { ids[i] = descendant.id; } return ids; @@ -435,7 +441,9 @@ export function getDescendantIds(block: Block): string[] { * @throws {Error} if an event type is not found in the registry. */ export function fromJson( - json: AnyDuringMigration, workspace: Workspace): Abstract { + json: AnyDuringMigration, + workspace: Workspace +): Abstract { const eventClass = get(json['type']); if (!eventClass) throw Error('Unknown event type.'); @@ -457,11 +465,14 @@ export function fromJson( * Returns false if no static fromJson method exists on the contructor, or if * the static fromJson method is inheritted. */ -function eventClassHasStaticFromJson(eventClass: new (...p: any[]) => Abstract): - boolean { +function eventClassHasStaticFromJson( + eventClass: new (...p: any[]) => Abstract +): boolean { const untypedEventClass = eventClass as any; - return Object.getOwnPropertyDescriptors(untypedEventClass).fromJson && - typeof untypedEventClass.fromJson === 'function'; + return ( + Object.getOwnPropertyDescriptors(untypedEventClass).fromJson && + typeof untypedEventClass.fromJson === 'function' + ); } /** @@ -470,8 +481,9 @@ function eventClassHasStaticFromJson(eventClass: new (...p: any[]) => Abstract): * @param eventType The type of the event to get. * @returns The event class with the given type. */ -export function get(eventType: string): - (new (...p1: AnyDuringMigration[]) => Abstract) { +export function get( + eventType: string +): new (...p1: AnyDuringMigration[]) => Abstract { const event = registry.getClass(registry.Type.EVENT, eventType); if (!event) { throw new Error(`Event type ${eventType} not found in registry.`); @@ -493,8 +505,9 @@ export function disableOrphans(event: Abstract) { if (!blockEvent.workspaceId) { return; } - const eventWorkspace = - common.getWorkspaceById(blockEvent.workspaceId) as WorkspaceSvg; + const eventWorkspace = common.getWorkspaceById( + blockEvent.workspaceId + ) as WorkspaceSvg; if (!blockEvent.blockId) { throw new Error('Encountered a blockEvent without a proper blockId'); } @@ -507,12 +520,13 @@ export function disableOrphans(event: Abstract) { const parent = block.getParent(); if (parent && parent.isEnabled()) { const children = block.getDescendants(false); - for (let i = 0, child; child = children[i]; i++) { + for (let i = 0, child; (child = children[i]); i++) { child.setEnabled(true); } } else if ( - (block.outputConnection || block.previousConnection) && - !eventWorkspace.isDragging()) { + (block.outputConnection || block.previousConnection) && + !eventWorkspace.isDragging() + ) { do { block.setEnabled(false); block = block.getNextBlock(); diff --git a/core/events/workspace_events.ts b/core/events/workspace_events.ts index 30fab99b74a..2d16777dd93 100644 --- a/core/events/workspace_events.ts +++ b/core/events/workspace_events.ts @@ -14,10 +14,12 @@ goog.declareModuleId('Blockly.Events.FinishedLoading'); import * as registry from '../registry.js'; import type {Workspace} from '../workspace.js'; -import {Abstract as AbstractEvent, AbstractEventJson} from './events_abstract.js'; +import { + Abstract as AbstractEvent, + AbstractEventJson, +} from './events_abstract.js'; import * as eventUtils from './utils.js'; - /** * Notifies listeners when the workspace has finished deserializing from * JSON/XML. @@ -49,8 +51,9 @@ export class FinishedLoading extends AbstractEvent { const json = super.toJson() as FinishedLoadingJson; if (!this.workspaceId) { throw new Error( - 'The workspace ID is undefined. Either pass a workspace to ' + - 'the constructor, or call fromJson'); + 'The workspace ID is undefined. Either pass a workspace to ' + + 'the constructor, or call fromJson' + ); } json['workspaceId'] = this.workspaceId; return json; @@ -72,4 +75,7 @@ export interface FinishedLoadingJson extends AbstractEventJson { } registry.register( - registry.Type.EVENT, eventUtils.FINISHED_LOADING, FinishedLoading); + registry.Type.EVENT, + eventUtils.FINISHED_LOADING, + FinishedLoading +); diff --git a/core/extensions.ts b/core/extensions.ts index 20e1ed47edc..5b6e3fd1ca8 100644 --- a/core/extensions.ts +++ b/core/extensions.ts @@ -13,7 +13,6 @@ import {FieldDropdown} from './field_dropdown.js'; import {Mutator} from './mutator.js'; import * as parsing from './utils/parsing.js'; - /** The set of all registered extensions, keyed by extension name/id. */ const allExtensions = Object.create(null); export const TEST_ONLY = {allExtensions}; @@ -54,7 +53,7 @@ export function registerMixin(name: string, mixinObj: AnyDuringMigration) { if (!mixinObj || typeof mixinObj !== 'object') { throw Error('Error: Mixin "' + name + '" must be a object'); } - register(name, function(this: Block) { + register(name, function (this: Block) { this.mixin(mixinObj); }); } @@ -73,8 +72,11 @@ export function registerMixin(name: string, mixinObj: AnyDuringMigration) { * @throws {Error} if the mutation is invalid or can't be applied to the block. */ export function registerMutator( - name: string, mixinObj: AnyDuringMigration, - opt_helperFn?: () => AnyDuringMigration, opt_blockList?: string[]) { + name: string, + mixinObj: AnyDuringMigration, + opt_helperFn?: () => AnyDuringMigration, + opt_blockList?: string[] +) { const errorPrefix = 'Error when registering mutator "' + name + '": '; checkHasMutatorProperties(errorPrefix, mixinObj); @@ -85,7 +87,7 @@ export function registerMutator( } // Sanity checks passed. - register(name, function(this: Block) { + register(name, function (this: Block) { if (hasMutatorDialog) { this.setMutator(new Mutator(opt_blockList || [], this as BlockSvg)); } @@ -108,7 +110,8 @@ export function unregister(name: string) { delete allExtensions[name]; } else { console.warn( - 'No extension mapping for name "' + name + '" found to unregister'); + 'No extension mapping for name "' + name + '" found to unregister' + ); } } @@ -151,11 +154,15 @@ export function apply(name: string, block: Block, isMutator: boolean) { const errorPrefix = 'Error after applying mutator "' + name + '": '; checkHasMutatorProperties(errorPrefix, block); } else { - if (!mutatorPropertiesMatch( - mutatorProperties as AnyDuringMigration[], block)) { + if ( + !mutatorPropertiesMatch(mutatorProperties as AnyDuringMigration[], block) + ) { throw Error( - 'Error when applying extension "' + name + '": ' + - 'mutation properties changed when applying a non-mutator extension.'); + 'Error when applying extension "' + + name + + '": ' + + 'mutation properties changed when applying a non-mutator extension.' + ); } } } @@ -173,9 +180,12 @@ function checkNoMutatorProperties(mutationName: string, block: Block) { const properties = getMutatorProperties(block); if (properties.length) { throw Error( - 'Error: tried to apply mutation "' + mutationName + + 'Error: tried to apply mutation "' + + mutationName + '" to a block that already has mutator functions.' + - ' Block id: ' + block.id); + ' Block id: ' + + block.id + ); } } @@ -191,10 +201,14 @@ function checkNoMutatorProperties(mutationName: string, block: Block) { * actually a function. */ function checkXmlHooks( - object: AnyDuringMigration, errorPrefix: string): boolean { + object: AnyDuringMigration, + errorPrefix: string +): boolean { return checkHasFunctionPair( - object.mutationToDom, object.domToMutation, - errorPrefix + ' mutationToDom/domToMutation'); + object.mutationToDom, + object.domToMutation, + errorPrefix + ' mutationToDom/domToMutation' + ); } /** * Checks if the given object has both the 'saveExtraState' and 'loadExtraState' @@ -208,10 +222,14 @@ function checkXmlHooks( * actually a function. */ function checkJsonHooks( - object: AnyDuringMigration, errorPrefix: string): boolean { + object: AnyDuringMigration, + errorPrefix: string +): boolean { return checkHasFunctionPair( - object.saveExtraState, object.loadExtraState, - errorPrefix + ' saveExtraState/loadExtraState'); + object.saveExtraState, + object.loadExtraState, + errorPrefix + ' saveExtraState/loadExtraState' + ); } /** @@ -225,9 +243,14 @@ function checkJsonHooks( * actually a function. */ function checkMutatorDialog( - object: AnyDuringMigration, errorPrefix: string): boolean { + object: AnyDuringMigration, + errorPrefix: string +): boolean { return checkHasFunctionPair( - object.compose, object.decompose, errorPrefix + ' compose/decompose'); + object.compose, + object.decompose, + errorPrefix + ' compose/decompose' + ); } /** @@ -243,8 +266,10 @@ function checkMutatorDialog( * actually a function. */ function checkHasFunctionPair( - func1: AnyDuringMigration, func2: AnyDuringMigration, - errorPrefix: string): boolean { + func1: AnyDuringMigration, + func2: AnyDuringMigration, + errorPrefix: string +): boolean { if (func1 && func2) { if (typeof func1 !== 'function' || typeof func2 !== 'function') { throw Error(errorPrefix + ' must be a function'); @@ -263,13 +288,16 @@ function checkHasFunctionPair( * @param object The object to inspect. */ function checkHasMutatorProperties( - errorPrefix: string, object: AnyDuringMigration) { + errorPrefix: string, + object: AnyDuringMigration +) { const hasXmlHooks = checkXmlHooks(object, errorPrefix); const hasJsonHooks = checkJsonHooks(object, errorPrefix); if (!hasXmlHooks && !hasJsonHooks) { throw Error( - errorPrefix + - 'Mutations must contain either XML hooks, or JSON hooks, or both'); + errorPrefix + + 'Mutations must contain either XML hooks, or JSON hooks, or both' + ); } // A block with a mutator isn't required to have a mutation dialog, but // it should still have both or neither of compose and decompose. @@ -318,7 +346,9 @@ function getMutatorProperties(block: Block): AnyDuringMigration[] { * @returns True if the property lists match. */ function mutatorPropertiesMatch( - oldProperties: AnyDuringMigration[], block: Block): boolean { + oldProperties: AnyDuringMigration[], + block: Block +): boolean { const newProperties = getMutatorProperties(block); if (newProperties.length !== oldProperties.length) { return false; @@ -343,10 +373,10 @@ export function runAfterPageLoad(fn: () => void) { throw Error('runAfterPageLoad() requires browser document.'); } if (document.readyState === 'complete') { - fn(); // Page has already loaded. Call immediately. + fn(); // Page has already loaded. Call immediately. } else { // Poll readyState. - const readyStateCheckInterval = setInterval(function() { + const readyStateCheckInterval = setInterval(function () { if (document.readyState === 'complete') { clearInterval(readyStateCheckInterval); fn(); @@ -375,7 +405,9 @@ export function runAfterPageLoad(fn: () => void) { * @returns The extension function. */ export function buildTooltipForDropdown( - dropdownName: string, lookupTable: {[key: string]: string}): Function { + dropdownName: string, + lookupTable: {[key: string]: string} +): Function { // List of block types already validated, to minimize duplicate warnings. const blockTypesChecked: AnyDuringMigration[] = []; @@ -383,8 +415,9 @@ export function buildTooltipForDropdown( // Wait for load, in case Blockly.Msg is not yet populated. // runAfterPageLoad() does not run in a Node.js environment due to lack // of document object, in which case skip the validation. - if (typeof document === 'object') { // Relies on document.readyState - runAfterPageLoad(function() { + if (typeof document === 'object') { + // Relies on document.readyState + runAfterPageLoad(function () { for (const key in lookupTable) { // Will print warnings if reference is missing. parsing.checkMessageReferences(lookupTable[key]); @@ -399,24 +432,29 @@ export function buildTooltipForDropdown( blockTypesChecked.push(this.type); } - this.setTooltip(function(this: Block) { - const value = String(this.getFieldValue(dropdownName)); - let tooltip = lookupTable[value]; - if (tooltip === null) { - if (blockTypesChecked.indexOf(this.type) === -1) { - // Warn for missing values on generated tooltips. - let warning = 'No tooltip mapping for value ' + value + ' of field ' + + this.setTooltip( + function (this: Block) { + const value = String(this.getFieldValue(dropdownName)); + let tooltip = lookupTable[value]; + if (tooltip === null) { + if (blockTypesChecked.indexOf(this.type) === -1) { + // Warn for missing values on generated tooltips. + let warning = + 'No tooltip mapping for value ' + + value + + ' of field ' + dropdownName; - if (this.type !== null) { - warning += ' of block type ' + this.type; + if (this.type !== null) { + warning += ' of block type ' + this.type; + } + console.warn(warning + '.'); } - console.warn(warning + '.'); + } else { + tooltip = parsing.replaceMessageReferences(tooltip); } - } else { - tooltip = parsing.replaceMessageReferences(tooltip); - } - return tooltip; - }.bind(this)); + return tooltip; + }.bind(this) + ); } return extensionFn; } @@ -430,17 +468,25 @@ export function buildTooltipForDropdown( * @param lookupTable The string lookup table */ function checkDropdownOptionsInTable( - block: Block, dropdownName: string, lookupTable: {[key: string]: string}) { + block: Block, + dropdownName: string, + lookupTable: {[key: string]: string} +) { // Validate all dropdown options have values. const dropdown = block.getField(dropdownName); if (dropdown instanceof FieldDropdown && !dropdown.isOptionListDynamic()) { const options = dropdown.getOptions(); for (let i = 0; i < options.length; i++) { - const optionKey = options[i][1]; // label, then value + const optionKey = options[i][1]; // label, then value if (lookupTable[optionKey] === null) { console.warn( - 'No tooltip mapping for value ' + optionKey + ' of field ' + - dropdownName + ' of block type ' + block.type); + 'No tooltip mapping for value ' + + optionKey + + ' of field ' + + dropdownName + + ' of block type ' + + block.type + ); } } } @@ -457,13 +503,16 @@ function checkDropdownOptionsInTable( * @returns The extension function. */ export function buildTooltipWithFieldText( - msgTemplate: string, fieldName: string): Function { + msgTemplate: string, + fieldName: string +): Function { // Check the tooltip string messages for invalid references. // Wait for load, in case Blockly.Msg is not yet populated. // runAfterPageLoad() does not run in a Node.js environment due to lack // of document object, in which case skip the validation. - if (typeof document === 'object') { // Relies on document.readyState - runAfterPageLoad(function() { + if (typeof document === 'object') { + // Relies on document.readyState + runAfterPageLoad(function () { // Will print warnings if reference is missing. parsing.checkMessageReferences(msgTemplate); }); @@ -471,11 +520,14 @@ export function buildTooltipWithFieldText( /** The actual extension. */ function extensionFn(this: Block) { - this.setTooltip(function(this: Block) { - const field = this.getField(fieldName); - return parsing.replaceMessageReferences(msgTemplate) + this.setTooltip( + function (this: Block) { + const field = this.getField(fieldName); + return parsing + .replaceMessageReferences(msgTemplate) .replace('%1', field ? field.getText() : ''); - }.bind(this)); + }.bind(this) + ); } return extensionFn; } @@ -488,10 +540,14 @@ export function buildTooltipWithFieldText( */ function extensionParentTooltip(this: Block) { const tooltipWhenNotConnected = this.tooltip; - this.setTooltip(function(this: Block) { - const parent = this.getParent(); - return parent && parent.getInputsInline() && parent.tooltip || - tooltipWhenNotConnected; - }.bind(this)); + this.setTooltip( + function (this: Block) { + const parent = this.getParent(); + return ( + (parent && parent.getInputsInline() && parent.tooltip) || + tooltipWhenNotConnected + ); + }.bind(this) + ); } register('parent_tooltip_when_inline', extensionParentTooltip); diff --git a/core/field.ts b/core/field.ts index 391ab5ac84a..83bebab9850 100644 --- a/core/field.ts +++ b/core/field.ts @@ -59,17 +59,21 @@ import {ISerializable} from './interfaces/i_serializable.js'; * * - `undefined` to set `newValue` as is. */ -export type FieldValidator = (newValue: T) => T|null|undefined; +export type FieldValidator = (newValue: T) => T | null | undefined; /** * Abstract class for an editable field. * * @typeParam T - The value stored on the field. */ -export abstract class Field implements IASTNodeLocationSvg, - IASTNodeLocationWithBlock, - IKeyboardAccessible, - IRegistrable, ISerializable { +export abstract class Field + implements + IASTNodeLocationSvg, + IASTNodeLocationWithBlock, + IKeyboardAccessible, + IRegistrable, + ISerializable +{ /** * To overwrite the default value which is set in **Field**, directly update * the prototype. @@ -79,7 +83,7 @@ export abstract class Field implements IASTNodeLocationSvg, * FieldImage.prototype.DEFAULT_VALUE = null; * ``` */ - DEFAULT_VALUE: T|null = null; + DEFAULT_VALUE: T | null = null; /** Non-breaking space. */ static readonly NBSP = '\u00A0'; @@ -96,47 +100,47 @@ export abstract class Field implements IASTNodeLocationSvg, * Static labels are usually unnamed. */ name?: string = undefined; - protected value_: T|null; + protected value_: T | null; /** Validation function called when user edits an editable field. */ - protected validator_: FieldValidator|null = null; + protected validator_: FieldValidator | null = null; /** * Used to cache the field's tooltip value if setTooltip is called when the * field is not yet initialized. Is *not* guaranteed to be accurate. */ - private tooltip_: Tooltip.TipInfo|null = null; + private tooltip_: Tooltip.TipInfo | null = null; protected size_: Size; /** * Holds the cursors svg element when the cursor is attached to the field. * This is null if there is no cursor on the field. */ - private cursorSvg_: SVGElement|null = null; + private cursorSvg_: SVGElement | null = null; /** * Holds the markers svg element when the marker is attached to the field. * This is null if there is no marker on the field. */ - private markerSvg_: SVGElement|null = null; + private markerSvg_: SVGElement | null = null; /** The rendered field's SVG group element. */ - protected fieldGroup_: SVGGElement|null = null; + protected fieldGroup_: SVGGElement | null = null; /** The rendered field's SVG border element. */ - protected borderRect_: SVGRectElement|null = null; + protected borderRect_: SVGRectElement | null = null; /** The rendered field's SVG text element. */ - protected textElement_: SVGTextElement|null = null; + protected textElement_: SVGTextElement | null = null; /** The rendered field's text content element. */ - protected textContent_: Text|null = null; + protected textContent_: Text | null = null; /** Mouse down event listener data. */ - private mouseDownWrapper_: browserEvents.Data|null = null; + private mouseDownWrapper_: browserEvents.Data | null = null; /** Constants associated with the source block's renderer. */ - protected constants_: ConstantProvider|null = null; + protected constants_: ConstantProvider | null = null; /** * Has this field been disposed of? @@ -149,7 +153,7 @@ export abstract class Field implements IASTNodeLocationSvg, maxDisplayLength = 50; /** Block this field is attached to. Starts as null, then set in init. */ - protected sourceBlock_: Block|null = null; + protected sourceBlock_: Block | null = null; /** Does this block need to be re-rendered? */ protected isDirty_ = true; @@ -163,21 +167,21 @@ export abstract class Field implements IASTNodeLocationSvg, protected enabled_ = true; /** The element the click handler is bound to. */ - protected clickTarget_: Element|null = null; + protected clickTarget_: Element | null = null; /** * The prefix field. * * @internal */ - prefixField: string|null = null; + prefixField: string | null = null; /** * The suffix field. * * @internal */ - suffixField: string|null = null; + suffixField: string | null = null; /** * Editable fields usually show some sort of UI indicating they are @@ -208,15 +212,18 @@ export abstract class Field implements IASTNodeLocationSvg, * this parameter supports. */ constructor( - value: T|typeof Field.SKIP_SETUP, validator?: FieldValidator|null, - config?: FieldConfig) { + value: T | typeof Field.SKIP_SETUP, + validator?: FieldValidator | null, + config?: FieldConfig + ) { /** * A generic value possessed by the field. * Should generally be non-null, only null when the field is created. */ - this.value_ = 'DEFAULT_VALUE' in new.target.prototype ? - new.target.prototype.DEFAULT_VALUE : - this.DEFAULT_VALUE; + this.value_ = + 'DEFAULT_VALUE' in new.target.prototype + ? new.target.prototype.DEFAULT_VALUE + : this.DEFAULT_VALUE; /** The size of the area rendered by the field. */ this.size_ = new Size(0, 0); @@ -263,13 +270,16 @@ export abstract class Field implements IASTNodeLocationSvg, * * @returns The renderer constant provider. */ - getConstants(): ConstantProvider|null { - if (!this.constants_ && this.sourceBlock_ && - !this.sourceBlock_.isDeadOrDying() && - this.sourceBlock_.workspace.rendered) { + getConstants(): ConstantProvider | null { + if ( + !this.constants_ && + this.sourceBlock_ && + !this.sourceBlock_.isDeadOrDying() && + this.sourceBlock_.workspace.rendered + ) { this.constants_ = (this.sourceBlock_.workspace as WorkspaceSvg) - .getRenderer() - .getConstants(); + .getRenderer() + .getConstants(); } return this.constants_; } @@ -280,7 +290,7 @@ export abstract class Field implements IASTNodeLocationSvg, * @returns The block containing this field. * @throws An error if the source block is not defined. */ - getSourceBlock(): Block|null { + getSourceBlock(): Block | null { return this.sourceBlock_; } @@ -334,16 +344,18 @@ export abstract class Field implements IASTNodeLocationSvg, */ protected createBorderRect_() { this.borderRect_ = dom.createSvgElement( - Svg.RECT, { - 'rx': this.getConstants()!.FIELD_BORDER_RECT_RADIUS, - 'ry': this.getConstants()!.FIELD_BORDER_RECT_RADIUS, - 'x': 0, - 'y': 0, - 'height': this.size_.height, - 'width': this.size_.width, - 'class': 'blocklyFieldRect', - }, - this.fieldGroup_); + Svg.RECT, + { + 'rx': this.getConstants()!.FIELD_BORDER_RECT_RADIUS, + 'ry': this.getConstants()!.FIELD_BORDER_RECT_RADIUS, + 'x': 0, + 'y': 0, + 'height': this.size_.height, + 'width': this.size_.width, + 'class': 'blocklyFieldRect', + }, + this.fieldGroup_ + ); } /** @@ -353,10 +365,12 @@ export abstract class Field implements IASTNodeLocationSvg, */ protected createTextElement_() { this.textElement_ = dom.createSvgElement( - Svg.TEXT, { - 'class': 'blocklyText', - }, - this.fieldGroup_); + Svg.TEXT, + { + 'class': 'blocklyText', + }, + this.fieldGroup_ + ); if (this.getConstants()!.FIELD_TEXT_BASELINE_CENTER) { this.textElement_.setAttribute('dominant-baseline', 'central'); } @@ -373,7 +387,11 @@ export abstract class Field implements IASTNodeLocationSvg, if (!clickTarget) throw new Error('A click target has not been set.'); Tooltip.bindMouseEvents(clickTarget); this.mouseDownWrapper_ = browserEvents.conditionalBind( - clickTarget, 'pointerdown', this, this.onMouseDown_); + clickTarget, + 'pointerdown', + this, + this.onMouseDown_ + ); } /** @@ -443,14 +461,18 @@ export abstract class Field implements IASTNodeLocationSvg, * Used to see if `this` has overridden any relevant hooks. * @returns The stringified version of the XML state, or null. */ - protected saveLegacyState(callingClass: FieldProto): string|null { - if (callingClass.prototype.saveState === this.saveState && - callingClass.prototype.toXml !== this.toXml) { + protected saveLegacyState(callingClass: FieldProto): string | null { + if ( + callingClass.prototype.saveState === this.saveState && + callingClass.prototype.toXml !== this.toXml + ) { const elem = utilsXml.createElement('field'); elem.setAttribute('name', this.name || ''); const text = utilsXml.domToText(this.toXml(elem)); return text.replace( - ' xmlns="https://developers.google.com/blockly/xml"', ''); + ' xmlns="https://developers.google.com/blockly/xml"', + '' + ); } // Either they called this on purpose from their saveState, or they have // no implementations of either hook. Just do our thing. @@ -466,10 +488,14 @@ export abstract class Field implements IASTNodeLocationSvg, * @param state The state to apply to the field. * @returns Whether the state was applied or not. */ - loadLegacyState(callingClass: FieldProto, state: AnyDuringMigration): - boolean { - if (callingClass.prototype.loadState === this.loadState && - callingClass.prototype.fromXml !== this.fromXml) { + loadLegacyState( + callingClass: FieldProto, + state: AnyDuringMigration + ): boolean { + if ( + callingClass.prototype.loadState === this.loadState && + callingClass.prototype.fromXml !== this.fromXml + ) { this.fromXml(utilsXml.textToDom(state as string)); return true; } @@ -539,9 +565,12 @@ export abstract class Field implements IASTNodeLocationSvg, * @returns Whether this field is clickable. */ isClickable(): boolean { - return this.enabled_ && !!this.sourceBlock_ && - this.sourceBlock_.isEditable() && - this.showEditor_ !== Field.prototype.showEditor_; + return ( + this.enabled_ && + !!this.sourceBlock_ && + this.sourceBlock_.isEditable() && + this.showEditor_ !== Field.prototype.showEditor_ + ); } /** @@ -553,8 +582,12 @@ export abstract class Field implements IASTNodeLocationSvg, * editable block. */ isCurrentlyEditable(): boolean { - return this.enabled_ && this.EDITABLE && !!this.sourceBlock_ && - this.sourceBlock_.isEditable(); + return ( + this.enabled_ && + this.EDITABLE && + !!this.sourceBlock_ && + this.sourceBlock_.isEditable() + ); } /** @@ -570,9 +603,10 @@ export abstract class Field implements IASTNodeLocationSvg, isSerializable = true; } else if (this.EDITABLE) { console.warn( - 'Detected an editable field that was not serializable.' + + 'Detected an editable field that was not serializable.' + ' Please define SERIALIZABLE property as true on all editable custom' + - ' fields. Proceeding with serialization.'); + ' fields. Proceeding with serialization.' + ); isSerializable = true; } } @@ -630,7 +664,7 @@ export abstract class Field implements IASTNodeLocationSvg, * * @returns Validation function, or null. */ - getValidator(): FieldValidator|null { + getValidator(): FieldValidator | null { return this.validator_; } @@ -640,7 +674,7 @@ export abstract class Field implements IASTNodeLocationSvg, * * @returns The group element. */ - getSvgRoot(): SVGGElement|null { + getSvgRoot(): SVGGElement | null { return this.fieldGroup_; } @@ -764,17 +798,23 @@ export abstract class Field implements IASTNodeLocationSvg, */ protected updateSize_(margin?: number) { const constants = this.getConstants(); - const xOffset = margin !== undefined ? margin : - this.borderRect_ ? this.getConstants()!.FIELD_BORDER_RECT_X_PADDING : - 0; + const xOffset = + margin !== undefined + ? margin + : this.borderRect_ + ? this.getConstants()!.FIELD_BORDER_RECT_X_PADDING + : 0; let totalWidth = xOffset * 2; let totalHeight = constants!.FIELD_TEXT_HEIGHT; let contentWidth = 0; if (this.textElement_) { contentWidth = dom.getFastTextWidth( - this.textElement_, constants!.FIELD_TEXT_FONTSIZE, - constants!.FIELD_TEXT_FONTWEIGHT, constants!.FIELD_TEXT_FONTFAMILY); + this.textElement_, + constants!.FIELD_TEXT_FONTSIZE, + constants!.FIELD_TEXT_FONTWEIGHT, + constants!.FIELD_TEXT_FONTFAMILY + ); totalWidth += contentWidth; } if (this.borderRect_) { @@ -803,18 +843,23 @@ export abstract class Field implements IASTNodeLocationSvg, const halfHeight = this.size_.height / 2; this.textElement_.setAttribute( - 'x', - String( - this.getSourceBlock()?.RTL ? - this.size_.width - contentWidth - xOffset : - xOffset)); + 'x', + String( + this.getSourceBlock()?.RTL + ? this.size_.width - contentWidth - xOffset + : xOffset + ) + ); this.textElement_.setAttribute( - 'y', - String( - constants!.FIELD_TEXT_BASELINE_CENTER ? - halfHeight : - halfHeight - constants!.FIELD_TEXT_HEIGHT / 2 + - constants!.FIELD_TEXT_BASELINE)); + 'y', + String( + constants!.FIELD_TEXT_BASELINE_CENTER + ? halfHeight + : halfHeight - + constants!.FIELD_TEXT_HEIGHT / 2 + + constants!.FIELD_TEXT_BASELINE + ) + ); } /** Position a field's border rect after a size change. */ @@ -825,9 +870,13 @@ export abstract class Field implements IASTNodeLocationSvg, this.borderRect_.setAttribute('width', String(this.size_.width)); this.borderRect_.setAttribute('height', String(this.size_.height)); this.borderRect_.setAttribute( - 'rx', String(this.getConstants()!.FIELD_BORDER_RECT_RADIUS)); + 'rx', + String(this.getConstants()!.FIELD_BORDER_RECT_RADIUS) + ); this.borderRect_.setAttribute( - 'ry', String(this.getConstants()!.FIELD_BORDER_RECT_RADIUS)); + 'ry', + String(this.getConstants()!.FIELD_BORDER_RECT_RADIUS) + ); } /** @@ -852,8 +901,9 @@ export abstract class Field implements IASTNodeLocationSvg, // Don't issue a warning if the field is actually zero width. if (this.size_.width !== 0) { console.warn( - 'Deprecated use of setting size_.width to 0 to rerender a' + - ' field. Set field.isDirty_ to true instead.'); + 'Deprecated use of setting size_.width to 0 to rerender a' + + ' field. Set field.isDirty_ to true instead.' + ); } } return this.size_; @@ -953,7 +1003,7 @@ export abstract class Field implements IASTNodeLocationSvg, * * @returns Current text or null. */ - protected getText_(): string|null { + protected getText_(): string | null { return null; } @@ -1031,8 +1081,15 @@ export abstract class Field implements IASTNodeLocationSvg, this.doValueUpdate_(localValue); if (source && eventUtils.isEnabled()) { - eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CHANGE))( - source, 'field', this.name || null, oldValue, localValue)); + eventUtils.fire( + new (eventUtils.get(eventUtils.BLOCK_CHANGE))( + source, + 'field', + this.name || null, + oldValue, + localValue + ) + ); } if (this.isDirty_) { this.forceRerender(); @@ -1048,7 +1105,9 @@ export abstract class Field implements IASTNodeLocationSvg, * @returns New value, or an Error object. */ private processValidation_( - newValue: AnyDuringMigration, validatedValue: T|null|undefined): T|Error { + newValue: AnyDuringMigration, + validatedValue: T | null | undefined + ): T | Error { if (validatedValue === null) { this.doValueInvalid_(newValue); if (this.isDirty_) { @@ -1056,7 +1115,7 @@ export abstract class Field implements IASTNodeLocationSvg, } return Error(); } - return validatedValue === undefined ? newValue as T : validatedValue; + return validatedValue === undefined ? (newValue as T) : validatedValue; } /** @@ -1064,7 +1123,7 @@ export abstract class Field implements IASTNodeLocationSvg, * * @returns Current value. */ - getValue(): T|null { + getValue(): T | null { return this.value_; } @@ -1088,10 +1147,11 @@ export abstract class Field implements IASTNodeLocationSvg, * * - `undefined` to set `newValue` as is. */ - protected doClassValidation_(newValue: T): T|null|undefined; - protected doClassValidation_(newValue?: AnyDuringMigration): T|null; - protected doClassValidation_(newValue?: T|AnyDuringMigration): T|null - |undefined { + protected doClassValidation_(newValue: T): T | null | undefined; + protected doClassValidation_(newValue?: AnyDuringMigration): T | null; + protected doClassValidation_( + newValue?: T | AnyDuringMigration + ): T | null | undefined { if (newValue === null || newValue === undefined) { return null; } @@ -1143,8 +1203,9 @@ export abstract class Field implements IASTNodeLocationSvg, * display the tooltip of the parent block. To not display a tooltip pass * the empty string. */ - setTooltip(newTip: Tooltip.TipInfo|null) { - if (!newTip && newTip !== '') { // If null or undefined. + setTooltip(newTip: Tooltip.TipInfo | null) { + if (!newTip && newTip !== '') { + // If null or undefined. newTip = this.sourceBlock_; } const clickTarget = this.getClickTarget_(); @@ -1177,7 +1238,7 @@ export abstract class Field implements IASTNodeLocationSvg, * * @returns Element to bind click handler to. */ - protected getClickTarget_(): Element|null { + protected getClickTarget_(): Element | null { return this.clickTarget_ || this.getSvgRoot(); } @@ -1351,7 +1412,8 @@ export class UnattachedFieldError extends Error { /** @internal */ constructor() { super( - 'The field has not yet been attached to its input. ' + - 'Call appendField to attach it.'); + 'The field has not yet been attached to its input. ' + + 'Call appendField to attach it.' + ); } } diff --git a/core/field_angle.ts b/core/field_angle.ts index e5fbbf35a15..2069881ed3f 100644 --- a/core/field_angle.ts +++ b/core/field_angle.ts @@ -18,7 +18,11 @@ import * as Css from './css.js'; import * as dropDownDiv from './dropdowndiv.js'; import {Field, UnattachedFieldError} from './field.js'; import * as fieldRegistry from './field_registry.js'; -import {FieldInput, FieldInputConfig, FieldInputValidator} from './field_input.js'; +import { + FieldInput, + FieldInputConfig, + FieldInputValidator, +} from './field_input.js'; import * as dom from './utils/dom.js'; import * as math from './utils/math.js'; import {Svg} from './utils/svg.js'; @@ -92,13 +96,13 @@ export class FieldAngle extends FieldInput { private boundEvents: browserEvents.Data[] = []; /** Dynamic red line pointing at the value's angle. */ - private line: SVGLineElement|null = null; + private line: SVGLineElement | null = null; /** Dynamic pink area extending from 0 to the value's angle. */ - private gauge: SVGPathElement|null = null; + private gauge: SVGPathElement | null = null; /** The degree symbol for this field. */ - protected symbol_: SVGTSpanElement|null = null; + protected symbol_: SVGTSpanElement | null = null; /** * @param value The initial value of the field. Should cast to a number. @@ -114,8 +118,10 @@ export class FieldAngle extends FieldInput { * for a list of properties this parameter supports. */ constructor( - value?: string|number|typeof Field.SKIP_SETUP, - validator?: FieldAngleValidator, config?: FieldAngleConfig) { + value?: string | number | typeof Field.SKIP_SETUP, + validator?: FieldAngleValidator, + config?: FieldAngleConfig + ) { super(Field.SKIP_SETUP); if (value === Field.SKIP_SETUP) return; @@ -192,8 +198,9 @@ export class FieldAngle extends FieldInput { if (this.sourceBlock_ instanceof BlockSvg) { dropDownDiv.setColour( - this.sourceBlock_.style.colourPrimary, - this.sourceBlock_.style.colourTertiary); + this.sourceBlock_.style.colourPrimary, + this.sourceBlock_.style.colourTertiary + ); } dropDownDiv.showPositionedByField(this, this.dropdownDispose.bind(this)); @@ -217,50 +224,80 @@ export class FieldAngle extends FieldInput { 'style': 'touch-action: none', }); const circle = dom.createSvgElement( - Svg.CIRCLE, { - 'cx': FieldAngle.HALF, - 'cy': FieldAngle.HALF, - 'r': FieldAngle.RADIUS, - 'class': 'blocklyAngleCircle', - }, - svg); - this.gauge = - dom.createSvgElement(Svg.PATH, {'class': 'blocklyAngleGauge'}, svg); + Svg.CIRCLE, + { + 'cx': FieldAngle.HALF, + 'cy': FieldAngle.HALF, + 'r': FieldAngle.RADIUS, + 'class': 'blocklyAngleCircle', + }, + svg + ); + this.gauge = dom.createSvgElement( + Svg.PATH, + {'class': 'blocklyAngleGauge'}, + svg + ); this.line = dom.createSvgElement( - Svg.LINE, { - 'x1': FieldAngle.HALF, - 'y1': FieldAngle.HALF, - 'class': 'blocklyAngleLine', - }, - svg); + Svg.LINE, + { + 'x1': FieldAngle.HALF, + 'y1': FieldAngle.HALF, + 'class': 'blocklyAngleLine', + }, + svg + ); // Draw markers around the edge. for (let angle = 0; angle < 360; angle += 15) { dom.createSvgElement( - Svg.LINE, { - 'x1': FieldAngle.HALF + FieldAngle.RADIUS, - 'y1': FieldAngle.HALF, - 'x2': FieldAngle.HALF + FieldAngle.RADIUS - - (angle % 45 === 0 ? 10 : 5), - 'y2': FieldAngle.HALF, - 'class': 'blocklyAngleMarks', - 'transform': 'rotate(' + angle + ',' + FieldAngle.HALF + ',' + - FieldAngle.HALF + ')', - }, - svg); + Svg.LINE, + { + 'x1': FieldAngle.HALF + FieldAngle.RADIUS, + 'y1': FieldAngle.HALF, + 'x2': + FieldAngle.HALF + FieldAngle.RADIUS - (angle % 45 === 0 ? 10 : 5), + 'y2': FieldAngle.HALF, + 'class': 'blocklyAngleMarks', + 'transform': + 'rotate(' + + angle + + ',' + + FieldAngle.HALF + + ',' + + FieldAngle.HALF + + ')', + }, + svg + ); } // The angle picker is different from other fields in that it updates on // mousemove even if it's not in the middle of a drag. In future we may // change this behaviour. this.boundEvents.push( - browserEvents.conditionalBind(svg, 'click', this, this.hide)); + browserEvents.conditionalBind(svg, 'click', this, this.hide) + ); // On touch devices, the picker's value is only updated with a drag. Add // a click handler on the drag surface to update the value if the surface // is clicked. - this.boundEvents.push(browserEvents.conditionalBind( - circle, 'pointerdown', this, this.onMouseMove_, true)); - this.boundEvents.push(browserEvents.conditionalBind( - circle, 'pointermove', this, this.onMouseMove_, true)); + this.boundEvents.push( + browserEvents.conditionalBind( + circle, + 'pointerdown', + this, + this.onMouseMove_, + true + ) + ); + this.boundEvents.push( + browserEvents.conditionalBind( + circle, + 'pointermove', + this, + this.onMouseMove_, + true + ) + ); return svg; } @@ -353,14 +390,31 @@ export class FieldAngle extends FieldInput { x2 += Math.cos(angleRadians) * FieldAngle.RADIUS; y2 -= Math.sin(angleRadians) * FieldAngle.RADIUS; // Don't ask how the flag calculations work. They just do. - let largeFlag = - Math.abs(Math.floor((angleRadians - angle1) / Math.PI) % 2); + let largeFlag = Math.abs( + Math.floor((angleRadians - angle1) / Math.PI) % 2 + ); if (clockwiseFlag) { largeFlag = 1 - largeFlag; } path.push( - ' l ', x1, ',', y1, ' A ', FieldAngle.RADIUS, ',', FieldAngle.RADIUS, - ' 0 ', largeFlag, ' ', clockwiseFlag, ' ', x2, ',', y2, ' z'); + ' l ', + x1, + ',', + y1, + ' A ', + FieldAngle.RADIUS, + ',', + FieldAngle.RADIUS, + ' 0 ', + largeFlag, + ' ', + clockwiseFlag, + ' ', + x2, + ',', + y2, + ' z' + ); } this.gauge.setAttribute('d', path.join('')); this.line.setAttribute('x2', `${x2}`); @@ -412,7 +466,7 @@ export class FieldAngle extends FieldInput { * @param newValue The input value. * @returns A valid angle, or null if invalid. */ - protected override doClassValidation_(newValue?: any): number|null { + protected override doClassValidation_(newValue?: any): number | null { const value = Number(newValue); if (isNaN(value) || !isFinite(value)) { return null; @@ -456,7 +510,6 @@ fieldRegistry.register('field_angle', FieldAngle); FieldAngle.prototype.DEFAULT_VALUE = 0; - /** * CSS for angle field. */ diff --git a/core/field_checkbox.ts b/core/field_checkbox.ts index a5624f7cc23..bd8533c2dcb 100644 --- a/core/field_checkbox.ts +++ b/core/field_checkbox.ts @@ -19,8 +19,8 @@ import * as dom from './utils/dom.js'; import {Field, FieldConfig, FieldValidator} from './field.js'; import * as fieldRegistry from './field_registry.js'; -type BoolString = 'TRUE'|'FALSE'; -type CheckboxBool = BoolString|boolean; +type BoolString = 'TRUE' | 'FALSE'; +type CheckboxBool = BoolString | boolean; /** * Class for a checkbox field. @@ -45,7 +45,7 @@ export class FieldCheckbox extends Field { * NOTE: The default value is set in `Field`, so maintain that value instead * of overwriting it here or in the constructor. */ - override value_: boolean|null = this.value_; + override value_: boolean | null = this.value_; /** * @param value The initial value of the field. Should either be 'TRUE', @@ -62,8 +62,10 @@ export class FieldCheckbox extends Field { * for a list of properties this parameter supports. */ constructor( - value?: CheckboxBool|typeof Field.SKIP_SETUP, - validator?: FieldCheckboxValidator, config?: FieldCheckboxConfig) { + value?: CheckboxBool | typeof Field.SKIP_SETUP, + validator?: FieldCheckboxValidator, + config?: FieldCheckboxConfig + ) { super(Field.SKIP_SETUP); /** @@ -136,7 +138,7 @@ export class FieldCheckbox extends Field { * @param character The character to use for the check mark, or null to use * the default. */ - setCheckCharacter(character: string|null) { + setCheckCharacter(character: string | null) { this.checkChar = character || FieldCheckbox.CHECK_CHAR; this.forceRerender(); } @@ -152,8 +154,9 @@ export class FieldCheckbox extends Field { * @param newValue The input value. * @returns A valid value ('TRUE' or 'FALSE), or null if invalid. */ - protected override doClassValidation_(newValue?: AnyDuringMigration): - BoolString|null { + protected override doClassValidation_( + newValue?: AnyDuringMigration + ): BoolString | null { if (newValue === true || newValue === 'TRUE') { return 'TRUE'; } @@ -191,7 +194,7 @@ export class FieldCheckbox extends Field { * * @returns The boolean value of this field. */ - getValueBoolean(): boolean|null { + getValueBoolean(): boolean | null { return this.value_; } @@ -213,7 +216,7 @@ export class FieldCheckbox extends Field { * @param value The value to convert. * @returns The converted value. */ - private convertValueToBool_(value: CheckboxBool|null): boolean { + private convertValueToBool_(value: CheckboxBool | null): boolean { if (typeof value === 'string') return value === 'TRUE'; return !!value; } diff --git a/core/field_colour.ts b/core/field_colour.ts index 468b2f3dfff..5ff12c595ff 100644 --- a/core/field_colour.ts +++ b/core/field_colour.ts @@ -75,10 +75,10 @@ export class FieldColour extends Field { static COLUMNS = 7; /** The field's colour picker element. */ - private picker: HTMLElement|null = null; + private picker: HTMLElement | null = null; /** Index of the currently highlighted element. */ - private highlightedIndex: number|null = null; + private highlightedIndex: number | null = null; /** * Array holding info needed to unbind events. @@ -104,13 +104,13 @@ export class FieldColour extends Field { protected override isDirty_ = false; /** Array of colours used by this field. If null, use the global list. */ - private colours: string[]|null = null; + private colours: string[] | null = null; /** * Array of colour tooltips used by this field. If null, use the global * list. */ - private titles: string[]|null = null; + private titles: string[] | null = null; /** * Number of colour columns used by this field. If 0, use the global @@ -133,8 +133,10 @@ export class FieldColour extends Field { * for a list of properties this parameter supports. */ constructor( - value?: string|typeof Field.SKIP_SETUP, validator?: FieldColourValidator, - config?: FieldColourConfig) { + value?: string | typeof Field.SKIP_SETUP, + validator?: FieldColourValidator, + config?: FieldColourConfig + ) { super(Field.SKIP_SETUP); if (value === Field.SKIP_SETUP) return; @@ -166,8 +168,9 @@ export class FieldColour extends Field { */ override initView() { this.size_ = new Size( - this.getConstants()!.FIELD_COLOUR_DEFAULT_WIDTH, - this.getConstants()!.FIELD_COLOUR_DEFAULT_HEIGHT); + this.getConstants()!.FIELD_COLOUR_DEFAULT_WIDTH, + this.getConstants()!.FIELD_COLOUR_DEFAULT_HEIGHT + ); if (!this.getConstants()!.FIELD_COLOUR_FULL_BLOCK) { this.createBorderRect_(); this.getBorderRect().style['fillOpacity'] = '1'; @@ -186,7 +189,9 @@ export class FieldColour extends Field { } } else if (this.sourceBlock_ instanceof BlockSvg) { this.sourceBlock_.pathObject.svgPath.setAttribute( - 'fill', this.getValue() as string); + 'fill', + this.getValue() as string + ); this.sourceBlock_.pathObject.svgPath.setAttribute('stroke', '#fff'); } } @@ -197,7 +202,7 @@ export class FieldColour extends Field { * @param newValue The input value. * @returns A valid colour, or null if invalid. */ - protected override doClassValidation_(newValue?: any): string|null { + protected override doClassValidation_(newValue?: any): string | null { if (typeof newValue !== 'string') { return null; } @@ -215,8 +220,10 @@ export class FieldColour extends Field { if (this.borderRect_) { this.borderRect_.style.fill = newValue; } else if ( - this.sourceBlock_ && this.sourceBlock_.rendered && - this.sourceBlock_ instanceof BlockSvg) { + this.sourceBlock_ && + this.sourceBlock_.rendered && + this.sourceBlock_ instanceof BlockSvg + ) { this.sourceBlock_.pathObject.svgPath.setAttribute('fill', newValue); this.sourceBlock_.pathObject.svgPath.setAttribute('stroke', '#fff'); } @@ -298,7 +305,7 @@ export class FieldColour extends Field { */ private onKeyDown(e: KeyboardEvent) { let handled = true; - let highlighted: HTMLElement|null; + let highlighted: HTMLElement | null; switch (e.key) { case 'ArrowUp': this.moveHighlightBy(0, -1); @@ -424,7 +431,7 @@ export class FieldColour extends Field { * * @returns Highlighted item (null if none). */ - private getHighlighted(): HTMLElement|null { + private getHighlighted(): HTMLElement | null { if (!this.highlightedIndex) { return null; } @@ -477,7 +484,10 @@ export class FieldColour extends Field { aria.setRole(table, aria.Role.GRID); aria.setState(table, aria.State.EXPANDED, true); aria.setState( - table, aria.State.ROWCOUNT, Math.floor(colours.length / columns)); + table, + aria.State.ROWCOUNT, + Math.floor(colours.length / columns) + ); aria.setState(table, aria.State.COLCOUNT, columns); let row: Element; for (let i = 0; i < colours.length; i++) { @@ -486,7 +496,7 @@ export class FieldColour extends Field { aria.setRole(row, aria.Role.ROW); table.appendChild(row); } - const cell = (document.createElement('td')); + const cell = document.createElement('td'); row!.appendChild(cell); // This becomes the value, if clicked. cell.setAttribute('data-colour', colours[i]); @@ -504,16 +514,51 @@ export class FieldColour extends Field { } // Configure event handler on the table to listen for any event in a cell. - this.boundEvents.push(browserEvents.conditionalBind( - table, 'pointerdown', this, this.onClick, true)); - this.boundEvents.push(browserEvents.conditionalBind( - table, 'pointermove', this, this.onMouseMove, true)); - this.boundEvents.push(browserEvents.conditionalBind( - table, 'pointerenter', this, this.onMouseEnter, true)); - this.boundEvents.push(browserEvents.conditionalBind( - table, 'pointerleave', this, this.onMouseLeave, true)); - this.boundEvents.push(browserEvents.conditionalBind( - table, 'keydown', this, this.onKeyDown, false)); + this.boundEvents.push( + browserEvents.conditionalBind( + table, + 'pointerdown', + this, + this.onClick, + true + ) + ); + this.boundEvents.push( + browserEvents.conditionalBind( + table, + 'pointermove', + this, + this.onMouseMove, + true + ) + ); + this.boundEvents.push( + browserEvents.conditionalBind( + table, + 'pointerenter', + this, + this.onMouseEnter, + true + ) + ); + this.boundEvents.push( + browserEvents.conditionalBind( + table, + 'pointerleave', + this, + this.onMouseLeave, + true + ) + ); + this.boundEvents.push( + browserEvents.conditionalBind( + table, + 'keydown', + this, + this.onKeyDown, + false + ) + ); this.picker = table; } @@ -548,7 +593,6 @@ FieldColour.prototype.DEFAULT_VALUE = FieldColour.COLOURS[0]; fieldRegistry.register('field_colour', FieldColour); - /** * CSS for colour picker. */ diff --git a/core/field_dropdown.ts b/core/field_dropdown.ts index 6836e5754c1..251ebd81af3 100644 --- a/core/field_dropdown.ts +++ b/core/field_dropdown.ts @@ -16,7 +16,12 @@ goog.declareModuleId('Blockly.FieldDropdown'); import type {BlockSvg} from './block_svg.js'; import * as dropDownDiv from './dropdowndiv.js'; -import {Field, FieldConfig, FieldValidator, UnattachedFieldError} from './field.js'; +import { + Field, + FieldConfig, + FieldValidator, + UnattachedFieldError, +} from './field.js'; import * as fieldRegistry from './field_registry.js'; import {Menu} from './menu.js'; import {MenuItem} from './menuitem.js'; @@ -44,21 +49,21 @@ export class FieldDropdown extends Field { static ARROW_CHAR = '▾'; /** A reference to the currently selected menu item. */ - private selectedMenuItem: MenuItem|null = null; + private selectedMenuItem: MenuItem | null = null; /** The dropdown menu. */ - protected menu_: Menu|null = null; + protected menu_: Menu | null = null; /** * SVG image element if currently selected option is an image, or null. */ - private imageElement: SVGImageElement|null = null; + private imageElement: SVGImageElement | null = null; /** Tspan based arrow element. */ - private arrow: SVGTSpanElement|null = null; + private arrow: SVGTSpanElement | null = null; /** SVG based arrow element. */ - private svgArrow: SVGElement|null = null; + private svgArrow: SVGElement | null = null; /** * Serializable fields are saved by the serializer, non-serializable fields @@ -72,24 +77,24 @@ export class FieldDropdown extends Field { protected menuGenerator_?: MenuGenerator; /** A cache of the most recently generated options. */ - private generatedOptions: MenuOption[]|null = null; + private generatedOptions: MenuOption[] | null = null; /** * The prefix field label, of common words set after options are trimmed. * * @internal */ - override prefixField: string|null = null; + override prefixField: string | null = null; /** * The suffix field label, of common words set after options are trimmed. * * @internal */ - override suffixField: string|null = null; + override suffixField: string | null = null; // TODO(b/109816955): remove '!', see go/strict-prop-init-fix. private selectedOption!: MenuOption; - override clickTarget_: SVGElement|null = null; + override clickTarget_: SVGElement | null = null; /** * @param menuGenerator A non-empty array of options for a dropdown list, or a @@ -108,15 +113,15 @@ export class FieldDropdown extends Field { * @throws {TypeError} If `menuGenerator` options are incorrectly structured. */ constructor( - menuGenerator: MenuGenerator, - validator?: FieldDropdownValidator, - config?: FieldDropdownConfig, + menuGenerator: MenuGenerator, + validator?: FieldDropdownValidator, + config?: FieldDropdownConfig ); constructor(menuGenerator: typeof Field.SKIP_SETUP); constructor( - menuGenerator: MenuGenerator|typeof Field.SKIP_SETUP, - validator?: FieldDropdownValidator, - config?: FieldDropdownConfig, + menuGenerator: MenuGenerator | typeof Field.SKIP_SETUP, + validator?: FieldDropdownValidator, + config?: FieldDropdownConfig ) { super(Field.SKIP_SETUP); @@ -210,17 +215,23 @@ export class FieldDropdown extends Field { * @returns True if the dropdown field should add a border rect. */ protected shouldAddBorderRect_(): boolean { - return !this.getConstants()!.FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW || - this.getConstants()!.FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW && - !this.getSourceBlock()?.isShadow(); + return ( + !this.getConstants()!.FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW || + (this.getConstants()!.FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW && + !this.getSourceBlock()?.isShadow()) + ); } /** Create a tspan based arrow. */ protected createTextArrow_() { this.arrow = dom.createSvgElement(Svg.TSPAN, {}, this.textElement_); - this.arrow!.appendChild(document.createTextNode( - this.getSourceBlock()?.RTL ? FieldDropdown.ARROW_CHAR + ' ' : - ' ' + FieldDropdown.ARROW_CHAR)); + this.arrow!.appendChild( + document.createTextNode( + this.getSourceBlock()?.RTL + ? FieldDropdown.ARROW_CHAR + ' ' + : ' ' + FieldDropdown.ARROW_CHAR + ) + ); if (this.getSourceBlock()?.RTL) { this.getTextElement().insertBefore(this.arrow, this.textContent_); } else { @@ -231,14 +242,18 @@ export class FieldDropdown extends Field { /** Create an SVG based arrow. */ protected createSVGArrow_() { this.svgArrow = dom.createSvgElement( - Svg.IMAGE, { - 'height': this.getConstants()!.FIELD_DROPDOWN_SVG_ARROW_SIZE + 'px', - 'width': this.getConstants()!.FIELD_DROPDOWN_SVG_ARROW_SIZE + 'px', - }, - this.fieldGroup_); + Svg.IMAGE, + { + 'height': this.getConstants()!.FIELD_DROPDOWN_SVG_ARROW_SIZE + 'px', + 'width': this.getConstants()!.FIELD_DROPDOWN_SVG_ARROW_SIZE + 'px', + }, + this.fieldGroup_ + ); this.svgArrow!.setAttributeNS( - dom.XLINK_NS, 'xlink:href', - this.getConstants()!.FIELD_DROPDOWN_SVG_ARROW_DATAURI); + dom.XLINK_NS, + 'xlink:href', + this.getConstants()!.FIELD_DROPDOWN_SVG_ARROW_DATAURI + ); } /** @@ -266,11 +281,12 @@ export class FieldDropdown extends Field { dom.addClass(menuElement, 'blocklyDropdownMenu'); if (this.getConstants()!.FIELD_DROPDOWN_COLOURED_DIV) { - const primaryColour = - block.isShadow() ? block.getParent()!.getColour() : block.getColour(); - const borderColour = block.isShadow() ? - (block.getParent() as BlockSvg).style.colourTertiary : - (this.sourceBlock_ as BlockSvg).style.colourTertiary; + const primaryColour = block.isShadow() + ? block.getParent()!.getColour() + : block.getColour(); + const borderColour = block.isShadow() + ? (block.getParent() as BlockSvg).style.colourTertiary + : (this.sourceBlock_ as BlockSvg).style.colourTertiary; dropDownDiv.setColour(primaryColour, borderColour); } @@ -284,8 +300,10 @@ export class FieldDropdown extends Field { if (this.selectedMenuItem) { this.menu_!.setHighlighted(this.selectedMenuItem); style.scrollIntoContainerView( - this.selectedMenuItem.getElement()!, dropDownDiv.getContentDiv(), - true); + this.selectedMenuItem.getElement()!, + dropDownDiv.getContentDiv(), + true + ); } this.applyColour(); @@ -397,16 +415,21 @@ export class FieldDropdown extends Field { * @param newValue The input value. * @returns A valid language-neutral option, or null if invalid. */ - protected override doClassValidation_(newValue?: string): string|null { + protected override doClassValidation_(newValue?: string): string | null { const options = this.getOptions(true); const isValueValid = options.some((option) => option[1] === newValue); if (!isValueValid) { if (this.sourceBlock_) { console.warn( - 'Cannot set the dropdown\'s value to an unavailable option.' + - ' Block type: ' + this.sourceBlock_.type + - ', Field name: ' + this.name + ', Value: ' + newValue); + "Cannot set the dropdown's value to an unavailable option." + + ' Block type: ' + + this.sourceBlock_.type + + ', Field name: ' + + this.name + + ', Value: ' + + newValue + ); } return null; } @@ -422,7 +445,7 @@ export class FieldDropdown extends Field { protected override doValueUpdate_(newValue: string) { super.doValueUpdate_(newValue); const options = this.getOptions(true); - for (let i = 0, option; option = options[i]; i++) { + for (let i = 0, option; (option = options[i]); i++) { if (option[1] === this.value_) { this.selectedOption = option; } @@ -481,7 +504,10 @@ export class FieldDropdown extends Field { } this.imageElement!.style.display = ''; this.imageElement!.setAttributeNS( - dom.XLINK_NS, 'xlink:href', imageJson.src); + dom.XLINK_NS, + 'xlink:href', + imageJson.src + ); this.imageElement!.setAttribute('height', String(imageJson.height)); this.imageElement!.setAttribute('width', String(imageJson.width)); @@ -491,21 +517,25 @@ export class FieldDropdown extends Field { // Height and width include the border rect. const hasBorder = !!this.borderRect_; const height = Math.max( - hasBorder ? this.getConstants()!.FIELD_DROPDOWN_BORDER_RECT_HEIGHT : 0, - imageHeight + IMAGE_Y_PADDING); - const xPadding = - hasBorder ? this.getConstants()!.FIELD_BORDER_RECT_X_PADDING : 0; + hasBorder ? this.getConstants()!.FIELD_DROPDOWN_BORDER_RECT_HEIGHT : 0, + imageHeight + IMAGE_Y_PADDING + ); + const xPadding = hasBorder + ? this.getConstants()!.FIELD_BORDER_RECT_X_PADDING + : 0; let arrowWidth = 0; if (this.svgArrow) { arrowWidth = this.positionSVGArrow( - imageWidth + xPadding, - height / 2 - this.getConstants()!.FIELD_DROPDOWN_SVG_ARROW_SIZE / 2); + imageWidth + xPadding, + height / 2 - this.getConstants()!.FIELD_DROPDOWN_SVG_ARROW_SIZE / 2 + ); } else { arrowWidth = dom.getFastTextWidth( - this.arrow as SVGTSpanElement, - this.getConstants()!.FIELD_TEXT_FONTSIZE, - this.getConstants()!.FIELD_TEXT_FONTWEIGHT, - this.getConstants()!.FIELD_TEXT_FONTFAMILY); + this.arrow as SVGTSpanElement, + this.getConstants()!.FIELD_TEXT_FONTSIZE, + this.getConstants()!.FIELD_TEXT_FONTWEIGHT, + this.getConstants()!.FIELD_TEXT_FONTFAMILY + ); } this.size_.width = imageWidth + arrowWidth + xPadding * 2; this.size_.height = height; @@ -535,19 +565,24 @@ export class FieldDropdown extends Field { // Height and width include the border rect. const hasBorder = !!this.borderRect_; const height = Math.max( - hasBorder ? this.getConstants()!.FIELD_DROPDOWN_BORDER_RECT_HEIGHT : 0, - this.getConstants()!.FIELD_TEXT_HEIGHT); + hasBorder ? this.getConstants()!.FIELD_DROPDOWN_BORDER_RECT_HEIGHT : 0, + this.getConstants()!.FIELD_TEXT_HEIGHT + ); const textWidth = dom.getFastTextWidth( - this.getTextElement(), this.getConstants()!.FIELD_TEXT_FONTSIZE, - this.getConstants()!.FIELD_TEXT_FONTWEIGHT, - this.getConstants()!.FIELD_TEXT_FONTFAMILY); - const xPadding = - hasBorder ? this.getConstants()!.FIELD_BORDER_RECT_X_PADDING : 0; + this.getTextElement(), + this.getConstants()!.FIELD_TEXT_FONTSIZE, + this.getConstants()!.FIELD_TEXT_FONTWEIGHT, + this.getConstants()!.FIELD_TEXT_FONTFAMILY + ); + const xPadding = hasBorder + ? this.getConstants()!.FIELD_BORDER_RECT_X_PADDING + : 0; let arrowWidth = 0; if (this.svgArrow) { arrowWidth = this.positionSVGArrow( - textWidth + xPadding, - height / 2 - this.getConstants()!.FIELD_DROPDOWN_SVG_ARROW_SIZE / 2); + textWidth + xPadding, + height / 2 - this.getConstants()!.FIELD_DROPDOWN_SVG_ARROW_SIZE / 2 + ); } this.size_.width = textWidth + arrowWidth + xPadding * 2; this.size_.height = height; @@ -571,13 +606,16 @@ export class FieldDropdown extends Field { throw new UnattachedFieldError(); } const hasBorder = !!this.borderRect_; - const xPadding = - hasBorder ? this.getConstants()!.FIELD_BORDER_RECT_X_PADDING : 0; + const xPadding = hasBorder + ? this.getConstants()!.FIELD_BORDER_RECT_X_PADDING + : 0; const textPadding = this.getConstants()!.FIELD_DROPDOWN_SVG_ARROW_PADDING; const svgArrowSize = this.getConstants()!.FIELD_DROPDOWN_SVG_ARROW_SIZE; const arrowX = block.RTL ? xPadding : x + textPadding; this.svgArrow.setAttribute( - 'transform', 'translate(' + arrowX + ',' + y + ')'); + 'transform', + 'translate(' + arrowX + ',' + y + ')' + ); return svgArrowSize + textPadding; } @@ -588,7 +626,7 @@ export class FieldDropdown extends Field { * * @returns Selected option text. */ - protected override getText_(): string|null { + protected override getText_(): string | null { if (!this.selectedOption) { return null; } @@ -610,9 +648,10 @@ export class FieldDropdown extends Field { static fromJson(options: FieldDropdownFromJsonConfig): FieldDropdown { if (!options.options) { throw new Error( - 'options are required for the dropdown field. The ' + + 'options are required for the dropdown field. The ' + 'options property must be assigned an array of ' + - '[humanReadableValue, languageNeutralValue] tuples.'); + '[humanReadableValue, languageNeutralValue] tuples.' + ); } // `this` might be a subclass of FieldDropdown if that class doesn't // override the static fromJson method. @@ -647,7 +686,7 @@ export type MenuGeneratorFunction = (this: FieldDropdown) => MenuOption[]; * Either an array of menu options or a function that generates an array of * menu options for FieldDropdown or its descendants. */ -export type MenuGenerator = MenuOption[]|MenuGeneratorFunction; +export type MenuGenerator = MenuOption[] | MenuGeneratorFunction; /** * Config options for the dropdown field. @@ -691,8 +730,11 @@ const IMAGE_Y_PADDING: number = IMAGE_Y_OFFSET * 2; * Factor out common words in statically defined options. * Create prefix and/or suffix labels. */ -function trimOptions(options: MenuOption[]): - {options: MenuOption[]; prefix?: string; suffix?: string;} { +function trimOptions(options: MenuOption[]): { + options: MenuOption[]; + prefix?: string; + suffix?: string; +} { let hasImages = false; const trimmedOptions = options.map(([label, value]): MenuOption => { if (typeof label === 'string') { @@ -702,9 +744,10 @@ function trimOptions(options: MenuOption[]): hasImages = true; // Copy the image properties so they're not influenced by the original. // NOTE: No need to deep copy since image properties are only 1 level deep. - const imageLabel = label.alt !== null ? - {...label, alt: parsing.replaceMessageReferences(label.alt)} : - {...label}; + const imageLabel = + label.alt !== null + ? {...label, alt: parsing.replaceMessageReferences(label.alt)} + : {...label}; return [imageLabel, value]; }); @@ -717,16 +760,20 @@ function trimOptions(options: MenuOption[]): const prefixLength = utilsString.commonWordPrefix(stringLabels, shortest); const suffixLength = utilsString.commonWordSuffix(stringLabels, shortest); - if ((!prefixLength && !suffixLength) || - (shortest <= prefixLength + suffixLength)) { + if ( + (!prefixLength && !suffixLength) || + shortest <= prefixLength + suffixLength + ) { // One or more strings will entirely vanish if we proceed. Abort. return {options: stringOptions}; } - const prefix = - prefixLength ? stringLabels[0].substring(0, prefixLength - 1) : undefined; - const suffix = - suffixLength ? stringLabels[0].substr(1 - suffixLength) : undefined; + const prefix = prefixLength + ? stringLabels[0].substring(0, prefixLength - 1) + : undefined; + const suffix = suffixLength + ? stringLabels[0].substr(1 - suffixLength) + : undefined; return { options: applyTrim(stringOptions, prefixLength, suffixLength), prefix, @@ -745,12 +792,13 @@ function trimOptions(options: MenuOption[]): * @returns A new array with all of the option text trimmed. */ function applyTrim( - options: [string, string][], prefixLength: number, - suffixLength: number): MenuOption[] { - return options.map( - ([text, value]) => - [text.substring(prefixLength, text.length - suffixLength), - value, + options: [string, string][], + prefixLength: number, + suffixLength: number +): MenuOption[] { + return options.map(([text, value]) => [ + text.substring(prefixLength, text.length - suffixLength), + value, ]); } @@ -773,23 +821,38 @@ function validateOptions(options: MenuOption[]) { if (!Array.isArray(tuple)) { foundError = true; console.error( - 'Invalid option[' + i + ']: Each FieldDropdown option must be an ' + - 'array. Found: ', - tuple); + 'Invalid option[' + + i + + ']: Each FieldDropdown option must be an ' + + 'array. Found: ', + tuple + ); } else if (typeof tuple[1] !== 'string') { foundError = true; console.error( - 'Invalid option[' + i + ']: Each FieldDropdown option id must be ' + - 'a string. Found ' + tuple[1] + ' in: ', - tuple); + 'Invalid option[' + + i + + ']: Each FieldDropdown option id must be ' + + 'a string. Found ' + + tuple[1] + + ' in: ', + tuple + ); } else if ( - tuple[0] && typeof tuple[0] !== 'string' && - typeof tuple[0].src !== 'string') { + tuple[0] && + typeof tuple[0] !== 'string' && + typeof tuple[0].src !== 'string' + ) { foundError = true; console.error( - 'Invalid option[' + i + ']: Each FieldDropdown option must have a ' + - 'string label or image description. Found' + tuple[0] + ' in: ', - tuple); + 'Invalid option[' + + i + + ']: Each FieldDropdown option must have a ' + + 'string label or image description. Found' + + tuple[0] + + ' in: ', + tuple + ); } } if (foundError) { diff --git a/core/field_image.ts b/core/field_image.ts index 833229fb7b1..b8f04f2d4a7 100644 --- a/core/field_image.ts +++ b/core/field_image.ts @@ -32,10 +32,10 @@ export class FieldImage extends Field { private readonly imageHeight: number; /** The function to be called when this field is clicked. */ - private clickHandler: ((p1: FieldImage) => void)|null = null; + private clickHandler: ((p1: FieldImage) => void) | null = null; /** The rendered field's image element. */ - private imageElement: SVGImageElement|null = null; + private imageElement: SVGImageElement | null = null; /** * Editable fields usually show some sort of UI indicating they are @@ -73,22 +73,27 @@ export class FieldImage extends Field { * for a list of properties this parameter supports. */ constructor( - src: string|typeof Field.SKIP_SETUP, width: string|number, - height: string|number, alt?: string, onClick?: (p1: FieldImage) => void, - flipRtl?: boolean, config?: FieldImageConfig) { + src: string | typeof Field.SKIP_SETUP, + width: string | number, + height: string | number, + alt?: string, + onClick?: (p1: FieldImage) => void, + flipRtl?: boolean, + config?: FieldImageConfig + ) { super(Field.SKIP_SETUP); const imageHeight = Number(parsing.replaceMessageReferences(height)); const imageWidth = Number(parsing.replaceMessageReferences(width)); if (isNaN(imageHeight) || isNaN(imageWidth)) { throw Error( - 'Height and width values of an image field must cast to' + - ' numbers.'); + 'Height and width values of an image field must cast to' + ' numbers.' + ); } if (imageHeight <= 0 || imageWidth <= 0) { throw Error( - 'Height and width values of an image field must be greater' + - ' than 0.'); + 'Height and width values of an image field must be greater' + ' than 0.' + ); } /** The size of the area rendered by the field. */ @@ -134,14 +139,19 @@ export class FieldImage extends Field { */ override initView() { this.imageElement = dom.createSvgElement( - Svg.IMAGE, { - 'height': this.imageHeight + 'px', - 'width': this.size_.width + 'px', - 'alt': this.altText, - }, - this.fieldGroup_); + Svg.IMAGE, + { + 'height': this.imageHeight + 'px', + 'width': this.size_.width + 'px', + 'alt': this.altText, + }, + this.fieldGroup_ + ); this.imageElement.setAttributeNS( - dom.XLINK_NS, 'xlink:href', this.value_ as string); + dom.XLINK_NS, + 'xlink:href', + this.value_ as string + ); if (this.clickHandler) { this.imageElement.style.cursor = 'pointer'; @@ -157,7 +167,7 @@ export class FieldImage extends Field { * @param newValue The input value. * @returns A string, or null if invalid. */ - protected override doClassValidation_(newValue?: any): string|null { + protected override doClassValidation_(newValue?: any): string | null { if (typeof newValue !== 'string') { return null; } @@ -191,7 +201,7 @@ export class FieldImage extends Field { * * @param alt New alt text. */ - setAlt(alt: string|null) { + setAlt(alt: string | null) { if (alt === this.altText) { return; } @@ -217,7 +227,7 @@ export class FieldImage extends Field { * @param func The function that is called when the image is clicked, or null * to remove. */ - setOnClickHandler(func: ((p1: FieldImage) => void)|null) { + setOnClickHandler(func: ((p1: FieldImage) => void) | null) { this.clickHandler = func; } @@ -228,7 +238,7 @@ export class FieldImage extends Field { * * @returns The image alt text. */ - protected override getText_(): string|null { + protected override getText_(): string | null { return this.altText; } @@ -245,14 +255,21 @@ export class FieldImage extends Field { static fromJson(options: FieldImageFromJsonConfig): FieldImage { if (!options.src || !options.width || !options.height) { throw new Error( - 'src, width, and height values for an image field are' + - 'required. The width and height must be non-zero.'); + 'src, width, and height values for an image field are' + + 'required. The width and height must be non-zero.' + ); } // `this` might be a subclass of FieldImage if that class doesn't override // the static fromJson method. return new this( - options.src, options.width, options.height, undefined, undefined, - undefined, options); + options.src, + options.width, + options.height, + undefined, + undefined, + undefined, + options + ); } } diff --git a/core/field_input.ts b/core/field_input.ts index 3a0faf0632a..7ea767dc700 100644 --- a/core/field_input.ts +++ b/core/field_input.ts @@ -22,7 +22,12 @@ import * as dialog from './dialog.js'; import * as dom from './utils/dom.js'; import * as dropDownDiv from './dropdowndiv.js'; import * as eventUtils from './events/utils.js'; -import {Field, FieldConfig, FieldValidator, UnattachedFieldError} from './field.js'; +import { + Field, + FieldConfig, + FieldValidator, + UnattachedFieldError, +} from './field.js'; import {Msg} from './msg.js'; import * as aria from './utils/aria.js'; import {Coordinate} from './utils/coordinate.js'; @@ -36,7 +41,7 @@ import * as renderManagement from './render_management.js'; * * @internal */ -type InputTypes = string|number; +type InputTypes = string | number; /** * Abstract class for an editable input field. @@ -44,7 +49,9 @@ type InputTypes = string|number; * @typeParam T - The value stored on the field. * @internal */ -export abstract class FieldInput extends Field { +export abstract class FieldInput extends Field< + string | T +> { /** * Pixel size of input border radius. * Should match blocklyText's border-radius in CSS. @@ -55,7 +62,7 @@ export abstract class FieldInput extends Field { protected spellcheck_ = true; /** The HTML input element. */ - protected htmlInput_: HTMLInputElement|null = null; + protected htmlInput_: HTMLInputElement | null = null; /** True if the field's value is currently being edited via the UI. */ protected isBeingEdited_ = false; @@ -66,19 +73,19 @@ export abstract class FieldInput extends Field { protected isTextValid_ = false; /** Key down event data. */ - private onKeyDownWrapper_: browserEvents.Data|null = null; + private onKeyDownWrapper_: browserEvents.Data | null = null; /** Key input event data. */ - private onKeyInputWrapper_: browserEvents.Data|null = null; + private onKeyInputWrapper_: browserEvents.Data | null = null; /** * Whether the field should consider the whole parent block to be its click * target. */ - fullBlockClickTarget_: boolean|null = false; + fullBlockClickTarget_: boolean | null = false; /** The workspace that this field belongs to. */ - protected workspace_: WorkspaceSvg|null = null; + protected workspace_: WorkspaceSvg | null = null; /** * Serializable fields are saved by the serializer, non-serializable fields @@ -104,8 +111,10 @@ export abstract class FieldInput extends Field { * for a list of properties this parameter supports. */ constructor( - value?: string|typeof Field.SKIP_SETUP, - validator?: FieldInputValidator|null, config?: FieldInputConfig) { + value?: string | typeof Field.SKIP_SETUP, + validator?: FieldInputValidator | null, + config?: FieldInputConfig + ) { super(Field.SKIP_SETUP); if (value === Field.SKIP_SETUP) return; @@ -137,7 +146,7 @@ export abstract class FieldInput extends Field { let nFields = 0; let nConnections = 0; // Count the number of fields, excluding text fields - for (let i = 0, input; input = block.inputList[i]; i++) { + for (let i = 0, input; (input = block.inputList[i]); i++) { for (let j = 0; input.fieldRow[j]; j++) { nFields++; } @@ -148,7 +157,7 @@ export abstract class FieldInput extends Field { // The special case is when this is the only non-label field on the block // and it has an output but no inputs. this.fullBlockClickTarget_ = - nFields <= 1 && block.outputConnection && !nConnections; + nFields <= 1 && block.outputConnection && !nConnections; } else { this.fullBlockClickTarget_ = false; } @@ -178,9 +187,15 @@ export abstract class FieldInput extends Field { // Revert value when the text becomes invalid. this.value_ = this.htmlInput_!.getAttribute('data-untyped-default-value'); if (this.sourceBlock_ && eventUtils.isEnabled()) { - eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CHANGE))( - this.sourceBlock_, 'field', this.name || null, oldValue, - this.value_)); + eventUtils.fire( + new (eventUtils.get(eventUtils.BLOCK_CHANGE))( + this.sourceBlock_, + 'field', + this.name || null, + oldValue, + this.value_ + ) + ); } } } @@ -193,7 +208,7 @@ export abstract class FieldInput extends Field { * @param newValue The value to be saved. The default validator guarantees * that this is a string. */ - protected override doValueUpdate_(newValue: string|T) { + protected override doValueUpdate_(newValue: string | T) { this.isDirty_ = true; this.isTextValid_ = true; this.value_ = newValue; @@ -211,7 +226,9 @@ export abstract class FieldInput extends Field { this.borderRect_.setAttribute('stroke', source.style.colourTertiary); } else { source.pathObject.svgPath.setAttribute( - 'fill', this.getConstants()!.FIELD_BORDER_RECT_COLOUR); + 'fill', + this.getConstants()!.FIELD_BORDER_RECT_COLOUR + ); } } @@ -250,7 +267,9 @@ export abstract class FieldInput extends Field { // AnyDuringMigration because: Argument of type 'boolean' is not // assignable to parameter of type 'string'. this.htmlInput_.setAttribute( - 'spellcheck', this.spellcheck_ as AnyDuringMigration); + 'spellcheck', + this.spellcheck_ as AnyDuringMigration + ); } } @@ -267,8 +286,11 @@ export abstract class FieldInput extends Field { */ protected override showEditor_(_e?: Event, quietInput = false) { this.workspace_ = (this.sourceBlock_ as BlockSvg).workspace; - if (!quietInput && this.workspace_.options.modalInputs && - (userAgent.MOBILE || userAgent.ANDROID || userAgent.IPAD)) { + if ( + !quietInput && + this.workspace_.options.modalInputs && + (userAgent.MOBILE || userAgent.ANDROID || userAgent.IPAD) + ) { this.showPromptEditor_(); } else { this.showInlineEditor_(quietInput); @@ -282,12 +304,15 @@ export abstract class FieldInput extends Field { */ private showPromptEditor_() { dialog.prompt( - Msg['CHANGE_VALUE_TITLE'], this.getText(), (text: string|null) => { - // Text is null if user pressed cancel button. - if (text !== null) { - this.setValue(this.getValueFromEditorText_(text)); - } - }); + Msg['CHANGE_VALUE_TITLE'], + this.getText(), + (text: string | null) => { + // Text is null if user pressed cancel button. + if (text !== null) { + this.setValue(this.getValueFromEditorText_(text)); + } + } + ); } /** @@ -329,12 +354,14 @@ export abstract class FieldInput extends Field { if (!clickTarget) throw new Error('A click target has not been set.'); dom.addClass(clickTarget, 'editing'); - const htmlInput = (document.createElement('input')); + const htmlInput = document.createElement('input'); htmlInput.className = 'blocklyHtmlInput'; // AnyDuringMigration because: Argument of type 'boolean' is not assignable // to parameter of type 'string'. htmlInput.setAttribute( - 'spellcheck', this.spellcheck_ as AnyDuringMigration); + 'spellcheck', + this.spellcheck_ as AnyDuringMigration + ); const scale = this.workspace_!.getScale(); const fontSize = this.getConstants()!.FIELD_TEXT_FONTSIZE * scale + 'pt'; div!.style.fontSize = fontSize; @@ -347,15 +374,15 @@ export abstract class FieldInput extends Field { // Override border radius. borderRadius = (bBox.bottom - bBox.top) / 2 + 'px'; // Pull stroke colour from the existing shadow block - const strokeColour = block.getParent() ? - (block.getParent() as BlockSvg).style.colourTertiary : - (this.sourceBlock_ as BlockSvg).style.colourTertiary; + const strokeColour = block.getParent() + ? (block.getParent() as BlockSvg).style.colourTertiary + : (this.sourceBlock_ as BlockSvg).style.colourTertiary; htmlInput.style.border = 1 * scale + 'px solid ' + strokeColour; div!.style.borderRadius = borderRadius; div!.style.transition = 'box-shadow 0.25s ease 0s'; if (this.getConstants()!.FIELD_TEXTINPUT_BOX_SHADOW) { div!.style.boxShadow = - 'rgba(255, 255, 255, 0.3) 0 0 0 ' + (4 * scale) + 'px'; + 'rgba(255, 255, 255, 0.3) 0 0 0 ' + 4 * scale + 'px'; } } htmlInput.style.borderRadius = borderRadius; @@ -417,10 +444,18 @@ export abstract class FieldInput extends Field { protected bindInputEvents_(htmlInput: HTMLElement) { // Trap Enter without IME and Esc to hide. this.onKeyDownWrapper_ = browserEvents.conditionalBind( - htmlInput, 'keydown', this, this.onHtmlInputKeyDown_); + htmlInput, + 'keydown', + this, + this.onHtmlInputKeyDown_ + ); // Resize after every input change. this.onKeyInputWrapper_ = browserEvents.conditionalBind( - htmlInput, 'input', this, this.onHtmlInputChange_); + htmlInput, + 'input', + this, + this.onHtmlInputChange_ + ); } /** Unbind handlers for user input and workspace size changes. */ @@ -446,7 +481,8 @@ export abstract class FieldInput extends Field { dropDownDiv.hideWithoutAnimation(); } else if (e.key === 'Escape') { this.setValue( - this.htmlInput_!.getAttribute('data-untyped-default-value')); + this.htmlInput_!.getAttribute('data-untyped-default-value') + ); WidgetDiv.hide(); dropDownDiv.hideWithoutAnimation(); } else if (e.key === 'Tab') { @@ -525,8 +561,10 @@ export abstract class FieldInput extends Field { if (!(block instanceof BlockSvg)) return false; bumpObjects.bumpIntoBounds( - this.workspace_!, - this.workspace_!.getMetricsManager().getViewMetrics(true), block); + this.workspace_!, + this.workspace_!.getMetricsManager().getViewMetrics(true), + block + ); this.resizeEditor_(); @@ -550,7 +588,7 @@ export abstract class FieldInput extends Field { * * @returns The HTML value if we're editing, otherwise null. */ - protected override getText_(): string|null { + protected override getText_(): string | null { if (this.isBeingEdited_ && this.htmlInput_) { // We are currently editing, return the HTML input value instead. return this.htmlInput_.value; @@ -611,5 +649,6 @@ export interface FieldInputConfig extends FieldConfig { * - `undefined` to set `newValue` as is. * @internal */ -export type FieldInputValidator = - FieldValidator; +export type FieldInputValidator = FieldValidator< + string | T +>; diff --git a/core/field_label.ts b/core/field_label.ts index fb72be8a693..06426a4c2e9 100644 --- a/core/field_label.ts +++ b/core/field_label.ts @@ -23,7 +23,7 @@ import * as parsing from './utils/parsing.js'; */ export class FieldLabel extends Field { /** The HTML class name to use for this field. */ - private class: string|null = null; + private class: string | null = null; /** * Editable fields usually show some sort of UI indicating they are @@ -47,8 +47,10 @@ export class FieldLabel extends Field { * for a list of properties this parameter supports. */ constructor( - value?: string|typeof Field.SKIP_SETUP, textClass?: string, - config?: FieldLabelConfig) { + value?: string | typeof Field.SKIP_SETUP, + textClass?: string, + config?: FieldLabelConfig + ) { super(Field.SKIP_SETUP); if (value === Field.SKIP_SETUP) return; @@ -83,8 +85,9 @@ export class FieldLabel extends Field { * @param newValue The input value. * @returns A valid string, or null if invalid. */ - protected override doClassValidation_(newValue?: AnyDuringMigration): string - |null { + protected override doClassValidation_( + newValue?: AnyDuringMigration + ): string | null { if (newValue === null || newValue === undefined) { return null; } @@ -96,7 +99,7 @@ export class FieldLabel extends Field { * * @param cssClass The new CSS class name, or null to remove. */ - setClass(cssClass: string|null) { + setClass(cssClass: string | null) { if (this.textElement_) { if (this.class) { dom.removeClass(this.textElement_, this.class); @@ -139,7 +142,6 @@ export interface FieldLabelConfig extends FieldConfig { } // clang-format on - /** * fromJson config options for the label field. */ diff --git a/core/field_label_serializable.ts b/core/field_label_serializable.ts index 55ef8a1fbc0..d493aa598ea 100644 --- a/core/field_label_serializable.ts +++ b/core/field_label_serializable.ts @@ -14,7 +14,11 @@ import * as goog from '../closure/goog/goog.js'; goog.declareModuleId('Blockly.FieldLabelSerializable'); -import {FieldLabel, FieldLabelConfig, FieldLabelFromJsonConfig} from './field_label.js'; +import { + FieldLabel, + FieldLabelConfig, + FieldLabelFromJsonConfig, +} from './field_label.js'; import * as fieldRegistry from './field_registry.js'; import * as parsing from './utils/parsing.js'; @@ -57,8 +61,9 @@ export class FieldLabelSerializable extends FieldLabel { * @nocollapse * @internal */ - static override fromJson(options: FieldLabelFromJsonConfig): - FieldLabelSerializable { + static override fromJson( + options: FieldLabelFromJsonConfig + ): FieldLabelSerializable { const text = parsing.replaceMessageReferences(options.text); // `this` might be a subclass of FieldLabelSerializable if that class // doesn't override the static fromJson method. diff --git a/core/field_multilineinput.ts b/core/field_multilineinput.ts index 1472c8996ee..631d7e31e3e 100644 --- a/core/field_multilineinput.ts +++ b/core/field_multilineinput.ts @@ -15,7 +15,11 @@ goog.declareModuleId('Blockly.FieldMultilineInput'); import * as Css from './css.js'; import {Field, UnattachedFieldError} from './field.js'; import * as fieldRegistry from './field_registry.js'; -import {FieldTextInput, FieldTextInputConfig, FieldTextInputValidator} from './field_textinput.js'; +import { + FieldTextInput, + FieldTextInputConfig, + FieldTextInputValidator, +} from './field_textinput.js'; import * as aria from './utils/aria.js'; import * as dom from './utils/dom.js'; import * as parsing from './utils/parsing.js'; @@ -31,7 +35,7 @@ export class FieldMultilineInput extends FieldTextInput { * The SVG group element that will contain a text element for each text row * when initialized. */ - textGroup: SVGGElement|null = null; + textGroup: SVGGElement | null = null; /** * Defines the maximum number of lines of field. @@ -58,9 +62,10 @@ export class FieldMultilineInput extends FieldTextInput { * for a list of properties this parameter supports. */ constructor( - value?: string|typeof Field.SKIP_SETUP, - validator?: FieldMultilineInputValidator, - config?: FieldMultilineInputConfig) { + value?: string | typeof Field.SKIP_SETUP, + validator?: FieldMultilineInputValidator, + config?: FieldMultilineInputConfig + ) { super(Field.SKIP_SETUP); if (value === Field.SKIP_SETUP) return; @@ -96,8 +101,10 @@ export class FieldMultilineInput extends FieldTextInput { // needed so the plain-text representation of the XML produced by // `Blockly.Xml.domToText` will appear on a single line (this is a // limitation of the plain-text format). - fieldElement.textContent = - (this.getValue() as string).replace(/\n/g, ' '); + fieldElement.textContent = (this.getValue() as string).replace( + /\n/g, + ' ' + ); return fieldElement; } @@ -151,10 +158,12 @@ export class FieldMultilineInput extends FieldTextInput { override initView() { this.createBorderRect_(); this.textGroup = dom.createSvgElement( - Svg.G, { - 'class': 'blocklyEditableText', - }, - this.fieldGroup_); + Svg.G, + { + 'class': 'blocklyEditableText', + }, + this.fieldGroup_ + ); } /** @@ -175,8 +184,9 @@ export class FieldMultilineInput extends FieldTextInput { } const lines = textLines.split('\n'); textLines = ''; - const displayLinesNumber = - this.isOverflowedY_ ? this.maxLines_ : lines.length; + const displayLinesNumber = this.isOverflowedY_ + ? this.maxLines_ + : lines.length; for (let i = 0; i < displayLinesNumber; i++) { let text = lines[i]; if (text.length > this.maxDisplayLength) { @@ -226,7 +236,7 @@ export class FieldMultilineInput extends FieldTextInput { // Remove all text group children. let currentChild; const textGroup = this.textGroup; - while (currentChild = textGroup!.firstChild) { + while ((currentChild = textGroup!.firstChild)) { textGroup!.removeChild(currentChild); } @@ -234,16 +244,19 @@ export class FieldMultilineInput extends FieldTextInput { const lines = this.getDisplayText_().split('\n'); let y = 0; for (let i = 0; i < lines.length; i++) { - const lineHeight = this.getConstants()!.FIELD_TEXT_HEIGHT + - this.getConstants()!.FIELD_BORDER_RECT_Y_PADDING; + const lineHeight = + this.getConstants()!.FIELD_TEXT_HEIGHT + + this.getConstants()!.FIELD_BORDER_RECT_Y_PADDING; const span = dom.createSvgElement( - Svg.TEXT, { - 'class': 'blocklyText blocklyMultilineText', - 'x': this.getConstants()!.FIELD_BORDER_RECT_X_PADDING, - 'y': y + this.getConstants()!.FIELD_BORDER_RECT_Y_PADDING, - 'dy': this.getConstants()!.FIELD_TEXT_BASELINE, - }, - textGroup); + Svg.TEXT, + { + 'class': 'blocklyText blocklyMultilineText', + 'x': this.getConstants()!.FIELD_BORDER_RECT_X_PADDING, + 'y': y + this.getConstants()!.FIELD_BORDER_RECT_Y_PADDING, + 'dy': this.getConstants()!.FIELD_TEXT_BASELINE, + }, + textGroup + ); span.appendChild(document.createTextNode(lines[i])); y += lineHeight; } @@ -289,13 +302,18 @@ export class FieldMultilineInput extends FieldTextInput { let totalHeight = 0; for (let i = 0; i < nodes.length; i++) { const tspan = nodes[i] as SVGTextElement; - const textWidth = - dom.getFastTextWidth(tspan, fontSize, fontWeight, fontFamily); + const textWidth = dom.getFastTextWidth( + tspan, + fontSize, + fontWeight, + fontFamily + ); if (textWidth > totalWidth) { totalWidth = textWidth; } - totalHeight += this.getConstants()!.FIELD_TEXT_HEIGHT + - (i > 0 ? this.getConstants()!.FIELD_BORDER_RECT_Y_PADDING : 0); + totalHeight += + this.getConstants()!.FIELD_TEXT_HEIGHT + + (i > 0 ? this.getConstants()!.FIELD_BORDER_RECT_Y_PADDING : 0); } if (this.isBeingEdited_) { // The default width is based on the longest line in the display text, @@ -304,24 +322,31 @@ export class FieldMultilineInput extends FieldTextInput { // Otherwise we would get wrong editor width when there are more // lines than this.maxLines_. const actualEditorLines = String(this.value_).split('\n'); - const dummyTextElement = dom.createSvgElement( - Svg.TEXT, {'class': 'blocklyText blocklyMultilineText'}); + const dummyTextElement = dom.createSvgElement(Svg.TEXT, { + 'class': 'blocklyText blocklyMultilineText', + }); for (let i = 0; i < actualEditorLines.length; i++) { if (actualEditorLines[i].length > this.maxDisplayLength) { - actualEditorLines[i] = - actualEditorLines[i].substring(0, this.maxDisplayLength); + actualEditorLines[i] = actualEditorLines[i].substring( + 0, + this.maxDisplayLength + ); } dummyTextElement.textContent = actualEditorLines[i]; const lineWidth = dom.getFastTextWidth( - dummyTextElement, fontSize, fontWeight, fontFamily); + dummyTextElement, + fontSize, + fontWeight, + fontFamily + ); if (lineWidth > totalWidth) { totalWidth = lineWidth; } } const scrollbarWidth = - this.htmlInput_!.offsetWidth - this.htmlInput_!.clientWidth; + this.htmlInput_!.offsetWidth - this.htmlInput_!.clientWidth; totalWidth += scrollbarWidth; } if (this.borderRect_) { @@ -360,7 +385,7 @@ export class FieldMultilineInput extends FieldTextInput { const div = WidgetDiv.getDiv(); const scale = this.workspace_!.getScale(); - const htmlInput = (document.createElement('textarea')); + const htmlInput = document.createElement('textarea'); htmlInput.className = 'blocklyHtmlInput blocklyHtmlTextAreaInput'; htmlInput.setAttribute('spellcheck', String(this.spellcheck_)); const fontSize = this.getConstants()!.FIELD_TEXT_FONTSIZE * scale + 'pt'; @@ -370,11 +395,12 @@ export class FieldMultilineInput extends FieldTextInput { htmlInput.style.borderRadius = borderRadius; const paddingX = this.getConstants()!.FIELD_BORDER_RECT_X_PADDING * scale; const paddingY = - this.getConstants()!.FIELD_BORDER_RECT_Y_PADDING * scale / 2; - htmlInput.style.padding = paddingY + 'px ' + paddingX + 'px ' + paddingY + - 'px ' + paddingX + 'px'; - const lineHeight = this.getConstants()!.FIELD_TEXT_HEIGHT + - this.getConstants()!.FIELD_BORDER_RECT_Y_PADDING; + (this.getConstants()!.FIELD_BORDER_RECT_Y_PADDING * scale) / 2; + htmlInput.style.padding = + paddingY + 'px ' + paddingX + 'px ' + paddingY + 'px ' + paddingX + 'px'; + const lineHeight = + this.getConstants()!.FIELD_TEXT_HEIGHT + + this.getConstants()!.FIELD_BORDER_RECT_Y_PADDING; htmlInput.style.lineHeight = lineHeight * scale + 'px'; div!.appendChild(htmlInput); @@ -401,8 +427,11 @@ export class FieldMultilineInput extends FieldTextInput { * scrolling functionality is enabled. */ setMaxLines(maxLines: number) { - if (typeof maxLines === 'number' && maxLines > 0 && - maxLines !== this.maxLines_) { + if ( + typeof maxLines === 'number' && + maxLines > 0 && + maxLines !== this.maxLines_ + ) { this.maxLines_ = maxLines; this.forceRerender(); } @@ -438,8 +467,9 @@ export class FieldMultilineInput extends FieldTextInput { * @nocollapse * @internal */ - static override fromJson(options: FieldMultilineInputFromJsonConfig): - FieldMultilineInput { + static override fromJson( + options: FieldMultilineInputFromJsonConfig + ): FieldMultilineInput { const text = parsing.replaceMessageReferences(options.text); // `this` might be a subclass of FieldMultilineInput if that class doesn't // override the static fromJson method. @@ -449,7 +479,6 @@ export class FieldMultilineInput extends FieldTextInput { fieldRegistry.register('field_multilinetext', FieldMultilineInput); - /** * CSS for multiline field. */ @@ -477,8 +506,8 @@ export interface FieldMultilineInputConfig extends FieldTextInputConfig { /** * fromJson config options for the multiline input field. */ -export interface FieldMultilineInputFromJsonConfig extends - FieldMultilineInputConfig { +export interface FieldMultilineInputFromJsonConfig + extends FieldMultilineInputConfig { text?: string; } diff --git a/core/field_number.ts b/core/field_number.ts index e7a40f9a14a..7ca36d03905 100644 --- a/core/field_number.ts +++ b/core/field_number.ts @@ -14,7 +14,11 @@ goog.declareModuleId('Blockly.FieldNumber'); import {Field} from './field.js'; import * as fieldRegistry from './field_registry.js'; -import {FieldInput, FieldInputConfig, FieldInputValidator} from './field_input.js'; +import { + FieldInput, + FieldInputConfig, + FieldInputValidator, +} from './field_input.js'; import * as aria from './utils/aria.js'; /** @@ -34,7 +38,7 @@ export class FieldNumber extends FieldInput { * The number of decimal places to allow, or null to allow any number of * decimal digits. */ - private decimalPlaces: number|null = null; + private decimalPlaces: number | null = null; /** Don't spellcheck numbers. Our validator does a better job. */ protected override spellcheck_ = false; @@ -59,9 +63,13 @@ export class FieldNumber extends FieldInput { * for a list of properties this parameter supports. */ constructor( - value?: string|number|typeof Field.SKIP_SETUP, min?: string|number|null, - max?: string|number|null, precision?: string|number|null, - validator?: FieldNumberValidator|null, config?: FieldNumberConfig) { + value?: string | number | typeof Field.SKIP_SETUP, + min?: string | number | null, + max?: string | number | null, + precision?: string | number | null, + validator?: FieldNumberValidator | null, + config?: FieldNumberConfig + ) { // Pass SENTINEL so that we can define properties before value validation. super(Field.SKIP_SETUP); @@ -103,8 +111,10 @@ export class FieldNumber extends FieldInput { * @param precision Precision for value. */ setConstraints( - min: number|string|undefined|null, max: number|string|undefined|null, - precision: number|string|undefined|null) { + min: number | string | undefined | null, + max: number | string | undefined | null, + precision: number | string | undefined | null + ) { this.setMinInternal(min); this.setMaxInternal(max); this.setPrecisionInternal(precision); @@ -117,7 +127,7 @@ export class FieldNumber extends FieldInput { * * @param min Minimum value. */ - setMin(min: number|string|undefined|null) { + setMin(min: number | string | undefined | null) { this.setMinInternal(min); this.setValue(this.getValue()); } @@ -128,7 +138,7 @@ export class FieldNumber extends FieldInput { * * @param min Minimum value. */ - private setMinInternal(min: number|string|undefined|null) { + private setMinInternal(min: number | string | undefined | null) { if (min == null) { this.min_ = -Infinity; } else { @@ -155,7 +165,7 @@ export class FieldNumber extends FieldInput { * * @param max Maximum value. */ - setMax(max: number|string|undefined|null) { + setMax(max: number | string | undefined | null) { this.setMaxInternal(max); this.setValue(this.getValue()); } @@ -166,7 +176,7 @@ export class FieldNumber extends FieldInput { * * @param max Maximum value. */ - private setMaxInternal(max: number|string|undefined|null) { + private setMaxInternal(max: number | string | undefined | null) { if (max == null) { this.max_ = Infinity; } else { @@ -193,7 +203,7 @@ export class FieldNumber extends FieldInput { * * @param precision The number to which the field's value is rounded. */ - setPrecision(precision: number|string|undefined|null) { + setPrecision(precision: number | string | undefined | null) { this.setPrecisionInternal(precision); this.setValue(this.getValue()); } @@ -204,14 +214,15 @@ export class FieldNumber extends FieldInput { * * @param precision The number to which the field's value is rounded. */ - private setPrecisionInternal(precision: number|string|undefined|null) { + private setPrecisionInternal(precision: number | string | undefined | null) { this.precision_ = Number(precision) || 0; let precisionString = String(this.precision_); if (precisionString.indexOf('e') !== -1) { // String() is fast. But it turns .0000001 into '1e-7'. // Use the much slower toLocaleString to access all the digits. - precisionString = - this.precision_.toLocaleString('en-US', {maximumFractionDigits: 20}); + precisionString = this.precision_.toLocaleString('en-US', { + maximumFractionDigits: 20, + }); } const decimalIndex = precisionString.indexOf('.'); if (decimalIndex === -1) { @@ -241,8 +252,9 @@ export class FieldNumber extends FieldInput { * @param newValue The input value. * @returns A valid number, or null if invalid. */ - protected override doClassValidation_(newValue?: AnyDuringMigration): number - |null { + protected override doClassValidation_( + newValue?: AnyDuringMigration + ): number | null { if (newValue === null) { return null; } @@ -251,7 +263,7 @@ export class FieldNumber extends FieldInput { newValue = `${newValue}`; // TODO: Handle cases like 'ten', '1.203,14', etc. // 'O' is sometimes mistaken for '0' by inexperienced users. - newValue = newValue.replace(/O/ig, '0'); + newValue = newValue.replace(/O/gi, '0'); // Strip out thousands separators. newValue = newValue.replace(/,/g, ''); // Ignore case of 'Infinity'. @@ -309,7 +321,13 @@ export class FieldNumber extends FieldInput { // `this` might be a subclass of FieldNumber if that class doesn't override // the static fromJson method. return new this( - options.value, undefined, undefined, undefined, undefined, options); + options.value, + undefined, + undefined, + undefined, + undefined, + options + ); } } diff --git a/core/field_registry.ts b/core/field_registry.ts index 25364d4485f..11930272319 100644 --- a/core/field_registry.ts +++ b/core/field_registry.ts @@ -50,7 +50,7 @@ export function unregister(type: string) { * given type name * @internal */ -export function fromJson(options: RegistryOptions): Field|null { +export function fromJson(options: RegistryOptions): Field | null { return TEST_ONLY.fromJsonInternal(options); } @@ -59,14 +59,16 @@ export function fromJson(options: RegistryOptions): Field|null { * * @param options */ -function fromJsonInternal(options: RegistryOptions): Field|null { +function fromJsonInternal(options: RegistryOptions): Field | null { const fieldObject = registry.getObject(registry.Type.FIELD, options.type); if (!fieldObject) { console.warn( - 'Blockly could not create a field of type ' + options['type'] + + 'Blockly could not create a field of type ' + + options['type'] + '. The field is probably not being registered. This could be because' + ' the file is not loaded, the field does not register itself (Issue' + - ' #1584), or the registration is not being reached.'); + ' #1584), or the registration is not being reached.' + ); return null; } else if (typeof (fieldObject as any).fromJson !== 'function') { throw new TypeError('returned Field was not a IRegistrableField'); diff --git a/core/field_textinput.ts b/core/field_textinput.ts index 600e14cb1a7..3dc1052b06c 100644 --- a/core/field_textinput.ts +++ b/core/field_textinput.ts @@ -16,7 +16,11 @@ goog.declareModuleId('Blockly.FieldTextInput'); import './events/events_block_change.js'; import {Field} from './field.js'; -import {FieldInput, FieldInputConfig, FieldInputValidator} from './field_input.js'; +import { + FieldInput, + FieldInputConfig, + FieldInputValidator, +} from './field_input.js'; import * as fieldRegistry from './field_registry.js'; import * as parsing from './utils/parsing.js'; @@ -39,8 +43,10 @@ export class FieldTextInput extends FieldInput { * for a list of properties this parameter supports. */ constructor( - value?: string|typeof Field.SKIP_SETUP, - validator?: FieldTextInputValidator|null, config?: FieldTextInputConfig) { + value?: string | typeof Field.SKIP_SETUP, + validator?: FieldTextInputValidator | null, + config?: FieldTextInputConfig + ) { super(value, validator, config); } @@ -50,8 +56,9 @@ export class FieldTextInput extends FieldInput { * @param newValue The input value. * @returns A valid string, or null if invalid. */ - protected override doClassValidation_(newValue?: AnyDuringMigration): string - |null { + protected override doClassValidation_( + newValue?: AnyDuringMigration + ): string | null { if (newValue === undefined) { return null; } diff --git a/core/field_variable.ts b/core/field_variable.ts index f1c6e8dda15..6c9bfed41ba 100644 --- a/core/field_variable.ts +++ b/core/field_variable.ts @@ -17,7 +17,12 @@ import './events/events_block_change.js'; import type {Block} from './block.js'; import {Field, FieldConfig, UnattachedFieldError} from './field.js'; -import {FieldDropdown, FieldDropdownValidator, MenuGenerator, MenuOption} from './field_dropdown.js'; +import { + FieldDropdown, + FieldDropdownValidator, + MenuGenerator, + MenuOption, +} from './field_dropdown.js'; import * as fieldRegistry from './field_registry.js'; import * as internalConstants from './internal_constants.js'; import type {Menu} from './menu.js'; @@ -33,7 +38,7 @@ import * as Xml from './xml.js'; * Class for a variable's dropdown field. */ export class FieldVariable extends FieldDropdown { - protected override menuGenerator_: MenuGenerator|undefined; + protected override menuGenerator_: MenuGenerator | undefined; defaultVariableName: string; /** The type of the default variable for this field. */ @@ -43,11 +48,11 @@ export class FieldVariable extends FieldDropdown { * All of the types of variables that will be available in this field's * dropdown. */ - variableTypes: string[]|null = []; + variableTypes: string[] | null = []; protected override size_: Size; /** The variable model associated with this field. */ - private variable: VariableModel|null = null; + private variable: VariableModel | null = null; /** * Serializable fields are saved by the serializer, non-serializable fields @@ -75,9 +80,12 @@ export class FieldVariable extends FieldDropdown { * for a list of properties this parameter supports. */ constructor( - varName: string|null|typeof Field.SKIP_SETUP, - validator?: FieldVariableValidator, variableTypes?: string[], - defaultType?: string, config?: FieldVariableConfig) { + varName: string | null | typeof Field.SKIP_SETUP, + validator?: FieldVariableValidator, + variableTypes?: string[], + defaultType?: string, + config?: FieldVariableConfig + ) { super(Field.SKIP_SETUP); /** @@ -131,10 +139,14 @@ export class FieldVariable extends FieldDropdown { throw new UnattachedFieldError(); } if (this.variable) { - return; // Initialization already happened. + return; // Initialization already happened. } const variable = Variables.getOrCreateVariablePackage( - block.workspace, null, this.defaultVariableName, this.defaultType); + block.workspace, + null, + this.defaultVariableName, + this.defaultType + ); // Don't call setValue because we don't want to cause a rerender. this.doValueUpdate_(variable.getId()); } @@ -144,9 +156,11 @@ export class FieldVariable extends FieldDropdown { if (!block) { throw new UnattachedFieldError(); } - return super.shouldAddBorderRect_() && - (!this.getConstants()!.FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW || - block.type !== 'variables_get'); + return ( + super.shouldAddBorderRect_() && + (!this.getConstants()!.FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW || + block.type !== 'variables_get') + ); } /** @@ -164,21 +178,32 @@ export class FieldVariable extends FieldDropdown { const variableName = fieldElement.textContent; // 'variabletype' should be lowercase, but until July 2019 it was sometimes // recorded as 'variableType'. Thus we need to check for both. - const variableType = fieldElement.getAttribute('variabletype') || - fieldElement.getAttribute('variableType') || ''; + const variableType = + fieldElement.getAttribute('variabletype') || + fieldElement.getAttribute('variableType') || + ''; // AnyDuringMigration because: Argument of type 'string | null' is not // assignable to parameter of type 'string | undefined'. const variable = Variables.getOrCreateVariablePackage( - block.workspace, id, variableName as AnyDuringMigration, variableType); + block.workspace, + id, + variableName as AnyDuringMigration, + variableType + ); // This should never happen :) if (variableType !== null && variableType !== variable.type) { throw Error( - 'Serialized variable type with id \'' + variable.getId() + - '\' had type ' + variable.type + ', and ' + + "Serialized variable type with id '" + + variable.getId() + + "' had type " + + variable.type + + ', and ' + 'does not match variable field that references it: ' + - Xml.domToText(fieldElement) + '.'); + Xml.domToText(fieldElement) + + '.' + ); } this.setValue(variable.getId()); @@ -243,8 +268,11 @@ export class FieldVariable extends FieldDropdown { } // This is necessary so that blocks in the flyout can have custom var names. const variable = Variables.getOrCreateVariablePackage( - block.workspace, state['id'] || null, state['name'], - state['type'] || ''); + block.workspace, + state['id'] || null, + state['name'], + state['type'] || '' + ); this.setValue(variable.getId()); } @@ -265,7 +293,7 @@ export class FieldVariable extends FieldDropdown { * * @returns Current variable's ID. */ - override getValue(): string|null { + override getValue(): string | null { return this.variable ? this.variable.getId() : null; } @@ -287,7 +315,7 @@ export class FieldVariable extends FieldDropdown { * @returns The selected variable, or null if none was selected. * @internal */ - getVariable(): VariableModel|null { + getVariable(): VariableModel | null { return this.variable; } @@ -299,7 +327,7 @@ export class FieldVariable extends FieldDropdown { * * @returns Validation function, or null. */ - override getValidator(): FieldVariableValidator|null { + override getValidator(): FieldVariableValidator | null { // Validators shouldn't operate on the initial setValue call. // Normally this is achieved by calling setValidator after setValue, but // this is not a possibility with variable fields. @@ -315,8 +343,9 @@ export class FieldVariable extends FieldDropdown { * @param newValue The ID of the new variable to set. * @returns The validated ID, or null if invalid. */ - protected override doClassValidation_(newValue?: AnyDuringMigration): string - |null { + protected override doClassValidation_( + newValue?: AnyDuringMigration + ): string | null { if (newValue === null) { return null; } @@ -328,15 +357,14 @@ export class FieldVariable extends FieldDropdown { const variable = Variables.getVariable(block.workspace, newId); if (!variable) { console.warn( - 'Variable id doesn\'t point to a real variable! ' + - 'ID was ' + newId); + "Variable id doesn't point to a real variable! " + 'ID was ' + newId + ); return null; } // Type Checks. const type = variable.type; if (!this.typeIsAllowed(type)) { - console.warn( - 'Variable type doesn\'t match this field! Type was ' + type); + console.warn("Variable type doesn't match this field! Type was " + type); return null; } return newId; @@ -368,7 +396,7 @@ export class FieldVariable extends FieldDropdown { private typeIsAllowed(type: string): boolean { const typeList = this.getVariableTypes(); if (!typeList) { - return true; // If it's null, all types are valid. + return true; // If it's null, all types are valid. } for (let i = 0; i < typeList.length; i++) { if (type === typeList[i]) { @@ -397,7 +425,8 @@ export class FieldVariable extends FieldDropdown { // Throw an error if variableTypes is an empty list. const name = this.getText(); throw Error( - '\'variableTypes\' of field variable ' + name + ' was an empty list'); + "'variableTypes' of field variable " + name + ' was an empty list' + ); } return variableTypes; } @@ -412,7 +441,7 @@ export class FieldVariable extends FieldDropdown { * @param defaultType The type of the variable to create if this field's * value is not explicitly set. Defaults to ''. */ - private setTypes(variableTypes: string[]|null = null, defaultType = '') { + private setTypes(variableTypes: string[] | null = null, defaultType = '') { // If you expected that the default type would be the same as the only entry // in the variable types array, tell the Blockly team by commenting on // #1499. @@ -428,13 +457,17 @@ export class FieldVariable extends FieldDropdown { } if (!isInArray) { throw Error( - 'Invalid default type \'' + defaultType + '\' in ' + - 'the definition of a FieldVariable'); + "Invalid default type '" + + defaultType + + "' in " + + 'the definition of a FieldVariable' + ); } } else if (variableTypes !== null) { throw Error( - '\'variableTypes\' was not an array in the definition of ' + - 'a FieldVariable'); + "'variableTypes' was not an array in the definition of " + + 'a FieldVariable' + ); } // Only update the field once all checks pass. this.defaultType = defaultType; @@ -467,7 +500,9 @@ export class FieldVariable extends FieldDropdown { if (id === internalConstants.RENAME_VARIABLE_ID) { // Rename variable. Variables.renameVariable( - this.sourceBlock_.workspace, this.variable as VariableModel); + this.sourceBlock_.workspace, + this.variable as VariableModel + ); return; } else if (id === internalConstants.DELETE_VARIABLE_ID) { // Delete variable. @@ -500,8 +535,9 @@ export class FieldVariable extends FieldDropdown { * @nocollapse * @internal */ - static override fromJson(options: FieldVariableFromJsonConfig): - FieldVariable { + static override fromJson( + options: FieldVariableFromJsonConfig + ): FieldVariable { const varName = parsing.replaceMessageReferences(options.variable); // `this` might be a subclass of FieldVariable if that class doesn't // override the static fromJson method. @@ -517,8 +553,9 @@ export class FieldVariable extends FieldDropdown { static dropdownCreate(this: FieldVariable): MenuOption[] { if (!this.variable) { throw Error( - 'Tried to call dropdownCreate on a variable field with no' + - ' variable selected.'); + 'Tried to call dropdownCreate on a variable field with no' + + ' variable selected.' + ); } const name = this.getText(); let variableModelList: VariableModel[] = []; @@ -529,7 +566,7 @@ export class FieldVariable extends FieldDropdown { for (let i = 0; i < variableTypes.length; i++) { const variableType = variableTypes[i]; const variables = - this.sourceBlock_.workspace.getVariablesOfType(variableType); + this.sourceBlock_.workspace.getVariablesOfType(variableType); variableModelList = variableModelList.concat(variables); } } @@ -540,8 +577,10 @@ export class FieldVariable extends FieldDropdown { // Set the UUID as the internal representation of the variable. options[i] = [variableModelList[i].name, variableModelList[i].getId()]; } - options.push( - [Msg['RENAME_VARIABLE'], internalConstants.RENAME_VARIABLE_ID]); + options.push([ + Msg['RENAME_VARIABLE'], + internalConstants.RENAME_VARIABLE_ID, + ]); if (Msg['DELETE_VARIABLE']) { options.push([ Msg['DELETE_VARIABLE'].replace('%1', name), diff --git a/core/flyout_base.ts b/core/flyout_base.ts index a4ae4d70756..68ddd90c8f9 100644 --- a/core/flyout_base.ts +++ b/core/flyout_base.ts @@ -36,7 +36,6 @@ import {WorkspaceSvg} from './workspace_svg.js'; import * as utilsXml from './utils/xml.js'; import * as Xml from './xml.js'; - enum FlyoutItemType { BLOCK = 'block', BUTTON = 'button', @@ -69,7 +68,7 @@ export abstract class Flyout extends DeleteArea implements IFlyout { * between 0 and 1 specifying the degree of scrolling and a * similar x property. */ - protected abstract setMetrics_(xyRatio: {x?: number, y?: number}): void; + protected abstract setMetrics_(xyRatio: {x?: number; y?: number}): void; /** * Lay out the blocks in the flyout. @@ -137,14 +136,14 @@ export abstract class Flyout extends DeleteArea implements IFlyout { * Function that will be registered as a change listener on the workspace * to reflow when blocks in the flyout workspace change. */ - private reflowWrapper: Function|null = null; + private reflowWrapper: Function | null = null; /** * Function that disables blocks in the flyout based on max block counts * allowed in the target workspace. Registered as a change listener on the * target workspace. */ - private filterWrapper: Function|null = null; + private filterWrapper: Function | null = null; /** * List of background mats that lurk behind each block to catch clicks @@ -247,12 +246,12 @@ export abstract class Flyout extends DeleteArea implements IFlyout { * The path around the background of the flyout, which will be filled with a * background colour. */ - protected svgBackground_: SVGPathElement|null = null; + protected svgBackground_: SVGPathElement | null = null; /** * The root SVG group for the button or label. */ - protected svgGroup_: SVGGElement|null = null; + protected svgGroup_: SVGGElement | null = null; /** * @param workspaceOptions Dictionary of options for the * workspace. @@ -263,7 +262,8 @@ export abstract class Flyout extends DeleteArea implements IFlyout { this.workspace_ = new WorkspaceSvg(workspaceOptions); this.workspace_.setMetricsManager( - new FlyoutMetricsManager(this.workspace_, this)); + new FlyoutMetricsManager(this.workspace_, this) + ); this.workspace_.internalIsFlyout = true; // Keep the workspace visibility consistent with the flyout's visibility. @@ -326,7 +326,9 @@ export abstract class Flyout extends DeleteArea implements IFlyout { * put the flyout in. This should be or . * @returns The flyout's SVG group. */ - createDom(tagName: string|Svg|Svg): SVGElement { + createDom( + tagName: string | Svg | Svg + ): SVGElement { /* @@ -335,15 +337,22 @@ export abstract class Flyout extends DeleteArea implements IFlyout { */ // Setting style to display:none to start. The toolbox and flyout // hide/show code will set up proper visibility and size later. - this.svgGroup_ = dom.createSvgElement( - tagName, {'class': 'blocklyFlyout', 'style': 'display: none'}); + this.svgGroup_ = dom.createSvgElement(tagName, { + 'class': 'blocklyFlyout', + 'style': 'display: none', + }); this.svgBackground_ = dom.createSvgElement( - Svg.PATH, {'class': 'blocklyFlyoutBackground'}, this.svgGroup_); + Svg.PATH, + {'class': 'blocklyFlyoutBackground'}, + this.svgGroup_ + ); this.svgGroup_.appendChild(this.workspace_.createDom()); - this.workspace_.getThemeManager().subscribe( - this.svgBackground_, 'flyoutBackgroundColour', 'fill'); - this.workspace_.getThemeManager().subscribe( - this.svgBackground_, 'flyoutOpacity', 'fill-opacity'); + this.workspace_ + .getThemeManager() + .subscribe(this.svgBackground_, 'flyoutBackgroundColour', 'fill'); + this.workspace_ + .getThemeManager() + .subscribe(this.svgBackground_, 'flyoutOpacity', 'fill-opacity'); return this.svgGroup_; } @@ -358,26 +367,42 @@ export abstract class Flyout extends DeleteArea implements IFlyout { this.workspace_.targetWorkspace = targetWorkspace; this.workspace_.scrollbar = new ScrollbarPair( - this.workspace_, this.horizontalLayout, !this.horizontalLayout, - 'blocklyFlyoutScrollbar', this.SCROLLBAR_MARGIN); + this.workspace_, + this.horizontalLayout, + !this.horizontalLayout, + 'blocklyFlyoutScrollbar', + this.SCROLLBAR_MARGIN + ); this.hide(); - this.boundEvents.push(browserEvents.conditionalBind( - (this.svgGroup_ as SVGGElement), 'wheel', this, this.wheel_)); + this.boundEvents.push( + browserEvents.conditionalBind( + this.svgGroup_ as SVGGElement, + 'wheel', + this, + this.wheel_ + ) + ); if (!this.autoClose) { this.filterWrapper = this.filterForCapacity.bind(this); this.targetWorkspace.addChangeListener(this.filterWrapper); } // Dragging the flyout up and down. - this.boundEvents.push(browserEvents.conditionalBind( - (this.svgBackground_ as SVGPathElement), 'pointerdown', this, - this.onMouseDown)); + this.boundEvents.push( + browserEvents.conditionalBind( + this.svgBackground_ as SVGPathElement, + 'pointerdown', + this, + this.onMouseDown + ) + ); // A flyout connected to a workspace doesn't have its own current gesture. - this.workspace_.getGesture = - this.targetWorkspace.getGesture.bind(this.targetWorkspace); + this.workspace_.getGesture = this.targetWorkspace.getGesture.bind( + this.targetWorkspace + ); // Get variables from the main workspace rather than the target workspace. this.workspace_.setVariableMap(this.targetWorkspace.getVariableMap()); @@ -546,11 +571,15 @@ export abstract class Flyout extends DeleteArea implements IFlyout { // reposition in resize, we need to call setPosition. See issue #4692. if (scrollbar.hScroll) { scrollbar.hScroll.setPosition( - scrollbar.hScroll.position.x, scrollbar.hScroll.position.y); + scrollbar.hScroll.position.x, + scrollbar.hScroll.position.y + ); } if (scrollbar.vScroll) { scrollbar.vScroll.setPosition( - scrollbar.vScroll.position.x, scrollbar.vScroll.position.y); + scrollbar.vScroll.position.x, + scrollbar.vScroll.position.y + ); } } } @@ -583,7 +612,7 @@ export abstract class Flyout extends DeleteArea implements IFlyout { * in the flyout. This is either an array of Nodes, a NodeList, a * toolbox definition, or a string with the name of the dynamic category. */ - show(flyoutDef: toolbox.FlyoutDefinition|string) { + show(flyoutDef: toolbox.FlyoutDefinition | string) { this.workspace_.setResizesEnabled(false); this.hide(); this.clearOldBlocks(); @@ -626,40 +655,42 @@ export abstract class Flyout extends DeleteArea implements IFlyout { * of objects to show in the flyout. * @returns The list of contents and gaps needed to lay out the flyout. */ - private createFlyoutInfo(parsedContent: toolbox.FlyoutItemInfoArray): - {contents: FlyoutItem[], gaps: number[]} { + private createFlyoutInfo(parsedContent: toolbox.FlyoutItemInfoArray): { + contents: FlyoutItem[]; + gaps: number[]; + } { const contents: FlyoutItem[] = []; const gaps: number[] = []; this.permanentlyDisabled.length = 0; const defaultGap = this.horizontalLayout ? this.GAP_X : this.GAP_Y; for (const info of parsedContent) { if ('custom' in info) { - const customInfo = (info as toolbox.DynamicCategoryInfo); + const customInfo = info as toolbox.DynamicCategoryInfo; const categoryName = customInfo['custom']; const flyoutDef = this.getDynamicCategoryContents(categoryName); const parsedDynamicContent = - toolbox.convertFlyoutDefToJsonArray(flyoutDef); + toolbox.convertFlyoutDefToJsonArray(flyoutDef); const {contents: dynamicContents, gaps: dynamicGaps} = - this.createFlyoutInfo(parsedDynamicContent); + this.createFlyoutInfo(parsedDynamicContent); contents.push(...dynamicContents); gaps.push(...dynamicGaps); } switch (info['kind'].toUpperCase()) { case 'BLOCK': { - const blockInfo = (info as toolbox.BlockInfo); + const blockInfo = info as toolbox.BlockInfo; const block = this.createFlyoutBlock(blockInfo); contents.push({type: FlyoutItemType.BLOCK, block: block}); this.addBlockGap(blockInfo, gaps, defaultGap); break; } case 'SEP': { - const sepInfo = (info as toolbox.SeparatorInfo); + const sepInfo = info as toolbox.SeparatorInfo; this.addSeparatorGap(sepInfo, gaps, defaultGap); break; } case 'LABEL': { - const labelInfo = (info as toolbox.LabelInfo); + const labelInfo = info as toolbox.LabelInfo; // A label is a button with different styling. const label = this.createButton(labelInfo, /** isLabel */ true); contents.push({type: FlyoutItemType.BUTTON, button: label}); @@ -667,7 +698,7 @@ export abstract class Flyout extends DeleteArea implements IFlyout { break; } case 'BUTTON': { - const buttonInfo = (info as toolbox.ButtonInfo); + const buttonInfo = info as toolbox.ButtonInfo; const button = this.createButton(buttonInfo, /** isLabel */ false); contents.push({type: FlyoutItemType.BUTTON, button: button}); gaps.push(defaultGap); @@ -686,17 +717,18 @@ export abstract class Flyout extends DeleteArea implements IFlyout { * @returns The definition of the * flyout in one of its many forms. */ - private getDynamicCategoryContents(categoryName: string): - toolbox.FlyoutDefinition { + private getDynamicCategoryContents( + categoryName: string + ): toolbox.FlyoutDefinition { // Look up the correct category generation function and call that to get a // valid XML list. const fnToApply = - this.workspace_.targetWorkspace!.getToolboxCategoryCallback( - categoryName); + this.workspace_.targetWorkspace!.getToolboxCategoryCallback(categoryName); if (typeof fnToApply !== 'function') { throw TypeError( - 'Couldn\'t find a callback function when opening' + - ' a toolbox category.'); + "Couldn't find a callback function when opening" + + ' a toolbox category.' + ); } return fnToApply(this.workspace_.targetWorkspace!); } @@ -709,11 +741,16 @@ export abstract class Flyout extends DeleteArea implements IFlyout { * @returns The object used to display the button in the * flyout. */ - private createButton(btnInfo: toolbox.ButtonOrLabelInfo, isLabel: boolean): - FlyoutButton { + private createButton( + btnInfo: toolbox.ButtonOrLabelInfo, + isLabel: boolean + ): FlyoutButton { const curButton = new FlyoutButton( - this.workspace_, (this.targetWorkspace as WorkspaceSvg), btnInfo, - isLabel); + this.workspace_, + this.targetWorkspace as WorkspaceSvg, + btnInfo, + isLabel + ); return curButton; } @@ -727,9 +764,11 @@ export abstract class Flyout extends DeleteArea implements IFlyout { private createFlyoutBlock(blockInfo: toolbox.BlockInfo): BlockSvg { let block; if (blockInfo['blockxml']) { - const xml = (typeof blockInfo['blockxml'] === 'string' ? - utilsXml.textToDom(blockInfo['blockxml']) : - blockInfo['blockxml']) as Element; + const xml = ( + typeof blockInfo['blockxml'] === 'string' + ? utilsXml.textToDom(blockInfo['blockxml']) + : blockInfo['blockxml'] + ) as Element; block = this.getRecycledBlock(xml.getAttribute('type')!); if (!block) { block = Xml.domToBlock(xml, this.workspace_); @@ -738,10 +777,10 @@ export abstract class Flyout extends DeleteArea implements IFlyout { block = this.getRecycledBlock(blockInfo['type']!); if (!block) { if (blockInfo['enabled'] === undefined) { - blockInfo['enabled'] = blockInfo['disabled'] !== 'true' && - blockInfo['disabled'] !== true; + blockInfo['enabled'] = + blockInfo['disabled'] !== 'true' && blockInfo['disabled'] !== true; } - block = blocks.append((blockInfo as blocks.State), this.workspace_); + block = blocks.append(blockInfo as blocks.State, this.workspace_); } } @@ -750,7 +789,7 @@ export abstract class Flyout extends DeleteArea implements IFlyout { // Do not enable these blocks as a result of capacity filtering. this.permanentlyDisabled.push(block); } - return (block as BlockSvg); + return block as BlockSvg; } /** @@ -761,7 +800,7 @@ export abstract class Flyout extends DeleteArea implements IFlyout { * @returns The recycled block, or undefined if * one could not be recycled. */ - private getRecycledBlock(blockType: string): BlockSvg|undefined { + private getRecycledBlock(blockType: string): BlockSvg | undefined { let index = -1; for (let i = 0; i < this.recycledBlocks.length; i++) { if (this.recycledBlocks[i].type === blockType) { @@ -781,14 +820,19 @@ export abstract class Flyout extends DeleteArea implements IFlyout { * next. */ private addBlockGap( - blockInfo: toolbox.BlockInfo, gaps: number[], defaultGap: number) { + blockInfo: toolbox.BlockInfo, + gaps: number[], + defaultGap: number + ) { let gap; if (blockInfo['gap']) { gap = parseInt(String(blockInfo['gap'])); } else if (blockInfo['blockxml']) { - const xml = (typeof blockInfo['blockxml'] === 'string' ? - utilsXml.textToDom(blockInfo['blockxml']) : - blockInfo['blockxml']) as Element; + const xml = ( + typeof blockInfo['blockxml'] === 'string' + ? utilsXml.textToDom(blockInfo['blockxml']) + : blockInfo['blockxml'] + ) as Element; gap = parseInt(xml.getAttribute('gap')!); } gaps.push(!gap || isNaN(gap) ? defaultGap : gap); @@ -804,7 +848,10 @@ export abstract class Flyout extends DeleteArea implements IFlyout { * element. */ private addSeparatorGap( - sepInfo: toolbox.SeparatorInfo, gaps: number[], defaultGap: number) { + sepInfo: toolbox.SeparatorInfo, + gaps: number[], + defaultGap: number + ) { // Change the gap between two toolbox elements. // // The default gap is 24, can be set larger or smaller. @@ -824,7 +871,7 @@ export abstract class Flyout extends DeleteArea implements IFlyout { private clearOldBlocks() { // Delete any blocks from a previous showing. const oldBlocks = this.workspace_.getTopBlocks(false); - for (let i = 0, block; block = oldBlocks[i]; i++) { + for (let i = 0, block; (block = oldBlocks[i]); i++) { if (this.blockIsRecyclable_(block)) { this.recycleBlock(block); } else { @@ -841,7 +888,7 @@ export abstract class Flyout extends DeleteArea implements IFlyout { } this.mats.length = 0; // Delete any buttons from a previous showing. - for (let i = 0, button; button = this.buttons_[i]; i++) { + for (let i = 0, button; (button = this.buttons_[i]); i++) { button.dispose(); } this.buttons_.length = 0; @@ -893,19 +940,38 @@ export abstract class Flyout extends DeleteArea implements IFlyout { * as a mat for that block. */ protected addBlockListeners_( - root: SVGElement, block: BlockSvg, rect: SVGElement) { - this.listeners.push(browserEvents.conditionalBind( - root, 'pointerdown', null, this.blockMouseDown(block))); - this.listeners.push(browserEvents.conditionalBind( - rect, 'pointerdown', null, this.blockMouseDown(block))); + root: SVGElement, + block: BlockSvg, + rect: SVGElement + ) { + this.listeners.push( + browserEvents.conditionalBind( + root, + 'pointerdown', + null, + this.blockMouseDown(block) + ) + ); this.listeners.push( - browserEvents.bind(root, 'pointerenter', block, block.addSelect)); + browserEvents.conditionalBind( + rect, + 'pointerdown', + null, + this.blockMouseDown(block) + ) + ); this.listeners.push( - browserEvents.bind(root, 'pointerleave', block, block.removeSelect)); + browserEvents.bind(root, 'pointerenter', block, block.addSelect) + ); this.listeners.push( - browserEvents.bind(rect, 'pointerenter', block, block.addSelect)); + browserEvents.bind(root, 'pointerleave', block, block.removeSelect) + ); this.listeners.push( - browserEvents.bind(rect, 'pointerleave', block, block.removeSelect)); + browserEvents.bind(rect, 'pointerenter', block, block.addSelect) + ); + this.listeners.push( + browserEvents.bind(rect, 'pointerleave', block, block.removeSelect) + ); } /** @@ -972,7 +1038,9 @@ export abstract class Flyout extends DeleteArea implements IFlyout { this.targetWorkspace.hideChaff(); const newVariables = Variables.getAddedVariables( - this.targetWorkspace, variablesBeforeCreation); + this.targetWorkspace, + variablesBeforeCreation + ); if (eventUtils.isEnabled()) { eventUtils.setGroup(true); @@ -980,7 +1048,8 @@ export abstract class Flyout extends DeleteArea implements IFlyout { for (let i = 0; i < newVariables.length; i++) { const thisVariable = newVariables[i]; eventUtils.fire( - new (eventUtils.get(eventUtils.VAR_CREATE))(thisVariable)); + new (eventUtils.get(eventUtils.VAR_CREATE))(thisVariable) + ); } // Block events come after var events, in case they refer to newly created @@ -1009,8 +1078,14 @@ export abstract class Flyout extends DeleteArea implements IFlyout { button.show(); // Clicking on a flyout button or label is a lot like clicking on the // flyout background. - this.listeners.push(browserEvents.conditionalBind( - buttonSvg, 'pointerdown', this, this.onMouseDown)); + this.listeners.push( + browserEvents.conditionalBind( + buttonSvg, + 'pointerdown', + this, + this.onMouseDown + ) + ); this.buttons_.push(button); } @@ -1029,8 +1104,12 @@ export abstract class Flyout extends DeleteArea implements IFlyout { * the block. */ protected createRect_( - block: BlockSvg, x: number, y: number, - blockHW: {height: number, width: number}, index: number): SVGElement { + block: BlockSvg, + x: number, + y: number, + blockHW: {height: number; width: number}, + index: number + ): SVGElement { // Create an invisible rectangle under the block to act as a button. Just // using the block as a button is poor, since blocks have holes in them. const rect = dom.createSvgElement(Svg.RECT, { @@ -1065,7 +1144,9 @@ export abstract class Flyout extends DeleteArea implements IFlyout { const blockXY = block.getRelativeToSurfaceXY(); rect.setAttribute('y', String(blockXY.y)); rect.setAttribute( - 'x', String(this.RTL ? blockXY.x - blockHW.width : blockXY.x)); + 'x', + String(this.RTL ? blockXY.x - blockHW.width : blockXY.x) + ); } /** @@ -1076,10 +1157,11 @@ export abstract class Flyout extends DeleteArea implements IFlyout { */ private filterForCapacity() { const blocks = this.workspace_.getTopBlocks(false); - for (let i = 0, block; block = blocks[i]; i++) { + for (let i = 0, block; (block = blocks[i]); i++) { if (this.permanentlyDisabled.indexOf(block) === -1) { const enable = this.targetWorkspace.isCapacityAvailable( - common.getBlockTypeCounts(block)); + common.getBlockTypeCounts(block) + ); while (block) { block.setEnabled(enable); block = block.getNextBlock(); @@ -1107,8 +1189,9 @@ export abstract class Flyout extends DeleteArea implements IFlyout { * @internal */ isScrollable(): boolean { - return this.workspace_.scrollbar ? this.workspace_.scrollbar.isVisible() : - false; + return this.workspace_.scrollbar + ? this.workspace_.scrollbar.isVisible() + : false; } /** @@ -1125,10 +1208,10 @@ export abstract class Flyout extends DeleteArea implements IFlyout { } // Clone the block. - const json = (blocks.save(oldBlock) as blocks.State); + const json = blocks.save(oldBlock) as blocks.State; // Normallly this resizes leading to weird jumps. Save it for terminateDrag. targetWorkspace.setResizesEnabled(false); - const block = (blocks.append(json, targetWorkspace) as BlockSvg); + const block = blocks.append(json, targetWorkspace) as BlockSvg; this.positionNewBlock(oldBlock, block); @@ -1160,13 +1243,17 @@ export abstract class Flyout extends DeleteArea implements IFlyout { // The position of the old block in pixels relative to the upper left corner // of the injection div. - const oldBlockOffsetPixels = - Coordinate.sum(flyoutOffsetPixels, oldBlockPos); + const oldBlockOffsetPixels = Coordinate.sum( + flyoutOffsetPixels, + oldBlockPos + ); // The position of the old block in pixels relative to the origin of the // main workspace. - const finalOffset = - Coordinate.difference(oldBlockOffsetPixels, mainOffsetPixels); + const finalOffset = Coordinate.difference( + oldBlockOffsetPixels, + mainOffsetPixels + ); // The position of the old block in main workspace coordinates. finalOffset.scale(1 / targetWorkspace.scale); @@ -1180,6 +1267,6 @@ export abstract class Flyout extends DeleteArea implements IFlyout { */ export interface FlyoutItem { type: FlyoutItemType; - button?: FlyoutButton|undefined; - block?: BlockSvg|undefined; + button?: FlyoutButton | undefined; + block?: BlockSvg | undefined; } diff --git a/core/flyout_button.ts b/core/flyout_button.ts index ad71dd4e4e9..e9bf40d5f2a 100644 --- a/core/flyout_button.ts +++ b/core/flyout_button.ts @@ -22,7 +22,6 @@ import {Svg} from './utils/svg.js'; import type * as toolbox from './utils/toolbox.js'; import type {WorkspaceSvg} from './workspace_svg.js'; - /** * Class for a button or label in the flyout. */ @@ -39,10 +38,10 @@ export class FlyoutButton { private readonly text: string; private readonly position: Coordinate; private readonly callbackKey: string; - private readonly cssClass: string|null; + private readonly cssClass: string | null; /** Mouse up event data. */ - private onMouseUpWrapper: browserEvents.Data|null = null; + private onMouseUpWrapper: browserEvents.Data | null = null; info: toolbox.ButtonOrLabelInfo; /** The width of the button's rect. */ @@ -52,10 +51,10 @@ export class FlyoutButton { height = 0; /** The root SVG group for the button or label. */ - private svgGroup: SVGGElement|null = null; + private svgGroup: SVGGElement | null = null; /** The SVG element with the text of the label or button. */ - private svgText: SVGTextElement|null = null; + private svgText: SVGTextElement | null = null; /** * @param workspace The workspace in which to place this button. @@ -65,19 +64,22 @@ export class FlyoutButton { * @internal */ constructor( - private readonly workspace: WorkspaceSvg, - private readonly targetWorkspace: WorkspaceSvg, - json: toolbox.ButtonOrLabelInfo, private readonly isLabel_: boolean) { + private readonly workspace: WorkspaceSvg, + private readonly targetWorkspace: WorkspaceSvg, + json: toolbox.ButtonOrLabelInfo, + private readonly isLabel_: boolean + ) { this.text = json['text']; this.position = new Coordinate(0, 0); /** The key to the function called when this button is clicked. */ this.callbackKey = - (json as - AnyDuringMigration)['callbackKey'] || /* Check the lower case version - too to satisfy IE */ - (json as AnyDuringMigration)['callbackkey']; + (json as AnyDuringMigration)[ + 'callbackKey' + ] /* Check the lower case version + too to satisfy IE */ || + (json as AnyDuringMigration)['callbackkey']; /** If specified, a CSS class to add to this button. */ this.cssClass = (json as AnyDuringMigration)['web-class'] || null; @@ -98,39 +100,49 @@ export class FlyoutButton { } this.svgGroup = dom.createSvgElement( - Svg.G, {'class': cssClass}, this.workspace.getCanvas()); + Svg.G, + {'class': cssClass}, + this.workspace.getCanvas() + ); let shadow; if (!this.isLabel_) { // Shadow rectangle (light source does not mirror in RTL). shadow = dom.createSvgElement( - Svg.RECT, { - 'class': 'blocklyFlyoutButtonShadow', - 'rx': FlyoutButton.BORDER_RADIUS, - 'ry': FlyoutButton.BORDER_RADIUS, - 'x': 1, - 'y': 1, - }, - this.svgGroup!); - } - // Background rectangle. - const rect = dom.createSvgElement( - Svg.RECT, { - 'class': this.isLabel_ ? 'blocklyFlyoutLabelBackground' : - 'blocklyFlyoutButtonBackground', + Svg.RECT, + { + 'class': 'blocklyFlyoutButtonShadow', 'rx': FlyoutButton.BORDER_RADIUS, 'ry': FlyoutButton.BORDER_RADIUS, + 'x': 1, + 'y': 1, }, - this.svgGroup!); + this.svgGroup! + ); + } + // Background rectangle. + const rect = dom.createSvgElement( + Svg.RECT, + { + 'class': this.isLabel_ + ? 'blocklyFlyoutLabelBackground' + : 'blocklyFlyoutButtonBackground', + 'rx': FlyoutButton.BORDER_RADIUS, + 'ry': FlyoutButton.BORDER_RADIUS, + }, + this.svgGroup! + ); const svgText = dom.createSvgElement( - Svg.TEXT, { - 'class': this.isLabel_ ? 'blocklyFlyoutLabelText' : 'blocklyText', - 'x': 0, - 'y': 0, - 'text-anchor': 'middle', - }, - this.svgGroup!); + Svg.TEXT, + { + 'class': this.isLabel_ ? 'blocklyFlyoutLabelText' : 'blocklyText', + 'x': 0, + 'y': 0, + 'text-anchor': 'middle', + }, + this.svgGroup! + ); let text = parsing.replaceMessageReferences(this.text); if (this.workspace.RTL) { // Force text to be RTL by adding an RLM. @@ -139,17 +151,26 @@ export class FlyoutButton { svgText.textContent = text; if (this.isLabel_) { this.svgText = svgText; - this.workspace.getThemeManager().subscribe( - this.svgText, 'flyoutForegroundColour', 'fill'); + this.workspace + .getThemeManager() + .subscribe(this.svgText, 'flyoutForegroundColour', 'fill'); } const fontSize = style.getComputedStyle(svgText, 'fontSize'); const fontWeight = style.getComputedStyle(svgText, 'fontWeight'); const fontFamily = style.getComputedStyle(svgText, 'fontFamily'); this.width = dom.getFastTextWidthWithSizeString( - svgText, fontSize, fontWeight, fontFamily); - const fontMetrics = - dom.measureFontMetrics(text, fontSize, fontWeight, fontFamily); + svgText, + fontSize, + fontWeight, + fontFamily + ); + const fontMetrics = dom.measureFontMetrics( + text, + fontSize, + fontWeight, + fontFamily + ); this.height = fontMetrics.height; if (!this.isLabel_) { @@ -163,16 +184,20 @@ export class FlyoutButton { svgText.setAttribute('x', String(this.width / 2)); svgText.setAttribute( - 'y', - String( - this.height / 2 - fontMetrics.height / 2 + fontMetrics.baseline)); + 'y', + String(this.height / 2 - fontMetrics.height / 2 + fontMetrics.baseline) + ); this.updateTransform(); // AnyDuringMigration because: Argument of type 'SVGGElement | null' is not // assignable to parameter of type 'EventTarget'. this.onMouseUpWrapper = browserEvents.conditionalBind( - this.svgGroup as AnyDuringMigration, 'pointerup', this, this.onMouseUp); + this.svgGroup as AnyDuringMigration, + 'pointerup', + this, + this.onMouseUp + ); return this.svgGroup!; } @@ -185,8 +210,9 @@ export class FlyoutButton { /** Update SVG attributes to match internal state. */ private updateTransform() { this.svgGroup!.setAttribute( - 'transform', - 'translate(' + this.position.x + ',' + this.position.y + ')'); + 'transform', + 'translate(' + this.position.x + ',' + this.position.y + ')' + ); } /** @@ -256,11 +282,15 @@ export class FlyoutButton { if (this.isLabel_ && this.callbackKey) { console.warn( - 'Labels should not have callbacks. Label text: ' + this.text); + 'Labels should not have callbacks. Label text: ' + this.text + ); } else if ( - !this.isLabel_ && - !(this.callbackKey && - this.targetWorkspace.getButtonCallback(this.callbackKey))) { + !this.isLabel_ && + !( + this.callbackKey && + this.targetWorkspace.getButtonCallback(this.callbackKey) + ) + ) { console.warn('Buttons should have callbacks. Button text: ' + this.text); } else if (!this.isLabel_) { const callback = this.targetWorkspace.getButtonCallback(this.callbackKey); diff --git a/core/flyout_horizontal.ts b/core/flyout_horizontal.ts index 91602a79adf..192b46797ad 100644 --- a/core/flyout_horizontal.ts +++ b/core/flyout_horizontal.ts @@ -24,7 +24,6 @@ import {Rect} from './utils/rect.js'; import * as toolbox from './utils/toolbox.js'; import * as WidgetDiv from './widgetdiv.js'; - /** * Class for a flyout. */ @@ -42,7 +41,7 @@ export class HorizontalFlyout extends Flyout { * @param xyRatio Contains a y property which is a float between 0 and 1 * specifying the degree of scrolling and a similar x property. */ - protected override setMetrics_(xyRatio: {x: number, y: number}) { + protected override setMetrics_(xyRatio: {x: number; y: number}) { if (!this.isVisible()) { return; } @@ -53,14 +52,16 @@ export class HorizontalFlyout extends Flyout { const absoluteMetrics = metricsManager.getAbsoluteMetrics(); if (typeof xyRatio.x === 'number') { - this.workspace_.scrollX = - -(scrollMetrics.left + - (scrollMetrics.width - viewMetrics.width) * xyRatio.x); + this.workspace_.scrollX = -( + scrollMetrics.left + + (scrollMetrics.width - viewMetrics.width) * xyRatio.x + ); } this.workspace_.translate( - this.workspace_.scrollX + absoluteMetrics.left, - this.workspace_.scrollY + absoluteMetrics.top); + this.workspace_.scrollX + absoluteMetrics.left, + this.workspace_.scrollY + absoluteMetrics.top + ); } /** @@ -151,7 +152,9 @@ export class HorizontalFlyout extends Flyout { private setBackgroundPath(width: number, height: number) { const atTop = this.toolboxPosition_ === toolbox.Position.TOP; // Start at top left. - const path: (string|number)[] = ['M 0,' + (atTop ? 0 : this.CORNER_RADIUS)]; + const path: (string | number)[] = [ + 'M 0,' + (atTop ? 0 : this.CORNER_RADIUS), + ]; if (atTop) { // Top. @@ -160,24 +163,52 @@ export class HorizontalFlyout extends Flyout { path.push('v', height); // Bottom. path.push( - 'a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1, - -this.CORNER_RADIUS, this.CORNER_RADIUS); + 'a', + this.CORNER_RADIUS, + this.CORNER_RADIUS, + 0, + 0, + 1, + -this.CORNER_RADIUS, + this.CORNER_RADIUS + ); path.push('h', -width); // Left. path.push( - 'a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1, - -this.CORNER_RADIUS, -this.CORNER_RADIUS); + 'a', + this.CORNER_RADIUS, + this.CORNER_RADIUS, + 0, + 0, + 1, + -this.CORNER_RADIUS, + -this.CORNER_RADIUS + ); path.push('z'); } else { // Top. path.push( - 'a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1, - this.CORNER_RADIUS, -this.CORNER_RADIUS); + 'a', + this.CORNER_RADIUS, + this.CORNER_RADIUS, + 0, + 0, + 1, + this.CORNER_RADIUS, + -this.CORNER_RADIUS + ); path.push('h', width); // Right. path.push( - 'a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1, - this.CORNER_RADIUS, this.CORNER_RADIUS); + 'a', + this.CORNER_RADIUS, + this.CORNER_RADIUS, + 0, + 0, + 1, + this.CORNER_RADIUS, + this.CORNER_RADIUS + ); path.push('v', height); // Bottom. path.push('h', -width - 2 * this.CORNER_RADIUS); @@ -234,11 +265,11 @@ export class HorizontalFlyout extends Flyout { contents = contents.reverse(); } - for (let i = 0, item; item = contents[i]; i++) { + for (let i = 0, item; (item = contents[i]); i++) { if (item.type === 'block') { const block = item.block; const allBlocks = block!.getDescendants(false); - for (let j = 0, child; child = allBlocks[j]; j++) { + for (let j = 0, child; (child = allBlocks[j]); j++) { // Mark blocks as being inside a flyout. This is used to detect and // prevent the closure of the flyout if the user right-clicks on such // a block. @@ -282,12 +313,14 @@ export class HorizontalFlyout extends Flyout { const dx = currentDragDeltaXY.x; const dy = currentDragDeltaXY.y; // Direction goes from -180 to 180, with 0 toward the right and 90 on top. - const dragDirection = Math.atan2(dy, dx) / Math.PI * 180; + const dragDirection = (Math.atan2(dy, dx) / Math.PI) * 180; const range = this.dragAngleRange_; // Check for up or down dragging. - if (dragDirection < 90 + range && dragDirection > 90 - range || - dragDirection > -90 - range && dragDirection < -90 + range) { + if ( + (dragDirection < 90 + range && dragDirection > 90 - range) || + (dragDirection > -90 - range && dragDirection < -90 + range) + ) { return true; } return false; @@ -300,7 +333,7 @@ export class HorizontalFlyout extends Flyout { * @returns The component's bounding box. Null if drag target area should be * ignored. */ - override getClientRect(): Rect|null { + override getClientRect(): Rect | null { if (!this.svgGroup_ || this.autoClose || !this.isVisible()) { // The bounding rectangle won't compute correctly if the flyout is closed // and auto-close flyouts aren't valid drag targets (or delete areas). @@ -318,7 +351,8 @@ export class HorizontalFlyout extends Flyout { if (this.toolboxPosition_ === toolbox.Position.TOP) { const height = flyoutRect.height; return new Rect(-BIG_NUM, top + height, -BIG_NUM, BIG_NUM); - } else { // Bottom. + } else { + // Bottom. return new Rect(top, BIG_NUM, -BIG_NUM, BIG_NUM); } } @@ -331,11 +365,11 @@ export class HorizontalFlyout extends Flyout { this.workspace_.scale = this.getFlyoutScale(); let flyoutHeight = 0; const blocks = this.workspace_.getTopBlocks(false); - for (let i = 0, block; block = blocks[i]; i++) { + for (let i = 0, block; (block = blocks[i]); i++) { flyoutHeight = Math.max(flyoutHeight, block.getHeightWidth().height); } const buttons = this.buttons_; - for (let i = 0, button; button = buttons[i]; i++) { + for (let i = 0, button; (button = buttons[i]); i++) { flyoutHeight = Math.max(flyoutHeight, button.height); } flyoutHeight += this.MARGIN * 1.5; @@ -343,21 +377,24 @@ export class HorizontalFlyout extends Flyout { flyoutHeight += Scrollbar.scrollbarThickness; if (this.height_ !== flyoutHeight) { - for (let i = 0, block; block = blocks[i]; i++) { + for (let i = 0, block; (block = blocks[i]); i++) { if (this.rectMap_.has(block)) { this.moveRectToBlock_(this.rectMap_.get(block)!, block); } } - if (this.targetWorkspace!.toolboxPosition === this.toolboxPosition_ && - this.toolboxPosition_ === toolbox.Position.TOP && - !this.targetWorkspace!.getToolbox()) { + if ( + this.targetWorkspace!.toolboxPosition === this.toolboxPosition_ && + this.toolboxPosition_ === toolbox.Position.TOP && + !this.targetWorkspace!.getToolbox() + ) { // This flyout is a simple toolbox. Reposition the workspace so that // (0,0) is in the correct position relative to the new absolute edge // (ie toolbox edge). this.targetWorkspace!.translate( - this.targetWorkspace!.scrollX, - this.targetWorkspace!.scrollY + flyoutHeight); + this.targetWorkspace!.scrollX, + this.targetWorkspace!.scrollY + flyoutHeight + ); } this.height_ = flyoutHeight; this.position(); @@ -367,5 +404,7 @@ export class HorizontalFlyout extends Flyout { } registry.register( - registry.Type.FLYOUTS_HORIZONTAL_TOOLBOX, registry.DEFAULT, - HorizontalFlyout); + registry.Type.FLYOUTS_HORIZONTAL_TOOLBOX, + registry.DEFAULT, + HorizontalFlyout +); diff --git a/core/flyout_metrics_manager.ts b/core/flyout_metrics_manager.ts index 48ca9379a4f..baccbbbd549 100644 --- a/core/flyout_metrics_manager.ts +++ b/core/flyout_metrics_manager.ts @@ -16,7 +16,6 @@ import type {IFlyout} from './interfaces/i_flyout.js'; import {ContainerRegion, MetricsManager} from './metrics_manager.js'; import type {WorkspaceSvg} from './workspace_svg.js'; - /** * Calculates metrics for a flyout's workspace. * The metrics are mainly used to size scrollbars for the flyout. @@ -40,8 +39,9 @@ export class FlyoutMetricsManager extends MetricsManager { * * @returns The bounding box of the blocks on the workspace. */ - private getBoundingBox_(): SVGRect| - {height: number, y: number, width: number, x: number} { + private getBoundingBox_(): + | SVGRect + | {height: number; y: number; width: number; x: number} { let blockBoundingBox; try { blockBoundingBox = this.workspace_.getCanvas().getBBox(); @@ -68,8 +68,10 @@ export class FlyoutMetricsManager extends MetricsManager { } override getScrollMetrics( - opt_getWorkspaceCoordinates?: boolean, opt_viewMetrics?: ContainerRegion, - opt_contentMetrics?: ContainerRegion) { + opt_getWorkspaceCoordinates?: boolean, + opt_viewMetrics?: ContainerRegion, + opt_contentMetrics?: ContainerRegion + ) { const contentMetrics = opt_contentMetrics || this.getContentMetrics(); const margin = this.flyout_.MARGIN * this.workspace_.scale; const scale = opt_getWorkspaceCoordinates ? this.workspace_.scale : 1; diff --git a/core/flyout_vertical.ts b/core/flyout_vertical.ts index 3fab3e6ddec..2d24cfb33a1 100644 --- a/core/flyout_vertical.ts +++ b/core/flyout_vertical.ts @@ -24,7 +24,6 @@ import {Rect} from './utils/rect.js'; import * as toolbox from './utils/toolbox.js'; import * as WidgetDiv from './widgetdiv.js'; - /** * Class for a flyout. */ @@ -43,7 +42,7 @@ export class VerticalFlyout extends Flyout { * @param xyRatio Contains a y property which is a float between 0 and 1 * specifying the degree of scrolling and a similar x property. */ - protected override setMetrics_(xyRatio: {x: number, y: number}) { + protected override setMetrics_(xyRatio: {x: number; y: number}) { if (!this.isVisible()) { return; } @@ -53,13 +52,15 @@ export class VerticalFlyout extends Flyout { const absoluteMetrics = metricsManager.getAbsoluteMetrics(); if (typeof xyRatio.y === 'number') { - this.workspace_.scrollY = - -(scrollMetrics.top + - (scrollMetrics.height - viewMetrics.height) * xyRatio.y); + this.workspace_.scrollY = -( + scrollMetrics.top + + (scrollMetrics.height - viewMetrics.height) * xyRatio.y + ); } this.workspace_.translate( - this.workspace_.scrollX + absoluteMetrics.left, - this.workspace_.scrollY + absoluteMetrics.top); + this.workspace_.scrollX + absoluteMetrics.left, + this.workspace_.scrollY + absoluteMetrics.top + ); } /** @@ -132,7 +133,7 @@ export class VerticalFlyout extends Flyout { const edgeWidth = this.width_ - this.CORNER_RADIUS; const edgeHeight = - targetWorkspaceViewMetrics.height - 2 * this.CORNER_RADIUS; + targetWorkspaceViewMetrics.height - 2 * this.CORNER_RADIUS; this.setBackgroundPath(edgeWidth, edgeHeight); const x = this.getX(); @@ -152,22 +153,37 @@ export class VerticalFlyout extends Flyout { const totalWidth = width + this.CORNER_RADIUS; // Decide whether to start on the left or right. - const path: Array = - ['M ' + (atRight ? totalWidth : 0) + ',0']; + const path: Array = [ + 'M ' + (atRight ? totalWidth : 0) + ',0', + ]; // Top. - path.push('h', (atRight ? -width : width)); + path.push('h', atRight ? -width : width); // Rounded corner. path.push( - 'a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, atRight ? 0 : 1, - atRight ? -this.CORNER_RADIUS : this.CORNER_RADIUS, this.CORNER_RADIUS); + 'a', + this.CORNER_RADIUS, + this.CORNER_RADIUS, + 0, + 0, + atRight ? 0 : 1, + atRight ? -this.CORNER_RADIUS : this.CORNER_RADIUS, + this.CORNER_RADIUS + ); // Side closest to workspace. path.push('v', Math.max(0, height)); // Rounded corner. path.push( - 'a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, atRight ? 0 : 1, - atRight ? this.CORNER_RADIUS : -this.CORNER_RADIUS, this.CORNER_RADIUS); + 'a', + this.CORNER_RADIUS, + this.CORNER_RADIUS, + 0, + 0, + atRight ? 0 : 1, + atRight ? this.CORNER_RADIUS : -this.CORNER_RADIUS, + this.CORNER_RADIUS + ); // Bottom. - path.push('h', (atRight ? width : -width)); + path.push('h', atRight ? width : -width); path.push('z'); this.svgBackground_!.setAttribute('d', path.join(' ')); } @@ -215,11 +231,11 @@ export class VerticalFlyout extends Flyout { const cursorX = this.RTL ? margin : margin + this.tabWidth_; let cursorY = margin; - for (let i = 0, item; item = contents[i]; i++) { + for (let i = 0, item; (item = contents[i]); i++) { if (item.type === 'block') { const block = item.block; const allBlocks = block!.getDescendants(false); - for (let j = 0, child; child = allBlocks[j]; j++) { + for (let j = 0, child; (child = allBlocks[j]); j++) { // Mark blocks as being inside a flyout. This is used to detect and // prevent the closure of the flyout if the user right-clicks on such // a block. @@ -227,13 +243,18 @@ export class VerticalFlyout extends Flyout { } const root = block!.getSvgRoot(); const blockHW = block!.getHeightWidth(); - const moveX = - block!.outputConnection ? cursorX - this.tabWidth_ : cursorX; + const moveX = block!.outputConnection + ? cursorX - this.tabWidth_ + : cursorX; block!.moveBy(moveX, cursorY); const rect = this.createRect_( - block!, this.RTL ? moveX - blockHW.width : moveX, cursorY, blockHW, - i); + block!, + this.RTL ? moveX - blockHW.width : moveX, + cursorY, + blockHW, + i + ); this.addBlockListeners_(root, block!, rect); @@ -260,12 +281,15 @@ export class VerticalFlyout extends Flyout { const dx = currentDragDeltaXY.x; const dy = currentDragDeltaXY.y; // Direction goes from -180 to 180, with 0 toward the right and 90 on top. - const dragDirection = Math.atan2(dy, dx) / Math.PI * 180; + const dragDirection = (Math.atan2(dy, dx) / Math.PI) * 180; const range = this.dragAngleRange_; // Check for left or right dragging. - if (dragDirection < range && dragDirection > -range || - (dragDirection < -180 + range || dragDirection > 180 - range)) { + if ( + (dragDirection < range && dragDirection > -range) || + dragDirection < -180 + range || + dragDirection > 180 - range + ) { return true; } return false; @@ -278,7 +302,7 @@ export class VerticalFlyout extends Flyout { * @returns The component's bounding box. Null if drag target area should be * ignored. */ - override getClientRect(): Rect|null { + override getClientRect(): Rect | null { if (!this.svgGroup_ || this.autoClose || !this.isVisible()) { // The bounding rectangle won't compute correctly if the flyout is closed // and auto-close flyouts aren't valid drag targets (or delete areas). @@ -296,7 +320,8 @@ export class VerticalFlyout extends Flyout { if (this.toolboxPosition_ === toolbox.Position.LEFT) { const width = flyoutRect.width; return new Rect(-BIG_NUM, BIG_NUM, -BIG_NUM, left + width); - } else { // Right + } else { + // Right return new Rect(-BIG_NUM, BIG_NUM, left, BIG_NUM); } } @@ -309,14 +334,14 @@ export class VerticalFlyout extends Flyout { this.workspace_.scale = this.getFlyoutScale(); let flyoutWidth = 0; const blocks = this.workspace_.getTopBlocks(false); - for (let i = 0, block; block = blocks[i]; i++) { + for (let i = 0, block; (block = blocks[i]); i++) { let width = block.getHeightWidth().width; if (block.outputConnection) { width -= this.tabWidth_; } flyoutWidth = Math.max(flyoutWidth, width); } - for (let i = 0, button; button = this.buttons_[i]; i++) { + for (let i = 0, button; (button = this.buttons_[i]); i++) { flyoutWidth = Math.max(flyoutWidth, button.width); } flyoutWidth += this.MARGIN * 1.5 + this.tabWidth_; @@ -324,7 +349,7 @@ export class VerticalFlyout extends Flyout { flyoutWidth += Scrollbar.scrollbarThickness; if (this.width_ !== flyoutWidth) { - for (let i = 0, block; block = blocks[i]; i++) { + for (let i = 0, block; (block = blocks[i]); i++) { if (this.RTL) { // With the flyoutWidth known, right-align the blocks. const oldX = block.getRelativeToSurfaceXY().x; @@ -340,23 +365,29 @@ export class VerticalFlyout extends Flyout { } if (this.RTL) { // With the flyoutWidth known, right-align the buttons. - for (let i = 0, button; button = this.buttons_[i]; i++) { + for (let i = 0, button; (button = this.buttons_[i]); i++) { const y = button.getPosition().y; - const x = flyoutWidth / this.workspace_.scale - button.width - - this.MARGIN - this.tabWidth_; + const x = + flyoutWidth / this.workspace_.scale - + button.width - + this.MARGIN - + this.tabWidth_; button.moveTo(x, y); } } - if (this.targetWorkspace!.toolboxPosition === this.toolboxPosition_ && - this.toolboxPosition_ === toolbox.Position.LEFT && - !this.targetWorkspace!.getToolbox()) { + if ( + this.targetWorkspace!.toolboxPosition === this.toolboxPosition_ && + this.toolboxPosition_ === toolbox.Position.LEFT && + !this.targetWorkspace!.getToolbox() + ) { // This flyout is a simple toolbox. Reposition the workspace so that // (0,0) is in the correct position relative to the new absolute edge // (ie toolbox edge). this.targetWorkspace!.translate( - this.targetWorkspace!.scrollX + flyoutWidth, - this.targetWorkspace!.scrollY); + this.targetWorkspace!.scrollX + flyoutWidth, + this.targetWorkspace!.scrollY + ); } this.width_ = flyoutWidth; this.position(); @@ -366,4 +397,7 @@ export class VerticalFlyout extends Flyout { } registry.register( - registry.Type.FLYOUTS_VERTICAL_TOOLBOX, registry.DEFAULT, VerticalFlyout); + registry.Type.FLYOUTS_VERTICAL_TOOLBOX, + registry.DEFAULT, + VerticalFlyout +); diff --git a/core/generator.ts b/core/generator.ts index e3261dfa55a..058e73cbe6f 100644 --- a/core/generator.ts +++ b/core/generator.ts @@ -19,7 +19,6 @@ import {Names, NameType} from './names.js'; import * as deprecation from './utils/deprecation.js'; import type {Workspace} from './workspace.js'; - /** * Class for a code generator that translates the blocks into a language. * @@ -42,21 +41,21 @@ export class CodeGenerator { * Any instances of '%1' will be replaced by the block ID that failed. * E.g. ` checkTimeout(%1);\n` */ - INFINITE_LOOP_TRAP: string|null = null; + INFINITE_LOOP_TRAP: string | null = null; /** * Arbitrary code to inject before every statement. * Any instances of '%1' will be replaced by the block ID of the statement. * E.g. `highlight(%1);\n` */ - STATEMENT_PREFIX: string|null = null; + STATEMENT_PREFIX: string | null = null; /** * Arbitrary code to inject after every statement. * Any instances of '%1' will be replaced by the block ID of the statement. * E.g. `highlight(%1);\n` */ - STATEMENT_SUFFIX: string|null = null; + STATEMENT_SUFFIX: string | null = null; /** * The method of indenting. Defaults to two spaces, but language generators @@ -79,7 +78,7 @@ export class CodeGenerator { * will cause blockToCode to emit a warning if the generator has not been * initialized. If this flag is untouched, it will have no effect. */ - isInitialized: boolean|null = null; + isInitialized: boolean | null = null; /** Comma-separated list of reserved words. */ protected RESERVED_WORDS_ = ''; @@ -100,8 +99,10 @@ export class CodeGenerator { constructor(name: string) { this.name_ = name; - this.FUNCTION_NAME_PLACEHOLDER_REGEXP_ = - new RegExp(this.FUNCTION_NAME_PLACEHOLDER_, 'g'); + this.FUNCTION_NAME_PLACEHOLDER_REGEXP_ = new RegExp( + this.FUNCTION_NAME_PLACEHOLDER_, + 'g' + ); } /** @@ -114,13 +115,14 @@ export class CodeGenerator { if (!workspace) { // Backwards compatibility from before there could be multiple workspaces. console.warn( - 'No workspace specified in workspaceToCode call. Guessing.'); + 'No workspace specified in workspaceToCode call. Guessing.' + ); workspace = common.getMainWorkspace(); } const code = []; this.init(workspace); const blocks = workspace.getTopBlocks(true); - for (let i = 0, block; block = blocks[i]; i++) { + for (let i = 0, block; (block = blocks[i]); i++) { let line = this.blockToCode(block); if (Array.isArray(line)) { // Value blocks return tuples of code and operator order. @@ -200,11 +202,14 @@ export class CodeGenerator { * For value blocks, an array containing the generated code and an * operator order value. Returns '' if block is null. */ - blockToCode(block: Block|null, opt_thisOnly?: boolean): string - |[string, number] { + blockToCode( + block: Block | null, + opt_thisOnly?: boolean + ): string | [string, number] { if (this.isInitialized === false) { console.warn( - 'CodeGenerator init was not called before blockToCode was called.'); + 'CodeGenerator init was not called before blockToCode was called.' + ); } if (!block) { return ''; @@ -221,8 +226,13 @@ export class CodeGenerator { const func = (this as any)[block.type]; if (typeof func !== 'function') { throw Error( - 'Language "' + this.name_ + '" does not know how to generate ' + - 'code for block type "' + block.type + '".'); + 'Language "' + + this.name_ + + '" does not know how to generate ' + + 'code for block type "' + + block.type + + '".' + ); } // First argument to func.call is the value of 'this' in the generator. // Prior to 24 September 2013 'this' was the only way to access the block. @@ -278,15 +288,17 @@ export class CodeGenerator { // Statement blocks must only return code. if (!Array.isArray(tuple)) { throw TypeError( - `Expecting tuple from value block: ${targetBlock.type} See ` + + `Expecting tuple from value block: ${targetBlock.type} See ` + `developers.google.com/blockly/guides/create-custom-blocks/generating-code ` + - `for more information`); + `for more information` + ); } let code = tuple[0]; const innerOrder = tuple[1]; if (isNaN(innerOrder)) { throw TypeError( - 'Expecting valid order from value block: ' + targetBlock.type); + 'Expecting valid order from value block: ' + targetBlock.type + ); } if (!code) { return ''; @@ -297,8 +309,10 @@ export class CodeGenerator { const outerOrderClass = Math.floor(outerOrder); const innerOrderClass = Math.floor(innerOrder); if (outerOrderClass <= innerOrderClass) { - if (outerOrderClass === innerOrderClass && - (outerOrderClass === 0 || outerOrderClass === 99)) { + if ( + outerOrderClass === innerOrderClass && + (outerOrderClass === 0 || outerOrderClass === 99) + ) { // Don't generate parens around NONE-NONE and ATOMIC-ATOMIC pairs. // 0 is the atomic order, 99 is the none order. No parentheses needed. // In all known languages multiple such code blocks are not order @@ -310,8 +324,10 @@ export class CodeGenerator { parensNeeded = true; // Check for special exceptions. for (let i = 0; i < this.ORDER_OVERRIDES.length; i++) { - if (this.ORDER_OVERRIDES[i][0] === outerOrder && - this.ORDER_OVERRIDES[i][1] === innerOrder) { + if ( + this.ORDER_OVERRIDES[i][0] === outerOrder && + this.ORDER_OVERRIDES[i][1] === innerOrder + ) { parensNeeded = false; break; } @@ -343,11 +359,12 @@ export class CodeGenerator { // Statement blocks must only return code. if (typeof code !== 'string') { throw TypeError( - 'Expecting code from statement block: ' + - (targetBlock && targetBlock.type)); + 'Expecting code from statement block: ' + + (targetBlock && targetBlock.type) + ); } if (code) { - code = this.prefixLines((code), this.INDENT); + code = this.prefixLines(code, this.INDENT); } return code; } @@ -364,19 +381,26 @@ export class CodeGenerator { */ addLoopTrap(branch: string, block: Block): string { if (this.INFINITE_LOOP_TRAP) { - branch = this.prefixLines( - this.injectId(this.INFINITE_LOOP_TRAP, block), this.INDENT) + - branch; + branch = + this.prefixLines( + this.injectId(this.INFINITE_LOOP_TRAP, block), + this.INDENT + ) + branch; } if (this.STATEMENT_SUFFIX && !block.suppressPrefixSuffix) { - branch = this.prefixLines( - this.injectId(this.STATEMENT_SUFFIX, block), this.INDENT) + - branch; + branch = + this.prefixLines( + this.injectId(this.STATEMENT_SUFFIX, block), + this.INDENT + ) + branch; } if (this.STATEMENT_PREFIX && !block.suppressPrefixSuffix) { - branch = branch + - this.prefixLines( - this.injectId(this.STATEMENT_PREFIX, block), this.INDENT); + branch = + branch + + this.prefixLines( + this.injectId(this.STATEMENT_PREFIX, block), + this.INDENT + ); } return branch; } @@ -390,8 +414,8 @@ export class CodeGenerator { * @returns Code snippet with ID. */ injectId(msg: string, block: Block): string { - const id = block.id.replace(/\$/g, '$$$$'); // Issue 251. - return msg.replace(/%1/g, '\'' + id + '\''); + const id = block.id.replace(/\$/g, '$$$$'); // Issue 251. + return msg.replace(/%1/g, "'" + id + "'"); } /** @@ -424,17 +448,22 @@ export class CodeGenerator { * @returns The actual name of the new function. This may differ from * desiredName if the former has already been taken by the user. */ - protected provideFunction_(desiredName: string, code: string[]|string): - string { + protected provideFunction_( + desiredName: string, + code: string[] | string + ): string { if (!this.definitions_[desiredName]) { - const functionName = - this.nameDB_!.getDistinctName(desiredName, NameType.PROCEDURE); + const functionName = this.nameDB_!.getDistinctName( + desiredName, + NameType.PROCEDURE + ); this.functionNames_[desiredName] = functionName; if (Array.isArray(code)) { code = code.join('\n'); } - let codeText = code.trim().replace( - this.FUNCTION_NAME_PLACEHOLDER_REGEXP_, functionName); + let codeText = code + .trim() + .replace(this.FUNCTION_NAME_PLACEHOLDER_REGEXP_, functionName); // Change all ' ' indents into the desired indent. // To avoid an infinite loop of replacements, change all indents to '\0' // character first, then replace them all with the indent. @@ -479,8 +508,11 @@ export class CodeGenerator { * @param _opt_thisOnly True to generate code for only this statement. * @returns Code with comments and subsequent blocks added. */ - protected scrub_(_block: Block, code: string, _opt_thisOnly?: boolean): - string { + protected scrub_( + _block: Block, + code: string, + _opt_thisOnly?: boolean + ): string { // Optionally override return code; } @@ -524,17 +556,16 @@ Object.defineProperties(CodeGenerator.prototype, { * @deprecated 'variableDB_' was renamed to 'nameDB_' (May 2021). * @suppress {checkTypes} */ - variableDB_: ({ + variableDB_: { /** @returns Name database. */ - get(this: CodeGenerator): Names | - undefined { - deprecation.warn('variableDB_', 'version 9', 'version 10', 'nameDB_'); - return this.nameDB_; - }, + get(this: CodeGenerator): Names | undefined { + deprecation.warn('variableDB_', 'version 9', 'version 10', 'nameDB_'); + return this.nameDB_; + }, /** @param nameDb New name database. */ - set(this: CodeGenerator, nameDb: Names|undefined) { + set(this: CodeGenerator, nameDb: Names | undefined) { deprecation.warn('variableDB_', 'version 9', 'version 10', 'nameDB_'); this.nameDB_ = nameDb; }, - }), + }, }); diff --git a/core/gesture.ts b/core/gesture.ts index be3a0d3736d..ab807ff2d20 100644 --- a/core/gesture.ts +++ b/core/gesture.ts @@ -37,7 +37,6 @@ import {WorkspaceCommentSvg} from './workspace_comment_svg.js'; import {WorkspaceDragger} from './workspace_dragger.js'; import type {WorkspaceSvg} from './workspace_svg.js'; - /** * Note: In this file "start" refers to pointerdown * events. "End" refers to pointerup events. @@ -65,19 +64,19 @@ export class Gesture { * The bubble that the gesture started on, or null if it did not start on a * bubble. */ - private startBubble: IBubble|null = null; + private startBubble: IBubble | null = null; /** * The field that the gesture started on, or null if it did not start on a * field. */ - private startField: Field|null = null; + private startField: Field | null = null; /** * The block that the gesture started on, or null if it did not start on a * block. */ - private startBlock: BlockSvg|null = null; + private startBlock: BlockSvg | null = null; /** * The block that this gesture targets. If the gesture started on a @@ -85,14 +84,14 @@ export class Gesture { * gesture started in the flyout, this is the root block of the block group * that was clicked or dragged. */ - private targetBlock: BlockSvg|null = null; + private targetBlock: BlockSvg | null = null; /** * The workspace that the gesture started on. There may be multiple * workspaces on a page; this is more accurate than using * Blockly.common.getMainWorkspace(). */ - protected startWorkspace_: WorkspaceSvg|null = null; + protected startWorkspace_: WorkspaceSvg | null = null; /** * Whether the pointer has at any point moved out of the drag radius. @@ -109,19 +108,19 @@ export class Gesture { private boundEvents: browserEvents.Data[] = []; /** The object tracking a bubble drag, or null if none is in progress. */ - private bubbleDragger: BubbleDragger|null = null; + private bubbleDragger: BubbleDragger | null = null; /** The object tracking a block drag, or null if none is in progress. */ - private blockDragger: IBlockDragger|null = null; + private blockDragger: IBlockDragger | null = null; /** * The object tracking a workspace or flyout workspace drag, or null if none * is in progress. */ - private workspaceDragger: WorkspaceDragger|null = null; + private workspaceDragger: WorkspaceDragger | null = null; /** The flyout a gesture started in, if any. */ - private flyout: IFlyout|null = null; + private flyout: IFlyout | null = null; /** Boolean for sanity-checking that some code is only called once. */ private calledUpdateIsDragging = false; @@ -140,7 +139,7 @@ export class Gesture { private isMultiTouch_ = false; /** A map of cached points used for tracking multi-touch gestures. */ - private cachedPoints = new Map(); + private cachedPoints = new Map(); /** * This is the ratio between the starting distance between the touch points @@ -154,7 +153,7 @@ export class Gesture { private startDistance = 0; /** Boolean for whether or not the workspace supports pinch-zoom. */ - private isPinchZoomEnabled: boolean|null = null; + private isPinchZoomEnabled: boolean | null = null; /** * The owner of the dropdownDiv when this gesture first starts. @@ -162,7 +161,7 @@ export class Gesture { * act on their events, and some fields care about who owns * the dropdown. */ - currentDropdownOwner: Field|null = null; + currentDropdownOwner: Field | null = null; /** * @param e The event that kicked off this gesture. @@ -170,7 +169,9 @@ export class Gesture { * reference to it. */ constructor( - e: PointerEvent, private readonly creatorWorkspace: WorkspaceSvg) { + e: PointerEvent, + private readonly creatorWorkspace: WorkspaceSvg + ) { this.mostRecentEvent = e; /** @@ -235,15 +236,18 @@ export class Gesture { * @returns True if the drag just exceeded the drag radius for the first time. */ private updateDragDelta(currentXY: Coordinate): boolean { - this.currentDragDeltaXY = - Coordinate.difference(currentXY, (this.mouseDownXY)); + this.currentDragDeltaXY = Coordinate.difference( + currentXY, + this.mouseDownXY + ); if (!this.hasExceededDragRadius) { const currentDragDelta = Coordinate.magnitude(this.currentDragDeltaXY); // The flyout has a different drag radius from the rest of Blockly. - const limitRadius = - this.flyout ? config.flyoutDragRadius : config.dragRadius; + const limitRadius = this.flyout + ? config.flyoutDragRadius + : config.dragRadius; this.hasExceededDragRadius = currentDragDelta > limitRadius; return this.hasExceededDragRadius; @@ -270,8 +274,10 @@ export class Gesture { throw new Error(`Cannot update dragging from the flyout because the ' + 'flyout's target workspace is undefined`); } - if (!this.flyout.isScrollable() || - this.flyout.isDragTowardWorkspace(this.currentDragDeltaXY)) { + if ( + !this.flyout.isScrollable() || + this.flyout.isDragTowardWorkspace(this.currentDragDeltaXY) + ) { this.startWorkspace_ = this.flyout.targetWorkspace; this.startWorkspace_.updateScreenCalculationsIfScrolled(); // Start the event group now, so that the same event group is used for @@ -347,13 +353,14 @@ export class Gesture { private updateIsDraggingWorkspace() { if (!this.startWorkspace_) { throw new Error( - 'Cannot update dragging the workspace because the ' + - 'start workspace is undefined'); + 'Cannot update dragging the workspace because the ' + + 'start workspace is undefined' + ); } - const wsMovable = this.flyout ? - this.flyout.isScrollable() : - this.startWorkspace_ && this.startWorkspace_.isDraggable(); + const wsMovable = this.flyout + ? this.flyout.isScrollable() + : this.startWorkspace_ && this.startWorkspace_.isDraggable(); if (!wsMovable) return; this.workspaceDragger = new WorkspaceDragger(this.startWorkspace_); @@ -390,10 +397,15 @@ export class Gesture { /** Create a block dragger and start dragging the selected block. */ private startDraggingBlock() { const BlockDraggerClass = registry.getClassFromOptions( - registry.Type.BLOCK_DRAGGER, this.creatorWorkspace.options, true); - - this.blockDragger = - new BlockDraggerClass!((this.targetBlock), (this.startWorkspace_)); + registry.Type.BLOCK_DRAGGER, + this.creatorWorkspace.options, + true + ); + + this.blockDragger = new BlockDraggerClass!( + this.targetBlock, + this.startWorkspace_ + ); this.blockDragger!.startDrag(this.currentDragDeltaXY, this.healStack); this.blockDragger!.drag(this.mostRecentEvent, this.currentDragDeltaXY); } @@ -402,20 +414,26 @@ export class Gesture { private startDraggingBubble() { if (!this.startBubble) { throw new Error( - 'Cannot update dragging the bubble because the start ' + - 'bubble is undefined'); + 'Cannot update dragging the bubble because the start ' + + 'bubble is undefined' + ); } if (!this.startWorkspace_) { throw new Error( - 'Cannot update dragging the bubble because the start ' + - 'workspace is undefined'); + 'Cannot update dragging the bubble because the start ' + + 'workspace is undefined' + ); } - this.bubbleDragger = - new BubbleDragger(this.startBubble, this.startWorkspace_); + this.bubbleDragger = new BubbleDragger( + this.startBubble, + this.startWorkspace_ + ); this.bubbleDragger.startBubbleDrag(); this.bubbleDragger.dragBubble( - this.mostRecentEvent, this.currentDragDeltaXY); + this.mostRecentEvent, + this.currentDragDeltaXY + ); } /** @@ -428,11 +446,13 @@ export class Gesture { doStart(e: PointerEvent) { if (!this.startWorkspace_) { throw new Error( - 'Cannot start the touch gesture becauase the start ' + - 'workspace is undefined'); + 'Cannot start the touch gesture becauase the start ' + + 'workspace is undefined' + ); } - this.isPinchZoomEnabled = this.startWorkspace_.options.zoomOptions && - this.startWorkspace_.options.zoomOptions.pinch; + this.isPinchZoomEnabled = + this.startWorkspace_.options.zoomOptions && + this.startWorkspace_.options.zoomOptions.pinch; if (browserEvents.isTargetInput(e)) { this.cancel(); @@ -491,15 +511,33 @@ export class Gesture { * @internal */ bindMouseEvents(e: PointerEvent) { - this.boundEvents.push(browserEvents.conditionalBind( - document, 'pointerdown', null, this.handleStart.bind(this), - /* opt_noCaptureIdentifier */ true)); - this.boundEvents.push(browserEvents.conditionalBind( - document, 'pointermove', null, this.handleMove.bind(this), - /* opt_noCaptureIdentifier */ true)); - this.boundEvents.push(browserEvents.conditionalBind( - document, 'pointerup', null, this.handleUp.bind(this), - /* opt_noCaptureIdentifier */ true)); + this.boundEvents.push( + browserEvents.conditionalBind( + document, + 'pointerdown', + null, + this.handleStart.bind(this), + /* opt_noCaptureIdentifier */ true + ) + ); + this.boundEvents.push( + browserEvents.conditionalBind( + document, + 'pointermove', + null, + this.handleMove.bind(this), + /* opt_noCaptureIdentifier */ true + ) + ); + this.boundEvents.push( + browserEvents.conditionalBind( + document, + 'pointerup', + null, + this.handleUp.bind(this), + /* opt_noCaptureIdentifier */ true + ) + ); e.preventDefault(); e.stopPropagation(); @@ -530,8 +568,10 @@ export class Gesture { * @internal */ handleMove(e: PointerEvent) { - if ((this.isDragging() && Touch.shouldHandleEvent(e)) || - !this.isMultiTouch()) { + if ( + (this.isDragging() && Touch.shouldHandleEvent(e)) || + !this.isMultiTouch() + ) { this.updateFromEvent(e); if (this.workspaceDragger) { this.workspaceDragger.drag(this.currentDragDeltaXY); @@ -539,7 +579,9 @@ export class Gesture { this.blockDragger.drag(this.mostRecentEvent, this.currentDragDeltaXY); } else if (this.bubbleDragger) { this.bubbleDragger.dragBubble( - this.mostRecentEvent, this.currentDragDeltaXY); + this.mostRecentEvent, + this.currentDragDeltaXY + ); } e.preventDefault(); e.stopPropagation(); @@ -618,8 +660,8 @@ export class Gesture { const pointers = Array.from(this.cachedPoints.keys()); // If two pointers are down, store info if (pointers.length === 2) { - const point0 = (this.cachedPoints.get(pointers[0]))!; - const point1 = (this.cachedPoints.get(pointers[1]))!; + const point0 = this.cachedPoints.get(pointers[0])!; + const point1 = this.cachedPoints.get(pointers[1])!; this.startDistance = Coordinate.distance(point0, point1); this.isMultiTouch_ = true; e.preventDefault(); @@ -653,23 +695,28 @@ export class Gesture { private handlePinch(e: PointerEvent) { const pointers = Array.from(this.cachedPoints.keys()); // Calculate the distance between the two pointers - const point0 = (this.cachedPoints.get(pointers[0]))!; - const point1 = (this.cachedPoints.get(pointers[1]))!; + const point0 = this.cachedPoints.get(pointers[0])!; + const point1 = this.cachedPoints.get(pointers[1])!; const moveDistance = Coordinate.distance(point0, point1); const scale = moveDistance / this.startDistance; if (this.previousScale > 0 && this.previousScale < Infinity) { const gestureScale = scale - this.previousScale; - const delta = gestureScale > 0 ? gestureScale * ZOOM_IN_MULTIPLIER : - gestureScale * ZOOM_OUT_MULTIPLIER; + const delta = + gestureScale > 0 + ? gestureScale * ZOOM_IN_MULTIPLIER + : gestureScale * ZOOM_OUT_MULTIPLIER; if (!this.startWorkspace_) { throw new Error( - 'Cannot handle a pinch because the start workspace ' + - 'is undefined'); + 'Cannot handle a pinch because the start workspace ' + 'is undefined' + ); } const workspace = this.startWorkspace_; const position = browserEvents.mouseToSvg( - e, workspace.getParentSvg(), workspace.getInverseScreenCTM()); + e, + workspace.getParentSvg(), + workspace.getInverseScreenCTM() + ); workspace.zoom(position.x, position.y, delta); } this.previousScale = scale; @@ -700,7 +747,7 @@ export class Gesture { * @returns The current touch point coordinate * @internal */ - getTouchPoint(e: PointerEvent): Coordinate|null { + getTouchPoint(e: PointerEvent): Coordinate | null { if (!this.startWorkspace_) { return null; } @@ -733,7 +780,9 @@ export class Gesture { Touch.longStop(); if (this.bubbleDragger) { this.bubbleDragger.endBubbleDrag( - this.mostRecentEvent, this.currentDragDeltaXY); + this.mostRecentEvent, + this.currentDragDeltaXY + ); } else if (this.blockDragger) { this.blockDragger.endDrag(this.mostRecentEvent, this.currentDragDeltaXY); } else if (this.workspaceDragger) { @@ -777,8 +826,9 @@ export class Gesture { handleWsStart(e: PointerEvent, ws: WorkspaceSvg) { if (this.gestureHasStarted) { throw Error( - 'Tried to call gesture.handleWsStart, ' + - 'but the gesture had already been started.'); + 'Tried to call gesture.handleWsStart, ' + + 'but the gesture had already been started.' + ); } this.setStartWorkspace(ws); this.mostRecentEvent = e; @@ -792,7 +842,8 @@ export class Gesture { */ private fireWorkspaceClick(ws: WorkspaceSvg) { eventUtils.fire( - new (eventUtils.get(eventUtils.CLICK))(null, ws.id, 'workspace')); + new (eventUtils.get(eventUtils.CLICK))(null, ws.id, 'workspace') + ); } /** @@ -805,8 +856,9 @@ export class Gesture { handleFlyoutStart(e: PointerEvent, flyout: IFlyout) { if (this.gestureHasStarted) { throw Error( - 'Tried to call gesture.handleFlyoutStart, ' + - 'but the gesture had already been started.'); + 'Tried to call gesture.handleFlyoutStart, ' + + 'but the gesture had already been started.' + ); } this.setStartFlyout(flyout); this.handleWsStart(e, flyout.getWorkspace()); @@ -822,8 +874,9 @@ export class Gesture { handleBlockStart(e: PointerEvent, block: BlockSvg) { if (this.gestureHasStarted) { throw Error( - 'Tried to call gesture.handleBlockStart, ' + - 'but the gesture had already been started.'); + 'Tried to call gesture.handleBlockStart, ' + + 'but the gesture had already been started.' + ); } this.setStartBlock(block); this.mostRecentEvent = e; @@ -839,8 +892,9 @@ export class Gesture { handleBubbleStart(e: PointerEvent, bubble: IBubble) { if (this.gestureHasStarted) { throw Error( - 'Tried to call gesture.handleBubbleStart, ' + - 'but the gesture had already been started.'); + 'Tried to call gesture.handleBubbleStart, ' + + 'but the gesture had already been started.' + ); } this.setStartBubble(bubble); this.mostRecentEvent = e; @@ -863,8 +917,8 @@ export class Gesture { private doFieldClick() { if (!this.startField) { throw new Error( - 'Cannot do a field click because the start field is ' + - 'undefined'); + 'Cannot do a field click because the start field is ' + 'undefined' + ); } // Only show the editor if the field's editor wasn't already open @@ -882,8 +936,8 @@ export class Gesture { if (this.flyout && this.flyout.autoClose) { if (!this.targetBlock) { throw new Error( - 'Cannot do a block click because the target block is ' + - 'undefined'); + 'Cannot do a block click because the target block is ' + 'undefined' + ); } if (this.targetBlock.isEnabled()) { if (!eventUtils.getGroup()) { @@ -895,12 +949,16 @@ export class Gesture { } else { if (!this.startWorkspace_) { throw new Error( - 'Cannot do a block click because the start workspace ' + - 'is undefined'); + 'Cannot do a block click because the start workspace ' + + 'is undefined' + ); } // Clicks events are on the start block, even if it was a shadow. const event = new (eventUtils.get(eventUtils.CLICK))( - this.startBlock, this.startWorkspace_.id, 'block'); + this.startBlock, + this.startWorkspace_.id, + 'block' + ); eventUtils.fire(event); } this.bringBlockToFront(); @@ -948,8 +1006,9 @@ export class Gesture { setStartField(field: Field) { if (this.gestureHasStarted) { throw Error( - 'Tried to call gesture.setStartField, ' + - 'but the gesture had already been started.'); + 'Tried to call gesture.setStartField, ' + + 'but the gesture had already been started.' + ); } if (!this.startField) { this.startField = field as Field; @@ -1063,10 +1122,14 @@ export class Gesture { * @returns Whether this gesture was a click on a field. */ private isFieldClick(): boolean { - const fieldClickable = - this.startField ? this.startField.isClickable() : false; - return fieldClickable && !this.hasExceededDragRadius && - (!this.flyout || !this.flyout.autoClose); + const fieldClickable = this.startField + ? this.startField.isClickable() + : false; + return ( + fieldClickable && + !this.hasExceededDragRadius && + (!this.flyout || !this.flyout.autoClose) + ); } /** @@ -1077,7 +1140,7 @@ export class Gesture { */ private isWorkspaceClick(): boolean { const onlyTouchedWorkspace = - !this.startBlock && !this.startBubble && !this.startField; + !this.startBlock && !this.startBubble && !this.startField; return onlyTouchedWorkspace && !this.hasExceededDragRadius; } @@ -1092,8 +1155,9 @@ export class Gesture { * @internal */ isDragging(): boolean { - return !!this.workspaceDragger || !!this.blockDragger || - !!this.bubbleDragger; + return ( + !!this.workspaceDragger || !!this.blockDragger || !!this.bubbleDragger + ); } /** @@ -1129,7 +1193,7 @@ export class Gesture { * @returns The dragger that is currently in use or null if no drag is in * progress. */ - getCurrentDragger(): WorkspaceDragger|BubbleDragger|IBlockDragger|null { + getCurrentDragger(): WorkspaceDragger | BubbleDragger | IBlockDragger | null { return this.blockDragger ?? this.workspaceDragger ?? this.bubbleDragger; } @@ -1140,7 +1204,7 @@ export class Gesture { */ static inProgress(): boolean { const workspaces = common.getAllWorkspaces(); - for (let i = 0, workspace; workspace = workspaces[i]; i++) { + for (let i = 0, workspace; (workspace = workspaces[i]); i++) { // Not actually necessarily a WorkspaceSvg, but it doesn't matter b/c // we're just checking if the property exists. Theoretically we would // want to use instanceof, but that causes a circular dependency. diff --git a/core/grid.ts b/core/grid.ts index 079ae5d1959..491ca5df40e 100644 --- a/core/grid.ts +++ b/core/grid.ts @@ -17,7 +17,6 @@ import * as dom from './utils/dom.js'; import {Svg} from './utils/svg.js'; import {GridOptions} from './options.js'; - /** * Class for a workspace's grid. */ @@ -45,7 +44,7 @@ export class Grid { this.line1 = pattern.firstChild as SVGElement; /** The vertical grid line, if it exists. */ - this.line2 = this.line1 && this.line1.nextSibling as SVGElement; + this.line2 = this.line1 && (this.line1.nextSibling as SVGElement); /** Whether blocks should snap to the grid. */ this.snapToGrid = options['snap'] ?? false; @@ -118,8 +117,13 @@ export class Grid { * @param y2 The new y end position of the line (in px). */ private setLineAttributes( - line: SVGElement, width: number, x1: number, x2: number, y1: number, - y2: number) { + line: SVGElement, + width: number, + x1: number, + x2: number, + y1: number, + y2: number + ) { if (line) { line.setAttribute('stroke-width', `${width}`); line.setAttribute('x1', `${x1}`); @@ -151,8 +155,11 @@ export class Grid { * @returns The SVG element for the grid pattern. * @internal */ - static createDom(rnd: string, gridOptions: GridOptions, defs: SVGElement): - SVGElement { + static createDom( + rnd: string, + gridOptions: GridOptions, + defs: SVGElement + ): SVGElement { /* @@ -160,16 +167,23 @@ export class Grid { */ const gridPattern = dom.createSvgElement( - Svg.PATTERN, - {'id': 'blocklyGridPattern' + rnd, 'patternUnits': 'userSpaceOnUse'}, - defs); + Svg.PATTERN, + {'id': 'blocklyGridPattern' + rnd, 'patternUnits': 'userSpaceOnUse'}, + defs + ); // x1, y1, x1, x2 properties will be set later in update. if ((gridOptions['length'] ?? 1) > 0 && (gridOptions['spacing'] ?? 0) > 0) { dom.createSvgElement( - Svg.LINE, {'stroke': gridOptions['colour']}, gridPattern); + Svg.LINE, + {'stroke': gridOptions['colour']}, + gridPattern + ); if (gridOptions['length'] ?? 1 > 1) { dom.createSvgElement( - Svg.LINE, {'stroke': gridOptions['colour']}, gridPattern); + Svg.LINE, + {'stroke': gridOptions['colour']}, + gridPattern + ); } } else { // Edge 16 doesn't handle empty patterns diff --git a/core/icon_old.ts b/core/icon_old.ts index 5d8002165f9..333c3d467bd 100644 --- a/core/icon_old.ts +++ b/core/icon_old.ts @@ -22,14 +22,13 @@ import {Svg} from './utils/svg.js'; import * as svgMath from './utils/svg_math.js'; import * as deprecation from './utils/deprecation.js'; - /** * Class for an icon. */ export abstract class Icon { - protected block_: BlockSvg|null; + protected block_: BlockSvg | null; /** The icon SVG group. */ - iconGroup_: SVGGElement|null = null; + iconGroup_: SVGGElement | null = null; /** Whether this icon gets hidden when the block is collapsed. */ collapseHidden = true; @@ -38,17 +37,20 @@ export abstract class Icon { readonly SIZE = 17; /** Bubble UI (if visible). */ - protected bubble_: Bubble|null = null; + protected bubble_: Bubble | null = null; /** Absolute coordinate of icon's center. */ - protected iconXY_: Coordinate|null = null; + protected iconXY_: Coordinate | null = null; /** @param block The block associated with this icon. */ - constructor(block: BlockSvg|null) { + constructor(block: BlockSvg | null) { if (!block) { deprecation.warn( - 'Calling the Icon constructor with a null block', 'version 9', - 'version 10', 'a non-null block'); + 'Calling the Icon constructor with a null block', + 'version 9', + 'version 10', + 'a non-null block' + ); } this.block_ = block; } @@ -64,8 +66,9 @@ export abstract class Icon { ... */ - this.iconGroup_ = - dom.createSvgElement(Svg.G, {'class': 'blocklyIconGroup'}); + this.iconGroup_ = dom.createSvgElement(Svg.G, { + 'class': 'blocklyIconGroup', + }); if (this.getBlock().isInFlyout) { dom.addClass(this.iconGroup_, 'blocklyIconGroupReadonly'); } @@ -73,7 +76,11 @@ export abstract class Icon { this.getBlock().getSvgRoot().appendChild(this.iconGroup_); browserEvents.conditionalBind( - this.iconGroup_, 'pointerup', this, this.iconClick_); + this.iconGroup_, + 'pointerup', + this, + this.iconClick_ + ); this.updateEditable(); } @@ -82,7 +89,7 @@ export abstract class Icon { if (!this.getBlock().isDeadOrDying()) { dom.removeNode(this.iconGroup_); } - this.setVisible(false); // Dispose of and unlink the bubble. + this.setVisible(false); // Dispose of and unlink the bubble. } /** Add or remove the UI indicating if this icon may be clicked or not. */ @@ -142,8 +149,9 @@ export abstract class Icon { const blockXY = this.getBlock().getRelativeToSurfaceXY(); const iconXY = svgMath.getRelativeXY(this.iconGroup_ as SVGElement); const newXY = new Coordinate( - blockXY.x + iconXY.x + this.SIZE / 2, - blockXY.y + iconXY.y + this.SIZE / 2); + blockXY.x + iconXY.x + this.SIZE / 2, + blockXY.y + iconXY.y + this.SIZE / 2 + ); if (!Coordinate.equals(this.getIconLocation(), newXY)) { this.setIconLocation(newXY); } @@ -154,7 +162,7 @@ export abstract class Icon { * * @returns Object with x and y properties in workspace coordinates. */ - getIconLocation(): Coordinate|null { + getIconLocation(): Coordinate | null { return this.iconXY_; } diff --git a/core/icons/icon.ts b/core/icons/icon.ts index f85195bf49a..4973b2e5162 100644 --- a/core/icons/icon.ts +++ b/core/icons/icon.ts @@ -24,7 +24,7 @@ export abstract class Icon implements IIcon { protected workspaceLocation: Coordinate = new Coordinate(0, 0); /** The root svg element visually representing this icon. */ - protected svgRoot: SVGGElement|null = null; + protected svgRoot: SVGGElement | null = null; constructor(protected sourceBlock: Block) {} @@ -33,13 +33,17 @@ export abstract class Icon implements IIcon { } initView(pointerdownListener: (e: PointerEvent) => void): void { - if (this.svgRoot) return; // The icon has already been initialized. + if (this.svgRoot) return; // The icon has already been initialized. const svgBlock = this.sourceBlock as BlockSvg; this.svgRoot = dom.createSvgElement(Svg.G, {'class': 'blocklyIconGroup'}); svgBlock.getSvgRoot().appendChild(this.svgRoot); browserEvents.conditionalBind( - this.svgRoot, 'pointerdown', this, pointerdownListener); + this.svgRoot, + 'pointerdown', + this, + pointerdownListener + ); } dispose(): void {} @@ -59,8 +63,9 @@ export abstract class Icon implements IIcon { updateCollapsed(): void { if (!this.svgRoot) { throw new Error( - 'Attempt to update the collapsed-ness of an icon before its ' + - 'view has been initialized.'); + 'Attempt to update the collapsed-ness of an icon before its ' + + 'view has been initialized.' + ); } if (this.sourceBlock.isCollapsed()) { this.svgRoot.style.display = 'none'; diff --git a/core/inject.ts b/core/inject.ts index 1fd3d58d687..21ad9b1b0a6 100644 --- a/core/inject.ts +++ b/core/inject.ts @@ -29,7 +29,6 @@ import * as WidgetDiv from './widgetdiv.js'; import {WorkspaceDragSurfaceSvg} from './workspace_drag_surface_svg.js'; import {WorkspaceSvg} from './workspace_svg.js'; - /** * Inject a Blockly editor into the specified container element (usually a div). * @@ -38,11 +37,13 @@ import {WorkspaceSvg} from './workspace_svg.js'; * @returns Newly created main workspace. */ export function inject( - container: Element|string, opt_options?: BlocklyOptions): WorkspaceSvg { - let containerElement: Element|null = null; + container: Element | string, + opt_options?: BlocklyOptions +): WorkspaceSvg { + let containerElement: Element | null = null; if (typeof container === 'string') { containerElement = - document.getElementById(container) || document.querySelector(container); + document.getElementById(container) || document.querySelector(container); } else { containerElement = container; } @@ -50,8 +51,8 @@ export function inject( if (!document.contains(containerElement)) { throw Error('Error: container is not in current document'); } - const options = new Options(opt_options || {} as BlocklyOptions); - const subContainer = (document.createElement('div')); + const options = new Options(opt_options || ({} as BlocklyOptions)); + const subContainer = document.createElement('div'); subContainer.className = 'injectionDiv'; subContainer.tabIndex = 0; aria.setState(subContainer, aria.State.LABEL, Msg['WORKSPACE_ARIA_LABEL']); @@ -65,8 +66,12 @@ export function inject( const workspaceDragSurface = new WorkspaceDragSurfaceSvg(subContainer); - const workspace = - createMainWorkspace(svg, options, blockDragSurface, workspaceDragSurface); + const workspace = createMainWorkspace( + svg, + options, + blockDragSurface, + workspaceDragSurface + ); init(workspace); @@ -76,7 +81,7 @@ export function inject( common.svgResize(workspace); - subContainer.addEventListener('focusin', function() { + subContainer.addEventListener('focusin', function () { common.setMainWorkspace(workspace); }); @@ -111,15 +116,17 @@ function createDom(container: Element, options: Options): SVGElement { */ const svg = dom.createSvgElement( - Svg.SVG, { - 'xmlns': dom.SVG_NS, - 'xmlns:html': dom.HTML_NS, - 'xmlns:xlink': dom.XLINK_NS, - 'version': '1.1', - 'class': 'blocklySvg', - 'tabindex': '0', - }, - container); + Svg.SVG, + { + 'xmlns': dom.SVG_NS, + 'xmlns:html': dom.HTML_NS, + 'xmlns:xlink': dom.XLINK_NS, + 'version': '1.1', + 'class': 'blocklySvg', + 'tabindex': '0', + }, + container + ); /* ... filters go here ... @@ -145,11 +152,17 @@ function createDom(container: Element, options: Options): SVGElement { * @returns Newly created main workspace. */ function createMainWorkspace( - svg: SVGElement, options: Options, blockDragSurface: BlockDragSurfaceSvg, - workspaceDragSurface: WorkspaceDragSurfaceSvg): WorkspaceSvg { + svg: SVGElement, + options: Options, + blockDragSurface: BlockDragSurfaceSvg, + workspaceDragSurface: WorkspaceDragSurfaceSvg +): WorkspaceSvg { options.parentWorkspace = null; - const mainWorkspace = - new WorkspaceSvg(options, blockDragSurface, workspaceDragSurface); + const mainWorkspace = new WorkspaceSvg( + options, + blockDragSurface, + workspaceDragSurface + ); const wsOptions = mainWorkspace.options; mainWorkspace.scale = wsOptions.zoomOptions.startScale; svg.appendChild(mainWorkspace.createDom('blocklyMainBackground')); @@ -177,14 +190,16 @@ function createMainWorkspace( mainWorkspace.addZoomControls(); } // Register the workspace svg as a UI component. - mainWorkspace.getThemeManager().subscribe( - svg, 'workspaceBackgroundColour', 'background-color'); + mainWorkspace + .getThemeManager() + .subscribe(svg, 'workspaceBackgroundColour', 'background-color'); // A null translation will also apply the correct initial scale. mainWorkspace.translate(0, 0); mainWorkspace.addChangeListener( - bumpObjects.bumpIntoBoundsHandler(mainWorkspace)); + bumpObjects.bumpIntoBoundsHandler(mainWorkspace) + ); // The SVG is now fully assembled. common.svgResize(mainWorkspace); @@ -205,23 +220,31 @@ function init(mainWorkspace: WorkspaceSvg) { // Suppress the browser's context menu. browserEvents.conditionalBind( - svg.parentNode as Element, 'contextmenu', null, function(e: Event) { - if (!browserEvents.isTargetInput(e)) { - e.preventDefault(); - } - }); - - const workspaceResizeHandler = - browserEvents.conditionalBind(window, 'resize', null, function() { - // Don't hide all the chaff. Leave the dropdown and widget divs open if - // possible. - Tooltip.hide(); - mainWorkspace.hideComponents(true); - dropDownDiv.repositionForWindowResize(); - WidgetDiv.repositionForWindowResize(); - common.svgResize(mainWorkspace); - bumpObjects.bumpTopObjectsIntoBounds(mainWorkspace); - }); + svg.parentNode as Element, + 'contextmenu', + null, + function (e: Event) { + if (!browserEvents.isTargetInput(e)) { + e.preventDefault(); + } + } + ); + + const workspaceResizeHandler = browserEvents.conditionalBind( + window, + 'resize', + null, + function () { + // Don't hide all the chaff. Leave the dropdown and widget divs open if + // possible. + Tooltip.hide(); + mainWorkspace.hideComponents(true); + dropDownDiv.repositionForWindowResize(); + WidgetDiv.repositionForWindowResize(); + common.svgResize(mainWorkspace); + bumpObjects.bumpTopObjectsIntoBounds(mainWorkspace); + } + ); mainWorkspace.setResizeHandlerWrapper(workspaceResizeHandler); bindDocumentEvents(); @@ -249,13 +272,18 @@ function init(mainWorkspace: WorkspaceSvg) { } if (options.moveOptions && options.moveOptions.scrollbars) { - const horizontalScroll = options.moveOptions.scrollbars === true || - !!options.moveOptions.scrollbars.horizontal; - const verticalScroll = options.moveOptions.scrollbars === true || - !!options.moveOptions.scrollbars.vertical; + const horizontalScroll = + options.moveOptions.scrollbars === true || + !!options.moveOptions.scrollbars.horizontal; + const verticalScroll = + options.moveOptions.scrollbars === true || + !!options.moveOptions.scrollbars.vertical; mainWorkspace.scrollbar = new ScrollbarPair( - mainWorkspace, horizontalScroll, verticalScroll, - 'blocklyMainWorkspaceScrollbar'); + mainWorkspace, + horizontalScroll, + verticalScroll, + 'blocklyMainWorkspaceScrollbar' + ); mainWorkspace.scrollbar.resize(); } else { mainWorkspace.setMetrics({x: 0.5, y: 0.5}); @@ -281,8 +309,10 @@ function onKeyDown(e: KeyboardEvent) { return; } - if (browserEvents.isTargetInput(e) || - mainWorkspace.rendered && !mainWorkspace.isVisible()) { + if ( + browserEvents.isTargetInput(e) || + (mainWorkspace.rendered && !mainWorkspace.isVisible()) + ) { // When focused on an HTML text input widget, don't trap any keys. // Ignore keypresses on rendered workspaces that have been explicitly // hidden. @@ -309,9 +339,9 @@ let documentEventsBound = false; */ function bindDocumentEvents() { if (!documentEventsBound) { - browserEvents.conditionalBind(document, 'scroll', null, function() { + browserEvents.conditionalBind(document, 'scroll', null, function () { const workspaces = common.getAllWorkspaces(); - for (let i = 0, workspace; workspace = workspaces[i]; i++) { + for (let i = 0, workspace; (workspace = workspaces[i]); i++) { if (workspace instanceof WorkspaceSvg) { workspace.updateInverseScreenCTM(); } @@ -325,10 +355,14 @@ function bindDocumentEvents() { // Some iPad versions don't fire resize after portrait to landscape change. if (userAgent.IPAD) { browserEvents.conditionalBind( - window, 'orientationchange', document, function() { - // TODO (#397): Fix for multiple Blockly workspaces. - common.svgResize(common.getMainWorkspace() as WorkspaceSvg); - }); + window, + 'orientationchange', + document, + function () { + // TODO (#397): Fix for multiple Blockly workspaces. + common.svgResize(common.getMainWorkspace() as WorkspaceSvg); + } + ); } } documentEventsBound = true; @@ -343,26 +377,29 @@ function bindDocumentEvents() { function loadSounds(pathToMedia: string, workspace: WorkspaceSvg) { const audioMgr = workspace.getAudioManager(); audioMgr.load( - [ - pathToMedia + 'click.mp3', - pathToMedia + 'click.wav', - pathToMedia + 'click.ogg', - ], - 'click'); + [ + pathToMedia + 'click.mp3', + pathToMedia + 'click.wav', + pathToMedia + 'click.ogg', + ], + 'click' + ); audioMgr.load( - [ - pathToMedia + 'disconnect.wav', - pathToMedia + 'disconnect.mp3', - pathToMedia + 'disconnect.ogg', - ], - 'disconnect'); + [ + pathToMedia + 'disconnect.wav', + pathToMedia + 'disconnect.mp3', + pathToMedia + 'disconnect.ogg', + ], + 'disconnect' + ); audioMgr.load( - [ - pathToMedia + 'delete.mp3', - pathToMedia + 'delete.ogg', - pathToMedia + 'delete.wav', - ], - 'delete'); + [ + pathToMedia + 'delete.mp3', + pathToMedia + 'delete.ogg', + pathToMedia + 'delete.wav', + ], + 'delete' + ); // Bind temporary hooks that preload the sounds. const soundBinds: browserEvents.Data[] = []; @@ -385,8 +422,22 @@ function loadSounds(pathToMedia: string, workspace: WorkspaceSvg) { // happens on a click, not a drag, so that's not necessary. // Android ignores any sound not loaded as a result of a user action. - soundBinds.push(browserEvents.conditionalBind( - document, 'pointermove', null, unbindSounds, true)); - soundBinds.push(browserEvents.conditionalBind( - document, 'touchstart', null, unbindSounds, true)); + soundBinds.push( + browserEvents.conditionalBind( + document, + 'pointermove', + null, + unbindSounds, + true + ) + ); + soundBinds.push( + browserEvents.conditionalBind( + document, + 'touchstart', + null, + unbindSounds, + true + ) + ); } diff --git a/core/inputs/dummy_input.ts b/core/inputs/dummy_input.ts index 558fc6d6f2e..7abfbea93be 100644 --- a/core/inputs/dummy_input.ts +++ b/core/inputs/dummy_input.ts @@ -8,7 +8,6 @@ import type {Block} from '../block.js'; import {Input} from './input.js'; import {inputTypes} from './input_types.js'; - /** Represents an input on a block with no connection. */ export class DummyInput extends Input { readonly type = inputTypes.DUMMY; diff --git a/core/inputs/input.ts b/core/inputs/input.ts index 990d644ff7b..77112c22c50 100644 --- a/core/inputs/input.ts +++ b/core/inputs/input.ts @@ -23,7 +23,6 @@ import * as fieldRegistry from '../field_registry.js'; import type {RenderedConnection} from '../rendered_connection.js'; import {inputTypes} from './input_types.js'; - /** * Class for an input with an optional field. */ @@ -46,8 +45,10 @@ export class Input { * optionally construct a connection. */ constructor( - public name: string, private sourceBlock: Block, - public connection: Connection|null) {} + public name: string, + private sourceBlock: Block, + public connection: Connection | null + ) {} /** * Get the source block for this input. @@ -67,7 +68,7 @@ export class Input { * field again. Should be unique to the host block. * @returns The input being append to (to allow chaining). */ - appendField(field: string|Field, opt_name?: string): Input { + appendField(field: string | Field, opt_name?: string): Input { this.insertFieldAt(this.fieldRow.length, field, opt_name); return this; } @@ -82,8 +83,11 @@ export class Input { * field again. Should be unique to the host block. * @returns The index following the last inserted field. */ - insertFieldAt(index: number, field: string|Field, opt_name?: string): - number { + insertFieldAt( + index: number, + field: string | Field, + opt_name?: string + ): number { if (index < 0 || index > this.fieldRow.length) { throw Error('index ' + index + ' out of bounds.'); } @@ -139,7 +143,7 @@ export class Input { * @throws {Error} if the field is not present and opt_quiet is false. */ removeField(name: string, opt_quiet?: boolean): boolean { - for (let i = 0, field; field = this.fieldRow[i]; i++) { + for (let i = 0, field; (field = this.fieldRow[i]); i++) { if (field.name === name) { field.dispose(); this.fieldRow.splice(i, 1); @@ -184,7 +188,7 @@ export class Input { } this.visible = visible; - for (let y = 0, field; field = this.fieldRow[y]; y++) { + for (let y = 0, field; (field = this.fieldRow[y]); y++) { field.setVisible(visible); } if (this.connection) { @@ -209,7 +213,7 @@ export class Input { * @internal */ markDirty() { - for (let y = 0, field; field = this.fieldRow[y]; y++) { + for (let y = 0, field; (field = this.fieldRow[y]); y++) { field.markDirty(); } } @@ -221,7 +225,7 @@ export class Input { * types are compatible. * @returns The input being modified (to allow chaining). */ - setCheck(check: string|string[]|null): Input { + setCheck(check: string | string[] | null): Input { if (!this.connection) { throw Error('This input does not have a connection.'); } @@ -251,7 +255,7 @@ export class Input { * @param shadow DOM representation of a block or null. * @returns The input being modified (to allow chaining). */ - setShadowDom(shadow: Element|null): Input { + setShadowDom(shadow: Element | null): Input { if (!this.connection) { throw Error('This input does not have a connection.'); } @@ -264,7 +268,7 @@ export class Input { * * @returns Shadow DOM representation of a block or null. */ - getShadowDom(): Element|null { + getShadowDom(): Element | null { if (!this.connection) { throw Error('This input does not have a connection.'); } @@ -274,7 +278,7 @@ export class Input { /** Initialize the fields on this input. */ init() { if (!this.sourceBlock.workspace.rendered) { - return; // Headless blocks don't need fields initialized. + return; // Headless blocks don't need fields initialized. } for (let i = 0; i < this.fieldRow.length; i++) { this.fieldRow[i].init(); @@ -287,7 +291,7 @@ export class Input { * @suppress {checkTypes} */ dispose() { - for (let i = 0, field; field = this.fieldRow[i]; i++) { + for (let i = 0, field; (field = this.fieldRow[i]); i++) { field.dispose(); } if (this.connection) { diff --git a/core/inputs/input_types.ts b/core/inputs/input_types.ts index a7d30131e34..ff768deb266 100644 --- a/core/inputs/input_types.ts +++ b/core/inputs/input_types.ts @@ -9,7 +9,6 @@ goog.declareModuleId('Blockly.inputTypes'); import {ConnectionType} from '../connection_type.js'; - /** * Enum for the type of a connection or input. */ diff --git a/core/inputs/statement_input.ts b/core/inputs/statement_input.ts index fbfb5d7f729..bd7ea1714b5 100644 --- a/core/inputs/statement_input.ts +++ b/core/inputs/statement_input.ts @@ -9,7 +9,6 @@ import type {Connection} from '../connection.js'; import {Input} from './input.js'; import {inputTypes} from './input_types.js'; - /** Represents an input on a block with a statement connection. */ export class StatementInput extends Input { readonly type = inputTypes.STATEMENT; @@ -21,7 +20,10 @@ export class StatementInput extends Input { * @param connection The statement connection for this input. */ constructor( - public name: string, block: Block, public connection: Connection) { + public name: string, + block: Block, + public connection: Connection + ) { // Errors are maintained for people not using typescript. if (!name) throw new Error('Statement inputs must have a non-empty name'); if (!connection) throw new Error('Value inputs must have a connection'); diff --git a/core/inputs/value_input.ts b/core/inputs/value_input.ts index 905342441b4..1abe4d30345 100644 --- a/core/inputs/value_input.ts +++ b/core/inputs/value_input.ts @@ -9,7 +9,6 @@ import type {Connection} from '../connection.js'; import {Input} from './input.js'; import {inputTypes} from './input_types.js'; - /** Represents an input on a block with a value connection. */ export class ValueInput extends Input { readonly type = inputTypes.VALUE; @@ -21,7 +20,10 @@ export class ValueInput extends Input { * @param connection The value connection for this input. */ constructor( - public name: string, block: Block, public connection: Connection) { + public name: string, + block: Block, + public connection: Connection + ) { // Errors are maintained for people not using typescript. if (!name) throw new Error('Value inputs must have a non-empty name'); if (!connection) throw new Error('Value inputs must have a connection'); diff --git a/core/insertion_marker_manager.ts b/core/insertion_marker_manager.ts index 6743e2c2e47..016d76b90e3 100644 --- a/core/insertion_marker_manager.ts +++ b/core/insertion_marker_manager.ts @@ -26,7 +26,6 @@ import type {RenderedConnection} from './rendered_connection.js'; import type {Coordinate} from './utils/coordinate.js'; import type {WorkspaceSvg} from './workspace_svg.js'; - /** Represents a nearby valid connection. */ interface CandidateConnection { /** @@ -47,10 +46,11 @@ interface CandidateConnection { * An error message to throw if the block created by createMarkerBlock_ is * missing any components. */ -const DUPLICATE_BLOCK_ERROR = 'The insertion marker ' + - 'manager tried to create a marker but the result is missing %1. If ' + - 'you are using a mutator, make sure your domToMutation method is ' + - 'properly defined.'; +const DUPLICATE_BLOCK_ERROR = + 'The insertion marker ' + + 'manager tried to create a marker but the result is missing %1. If ' + + 'you are using a mutator, make sure your domToMutation method is ' + + 'properly defined.'; /** * Class that controls updates to connections during drags. It is primarily @@ -75,14 +75,14 @@ export class InsertionMarkerManager { * first block. * Set in initAvailableConnections, if at all. */ - private lastOnStack: RenderedConnection|null = null; + private lastOnStack: RenderedConnection | null = null; /** * The insertion marker corresponding to the last block in the stack, if * that's not the same as the first block in the stack. * Set in initAvailableConnections, if at all */ - private lastMarker: BlockSvg|null = null; + private lastMarker: BlockSvg | null = null; /** * The insertion marker that shows up between blocks to show where a block @@ -94,7 +94,7 @@ export class InsertionMarkerManager { * Information about the connection that would be made if the dragging block * were released immediately. Updated on every mouse move. */ - private activeCandidate: CandidateConnection|null = null; + private activeCandidate: CandidateConnection | null = null; /** * Whether the block would be deleted if it were dropped immediately. @@ -108,13 +108,13 @@ export class InsertionMarkerManager { * Connection on the insertion marker block that corresponds to * the active candidate's local connection on the currently dragged block. */ - private markerConnection: RenderedConnection|null = null; + private markerConnection: RenderedConnection | null = null; /** The block that currently has an input being highlighted, or null. */ - private highlightedBlock: BlockSvg|null = null; + private highlightedBlock: BlockSvg | null = null; /** The block being faded to indicate replacement, or null. */ - private fadedBlock: BlockSvg|null = null; + private fadedBlock: BlockSvg | null = null; /** * The connections on the dragging blocks that are available to connect to @@ -135,8 +135,9 @@ export class InsertionMarkerManager { this.availableConnections = this.initAvailableConnections(); if (this.lastOnStack) { - this.lastMarker = - this.createMarkerBlock(this.lastOnStack.getSourceBlock()); + this.lastMarker = this.createMarkerBlock( + this.lastOnStack.getSourceBlock() + ); } } @@ -206,13 +207,13 @@ export class InsertionMarkerManager { * @param dragTarget The drag target that the block is currently over. * @internal */ - update(dxy: Coordinate, dragTarget: IDragTarget|null) { + update(dxy: Coordinate, dragTarget: IDragTarget | null) { const newCandidate = this.getCandidate(dxy); this.wouldDeleteBlock = this.shouldDelete(!!newCandidate, dragTarget); const shouldUpdate = - this.wouldDeleteBlock || this.shouldUpdatePreviews(newCandidate, dxy); + this.wouldDeleteBlock || this.shouldUpdatePreviews(newCandidate, dxy); if (shouldUpdate) { // Don't fire events for insertion marker creation or movement. @@ -254,7 +255,7 @@ export class InsertionMarkerManager { for (let i = 0; i < sourceBlock.inputList.length; i++) { const sourceInput = sourceBlock.inputList[i]; if (sourceInput.name === constants.COLLAPSED_INPUT_NAME) { - continue; // Ignore the collapsed input. + continue; // Ignore the collapsed input. } const resultInput = result.inputList[i]; if (!resultInput) { @@ -309,7 +310,9 @@ export class InsertionMarkerManager { * @returns Whether the preview should be updated. */ private shouldUpdatePreviews( - newCandidate: CandidateConnection|null, dxy: Coordinate): boolean { + newCandidate: CandidateConnection | null, + dxy: Coordinate + ): boolean { // Only need to update if we were showing a preview before. if (!newCandidate) return !!this.activeCandidate; @@ -319,8 +322,10 @@ export class InsertionMarkerManager { // We're already showing an insertion marker. // Decide whether the new connection has higher priority. const {local: activeLocal, closest: activeClosest} = this.activeCandidate; - if (activeClosest === newCandidate.closest && - activeLocal === newCandidate.local) { + if ( + activeClosest === newCandidate.closest && + activeLocal === newCandidate.local + ) { // The connection was the same as the current connection. return false; } @@ -330,7 +335,8 @@ export class InsertionMarkerManager { const curDistance = Math.sqrt(xDiff * xDiff + yDiff * yDiff); // Slightly prefer the existing preview over a new preview. return ( - newCandidate.radius < curDistance - config.currentConnectionPreference); + newCandidate.radius < curDistance - config.currentConnectionPreference + ); } /** @@ -341,7 +347,7 @@ export class InsertionMarkerManager { * @returns An object containing a local connection, a closest connection, and * a radius. */ - private getCandidate(dxy: Coordinate): CandidateConnection|null { + private getCandidate(dxy: Coordinate): CandidateConnection | null { // It's possible that a block has added or removed connections during a // drag, (e.g. in a drag/move event handler), so let's update the available // connections. Note that this will be called on every move while dragging, @@ -383,8 +389,9 @@ export class InsertionMarkerManager { // insertion marker is created, which could cause the connection became out // of range. By increasing radiusConnection when a connection already // exists, we never "lose" the connection from the offset. - return this.activeCandidate ? config.connectingSnapRadius : - config.snapRadius; + return this.activeCandidate + ? config.connectingSnapRadius + : config.snapRadius; } /** @@ -395,15 +402,21 @@ export class InsertionMarkerManager { * @param dragTarget The drag target that the block is currently over. * @returns Whether dropping the block immediately would delete the block. */ - private shouldDelete(newCandidate: boolean, dragTarget: IDragTarget|null): - boolean { + private shouldDelete( + newCandidate: boolean, + dragTarget: IDragTarget | null + ): boolean { if (dragTarget) { const componentManager = this.workspace.getComponentManager(); const isDeleteArea = componentManager.hasCapability( - dragTarget.id, ComponentManager.Capability.DELETE_AREA); + dragTarget.id, + ComponentManager.Capability.DELETE_AREA + ); if (isDeleteArea) { - return (dragTarget as IDeleteArea) - .wouldDelete(this.topBlock, newCandidate); + return (dragTarget as IDeleteArea).wouldDelete( + this.topBlock, + newCandidate + ); } } return false; @@ -417,16 +430,18 @@ export class InsertionMarkerManager { * @param newCandidate A new candidate connection that may replace the current * best candidate. */ - private maybeShowPreview(newCandidate: CandidateConnection|null) { - if (this.wouldDeleteBlock) return; // Nope, don't add a marker. - if (!newCandidate) return; // Nothing to connect to. + private maybeShowPreview(newCandidate: CandidateConnection | null) { + if (this.wouldDeleteBlock) return; // Nope, don't add a marker. + if (!newCandidate) return; // Nothing to connect to. const closest = newCandidate.closest; // Something went wrong and we're trying to connect to an invalid // connection. - if (closest === this.activeCandidate?.closest || - closest.getSourceBlock().isInsertionMarker()) { + if ( + closest === this.activeCandidate?.closest || + closest.getSourceBlock().isInsertionMarker() + ) { console.log('Trying to connect to an insertion marker'); return; } @@ -445,7 +460,10 @@ export class InsertionMarkerManager { private showPreview(activeCandidate: CandidateConnection) { const renderer = this.workspace.getRenderer(); const method = renderer.getConnectionPreviewMethod( - activeCandidate.closest, activeCandidate.local, this.topBlock); + activeCandidate.closest, + activeCandidate.local, + this.topBlock + ); switch (method) { case InsertionMarkerManager.PREVIEW_TYPE.INPUT_OUTLINE: @@ -474,7 +492,7 @@ export class InsertionMarkerManager { * @param newCandidate A new candidate connection that may replace the current * best candidate. */ - private maybeHidePreview(newCandidate: CandidateConnection|null) { + private maybeHidePreview(newCandidate: CandidateConnection | null) { // If there's no new preview, remove the old one but don't bother deleting // it. We might need it later, and this saves disposing of it and recreating // it. @@ -483,14 +501,14 @@ export class InsertionMarkerManager { } else { if (this.activeCandidate) { const closestChanged = - this.activeCandidate.closest !== newCandidate.closest; + this.activeCandidate.closest !== newCandidate.closest; const localChanged = this.activeCandidate.local !== newCandidate.local; // If there's a new preview and there was a preview before, and either // connection has changed, remove the old preview. // Also hide if we had a preview before but now we're going to delete // instead. - if ((closestChanged || localChanged || this.wouldDeleteBlock)) { + if (closestChanged || localChanged || this.wouldDeleteBlock) { this.hidePreview(); } } @@ -507,8 +525,11 @@ export class InsertionMarkerManager { */ private hidePreview() { const closest = this.activeCandidate?.closest; - if (closest && closest.targetBlock() && - this.workspace.getRenderer().shouldHighlightConnection(closest)) { + if ( + closest && + closest.targetBlock() && + this.workspace.getRenderer().shouldHighlightConnection(closest) + ) { closest.unhighlight(); } this.hideReplacementFade(); @@ -530,13 +551,16 @@ export class InsertionMarkerManager { let insertionMarker = isLastInStack ? this.lastMarker : this.firstMarker; if (!insertionMarker) { throw new Error( - 'Cannot show the insertion marker because there is no insertion ' + - 'marker block'); + 'Cannot show the insertion marker because there is no insertion ' + + 'marker block' + ); } let imConn; try { - imConn = - insertionMarker.getMatchingConnection(local.getSourceBlock(), local); + imConn = insertionMarker.getMatchingConnection( + local.getSourceBlock(), + local + ); } catch (e) { // It's possible that the number of connections on the local block has // changed since the insertion marker was originally created. Let's @@ -546,8 +570,9 @@ export class InsertionMarkerManager { // might be too slow, so we only do it if necessary. if (isLastInStack && this.lastOnStack) { this.disposeInsertionMarker(this.lastMarker); - this.lastMarker = - this.createMarkerBlock(this.lastOnStack.getSourceBlock()); + this.lastMarker = this.createMarkerBlock( + this.lastOnStack.getSourceBlock() + ); insertionMarker = this.lastMarker; } else { this.disposeInsertionMarker(this.firstMarker); @@ -557,23 +582,28 @@ export class InsertionMarkerManager { if (!insertionMarker) { throw new Error( - 'Cannot show the insertion marker because there is no insertion ' + - 'marker block'); + 'Cannot show the insertion marker because there is no insertion ' + + 'marker block' + ); } - imConn = - insertionMarker.getMatchingConnection(local.getSourceBlock(), local); + imConn = insertionMarker.getMatchingConnection( + local.getSourceBlock(), + local + ); } if (!imConn) { throw new Error( - 'Cannot show the insertion marker because there is no ' + - 'associated connection'); + 'Cannot show the insertion marker because there is no ' + + 'associated connection' + ); } if (imConn === this.markerConnection) { throw new Error( - 'Made it to showInsertionMarker_ even though the marker isn\'t ' + - 'changing'); + "Made it to showInsertionMarker_ even though the marker isn't " + + 'changing' + ); } // Render disconnected from everything else so that we have a valid @@ -617,8 +647,9 @@ export class InsertionMarkerManager { if (markerConn.targetConnection) { throw Error( - 'markerConnection still connected at the end of ' + - 'disconnectInsertionMarker'); + 'markerConnection still connected at the end of ' + + 'disconnectInsertionMarker' + ); } this.markerConnection = null; @@ -646,11 +677,14 @@ export class InsertionMarkerManager { if (!this.activeCandidate) { throw new Error( - 'Cannot hide the insertion marker outline because ' + - 'there is no active candidate'); + 'Cannot hide the insertion marker outline because ' + + 'there is no active candidate' + ); } this.highlightedBlock.highlightShapeForInput( - this.activeCandidate.closest, false); + this.activeCandidate.closest, + false + ); this.highlightedBlock = null; } @@ -665,8 +699,9 @@ export class InsertionMarkerManager { this.fadedBlock = activeCandidate.closest.targetBlock(); if (!this.fadedBlock) { throw new Error( - 'Cannot show the replacement fade because the ' + - 'closest connection does not have a target block'); + 'Cannot show the replacement fade because the ' + + 'closest connection does not have a target block' + ); } this.fadedBlock.fadeForReplacement(true); } @@ -702,7 +737,7 @@ export class InsertionMarkerManager { /** * Safely disposes of an insertion marker. */ - private disposeInsertionMarker(marker: BlockSvg|null) { + private disposeInsertionMarker(marker: BlockSvg | null) { if (marker) { eventUtils.disable(); try { diff --git a/core/interfaces/i_ast_node_location.ts b/core/interfaces/i_ast_node_location.ts index 36c5e7eecc4..72f3ce2c50b 100644 --- a/core/interfaces/i_ast_node_location.ts +++ b/core/interfaces/i_ast_node_location.ts @@ -7,7 +7,6 @@ import * as goog from '../../closure/goog/goog.js'; goog.declareModuleId('Blockly.IASTNodeLocation'); - /** * An AST node location interface. */ diff --git a/core/interfaces/i_ast_node_location_svg.ts b/core/interfaces/i_ast_node_location_svg.ts index 0617b5d8eff..263c9d54dc1 100644 --- a/core/interfaces/i_ast_node_location_svg.ts +++ b/core/interfaces/i_ast_node_location_svg.ts @@ -9,7 +9,6 @@ goog.declareModuleId('Blockly.IASTNodeLocationSvg'); import type {IASTNodeLocation} from './i_ast_node_location.js'; - /** * An AST node location SVG interface. */ @@ -19,12 +18,12 @@ export interface IASTNodeLocationSvg extends IASTNodeLocation { * * @param markerSvg The SVG root of the marker to be added to the SVG group. */ - setMarkerSvg(markerSvg: SVGElement|null): void; + setMarkerSvg(markerSvg: SVGElement | null): void; /** * Add the cursor SVG to this node's SVG group. * * @param cursorSvg The SVG root of the cursor to be added to the SVG group. */ - setCursorSvg(cursorSvg: SVGElement|null): void; + setCursorSvg(cursorSvg: SVGElement | null): void; } diff --git a/core/interfaces/i_ast_node_location_with_block.ts b/core/interfaces/i_ast_node_location_with_block.ts index 56e12b3eecc..a4a7a8880e5 100644 --- a/core/interfaces/i_ast_node_location_with_block.ts +++ b/core/interfaces/i_ast_node_location_with_block.ts @@ -10,7 +10,6 @@ goog.declareModuleId('Blockly.IASTNodeLocationWithBlock'); import type {IASTNodeLocation} from './i_ast_node_location.js'; import type {Block} from '../block.js'; - /** * An AST node location that has an associated block. */ @@ -20,5 +19,5 @@ export interface IASTNodeLocationWithBlock extends IASTNodeLocation { * * @returns The source block. */ - getSourceBlock(): Block|null; + getSourceBlock(): Block | null; } diff --git a/core/interfaces/i_autohideable.ts b/core/interfaces/i_autohideable.ts index d7abcad1e25..f5e35101081 100644 --- a/core/interfaces/i_autohideable.ts +++ b/core/interfaces/i_autohideable.ts @@ -9,7 +9,6 @@ goog.declareModuleId('Blockly.IAutoHideable'); import type {IComponent} from './i_component.js'; - /** * Interface for a component that can be automatically hidden. */ diff --git a/core/interfaces/i_bubble.ts b/core/interfaces/i_bubble.ts index d8a1b686a12..04f99e0e649 100644 --- a/core/interfaces/i_bubble.ts +++ b/core/interfaces/i_bubble.ts @@ -12,7 +12,6 @@ goog.declareModuleId('Blockly.IBubble'); import type {IContextMenu} from './i_contextmenu.js'; import type {IDraggable} from './i_draggable.js'; - /** * A bubble interface. */ @@ -56,8 +55,10 @@ export interface IBubble extends IDraggable, IContextMenu { * or null if no drag surface is in use. * @param newLoc The location to translate to, in workspace coordinates. */ - moveDuringDrag(dragSurface: BlockDragSurfaceSvg|null, newLoc: Coordinate): - void; + moveDuringDrag( + dragSurface: BlockDragSurfaceSvg | null, + newLoc: Coordinate + ): void; /** * Move the bubble to the specified location in workspace coordinates. diff --git a/core/interfaces/i_collapsible_toolbox_item.ts b/core/interfaces/i_collapsible_toolbox_item.ts index 8e499e276e8..bc3a74b4a70 100644 --- a/core/interfaces/i_collapsible_toolbox_item.ts +++ b/core/interfaces/i_collapsible_toolbox_item.ts @@ -10,7 +10,6 @@ goog.declareModuleId('Blockly.ICollapsibleToolboxItem'); import type {ISelectableToolboxItem} from './i_selectable_toolbox_item.js'; import type {IToolboxItem} from './i_toolbox_item.js'; - /** * Interface for an item in the toolbox that can be collapsed. */ diff --git a/core/interfaces/i_component.ts b/core/interfaces/i_component.ts index 3a55702fd8f..8f3e3aede7f 100644 --- a/core/interfaces/i_component.ts +++ b/core/interfaces/i_component.ts @@ -7,7 +7,6 @@ import * as goog from '../../closure/goog/goog.js'; goog.declareModuleId('Blockly.IComponent'); - /** * The interface for a workspace component that can be registered with the * ComponentManager. diff --git a/core/interfaces/i_connection_checker.ts b/core/interfaces/i_connection_checker.ts index 4384511cc21..7264895df5f 100644 --- a/core/interfaces/i_connection_checker.ts +++ b/core/interfaces/i_connection_checker.ts @@ -25,8 +25,11 @@ export interface IConnectionChecker { * @returns Whether the connection is legal. */ canConnect( - a: Connection|null, b: Connection|null, isDragging: boolean, - opt_distance?: number): boolean; + a: Connection | null, + b: Connection | null, + isDragging: boolean, + opt_distance?: number + ): boolean; /** * Checks whether the current connection can connect with the target @@ -41,8 +44,11 @@ export interface IConnectionChecker { * otherwise. */ canConnectWithReason( - a: Connection|null, b: Connection|null, isDragging: boolean, - opt_distance?: number): number; + a: Connection | null, + b: Connection | null, + isDragging: boolean, + opt_distance?: number + ): number; /** * Helper method that translates a connection error code into a string. @@ -52,8 +58,11 @@ export interface IConnectionChecker { * @param b The second of the two connections being checked. * @returns A developer-readable error string. */ - getErrorMessage(errorCode: number, a: Connection|null, b: Connection|null): - string; + getErrorMessage( + errorCode: number, + a: Connection | null, + b: Connection | null + ): string; /** * Check that connecting the given connections is safe, meaning that it would @@ -63,7 +72,7 @@ export interface IConnectionChecker { * @param b The second of the connections to check. * @returns An enum with the reason this connection is safe or unsafe. */ - doSafetyChecks(a: Connection|null, b: Connection|null): number; + doSafetyChecks(a: Connection | null, b: Connection | null): number; /** * Check whether this connection is compatible with another connection with @@ -84,6 +93,9 @@ export interface IConnectionChecker { * @param distance The maximum allowable distance between connections. * @returns True if the connection is allowed during a drag. */ - doDragChecks(a: RenderedConnection, b: RenderedConnection, distance: number): - boolean; + doDragChecks( + a: RenderedConnection, + b: RenderedConnection, + distance: number + ): boolean; } diff --git a/core/interfaces/i_contextmenu.ts b/core/interfaces/i_contextmenu.ts index f205e9e2e7b..2ac29052b14 100644 --- a/core/interfaces/i_contextmenu.ts +++ b/core/interfaces/i_contextmenu.ts @@ -7,7 +7,6 @@ import * as goog from '../../closure/goog/goog.js'; goog.declareModuleId('Blockly.IContextMenu'); - export interface IContextMenu { /** * Show the context menu for this object. diff --git a/core/interfaces/i_copyable.ts b/core/interfaces/i_copyable.ts index cd3fe367802..d62d9ef83e2 100644 --- a/core/interfaces/i_copyable.ts +++ b/core/interfaces/i_copyable.ts @@ -10,7 +10,6 @@ goog.declareModuleId('Blockly.ICopyable'); import type {WorkspaceSvg} from '../workspace_svg.js'; import type {ISelectable} from './i_selectable.js'; - export interface ICopyable extends ISelectable { /** * Encode for copying. @@ -18,14 +17,14 @@ export interface ICopyable extends ISelectable { * @returns Copy metadata. * @internal */ - toCopyData(): CopyData|null; + toCopyData(): CopyData | null; } export namespace ICopyable { export interface CopyData { - saveInfo: Object|Element; + saveInfo: Object | Element; source: WorkspaceSvg; - typeCounts: {[key: string]: number}|null; + typeCounts: {[key: string]: number} | null; } } diff --git a/core/interfaces/i_deletable.ts b/core/interfaces/i_deletable.ts index 3593dd87855..0a8c12deb1b 100644 --- a/core/interfaces/i_deletable.ts +++ b/core/interfaces/i_deletable.ts @@ -7,7 +7,6 @@ import * as goog from '../../closure/goog/goog.js'; goog.declareModuleId('Blockly.IDeletable'); - /** * The interface for an object that can be deleted. */ diff --git a/core/interfaces/i_delete_area.ts b/core/interfaces/i_delete_area.ts index 9a72ec779ce..2dd3e3111cd 100644 --- a/core/interfaces/i_delete_area.ts +++ b/core/interfaces/i_delete_area.ts @@ -10,7 +10,6 @@ goog.declareModuleId('Blockly.IDeleteArea'); import type {IDragTarget} from './i_drag_target.js'; import type {IDraggable} from './i_draggable.js'; - /** * Interface for a component that can delete a block or bubble that is dropped * on top of it. diff --git a/core/interfaces/i_drag_target.ts b/core/interfaces/i_drag_target.ts index 5d2ba2bb89a..813036dd4d1 100644 --- a/core/interfaces/i_drag_target.ts +++ b/core/interfaces/i_drag_target.ts @@ -13,7 +13,6 @@ goog.declareModuleId('Blockly.IDragTarget'); import type {IComponent} from './i_component.js'; - /** * Interface for a component with custom behaviour when a block or bubble is * dragged over or dropped on top of it. @@ -26,7 +25,7 @@ export interface IDragTarget extends IComponent { * @returns The component's bounding box. Null if drag target area should be * ignored. */ - getClientRect(): Rect|null; + getClientRect(): Rect | null; /** * Handles when a cursor with a block or bubble enters this drag target. diff --git a/core/interfaces/i_draggable.ts b/core/interfaces/i_draggable.ts index 49e6976cae8..014899e5a95 100644 --- a/core/interfaces/i_draggable.ts +++ b/core/interfaces/i_draggable.ts @@ -9,7 +9,6 @@ goog.declareModuleId('Blockly.IDraggable'); import type {IDeletable} from './i_deletable.js'; - /** * The interface for an object that can be dragged. */ diff --git a/core/interfaces/i_flyout.ts b/core/interfaces/i_flyout.ts index 5f8503161a1..540f9ad690f 100644 --- a/core/interfaces/i_flyout.ts +++ b/core/interfaces/i_flyout.ts @@ -14,7 +14,6 @@ import type {FlyoutDefinition} from '../utils/toolbox.js'; import type {Svg} from '../utils/svg.js'; import type {IRegistrable} from './i_registrable.js'; - /** * Interface for a flyout. */ @@ -26,7 +25,7 @@ export interface IFlyout extends IRegistrable { RTL: boolean; /** The target workspace */ - targetWorkspace: WorkspaceSvg|null; + targetWorkspace: WorkspaceSvg | null; /** Margin around the edges of the blocks in the flyout. */ readonly MARGIN: number; @@ -46,7 +45,9 @@ export interface IFlyout extends IRegistrable { * or . * @returns The flyout's SVG group. */ - createDom(tagName: string|Svg|Svg): SVGElement; + createDom( + tagName: string | Svg | Svg + ): SVGElement; /** * Initializes the flyout. @@ -115,7 +116,7 @@ export interface IFlyout extends IRegistrable { * of Nodes, a NodeList, a toolbox definition, or a string with the name * of the dynamic category. */ - show(flyoutDef: FlyoutDefinition|string): void; + show(flyoutDef: FlyoutDefinition | string): void; /** * Create a copy of this block on the workspace. diff --git a/core/interfaces/i_has_bubble.ts b/core/interfaces/i_has_bubble.ts index f82f1a9dccb..f45c69d071b 100644 --- a/core/interfaces/i_has_bubble.ts +++ b/core/interfaces/i_has_bubble.ts @@ -4,7 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ - export interface IHasBubble { /** @return True if the bubble is currently open, false otherwise. */ isBubbleVisible(): boolean; @@ -15,6 +14,7 @@ export interface IHasBubble { /** Type guard that checks whether the given object is a IHasBubble. */ export function hasBubble(obj: any): obj is IHasBubble { - return obj.isBubbleVisible !== undefined && - obj.setBubbleVisible !== undefined; + return ( + obj.isBubbleVisible !== undefined && obj.setBubbleVisible !== undefined + ); } diff --git a/core/interfaces/i_icon.ts b/core/interfaces/i_icon.ts index fa7dc345d20..3e860dd28ce 100644 --- a/core/interfaces/i_icon.ts +++ b/core/interfaces/i_icon.ts @@ -7,7 +7,6 @@ import type {Coordinate} from '../utils/coordinate.js'; import type {Size} from '../utils/size.js'; - export interface IIcon { /** * @return the string representing the type of the icon. @@ -82,11 +81,18 @@ export interface IIcon { /** Type guard that checks whether the given object is an IIcon. */ export function isIcon(obj: any): obj is IIcon { - return obj.getType !== undefined && obj.initView !== undefined && - obj.dispose !== undefined && obj.getWeight !== undefined && - obj.getSize !== undefined && obj.applyColour !== undefined && - obj.updateEditable !== undefined && obj.updateCollapsed !== undefined && - obj.isShownWhenCollapsed !== undefined && - obj.setOffsetInBlock !== undefined && - obj.onLocationChange !== undefined && obj.onClick !== undefined; + return ( + obj.getType !== undefined && + obj.initView !== undefined && + obj.dispose !== undefined && + obj.getWeight !== undefined && + obj.getSize !== undefined && + obj.applyColour !== undefined && + obj.updateEditable !== undefined && + obj.updateCollapsed !== undefined && + obj.isShownWhenCollapsed !== undefined && + obj.setOffsetInBlock !== undefined && + obj.onLocationChange !== undefined && + obj.onClick !== undefined + ); } diff --git a/core/interfaces/i_legacy_procedure_blocks.ts b/core/interfaces/i_legacy_procedure_blocks.ts index 410048e4ed0..cab4dc93fc2 100644 --- a/core/interfaces/i_legacy_procedure_blocks.ts +++ b/core/interfaces/i_legacy_procedure_blocks.ts @@ -4,7 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ - /** * Legacy means of representing a procedure signature. The elements are * respectively: name, parameter names, and whether it has a return value. @@ -24,12 +23,13 @@ export interface ProcedureBlock { /** @internal */ export interface LegacyProcedureDefBlock { - getProcedureDef: () => ProcedureTuple + getProcedureDef: () => ProcedureTuple; } /** @internal */ -export function isLegacyProcedureDefBlock(block: Object): - block is LegacyProcedureDefBlock { +export function isLegacyProcedureDefBlock( + block: Object +): block is LegacyProcedureDefBlock { return (block as any).getProcedureDef !== undefined; } @@ -40,8 +40,11 @@ export interface LegacyProcedureCallBlock { } /** @internal */ -export function isLegacyProcedureCallBlock(block: Object): - block is LegacyProcedureCallBlock { - return (block as any).getProcedureCall !== undefined && - (block as any).renameProcedure !== undefined; +export function isLegacyProcedureCallBlock( + block: Object +): block is LegacyProcedureCallBlock { + return ( + (block as any).getProcedureCall !== undefined && + (block as any).renameProcedure !== undefined + ); } diff --git a/core/interfaces/i_metrics_manager.ts b/core/interfaces/i_metrics_manager.ts index a3d28d0fa69..a06e0ca4a06 100644 --- a/core/interfaces/i_metrics_manager.ts +++ b/core/interfaces/i_metrics_manager.ts @@ -5,7 +5,12 @@ */ import * as goog from '../../closure/goog/goog.js'; -import type {ContainerRegion, ToolboxMetrics, AbsoluteMetrics, UiMetrics} from '../metrics_manager.js'; +import type { + ContainerRegion, + ToolboxMetrics, + AbsoluteMetrics, + UiMetrics, +} from '../metrics_manager.js'; import type {Size} from '../utils/size.js'; import type {Metrics} from '../utils/metrics.js'; goog.declareModuleId('Blockly.IMetricsManager'); @@ -36,8 +41,10 @@ export interface IMetricsManager { * @returns The metrics for the scroll container */ getScrollMetrics( - opt_getWorkspaceCoordinates?: boolean, opt_viewMetrics?: ContainerRegion, - opt_contentMetrics?: ContainerRegion): ContainerRegion; + opt_getWorkspaceCoordinates?: boolean, + opt_viewMetrics?: ContainerRegion, + opt_contentMetrics?: ContainerRegion + ): ContainerRegion; /** * Gets the width and the height of the flyout in pixel diff --git a/core/interfaces/i_movable.ts b/core/interfaces/i_movable.ts index 2c249273a21..8ef11462cff 100644 --- a/core/interfaces/i_movable.ts +++ b/core/interfaces/i_movable.ts @@ -7,7 +7,6 @@ import * as goog from '../../closure/goog/goog.js'; goog.declareModuleId('Blockly.IMovable'); - /** * The interface for an object that is movable. */ diff --git a/core/interfaces/i_observable.ts b/core/interfaces/i_observable.ts index c6b02c9e673..96a2a0bc4e8 100644 --- a/core/interfaces/i_observable.ts +++ b/core/interfaces/i_observable.ts @@ -4,7 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ - /** * An object that fires events optionally. * diff --git a/core/interfaces/i_parameter_model.ts b/core/interfaces/i_parameter_model.ts index c712fdf22db..1b023970322 100644 --- a/core/interfaces/i_parameter_model.ts +++ b/core/interfaces/i_parameter_model.ts @@ -6,7 +6,6 @@ import {IProcedureModel} from './i_procedure_model'; - /** * A data model for a procedure. */ diff --git a/core/interfaces/i_positionable.ts b/core/interfaces/i_positionable.ts index 52ad96f9067..0485b51bfee 100644 --- a/core/interfaces/i_positionable.ts +++ b/core/interfaces/i_positionable.ts @@ -11,7 +11,6 @@ goog.declareModuleId('Blockly.IPositionable'); import type {IComponent} from './i_component.js'; - /** * Interface for a component that is positioned on top of the workspace. */ @@ -31,5 +30,5 @@ export interface IPositionable extends IComponent { * @returns The UI elements's bounding box. Null if bounding box should be * ignored by other UI elements. */ - getBoundingRectangle(): Rect|null; + getBoundingRectangle(): Rect | null; } diff --git a/core/interfaces/i_procedure_block.ts b/core/interfaces/i_procedure_block.ts index 133441013b7..e24e2ced3e3 100644 --- a/core/interfaces/i_procedure_block.ts +++ b/core/interfaces/i_procedure_block.ts @@ -9,7 +9,6 @@ import {IProcedureModel} from './i_procedure_model.js'; import * as goog from '../../closure/goog/goog.js'; goog.declareModuleId('Blockly.procedures.IProcedureBlock'); - /** The interface for a block which models a procedure. */ export interface IProcedureBlock { getProcedureModel(): IProcedureModel; @@ -18,9 +17,12 @@ export interface IProcedureBlock { } /** A type guard which checks if the given block is a procedure block. */ -export function isProcedureBlock(block: Block| - IProcedureBlock): block is IProcedureBlock { - return (block as IProcedureBlock).getProcedureModel !== undefined && - (block as IProcedureBlock).doProcedureUpdate !== undefined && - (block as IProcedureBlock).isProcedureDef !== undefined; +export function isProcedureBlock( + block: Block | IProcedureBlock +): block is IProcedureBlock { + return ( + (block as IProcedureBlock).getProcedureModel !== undefined && + (block as IProcedureBlock).doProcedureUpdate !== undefined && + (block as IProcedureBlock).isProcedureDef !== undefined + ); } diff --git a/core/interfaces/i_procedure_map.ts b/core/interfaces/i_procedure_map.ts index 0eead4025cc..e14d53a3384 100644 --- a/core/interfaces/i_procedure_map.ts +++ b/core/interfaces/i_procedure_map.ts @@ -6,7 +6,6 @@ import {IProcedureModel} from './i_procedure_model.js'; - export interface IProcedureMap extends Map { /** * Adds the given ProcedureModel to the map of procedure models, so that @@ -14,7 +13,6 @@ export interface IProcedureMap extends Map { */ add(proc: IProcedureModel): this; - /** Returns all of the procedures stored in this map. */ getProcedures(): IProcedureModel[]; } diff --git a/core/interfaces/i_procedure_model.ts b/core/interfaces/i_procedure_model.ts index dffdc984e71..cb5fda09f20 100644 --- a/core/interfaces/i_procedure_model.ts +++ b/core/interfaces/i_procedure_model.ts @@ -4,10 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ - import {IParameterModel} from './i_parameter_model.js'; - /** * A data model for a procedure. */ @@ -30,7 +28,7 @@ export interface IProcedureModel { * * Pass null to represent a procedure that does not return. */ - setReturnTypes(types: string[]|null): this; + setReturnTypes(types: string[] | null): this; /** * Sets whether this procedure is enabled/disabled. If a procedure is disabled @@ -55,7 +53,7 @@ export interface IProcedureModel { * * Null represents a procedure that does not return a value. */ - getReturnTypes(): string[]|null; + getReturnTypes(): string[] | null; /** * Returns whether the procedure is enabled/disabled. If a procedure is diff --git a/core/interfaces/i_registrable.ts b/core/interfaces/i_registrable.ts index f95e3c5591d..b8ea4b57f6f 100644 --- a/core/interfaces/i_registrable.ts +++ b/core/interfaces/i_registrable.ts @@ -7,7 +7,6 @@ import * as goog from '../../closure/goog/goog.js'; goog.declareModuleId('Blockly.IRegistrable'); - /** * The interface for a Blockly component that can be registered. */ diff --git a/core/interfaces/i_selectable.ts b/core/interfaces/i_selectable.ts index 0e99a58472f..7997c90f8fd 100644 --- a/core/interfaces/i_selectable.ts +++ b/core/interfaces/i_selectable.ts @@ -10,7 +10,6 @@ goog.declareModuleId('Blockly.ISelectable'); import type {IDeletable} from './i_deletable.js'; import type {IMovable} from './i_movable.js'; - /** * The interface for an object that is selectable. */ diff --git a/core/interfaces/i_selectable_toolbox_item.ts b/core/interfaces/i_selectable_toolbox_item.ts index 8f454372b45..453608c95e5 100644 --- a/core/interfaces/i_selectable_toolbox_item.ts +++ b/core/interfaces/i_selectable_toolbox_item.ts @@ -10,7 +10,6 @@ goog.declareModuleId('Blockly.ISelectableToolboxItem'); import type {IToolboxItem} from './i_toolbox_item.js'; - /** * Interface for an item in the toolbox that can be selected. */ @@ -28,7 +27,7 @@ export interface ISelectableToolboxItem extends IToolboxItem { * * @returns The definition of items to be displayed in the flyout. */ - getContents(): FlyoutItemInfoArray|string; + getContents(): FlyoutItemInfoArray | string; /** * Sets the current toolbox item as selected. @@ -58,7 +57,8 @@ export interface ISelectableToolboxItem extends IToolboxItem { /** * Type guard that checks whether an IToolboxItem is an ISelectableToolboxItem. */ -export function isSelectableToolboxItem(toolboxItem: IToolboxItem): - toolboxItem is ISelectableToolboxItem { +export function isSelectableToolboxItem( + toolboxItem: IToolboxItem +): toolboxItem is ISelectableToolboxItem { return toolboxItem.isSelectable(); } diff --git a/core/interfaces/i_serializable.ts b/core/interfaces/i_serializable.ts index ad65cb3f026..fc7c3f6a0f2 100644 --- a/core/interfaces/i_serializable.ts +++ b/core/interfaces/i_serializable.ts @@ -4,7 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ - export interface ISerializable { /** * @param doFullSerialization If true, this signals that any backing data diff --git a/core/interfaces/i_serializer.ts b/core/interfaces/i_serializer.ts index 37efa4626a8..30e682013f0 100644 --- a/core/interfaces/i_serializer.ts +++ b/core/interfaces/i_serializer.ts @@ -9,7 +9,6 @@ goog.declareModuleId('Blockly.serialization.ISerializer'); import type {Workspace} from '../workspace.js'; - /** * Serializes and deserializes a plugin or system. */ @@ -33,7 +32,7 @@ export interface ISerializer { * @returns A JS object containing the system's state, or null if there is no * state to record. */ - save(workspace: Workspace): Object|null; + save(workspace: Workspace): Object | null; /* eslint-enable valid-jsdoc */ /** diff --git a/core/interfaces/i_styleable.ts b/core/interfaces/i_styleable.ts index 86d9f2c00c6..d153bf02f5a 100644 --- a/core/interfaces/i_styleable.ts +++ b/core/interfaces/i_styleable.ts @@ -7,7 +7,6 @@ import * as goog from '../../closure/goog/goog.js'; goog.declareModuleId('Blockly.IStyleable'); - /** * Interface for an object that a style can be added to. */ diff --git a/core/interfaces/i_toolbox.ts b/core/interfaces/i_toolbox.ts index cb4eeceb4f2..6cb7f0f74fe 100644 --- a/core/interfaces/i_toolbox.ts +++ b/core/interfaces/i_toolbox.ts @@ -13,7 +13,6 @@ import type {ToolboxInfo} from '../utils/toolbox.js'; import type {IFlyout} from './i_flyout.js'; import type {WorkspaceSvg} from '../workspace_svg.js'; - /** * Interface for a toolbox. */ @@ -47,7 +46,7 @@ export interface IToolbox extends IRegistrable { * * @returns The toolbox flyout. */ - getFlyout(): IFlyout|null; + getFlyout(): IFlyout | null; /** * Gets the workspace for the toolbox. @@ -107,7 +106,7 @@ export interface IToolbox extends IRegistrable { * * @returns The selected item, or null if no item is currently selected. */ - getSelectedItem(): IToolboxItem|null; + getSelectedItem(): IToolboxItem | null; /** Disposes of this toolbox. */ dispose(): void; diff --git a/core/interfaces/i_toolbox_item.ts b/core/interfaces/i_toolbox_item.ts index f7398acec13..bd46e4f8021 100644 --- a/core/interfaces/i_toolbox_item.ts +++ b/core/interfaces/i_toolbox_item.ts @@ -7,7 +7,6 @@ import * as goog from '../../closure/goog/goog.js'; goog.declareModuleId('Blockly.IToolboxItem'); - /** * Interface for an item in the toolbox. */ @@ -24,7 +23,7 @@ export interface IToolboxItem { * * @returns The div for the toolbox item. */ - getDiv(): Element|null; + getDiv(): Element | null; /** * Gets a unique identifier for this toolbox item. @@ -39,7 +38,7 @@ export interface IToolboxItem { * @returns The parent toolbox item, or null if this toolbox item is not * nested. */ - getParent(): IToolboxItem|null; + getParent(): IToolboxItem | null; /** * Gets the nested level of the category. @@ -71,7 +70,7 @@ export interface IToolboxItem { * * @returns The HTML element that receives clicks. */ - getClickTarget(): Element|null; + getClickTarget(): Element | null; /** * Sets whether the category is visible or not. diff --git a/core/interfaces/i_variable_backed_parameter_model.ts b/core/interfaces/i_variable_backed_parameter_model.ts index 39530b81e40..71433f94f2f 100644 --- a/core/interfaces/i_variable_backed_parameter_model.ts +++ b/core/interfaces/i_variable_backed_parameter_model.ts @@ -7,7 +7,6 @@ import type {VariableModel} from '../variable_model.js'; import {IParameterModel} from './i_parameter_model.js'; - /** Interface for a parameter model that holds a variable model. */ export interface IVariableBackedParameterModel extends IParameterModel { /** Returns the variable model held by this type. */ @@ -17,7 +16,8 @@ export interface IVariableBackedParameterModel extends IParameterModel { /** * Returns whether the given object is a variable holder or not. */ -export function isVariableBackedParameterModel(param: IParameterModel): - param is IVariableBackedParameterModel { +export function isVariableBackedParameterModel( + param: IParameterModel +): param is IVariableBackedParameterModel { return (param as any).getVariableModel !== undefined; } diff --git a/core/internal_constants.ts b/core/internal_constants.ts index 5d6a0340d79..fe0c2bbdd4b 100644 --- a/core/internal_constants.ts +++ b/core/internal_constants.ts @@ -9,7 +9,6 @@ goog.declareModuleId('Blockly.internalConstants'); import {ConnectionType} from './connection_type.js'; - /** * Number of characters to truncate a collapsed block to. * @@ -34,9 +33,9 @@ export const OPPOSITE_TYPE: number[] = []; OPPOSITE_TYPE[ConnectionType.INPUT_VALUE] = ConnectionType.OUTPUT_VALUE; OPPOSITE_TYPE[ConnectionType.OUTPUT_VALUE] = ConnectionType.INPUT_VALUE; OPPOSITE_TYPE[ConnectionType.NEXT_STATEMENT] = - ConnectionType.PREVIOUS_STATEMENT; + ConnectionType.PREVIOUS_STATEMENT; OPPOSITE_TYPE[ConnectionType.PREVIOUS_STATEMENT] = - ConnectionType.NEXT_STATEMENT; + ConnectionType.NEXT_STATEMENT; /** * String for use in the dropdown created in field_variable. diff --git a/core/keyboard_nav/ast_node.ts b/core/keyboard_nav/ast_node.ts index 769fd9e0e3c..ce01b1a7ba6 100644 --- a/core/keyboard_nav/ast_node.ts +++ b/core/keyboard_nav/ast_node.ts @@ -23,7 +23,6 @@ import type {IASTNodeLocationWithBlock} from '../interfaces/i_ast_node_location_ import {Coordinate} from '../utils/coordinate.js'; import type {Workspace} from '../workspace.js'; - /** * Class for an AST node. * It is recommended that you use one of the createNode methods instead of @@ -80,7 +79,7 @@ export class ASTNode { * * @param params The user specified parameters. */ - private processParams(params: Params|null) { + private processParams(params: Params | null) { if (!params) { return; } @@ -139,7 +138,7 @@ export class ASTNode { * @returns The AST node holding the next field or connection or null if there * is no editable field or input connection after the given input. */ - private findNextForInput(): ASTNode|null { + private findNextForInput(): ASTNode | null { const location = this.location as Connection; const parentInput = location.getParentInput(); const block = parentInput!.getSourceBlock(); @@ -169,13 +168,14 @@ export class ASTNode { * @returns The AST node pointing to the next field or connection or null if * there is no editable field or input connection after the given input. */ - private findNextForField(): ASTNode|null { + private findNextForField(): ASTNode | null { const location = this.location as Field; const input = location.getParentInput(); const block = location.getSourceBlock(); if (!block) { throw new Error( - 'The current AST location is not associated with a block'); + 'The current AST location is not associated with a block' + ); } const curIdx = block.inputList.indexOf(input); let fieldIdx = input.fieldRow.indexOf(location) + 1; @@ -203,7 +203,7 @@ export class ASTNode { * * @returns The AST node holding the previous field or connection. */ - private findPrevForInput(): ASTNode|null { + private findPrevForInput(): ASTNode | null { const location = this.location as Connection; const parentInput = location.getParentInput(); const block = parentInput!.getSourceBlock(); @@ -232,13 +232,14 @@ export class ASTNode { * * @returns The AST node holding the previous input or field. */ - private findPrevForField(): ASTNode|null { + private findPrevForField(): ASTNode | null { const location = this.location as Field; const parentInput = location.getParentInput(); const block = location.getSourceBlock(); if (!block) { throw new Error( - 'The current AST location is not associated with a block'); + 'The current AST location is not associated with a block' + ); } const curIdx = block.inputList.indexOf(parentInput); let fieldIdx = parentInput.fieldRow.indexOf(location) - 1; @@ -270,7 +271,7 @@ export class ASTNode { * @returns The first block of the next stack or null if there are no blocks * on the workspace. */ - private navigateBetweenStacks(forward: boolean): ASTNode|null { + private navigateBetweenStacks(forward: boolean): ASTNode | null { let curLocation = this.getLocation(); // TODO(#6097): Use instanceof checks to exit early for values of // curLocation that don't make sense. @@ -300,7 +301,8 @@ export class ASTNode { } } throw Error( - 'Couldn\'t find ' + (forward ? 'next' : 'previous') + ' stack?!'); + "Couldn't find " + (forward ? 'next' : 'previous') + ' stack?!' + ); } /** @@ -311,7 +313,7 @@ export class ASTNode { * @param block The block that we want to find the top connection on. * @returns The AST node containing the top connection. */ - private findTopASTNodeForBlock(block: Block): ASTNode|null { + private findTopASTNodeForBlock(block: Block): ASTNode | null { const topConnection = getParentConnection(block); if (topConnection) { return ASTNode.createConnectionNode(topConnection); @@ -328,7 +330,7 @@ export class ASTNode { * @returns The AST node pointing to the input connection or the top block of * the stack this block is in. */ - private getOutAstNodeForBlock(block: Block): ASTNode|null { + private getOutAstNodeForBlock(block: Block): ASTNode | null { if (!block) { return null; } @@ -338,13 +340,16 @@ export class ASTNode { const topConnection = getParentConnection(topBlock); // If the top connection has a parentInput, create an AST node pointing to // that input. - if (topConnection && topConnection.targetConnection && - topConnection.targetConnection.getParentInput()) { + if ( + topConnection && + topConnection.targetConnection && + topConnection.targetConnection.getParentInput() + ) { // AnyDuringMigration because: Argument of type 'Input | null' is not // assignable to parameter of type 'Input'. return ASTNode.createInputNode( - topConnection.targetConnection.getParentInput() as - AnyDuringMigration); + topConnection.targetConnection.getParentInput() as AnyDuringMigration + ); } else { // Go to stack level if you are not underneath an input. return ASTNode.createStackNode(topBlock); @@ -359,7 +364,7 @@ export class ASTNode { * Null if there are no editable fields or inputs with connections on the * block. */ - private findFirstFieldOrInput(block: Block): ASTNode|null { + private findFirstFieldOrInput(block: Block): ASTNode | null { const inputs = block.inputList; for (let i = 0; i < inputs.length; i++) { const input = inputs[i]; @@ -383,7 +388,7 @@ export class ASTNode { * @returns The source block of the location, or null if the node is of type * workspace. */ - getSourceBlock(): Block|null { + getSourceBlock(): Block | null { if (this.getType() === ASTNode.types.BLOCK) { return this.getLocation() as Block; } else if (this.getType() === ASTNode.types.STACK) { @@ -401,7 +406,7 @@ export class ASTNode { * @returns An AST node that wraps the next field, connection, block, or * workspace. Or null if there is no node to the right. */ - next(): ASTNode|null { + next(): ASTNode | null { switch (this.type) { case ASTNode.types.STACK: return this.navigateBetweenStacks(true); @@ -443,7 +448,7 @@ export class ASTNode { * @returns An AST node that wraps the next field, connection, workspace, or * block. Or null if there is nothing below this node. */ - in(): ASTNode|null { + in(): ASTNode | null { switch (this.type) { case ASTNode.types.WORKSPACE: { const workspace = this.location as Workspace; @@ -477,7 +482,7 @@ export class ASTNode { * @returns An AST node that wraps the previous field, connection, workspace * or block. Or null if no node exists to the left. null. */ - prev(): ASTNode|null { + prev(): ASTNode | null { switch (this.type) { case ASTNode.types.STACK: return this.navigateBetweenStacks(false); @@ -521,14 +526,16 @@ export class ASTNode { * @returns An AST node that wraps the next field, connection, workspace or * block. Or null if we are at the workspace level. */ - out(): ASTNode|null { + out(): ASTNode | null { switch (this.type) { case ASTNode.types.STACK: { const block = this.location as Block; const blockPos = block.getRelativeToSurfaceXY(); // TODO: Make sure this is in the bounds of the workspace. - const wsCoordinate = - new Coordinate(blockPos.x, blockPos.y + ASTNode.DEFAULT_OFFSET_Y); + const wsCoordinate = new Coordinate( + blockPos.x, + blockPos.y + ASTNode.DEFAULT_OFFSET_Y + ); return ASTNode.createWorkspaceNode(block.workspace, wsCoordinate); } case ASTNode.types.OUTPUT: { @@ -544,7 +551,8 @@ export class ASTNode { const block = field.getSourceBlock(); if (!block) { throw new Error( - 'The current AST location is not associated with a block'); + 'The current AST location is not associated with a block' + ); } return ASTNode.createBlockNode(block); } @@ -592,7 +600,7 @@ export class ASTNode { * @param field The location of the AST node. * @returns An AST node pointing to a field. */ - static createFieldNode(field: Field): ASTNode|null { + static createFieldNode(field: Field): ASTNode | null { if (!field) { return null; } @@ -607,7 +615,7 @@ export class ASTNode { * @param connection This is the connection the node will point to. * @returns An AST node pointing to a connection. */ - static createConnectionNode(connection: Connection): ASTNode|null { + static createConnectionNode(connection: Connection): ASTNode | null { if (!connection) { return null; } @@ -616,13 +624,17 @@ export class ASTNode { // AnyDuringMigration because: Argument of type 'Input | null' is not // assignable to parameter of type 'Input'. return ASTNode.createInputNode( - connection.getParentInput() as AnyDuringMigration); + connection.getParentInput() as AnyDuringMigration + ); } else if ( - type === ConnectionType.NEXT_STATEMENT && connection.getParentInput()) { + type === ConnectionType.NEXT_STATEMENT && + connection.getParentInput() + ) { // AnyDuringMigration because: Argument of type 'Input | null' is not // assignable to parameter of type 'Input'. return ASTNode.createInputNode( - connection.getParentInput() as AnyDuringMigration); + connection.getParentInput() as AnyDuringMigration + ); } else if (type === ConnectionType.NEXT_STATEMENT) { return new ASTNode(ASTNode.types.NEXT, connection); } else if (type === ConnectionType.OUTPUT_VALUE) { @@ -640,7 +652,7 @@ export class ASTNode { * @param input The input used to create an AST node. * @returns An AST node pointing to a input. */ - static createInputNode(input: Input): ASTNode|null { + static createInputNode(input: Input): ASTNode | null { if (!input || !input.connection) { return null; } @@ -653,7 +665,7 @@ export class ASTNode { * @param block The block used to create an AST node. * @returns An AST node pointing to a block. */ - static createBlockNode(block: Block): ASTNode|null { + static createBlockNode(block: Block): ASTNode | null { if (!block) { return null; } @@ -670,7 +682,7 @@ export class ASTNode { * @returns An AST node of type stack that points to the top block on the * stack. */ - static createStackNode(topBlock: Block): ASTNode|null { + static createStackNode(topBlock: Block): ASTNode | null { if (!topBlock) { return null; } @@ -686,7 +698,9 @@ export class ASTNode { * workspace. */ static createWorkspaceNode( - workspace: Workspace|null, wsCoordinate: Coordinate|null): ASTNode|null { + workspace: Workspace | null, + wsCoordinate: Coordinate | null + ): ASTNode | null { if (!wsCoordinate || !workspace) { return null; } @@ -701,7 +715,7 @@ export class ASTNode { * @param block The block to find the top most AST node on. * @returns The AST node holding the top most position on the block. */ - static createTopNode(block: Block): ASTNode|null { + static createTopNode(block: Block): ASTNode | null { let astNode; const topConnection = getParentConnection(block); if (topConnection) { @@ -735,7 +749,6 @@ export type Params = ASTNode.Params; // wasn't automatically converted by the automatic migration script, (2) the // name doesn't follow the styleguide. - /** * Gets the parent connection on a block. * This is either an output connection, previous connection or undefined. @@ -745,10 +758,12 @@ export type Params = ASTNode.Params; * @param block The block to find the parent connection on. * @returns The connection connecting to the parent of the block. */ -function getParentConnection(block: Block): Connection|null { +function getParentConnection(block: Block): Connection | null { let topConnection = block.outputConnection; - if (!topConnection || - block.previousConnection && block.previousConnection.isConnected()) { + if ( + !topConnection || + (block.previousConnection && block.previousConnection.isConnected()) + ) { topConnection = block.previousConnection; } return topConnection; diff --git a/core/keyboard_nav/basic_cursor.ts b/core/keyboard_nav/basic_cursor.ts index 4a3fb4d175d..96566a4c0aa 100644 --- a/core/keyboard_nav/basic_cursor.ts +++ b/core/keyboard_nav/basic_cursor.ts @@ -18,7 +18,6 @@ import * as registry from '../registry.js'; import {ASTNode} from './ast_node.js'; import {Cursor} from './cursor.js'; - /** * Class for a basic cursor. * This will allow the user to get to all nodes in the AST by hitting next or @@ -38,7 +37,7 @@ export class BasicCursor extends Cursor { * @returns The next node, or null if the current node is not set or there is * no next value. */ - override next(): ASTNode|null { + override next(): ASTNode | null { const curNode = this.getCurNode(); if (!curNode) { return null; @@ -59,7 +58,7 @@ export class BasicCursor extends Cursor { * @returns The next node, or null if the current node is not set or there is * no next value. */ - override in(): ASTNode|null { + override in(): ASTNode | null { return this.next(); } @@ -69,7 +68,7 @@ export class BasicCursor extends Cursor { * @returns The previous node, or null if the current node is not set or there * is no previous value. */ - override prev(): ASTNode|null { + override prev(): ASTNode | null { const curNode = this.getCurNode(); if (!curNode) { return null; @@ -90,7 +89,7 @@ export class BasicCursor extends Cursor { * @returns The previous node, or null if the current node is not set or there * is no previous value. */ - override out(): ASTNode|null { + override out(): ASTNode | null { return this.prev(); } @@ -105,8 +104,9 @@ export class BasicCursor extends Cursor { * @returns The next node in the traversal. */ protected getNextNode_( - node: ASTNode|null, isValid: (p1: ASTNode|null) => boolean): ASTNode - |null { + node: ASTNode | null, + isValid: (p1: ASTNode | null) => boolean + ): ASTNode | null { if (!node) { return null; } @@ -137,12 +137,13 @@ export class BasicCursor extends Cursor { * exists. */ protected getPreviousNode_( - node: ASTNode|null, isValid: (p1: ASTNode|null) => boolean): ASTNode - |null { + node: ASTNode | null, + isValid: (p1: ASTNode | null) => boolean + ): ASTNode | null { if (!node) { return null; } - let newNode: ASTNode|null = node.prev(); + let newNode: ASTNode | null = node.prev(); if (newNode) { newNode = this.getRightMostChild(newNode); @@ -164,12 +165,17 @@ export class BasicCursor extends Cursor { * @param node The AST node to check whether it is valid. * @returns True if the node should be visited, false otherwise. */ - protected validNode_(node: ASTNode|null): boolean { + protected validNode_(node: ASTNode | null): boolean { let isValid = false; const type = node && node.getType(); - if (type === ASTNode.types.OUTPUT || type === ASTNode.types.INPUT || - type === ASTNode.types.FIELD || type === ASTNode.types.NEXT || - type === ASTNode.types.PREVIOUS || type === ASTNode.types.WORKSPACE) { + if ( + type === ASTNode.types.OUTPUT || + type === ASTNode.types.INPUT || + type === ASTNode.types.FIELD || + type === ASTNode.types.NEXT || + type === ASTNode.types.PREVIOUS || + type === ASTNode.types.WORKSPACE + ) { isValid = true; } return isValid; @@ -181,7 +187,7 @@ export class BasicCursor extends Cursor { * @param node The current position in the AST. * @returns The parent AST node or null if there are no valid parents. */ - private findSiblingOrParent(node: ASTNode|null): ASTNode|null { + private findSiblingOrParent(node: ASTNode | null): ASTNode | null { if (!node) { return null; } @@ -199,7 +205,7 @@ export class BasicCursor extends Cursor { * @returns The right most child of the given node, or the node if no child * exists. */ - private getRightMostChild(node: ASTNode|null): ASTNode|null { + private getRightMostChild(node: ASTNode | null): ASTNode | null { if (!node!.in()) { return node; } @@ -212,4 +218,7 @@ export class BasicCursor extends Cursor { } registry.register( - registry.Type.CURSOR, BasicCursor.registrationName, BasicCursor); + registry.Type.CURSOR, + BasicCursor.registrationName, + BasicCursor +); diff --git a/core/keyboard_nav/cursor.ts b/core/keyboard_nav/cursor.ts index 31ca083d6dc..adc62acbf65 100644 --- a/core/keyboard_nav/cursor.ts +++ b/core/keyboard_nav/cursor.ts @@ -18,7 +18,6 @@ import * as registry from '../registry.js'; import {ASTNode} from './ast_node.js'; import {Marker} from './marker.js'; - /** * Class for a cursor. * A cursor controls how a user navigates the Blockly AST. @@ -36,16 +35,19 @@ export class Cursor extends Marker { * @returns The next element, or null if the current node is not set or there * is no next value. */ - next(): ASTNode|null { + next(): ASTNode | null { const curNode = this.getCurNode(); if (!curNode) { return null; } let newNode = curNode.next(); - while (newNode && newNode.next() && - (newNode.getType() === ASTNode.types.NEXT || - newNode.getType() === ASTNode.types.BLOCK)) { + while ( + newNode && + newNode.next() && + (newNode.getType() === ASTNode.types.NEXT || + newNode.getType() === ASTNode.types.BLOCK) + ) { newNode = newNode.next(); } @@ -61,15 +63,17 @@ export class Cursor extends Marker { * @returns The in element, or null if the current node is not set or there is * no in value. */ - in(): ASTNode|null { - let curNode: ASTNode|null = this.getCurNode(); + in(): ASTNode | null { + let curNode: ASTNode | null = this.getCurNode(); if (!curNode) { return null; } // If we are on a previous or output connection, go to the block level // before performing next operation. - if (curNode.getType() === ASTNode.types.PREVIOUS || - curNode.getType() === ASTNode.types.OUTPUT) { + if ( + curNode.getType() === ASTNode.types.PREVIOUS || + curNode.getType() === ASTNode.types.OUTPUT + ) { curNode = curNode.next(); } const newNode = curNode?.in() ?? null; @@ -86,16 +90,19 @@ export class Cursor extends Marker { * @returns The previous element, or null if the current node is not set or * there is no previous value. */ - prev(): ASTNode|null { + prev(): ASTNode | null { const curNode = this.getCurNode(); if (!curNode) { return null; } let newNode = curNode.prev(); - while (newNode && newNode.prev() && - (newNode.getType() === ASTNode.types.NEXT || - newNode.getType() === ASTNode.types.BLOCK)) { + while ( + newNode && + newNode.prev() && + (newNode.getType() === ASTNode.types.NEXT || + newNode.getType() === ASTNode.types.BLOCK) + ) { newNode = newNode.prev(); } @@ -111,7 +118,7 @@ export class Cursor extends Marker { * @returns The out element, or null if the current node is not set or there * is no out value. */ - out(): ASTNode|null { + out(): ASTNode | null { const curNode = this.getCurNode(); if (!curNode) { return null; diff --git a/core/keyboard_nav/marker.ts b/core/keyboard_nav/marker.ts index da57e484d96..489aa95ac2f 100644 --- a/core/keyboard_nav/marker.ts +++ b/core/keyboard_nav/marker.ts @@ -18,14 +18,13 @@ import type {MarkerSvg} from '../renderers/common/marker_svg.js'; import type {ASTNode} from './ast_node.js'; - /** * Class for a marker. * This is used in keyboard navigation to save a location in the Blockly AST. */ export class Marker { /** The colour of the marker. */ - colour: string|null = null; + colour: string | null = null; /** The current location of the marker. */ // AnyDuringMigration because: Type 'null' is not assignable to type diff --git a/core/keyboard_nav/tab_navigate_cursor.ts b/core/keyboard_nav/tab_navigate_cursor.ts index eb4395969be..7462358cf9a 100644 --- a/core/keyboard_nav/tab_navigate_cursor.ts +++ b/core/keyboard_nav/tab_navigate_cursor.ts @@ -18,7 +18,6 @@ import type {Field} from '../field.js'; import {ASTNode} from './ast_node.js'; import {BasicCursor} from './basic_cursor.js'; - /** * A cursor for navigating between tab navigable fields. */ @@ -29,13 +28,17 @@ export class TabNavigateCursor extends BasicCursor { * @param node The AST node to check whether it is valid. * @returns True if the node should be visited, false otherwise. */ - override validNode_(node: ASTNode|null): boolean { + override validNode_(node: ASTNode | null): boolean { let isValid = false; const type = node && node.getType(); if (node) { const location = node.getLocation() as Field; - if (type === ASTNode.types.FIELD && location && - location.isTabNavigable() && location.isClickable()) { + if ( + type === ASTNode.types.FIELD && + location && + location.isTabNavigable() && + location.isClickable() + ) { isValid = true; } } diff --git a/core/main.js b/core/main.js index 015157ab062..12228be72f6 100644 --- a/core/main.js +++ b/core/main.js @@ -44,15 +44,22 @@ Object.defineProperties(Blockly, { * @suppress {checkTypes} */ alert: { - set: function(newAlert) { + set: function (newAlert) { deprecation.warn( - 'Blockly.alert', 'version 9', 'version 10', - 'Blockly.dialog.setAlert'); + 'Blockly.alert', + 'version 9', + 'version 10', + 'Blockly.dialog.setAlert' + ); dialog.setAlert(newAlert); }, - get: function() { + get: function () { deprecation.warn( - 'Blockly.alert', 'version 9', 'version 10', 'Blockly.dialog.alert'); + 'Blockly.alert', + 'version 9', + 'version 10', + 'Blockly.dialog.alert' + ); return dialog.alert; }, }, @@ -66,16 +73,22 @@ Object.defineProperties(Blockly, { * @suppress {checkTypes} */ confirm: { - set: function(newConfirm) { + set: function (newConfirm) { deprecation.warn( - 'Blockly.confirm', 'version 9', 'version 10', - 'Blockly.dialog.setConfirm'); + 'Blockly.confirm', + 'version 9', + 'version 10', + 'Blockly.dialog.setConfirm' + ); dialog.setConfirm(newConfirm); }, - get: function() { + get: function () { deprecation.warn( - 'Blockly.confirm', 'version 9', 'version 10', - 'Blockly.dialog.confirm'); + 'Blockly.confirm', + 'version 9', + 'version 10', + 'Blockly.dialog.confirm' + ); return dialog.confirm; }, }, @@ -87,16 +100,22 @@ Object.defineProperties(Blockly, { * @suppress {checkTypes} */ mainWorkspace: { - set: function(x) { + set: function (x) { deprecation.warn( - 'Blockly.mainWorkspace', 'version 9', 'version 10', - 'Blockly.getMainWorkspace'); + 'Blockly.mainWorkspace', + 'version 9', + 'version 10', + 'Blockly.getMainWorkspace' + ); common.setMainWorkspace(x); }, - get: function() { + get: function () { deprecation.warn( - 'Blockly.mainWorkspace', 'version 9', 'version 10', - 'Blockly.getMainWorkspace'); + 'Blockly.mainWorkspace', + 'version 9', + 'version 10', + 'Blockly.getMainWorkspace' + ); return common.getMainWorkspace(); }, }, @@ -113,15 +132,22 @@ Object.defineProperties(Blockly, { * @suppress {checkTypes} */ prompt: { - set: function(newPrompt) { + set: function (newPrompt) { deprecation.warn( - 'Blockly.prompt', 'version 9', 'version 10', - 'Blockly.dialog.setPrompt'); + 'Blockly.prompt', + 'version 9', + 'version 10', + 'Blockly.dialog.setPrompt' + ); dialog.setPrompt(newPrompt); }, - get: function() { + get: function () { deprecation.warn( - 'Blockly.prompt', 'version 9', 'version 10', 'Blockly.dialog.prompt'); + 'Blockly.prompt', + 'version 9', + 'version 10', + 'Blockly.dialog.prompt' + ); return dialog.prompt; }, }, @@ -132,14 +158,22 @@ Object.defineProperties(Blockly, { * @suppress {checkTypes} */ selected: { - get: function() { + get: function () { deprecation.warn( - 'Blockly.selected', 'version 9', 'version 10', 'Blockly.getSelected'); + 'Blockly.selected', + 'version 9', + 'version 10', + 'Blockly.getSelected' + ); return common.getSelected(); }, - set: function(newSelection) { + set: function (newSelection) { deprecation.warn( - 'Blockly.selected', 'version 9', 'version 10', 'Blockly.getSelected'); + 'Blockly.selected', + 'version 9', + 'version 10', + 'Blockly.getSelected' + ); common.setSelected(newSelection); }, }, @@ -151,10 +185,10 @@ Object.defineProperties(Blockly, { * @suppress {checkTypes} */ HSV_SATURATION: { - get: function() { + get: function () { return colour.getHsvSaturation(); }, - set: function(newValue) { + set: function (newValue) { colour.setHsvSaturation(newValue); }, }, @@ -166,10 +200,10 @@ Object.defineProperties(Blockly, { * @suppress {checkTypes} */ HSV_VALUE: { - get: function() { + get: function () { return colour.getHsvValue(); }, - set: function(newValue) { + set: function (newValue) { colour.setHsvValue(newValue); }, }, @@ -187,16 +221,22 @@ Object.defineProperties(ContextMenu, { * @suppress {checkTypes} */ currentBlock: { - get: function() { + get: function () { deprecation.warn( - 'Blockly.ContextMenu.currentBlock', 'September 2021', - 'September 2022', 'Blockly.Tooltip.getCurrentBlock()'); + 'Blockly.ContextMenu.currentBlock', + 'September 2021', + 'September 2022', + 'Blockly.Tooltip.getCurrentBlock()' + ); return ContextMenu.getCurrentBlock(); }, - set: function(block) { + set: function (block) { deprecation.warn( - 'Blockly.ContextMenu.currentBlock', 'September 2021', - 'September 2022', 'Blockly.Tooltip.setCurrentBlock(block)'); + 'Blockly.ContextMenu.currentBlock', + 'September 2021', + 'September 2022', + 'Blockly.Tooltip.setCurrentBlock(block)' + ); ContextMenu.setCurrentBlock(block); }, }, @@ -214,22 +254,27 @@ Object.defineProperties(Events, { * @suppress {checkTypes} */ recordUndo: { - get: function() { + get: function () { deprecation.warn( - 'Blockly.Events.recordUndo', 'September 2021', 'September 2022', - 'Blockly.Events.getRecordUndo()'); + 'Blockly.Events.recordUndo', + 'September 2021', + 'September 2022', + 'Blockly.Events.getRecordUndo()' + ); return eventUtils.getRecordUndo(); }, - set: function(record) { + set: function (record) { deprecation.warn( - 'Blockly.Events.recordUndo', 'September 2021', 'September 2022', - 'Blockly.Events.setRecordUndo()'); + 'Blockly.Events.recordUndo', + 'September 2021', + 'September 2022', + 'Blockly.Events.setRecordUndo()' + ); eventUtils.setRecordUndo(record); }, }, }); - // Add accessors for properties on Blockly.Tooltip that have now been // deprecated. Object.defineProperties(Tooltip, { @@ -242,10 +287,13 @@ Object.defineProperties(Tooltip, { * @suppress {checkTypes} */ visible: { - get: function() { + get: function () { deprecation.warn( - 'Blockly.Tooltip.visible', 'September 2021', 'September 2022', - 'Blockly.Tooltip.isVisible()'); + 'Blockly.Tooltip.visible', + 'September 2021', + 'September 2022', + 'Blockly.Tooltip.isVisible()' + ); return Tooltip.isVisible(); }, }, @@ -258,10 +306,13 @@ Object.defineProperties(Tooltip, { * @suppress {checkTypes} */ DIV: { - get: function() { + get: function () { deprecation.warn( - 'Blockly.Tooltip.DIV', 'September 2021', 'September 2022', - 'Blockly.Tooltip.getDiv()'); + 'Blockly.Tooltip.DIV', + 'September 2021', + 'September 2022', + 'Blockly.Tooltip.getDiv()' + ); return Tooltip.getDiv(); }, }, @@ -279,10 +330,13 @@ Object.defineProperties(WidgetDiv, { * @suppress {checkTypes} */ DIV: { - get: function() { + get: function () { deprecation.warn( - 'Blockly.WidgetDiv.DIV', 'September 2021', 'September 2022', - 'Blockly.WidgetDiv.getDiv()'); + 'Blockly.WidgetDiv.DIV', + 'September 2021', + 'September 2022', + 'Blockly.WidgetDiv.getDiv()' + ); return WidgetDiv.getDiv(); }, }, diff --git a/core/marker_manager.ts b/core/marker_manager.ts index 653e220a960..d93c0ebff79 100644 --- a/core/marker_manager.ts +++ b/core/marker_manager.ts @@ -16,7 +16,6 @@ import type {Cursor} from './keyboard_nav/cursor.js'; import type {Marker} from './keyboard_nav/marker.js'; import type {WorkspaceSvg} from './workspace_svg.js'; - /** * Class to manage the multiple markers and the cursor on a workspace. */ @@ -25,16 +24,16 @@ export class MarkerManager { static readonly LOCAL_MARKER = 'local_marker_1'; /** The cursor. */ - private cursor_: Cursor|null = null; + private cursor_: Cursor | null = null; /** The cursor's SVG element. */ - private cursorSvg_: SVGElement|null = null; + private cursorSvg_: SVGElement | null = null; /** The map of markers for the workspace. */ private markers = new Map(); /** The marker's SVG element. */ - private markerSvg_: SVGElement|null = null; + private markerSvg_: SVGElement | null = null; /** * @param workspace The workspace for the marker manager. @@ -53,7 +52,8 @@ export class MarkerManager { this.unregisterMarker(id); } marker.setDrawer( - this.workspace.getRenderer().makeMarkerDrawer(this.workspace, marker)); + this.workspace.getRenderer().makeMarkerDrawer(this.workspace, marker) + ); this.setMarkerSvg(marker.getDrawer().createDom()); this.markers.set(id, marker); } @@ -70,8 +70,11 @@ export class MarkerManager { this.markers.delete(id); } else { throw Error( - 'Marker with ID ' + id + ' does not exist. ' + - 'Can only unregister markers that exist.'); + 'Marker with ID ' + + id + + ' does not exist. ' + + 'Can only unregister markers that exist.' + ); } } @@ -80,7 +83,7 @@ export class MarkerManager { * * @returns The cursor for this workspace. */ - getCursor(): Cursor|null { + getCursor(): Cursor | null { return this.cursor_; } @@ -91,7 +94,7 @@ export class MarkerManager { * @returns The marker that corresponds to the given ID, or null if none * exists. */ - getMarker(id: string): Marker|null { + getMarker(id: string): Marker | null { return this.markers.get(id) || null; } @@ -107,8 +110,9 @@ export class MarkerManager { } this.cursor_ = cursor; if (this.cursor_) { - const drawer = this.workspace.getRenderer().makeMarkerDrawer( - this.workspace, this.cursor_); + const drawer = this.workspace + .getRenderer() + .makeMarkerDrawer(this.workspace, this.cursor_); this.cursor_.setDrawer(drawer); this.setCursorSvg(this.cursor_.getDrawer().createDom()); } @@ -121,7 +125,7 @@ export class MarkerManager { * SVG group. * @internal */ - setCursorSvg(cursorSvg: SVGElement|null) { + setCursorSvg(cursorSvg: SVGElement | null) { if (!cursorSvg) { this.cursorSvg_ = null; return; @@ -138,7 +142,7 @@ export class MarkerManager { * SVG group. * @internal */ - setMarkerSvg(markerSvg: SVGElement|null) { + setMarkerSvg(markerSvg: SVGElement | null) { if (!markerSvg) { this.markerSvg_ = null; return; @@ -146,8 +150,9 @@ export class MarkerManager { if (this.workspace.getBlockCanvas()) { if (this.cursorSvg_) { - this.workspace.getBlockCanvas()!.insertBefore( - markerSvg, this.cursorSvg_); + this.workspace + .getBlockCanvas()! + .insertBefore(markerSvg, this.cursorSvg_); } else { this.workspace.getBlockCanvas()!.appendChild(markerSvg); } @@ -174,7 +179,7 @@ export class MarkerManager { */ dispose() { const markerIds = Object.keys(this.markers); - for (let i = 0, markerId; markerId = markerIds[i]; i++) { + for (let i = 0, markerId; (markerId = markerIds[i]); i++) { this.unregisterMarker(markerId); } this.markers.clear(); diff --git a/core/menu.ts b/core/menu.ts index d2ff7d53c76..b5a214515eb 100644 --- a/core/menu.ts +++ b/core/menu.ts @@ -20,7 +20,6 @@ import * as dom from './utils/dom.js'; import type {Size} from './utils/size.js'; import * as style from './utils/style.js'; - /** * A basic menu class. */ @@ -37,34 +36,34 @@ export class Menu { * prevent the consequent mouseup event due to a simple click from * activating a menu item immediately. */ - openingCoords: Coordinate|null = null; + openingCoords: Coordinate | null = null; /** * This is the element that we will listen to the real focus events on. * A value of null means no menu item is highlighted. */ - private highlightedItem: MenuItem|null = null; + private highlightedItem: MenuItem | null = null; /** Mouse over event data. */ - private mouseOverHandler: browserEvents.Data|null = null; + private mouseOverHandler: browserEvents.Data | null = null; /** Click event data. */ - private clickHandler: browserEvents.Data|null = null; + private clickHandler: browserEvents.Data | null = null; /** Mouse enter event data. */ - private mouseEnterHandler: browserEvents.Data|null = null; + private mouseEnterHandler: browserEvents.Data | null = null; /** Mouse leave event data. */ - private mouseLeaveHandler: browserEvents.Data|null = null; + private mouseLeaveHandler: browserEvents.Data | null = null; /** Key down event data. */ - private onKeyDownHandler: browserEvents.Data|null = null; + private onKeyDownHandler: browserEvents.Data | null = null; /** The menu's root DOM element. */ - private element: HTMLDivElement|null = null; + private element: HTMLDivElement | null = null; /** ARIA name for this menu. */ - private roleName: aria.Role|null = null; + private roleName: aria.Role | null = null; /** Constructs a new Menu instance. */ constructor() {} @@ -86,7 +85,7 @@ export class Menu { * @returns The menu's root DOM element. */ render(container: Element): HTMLDivElement { - const element = (document.createElement('div')); + const element = document.createElement('div'); // goog-menu is deprecated, use blocklyMenu. May 2020. element.className = 'blocklyMenu goog-menu blocklyNonSelectable'; element.tabIndex = 0; @@ -96,21 +95,45 @@ export class Menu { this.element = element; // Add menu items. - for (let i = 0, menuItem; menuItem = this.menuItems[i]; i++) { + for (let i = 0, menuItem; (menuItem = this.menuItems[i]); i++) { element.appendChild(menuItem.createDom()); } // Add event handlers. this.mouseOverHandler = browserEvents.conditionalBind( - element, 'pointerover', this, this.handleMouseOver, true); + element, + 'pointerover', + this, + this.handleMouseOver, + true + ); this.clickHandler = browserEvents.conditionalBind( - element, 'pointerup', this, this.handleClick, true); + element, + 'pointerup', + this, + this.handleClick, + true + ); this.mouseEnterHandler = browserEvents.conditionalBind( - element, 'pointerenter', this, this.handleMouseEnter, true); + element, + 'pointerenter', + this, + this.handleMouseEnter, + true + ); this.mouseLeaveHandler = browserEvents.conditionalBind( - element, 'pointerleave', this, this.handleMouseLeave, true); + element, + 'pointerleave', + this, + this.handleMouseLeave, + true + ); this.onKeyDownHandler = browserEvents.conditionalBind( - element, 'keydown', this, this.handleKeyEvent); + element, + 'keydown', + this, + this.handleKeyEvent + ); container.appendChild(element); return element; @@ -122,7 +145,7 @@ export class Menu { * @returns The DOM element. * @internal */ - getElement(): HTMLDivElement|null { + getElement(): HTMLDivElement | null { return this.element; } @@ -183,7 +206,7 @@ export class Menu { } // Remove menu items. - for (let i = 0, menuItem; menuItem = this.menuItems[i]; i++) { + for (let i = 0, menuItem; (menuItem = this.menuItems[i]); i++) { menuItem.dispose(); } this.element = null; @@ -198,17 +221,17 @@ export class Menu { * @param elem DOM element whose owner is to be returned. * @returns Menu item for which the DOM element belongs to. */ - private getMenuItem(elem: Element): MenuItem|null { + private getMenuItem(elem: Element): MenuItem | null { const menuElem = this.getElement(); // Node might be the menu border (resulting in no associated menu item), or // a menu item's div, or some element within the menu item. // Walk up parents until one meets either the menu's root element, or // a menu item's div. - let currentElement: Element|null = elem; + let currentElement: Element | null = elem; while (currentElement && currentElement !== menuElem) { if (currentElement.classList.contains('blocklyMenuItem')) { // Having found a menu item's div, locate that menu item in this menu. - for (let i = 0, menuItem; menuItem = this.menuItems[i]; i++) { + for (let i = 0, menuItem; (menuItem = this.menuItems[i]); i++) { if (menuItem.getElement() === currentElement) { return menuItem; } @@ -227,7 +250,7 @@ export class Menu { * @param item Item to highlight, or null. * @internal */ - setHighlighted(item: MenuItem|null) { + setHighlighted(item: MenuItem | null) { const currentHighlighted = this.highlightedItem; if (currentHighlighted) { currentHighlighted.setHighlighted(false); @@ -252,9 +275,9 @@ export class Menu { * @internal */ highlightNext() { - const index = this.highlightedItem ? - this.menuItems.indexOf(this.highlightedItem) : - -1; + const index = this.highlightedItem + ? this.menuItems.indexOf(this.highlightedItem) + : -1; this.highlightHelper(index, 1); } @@ -265,9 +288,9 @@ export class Menu { * @internal */ highlightPrevious() { - const index = this.highlightedItem ? - this.menuItems.indexOf(this.highlightedItem) : - -1; + const index = this.highlightedItem + ? this.menuItems.indexOf(this.highlightedItem) + : -1; this.highlightHelper(index < 0 ? this.menuItems.length : index, -1); } @@ -291,7 +314,7 @@ export class Menu { private highlightHelper(startIndex: number, delta: number) { let index = startIndex + delta; let menuItem; - while (menuItem = this.menuItems[index]) { + while ((menuItem = this.menuItems[index])) { if (menuItem.isEnabled()) { this.setHighlighted(menuItem); break; @@ -384,8 +407,12 @@ export class Menu { return; } const keyboardEvent = e as KeyboardEvent; - if (keyboardEvent.shiftKey || keyboardEvent.ctrlKey || - keyboardEvent.metaKey || keyboardEvent.altKey) { + if ( + keyboardEvent.shiftKey || + keyboardEvent.ctrlKey || + keyboardEvent.metaKey || + keyboardEvent.altKey + ) { // Do not handle the key event if any modifier key is pressed. return; } diff --git a/core/menuitem.ts b/core/menuitem.ts index 55c623ff8d2..f2df7f94e56 100644 --- a/core/menuitem.ts +++ b/core/menuitem.ts @@ -16,7 +16,6 @@ import * as aria from './utils/aria.js'; import * as dom from './utils/dom.js'; import * as idGenerator from './utils/idgenerator.js'; - /** * Class representing an item in a menu. */ @@ -25,13 +24,13 @@ export class MenuItem { private enabled = true; /** The DOM element for the menu item. */ - private element: HTMLDivElement|null = null; + private element: HTMLDivElement | null = null; /** Whether the menu item is rendered right-to-left. */ private rightToLeft = false; /** ARIA name for this menu. */ - private roleName: aria.Role|null = null; + private roleName: aria.Role | null = null; /** Is this menu item checkable. */ private checkable = false; @@ -43,7 +42,7 @@ export class MenuItem { private highlight = false; /** Bound function to call when this menu item is clicked. */ - private actionHandler: Function|null = null; + private actionHandler: Function | null = null; /** * @param content Text caption to display as the content of the item, or a @@ -51,8 +50,9 @@ export class MenuItem { * @param opt_value Data/model associated with the menu item. */ constructor( - private readonly content: string|HTMLElement, - private readonly opt_value?: string) {} + private readonly content: string | HTMLElement, + private readonly opt_value?: string + ) {} /** * Creates the menuitem's DOM. @@ -60,25 +60,26 @@ export class MenuItem { * @returns Completed DOM. */ createDom(): Element { - const element = (document.createElement('div')); + const element = document.createElement('div'); element.id = idGenerator.getNextUniqueId(); this.element = element; // Set class and style // goog-menuitem* is deprecated, use blocklyMenuItem*. May 2020. - element.className = 'blocklyMenuItem goog-menuitem ' + - (this.enabled ? '' : - 'blocklyMenuItemDisabled goog-menuitem-disabled ') + - (this.checked ? 'blocklyMenuItemSelected goog-option-selected ' : '') + - (this.highlight ? 'blocklyMenuItemHighlight goog-menuitem-highlight ' : - '') + - (this.rightToLeft ? 'blocklyMenuItemRtl goog-menuitem-rtl ' : ''); - - const content = (document.createElement('div')); + element.className = + 'blocklyMenuItem goog-menuitem ' + + (this.enabled ? '' : 'blocklyMenuItemDisabled goog-menuitem-disabled ') + + (this.checked ? 'blocklyMenuItemSelected goog-option-selected ' : '') + + (this.highlight + ? 'blocklyMenuItemHighlight goog-menuitem-highlight ' + : '') + + (this.rightToLeft ? 'blocklyMenuItemRtl goog-menuitem-rtl ' : ''); + + const content = document.createElement('div'); content.className = 'blocklyMenuItemContent goog-menuitem-content'; // Add a checkbox for checkable menu items. if (this.checkable) { - const checkbox = (document.createElement('div')); + const checkbox = document.createElement('div'); checkbox.className = 'blocklyMenuItemCheckbox goog-menuitem-checkbox'; content.appendChild(checkbox); } @@ -95,7 +96,10 @@ export class MenuItem { aria.setRole(element, this.roleName); } aria.setState( - element, aria.State.SELECTED, this.checkable && this.checked || false); + element, + aria.State.SELECTED, + (this.checkable && this.checked) || false + ); aria.setState(element, aria.State.DISABLED, !this.enabled); return element; @@ -112,7 +116,7 @@ export class MenuItem { * @returns The DOM element. * @internal */ - getElement(): Element|null { + getElement(): Element | null { return this.element; } @@ -132,7 +136,7 @@ export class MenuItem { * @returns value Value associated with the menu item. * @internal */ - getValue(): string|null { + getValue(): string | null { return this.opt_value ?? null; } diff --git a/core/metrics_manager.ts b/core/metrics_manager.ts index 615d1938533..a24d257d1f9 100644 --- a/core/metrics_manager.ts +++ b/core/metrics_manager.ts @@ -21,7 +21,6 @@ import {Size} from './utils/size.js'; import * as toolboxUtils from './utils/toolbox.js'; import type {WorkspaceSvg} from './workspace_svg.js'; - /** * The manager for all workspace metrics calculations. */ @@ -42,7 +41,7 @@ export class MetricsManager implements IMetricsManager { * @returns An object containing width and height attributes, which will both * be zero if elem did not exist. */ - protected getDimensionsPx_(elem: IToolbox|null|IFlyout): Size { + protected getDimensionsPx_(elem: IToolbox | null | IFlyout): Size { let width = 0; let height = 0; if (elem) { @@ -64,8 +63,9 @@ export class MetricsManager implements IMetricsManager { * @returns The width and height of the flyout. */ getFlyoutMetrics(opt_own?: boolean): ToolboxMetrics { - const flyoutDimensions = - this.getDimensionsPx_(this.workspace_.getFlyout(opt_own)); + const flyoutDimensions = this.getDimensionsPx_( + this.workspace_.getFlyout(opt_own) + ); return { width: flyoutDimensions.width, height: flyoutDimensions.height, @@ -82,8 +82,9 @@ export class MetricsManager implements IMetricsManager { * @returns The object with the width, height and position of the toolbox. */ getToolboxMetrics(): ToolboxMetrics { - const toolboxDimensions = - this.getDimensionsPx_(this.workspace_.getToolbox()); + const toolboxDimensions = this.getDimensionsPx_( + this.workspace_.getToolbox() + ); return { width: toolboxDimensions.width, @@ -115,8 +116,9 @@ export class MetricsManager implements IMetricsManager { const flyoutMetrics = this.getFlyoutMetrics(true); const doesToolboxExist = !!this.workspace_.getToolbox(); const doesFlyoutExist = !!this.workspace_.getFlyout(true); - const toolboxPosition = - doesToolboxExist ? toolboxMetrics.position : flyoutMetrics.position; + const toolboxPosition = doesToolboxExist + ? toolboxMetrics.position + : flyoutMetrics.position; const atLeft = toolboxPosition === toolboxUtils.Position.LEFT; const atTop = toolboxPosition === toolboxUtils.Position.TOP; @@ -153,25 +155,32 @@ export class MetricsManager implements IMetricsManager { const toolboxMetrics = this.getToolboxMetrics(); const flyoutMetrics = this.getFlyoutMetrics(true); const doesToolboxExist = !!this.workspace_.getToolbox(); - const toolboxPosition = - doesToolboxExist ? toolboxMetrics.position : flyoutMetrics.position; + const toolboxPosition = doesToolboxExist + ? toolboxMetrics.position + : flyoutMetrics.position; if (this.workspace_.getToolbox()) { - if (toolboxPosition === toolboxUtils.Position.TOP || - toolboxPosition === toolboxUtils.Position.BOTTOM) { + if ( + toolboxPosition === toolboxUtils.Position.TOP || + toolboxPosition === toolboxUtils.Position.BOTTOM + ) { svgMetrics.height -= toolboxMetrics.height; } else if ( - toolboxPosition === toolboxUtils.Position.LEFT || - toolboxPosition === toolboxUtils.Position.RIGHT) { + toolboxPosition === toolboxUtils.Position.LEFT || + toolboxPosition === toolboxUtils.Position.RIGHT + ) { svgMetrics.width -= toolboxMetrics.width; } } else if (this.workspace_.getFlyout(true)) { - if (toolboxPosition === toolboxUtils.Position.TOP || - toolboxPosition === toolboxUtils.Position.BOTTOM) { + if ( + toolboxPosition === toolboxUtils.Position.TOP || + toolboxPosition === toolboxUtils.Position.BOTTOM + ) { svgMetrics.height -= flyoutMetrics.height; } else if ( - toolboxPosition === toolboxUtils.Position.LEFT || - toolboxPosition === toolboxUtils.Position.RIGHT) { + toolboxPosition === toolboxUtils.Position.LEFT || + toolboxPosition === toolboxUtils.Position.RIGHT + ) { svgMetrics.width -= flyoutMetrics.width; } } @@ -213,8 +222,10 @@ export class MetricsManager implements IMetricsManager { */ hasFixedEdges(): boolean { // This exists for optimization of bump logic. - return !this.workspace_.isMovableHorizontally() || - !this.workspace_.isMovableVertically(); + return ( + !this.workspace_.isMovableHorizontally() || + !this.workspace_.isMovableVertically() + ); } /** @@ -225,8 +236,9 @@ export class MetricsManager implements IMetricsManager { * again, if it is needed. * @returns The fixed edges of the scroll area. */ - protected getComputedFixedEdges_(opt_viewMetrics?: ContainerRegion): - FixedEdges { + protected getComputedFixedEdges_( + opt_viewMetrics?: ContainerRegion + ): FixedEdges { if (!this.hasFixedEdges()) { // Return early if there are no edges. return {}; @@ -257,8 +269,9 @@ export class MetricsManager implements IMetricsManager { * @returns The padded content area. */ protected getPaddedContent_( - viewMetrics: ContainerRegion, contentMetrics: ContainerRegion): - {top: number, bottom: number, left: number, right: number} { + viewMetrics: ContainerRegion, + contentMetrics: ContainerRegion + ): {top: number; bottom: number; left: number; right: number} { const contentBottom = contentMetrics.top + contentMetrics.height; const contentRight = contentMetrics.left + contentMetrics.width; @@ -269,14 +282,22 @@ export class MetricsManager implements IMetricsManager { // Add a padding around the content that is at least half a screen wide. // Ensure padding is wide enough that blocks can scroll over entire screen. - const top = - Math.min(contentMetrics.top - halfHeight, contentBottom - viewHeight); - const left = - Math.min(contentMetrics.left - halfWidth, contentRight - viewWidth); - const bottom = - Math.max(contentBottom + halfHeight, contentMetrics.top + viewHeight); - const right = - Math.max(contentRight + halfWidth, contentMetrics.left + viewWidth); + const top = Math.min( + contentMetrics.top - halfHeight, + contentBottom - viewHeight + ); + const left = Math.min( + contentMetrics.left - halfWidth, + contentRight - viewWidth + ); + const bottom = Math.max( + contentBottom + halfHeight, + contentMetrics.top + viewHeight + ); + const right = Math.max( + contentRight + halfWidth, + contentMetrics.left + viewWidth + ); return {top, bottom, left, right}; } @@ -295,8 +316,10 @@ export class MetricsManager implements IMetricsManager { * @returns The metrics for the scroll container. */ getScrollMetrics( - opt_getWorkspaceCoordinates?: boolean, opt_viewMetrics?: ContainerRegion, - opt_contentMetrics?: ContainerRegion): ContainerRegion { + opt_getWorkspaceCoordinates?: boolean, + opt_viewMetrics?: ContainerRegion, + opt_contentMetrics?: ContainerRegion + ): ContainerRegion { const scale = opt_getWorkspaceCoordinates ? this.workspace_.scale : 1; const viewMetrics = opt_viewMetrics || this.getViewMetrics(false); const contentMetrics = opt_contentMetrics || this.getContentMetrics(); @@ -307,13 +330,15 @@ export class MetricsManager implements IMetricsManager { // Use combination of fixed bounds and padded content to make scroll area. const top = - fixedEdges.top !== undefined ? fixedEdges.top : paddedContent.top; + fixedEdges.top !== undefined ? fixedEdges.top : paddedContent.top; const left = - fixedEdges.left !== undefined ? fixedEdges.left : paddedContent.left; - const bottom = fixedEdges.bottom !== undefined ? fixedEdges.bottom : - paddedContent.bottom; + fixedEdges.left !== undefined ? fixedEdges.left : paddedContent.left; + const bottom = + fixedEdges.bottom !== undefined + ? fixedEdges.bottom + : paddedContent.bottom; const right = - fixedEdges.right !== undefined ? fixedEdges.right : paddedContent.right; + fixedEdges.right !== undefined ? fixedEdges.right : paddedContent.right; return { top: top / scale, @@ -378,8 +403,11 @@ export class MetricsManager implements IMetricsManager { const absoluteMetrics = this.getAbsoluteMetrics(); const viewMetrics = this.getViewMetrics(); const contentMetrics = this.getContentMetrics(); - const scrollMetrics = - this.getScrollMetrics(false, viewMetrics, contentMetrics); + const scrollMetrics = this.getScrollMetrics( + false, + viewMetrics, + contentMetrics + ); return { contentHeight: contentMetrics.height, @@ -464,4 +492,7 @@ export type FixedEdges = MetricsManager.FixedEdges; export type UiMetrics = MetricsManager.UiMetrics; registry.register( - registry.Type.METRICS_MANAGER, registry.DEFAULT, MetricsManager); + registry.Type.METRICS_MANAGER, + registry.DEFAULT, + MetricsManager +); diff --git a/core/msg.ts b/core/msg.ts index 50fdf3a8f8b..7ca611253ca 100644 --- a/core/msg.ts +++ b/core/msg.ts @@ -7,7 +7,6 @@ import * as goog from '../closure/goog/goog.js'; goog.declareModuleId('Blockly.Msg'); - /** A dictionary of localised messages. */ export const Msg: {[key: string]: string} = Object.create(null); @@ -22,8 +21,8 @@ export const Msg: {[key: string]: string} = Object.create(null); * * @param locale An object defining the messages for a given language. */ -export const setLocale = function(locale: {[key: string]: string}) { - Object.keys(locale).forEach(function(k) { +export const setLocale = function (locale: {[key: string]: string}) { + Object.keys(locale).forEach(function (k) { Msg[k] = locale[k]; }); }; diff --git a/core/mutator.ts b/core/mutator.ts index f659c9f44aa..4027a89f983 100644 --- a/core/mutator.ts +++ b/core/mutator.ts @@ -35,7 +35,6 @@ import * as xml from './utils/xml.js'; import * as deprecation from './utils/deprecation.js'; import type {WorkspaceSvg} from './workspace_svg.js'; - /** * Class for a mutator dialog. */ @@ -47,7 +46,7 @@ export class Mutator extends Icon { * Due to legacy code in procedure block definitions, this name * cannot change. */ - private workspace_: WorkspaceSvg|null = null; + private workspace_: WorkspaceSvg | null = null; /** Width of workspace. */ private workspaceWidth = 0; @@ -59,33 +58,35 @@ export class Mutator extends Icon { * The SVG element that is the parent of the mutator workspace, or null if * not created. */ - private svgDialog: SVGSVGElement|null = null; + private svgDialog: SVGSVGElement | null = null; /** * The root block of the mutator workspace, created by decomposing the * source block. */ - private rootBlock: BlockSvg|null = null; + private rootBlock: BlockSvg | null = null; /** * Function registered on the main workspace to update the mutator contents * when the main workspace changes. */ - private sourceListener: Function|null = null; + private sourceListener: Function | null = null; /** * The PID associated with the updateWorkpace_ timeout, or null if no timeout * is currently running. */ - private updateWorkspacePid: ReturnType|null = null; + private updateWorkspacePid: ReturnType | null = null; /** @param quarkNames List of names of sub-blocks for flyout. */ constructor(quarkNames: string[], block?: BlockSvg) { if (!block) { deprecation.warn( - 'Calling the Mutator constructor without passing the block it is attached to', - 'version 9', 'version 10', - 'the constructor by passing the list of subblocks and the block instance to attach the mutator to'); + 'Calling the Mutator constructor without passing the block it is attached to', + 'version 9', + 'version 10', + 'the constructor by passing the list of subblocks and the block instance to attach the mutator to' + ); } super(block ?? null); this.quarkNames = quarkNames; @@ -108,7 +109,7 @@ export class Mutator extends Icon { * mutator isn't open. * @internal */ - getWorkspace(): WorkspaceSvg|null { + getWorkspace(): WorkspaceSvg | null { return this.workspace_; } @@ -120,31 +121,38 @@ export class Mutator extends Icon { protected override drawIcon_(group: Element) { // Square with rounded corners. dom.createSvgElement( - Svg.RECT, { - 'class': 'blocklyIconShape', - 'rx': '4', - 'ry': '4', - 'height': '16', - 'width': '16', - }, - group); + Svg.RECT, + { + 'class': 'blocklyIconShape', + 'rx': '4', + 'ry': '4', + 'height': '16', + 'width': '16', + }, + group + ); // Gear teeth. dom.createSvgElement( - Svg.PATH, { - 'class': 'blocklyIconSymbol', - 'd': 'm4.203,7.296 0,1.368 -0.92,0.677 -0.11,0.41 0.9,1.559 0.41,' + - '0.11 1.043,-0.457 1.187,0.683 0.127,1.134 0.3,0.3 1.8,0 0.3,' + - '-0.299 0.127,-1.138 1.185,-0.682 1.046,0.458 0.409,-0.11 0.9,' + - '-1.559 -0.11,-0.41 -0.92,-0.677 0,-1.366 0.92,-0.677 0.11,' + - '-0.41 -0.9,-1.559 -0.409,-0.109 -1.046,0.458 -1.185,-0.682 ' + - '-0.127,-1.138 -0.3,-0.299 -1.8,0 -0.3,0.3 -0.126,1.135 -1.187,' + - '0.682 -1.043,-0.457 -0.41,0.11 -0.899,1.559 0.108,0.409z', - }, - group); + Svg.PATH, + { + 'class': 'blocklyIconSymbol', + 'd': + 'm4.203,7.296 0,1.368 -0.92,0.677 -0.11,0.41 0.9,1.559 0.41,' + + '0.11 1.043,-0.457 1.187,0.683 0.127,1.134 0.3,0.3 1.8,0 0.3,' + + '-0.299 0.127,-1.138 1.185,-0.682 1.046,0.458 0.409,-0.11 0.9,' + + '-1.559 -0.11,-0.41 -0.92,-0.677 0,-1.366 0.92,-0.677 0.11,' + + '-0.41 -0.9,-1.559 -0.409,-0.109 -1.046,0.458 -1.185,-0.682 ' + + '-0.127,-1.138 -0.3,-0.299 -1.8,0 -0.3,0.3 -0.126,1.135 -1.187,' + + '0.682 -1.043,-0.457 -0.41,0.11 -0.899,1.559 0.108,0.409z', + }, + group + ); // Axle hole. dom.createSvgElement( - Svg.CIRCLE, - {'class': 'blocklyIconShape', 'r': '2.7', 'cx': '8', 'cy': '8'}, group); + Svg.CIRCLE, + {'class': 'blocklyIconShape', 'r': '2.7', 'cx': '8', 'cy': '8'}, + group + ); } /** @@ -170,13 +178,15 @@ export class Mutator extends Icon { [Workspace] */ - this.svgDialog = dom.createSvgElement( - Svg.SVG, {'x': Bubble.BORDER_WIDTH, 'y': Bubble.BORDER_WIDTH}); + this.svgDialog = dom.createSvgElement(Svg.SVG, { + 'x': Bubble.BORDER_WIDTH, + 'y': Bubble.BORDER_WIDTH, + }); // Convert the list of names into a list of XML objects for the flyout. let quarkXml; if (this.quarkNames.length) { quarkXml = xml.createElement('xml'); - for (let i = 0, quarkName; quarkName = this.quarkNames[i]; i++) { + for (let i = 0, quarkName; (quarkName = this.quarkNames[i]); i++) { const element = xml.createElement('block'); element.setAttribute('type', quarkName); quarkXml.appendChild(element); @@ -185,7 +195,7 @@ export class Mutator extends Icon { quarkXml = null; } const block = this.getBlock(); - const workspaceOptions = new Options(({ + const workspaceOptions = new Options({ // If you want to enable disabling, also remove the // event filter from workspaceChanged_ . 'disable': false, @@ -195,9 +205,10 @@ export class Mutator extends Icon { 'horizontalLayout': false, 'renderer': block.workspace.options.renderer, 'rendererOverrides': block.workspace.options.rendererOverrides, - } as BlocklyOptions)); - workspaceOptions.toolboxPosition = - block.RTL ? toolbox.Position.RIGHT : toolbox.Position.LEFT; + } as BlocklyOptions); + workspaceOptions.toolboxPosition = block.RTL + ? toolbox.Position.RIGHT + : toolbox.Position.LEFT; const hasFlyout = !!quarkXml; if (hasFlyout) { workspaceOptions.languageTree = toolbox.convertToolboxDefToJson(quarkXml); @@ -230,8 +241,9 @@ export class Mutator extends Icon { // eslint-disable-next-line @typescript-eslint/no-unused-vars newWorkspaceSvg(options: Options): WorkspaceSvg { throw new Error( - 'The implementation of newWorkspaceSvg should be ' + - 'monkey-patched in by blockly.ts'); + 'The implementation of newWorkspaceSvg should be ' + + 'monkey-patched in by blockly.ts' + ); } /** Add or remove the UI indicating if this icon may be clicked or not. */ @@ -265,8 +277,10 @@ export class Mutator extends Icon { let height = workspaceSize.height + doubleBorderWidth * 3; const flyout = this.workspace_.getFlyout(); if (flyout) { - const flyoutScrollMetrics = - flyout.getWorkspace().getMetricsManager().getScrollMetrics(); + const flyoutScrollMetrics = flyout + .getWorkspace() + .getMetricsManager() + .getScrollMetrics(); height = Math.max(height, flyoutScrollMetrics.height + 20); width += flyout.getWidth(); } @@ -277,14 +291,18 @@ export class Mutator extends Icon { width += doubleBorderWidth * 3; // Only resize if the size difference is significant. Eliminates // shuddering. - if (Math.abs(this.workspaceWidth - width) > doubleBorderWidth || - Math.abs(this.workspaceHeight - height) > doubleBorderWidth) { + if ( + Math.abs(this.workspaceWidth - width) > doubleBorderWidth || + Math.abs(this.workspaceHeight - height) > doubleBorderWidth + ) { // Record some layout information for workspace metrics. this.workspaceWidth = width; this.workspaceHeight = height; // Resize the bubble. this.bubble_!.setBubbleSize( - width + doubleBorderWidth, height + doubleBorderWidth); + width + doubleBorderWidth, + height + doubleBorderWidth + ); this.svgDialog!.setAttribute('width', `${width}`); this.svgDialog!.setAttribute('height', `${height}`); this.workspace_.setCachedParentSvgSize(width, height); @@ -318,8 +336,13 @@ export class Mutator extends Icon { if (visible) { // Create the bubble. this.bubble_ = new Bubble( - block.workspace, this.createEditor(), block.pathObject.svgPath, - (this.iconXY_ as Coordinate), null, null); + block.workspace, + this.createEditor(), + block.pathObject.svgPath, + this.iconXY_ as Coordinate, + null, + null + ); // The workspace was created in createEditor. const ws = this.workspace_!; // Expose this mutator's block's ID on its top-level SVG group. @@ -334,7 +357,7 @@ export class Mutator extends Icon { this.rootBlock = block.decompose!(ws)!; const blocks = this.rootBlock.getDescendants(false); - for (let i = 0, child; child = blocks[i]; i++) { + for (let i = 0, child; (child = blocks[i]); i++) { child.queueRender(); } // The root block should not be draggable or deletable. @@ -388,8 +411,9 @@ export class Mutator extends Icon { this.sourceListener = null; } } - eventUtils.fire(new (eventUtils.get(eventUtils.BUBBLE_OPEN))( - block, visible, 'mutator')); + eventUtils.fire( + new (eventUtils.get(eventUtils.BUBBLE_OPEN))(block, visible, 'mutator') + ); } /** @@ -414,9 +438,12 @@ export class Mutator extends Icon { * @returns Whether to ignore the event or not. */ shouldIgnoreMutatorEvent_(e: Abstract) { - return e.isUiEvent || e.type === eventUtils.CREATE || - e.type === eventUtils.CHANGE && - (e as BlockChange).element === 'disabled'; + return ( + e.isUiEvent || + e.type === eventUtils.CREATE || + (e.type === eventUtils.CHANGE && + (e as BlockChange).element === 'disabled') + ); } /** @@ -428,7 +455,7 @@ export class Mutator extends Icon { const blocks = this.workspace_!.getTopBlocks(false); const MARGIN = 20; - for (let b = 0, block; block = blocks[b]; b++) { + for (let b = 0, block; (block = blocks[b]); b++) { const blockXY = block.getRelativeToSurfaceXY(); // Bump any block that's above the top back inside. @@ -464,11 +491,18 @@ export class Mutator extends Icon { const newExtraState = BlockChange.getExtraBlockState_(block); if (oldExtraState !== newExtraState) { - eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CHANGE))( - block, 'mutation', null, oldExtraState, newExtraState)); + eventUtils.fire( + new (eventUtils.get(eventUtils.BLOCK_CHANGE))( + block, + 'mutation', + null, + oldExtraState, + newExtraState + ) + ); // Ensure that any bump is part of this mutation's event group. const mutationGroup = eventUtils.getGroup(); - setTimeout(function() { + setTimeout(function () { const oldGroup = eventUtils.getGroup(); eventUtils.setGroup(mutationGroup); block.bumpNeighbours(); @@ -497,14 +531,14 @@ export class Mutator extends Icon { if (ws && ws.getAllBlocks(false)) { const workspaceBlocks = ws.getAllBlocks(false); - for (let i = 0, block; block = workspaceBlocks[i]; i++) { + for (let i = 0, block; (block = workspaceBlocks[i]); i++) { block.setStyle(block.getStyleName()); } const flyout = ws.getFlyout(); if (flyout) { const flyoutBlocks = flyout.getWorkspace().getAllBlocks(false); - for (let i = 0, block; block = flyoutBlocks[i]; i++) { + for (let i = 0, block; (block = flyoutBlocks[i]); i++) { block.setStyle(block.getStyleName()); } } @@ -520,14 +554,20 @@ export class Mutator extends Icon { * @returns True iff a reconnection was made, false otherwise. */ static reconnect( - connectionChild: Connection, block: Block, inputName: string): boolean { + connectionChild: Connection, + block: Block, + inputName: string + ): boolean { if (!connectionChild || !connectionChild.getSourceBlock().workspace) { - return false; // No connection or block has been deleted. + return false; // No connection or block has been deleted. } const connectionParent = block.getInput(inputName)!.connection; const currentParent = connectionChild.targetBlock(); - if ((!currentParent || currentParent === block) && connectionParent && - connectionParent.targetConnection !== connectionChild) { + if ( + (!currentParent || currentParent === block) && + connectionParent && + connectionParent.targetConnection !== connectionChild + ) { if (connectionParent.isConnected()) { // There's already something connected here. Get rid of it. connectionParent.disconnect(); @@ -545,7 +585,7 @@ export class Mutator extends Icon { * @param workspace The workspace that is inside a mutator. * @returns The mutator's parent workspace or null. */ - static findParentWs(workspace: WorkspaceSvg): WorkspaceSvg|null { + static findParentWs(workspace: WorkspaceSvg): WorkspaceSvg | null { let outerWs = null; if (workspace && workspace.options) { const parent = workspace.options.parentWorkspace; diff --git a/core/names.ts b/core/names.ts index 3d6d0088708..8f8b45c0724 100644 --- a/core/names.ts +++ b/core/names.ts @@ -18,7 +18,6 @@ import type {VariableMap} from './variable_map.js'; import * as Variables from './variables.js'; import type {Workspace} from './workspace.js'; - /** * Class for a database of entity names (variables, procedures, etc). */ @@ -41,7 +40,7 @@ export class Names { /** * The variable map from the workspace, containing Blockly variable models. */ - private variableMap: VariableMap|null = null; + private variableMap: VariableMap | null = null; /** * @param reservedWordsList A comma-separated string of words that are illegal @@ -53,8 +52,9 @@ export class Names { /** The prefix to attach to variable names in generated code. */ this.variablePrefix = opt_variablePrefix || ''; - this.reservedWords = - new Set(reservedWordsList ? reservedWordsList.split(',') : []); + this.reservedWords = new Set( + reservedWordsList ? reservedWordsList.split(',') : [] + ); } /** @@ -83,14 +83,15 @@ export class Names { * @returns The name of the referenced variable, or null if there was no * variable map or the variable was not found in the map. */ - private getNameForUserVariable(id: string): string|null { + private getNameForUserVariable(id: string): string | null { if (!this.variableMap) { console.warn( - 'Deprecated call to Names.prototype.getName without ' + + 'Deprecated call to Names.prototype.getName without ' + 'defining a variable map. To fix, add the following code in your ' + - 'generator\'s init() function:\n' + + "generator's init() function:\n" + 'Blockly.YourGeneratorName.nameDB_.setVariableMap(' + - 'workspace.getVariableMap());'); + 'workspace.getVariableMap());' + ); return null; } const variable = this.variableMap.getVariableById(id); @@ -120,8 +121,9 @@ export class Names { // eslint-disable-next-line @typescript-eslint/no-unused-vars populateProcedures(workspace: Workspace) { throw new Error( - 'The implementation of populateProcedures should be ' + - 'monkey-patched in by blockly.ts'); + 'The implementation of populateProcedures should be ' + + 'monkey-patched in by blockly.ts' + ); } /** @@ -132,7 +134,7 @@ export class Names { * 'DEVELOPER_VARIABLE', etc...). * @returns An entity name that is legal in the exported language. */ - getName(nameOrId: string, type: NameType|string): string { + getName(nameOrId: string, type: NameType | string): string { let name = nameOrId; if (type === NameType.VARIABLE) { const varName = this.getNameForUserVariable(nameOrId); @@ -144,7 +146,7 @@ export class Names { const normalizedName = name.toLowerCase(); const isVar = - type === NameType.VARIABLE || type === NameType.DEVELOPER_VARIABLE; + type === NameType.VARIABLE || type === NameType.DEVELOPER_VARIABLE; const prefix = isVar ? this.variablePrefix : ''; if (!this.db.has(type)) { @@ -166,7 +168,7 @@ export class Names { * 'DEVELOPER_VARIABLE', etc...). * @returns A list of Blockly entity names (no constraints). */ - getUserNames(type: NameType|string): string[] { + getUserNames(type: NameType | string): string[] { const userNames = this.db.get(type)?.keys(); return userNames ? Array.from(userNames) : []; } @@ -182,18 +184,20 @@ export class Names { * 'DEVELOPER_VARIABLE', etc...). * @returns An entity name that is legal in the exported language. */ - getDistinctName(name: string, type: NameType|string): string { + getDistinctName(name: string, type: NameType | string): string { let safeName = this.safeName(name); - let i: number|null = null; - while (this.dbReverse.has(safeName + (i ?? '')) || - this.reservedWords.has(safeName + (i ?? ''))) { + let i: number | null = null; + while ( + this.dbReverse.has(safeName + (i ?? '')) || + this.reservedWords.has(safeName + (i ?? '')) + ) { // Collision with existing name. Create a unique name. i = i ? i + 1 : 2; } - safeName += (i ?? ''); + safeName += i ?? ''; this.dbReverse.add(safeName); const isVar = - type === NameType.VARIABLE || type === NameType.DEVELOPER_VARIABLE; + type === NameType.VARIABLE || type === NameType.DEVELOPER_VARIABLE; const prefix = isVar ? this.variablePrefix : ''; return prefix + safeName; } diff --git a/core/observable_procedure_map.ts b/core/observable_procedure_map.ts index 8105c905ed5..e8722bcbf89 100644 --- a/core/observable_procedure_map.ts +++ b/core/observable_procedure_map.ts @@ -8,9 +8,10 @@ import {IProcedureMap} from './interfaces/i_procedure_map.js'; import type {IProcedureModel} from './interfaces/i_procedure_model.js'; import {isObservable} from './interfaces/i_observable.js'; - -export class ObservableProcedureMap extends - Map implements IProcedureMap { +export class ObservableProcedureMap + extends Map + implements IProcedureMap +{ /** @internal */ constructor() { super(); diff --git a/core/options.ts b/core/options.ts index 4f66582bb18..03060f9f389 100644 --- a/core/options.ts +++ b/core/options.ts @@ -22,7 +22,6 @@ import type {Metrics} from './utils/metrics.js'; import * as toolbox from './utils/toolbox.js'; import type {WorkspaceSvg} from './workspace_svg.js'; - /** * Parse the user-specified options, using reasonable defaults where behaviour * is unspecified. @@ -35,7 +34,7 @@ export class Options { disable: boolean; readOnly: boolean; maxBlocks: number; - maxInstances: {[key: string]: number}|null; + maxInstances: {[key: string]: number} | null; modalInputs: boolean; pathToMedia: string; hasCategories: boolean; @@ -46,21 +45,21 @@ export class Options { hasSounds: boolean; hasCss: boolean; horizontalLayout: boolean; - languageTree: toolbox.ToolboxInfo|null; + languageTree: toolbox.ToolboxInfo | null; gridOptions: GridOptions; zoomOptions: ZoomOptions; toolboxPosition: toolbox.Position; theme: Theme; renderer: string; - rendererOverrides: {[rendererConstant: string]: any}|null; + rendererOverrides: {[rendererConstant: string]: any} | null; /** * The SVG element for the grid pattern. * Created during injection. */ - gridPattern: SVGElement|null = null; - parentWorkspace: WorkspaceSvg|null; - plugins: {[key: string]: (new(...p1: any[]) => any)|string}; + gridPattern: SVGElement | null = null; + parentWorkspace: WorkspaceSvg | null; + plugins: {[key: string]: (new (...p1: any[]) => any) | string}; /** * If set, sets the translation of the workspace to match the scrollbars. @@ -69,13 +68,13 @@ export class Options { * argument Contains an x and/or y property which is a float between 0 * and 1 specifying the degree of scrolling. */ - setMetrics?: ((p1: {x?: number, y?: number}) => void) = undefined; + setMetrics?: (p1: {x?: number; y?: number}) => void = undefined; /** * A function that returns a metrics * object that describes the current workspace. */ - getMetrics?: (() => Metrics) = undefined; + getMetrics?: () => Metrics = undefined; /** * @param options Dictionary of options. @@ -92,18 +91,19 @@ export class Options { let hasSounds = false; const readOnly = !!options['readOnly']; if (!readOnly) { - toolboxJsonDef = - toolbox.convertToolboxDefToJson(options['toolbox'] ?? null); + toolboxJsonDef = toolbox.convertToolboxDefToJson( + options['toolbox'] ?? null + ); hasCategories = toolbox.hasCategories(toolboxJsonDef); const rawHasTrashcan = options['trashcan']; hasTrashcan = - rawHasTrashcan === undefined ? hasCategories : rawHasTrashcan; + rawHasTrashcan === undefined ? hasCategories : rawHasTrashcan; const rawHasCollapse = options['collapse']; hasCollapse = - rawHasCollapse === undefined ? hasCategories : rawHasCollapse; + rawHasCollapse === undefined ? hasCategories : rawHasCollapse; const rawHasComments = options['comments']; hasComments = - rawHasComments === undefined ? hasCategories : rawHasComments; + rawHasComments === undefined ? hasCategories : rawHasComments; const rawHasDisable = options['disable']; hasDisable = rawHasDisable === undefined ? hasCategories : rawHasDisable; const rawHasSounds = options['sounds']; @@ -127,11 +127,12 @@ export class Options { let toolboxPosition: toolbox.Position; if (horizontalLayout) { - toolboxPosition = - toolboxAtStart ? toolbox.Position.TOP : toolbox.Position.BOTTOM; + toolboxPosition = toolboxAtStart + ? toolbox.Position.TOP + : toolbox.Position.BOTTOM; } else { - toolboxPosition = toolboxAtStart === rtl ? toolbox.Position.RIGHT : - toolbox.Position.LEFT; + toolboxPosition = + toolboxAtStart === rtl ? toolbox.Position.RIGHT : toolbox.Position.LEFT; } let hasCss = options['css']; @@ -140,8 +141,9 @@ export class Options { } let pathToMedia = 'https://blockly-demo.appspot.com/static/media/'; if (options['media']) { - pathToMedia = options['media'].endsWith('/') ? options['media'] : - options['media'] + '/'; + pathToMedia = options['media'].endsWith('/') + ? options['media'] + : options['media'] + '/'; } else if ('path' in options) { // 'path' is a deprecated option which has been replaced by 'media'. deprecation.warn('path', 'Nov 2014', 'Jul 2023', 'media'); @@ -149,7 +151,7 @@ export class Options { } const rawOneBasedIndex = options['oneBasedIndex']; const oneBasedIndex = - rawOneBasedIndex === undefined ? true : rawOneBasedIndex; + rawOneBasedIndex === undefined ? true : rawOneBasedIndex; const renderer = options['renderer'] || 'geras'; const plugins = options['plugins'] || {}; @@ -206,11 +208,15 @@ export class Options { * @returns Normalized move options. */ private static parseMoveOptions_( - options: BlocklyOptions, hasCategories: boolean): MoveOptions { + options: BlocklyOptions, + hasCategories: boolean + ): MoveOptions { const move = options['move'] || {}; const moveOptions = {} as MoveOptions; - if (move['scrollbars'] === undefined && - options['scrollbars'] === undefined) { + if ( + move['scrollbars'] === undefined && + options['scrollbars'] === undefined + ) { moveOptions.scrollbars = hasCategories; } else if (typeof move['scrollbars'] === 'object') { moveOptions.scrollbars = { @@ -220,12 +226,15 @@ export class Options { // Convert scrollbars object to boolean if they have the same value. // This allows us to easily check for whether any scrollbars exist using // !!moveOptions.scrollbars. - if (moveOptions.scrollbars.horizontal && - moveOptions.scrollbars.vertical) { + if ( + moveOptions.scrollbars.horizontal && + moveOptions.scrollbars.vertical + ) { moveOptions.scrollbars = true; } else if ( - !moveOptions.scrollbars.horizontal && - !moveOptions.scrollbars.vertical) { + !moveOptions.scrollbars.horizontal && + !moveOptions.scrollbars.vertical + ) { moveOptions.scrollbars = false; } } else { @@ -312,7 +321,7 @@ export class Options { gridOptions.spacing = Number(grid['spacing']) || 0; gridOptions.colour = grid['colour'] || '#888'; gridOptions.length = - grid['length'] === undefined ? 1 : Number(grid['length']); + grid['length'] === undefined ? 1 : Number(grid['length']); gridOptions.snap = gridOptions.spacing > 0 && !!grid['snap']; return gridOptions; } @@ -332,7 +341,9 @@ export class Options { return theme; } return Theme.defineTheme( - theme.name || 'builtin' + idGenerator.getNextUniqueId(), theme); + theme.name || 'builtin' + idGenerator.getNextUniqueId(), + theme + ); } } @@ -346,7 +357,7 @@ export namespace Options { export interface MoveOptions { drag: boolean; - scrollbars: boolean|ScrollbarOptions; + scrollbars: boolean | ScrollbarOptions; wheel: boolean; } diff --git a/core/positionable_helpers.ts b/core/positionable_helpers.ts index b0967c48dd7..ae221468a3b 100644 --- a/core/positionable_helpers.ts +++ b/core/positionable_helpers.ts @@ -14,7 +14,6 @@ import type {Size} from './utils/size.js'; import * as toolbox from './utils/toolbox.js'; import type {WorkspaceSvg} from './workspace_svg.js'; - /** * Enum for vertical positioning. * @@ -22,7 +21,7 @@ import type {WorkspaceSvg} from './workspace_svg.js'; */ export enum verticalPosition { TOP, - BOTTOM + BOTTOM, } /** @@ -32,7 +31,7 @@ export enum verticalPosition { */ export enum horizontalPosition { LEFT, - RIGHT + RIGHT, } /** @@ -52,7 +51,7 @@ export interface Position { */ export enum bumpDirection { UP, - DOWN + DOWN, } /** @@ -71,21 +70,29 @@ export enum bumpDirection { * @internal */ export function getStartPositionRect( - position: Position, size: Size, horizontalPadding: number, - verticalPadding: number, metrics: UiMetrics, - workspace: WorkspaceSvg): Rect { + position: Position, + size: Size, + horizontalPadding: number, + verticalPadding: number, + metrics: UiMetrics, + workspace: WorkspaceSvg +): Rect { // Horizontal positioning. let left = 0; const hasVerticalScrollbar = - workspace.scrollbar && workspace.scrollbar.canScrollVertically(); + workspace.scrollbar && workspace.scrollbar.canScrollVertically(); if (position.horizontal === horizontalPosition.LEFT) { left = metrics.absoluteMetrics.left + horizontalPadding; if (hasVerticalScrollbar && workspace.RTL) { left += Scrollbar.scrollbarThickness; } - } else { // position.horizontal === horizontalPosition.RIGHT - left = metrics.absoluteMetrics.left + metrics.viewMetrics.width - - size.width - horizontalPadding; + } else { + // position.horizontal === horizontalPosition.RIGHT + left = + metrics.absoluteMetrics.left + + metrics.viewMetrics.width - + size.width - + horizontalPadding; if (hasVerticalScrollbar && !workspace.RTL) { left -= Scrollbar.scrollbarThickness; } @@ -94,9 +101,13 @@ export function getStartPositionRect( let top = 0; if (position.vertical === verticalPosition.TOP) { top = metrics.absoluteMetrics.top + verticalPadding; - } else { // position.vertical === verticalPosition.BOTTOM - top = metrics.absoluteMetrics.top + metrics.viewMetrics.height - - size.height - verticalPadding; + } else { + // position.vertical === verticalPosition.BOTTOM + top = + metrics.absoluteMetrics.top + + metrics.viewMetrics.height - + size.height - + verticalPadding; if (workspace.scrollbar && workspace.scrollbar.canScrollHorizontally()) { // The scrollbars are always positioned on the bottom if they exist. top -= Scrollbar.scrollbarThickness; @@ -117,13 +128,16 @@ export function getStartPositionRect( * @internal */ export function getCornerOppositeToolbox( - workspace: WorkspaceSvg, metrics: UiMetrics): Position { + workspace: WorkspaceSvg, + metrics: UiMetrics +): Position { const leftCorner = - metrics.toolboxMetrics.position !== toolbox.Position.LEFT && - (!workspace.horizontalLayout || workspace.RTL); + metrics.toolboxMetrics.position !== toolbox.Position.LEFT && + (!workspace.horizontalLayout || workspace.RTL); const topCorner = metrics.toolboxMetrics.position === toolbox.Position.BOTTOM; - const hPosition = - leftCorner ? horizontalPosition.LEFT : horizontalPosition.RIGHT; + const hPosition = leftCorner + ? horizontalPosition.LEFT + : horizontalPosition.RIGHT; const vPosition = topCorner ? verticalPosition.TOP : verticalPosition.BOTTOM; return {horizontal: hPosition, vertical: vPosition}; } @@ -143,8 +157,11 @@ export function getCornerOppositeToolbox( * @internal */ export function bumpPositionRect( - startRect: Rect, margin: number, bumpDir: bumpDirection, - savedPositions: Rect[]): Rect { + startRect: Rect, + margin: number, + bumpDir: bumpDirection, + savedPositions: Rect[] +): Rect { let top = startRect.top; const left = startRect.left; const width = startRect.right - startRect.left; @@ -157,7 +174,8 @@ export function bumpPositionRect( if (boundingRect.intersects(otherEl)) { if (bumpDir === bumpDirection.UP) { top = otherEl.top - height - margin; - } else { // bumpDir === bumpDirection.DOWN + } else { + // bumpDir === bumpDirection.DOWN top = otherEl.bottom + margin; } // Recheck other savedPositions diff --git a/core/procedures.ts b/core/procedures.ts index 1bc1d9d80dd..dd1fa7df9ed 100644 --- a/core/procedures.ts +++ b/core/procedures.ts @@ -23,15 +23,22 @@ import {Names} from './names.js'; import {IParameterModel} from './interfaces/i_parameter_model.js'; import {IProcedureMap} from './interfaces/i_procedure_map.js'; import {IProcedureModel} from './interfaces/i_procedure_model.js'; -import {IProcedureBlock, isProcedureBlock} from './interfaces/i_procedure_block.js'; -import {isLegacyProcedureCallBlock, isLegacyProcedureDefBlock, ProcedureBlock, ProcedureTuple} from './interfaces/i_legacy_procedure_blocks.js'; +import { + IProcedureBlock, + isProcedureBlock, +} from './interfaces/i_procedure_block.js'; +import { + isLegacyProcedureCallBlock, + isLegacyProcedureDefBlock, + ProcedureBlock, + ProcedureTuple, +} from './interfaces/i_legacy_procedure_blocks.js'; import {ObservableProcedureMap} from './observable_procedure_map.js'; import * as utilsXml from './utils/xml.js'; import * as Variables from './variables.js'; import type {Workspace} from './workspace.js'; import type {WorkspaceSvg} from './workspace_svg.js'; - /** * String for use in the "custom" attribute of a category in toolbox XML. * This string indicates that the category should be dynamically populated with @@ -54,34 +61,33 @@ export const DEFAULT_ARG = 'x'; * variables, the second with. Each procedure is defined by a three-element * list of name, parameter list, and return value boolean. */ -export function allProcedures(root: Workspace): - [ProcedureTuple[], ProcedureTuple[]] { - const proceduresNoReturn: ProcedureTuple[] = - root.getProcedureMap() - .getProcedures() - .filter((p) => !p.getReturnTypes()) - .map( - (p) => - [p.getName(), - p.getParameters().map((pa) => pa.getName()), - false, - ]); +export function allProcedures( + root: Workspace +): [ProcedureTuple[], ProcedureTuple[]] { + const proceduresNoReturn: ProcedureTuple[] = root + .getProcedureMap() + .getProcedures() + .filter((p) => !p.getReturnTypes()) + .map((p) => [ + p.getName(), + p.getParameters().map((pa) => pa.getName()), + false, + ]); root.getBlocksByType('procedures_defnoreturn', false).forEach((b) => { if (!isProcedureBlock(b) && isLegacyProcedureDefBlock(b)) { proceduresNoReturn.push(b.getProcedureDef()); } }); - const proceduresReturn: ProcedureTuple[] = - root.getProcedureMap() - .getProcedures() - .filter((p) => !!p.getReturnTypes()) - .map( - (p) => - [p.getName(), - p.getParameters().map((pa) => pa.getName()), - true, - ]); + const proceduresReturn: ProcedureTuple[] = root + .getProcedureMap() + .getProcedures() + .filter((p) => !!p.getReturnTypes()) + .map((p) => [ + p.getName(), + p.getParameters().map((pa) => pa.getName()), + true, + ]); root.getBlocksByType('procedures_defreturn', false).forEach((b) => { if (!isProcedureBlock(b) && isLegacyProcedureDefBlock(b)) { proceduresReturn.push(b.getProcedureDef()); @@ -141,7 +147,10 @@ export function findLegalName(name: string, block: Block): string { * @returns True if the name is legal. */ function isLegalName( - name: string, workspace: Workspace, opt_exclude?: Block): boolean { + name: string, + workspace: Workspace, + opt_exclude?: Block +): boolean { return !isNameUsed(name, workspace, opt_exclude); } @@ -155,19 +164,25 @@ function isLegalName( * @returns True if the name is used, otherwise return false. */ export function isNameUsed( - name: string, workspace: Workspace, opt_exclude?: Block): boolean { + name: string, + workspace: Workspace, + opt_exclude?: Block +): boolean { for (const block of workspace.getAllBlocks(false)) { if (block === opt_exclude) continue; - if (isLegacyProcedureDefBlock(block) && - Names.equals(block.getProcedureDef()[0], name)) { + if ( + isLegacyProcedureDefBlock(block) && + Names.equals(block.getProcedureDef()[0], name) + ) { return true; } } - const excludeModel = opt_exclude && isProcedureBlock(opt_exclude) ? - opt_exclude?.getProcedureModel() : - undefined; + const excludeModel = + opt_exclude && isProcedureBlock(opt_exclude) + ? opt_exclude?.getProcedureModel() + : undefined; for (const model of workspace.getProcedureMap().getProcedures()) { if (model === excludeModel) continue; if (Names.equals(model.getName(), name)) return true; @@ -227,7 +242,8 @@ export function flyoutCategory(workspace: WorkspaceSvg): Element[] { const nameField = utilsXml.createElement('field'); nameField.setAttribute('name', 'NAME'); nameField.appendChild( - utilsXml.createTextNode(Msg['PROCEDURES_DEFNORETURN_PROCEDURE'])); + utilsXml.createTextNode(Msg['PROCEDURES_DEFNORETURN_PROCEDURE']) + ); block.appendChild(nameField); xmlList.push(block); } @@ -241,7 +257,8 @@ export function flyoutCategory(workspace: WorkspaceSvg): Element[] { const nameField = utilsXml.createElement('field'); nameField.setAttribute('name', 'NAME'); nameField.appendChild( - utilsXml.createTextNode(Msg['PROCEDURES_DEFRETURN_PROCEDURE'])); + utilsXml.createTextNode(Msg['PROCEDURES_DEFRETURN_PROCEDURE']) + ); block.appendChild(nameField); xmlList.push(block); } @@ -265,7 +282,9 @@ export function flyoutCategory(workspace: WorkspaceSvg): Element[] { * @param templateName The type of the block to generate. */ function populateProcedures( - procedureList: ProcedureTuple[], templateName: string) { + procedureList: ProcedureTuple[], + templateName: string + ) { for (let i = 0; i < procedureList.length; i++) { const name = procedureList[i][0]; const args = procedureList[i][1]; @@ -305,7 +324,7 @@ export function flyoutCategory(workspace: WorkspaceSvg): Element[] { function updateMutatorFlyout(workspace: WorkspaceSvg) { const usedNames = []; const blocks = workspace.getBlocksByType('procedures_mutatorarg', false); - for (let i = 0, block; block = blocks[i]; i++) { + for (let i = 0, block; (block = blocks[i]); i++) { usedNames.push(block.getFieldValue('NAME')); } @@ -314,8 +333,10 @@ function updateMutatorFlyout(workspace: WorkspaceSvg) { argBlock.setAttribute('type', 'procedures_mutatorarg'); const nameField = utilsXml.createElement('field'); nameField.setAttribute('name', 'NAME'); - const argValue = - Variables.generateUniqueNameFromOptions(DEFAULT_ARG, usedNames); + const argValue = Variables.generateUniqueNameFromOptions( + DEFAULT_ARG, + usedNames + ); const fieldContent = utilsXml.createTextNode(argValue); nameField.appendChild(fieldContent); @@ -337,13 +358,16 @@ export function mutatorOpenListener(e: Abstract) { return; } const bubbleEvent = e as BubbleOpen; - if (!(bubbleEvent.bubbleType === 'mutator' && bubbleEvent.isOpen) || - !bubbleEvent.blockId) { + if ( + !(bubbleEvent.bubbleType === 'mutator' && bubbleEvent.isOpen) || + !bubbleEvent.blockId + ) { return; } - const workspaceId = (bubbleEvent.workspaceId); - const block = common.getWorkspaceById(workspaceId)!.getBlockById( - bubbleEvent.blockId) as BlockSvg; + const workspaceId = bubbleEvent.workspaceId; + const block = common + .getWorkspaceById(workspaceId)! + .getBlockById(bubbleEvent.blockId) as BlockSvg; const type = block.type; if (type !== 'procedures_defnoreturn' && type !== 'procedures_defreturn') { return; @@ -359,9 +383,11 @@ export function mutatorOpenListener(e: Abstract) { * @param e The event that triggered this listener. */ function mutatorChangeListener(e: Abstract) { - if (e.type !== eventUtils.BLOCK_CREATE && - e.type !== eventUtils.BLOCK_DELETE && - e.type !== eventUtils.BLOCK_CHANGE) { + if ( + e.type !== eventUtils.BLOCK_CREATE && + e.type !== eventUtils.BLOCK_DELETE && + e.type !== eventUtils.BLOCK_CHANGE + ) { return; } const workspaceId = e.workspaceId as string; @@ -378,9 +404,11 @@ function mutatorChangeListener(e: Abstract) { */ export function getCallers(name: string, workspace: Workspace): Block[] { return workspace.getAllBlocks(false).filter((block) => { - return blockIsModernCallerFor(block, name) || - (isLegacyProcedureCallBlock(block) && - Names.equals(block.getProcedureCall(), name)); + return ( + blockIsModernCallerFor(block, name) || + (isLegacyProcedureCallBlock(block) && + Names.equals(block.getProcedureCall(), name)) + ); }); } @@ -389,9 +417,12 @@ export function getCallers(name: string, workspace: Workspace): Block[] { * procedure name. */ function blockIsModernCallerFor(block: Block, procName: string): boolean { - return isProcedureBlock(block) && !block.isProcedureDef() && - block.getProcedureModel() && - Names.equals(block.getProcedureModel().getName(), procName); + return ( + isProcedureBlock(block) && + !block.isProcedureDef() && + block.getProcedureModel() && + Names.equals(block.getProcedureModel().getName(), procName) + ); } /** @@ -406,7 +437,7 @@ export function mutateCallers(defBlock: Block) { const name = procedureBlock.getProcedureDef()[0]; const xmlElement = defBlock.mutationToDom!(true); const callers = getCallers(name, defBlock.workspace); - for (let i = 0, caller; caller = callers[i]; i++) { + for (let i = 0, caller; (caller = callers[i]); i++) { const oldMutationDom = caller.mutationToDom!(); const oldMutation = oldMutationDom && utilsXml.domToText(oldMutationDom); if (caller.domToMutation) { @@ -419,8 +450,15 @@ export function mutateCallers(defBlock: Block) { // undo action since it is deterministically tied to the procedure's // definition mutation. eventUtils.setRecordUndo(false); - eventUtils.fire(new (eventUtils.get(eventUtils.BLOCK_CHANGE))( - caller, 'mutation', null, oldMutation, newMutation)); + eventUtils.fire( + new (eventUtils.get(eventUtils.BLOCK_CHANGE))( + caller, + 'mutation', + null, + oldMutation, + newMutation + ) + ); eventUtils.setRecordUndo(oldRecordUndo); } } @@ -433,17 +471,25 @@ export function mutateCallers(defBlock: Block) { * @param workspace The workspace to search. * @returns The procedure definition block, or null not found. */ -export function getDefinition(name: string, workspace: Workspace): Block|null { +export function getDefinition( + name: string, + workspace: Workspace +): Block | null { // Do not assume procedure is a top block. Some languages allow nested // procedures. Also do not assume it is one of the built-in blocks. Only // rely on isProcedureDef and getProcedureDef. for (const block of workspace.getAllBlocks(false)) { - if (isProcedureBlock(block) && block.isProcedureDef() && - Names.equals(block.getProcedureModel().getName(), name)) { + if ( + isProcedureBlock(block) && + block.isProcedureDef() && + Names.equals(block.getProcedureModel().getName(), name) + ) { return block; } - if (isLegacyProcedureDefBlock(block) && - Names.equals(block.getProcedureDef()[0], name)) { + if ( + isLegacyProcedureDefBlock(block) && + Names.equals(block.getProcedureDef()[0], name) + ) { return block; } } diff --git a/core/registry.ts b/core/registry.ts index 74cf8411bfc..52618b6b6d0 100644 --- a/core/registry.ts +++ b/core/registry.ts @@ -22,15 +22,15 @@ import type {Renderer} from './renderers/common/renderer.js'; import type {Theme} from './theme.js'; import type {ToolboxItem} from './toolbox/toolbox_item.js'; - /** * A map of maps. With the keys being the type and name of the class we are * registering and the value being the constructor function. * e.g. {'field': {'field_angle': Blockly.FieldAngle}} */ const typeMap: { - [key: string]: - {[key: string]: (new () => AnyDuringMigration)|AnyDuringMigration} + [key: string]: { + [key: string]: (new () => AnyDuringMigration) | AnyDuringMigration; + }; } = Object.create(null); export const TEST_ONLY = {typeMap}; @@ -82,8 +82,9 @@ export class Type<_T> { static FLYOUTS_VERTICAL_TOOLBOX = new Type('flyoutsVerticalToolbox'); - static FLYOUTS_HORIZONTAL_TOOLBOX = - new Type('flyoutsHorizontalToolbox'); + static FLYOUTS_HORIZONTAL_TOOLBOX = new Type( + 'flyoutsHorizontalToolbox' + ); static METRICS_MANAGER = new Type('metricsManager'); @@ -107,22 +108,31 @@ export class Type<_T> { * its type. */ export function register( - type: string|Type, name: string, - registryItem: (new (...p1: AnyDuringMigration[]) => T)|null| - AnyDuringMigration, - opt_allowOverrides?: boolean): void { - if (!(type instanceof Type) && typeof type !== 'string' || - `${type}`.trim() === '') { + type: string | Type, + name: string, + registryItem: + | (new (...p1: AnyDuringMigration[]) => T) + | null + | AnyDuringMigration, + opt_allowOverrides?: boolean +): void { + if ( + (!(type instanceof Type) && typeof type !== 'string') || + `${type}`.trim() === '' + ) { throw Error( - 'Invalid type "' + type + '". The type must be a' + - ' non-empty string or a Blockly.registry.Type.'); + 'Invalid type "' + + type + + '". The type must be a' + + ' non-empty string or a Blockly.registry.Type.' + ); } type = `${type}`.toLowerCase(); if (typeof name !== 'string' || name.trim() === '') { throw Error( - 'Invalid name "' + name + '". The name must be a' + - ' non-empty string.'); + 'Invalid name "' + name + '". The name must be a' + ' non-empty string.' + ); } const caselessName = name.toLowerCase(); if (!registryItem) { @@ -142,8 +152,8 @@ export function register( // Don't throw an error if opt_allowOverrides is true. if (!opt_allowOverrides && typeRegistry[caselessName]) { throw Error( - 'Name "' + caselessName + '" with type "' + type + - '" already registered.'); + 'Name "' + caselessName + '" with type "' + type + '" already registered.' + ); } typeRegistry[caselessName] = registryItem; nameRegistry[caselessName] = name; @@ -157,7 +167,7 @@ export function register( * @param registryItem A class or object that we are checking for the required * properties. */ -function validate(type: string, registryItem: Function|AnyDuringMigration) { +function validate(type: string, registryItem: Function | AnyDuringMigration) { switch (type) { case String(Type.FIELD): if (typeof registryItem.fromJson !== 'function') { @@ -174,14 +184,19 @@ function validate(type: string, registryItem: Function|AnyDuringMigration) { * (e.g. Field, Renderer) * @param name The plugin's name. (Ex. field_angle, geras) */ -export function unregister(type: string|Type, name: string) { +export function unregister(type: string | Type, name: string) { type = `${type}`.toLowerCase(); name = name.toLowerCase(); const typeRegistry = typeMap[type]; if (!typeRegistry || !typeRegistry[name]) { console.warn( - 'Unable to unregister [' + name + '][' + type + '] from the ' + - 'registry.'); + 'Unable to unregister [' + + name + + '][' + + type + + '] from the ' + + 'registry.' + ); return; } delete typeMap[type][name]; @@ -201,8 +216,10 @@ export function unregister(type: string|Type, name: string) { * exists. */ function getItem( - type: string|Type, name: string, opt_throwIfMissing?: boolean): - (new (...p1: AnyDuringMigration[]) => T)|null|AnyDuringMigration { + type: string | Type, + name: string, + opt_throwIfMissing?: boolean +): (new (...p1: AnyDuringMigration[]) => T) | null | AnyDuringMigration { type = `${type}`.toLowerCase(); name = name.toLowerCase(); const typeRegistry = typeMap[type]; @@ -210,7 +227,8 @@ function getItem( const msg = 'Unable to find [' + name + '][' + type + '] in the registry.'; if (opt_throwIfMissing) { throw new Error( - msg + ' You must require or register a ' + type + ' plugin.'); + msg + ' You must require or register a ' + type + ' plugin.' + ); } else { console.warn(msg); } @@ -229,7 +247,7 @@ function getItem( * @returns True if the registry has an item with the given type and name, false * otherwise. */ -export function hasItem(type: string|Type, name: string): boolean { +export function hasItem(type: string | Type, name: string): boolean { type = `${type}`.toLowerCase(); name = name.toLowerCase(); const typeRegistry = typeMap[type]; @@ -250,11 +268,13 @@ export function hasItem(type: string|Type, name: string): boolean { * @returns The class with the given name and type or null if none exists. */ export function getClass( - type: string|Type, name: string, opt_throwIfMissing?: boolean): - (new (...p1: AnyDuringMigration[]) => T)|null { - return getItem(type, name, opt_throwIfMissing) as ( - new (...p1: AnyDuringMigration[]) => T) | - null; + type: string | Type, + name: string, + opt_throwIfMissing?: boolean +): (new (...p1: AnyDuringMigration[]) => T) | null { + return getItem(type, name, opt_throwIfMissing) as + | (new (...p1: AnyDuringMigration[]) => T) + | null; } /** @@ -268,7 +288,10 @@ export function getClass( * @returns The object with the given name and type or null if none exists. */ export function getObject( - type: string|Type, name: string, opt_throwIfMissing?: boolean): T|null { + type: string | Type, + name: string, + opt_throwIfMissing?: boolean +): T | null { return getItem(type, name, opt_throwIfMissing) as T; } @@ -283,8 +306,10 @@ export function getObject( * @returns A map of objects with the given type, or null if none exists. */ export function getAllItems( - type: string|Type, opt_cased?: boolean, opt_throwIfMissing?: boolean): - {[key: string]: T|null|(new (...p1: AnyDuringMigration[]) => T)}|null { + type: string | Type, + opt_cased?: boolean, + opt_throwIfMissing?: boolean +): {[key: string]: T | null | (new (...p1: AnyDuringMigration[]) => T)} | null { type = `${type}`.toLowerCase(); const typeRegistry = typeMap[type]; if (!typeRegistry) { @@ -318,8 +343,10 @@ export function getAllItems( * @returns The class for the plugin. */ export function getClassFromOptions( - type: Type, options: Options, opt_throwIfMissing?: boolean): - (new (...p1: AnyDuringMigration[]) => T)|null { + type: Type, + options: Options, + opt_throwIfMissing?: boolean +): (new (...p1: AnyDuringMigration[]) => T) | null { const plugin = options.plugins[String(type)] || DEFAULT; // If the user passed in a plugin class instead of a registered plugin name. diff --git a/core/render_management.ts b/core/render_management.ts index 1139c2de8ed..c71d7d0bb87 100644 --- a/core/render_management.ts +++ b/core/render_management.ts @@ -7,7 +7,6 @@ import {BlockSvg} from './block_svg.js'; import {Coordinate} from './utils/coordinate.js'; - /** The set of all blocks in need of rendering which don't have parents. */ const rootBlocks = new Set(); @@ -20,7 +19,7 @@ let dirtyBlocks = new WeakSet(); * * Stored so that we can return it from afterQueuedRenders. */ -let afterRendersPromise: Promise|null = null; +let afterRendersPromise: Promise | null = null; /** * Registers that the given block and all of its parents need to be rerendered, @@ -126,7 +125,9 @@ function updateConnectionLocations(block: BlockSvg, blockOrigin: Coordinate) { if (!target) continue; if (moved || dirtyBlocks.has(target)) { updateConnectionLocations( - target, Coordinate.sum(blockOrigin, target.relativeCoords)); + target, + Coordinate.sum(blockOrigin, target.relativeCoords) + ); } } } diff --git a/core/rendered_connection.ts b/core/rendered_connection.ts index 3e4e00b8e6d..cf192a0ef00 100644 --- a/core/rendered_connection.ts +++ b/core/rendered_connection.ts @@ -27,7 +27,6 @@ import {Svg} from './utils/svg.js'; import * as svgMath from './utils/svg_math.js'; import * as svgPaths from './utils/svg_paths.js'; - /** A shape that has a pathDown property. */ interface PathDownShape { pathDown: string; @@ -51,10 +50,10 @@ export class RenderedConnection extends Connection { private readonly dbOpposite: ConnectionDB; private readonly offsetInBlock: Coordinate; private trackedState: TrackedState; - private highlightPath: SVGPathElement|null = null; + private highlightPath: SVGPathElement | null = null; /** Connection this connection connects to. Null if not connected. */ - override targetConnection: RenderedConnection|null = null; + override targetConnection: RenderedConnection | null = null; /** * @param source The block establishing this connection. @@ -74,8 +73,7 @@ export class RenderedConnection extends Connection { * current workspace. */ this.dbOpposite = - source.workspace - .connectionDBList[internalConstants.OPPOSITE_TYPE[type]]; + source.workspace.connectionDBList[internalConstants.OPPOSITE_TYPE[type]]; /** Workspace units, (0, 0) is top left of block. */ this.offsetInBlock = new Coordinate(0, 0); @@ -111,7 +109,7 @@ export class RenderedConnection extends Connection { * * @returns The connected block or null if none is connected. */ - override targetBlock(): BlockSvg|null { + override targetBlock(): BlockSvg | null { return super.targetBlock() as BlockSvg; } @@ -162,17 +160,26 @@ export class RenderedConnection extends Connection { // Raise it to the top for extra visibility. const selected = common.getSelected() == rootBlock; selected || rootBlock.addSelect(); - let dx = staticConnection.x + config.snapRadius + - Math.floor(Math.random() * BUMP_RANDOMNESS) - this.x; - let dy = staticConnection.y + config.snapRadius + - Math.floor(Math.random() * BUMP_RANDOMNESS) - this.y; + let dx = + staticConnection.x + + config.snapRadius + + Math.floor(Math.random() * BUMP_RANDOMNESS) - + this.x; + let dy = + staticConnection.y + + config.snapRadius + + Math.floor(Math.random() * BUMP_RANDOMNESS) - + this.y; if (reverse) { // When reversing a bump due to an uneditable block, bump up. dy = -dy; } if (rootBlock.RTL) { - dx = staticConnection.x - config.snapRadius - - Math.floor(Math.random() * BUMP_RANDOMNESS) - this.x; + dx = + staticConnection.x - + config.snapRadius - + Math.floor(Math.random() * BUMP_RANDOMNESS) - + this.x; } rootBlock.moveBy(dx, dy, ['bump']); selected || rootBlock.removeSelect(); @@ -197,8 +204,9 @@ export class RenderedConnection extends Connection { this.trackedState = RenderedConnection.TrackedState.TRACKED; updated = true; } else if ( - this.trackedState === RenderedConnection.TrackedState.TRACKED && - moved) { + this.trackedState === RenderedConnection.TrackedState.TRACKED && + moved + ) { this.db.removeConnection(this, this.y); this.db.addConnection(this, y); updated = true; @@ -233,7 +241,9 @@ export class RenderedConnection extends Connection { */ moveToOffset(blockTL: Coordinate): boolean { return this.moveTo( - blockTL.x + this.offsetInBlock.x, blockTL.y + this.offsetInBlock.y); + blockTL.x + this.offsetInBlock.x, + blockTL.y + this.offsetInBlock.y + ); } /** @@ -288,8 +298,10 @@ export class RenderedConnection extends Connection { const target = this.targetConnection; const block = this.targetBlock(); if (!target || !block) return; - const offset = - Coordinate.difference(this.offsetInBlock, target.offsetInBlock); + const offset = Coordinate.difference( + this.offsetInBlock, + target.offsetInBlock + ); block.translate(offset.x, offset.y); } @@ -303,8 +315,10 @@ export class RenderedConnection extends Connection { * @returns Contains two properties: 'connection' which is either another * connection or null, and 'radius' which is the distance. */ - closest(maxLimit: number, dxy: Coordinate): - {connection: RenderedConnection|null, radius: number} { + closest( + maxLimit: number, + dxy: Coordinate + ): {connection: RenderedConnection | null; radius: number} { return this.dbOpposite.searchForClosest(this, maxLimit, dxy); } @@ -315,34 +329,44 @@ export class RenderedConnection extends Connection { return; } let steps; - const sourceBlockSvg = (this.sourceBlock_); - const renderConstants = - sourceBlockSvg.workspace.getRenderer().getConstants(); + const sourceBlockSvg = this.sourceBlock_; + const renderConstants = sourceBlockSvg.workspace + .getRenderer() + .getConstants(); const shape = renderConstants.shapeFor(this); - if (this.type === ConnectionType.INPUT_VALUE || - this.type === ConnectionType.OUTPUT_VALUE) { + if ( + this.type === ConnectionType.INPUT_VALUE || + this.type === ConnectionType.OUTPUT_VALUE + ) { // Vertical line, puzzle tab, vertical line. const yLen = renderConstants.TAB_OFFSET_FROM_TOP; - steps = svgPaths.moveBy(0, -yLen) + svgPaths.lineOnAxis('v', yLen) + - (shape as unknown as PathDownShape).pathDown + - svgPaths.lineOnAxis('v', yLen); + steps = + svgPaths.moveBy(0, -yLen) + + svgPaths.lineOnAxis('v', yLen) + + (shape as unknown as PathDownShape).pathDown + + svgPaths.lineOnAxis('v', yLen); } else { const xLen = - renderConstants.NOTCH_OFFSET_LEFT - renderConstants.CORNER_RADIUS; + renderConstants.NOTCH_OFFSET_LEFT - renderConstants.CORNER_RADIUS; // Horizontal line, notch, horizontal line. - steps = svgPaths.moveBy(-xLen, 0) + svgPaths.lineOnAxis('h', xLen) + - (shape as unknown as PathLeftShape).pathLeft + - svgPaths.lineOnAxis('h', xLen); + steps = + svgPaths.moveBy(-xLen, 0) + + svgPaths.lineOnAxis('h', xLen) + + (shape as unknown as PathLeftShape).pathLeft + + svgPaths.lineOnAxis('h', xLen); } const offset = this.offsetInBlock; this.highlightPath = dom.createSvgElement( - Svg.PATH, { - 'class': 'blocklyHighlightedConnectionPath', - 'd': steps, - 'transform': `translate(${offset.x}, ${offset.y})` + - (this.sourceBlock_.RTL ? ' scale(-1 1)' : ''), - }, - this.sourceBlock_.getSvgRoot()); + Svg.PATH, + { + 'class': 'blocklyHighlightedConnectionPath', + 'd': steps, + 'transform': + `translate(${offset.x}, ${offset.y})` + + (this.sourceBlock_.RTL ? ' scale(-1 1)' : ''), + }, + this.sourceBlock_.getSvgRoot() + ); } /** Remove the highlighting around this connection. */ @@ -360,10 +384,12 @@ export class RenderedConnection extends Connection { * @internal */ setTracking(doTracking: boolean) { - if (doTracking && - this.trackedState === RenderedConnection.TrackedState.TRACKED || - !doTracking && - this.trackedState === RenderedConnection.TrackedState.UNTRACKED) { + if ( + (doTracking && + this.trackedState === RenderedConnection.TrackedState.TRACKED) || + (!doTracking && + this.trackedState === RenderedConnection.TrackedState.UNTRACKED) + ) { return; } if (this.sourceBlock_.isInFlyout) { @@ -399,7 +425,7 @@ export class RenderedConnection extends Connection { // Stop tracking connections of all children. const connections = block.getConnections_(true); for (let j = 0; j < connections.length; j++) { - (connections[j]).setTracking(false); + connections[j].setTracking(false); } // Close all bubbles of all children. const icons = block.getIcons(); @@ -424,8 +450,10 @@ export class RenderedConnection extends Connection { // of lower blocks. Also, since rendering a block renders all its parents, // we only need to render the leaf nodes. let renderList: Block[] = []; - if (this.type !== ConnectionType.INPUT_VALUE && - this.type !== ConnectionType.NEXT_STATEMENT) { + if ( + this.type !== ConnectionType.INPUT_VALUE && + this.type !== ConnectionType.NEXT_STATEMENT + ) { // Only spider down. return renderList; } @@ -466,13 +494,16 @@ export class RenderedConnection extends Connection { const block = this.getSourceBlock(); if (eventUtils.getRecordUndo()) { const group = eventUtils.getGroup(); - setTimeout(function(this: RenderedConnection) { - if (!block.isDisposed() && !block.getParent()) { - eventUtils.setGroup(group); - this.bumpAwayFrom(otherConnection as RenderedConnection); - eventUtils.setGroup(false); - } - }.bind(this), config.bumpDelay); + setTimeout( + function (this: RenderedConnection) { + if (!block.isDisposed() && !block.getParent()) { + eventUtils.setGroup(group); + this.bumpAwayFrom(otherConnection as RenderedConnection); + eventUtils.setGroup(false); + } + }.bind(this), + config.bumpDelay + ); } } @@ -486,7 +517,7 @@ export class RenderedConnection extends Connection { */ override disconnectInternal(setParent = true) { const {parentConnection, childConnection} = - this.getParentAndChildConnections(); + this.getParentAndChildConnections(); if (!parentConnection || !childConnection) return; const parent = parentConnection.getSourceBlock() as BlockSvg; const child = childConnection.getSourceBlock() as BlockSvg; @@ -553,8 +584,10 @@ export class RenderedConnection extends Connection { childBlock.updateDisabled(); } if (parentRendered && childRendered) { - if (this.type === ConnectionType.NEXT_STATEMENT || - this.type === ConnectionType.PREVIOUS_STATEMENT) { + if ( + this.type === ConnectionType.NEXT_STATEMENT || + this.type === ConnectionType.PREVIOUS_STATEMENT + ) { // Child block may need to square off its corners if it is in a stack. // Rendering a child will render its parent. childBlock.queueRender(); @@ -578,10 +611,15 @@ export class RenderedConnection extends Connection { */ protected override onCheckChanged_() { // The new value type may not be compatible with the existing connection. - if (this.isConnected() && - (!this.targetConnection || - !this.getConnectionChecker().canConnect( - this, this.targetConnection, false))) { + if ( + this.isConnected() && + (!this.targetConnection || + !this.getConnectionChecker().canConnect( + this, + this.targetConnection, + false + )) + ) { const child = this.isSuperior() ? this.targetBlock() : this.sourceBlock_; child!.unplug(); // Bump away. diff --git a/core/renderers/common/block_rendering.ts b/core/renderers/common/block_rendering.ts index 34d39e4c3ff..d64163942f3 100644 --- a/core/renderers/common/block_rendering.ts +++ b/core/renderers/common/block_rendering.ts @@ -72,9 +72,11 @@ export function unregister(name: string) { */ export function stopDebugger() { deprecation.warn( - 'Blockly.blockRendering.stopDebugger()', 'September 2021', - 'September 2022', - 'the debug renderer in @blockly/dev-tools (See https://www.npmjs.com/package/@blockly/dev-tools.)'); + 'Blockly.blockRendering.stopDebugger()', + 'September 2021', + 'September 2022', + 'the debug renderer in @blockly/dev-tools (See https://www.npmjs.com/package/@blockly/dev-tools.)' + ); debug.stopDebugger(); } @@ -89,8 +91,10 @@ export function stopDebugger() { * @internal */ export function init( - name: string, theme: Theme, - opt_rendererOverrides?: {[rendererConstant: string]: any}): Renderer { + name: string, + theme: Theme, + opt_rendererOverrides?: {[rendererConstant: string]: any} +): Renderer { const rendererClass = registry.getClass(registry.Type.RENDERER, name); const renderer = new rendererClass!(name); renderer.init(theme, opt_rendererOverrides); diff --git a/core/renderers/common/constants.ts b/core/renderers/common/constants.ts index 0d3105d042f..dc0ad214bb4 100644 --- a/core/renderers/common/constants.ts +++ b/core/renderers/common/constants.ts @@ -16,7 +16,6 @@ import * as parsing from '../../utils/parsing.js'; import {Svg} from '../../utils/svg.js'; import * as svgPaths from '../../utils/svg_paths.js'; - /** An object containing sizing and path information about outside corners. */ export interface OutsideCorners { topLeft: string; @@ -55,8 +54,8 @@ export interface PuzzleTab { type: number; width: number; height: number; - pathDown: string|((p1: number) => string); - pathUp: string|((p1: number) => string); + pathDown: string | ((p1: number) => string); + pathUp: string | ((p1: number) => string); } /** @@ -70,12 +69,16 @@ export interface JaggedTeeth { } export type BaseShape = { - type: number; width: number; height: number; + type: number; + width: number; + height: number; }; /** An object containing sizing and type information about a dynamic shape. */ export type DynamicShape = { - type: number; width: (p1: number) => number; height: (p1: number) => number; + type: number; + width: (p1: number) => number; + height: (p1: number) => number; isDynamic: true; connectionOffsetY: (p1: number) => number; connectionOffsetX: (p1: number) => number; @@ -86,7 +89,7 @@ export type DynamicShape = { }; /** An object containing sizing and type information about a shape. */ -export type Shape = BaseShape|DynamicShape; +export type Shape = BaseShape | DynamicShape; /** * Returns whether the shape is dynamic or not. @@ -210,13 +213,13 @@ export class ConstantProvider { * `setFontConstants_` to be the height of the text based on the font * used. */ - FIELD_TEXT_HEIGHT = -1; // Dynamically set. + FIELD_TEXT_HEIGHT = -1; // Dynamically set. /** * Text baseline. This constant is dynamically set in `setFontConstants_` * to be the baseline of the text based on the font used. */ - FIELD_TEXT_BASELINE = -1; // Dynamically set. + FIELD_TEXT_BASELINE = -1; // Dynamically set. /** A field's border rect corner radius. */ FIELD_BORDER_RECT_RADIUS = 4; @@ -279,7 +282,7 @@ export class ConstantProvider { * The defs tag that contains all filters and patterns for this Blockly * instance. */ - private defs: SVGElement|null = null; + private defs: SVGElement | null = null; /** * The ID of the emboss filter, or the empty string if no filter is set. @@ -287,7 +290,7 @@ export class ConstantProvider { embossFilterId = ''; /** The element to use for highlighting, or null if not set. */ - private embossFilter: SVGElement|null = null; + private embossFilter: SVGElement | null = null; /** * The ID of the disabled pattern, or the empty string if no pattern is set. @@ -297,7 +300,7 @@ export class ConstantProvider { /** * The element to use for disabled blocks, or null if not set. */ - private disabledPattern: SVGElement|null = null; + private disabledPattern: SVGElement | null = null; /** * The ID of the debug filter, or the empty string if no pattern is set. @@ -307,10 +310,10 @@ export class ConstantProvider { /** * The element to use for a debug highlight, or null if not set. */ - private debugFilter: SVGElement|null = null; + private debugFilter: SVGElement | null = null; /** The
- - + + @@ -138,13 +138,16 @@ 'tests/playgrounds/screenshot.js', 'node_modules/@blockly/dev-tools/dist/index.js', ], - } + };
-