From 1b62734a320ba8e78bc03d6333b7537e7d7fff02 Mon Sep 17 00:00:00 2001 From: Vadim Demchenko Date: Mon, 10 Jan 2022 11:18:01 +0300 Subject: [PATCH] Merge google blockly 7.20211209.1 (#53) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add top level `save` and `load` functions for JSO serialization (#5132) * Add top-level serialization API * Add using JSO system in serializer tests * Make compiler happy * feat: add deserialization of JSO block state (#5137) * Fixup tests * Add deserialization of blocks * Cleanup * PR commnts * feat: add initialization of blocks and event firing (#5166) * Change playground to use JSO system * Add tests for initialization and events * Add initialization of blocks * PR Comments * fix: touch up some deserialization behavior (#5181) * Add parameter for recording undo. This sets up the most common default behavior, but also makes it clear to people that it is happening, because it might not be expected. * Add grouping of events * Add text width caching * Add disabling workspace resizing * Add performance optimizations * Respect nulls from blocks.save * Cleanup from rebase * PR Comments * Cleanup from rebase * feat: upgrade block defs to have JSO serialization hooks (#5329) * Respect nulls from blocks.save * Upgrade list blocks to use JSO serialization * Upgrade logic blocks to use JSO serialization * Upgrade math blocks to use JSO serialization * Upgrade text blocks to use JSO serialization * Upgrade procedure blocks to use JSO serialization * Add more mutator tests * Fix firing enabled events * PR Comments * Add throwing exceptions during deserialization (#5200) * Add exception definitions * Add tests for throwing errors during deserialization * Add actually throwing exceptions * Cleanup * Cleanup * Fix tests * fix: PR Comments * feat: add serialization and deserialization of comments (#5216) * Add tests for (de)seralizing icons * Add logic for (de)serializing icons * fix: add docs for saveIcons * fix: add timeout for setting comment visible * Add serializing shadows as JSOs for the JSO system (#5246) * Move existing tests into new suite * Add tests for setShadowState * Add assertions for serialization * Unskip serialization tests * Add logic to handle shadows in both systems * Uncomment tests * fix: add access modifiers to new comment funcs * fix: fixup types * fix: remove addNextBlocks = true * feat: add real child of shadow errors * fix: types * fix: insertion markers and change events to work with JSO hooks (#5378) * fix: add tests for fixing change events * fix: change events and insertion markers * fix: build: * fix: remove duplicate code * fix: requires * feat: adds hooks for serializing plugins (#5276) * Reformat registry tests * Add tests for plugin hooks * Add plugin hooks for serialization * Switch PluginSerializer to IPluginSerializer * fix: types * fix: PR comments * fix: tests * cleanup: formatting * fix: types * feat: add respecting case in registry * feat: add separate registry for serializers * fix: rename serialiation registry alias * fix: move serializer interface into interface dir * fix: remove some attributes from the JSO system (#5356) * fix: remove some attributes from the JSO system Remove the deletable, movable, and editable attributes. Normally this would be a breaking change, but because this isn't released yet it's just a patch. * fixup: serializer tests * feat: add throwing errors for bad shadows (#5330) * fix: add throwing errors for bad shadows * tests: add tests for setShadowDom * fix: redo disconnect from shadow bug * fix: not being able to specifying variable names in toolbox (#5408) * fix: not being able to specifying variable names in toolbox * fix: id -> ID * feat: add support for defining toolboxes using pure json (#5392) * feat: add recycling to core * feat: add support for json block definitions in flyout * tests: reorganize tests * tests: add tests for generating contents * Fixup reycling * tests: add tests for recycling * fix: types * fix: lint * fix: PR comments * fix: creating blocks from flyout * test: add test block to playground * fix: types * feat: add support for enabled * fix: serializing edited shadows (#5424) * fix: serializing shadows * tests: add tests for serializing editted shadows * fix: cereal backwards compatibility (#5421) * fix: remove duplicate serialization hook implementations * feat: add backwards compatibility to field serialization * feat: add support for serializing old mutator hooks * fix: build * fix: refactor field changes into helpers * fix: typo * fix: removing xmlns * tests: add tests for serialization and deserialization of mutator hooks * fix: switch to early returns * fix: copy paste with json system (#5423) * fix: copy paste w/ json system * fix: pr comments * fix: dragging blocks from the flyout that only have XML hooks (#5422) * fix: positioning of flyout blocks * fix: move flyout to JSON system * cleanup: remove test code from playground * fix: loading blocks in RTL * fix: create and delete events, and the trashcan (#5425) * fix: create and delete events with JSON serialization * fix: trashcan with JSON serialization * fix: build * fix: tests * fix: PR comments * fix: types * fix: tests * fix: update code demo to include JSON (#5444) * fix: update code demo to include JSON * fix: do property renames * fix: change XML to place holder * fix: PR comments * fix: dragging variables from flyout (#5434) * fix: dragging variables from flyout * fix: rename positionBlock_ to positionNewBlock_ * fix: type * fix: try alternative method for handling variables in flyout * fix: project cereal cleanup (#5398) * fix: make exception constructors package * fix: rename blocks.load to blocks.append * fix: inline docs * fix: consistency in block serialization * fix: remove unexported functions * fix: interface requires * fix: tag TODO with issue number * fix: remove eslit disables * chore: rebuild deps after rebase * Make several exported private fields/methods TEST_ONLY (#5479) * refactor: Remove remaining references to Blockly.* from goog.modules (#5505) * Fix jsdoc in core/utils/useragent.js * Fix jsdoc in core/serialization/variables.js * chore: remove deprecated functions (#5509) * Remove declareLegacyNamespace from block-related files. * fix: enable missingRequire in build_tasks (#5510) * chore: remove deprecateLegacyNamespace from keyboard nav files * chore: Remove deprecateLegacyNamespace & fix tests * chore: export from the blockly file * chore: named exports for block* files (#5512) * chore: named export for block.js * chore: named export for block_drag_surface.js * chore: named export for block_dragger.js * chore: named export for block_svg.js * Fix import ordering * chore: fix imports using requireType * Remove extra require * Remove references to fully qualified namespace * Remove duplicate declaration * Fix bug in variable_map that was introduced during merge conflict resolving * chore: named exports for keyboard_nav* files (#5517) * chore: update dev tools dev dependency (#5516) * Update package-lock.json * fix: JSDoc generation for modules without classes * fix: updates the release task to build typings for msgs (#5495) * Migrate core/field_angle.js to to goog.module syntax (#5521) Move angle picker field to goog module and ES6. * chore: Remove declareLegacyNamespace() from toolbox/ files * Remove unecessary deprecation warnings stubs * Catch warnings logged during registry test * Initialize generators in generator tests * Remove usage of deprecated method in onKeyDown handler * Update captureWarnings usages to assertWarnings * Update test block in trashcan_test to not trigger warning log * Remove unecessary block variable block definition * chore: Remove declareLegacyNamespace() from field_* files * chore: Remove declareLegacyNamespace() from files in core (#5525) * chore: Remove declareLegacyNamespace() from files in core * fix: Update missing/errant re-exports in blockly.js * chore: Remove declareLegacyNamespace() from theme files * refactor: Rename Blockly.connectionTypes to Blockly.ConnectionType (#5407) * Renamed Blockly.connectionTypes to Blockly.ConnectionType * Renamed core/connection_types.js to connection_type.js * Add entry to renamings.js for renaming of Blockly.connectionTypes Co-authored-by: Christopher Allen * chore: Remove declareLegacyNamespace from renderers (#5528) * chore: Remove declareLegacyNamespace from Geras renderer * chore: Remove declareLegacyNamespace from minimalist renderer * chore: Remove declareLegacyNamespace from thrasos renderer * chore: Remove declareLegacyNamespace from zelos renderer * fix: Move debugger functionality out of Blockly.blockRendering to avoid dependency cycle when re-exporting submodules * chore: Remove declareLegacyNamespace from Blockly.blockRendering.* * Fix typo * chore: Remove declareLegacyNamespace() from files under interfaces/* * refactor: Migrate `Blockly.ConnectionType` to named exports (#5533) * refactor: Migrate Blockly.ConnectionType to named exports * Add corresponding information to renamings.js * Reexport global.globalThis as Blockly.utils.global (#5534) Fixes #5503 This makes the value of Blockly.utils.global the same as it was before PR #5451. * refactor: Rename `Blockly.Blocks`, migrate to named exports (#5515) * Rename Blockly.Blocks to Blockly.blocks Because it does not export a type as its default export. Part of #5073. * Name default export of Blockly.blocks Blocks. Use named exports in Blockly.blocks by giving the former default export the name Blocks. Part of #5153. * Reexport Blockly.blocks from blockly.js * Document the format of renamings.js better. * chore: remove declareLegacyNamespace from events (#5532) - Adds an extra events/utils.js file to hold helper methods related to events. * Fix typo in alias * fix: Add hack to copy accessors to global Blockly namespace object (#5536) Ugly, but it works. * fix: blocks dragged from RTL flyout being incorrectly positioned * fix: infinite loop when using defineProperties (#5549) * Add namespace and alias annotations to jsdoc (#5550) * Add annotations to files under core/events * Add annotations to files under core/interfaces * Add annotations to files under core/keyboard_nav * Add annotations to files under core/renderers * Add annotations to files under core/serialization * Add annotations to files under core/theme * Add annotations to files under core/toolbox * Add annotations to files under core/utils * Add annotations to files under core * Add missing @alias annotations (#5559) * Update @package annotations (#5558) * Add JSDoc to theme classes. (#5551) * Add JSDoc to theme classes. * Add missing alias annotations (#5561) * chore: Rollup of all Q3 message changes (#5564) Courtesy of Translatewiki * Replace namespace annotation with class annotation in files that define a class (#5566) * fix: JSDoc improvments for accessors, etc. (#5567) * fix: Revert removal of documentation for get/set accessors. This partially reverts commit 839cb7b, "fix: infinite loop when using defineProperties (#5549)" * docs: Use @name to attach JSDocs to accessors Reintroduce documentation for deprecated properties where it was removed when converting them to accessors. * docs: Remove duplicate @package declarations * fix: Fix eslint and compiler errors/warnings * fix: Minor JSDoc tweaks to address comments on PR #5567 * fix: Use require instead of requireType for interfaces (#5568) But only if the interface is used in an @implements or @extends declaration. Fixes #5450 * fix: Fix JS Spaghetti button. (#5569) * fix: RTL copy-paste and duplicate (#5560) * fix: Fix Block Factory preview (#5571) It's OK to modify the contents of Blockly.Blocks, but it's not OK to try to replace this export with a completely different object. Fixes #5556 * chore: rebuild for release (#5573) * chore: Bump @wdio/selenium-standalone-service from 7.13.0 to 7.13.2 (#5546) Bumps [@wdio/selenium-standalone-service](https://github.com/webdriverio/webdriverio) from 7.13.0 to 7.13.2. - [Release notes](https://github.com/webdriverio/webdriverio/releases) - [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md) - [Commits](https://github.com/webdriverio/webdriverio/compare/v7.13.0...v7.13.2) --- updated-dependencies: - dependency-name: "@wdio/selenium-standalone-service" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: Bump mocha from 9.1.1 to 9.1.2 (#5574) Bumps [mocha](https://github.com/mochajs/mocha) from 9.1.1 to 9.1.2. - [Release notes](https://github.com/mochajs/mocha/releases) - [Changelog](https://github.com/mochajs/mocha/blob/master/CHANGELOG.md) - [Commits](https://github.com/mochajs/mocha/compare/v9.1.1...v9.1.2) --- updated-dependencies: - dependency-name: mocha dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: Fix errors in injected CSS (#5587) * feat: add Blockly.Extensions.isRegistered function (#5500) Co-authored-by: Monica Kozbial <6621618+moniika@users.noreply.github.com> * chore: Run clang-format on core/*.js (#5589) * Run clang-format on core/*.js * Revert changes for css formatting * fix: Make update_metadata.sh work on macOS (#5590) * fix: clear dropdown div before rendering content (#5429) * Changes Css.register API to accept string param (#5472) * Chnages Css.register API to accept string param * Address self review comments and nits * Fix code-comment * Address minor review comments and nits * Allow passing an array of strings when registering CSS * Fix lint errors Co-authored-by: Aaron Dodson * Add Croatian to Code demo (#5583) Contributed courtesy of Lidija Kralj. * chore: Convert == to === and != to !== where possible. (#5599) * Remove @author tags (#5601) Our files are up to a decade old, and have churned so much, that the initial author of the file no longer has much meaning. Furthermore, this will encourage developers to post to the developer group, rather than emailing Googlers (usually me) directly. * fix: Don't kludge accessors in compiled mode (#5591) In compiled mode we don't need to add exports to the global Blockly object because they'll already be there - and attempting to do so causes problems when a project imports multiple separate copies of Blockly (which it shouldn't, but many plugins do). This is part of the fix for google/blockly-samples#902. * chore: Bump google-closure-compiler from 20210601.0.0 to 20211006.0.0 (#5592) * Bump google-closure-compiler from 20210601.0.0 to 20211006.0.0 Bumps [google-closure-compiler](https://github.com/google/closure-compiler-npm) from 20210601.0.0 to 20211006.0.0. - [Release notes](https://github.com/google/closure-compiler-npm/releases) - [Commits](https://github.com/google/closure-compiler-npm/compare/v20210601.0.0...v20211006.0.0) --- updated-dependencies: - dependency-name: google-closure-compiler dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * Remove deleted option from Closure Compiler arguments The closure compiler no longer supports the undefinedNames warning group (and indeed even prior to deletion it didn't do anything for some time). Per @lauraharker, enabling checkVars, missingProperties and strictMissingProperties gives about the same check coverage; we already enable the first two, while the third was already listed but commented out for the time being. * Provide externs for base.js functions Per comment: fixes compiler errors from build:debug. * Fix erroneous import Blockly.serialization.ISerializer uses named exports, so we need to destructure the import. Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Christopher Allen * fix: group context menu collapse blocks events (#5501) * Add Croatian to Code demo (#5583) (#5611) Contributed courtesy of Lidija Kralj. * fix: Positioning of pasted/duplicated blocks in LTR workspaces (#5613) * Bump yargs from 16.2.0 to 17.2.1 (#5603) Bumps [yargs](https://github.com/yargs/yargs) from 16.2.0 to 17.2.1. - [Release notes](https://github.com/yargs/yargs/releases) - [Changelog](https://github.com/yargs/yargs/blob/main/CHANGELOG.md) - [Commits](https://github.com/yargs/yargs/compare/v16.2.0...v17.2.1) --- updated-dependencies: - dependency-name: yargs dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump webdriverio from 7.13.0 to 7.14.1 (#5594) Bumps [webdriverio](https://github.com/webdriverio/webdriverio) from 7.13.0 to 7.14.1. - [Release notes](https://github.com/webdriverio/webdriverio/releases) - [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md) - [Commits](https://github.com/webdriverio/webdriverio/compare/v7.13.0...v7.14.1) --- updated-dependencies: - dependency-name: webdriverio dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump mocha from 9.1.2 to 9.1.3 (#5616) Bumps [mocha](https://github.com/mochajs/mocha) from 9.1.2 to 9.1.3. - [Release notes](https://github.com/mochajs/mocha/releases) - [Changelog](https://github.com/mochajs/mocha/blob/master/CHANGELOG.md) - [Commits](https://github.com/mochajs/mocha/compare/v9.1.2...v9.1.3) --- updated-dependencies: - dependency-name: mocha dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: update dependabot messages and labels (#5618) * Bump concurrently from 6.2.0 to 6.3.0 (#5581) Bumps [concurrently](https://github.com/open-cli-tools/concurrently) from 6.2.0 to 6.3.0. - [Release notes](https://github.com/open-cli-tools/concurrently/releases) - [Commits](https://github.com/open-cli-tools/concurrently/compare/v6.2.0...v6.3.0) --- updated-dependencies: - dependency-name: concurrently dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): Bump google-closure-deps from 20210601.0.0 to 20210808.0.0 (#5360) Bumps [google-closure-deps](https://github.com/google/closure-library) from 20210601.0.0 to 20210808.0.0. - [Release notes](https://github.com/google/closure-library/releases) - [Commits](https://github.com/google/closure-library/compare/v20210601...v20210808) --- updated-dependencies: - dependency-name: google-closure-deps dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump typescript from 4.3.2 to 4.4.4 (#5617) Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.3.2 to 4.4.4. - [Release notes](https://github.com/Microsoft/TypeScript/releases) - [Commits](https://github.com/Microsoft/TypeScript/compare/v4.3.2...v4.4.4) --- updated-dependencies: - dependency-name: typescript dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: create release.yml (#5588) * Create release.yml * chore: update release.yml * refactor: Migrate to named exports (#5623) * refactor: Migrate to named exports * fix: Sort requires * fix: Remove duplicate deps * chore: auto-fix violations of comma-dangle rule (#5625) * chore: replace var with const and let in blocks directory (#5626) * chore: use const and let in blocks/lists.js * chore: use const and let in blocks/logic.js * chore: use const and let in blocks/loops.js * chore: use const and let in blocks/math.js * chore: use const and let in blocks/procedures.js * chore: use const and let in blocks/text.js * chore: use const and let in blocks/variables_dynamic.js * chore: use const and let in blocks/variables.js * fix: updateShape_ variable scoping * fix: declarations in switch clauses * other: change while loops to for loops * fix: fix violation of no-cond-assign * chore: runs clang format on all files (#5627) * Bump selenium-standalone from 7.1.0 to 8.0.3 (#5632) Bumps [selenium-standalone](https://github.com/webdriverio/selenium-standalone) from 7.1.0 to 8.0.3. - [Release notes](https://github.com/webdriverio/selenium-standalone/releases) - [Changelog](https://github.com/webdriverio/selenium-standalone/blob/main/HISTORY.md) - [Commits](https://github.com/webdriverio/selenium-standalone/compare/v7.1.0...v8.0.3) --- updated-dependencies: - dependency-name: selenium-standalone dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: Update deps.js with modified lang values (#5636) This updates should have been included in PR #5626 but were apparently omitted (and the omission not noted by the code reviewer either!) * chore: replace more uses of var with const and let (#5628) * chore: fix uses of var in core/block_dragger * chore: fix uses of var in core/extensions.js * chore: fix uses of var in core/field_multilineinput.js * chore: fix uses of var in assorted core files * chore: fix uses of var in node test runner and playground screenshot code * fix: undefined return from measureFontMetrics * fix: violations of no-const-assign * chore: only one variable declaration per line * chore: run eslint--fix for no-var rule in tests/mocha (#5637) * chore: adds a check for properly formatted files (#5624) * chore: add check for clang format * chore: updates clang format script * fix: Change Marker in blockly.js becuase of the usage of the Marker class as a named export (#5629) Fixed marker as per style guide and the export of tabnavigator as well. Fixes #5606. * chore: replace var with let and const in mocha tests (#5638) * chore: replace var with let and const in mocha tests * chore: change let to const in changed lines * fix: Fix mutators when compiled with renames. (#5644) Previous code not compatible with advanced compilation since 'mutationToDom' and other function names were hardcoded in strings. * chore: update generators/dart.js to const and let (#5646) * Add feedback on expiry page. (#5648) Blank pages are bad for confirming anything happened. * Bump @wdio/selenium-standalone-service from 7.13.2 to 7.16.1 (#5635) Bumps [@wdio/selenium-standalone-service](https://github.com/webdriverio/webdriverio) from 7.13.2 to 7.16.1. - [Release notes](https://github.com/webdriverio/webdriverio/releases) - [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md) - [Commits](https://github.com/webdriverio/webdriverio/compare/v7.13.2...v7.16.1) --- updated-dependencies: - dependency-name: "@wdio/selenium-standalone-service" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: update dependabot messages and labels (#5653) The same as #5618, but against master because dependabot ignores changes on develop. * chore: replace var with const and let in python block generators (#5650) * chore: replace var with const and let in python block generators * chore: update test deps * chore: update bracket usage in switch statements * chore: update language generators to const and let (#5647) * chore: update generators/javascript.js to const and let * chore: update generators/lua.js to const and let * chore: update generators/php.js to const and let * chore: update generators/python.js to const and let * chore: update var to const and let in dart block generators (#5654) * chore: adds an action to assign reviewers from requested reviewers(#5642) * fix(zelos): typo (#5649) * Bump selenium-standalone from 8.0.3 to 8.0.4 (#5651) Bumps [selenium-standalone](https://github.com/webdriverio/selenium-standalone) from 8.0.3 to 8.0.4. - [Release notes](https://github.com/webdriverio/selenium-standalone/releases) - [Changelog](https://github.com/webdriverio/selenium-standalone/blob/main/HISTORY.md) - [Commits](https://github.com/webdriverio/selenium-standalone/compare/v8.0.3...v8.0.4) --- updated-dependencies: - dependency-name: selenium-standalone dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: Fix permissions for Assign requested reviewers workflow (#5666) * refactor: Inline assign_reviewers script to avoid checkout Per https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ it is not safe to do a checkout of the submitter-supplied code AND THEN RUN IT (via require). This is pretty bad. We want to give this script more permissions by running it `on: [pull_request_target]` (instead of `pull_request`); this would give it permission to modify the PR (e.g. add comments, change assignment). While it would be OK to do a checkout with default parameters (which in `pull_request_target` would check out *our* branch rather than the submitted one) it simplest just to inline this small script and thereby obviate the need to do a checkout at all. * chore: Give assign_reviewers action required permissions Changing it from `on: [pull_request]` to `on: [pull_request_review]` will give the action write access to our repository, allowing it to change the assignment of the PR. This is now safe as the script does not ever check out any submitter-supplied code. * docs: Comment tweaks for assign_reviewers.yml * Normalize ++x to x++. (#5660) There are only 10 instances of ++x in our codebase, compared with over 500 instances of x++. The stlye guide has no opinion on which to use, nor do I. But the lack of consistency was making regex searches for bugs more difficult. * chore: Update dependabot labelling config (#5664) Label all dependabot PRs as "PR: dependencies", following @rachel-fenichel's recent rationalisation of issue/PR labels. * fix: Don't crash when unable to post comment to PR (#5669) ... by not ever posting such comments from this workflow. There will be a separate PR for the other workflow that does post comments, because it needs to be in the master branch. Part of #5659. * chore: Create separate report_clang_format workflow in master branch (#5670) The initial version of this workflow just uses `console.log` to report the context of the `context` object. It is hoped that there will be enough information in this context to identify the PR to comment on, without the `check_clang_format` workflow having to upload a 'build artefact' of some kind - see example of what I hoe to avoid starting at `ReceivePR.yml` here: https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ A follow-up PR will (if possible) add the code to create comments when `check_clang_format` fails. Part of #5659. * chore: Remove spurious extra `on` clause in report_clang_format.yml (#5671) Removes an unwanted `on` clause that was overlooked in #5670 by author and reviewer. * chore: More spelunking in GitHub Action data (#5673) Looking for information about PR that triggered original check_clang_format run. * docs: Fix a few typos (#5655) There are small typos in: - closure/goog/base.js - core/contextmenu_items.js - core/insertion_marker_manager.js - core/toolbox/collapsible_category.js - demos/blockfactory/workspacefactory/wfactory_controller.js - demos/blockfactory/workspacefactory/wfactory_view.js - tests/mocha/field_colour_test.js Fixes: - Should read `updates` rather than `udpates`. - Should read `unhighlighting` rather than `unhiglighting`. - Should read `then` rather than `tehn`. - Should read `modified` rather than `modifed`. - Should read `different` rather than `diffferent`. - Should read `currently` rather than `currenly`. - Should read `browser` rather than `broswer`. * chore: update javascript block generators to const and let (#5661) * chore: update php block generators to const and let (#5663) * Bump webdriverio from 7.14.1 to 7.16.3 (#5652) Bumps [webdriverio](https://github.com/webdriverio/webdriverio) from 7.14.1 to 7.16.3. - [Release notes](https://github.com/webdriverio/webdriverio/releases) - [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md) - [Commits](https://github.com/webdriverio/webdriverio/compare/v7.14.1...v7.16.3) --- updated-dependencies: - dependency-name: webdriverio dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: update lua block generators to const and let (#5662) * fix: isPrime block should cast str as num (#5680) Fixes #5678 * fix: bad order of checks for setting flyout visibility (#5681) * chore: fix redeclares in tests * fix: bad order of checks for setting flyout visibility * chore: applies fixes to clang format (#5677) * chore: update the clang-format version * chore: specify the clang format version to use * chore: remove style tag since it will default to file * chore: apply prefer-const rule fixes in mocha tests (#5682) * chore: remove references to JSON extraction (#5683) * chore: revert github action (#5675) * fix: Don't try to set text fields to null on cancel (#5690) Mobile users get a window.prompt as an input, if they press the cancel button the return value is null. Don't attempt to set the value of the field to null. Causes errors in the custom note field which inherits from FieldTextInput. Detected in Blockly Games Music. * fix: Don't try to set text fields to null on cancel (#5692) Mobile users get a window.prompt as an input, if they press the cancel button the return value is null. Don't attempt to set the value of the field to null. Caused errors in the custom note field which inherits from FieldTextInput. Detected in Blockly Games Music. This PR is for the master branch and includes a recompile. The develop branch has changed enough that a cherrypick from develop to master won't work. The bug in question represents a significant number of the errors being reported from Blockly Games. * fix!(blocks): Rename Blockly.Blocks.* modules to Blockly.blocks.* (#5696) Use Blockly.blocks.* for blocks modules, leaving the Blockly.Blocks name for the block dictionary object. This resolves a problem with advanced compilation of Blockly Games, where, in the compressed output, (the minified name of) Blockly.Blocks gets overwritten, with the dictionary object defined in core/blocks.js being replaced by an empty namespace object created by the provides of Blockly.Blocks.* in blocks/*.js. Without this fix, some block definitions end up being created in the dictionary and some on the namespace object—with chaos predictably ensuing. * chore: fix more lint (#5676) * chore: fix assorted lint * chore: clang-format * chore: clang-format * chore: add command to launch mocha tests (#5679) * chore: update deps.js, deps.mocha.js (#5698) * chore: fix more lint (#5700) * chore: fix 918 violations of comma-dangle rule * chore: fix 2 violations of comma-spacing * chore: fix 13 violations of padded-blocks * chore: fix 50 violations of block-spacing * chore: fix one violation of semi-spacing * chore: fix 4 violations of space-before-blocks * chore: fix 38 violations of object-curly-spacing * chore: fix 30 violations of key-spacing * chore: fix 3 violations of quote-props * chore: fix 5 violations of arrow-parens * chore: fix 8 violations of no-tabs * chore: allow uncommented helper functions in mocha tests * chore: fix several more lint errors * chore: tweak eslint configuration in core and tests * chore: rebuild for tests * fix: Quote Msg prop name to survive compile (#5703) * chore(deps): bump webdriverio from 7.16.3 to 7.16.5 (#5701) Bumps [webdriverio](https://github.com/webdriverio/webdriverio) from 7.16.3 to 7.16.5. - [Release notes](https://github.com/webdriverio/webdriverio/releases) - [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md) - [Commits](https://github.com/webdriverio/webdriverio/compare/v7.16.3...v7.16.5) --- updated-dependencies: - dependency-name: webdriverio dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update dependabot.yml (#5705) * chore(deps): bump http-server from 13.0.2 to 14.0.0 (#5657) Bumps [http-server](https://github.com/http-party/http-server) from 13.0.2 to 14.0.0. - [Release notes](https://github.com/http-party/http-server/releases) - [Commits](https://github.com/http-party/http-server/compare/v13.0.2...v14.0.0) --- updated-dependencies: - dependency-name: http-server dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump @blockly/theme-modern from 2.1.19 to 2.1.24 (#5630) Bumps [@blockly/theme-modern](https://github.com/google/blockly-samples/tree/HEAD/plugins/theme-modern) from 2.1.19 to 2.1.24. - [Release notes](https://github.com/google/blockly-samples/releases) - [Commits](https://github.com/google/blockly-samples/commits/@blockly/theme-modern@2.1.24/plugins/theme-modern) --- updated-dependencies: - dependency-name: "@blockly/theme-modern" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump webdriverio from 7.16.5 to 7.16.6 (#5704) Bumps [webdriverio](https://github.com/webdriverio/webdriverio) from 7.16.5 to 7.16.6. - [Release notes](https://github.com/webdriverio/webdriverio/releases) - [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md) - [Commits](https://github.com/webdriverio/webdriverio/compare/v7.16.5...v7.16.6) --- updated-dependencies: - dependency-name: webdriverio dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump @wdio/selenium-standalone-service (#5708) Bumps [@wdio/selenium-standalone-service](https://github.com/webdriverio/webdriverio) from 7.16.1 to 7.16.6. - [Release notes](https://github.com/webdriverio/webdriverio/releases) - [Changelog](https://github.com/webdriverio/webdriverio/blob/main/CHANGELOG.md) - [Commits](https://github.com/webdriverio/webdriverio/compare/v7.16.1...v7.16.6) --- updated-dependencies: - dependency-name: "@wdio/selenium-standalone-service" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore(deps): bump concurrently from 6.3.0 to 6.4.0 (#5710) Bumps [concurrently](https://github.com/open-cli-tools/concurrently) from 6.3.0 to 6.4.0. - [Release notes](https://github.com/open-cli-tools/concurrently/releases) - [Commits](https://github.com/open-cli-tools/concurrently/compare/v6.3.0...v6.4.0) --- updated-dependencies: - dependency-name: concurrently dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: Move functions from utils (#5706) * chore: move functions from utils to more specific files * chore: use new names for utils functions * chore: run clang-format * chore: add deprecation warnings back to utils.js * chore: move remaining functions out of utils.js (#5714) * chore: move arrayRemove to a new utils.array namespace * chore: move getBlockTypeCounts out of utils.js * chore: remove last functions from utils.js * chore: reorder imports * chore: add re-export for runAfterPageLoad * refactor: Update uncompiled-mode dependency loading for playground, tests (#5715) * chore: rename module Blockly.blocks.Lists to ....lists All the other Blockly.blocks modules have lower-case names. This one being named with an upper-case initial appears to have been a typo on my part. This module name is not mentioned anywhere else in the source code (though it will be soon!) so no other files need to be edited. Further, it does not appear anywhere in the last release (which before PR #5696) so it is not necessary to add an entry in renamings.js for this change. * chore(build): Rationalise deps.js, deps.mocha.js * Include blocks/*.js (Blockly.blocks.*) in tests/deps.js, since these modules are used in the playground. (They are goog.provide modules loaded via '); - document.write(''); -} diff --git a/blocks/all.js b/blocks/all.js new file mode 100644 index 00000000000..17000f8c96d --- /dev/null +++ b/blocks/all.js @@ -0,0 +1,23 @@ +/** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview All the blocks. (Entry point for blocks_compressed.js.) + * @suppress {extraRequire} + */ +'use strict'; + +goog.module('Blockly.blocks.all'); + +goog.require('Blockly.blocks.colour'); +goog.require('Blockly.blocks.lists'); +goog.require('Blockly.blocks.logic'); +goog.require('Blockly.blocks.loops'); +goog.require('Blockly.blocks.math'); +goog.require('Blockly.blocks.procedures'); +goog.require('Blockly.blocks.texts'); +goog.require('Blockly.blocks.variables'); +goog.require('Blockly.blocks.variablesDynamic'); diff --git a/blocks/colour.js b/blocks/colour.js index 9ab3acb9517..c2a8b6b4b3f 100644 --- a/blocks/colour.js +++ b/blocks/colour.js @@ -6,117 +6,104 @@ /** * @fileoverview Colour blocks for Blockly. - * - * This file is scraped to extract a .json file of block definitions. The array - * passed to defineBlocksWithJsonArray(..) must be strict JSON: double quotes - * only, no outside references, no functions, no trailing commas, etc. The one - * exception is end-of-line comments, which the scraper will remove. - * @author fraser@google.com (Neil Fraser) */ 'use strict'; -goog.provide('Blockly.Blocks.colour'); // Deprecated -goog.provide('Blockly.Constants.Colour'); +goog.module('Blockly.blocks.colour'); -goog.require('Blockly'); -goog.require('Blockly.Blocks'); +const {defineBlocksWithJsonArray} = goog.require('Blockly.common'); +/** @suppress {extraRequire} */ goog.require('Blockly.FieldColour'); -goog.require('Blockly.FieldLabel'); -/** - * Unused constant for the common HSV hue for all blocks in this category. - * @deprecated Use Blockly.Msg['COLOUR_HUE']. (2018 April 5) - */ -Blockly.Constants.Colour.HUE = 20; - -Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT +defineBlocksWithJsonArray([ // Block for colour picker. { - "type": "colour_picker", - "message0": "%1", - "args0": [ + 'type': 'colour_picker', + 'message0': '%1', + 'args0': [ { - "type": "field_colour", - "name": "COLOUR", - "colour": "#ff0000" - } + 'type': 'field_colour', + 'name': 'COLOUR', + 'colour': '#ff0000', + }, ], - "output": "Colour", - "helpUrl": "%{BKY_COLOUR_PICKER_HELPURL}", - "style": "colour_blocks", - "tooltip": "%{BKY_COLOUR_PICKER_TOOLTIP}", - "extensions": ["parent_tooltip_when_inline"] + 'output': 'Colour', + 'helpUrl': '%{BKY_COLOUR_PICKER_HELPURL}', + 'style': 'colour_blocks', + 'tooltip': '%{BKY_COLOUR_PICKER_TOOLTIP}', + 'extensions': ['parent_tooltip_when_inline'], }, // Block for random colour. { - "type": "colour_random", - "message0": "%{BKY_COLOUR_RANDOM_TITLE}", - "output": "Colour", - "helpUrl": "%{BKY_COLOUR_RANDOM_HELPURL}", - "style": "colour_blocks", - "tooltip": "%{BKY_COLOUR_RANDOM_TOOLTIP}" + 'type': 'colour_random', + 'message0': '%{BKY_COLOUR_RANDOM_TITLE}', + 'output': 'Colour', + 'helpUrl': '%{BKY_COLOUR_RANDOM_HELPURL}', + 'style': 'colour_blocks', + 'tooltip': '%{BKY_COLOUR_RANDOM_TOOLTIP}', }, // Block for composing a colour from RGB components. { - "type": "colour_rgb", - "message0": "%{BKY_COLOUR_RGB_TITLE} %{BKY_COLOUR_RGB_RED} %1 %{BKY_COLOUR_RGB_GREEN} %2 %{BKY_COLOUR_RGB_BLUE} %3", - "args0": [ + 'type': 'colour_rgb', + 'message0': + '%{BKY_COLOUR_RGB_TITLE} %{BKY_COLOUR_RGB_RED} %1 %{BKY_COLOUR_RGB_GREEN} %2 %{BKY_COLOUR_RGB_BLUE} %3', + 'args0': [ { - "type": "input_value", - "name": "RED", - "check": "Number", - "align": "RIGHT" + 'type': 'input_value', + 'name': 'RED', + 'check': 'Number', + 'align': 'RIGHT', }, { - "type": "input_value", - "name": "GREEN", - "check": "Number", - "align": "RIGHT" + 'type': 'input_value', + 'name': 'GREEN', + 'check': 'Number', + 'align': 'RIGHT', }, { - "type": "input_value", - "name": "BLUE", - "check": "Number", - "align": "RIGHT" - } + 'type': 'input_value', + 'name': 'BLUE', + 'check': 'Number', + 'align': 'RIGHT', + }, ], - "output": "Colour", - "helpUrl": "%{BKY_COLOUR_RGB_HELPURL}", - "style": "colour_blocks", - "tooltip": "%{BKY_COLOUR_RGB_TOOLTIP}" + 'output': 'Colour', + 'helpUrl': '%{BKY_COLOUR_RGB_HELPURL}', + 'style': 'colour_blocks', + 'tooltip': '%{BKY_COLOUR_RGB_TOOLTIP}', }, // 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", - "args0": [ + 'type': 'colour_blend', + 'message0': '%{BKY_COLOUR_BLEND_TITLE} %{BKY_COLOUR_BLEND_COLOUR1} ' + + '%1 %{BKY_COLOUR_BLEND_COLOUR2} %2 %{BKY_COLOUR_BLEND_RATIO} %3', + 'args0': [ { - "type": "input_value", - "name": "COLOUR1", - "check": "Colour", - "align": "RIGHT" + 'type': 'input_value', + 'name': 'COLOUR1', + 'check': 'Colour', + 'align': 'RIGHT', }, { - "type": "input_value", - "name": "COLOUR2", - "check": "Colour", - "align": "RIGHT" + 'type': 'input_value', + 'name': 'COLOUR2', + 'check': 'Colour', + 'align': 'RIGHT', }, { - "type": "input_value", - "name": "RATIO", - "check": "Number", - "align": "RIGHT" - } + 'type': 'input_value', + 'name': 'RATIO', + 'check': 'Number', + 'align': 'RIGHT', + }, ], - "output": "Colour", - "helpUrl": "%{BKY_COLOUR_BLEND_HELPURL}", - "style": "colour_blocks", - "tooltip": "%{BKY_COLOUR_BLEND_TOOLTIP}" - } -]); // END JSON EXTRACT (Do not delete this comment.) + 'output': 'Colour', + 'helpUrl': '%{BKY_COLOUR_BLEND_HELPURL}', + 'style': 'colour_blocks', + 'tooltip': '%{BKY_COLOUR_BLEND_TOOLTIP}', + }, +]); diff --git a/blocks/lists.js b/blocks/lists.js index 7d1cc9be2be..80400536d75 100644 --- a/blocks/lists.js +++ b/blocks/lists.js @@ -6,160 +6,177 @@ /** * @fileoverview List blocks for Blockly. - * - * This file is scraped to extract a .json file of block definitions. The array - * passed to defineBlocksWithJsonArray(..) must be strict JSON: double quotes - * only, no outside references, no functions, no trailing commas, etc. The one - * exception is end-of-line comments, which the scraper will remove. - * @author fraser@google.com (Neil Fraser) + * @suppress {checkTypes} */ 'use strict'; -goog.provide('Blockly.Constants.Lists'); +goog.module('Blockly.blocks.lists'); -goog.require('Blockly'); -goog.require('Blockly.Blocks'); +const xmlUtils = goog.require('Blockly.utils.xml'); +const {Align} = goog.require('Blockly.Input'); +/* eslint-disable-next-line no-unused-vars */ +const {Block} = goog.requireType('Blockly.Block'); +const {Blocks} = goog.require('Blockly.blocks'); +const {ConnectionType} = goog.require('Blockly.ConnectionType'); +const {FieldDropdown} = goog.require('Blockly.FieldDropdown'); +const {Msg} = goog.require('Blockly.Msg'); +const {Mutator} = goog.require('Blockly.Mutator'); +/* eslint-disable-next-line no-unused-vars */ +const {Workspace} = goog.requireType('Blockly.Workspace'); +const {defineBlocksWithJsonArray} = goog.require('Blockly.common'); +/** @suppress {extraRequire} */ goog.require('Blockly.FieldDropdown'); -goog.require('Blockly.FieldLabel'); -goog.require('Blockly.Mutator'); -/** - * Unused constant for the common HSV hue for all blocks in this category. - * @deprecated Use Blockly.Msg['LISTS_HUE']. (2018 April 5) - */ -Blockly.Constants.Lists.HUE = 260; - -Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT +defineBlocksWithJsonArray([ // Block for creating an empty list // The 'list_create_with' block is preferred as it is more flexible. // // // { - "type": "lists_create_empty", - "message0": "%{BKY_LISTS_CREATE_EMPTY_TITLE}", - "output": "Array", - "style": "list_blocks", - "tooltip": "%{BKY_LISTS_CREATE_EMPTY_TOOLTIP}", - "helpUrl": "%{BKY_LISTS_CREATE_EMPTY_HELPURL}" + 'type': 'lists_create_empty', + 'message0': '%{BKY_LISTS_CREATE_EMPTY_TITLE}', + 'output': 'Array', + 'style': 'list_blocks', + 'tooltip': '%{BKY_LISTS_CREATE_EMPTY_TOOLTIP}', + 'helpUrl': '%{BKY_LISTS_CREATE_EMPTY_HELPURL}', }, // Block for creating a list with one element repeated. { - "type": "lists_repeat", - "message0": "%{BKY_LISTS_REPEAT_TITLE}", - "args0": [ + 'type': 'lists_repeat', + 'message0': '%{BKY_LISTS_REPEAT_TITLE}', + 'args0': [ { - "type": "input_value", - "name": "ITEM" + 'type': 'input_value', + 'name': 'ITEM', }, { - "type": "input_value", - "name": "NUM", - "check": "Number" - } + 'type': 'input_value', + 'name': 'NUM', + 'check': 'Number', + }, ], - "output": "Array", - "style": "list_blocks", - "tooltip": "%{BKY_LISTS_REPEAT_TOOLTIP}", - "helpUrl": "%{BKY_LISTS_REPEAT_HELPURL}" + 'output': 'Array', + 'style': 'list_blocks', + 'tooltip': '%{BKY_LISTS_REPEAT_TOOLTIP}', + 'helpUrl': '%{BKY_LISTS_REPEAT_HELPURL}', }, // Block for reversing a list. { - "type": "lists_reverse", - "message0": "%{BKY_LISTS_REVERSE_MESSAGE0}", - "args0": [ + 'type': 'lists_reverse', + 'message0': '%{BKY_LISTS_REVERSE_MESSAGE0}', + 'args0': [ { - "type": "input_value", - "name": "LIST", - "check": "Array" - } + 'type': 'input_value', + 'name': 'LIST', + 'check': 'Array', + }, ], - "output": "Array", - "inputsInline": true, - "style": "list_blocks", - "tooltip": "%{BKY_LISTS_REVERSE_TOOLTIP}", - "helpUrl": "%{BKY_LISTS_REVERSE_HELPURL}" + 'output': 'Array', + 'inputsInline': true, + 'style': 'list_blocks', + 'tooltip': '%{BKY_LISTS_REVERSE_TOOLTIP}', + 'helpUrl': '%{BKY_LISTS_REVERSE_HELPURL}', }, // Block for checking if a list is empty { - "type": "lists_isEmpty", - "message0": "%{BKY_LISTS_ISEMPTY_TITLE}", - "args0": [ + 'type': 'lists_isEmpty', + 'message0': '%{BKY_LISTS_ISEMPTY_TITLE}', + 'args0': [ { - "type": "input_value", - "name": "VALUE", - "check": ["String", "Array"] - } + 'type': 'input_value', + 'name': 'VALUE', + 'check': ['String', 'Array'], + }, ], - "output": "Boolean", - "style": "list_blocks", - "tooltip": "%{BKY_LISTS_ISEMPTY_TOOLTIP}", - "helpUrl": "%{BKY_LISTS_ISEMPTY_HELPURL}" + 'output': 'Boolean', + 'style': 'list_blocks', + 'tooltip': '%{BKY_LISTS_ISEMPTY_TOOLTIP}', + 'helpUrl': '%{BKY_LISTS_ISEMPTY_HELPURL}', }, // Block for getting the list length { - "type": "lists_length", - "message0": "%{BKY_LISTS_LENGTH_TITLE}", - "args0": [ + 'type': 'lists_length', + 'message0': '%{BKY_LISTS_LENGTH_TITLE}', + 'args0': [ { - "type": "input_value", - "name": "VALUE", - "check": ["String", "Array"] - } + 'type': 'input_value', + 'name': 'VALUE', + 'check': ['String', 'Array'], + }, ], - "output": "Number", - "style": "list_blocks", - "tooltip": "%{BKY_LISTS_LENGTH_TOOLTIP}", - "helpUrl": "%{BKY_LISTS_LENGTH_HELPURL}" - } -]); // END JSON EXTRACT (Do not delete this comment.) + 'output': 'Number', + 'style': 'list_blocks', + 'tooltip': '%{BKY_LISTS_LENGTH_TOOLTIP}', + 'helpUrl': '%{BKY_LISTS_LENGTH_HELPURL}', + }, +]); -Blockly.Blocks['lists_create_with'] = { +Blocks['lists_create_with'] = { /** * Block for creating a list with any number of elements of any type. - * @this {Blockly.Block} + * @this {Block} */ init: function() { - this.setHelpUrl(Blockly.Msg['LISTS_CREATE_WITH_HELPURL']); + this.setHelpUrl(Msg['LISTS_CREATE_WITH_HELPURL']); this.setStyle('list_blocks'); this.itemCount_ = 3; this.updateShape_(); this.setOutput(true, 'Array'); - this.setMutator(new Blockly.Mutator(['lists_create_with_item'])); - this.setTooltip(Blockly.Msg['LISTS_CREATE_WITH_TOOLTIP']); + this.setMutator(new Mutator(['lists_create_with_item'])); + this.setTooltip(Msg['LISTS_CREATE_WITH_TOOLTIP']); }, /** * Create XML to represent list inputs. + * Backwards compatible serialization implementation. * @return {!Element} XML storage element. - * @this {Blockly.Block} + * @this {Block} */ mutationToDom: function() { - var container = Blockly.utils.xml.createElement('mutation'); + const container = xmlUtils.createElement('mutation'); container.setAttribute('items', this.itemCount_); return container; }, /** * Parse XML to restore the list inputs. + * Backwards compatible serialization implementation. * @param {!Element} xmlElement XML storage element. - * @this {Blockly.Block} + * @this {Block} */ domToMutation: function(xmlElement) { this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10); this.updateShape_(); }, + /** + * Returns the state of this block as a JSON serializable object. + * @return {{itemCount: number}} The state of this block, ie the item count. + */ + saveExtraState: function() { + return { + 'itemCount': this.itemCount_, + }; + }, + /** + * Applies the given state to this block. + * @param {*} state The state to apply to this block, ie the item count. + */ + loadExtraState: function(state) { + this.itemCount_ = state['itemCount']; + this.updateShape_(); + }, /** * Populate the mutator's dialog with this block's components. - * @param {!Blockly.Workspace} workspace Mutator's workspace. - * @return {!Blockly.Block} Root block in mutator. - * @this {Blockly.Block} + * @param {!Workspace} workspace Mutator's workspace. + * @return {!Block} Root block in mutator. + * @this {Block} */ decompose: function(workspace) { - var containerBlock = workspace.newBlock('lists_create_with_container'); + const containerBlock = workspace.newBlock('lists_create_with_container'); containerBlock.initSvg(); - var connection = containerBlock.getInput('STACK').connection; - for (var i = 0; i < this.itemCount_; i++) { - var itemBlock = workspace.newBlock('lists_create_with_item'); + let connection = containerBlock.getInput('STACK').connection; + for (let i = 0; i < this.itemCount_; i++) { + const itemBlock = workspace.newBlock('lists_create_with_item'); itemBlock.initSvg(); connection.connect(itemBlock.previousConnection); connection = itemBlock.nextConnection; @@ -168,231 +185,229 @@ Blockly.Blocks['lists_create_with'] = { }, /** * Reconfigure this block based on the mutator dialog's components. - * @param {!Blockly.Block} containerBlock Root block in mutator. - * @this {Blockly.Block} + * @param {!Block} containerBlock Root block in mutator. + * @this {Block} */ compose: function(containerBlock) { - var itemBlock = containerBlock.getInputTargetBlock('STACK'); + let itemBlock = containerBlock.getInputTargetBlock('STACK'); // Count number of inputs. - var connections = []; + const connections = []; while (itemBlock && !itemBlock.isInsertionMarker()) { connections.push(itemBlock.valueConnection_); - itemBlock = itemBlock.nextConnection && - itemBlock.nextConnection.targetBlock(); + itemBlock = + itemBlock.nextConnection && itemBlock.nextConnection.targetBlock(); } // Disconnect any children that don't belong. - for (var i = 0; i < this.itemCount_; i++) { - var connection = this.getInput('ADD' + i).connection.targetConnection; - if (connection && connections.indexOf(connection) == -1) { + for (let i = 0; i < this.itemCount_; i++) { + const connection = this.getInput('ADD' + i).connection.targetConnection; + if (connection && connections.indexOf(connection) === -1) { connection.disconnect(); } } this.itemCount_ = connections.length; this.updateShape_(); // Reconnect any child blocks. - for (var i = 0; i < this.itemCount_; i++) { - Blockly.Mutator.reconnect(connections[i], this, 'ADD' + i); + for (let i = 0; i < this.itemCount_; i++) { + Mutator.reconnect(connections[i], this, 'ADD' + i); } }, /** * Store pointers to any connected child blocks. - * @param {!Blockly.Block} containerBlock Root block in mutator. - * @this {Blockly.Block} + * @param {!Block} containerBlock Root block in mutator. + * @this {Block} */ saveConnections: function(containerBlock) { - var itemBlock = containerBlock.getInputTargetBlock('STACK'); - var i = 0; + let itemBlock = containerBlock.getInputTargetBlock('STACK'); + let i = 0; while (itemBlock) { - var input = this.getInput('ADD' + i); + const input = this.getInput('ADD' + i); itemBlock.valueConnection_ = input && input.connection.targetConnection; + itemBlock = + itemBlock.nextConnection && itemBlock.nextConnection.targetBlock(); i++; - itemBlock = itemBlock.nextConnection && - itemBlock.nextConnection.targetBlock(); } }, /** * Modify this block to have the correct number of inputs. * @private - * @this {Blockly.Block} + * @this {Block} */ updateShape_: function() { if (this.itemCount_ && this.getInput('EMPTY')) { this.removeInput('EMPTY'); } else if (!this.itemCount_ && !this.getInput('EMPTY')) { - this.appendDummyInput('EMPTY') - .appendField(Blockly.Msg['LISTS_CREATE_EMPTY_TITLE']); + this.appendDummyInput('EMPTY').appendField( + Msg['LISTS_CREATE_EMPTY_TITLE']); } // Add new inputs. - for (var i = 0; i < this.itemCount_; i++) { + for (let i = 0; i < this.itemCount_; i++) { if (!this.getInput('ADD' + i)) { - var input = this.appendValueInput('ADD' + i) - .setAlign(Blockly.ALIGN_RIGHT); - if (i == 0) { - input.appendField(Blockly.Msg['LISTS_CREATE_WITH_INPUT_WITH']); + const input = this.appendValueInput('ADD' + i).setAlign(Align.RIGHT); + if (i === 0) { + input.appendField(Msg['LISTS_CREATE_WITH_INPUT_WITH']); } } } // Remove deleted inputs. - while (this.getInput('ADD' + i)) { + for (let i = this.itemCount_; this.getInput('ADD' + i); i++) { this.removeInput('ADD' + i); - i++; } - } + }, }; -Blockly.Blocks['lists_create_with_container'] = { +Blocks['lists_create_with_container'] = { /** * Mutator block for list container. - * @this {Blockly.Block} + * @this {Block} */ init: function() { this.setStyle('list_blocks'); - this.appendDummyInput() - .appendField(Blockly.Msg['LISTS_CREATE_WITH_CONTAINER_TITLE_ADD']); + this.appendDummyInput().appendField( + Msg['LISTS_CREATE_WITH_CONTAINER_TITLE_ADD']); this.appendStatementInput('STACK'); - this.setTooltip(Blockly.Msg['LISTS_CREATE_WITH_CONTAINER_TOOLTIP']); + this.setTooltip(Msg['LISTS_CREATE_WITH_CONTAINER_TOOLTIP']); this.contextMenu = false; - } + }, }; -Blockly.Blocks['lists_create_with_item'] = { +Blocks['lists_create_with_item'] = { /** * Mutator block for adding items. - * @this {Blockly.Block} + * @this {Block} */ init: function() { this.setStyle('list_blocks'); - this.appendDummyInput() - .appendField(Blockly.Msg['LISTS_CREATE_WITH_ITEM_TITLE']); + this.appendDummyInput().appendField(Msg['LISTS_CREATE_WITH_ITEM_TITLE']); this.setPreviousStatement(true); this.setNextStatement(true); - this.setTooltip(Blockly.Msg['LISTS_CREATE_WITH_ITEM_TOOLTIP']); + this.setTooltip(Msg['LISTS_CREATE_WITH_ITEM_TOOLTIP']); this.contextMenu = false; - } + }, }; -Blockly.Blocks['lists_indexOf'] = { +Blocks['lists_indexOf'] = { /** * Block for finding an item in the list. - * @this {Blockly.Block} + * @this {Block} */ init: function() { - var OPERATORS = - [ - [Blockly.Msg['LISTS_INDEX_OF_FIRST'], 'FIRST'], - [Blockly.Msg['LISTS_INDEX_OF_LAST'], 'LAST'] - ]; - this.setHelpUrl(Blockly.Msg['LISTS_INDEX_OF_HELPURL']); + const OPERATORS = [ + [Msg['LISTS_INDEX_OF_FIRST'], 'FIRST'], + [Msg['LISTS_INDEX_OF_LAST'], 'LAST'], + ]; + this.setHelpUrl(Msg['LISTS_INDEX_OF_HELPURL']); this.setStyle('list_blocks'); this.setOutput(true, 'Number'); - this.appendValueInput('VALUE') - .setCheck('Array') - .appendField(Blockly.Msg['LISTS_INDEX_OF_INPUT_IN_LIST']); - this.appendValueInput('FIND') - .appendField(new Blockly.FieldDropdown(OPERATORS), 'END'); + this.appendValueInput('VALUE').setCheck('Array').appendField( + Msg['LISTS_INDEX_OF_INPUT_IN_LIST']); + this.appendValueInput('FIND').appendField( + new FieldDropdown(OPERATORS), 'END'); this.setInputsInline(true); // Assign 'this' to a variable for use in the tooltip closure below. - var thisBlock = this; + const thisBlock = this; this.setTooltip(function() { - return Blockly.Msg['LISTS_INDEX_OF_TOOLTIP'].replace('%1', - thisBlock.workspace.options.oneBasedIndex ? '0' : '-1'); + return Msg['LISTS_INDEX_OF_TOOLTIP'].replace( + '%1', thisBlock.workspace.options.oneBasedIndex ? '0' : '-1'); }); - } + }, }; -Blockly.Blocks['lists_getIndex'] = { +Blocks['lists_getIndex'] = { /** * Block for getting element at index. - * @this {Blockly.Block} + * @this {Block} */ init: function() { - var MODE = - [ - [Blockly.Msg['LISTS_GET_INDEX_GET'], 'GET'], - [Blockly.Msg['LISTS_GET_INDEX_GET_REMOVE'], 'GET_REMOVE'], - [Blockly.Msg['LISTS_GET_INDEX_REMOVE'], 'REMOVE'] - ]; - this.WHERE_OPTIONS = - [ - [Blockly.Msg['LISTS_GET_INDEX_FROM_START'], 'FROM_START'], - [Blockly.Msg['LISTS_GET_INDEX_FROM_END'], 'FROM_END'], - [Blockly.Msg['LISTS_GET_INDEX_FIRST'], 'FIRST'], - [Blockly.Msg['LISTS_GET_INDEX_LAST'], 'LAST'], - [Blockly.Msg['LISTS_GET_INDEX_RANDOM'], 'RANDOM'] - ]; - this.setHelpUrl(Blockly.Msg['LISTS_GET_INDEX_HELPURL']); + const MODE = [ + [Msg['LISTS_GET_INDEX_GET'], 'GET'], + [Msg['LISTS_GET_INDEX_GET_REMOVE'], 'GET_REMOVE'], + [Msg['LISTS_GET_INDEX_REMOVE'], 'REMOVE'], + ]; + this.WHERE_OPTIONS = [ + [Msg['LISTS_GET_INDEX_FROM_START'], 'FROM_START'], + [Msg['LISTS_GET_INDEX_FROM_END'], 'FROM_END'], + [Msg['LISTS_GET_INDEX_FIRST'], 'FIRST'], + [Msg['LISTS_GET_INDEX_LAST'], 'LAST'], + [Msg['LISTS_GET_INDEX_RANDOM'], 'RANDOM'], + ]; + this.setHelpUrl(Msg['LISTS_GET_INDEX_HELPURL']); this.setStyle('list_blocks'); - var modeMenu = new Blockly.FieldDropdown(MODE, function(value) { - var isStatement = (value == 'REMOVE'); - this.getSourceBlock().updateStatement_(isStatement); - }); - this.appendValueInput('VALUE') - .setCheck('Array') - .appendField(Blockly.Msg['LISTS_GET_INDEX_INPUT_IN_LIST']); + const modeMenu = new FieldDropdown( + MODE, + /** + * @param {*} value The input value. + * @this {FieldDropdown} + */ + function(value) { + const isStatement = (value === 'REMOVE'); + this.getSourceBlock().updateStatement_(isStatement); + }); + this.appendValueInput('VALUE').setCheck('Array').appendField( + Msg['LISTS_GET_INDEX_INPUT_IN_LIST']); this.appendDummyInput() .appendField(modeMenu, 'MODE') .appendField('', 'SPACE'); this.appendDummyInput('AT'); - if (Blockly.Msg['LISTS_GET_INDEX_TAIL']) { - this.appendDummyInput('TAIL') - .appendField(Blockly.Msg['LISTS_GET_INDEX_TAIL']); + if (Msg['LISTS_GET_INDEX_TAIL']) { + this.appendDummyInput('TAIL').appendField(Msg['LISTS_GET_INDEX_TAIL']); } this.setInputsInline(true); this.setOutput(true); this.updateAt_(true); // Assign 'this' to a variable for use in the tooltip closure below. - var thisBlock = this; + const thisBlock = this; this.setTooltip(function() { - var mode = thisBlock.getFieldValue('MODE'); - var where = thisBlock.getFieldValue('WHERE'); - var tooltip = ''; + const mode = thisBlock.getFieldValue('MODE'); + const where = thisBlock.getFieldValue('WHERE'); + let tooltip = ''; switch (mode + ' ' + where) { case 'GET FROM_START': case 'GET FROM_END': - tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_GET_FROM']; + tooltip = Msg['LISTS_GET_INDEX_TOOLTIP_GET_FROM']; break; case 'GET FIRST': - tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_GET_FIRST']; + tooltip = Msg['LISTS_GET_INDEX_TOOLTIP_GET_FIRST']; break; case 'GET LAST': - tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_GET_LAST']; + tooltip = Msg['LISTS_GET_INDEX_TOOLTIP_GET_LAST']; break; case 'GET RANDOM': - tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_GET_RANDOM']; + tooltip = Msg['LISTS_GET_INDEX_TOOLTIP_GET_RANDOM']; break; case 'GET_REMOVE FROM_START': case 'GET_REMOVE FROM_END': - tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM']; + tooltip = Msg['LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM']; break; case 'GET_REMOVE FIRST': - tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST']; + tooltip = Msg['LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST']; break; case 'GET_REMOVE LAST': - tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST']; + tooltip = Msg['LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST']; break; case 'GET_REMOVE RANDOM': - tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM']; + tooltip = Msg['LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM']; break; case 'REMOVE FROM_START': case 'REMOVE FROM_END': - tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM']; + tooltip = Msg['LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM']; break; case 'REMOVE FIRST': - tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_REMOVE_FIRST']; + tooltip = Msg['LISTS_GET_INDEX_TOOLTIP_REMOVE_FIRST']; break; case 'REMOVE LAST': - tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_REMOVE_LAST']; + tooltip = Msg['LISTS_GET_INDEX_TOOLTIP_REMOVE_LAST']; break; case 'REMOVE RANDOM': - tooltip = Blockly.Msg['LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM']; + tooltip = Msg['LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM']; break; } - if (where == 'FROM_START' || where == 'FROM_END') { - var msg = (where == 'FROM_START') ? - Blockly.Msg['LISTS_INDEX_FROM_START_TOOLTIP'] : - Blockly.Msg['LISTS_INDEX_FROM_END_TOOLTIP']; - tooltip += ' ' + msg.replace('%1', - thisBlock.workspace.options.oneBasedIndex ? '#1' : '#0'); + if (where === 'FROM_START' || where === 'FROM_END') { + const msg = (where === 'FROM_START') ? + Msg['LISTS_INDEX_FROM_START_TOOLTIP'] : + Msg['LISTS_INDEX_FROM_END_TOOLTIP']; + tooltip += ' ' + + msg.replace( + '%1', thisBlock.workspace.options.oneBasedIndex ? '#1' : '#0'); } return tooltip; }); @@ -401,39 +416,45 @@ Blockly.Blocks['lists_getIndex'] = { * Create XML to represent whether the block is a statement or a value. * Also represent whether there is an 'AT' input. * @return {!Element} XML storage element. - * @this {Blockly.Block} + * @this {Block} */ mutationToDom: function() { - var container = Blockly.utils.xml.createElement('mutation'); - var isStatement = !this.outputConnection; + const container = xmlUtils.createElement('mutation'); + const isStatement = !this.outputConnection; container.setAttribute('statement', isStatement); - var isAt = this.getInput('AT').type == Blockly.INPUT_VALUE; + const isAt = this.getInput('AT').type === ConnectionType.INPUT_VALUE; container.setAttribute('at', isAt); return container; }, /** * Parse XML to restore the 'AT' input. * @param {!Element} xmlElement XML storage element. - * @this {Blockly.Block} + * @this {Block} */ domToMutation: function(xmlElement) { // Note: Until January 2013 this block did not have mutations, // so 'statement' defaults to false and 'at' defaults to true. - var isStatement = (xmlElement.getAttribute('statement') == 'true'); + const isStatement = (xmlElement.getAttribute('statement') === 'true'); this.updateStatement_(isStatement); - var isAt = (xmlElement.getAttribute('at') != 'false'); + const isAt = (xmlElement.getAttribute('at') !== 'false'); this.updateAt_(isAt); }, + + // This block does not need JSO serialization hooks (saveExtraState and + // loadExtraState) because the state of this object is already encoded in the + // dropdown values. + // XML hooks are kept for backwards compatibility. + /** * Switch between a value block and a statement block. * @param {boolean} newStatement True if the block should be a statement. * False if the block should be a value. * @private - * @this {Blockly.Block} + * @this {Block} */ updateStatement_: function(newStatement) { - var oldStatement = !this.outputConnection; - if (newStatement != oldStatement) { + const oldStatement = !this.outputConnection; + if (newStatement !== oldStatement) { this.unplug(true, true); if (newStatement) { this.setOutput(false); @@ -450,7 +471,7 @@ Blockly.Blocks['lists_getIndex'] = { * Create or delete an input for the numeric index. * @param {boolean} isAt True if the input should exist. * @private - * @this {Blockly.Block} + * @this {Block} */ updateAt_: function(isAt) { // Destroy old 'AT' and 'ORDINAL' inputs. @@ -459,105 +480,111 @@ Blockly.Blocks['lists_getIndex'] = { // Create either a value 'AT' input or a dummy input. if (isAt) { this.appendValueInput('AT').setCheck('Number'); - if (Blockly.Msg['ORDINAL_NUMBER_SUFFIX']) { - this.appendDummyInput('ORDINAL') - .appendField(Blockly.Msg['ORDINAL_NUMBER_SUFFIX']); + if (Msg['ORDINAL_NUMBER_SUFFIX']) { + this.appendDummyInput('ORDINAL').appendField( + Msg['ORDINAL_NUMBER_SUFFIX']); } } else { this.appendDummyInput('AT'); } - var menu = new Blockly.FieldDropdown(this.WHERE_OPTIONS, function(value) { - var newAt = (value == 'FROM_START') || (value == 'FROM_END'); - // The 'isAt' variable is available due to this function being a closure. - if (newAt != isAt) { - var block = this.getSourceBlock(); - block.updateAt_(newAt); - // This menu has been destroyed and replaced. Update the replacement. - block.setFieldValue(value, 'WHERE'); - return null; - } - return undefined; - }); + const menu = new FieldDropdown( + this.WHERE_OPTIONS, + /** + * @param {*} value The input value. + * @this {FieldDropdown} + * @returns {null|undefined} Null if the field has been replaced; + * otherwise undefined. + */ + function(value) { + 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(); + 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 (Blockly.Msg['LISTS_GET_INDEX_TAIL']) { + if (Msg['LISTS_GET_INDEX_TAIL']) { this.moveInputBefore('TAIL', null); } - } + }, }; -Blockly.Blocks['lists_setIndex'] = { +Blocks['lists_setIndex'] = { /** * Block for setting the element at index. - * @this {Blockly.Block} + * @this {Block} */ init: function() { - var MODE = - [ - [Blockly.Msg['LISTS_SET_INDEX_SET'], 'SET'], - [Blockly.Msg['LISTS_SET_INDEX_INSERT'], 'INSERT'] - ]; - this.WHERE_OPTIONS = - [ - [Blockly.Msg['LISTS_GET_INDEX_FROM_START'], 'FROM_START'], - [Blockly.Msg['LISTS_GET_INDEX_FROM_END'], 'FROM_END'], - [Blockly.Msg['LISTS_GET_INDEX_FIRST'], 'FIRST'], - [Blockly.Msg['LISTS_GET_INDEX_LAST'], 'LAST'], - [Blockly.Msg['LISTS_GET_INDEX_RANDOM'], 'RANDOM'] - ]; - this.setHelpUrl(Blockly.Msg['LISTS_SET_INDEX_HELPURL']); + const MODE = [ + [Msg['LISTS_SET_INDEX_SET'], 'SET'], + [Msg['LISTS_SET_INDEX_INSERT'], 'INSERT'], + ]; + this.WHERE_OPTIONS = [ + [Msg['LISTS_GET_INDEX_FROM_START'], 'FROM_START'], + [Msg['LISTS_GET_INDEX_FROM_END'], 'FROM_END'], + [Msg['LISTS_GET_INDEX_FIRST'], 'FIRST'], + [Msg['LISTS_GET_INDEX_LAST'], 'LAST'], + [Msg['LISTS_GET_INDEX_RANDOM'], 'RANDOM'], + ]; + this.setHelpUrl(Msg['LISTS_SET_INDEX_HELPURL']); this.setStyle('list_blocks'); - this.appendValueInput('LIST') - .setCheck('Array') - .appendField(Blockly.Msg['LISTS_SET_INDEX_INPUT_IN_LIST']); + this.appendValueInput('LIST').setCheck('Array').appendField( + Msg['LISTS_SET_INDEX_INPUT_IN_LIST']); this.appendDummyInput() - .appendField(new Blockly.FieldDropdown(MODE), 'MODE') + .appendField(new FieldDropdown(MODE), 'MODE') .appendField('', 'SPACE'); this.appendDummyInput('AT'); - this.appendValueInput('TO') - .appendField(Blockly.Msg['LISTS_SET_INDEX_INPUT_TO']); + this.appendValueInput('TO').appendField(Msg['LISTS_SET_INDEX_INPUT_TO']); this.setInputsInline(true); this.setPreviousStatement(true); this.setNextStatement(true); - this.setTooltip(Blockly.Msg['LISTS_SET_INDEX_TOOLTIP']); + this.setTooltip(Msg['LISTS_SET_INDEX_TOOLTIP']); this.updateAt_(true); // Assign 'this' to a variable for use in the tooltip closure below. - var thisBlock = this; + const thisBlock = this; this.setTooltip(function() { - var mode = thisBlock.getFieldValue('MODE'); - var where = thisBlock.getFieldValue('WHERE'); - var tooltip = ''; + const mode = thisBlock.getFieldValue('MODE'); + const where = thisBlock.getFieldValue('WHERE'); + let tooltip = ''; switch (mode + ' ' + where) { case 'SET FROM_START': case 'SET FROM_END': - tooltip = Blockly.Msg['LISTS_SET_INDEX_TOOLTIP_SET_FROM']; + tooltip = Msg['LISTS_SET_INDEX_TOOLTIP_SET_FROM']; break; case 'SET FIRST': - tooltip = Blockly.Msg['LISTS_SET_INDEX_TOOLTIP_SET_FIRST']; + tooltip = Msg['LISTS_SET_INDEX_TOOLTIP_SET_FIRST']; break; case 'SET LAST': - tooltip = Blockly.Msg['LISTS_SET_INDEX_TOOLTIP_SET_LAST']; + tooltip = Msg['LISTS_SET_INDEX_TOOLTIP_SET_LAST']; break; case 'SET RANDOM': - tooltip = Blockly.Msg['LISTS_SET_INDEX_TOOLTIP_SET_RANDOM']; + tooltip = Msg['LISTS_SET_INDEX_TOOLTIP_SET_RANDOM']; break; case 'INSERT FROM_START': case 'INSERT FROM_END': - tooltip = Blockly.Msg['LISTS_SET_INDEX_TOOLTIP_INSERT_FROM']; + tooltip = Msg['LISTS_SET_INDEX_TOOLTIP_INSERT_FROM']; break; case 'INSERT FIRST': - tooltip = Blockly.Msg['LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST']; + tooltip = Msg['LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST']; break; case 'INSERT LAST': - tooltip = Blockly.Msg['LISTS_SET_INDEX_TOOLTIP_INSERT_LAST']; + tooltip = Msg['LISTS_SET_INDEX_TOOLTIP_INSERT_LAST']; break; case 'INSERT RANDOM': - tooltip = Blockly.Msg['LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM']; + tooltip = Msg['LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM']; break; } - if (where == 'FROM_START' || where == 'FROM_END') { - tooltip += ' ' + Blockly.Msg['LISTS_INDEX_FROM_START_TOOLTIP'] - .replace('%1', - thisBlock.workspace.options.oneBasedIndex ? '#1' : '#0'); + if (where === 'FROM_START' || where === 'FROM_END') { + tooltip += ' ' + + Msg['LISTS_INDEX_FROM_START_TOOLTIP'].replace( + '%1', thisBlock.workspace.options.oneBasedIndex ? '#1' : '#0'); } return tooltip; }); @@ -565,30 +592,36 @@ Blockly.Blocks['lists_setIndex'] = { /** * Create XML to represent whether there is an 'AT' input. * @return {!Element} XML storage element. - * @this {Blockly.Block} + * @this {Block} */ mutationToDom: function() { - var container = Blockly.utils.xml.createElement('mutation'); - var isAt = this.getInput('AT').type == Blockly.INPUT_VALUE; + const container = xmlUtils.createElement('mutation'); + const isAt = this.getInput('AT').type === ConnectionType.INPUT_VALUE; container.setAttribute('at', isAt); return container; }, /** * Parse XML to restore the 'AT' input. * @param {!Element} xmlElement XML storage element. - * @this {Blockly.Block} + * @this {Block} */ domToMutation: function(xmlElement) { // Note: Until January 2013 this block did not have mutations, // so 'at' defaults to true. - var isAt = (xmlElement.getAttribute('at') != 'false'); + const isAt = (xmlElement.getAttribute('at') !== 'false'); this.updateAt_(isAt); }, + + // This block does not need JSO serialization hooks (saveExtraState and + // loadExtraState) because the state of this object is already encoded in the + // dropdown values. + // XML hooks are kept for backwards compatibility. + /** * Create or delete an input for the numeric index. * @param {boolean} isAt True if the input should exist. * @private - * @this {Blockly.Block} + * @this {Block} */ updateAt_: function(isAt) { // Destroy old 'AT' and 'ORDINAL' input. @@ -597,100 +630,112 @@ Blockly.Blocks['lists_setIndex'] = { // Create either a value 'AT' input or a dummy input. if (isAt) { this.appendValueInput('AT').setCheck('Number'); - if (Blockly.Msg['ORDINAL_NUMBER_SUFFIX']) { - this.appendDummyInput('ORDINAL') - .appendField(Blockly.Msg['ORDINAL_NUMBER_SUFFIX']); + if (Msg['ORDINAL_NUMBER_SUFFIX']) { + this.appendDummyInput('ORDINAL').appendField( + Msg['ORDINAL_NUMBER_SUFFIX']); } } else { this.appendDummyInput('AT'); } - var menu = new Blockly.FieldDropdown(this.WHERE_OPTIONS, function(value) { - var newAt = (value == 'FROM_START') || (value == 'FROM_END'); - // The 'isAt' variable is available due to this function being a closure. - if (newAt != isAt) { - var block = this.getSourceBlock(); - block.updateAt_(newAt); - // This menu has been destroyed and replaced. Update the replacement. - block.setFieldValue(value, 'WHERE'); - return null; - } - return undefined; - }); + const menu = new FieldDropdown( + this.WHERE_OPTIONS, + /** + * @param {*} value The input value. + * @this {FieldDropdown} + * @returns {null|undefined} Null if the field has been replaced; + * otherwise undefined. + */ + function(value) { + 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(); + 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'); } this.getInput('AT').appendField(menu, 'WHERE'); - } + }, }; -Blockly.Blocks['lists_getSublist'] = { +Blocks['lists_getSublist'] = { /** * Block for getting sublist. - * @this {Blockly.Block} + * @this {Block} */ init: function() { - this['WHERE_OPTIONS_1'] = - [ - [Blockly.Msg['LISTS_GET_SUBLIST_START_FROM_START'], 'FROM_START'], - [Blockly.Msg['LISTS_GET_SUBLIST_START_FROM_END'], 'FROM_END'], - [Blockly.Msg['LISTS_GET_SUBLIST_START_FIRST'], 'FIRST'] - ]; - this['WHERE_OPTIONS_2'] = - [ - [Blockly.Msg['LISTS_GET_SUBLIST_END_FROM_START'], 'FROM_START'], - [Blockly.Msg['LISTS_GET_SUBLIST_END_FROM_END'], 'FROM_END'], - [Blockly.Msg['LISTS_GET_SUBLIST_END_LAST'], 'LAST'] - ]; - this.setHelpUrl(Blockly.Msg['LISTS_GET_SUBLIST_HELPURL']); + this['WHERE_OPTIONS_1'] = [ + [Msg['LISTS_GET_SUBLIST_START_FROM_START'], 'FROM_START'], + [Msg['LISTS_GET_SUBLIST_START_FROM_END'], 'FROM_END'], + [Msg['LISTS_GET_SUBLIST_START_FIRST'], 'FIRST'], + ]; + this['WHERE_OPTIONS_2'] = [ + [Msg['LISTS_GET_SUBLIST_END_FROM_START'], 'FROM_START'], + [Msg['LISTS_GET_SUBLIST_END_FROM_END'], 'FROM_END'], + [Msg['LISTS_GET_SUBLIST_END_LAST'], 'LAST'], + ]; + this.setHelpUrl(Msg['LISTS_GET_SUBLIST_HELPURL']); this.setStyle('list_blocks'); - this.appendValueInput('LIST') - .setCheck('Array') - .appendField(Blockly.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 (Blockly.Msg['LISTS_GET_SUBLIST_TAIL']) { - this.appendDummyInput('TAIL') - .appendField(Blockly.Msg['LISTS_GET_SUBLIST_TAIL']); + if (Msg['LISTS_GET_SUBLIST_TAIL']) { + this.appendDummyInput('TAIL').appendField(Msg['LISTS_GET_SUBLIST_TAIL']); } this.setInputsInline(true); this.setOutput(true, 'Array'); this.updateAt_(1, true); this.updateAt_(2, true); - this.setTooltip(Blockly.Msg['LISTS_GET_SUBLIST_TOOLTIP']); + this.setTooltip(Msg['LISTS_GET_SUBLIST_TOOLTIP']); }, /** * Create XML to represent whether there are 'AT' inputs. * @return {!Element} XML storage element. - * @this {Blockly.Block} + * @this {Block} */ mutationToDom: function() { - var container = Blockly.utils.xml.createElement('mutation'); - var isAt1 = this.getInput('AT1').type == Blockly.INPUT_VALUE; + const container = xmlUtils.createElement('mutation'); + const isAt1 = this.getInput('AT1').type === ConnectionType.INPUT_VALUE; container.setAttribute('at1', isAt1); - var isAt2 = this.getInput('AT2').type == Blockly.INPUT_VALUE; + const isAt2 = this.getInput('AT2').type === ConnectionType.INPUT_VALUE; container.setAttribute('at2', isAt2); return container; }, /** * Parse XML to restore the 'AT' inputs. * @param {!Element} xmlElement XML storage element. - * @this {Blockly.Block} + * @this {Block} */ domToMutation: function(xmlElement) { - var isAt1 = (xmlElement.getAttribute('at1') == 'true'); - var isAt2 = (xmlElement.getAttribute('at2') == 'true'); + const isAt1 = (xmlElement.getAttribute('at1') === 'true'); + const isAt2 = (xmlElement.getAttribute('at2') === 'true'); this.updateAt_(1, isAt1); this.updateAt_(2, isAt2); }, + + // This block does not need JSO serialization hooks (saveExtraState and + // loadExtraState) because the state of this object is already encoded in the + // dropdown values. + // XML hooks are kept for backwards compatibility. + /** * Create or delete an input for a numeric index. * This block has two such inputs, independent of each other. * @param {number} n Specify first or second input (1 or 2). * @param {boolean} isAt True if the input should exist. * @private - * @this {Blockly.Block} + * @this {Block} */ updateAt_: function(n, isAt) { // Create or delete an input for the numeric index. @@ -700,20 +745,27 @@ Blockly.Blocks['lists_getSublist'] = { // Create either a value 'AT' input or a dummy input. if (isAt) { this.appendValueInput('AT' + n).setCheck('Number'); - if (Blockly.Msg['ORDINAL_NUMBER_SUFFIX']) { + if (Msg['ORDINAL_NUMBER_SUFFIX']) { this.appendDummyInput('ORDINAL' + n) - .appendField(Blockly.Msg['ORDINAL_NUMBER_SUFFIX']); + .appendField(Msg['ORDINAL_NUMBER_SUFFIX']); } } else { this.appendDummyInput('AT' + n); } - var menu = new Blockly.FieldDropdown(this['WHERE_OPTIONS_' + n], + const menu = new FieldDropdown( + this['WHERE_OPTIONS_' + n], + /** + * @param {*} value The input value. + * @this {FieldDropdown} + * @returns {null|undefined} Null if the field has been replaced; + * otherwise undefined. + */ function(value) { - var newAt = (value == 'FROM_START') || (value == 'FROM_END'); + const newAt = (value === 'FROM_START') || (value === 'FROM_END'); // The 'isAt' variable is available due to this function being a // closure. - if (newAt != isAt) { - var block = this.getSourceBlock(); + if (newAt !== isAt) { + const block = this.getSourceBlock(); block.updateAt_(n, newAt); // This menu has been destroyed and replaced. // Update the replacement. @@ -721,92 +773,89 @@ Blockly.Blocks['lists_getSublist'] = { return null; } }); - this.getInput('AT' + n) - .appendField(menu, 'WHERE' + n); - if (n == 1) { + this.getInput('AT' + n).appendField(menu, 'WHERE' + n); + if (n === 1) { this.moveInputBefore('AT1', 'AT2'); if (this.getInput('ORDINAL1')) { this.moveInputBefore('ORDINAL1', 'AT2'); } } - if (Blockly.Msg['LISTS_GET_SUBLIST_TAIL']) { + if (Msg['LISTS_GET_SUBLIST_TAIL']) { this.moveInputBefore('TAIL', null); } - } + }, }; -Blockly.Blocks['lists_sort'] = { +Blocks['lists_sort'] = { /** * Block for sorting a list. - * @this {Blockly.Block} + * @this {Block} */ init: function() { this.jsonInit({ - "message0": Blockly.Msg['LISTS_SORT_TITLE'], - "args0": [ + 'message0': Msg['LISTS_SORT_TITLE'], + 'args0': [ { - "type": "field_dropdown", - "name": "TYPE", - "options": [ - [Blockly.Msg['LISTS_SORT_TYPE_NUMERIC'], "NUMERIC"], - [Blockly.Msg['LISTS_SORT_TYPE_TEXT'], "TEXT"], - [Blockly.Msg['LISTS_SORT_TYPE_IGNORECASE'], "IGNORE_CASE"] - ] + 'type': 'field_dropdown', + 'name': 'TYPE', + 'options': [ + [Msg['LISTS_SORT_TYPE_NUMERIC'], 'NUMERIC'], + [Msg['LISTS_SORT_TYPE_TEXT'], 'TEXT'], + [Msg['LISTS_SORT_TYPE_IGNORECASE'], 'IGNORE_CASE'], + ], }, { - "type": "field_dropdown", - "name": "DIRECTION", - "options": [ - [Blockly.Msg['LISTS_SORT_ORDER_ASCENDING'], "1"], - [Blockly.Msg['LISTS_SORT_ORDER_DESCENDING'], "-1"] - ] + 'type': 'field_dropdown', + 'name': 'DIRECTION', + 'options': [ + [Msg['LISTS_SORT_ORDER_ASCENDING'], '1'], + [Msg['LISTS_SORT_ORDER_DESCENDING'], '-1'], + ], }, { - "type": "input_value", - "name": "LIST", - "check": "Array" - } + 'type': 'input_value', + 'name': 'LIST', + 'check': 'Array', + }, ], - "output": "Array", - "style": "list_blocks", - "tooltip": Blockly.Msg['LISTS_SORT_TOOLTIP'], - "helpUrl": Blockly.Msg['LISTS_SORT_HELPURL'] + 'output': 'Array', + 'style': 'list_blocks', + 'tooltip': Msg['LISTS_SORT_TOOLTIP'], + 'helpUrl': Msg['LISTS_SORT_HELPURL'], }); - } + }, }; -Blockly.Blocks['lists_split'] = { +Blocks['lists_split'] = { /** * Block for splitting text into a list, or joining a list into text. - * @this {Blockly.Block} + * @this {Block} */ init: function() { // Assign 'this' to a variable for use in the closures below. - var thisBlock = this; - var dropdown = new Blockly.FieldDropdown( + const thisBlock = this; + const dropdown = new FieldDropdown( [ - [Blockly.Msg['LISTS_SPLIT_LIST_FROM_TEXT'], 'SPLIT'], - [Blockly.Msg['LISTS_SPLIT_TEXT_FROM_LIST'], 'JOIN'] + [Msg['LISTS_SPLIT_LIST_FROM_TEXT'], 'SPLIT'], + [Msg['LISTS_SPLIT_TEXT_FROM_LIST'], 'JOIN'], ], function(newMode) { thisBlock.updateType_(newMode); }); - this.setHelpUrl(Blockly.Msg['LISTS_SPLIT_HELPURL']); + this.setHelpUrl(Msg['LISTS_SPLIT_HELPURL']); this.setStyle('list_blocks'); - this.appendValueInput('INPUT') - .setCheck('String') - .appendField(dropdown, 'MODE'); - this.appendValueInput('DELIM') - .setCheck('String') - .appendField(Blockly.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(function() { - var mode = thisBlock.getFieldValue('MODE'); - if (mode == 'SPLIT') { - return Blockly.Msg['LISTS_SPLIT_TOOLTIP_SPLIT']; - } else if (mode == 'JOIN') { - return Blockly.Msg['LISTS_SPLIT_TOOLTIP_JOIN']; + const mode = thisBlock.getFieldValue('MODE'); + if (mode === 'SPLIT') { + return Msg['LISTS_SPLIT_TOOLTIP_SPLIT']; + } else if (mode === 'JOIN') { + return Msg['LISTS_SPLIT_TOOLTIP_JOIN']; } throw Error('Unknown mode: ' + mode); }); @@ -815,14 +864,14 @@ Blockly.Blocks['lists_split'] = { * Modify this block to have the correct input and output types. * @param {string} newMode Either 'SPLIT' or 'JOIN'. * @private - * @this {Blockly.Block} + * @this {Block} */ updateType_: function(newMode) { - var mode = this.getFieldValue('MODE'); - if (mode != newMode) { - var inputConnection = this.getInput('INPUT').connection; + const mode = this.getFieldValue('MODE'); + if (mode !== newMode) { + const inputConnection = this.getInput('INPUT').connection; inputConnection.setShadowDom(null); - var inputBlock = inputConnection.targetBlock(); + const inputBlock = inputConnection.targetBlock(); if (inputBlock) { inputConnection.disconnect(); if (inputBlock.isShadow()) { @@ -832,7 +881,7 @@ Blockly.Blocks['lists_split'] = { } } } - if (newMode == 'SPLIT') { + if (newMode === 'SPLIT') { this.outputConnection.setCheck('Array'); this.getInput('INPUT').setCheck('String'); } else { @@ -843,19 +892,24 @@ Blockly.Blocks['lists_split'] = { /** * Create XML to represent the input and output types. * @return {!Element} XML storage element. - * @this {Blockly.Block} + * @this {Block} */ mutationToDom: function() { - var container = Blockly.utils.xml.createElement('mutation'); + const container = xmlUtils.createElement('mutation'); container.setAttribute('mode', this.getFieldValue('MODE')); return container; }, /** * Parse XML to restore the input and output types. * @param {!Element} xmlElement XML storage element. - * @this {Blockly.Block} + * @this {Block} */ domToMutation: function(xmlElement) { this.updateType_(xmlElement.getAttribute('mode')); - } + }, + + // This block does not need JSO serialization hooks (saveExtraState and + // loadExtraState) because the state of this object is already encoded in the + // dropdown values. + // XML hooks are kept for backwards compatibility. }; diff --git a/blocks/logic.js b/blocks/logic.js index c7b6acf0cdc..8a79d7f9474 100644 --- a/blocks/logic.js +++ b/blocks/logic.js @@ -6,267 +6,266 @@ /** * @fileoverview Logic blocks for Blockly. - * - * This file is scraped to extract a .json file of block definitions. The array - * passed to defineBlocksWithJsonArray(..) must be strict JSON: double quotes - * only, no outside references, no functions, no trailing commas, etc. The one - * exception is end-of-line comments, which the scraper will remove. - * @author q.neutron@gmail.com (Quynh Neutron) + * @suppress {checkTypes} */ 'use strict'; -goog.provide('Blockly.Blocks.logic'); // Deprecated -goog.provide('Blockly.Constants.Logic'); +goog.module('Blockly.blocks.logic'); -goog.require('Blockly'); -goog.require('Blockly.Blocks'); +/* eslint-disable-next-line no-unused-vars */ +const AbstractEvent = goog.requireType('Blockly.Events.Abstract'); +const Events = goog.require('Blockly.Events'); +const Extensions = goog.require('Blockly.Extensions'); +const xmlUtils = goog.require('Blockly.utils.xml'); +/* eslint-disable-next-line no-unused-vars */ +const {Block} = goog.requireType('Blockly.Block'); +const {Msg} = goog.require('Blockly.Msg'); +const {Mutator} = goog.require('Blockly.Mutator'); +/* eslint-disable-next-line no-unused-vars */ +const {RenderedConnection} = goog.requireType('Blockly.RenderedConnection'); +/* eslint-disable-next-line no-unused-vars */ +const {Workspace} = goog.requireType('Blockly.Workspace'); +const {defineBlocksWithJsonArray} = goog.require('Blockly.common'); +/** @suppress {extraRequire} */ goog.require('Blockly.FieldDropdown'); +/** @suppress {extraRequire} */ goog.require('Blockly.FieldLabel'); -goog.require('Blockly.Mutator'); -/** - * Unused constant for the common HSV hue for all blocks in this category. - * @deprecated Use Blockly.Msg['LOGIC_HUE']. (2018 April 5) - */ -Blockly.Constants.Logic.HUE = 210; - -Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT +defineBlocksWithJsonArray([ // Block for boolean data type: true and false. { - "type": "logic_boolean", - "message0": "%1", - "args0": [ + 'type': 'logic_boolean', + 'message0': '%1', + 'args0': [ { - "type": "field_dropdown", - "name": "BOOL", - "options": [ - ["%{BKY_LOGIC_BOOLEAN_TRUE}", "TRUE"], - ["%{BKY_LOGIC_BOOLEAN_FALSE}", "FALSE"] - ] - } + 'type': 'field_dropdown', + 'name': 'BOOL', + 'options': [ + ['%{BKY_LOGIC_BOOLEAN_TRUE}', 'TRUE'], + ['%{BKY_LOGIC_BOOLEAN_FALSE}', 'FALSE'], + ], + }, ], - "output": "Boolean", - "style": "logic_blocks", - "tooltip": "%{BKY_LOGIC_BOOLEAN_TOOLTIP}", - "helpUrl": "%{BKY_LOGIC_BOOLEAN_HELPURL}" + 'output': 'Boolean', + 'style': 'logic_blocks', + 'tooltip': '%{BKY_LOGIC_BOOLEAN_TOOLTIP}', + 'helpUrl': '%{BKY_LOGIC_BOOLEAN_HELPURL}', }, // Block for if/elseif/else condition. { - "type": "controls_if", - "message0": "%{BKY_CONTROLS_IF_MSG_IF} %1", - "args0": [ + 'type': 'controls_if', + 'message0': '%{BKY_CONTROLS_IF_MSG_IF} %1', + 'args0': [ { - "type": "input_value", - "name": "IF0", - "check": "Boolean" - } + 'type': 'input_value', + 'name': 'IF0', + 'check': 'Boolean', + }, ], - "message1": "%{BKY_CONTROLS_IF_MSG_THEN} %1", - "args1": [ + 'message1': '%{BKY_CONTROLS_IF_MSG_THEN} %1', + 'args1': [ { - "type": "input_statement", - "name": "DO0" - } + 'type': 'input_statement', + 'name': 'DO0', + }, ], - "previousStatement": null, - "nextStatement": null, - "style": "logic_blocks", - "helpUrl": "%{BKY_CONTROLS_IF_HELPURL}", - "mutator": "controls_if_mutator", - "extensions": ["controls_if_tooltip"] + 'previousStatement': null, + 'nextStatement': null, + 'style': 'logic_blocks', + 'helpUrl': '%{BKY_CONTROLS_IF_HELPURL}', + 'suppressPrefixSuffix': true, + 'mutator': 'controls_if_mutator', + 'extensions': ['controls_if_tooltip'], }, // If/else block that does not use a mutator. { - "type": "controls_ifelse", - "message0": "%{BKY_CONTROLS_IF_MSG_IF} %1", - "args0": [ + 'type': 'controls_ifelse', + 'message0': '%{BKY_CONTROLS_IF_MSG_IF} %1', + 'args0': [ { - "type": "input_value", - "name": "IF0", - "check": "Boolean" - } + 'type': 'input_value', + 'name': 'IF0', + 'check': 'Boolean', + }, ], - "message1": "%{BKY_CONTROLS_IF_MSG_THEN} %1", - "args1": [ + 'message1': '%{BKY_CONTROLS_IF_MSG_THEN} %1', + 'args1': [ { - "type": "input_statement", - "name": "DO0" - } + 'type': 'input_statement', + 'name': 'DO0', + }, ], - "message2": "%{BKY_CONTROLS_IF_MSG_ELSE} %1", - "args2": [ + 'message2': '%{BKY_CONTROLS_IF_MSG_ELSE} %1', + 'args2': [ { - "type": "input_statement", - "name": "ELSE" - } + 'type': 'input_statement', + 'name': 'ELSE', + }, ], - "previousStatement": null, - "nextStatement": null, - "style": "logic_blocks", - "tooltip": "%{BKYCONTROLS_IF_TOOLTIP_2}", - "helpUrl": "%{BKY_CONTROLS_IF_HELPURL}", - "extensions": ["controls_if_tooltip"] + 'previousStatement': null, + 'nextStatement': null, + 'style': 'logic_blocks', + 'tooltip': '%{BKYCONTROLS_IF_TOOLTIP_2}', + 'helpUrl': '%{BKY_CONTROLS_IF_HELPURL}', + 'suppressPrefixSuffix': true, + 'extensions': ['controls_if_tooltip'], }, // Block for comparison operator. { - "type": "logic_compare", - "message0": "%1 %2 %3", - "args0": [ + 'type': 'logic_compare', + 'message0': '%1 %2 %3', + 'args0': [ { - "type": "input_value", - "name": "A" + 'type': 'input_value', + 'name': 'A', }, { - "type": "field_dropdown", - "name": "OP", - "options": [ - ["=", "EQ"], - ["\u2260", "NEQ"], - ["\u200F<", "LT"], - ["\u200F\u2264", "LTE"], - ["\u200F>", "GT"], - ["\u200F\u2265", "GTE"] - ] + 'type': 'field_dropdown', + 'name': 'OP', + 'options': [ + ['=', 'EQ'], + ['\u2260', 'NEQ'], + ['\u200F<', 'LT'], + ['\u200F\u2264', 'LTE'], + ['\u200F>', 'GT'], + ['\u200F\u2265', 'GTE'], + ], }, { - "type": "input_value", - "name": "B" - } + 'type': 'input_value', + 'name': 'B', + }, ], - "inputsInline": true, - "output": "Boolean", - "style": "logic_blocks", - "helpUrl": "%{BKY_LOGIC_COMPARE_HELPURL}", - "extensions": ["logic_compare", "logic_op_tooltip"] + 'inputsInline': true, + 'output': 'Boolean', + 'style': 'logic_blocks', + 'helpUrl': '%{BKY_LOGIC_COMPARE_HELPURL}', + 'extensions': ['logic_compare', 'logic_op_tooltip'], }, // Block for logical operations: 'and', 'or'. { - "type": "logic_operation", - "message0": "%1 %2 %3", - "args0": [ + 'type': 'logic_operation', + 'message0': '%1 %2 %3', + 'args0': [ { - "type": "input_value", - "name": "A", - "check": "Boolean" + 'type': 'input_value', + 'name': 'A', + 'check': 'Boolean', }, { - "type": "field_dropdown", - "name": "OP", - "options": [ - ["%{BKY_LOGIC_OPERATION_AND}", "AND"], - ["%{BKY_LOGIC_OPERATION_OR}", "OR"] - ] + 'type': 'field_dropdown', + 'name': 'OP', + 'options': [ + ['%{BKY_LOGIC_OPERATION_AND}', 'AND'], + ['%{BKY_LOGIC_OPERATION_OR}', 'OR'], + ], }, { - "type": "input_value", - "name": "B", - "check": "Boolean" - } + 'type': 'input_value', + 'name': 'B', + 'check': 'Boolean', + }, ], - "inputsInline": true, - "output": "Boolean", - "style": "logic_blocks", - "helpUrl": "%{BKY_LOGIC_OPERATION_HELPURL}", - "extensions": ["logic_op_tooltip"] + 'inputsInline': true, + 'output': 'Boolean', + 'style': 'logic_blocks', + 'helpUrl': '%{BKY_LOGIC_OPERATION_HELPURL}', + 'extensions': ['logic_op_tooltip'], }, // Block for negation. { - "type": "logic_negate", - "message0": "%{BKY_LOGIC_NEGATE_TITLE}", - "args0": [ + 'type': 'logic_negate', + 'message0': '%{BKY_LOGIC_NEGATE_TITLE}', + 'args0': [ { - "type": "input_value", - "name": "BOOL", - "check": "Boolean" - } + 'type': 'input_value', + 'name': 'BOOL', + 'check': 'Boolean', + }, ], - "output": "Boolean", - "style": "logic_blocks", - "tooltip": "%{BKY_LOGIC_NEGATE_TOOLTIP}", - "helpUrl": "%{BKY_LOGIC_NEGATE_HELPURL}" + 'output': 'Boolean', + 'style': 'logic_blocks', + 'tooltip': '%{BKY_LOGIC_NEGATE_TOOLTIP}', + 'helpUrl': '%{BKY_LOGIC_NEGATE_HELPURL}', }, // Block for null data type. { - "type": "logic_null", - "message0": "%{BKY_LOGIC_NULL}", - "output": null, - "style": "logic_blocks", - "tooltip": "%{BKY_LOGIC_NULL_TOOLTIP}", - "helpUrl": "%{BKY_LOGIC_NULL_HELPURL}" + 'type': 'logic_null', + 'message0': '%{BKY_LOGIC_NULL}', + 'output': null, + 'style': 'logic_blocks', + 'tooltip': '%{BKY_LOGIC_NULL_TOOLTIP}', + 'helpUrl': '%{BKY_LOGIC_NULL_HELPURL}', }, // Block for ternary operator. { - "type": "logic_ternary", - "message0": "%{BKY_LOGIC_TERNARY_CONDITION} %1", - "args0": [ + 'type': 'logic_ternary', + 'message0': '%{BKY_LOGIC_TERNARY_CONDITION} %1', + 'args0': [ { - "type": "input_value", - "name": "IF", - "check": "Boolean" - } + 'type': 'input_value', + 'name': 'IF', + 'check': 'Boolean', + }, ], - "message1": "%{BKY_LOGIC_TERNARY_IF_TRUE} %1", - "args1": [ + 'message1': '%{BKY_LOGIC_TERNARY_IF_TRUE} %1', + 'args1': [ { - "type": "input_value", - "name": "THEN" - } + 'type': 'input_value', + 'name': 'THEN', + }, ], - "message2": "%{BKY_LOGIC_TERNARY_IF_FALSE} %1", - "args2": [ + 'message2': '%{BKY_LOGIC_TERNARY_IF_FALSE} %1', + 'args2': [ { - "type": "input_value", - "name": "ELSE" - } + 'type': 'input_value', + 'name': 'ELSE', + }, ], - "output": null, - "style": "logic_blocks", - "tooltip": "%{BKY_LOGIC_TERNARY_TOOLTIP}", - "helpUrl": "%{BKY_LOGIC_TERNARY_HELPURL}", - "extensions": ["logic_ternary"] - } -]); // END JSON EXTRACT (Do not delete this comment.) - -Blockly.defineBlocksWithJsonArray([ // Mutator blocks. Do not extract. + 'output': null, + 'style': 'logic_blocks', + 'tooltip': '%{BKY_LOGIC_TERNARY_TOOLTIP}', + 'helpUrl': '%{BKY_LOGIC_TERNARY_HELPURL}', + 'extensions': ['logic_ternary'], + }, // Block representing the if statement in the controls_if mutator. { - "type": "controls_if_if", - "message0": "%{BKY_CONTROLS_IF_IF_TITLE_IF}", - "nextStatement": null, - "enableContextMenu": false, - "style": "logic_blocks", - "tooltip": "%{BKY_CONTROLS_IF_IF_TOOLTIP}" + 'type': 'controls_if_if', + 'message0': '%{BKY_CONTROLS_IF_IF_TITLE_IF}', + 'nextStatement': null, + 'enableContextMenu': false, + 'style': 'logic_blocks', + 'tooltip': '%{BKY_CONTROLS_IF_IF_TOOLTIP}', }, // Block representing the else-if statement in the controls_if mutator. { - "type": "controls_if_elseif", - "message0": "%{BKY_CONTROLS_IF_ELSEIF_TITLE_ELSEIF}", - "previousStatement": null, - "nextStatement": null, - "enableContextMenu": false, - "style": "logic_blocks", - "tooltip": "%{BKY_CONTROLS_IF_ELSEIF_TOOLTIP}" + 'type': 'controls_if_elseif', + 'message0': '%{BKY_CONTROLS_IF_ELSEIF_TITLE_ELSEIF}', + 'previousStatement': null, + 'nextStatement': null, + 'enableContextMenu': false, + 'style': 'logic_blocks', + 'tooltip': '%{BKY_CONTROLS_IF_ELSEIF_TOOLTIP}', }, // Block representing the else statement in the controls_if mutator. { - "type": "controls_if_else", - "message0": "%{BKY_CONTROLS_IF_ELSE_TITLE_ELSE}", - "previousStatement": null, - "enableContextMenu": false, - "style": "logic_blocks", - "tooltip": "%{BKY_CONTROLS_IF_ELSE_TOOLTIP}" - } + 'type': 'controls_if_else', + 'message0': '%{BKY_CONTROLS_IF_ELSE_TITLE_ELSE}', + 'previousStatement': null, + 'enableContextMenu': false, + 'style': 'logic_blocks', + 'tooltip': '%{BKY_CONTROLS_IF_ELSE_TOOLTIP}', + }, ]); /** * Tooltip text, keyed by block OP value. Used by logic_compare and * logic_operation blocks. - * @see {Blockly.Extensions#buildTooltipForDropdown} - * @package + * @see {Extensions#buildTooltipForDropdown} * @readonly */ -Blockly.Constants.Logic.TOOLTIPS_BY_OP = { +const TOOLTIPS_BY_OP = { // logic_compare 'EQ': '%{BKY_LOGIC_COMPARE_TOOLTIP_EQ}', 'NEQ': '%{BKY_LOGIC_COMPARE_TOOLTIP_NEQ}', @@ -277,40 +276,34 @@ Blockly.Constants.Logic.TOOLTIPS_BY_OP = { // logic_operation 'AND': '%{BKY_LOGIC_OPERATION_TOOLTIP_AND}', - 'OR': '%{BKY_LOGIC_OPERATION_TOOLTIP_OR}' + 'OR': '%{BKY_LOGIC_OPERATION_TOOLTIP_OR}', }; -Blockly.Extensions.register('logic_op_tooltip', - Blockly.Extensions.buildTooltipForDropdown( - 'OP', Blockly.Constants.Logic.TOOLTIPS_BY_OP)); +Extensions.register( + 'logic_op_tooltip', + Extensions.buildTooltipForDropdown('OP', TOOLTIPS_BY_OP)); /** * Mutator methods added to controls_if blocks. * @mixin - * @augments Blockly.Block - * @package + * @augments Block * @readonly */ -Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN = { +const CONTROLS_IF_MUTATOR_MIXIN = { elseifCount_: 0, elseCount_: 0, - /** - * Don't automatically add STATEMENT_PREFIX and STATEMENT_SUFFIX to generated - * code. These will be handled manually in this block's generators. - */ - suppressPrefixSuffix: true, - /** * Create XML to represent the number of else-if and else inputs. + * Backwards compatible serialization implementation. * @return {Element} XML storage element. - * @this {Blockly.Block} + * @this {Block} */ mutationToDom: function() { if (!this.elseifCount_ && !this.elseCount_) { return null; } - var container = Blockly.utils.xml.createElement('mutation'); + const container = xmlUtils.createElement('mutation'); if (this.elseifCount_) { container.setAttribute('elseif', this.elseifCount_); } @@ -321,32 +314,61 @@ Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN = { }, /** * Parse XML to restore the else-if and else inputs. + * Backwards compatible serialization implementation. * @param {!Element} xmlElement XML storage element. - * @this {Blockly.Block} + * @this {Block} */ domToMutation: function(xmlElement) { this.elseifCount_ = parseInt(xmlElement.getAttribute('elseif'), 10) || 0; this.elseCount_ = parseInt(xmlElement.getAttribute('else'), 10) || 0; this.rebuildShape_(); }, + /** + * Returns the state of this block as a JSON serializable object. + * @return {?{elseIfCount: (number|undefined), haseElse: (boolean|undefined)}} + * The state of this block, ie the else if count and else state. + */ + saveExtraState: function() { + if (!this.elseifCount_ && !this.elseCount_) { + return null; + } + const state = Object.create(null); + if (this.elseifCount_) { + state['elseIfCount'] = this.elseifCount_; + } + if (this.elseCount_) { + state['hasElse'] = true; + } + return state; + }, + /** + * Applies the given state to this block. + * @param {*} state The state to apply to this block, ie the else if count and + * else state. + */ + loadExtraState: function(state) { + this.elseifCount_ = state['elseIfCount'] || 0; + this.elseCount_ = state['hasElse'] ? 1 : 0; + this.updateShape_(); + }, /** * Populate the mutator's dialog with this block's components. - * @param {!Blockly.Workspace} workspace Mutator's workspace. - * @return {!Blockly.Block} Root block in mutator. - * @this {Blockly.Block} + * @param {!Workspace} workspace Mutator's workspace. + * @return {!Block} Root block in mutator. + * @this {Block} */ decompose: function(workspace) { - var containerBlock = workspace.newBlock('controls_if_if'); + const containerBlock = workspace.newBlock('controls_if_if'); containerBlock.initSvg(); - var connection = containerBlock.nextConnection; - for (var i = 1; i <= this.elseifCount_; i++) { - var elseifBlock = workspace.newBlock('controls_if_elseif'); + let connection = containerBlock.nextConnection; + for (let i = 1; i <= this.elseifCount_; i++) { + const elseifBlock = workspace.newBlock('controls_if_elseif'); elseifBlock.initSvg(); connection.connect(elseifBlock.previousConnection); connection = elseifBlock.nextConnection; } if (this.elseCount_) { - var elseBlock = workspace.newBlock('controls_if_else'); + const elseBlock = workspace.newBlock('controls_if_else'); elseBlock.initSvg(); connection.connect(elseBlock.previousConnection); } @@ -354,17 +376,17 @@ Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN = { }, /** * Reconfigure this block based on the mutator dialog's components. - * @param {!Blockly.Block} containerBlock Root block in mutator. - * @this {Blockly.Block} + * @param {!Block} containerBlock Root block in mutator. + * @this {Block} */ compose: function(containerBlock) { - var clauseBlock = containerBlock.nextConnection.targetBlock(); + let clauseBlock = containerBlock.nextConnection.targetBlock(); // Count number of inputs. this.elseifCount_ = 0; this.elseCount_ = 0; - var valueConnections = [null]; - var statementConnections = [null]; - var elseStatementConnection = null; + const valueConnections = [null]; + const statementConnections = [null]; + let elseStatementConnection = null; while (clauseBlock && !clauseBlock.isInsertionMarker()) { switch (clauseBlock.type) { case 'controls_if_elseif': @@ -384,33 +406,35 @@ Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN = { } this.updateShape_(); // Reconnect any child blocks. - this.reconnectChildBlocks_(valueConnections, statementConnections, - elseStatementConnection); + this.reconnectChildBlocks_( + valueConnections, statementConnections, elseStatementConnection); }, /** * Store pointers to any connected child blocks. - * @param {!Blockly.Block} containerBlock Root block in mutator. - * @this {Blockly.Block} + * @param {!Block} containerBlock Root block in mutator. + * @this {Block} */ saveConnections: function(containerBlock) { - var clauseBlock = containerBlock.nextConnection.targetBlock(); - var i = 1; + let clauseBlock = containerBlock.nextConnection.targetBlock(); + let i = 1; while (clauseBlock) { switch (clauseBlock.type) { - case 'controls_if_elseif': - var inputIf = this.getInput('IF' + i); - var inputDo = this.getInput('DO' + i); + case 'controls_if_elseif': { + const inputIf = this.getInput('IF' + i); + const inputDo = this.getInput('DO' + i); clauseBlock.valueConnection_ = inputIf && inputIf.connection.targetConnection; clauseBlock.statementConnection_ = inputDo && inputDo.connection.targetConnection; i++; break; - case 'controls_if_else': - var inputDo = this.getInput('ELSE'); + } + case 'controls_if_else': { + const inputDo = this.getInput('ELSE'); clauseBlock.statementConnection_ = inputDo && inputDo.connection.targetConnection; break; + } default: throw TypeError('Unknown block type: ' + clauseBlock.type); } @@ -420,31 +444,30 @@ Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN = { }, /** * Reconstructs the block with all child blocks attached. - * @this {Blockly.Block} + * @this {Block} */ rebuildShape_: function() { - var valueConnections = [null]; - var statementConnections = [null]; - var elseStatementConnection = null; + const valueConnections = [null]; + const statementConnections = [null]; + let elseStatementConnection = null; if (this.getInput('ELSE')) { - elseStatementConnection = this.getInput('ELSE').connection.targetConnection; + elseStatementConnection = + this.getInput('ELSE').connection.targetConnection; } - var i = 1; - while (this.getInput('IF' + i)) { - var inputIf = this.getInput('IF' + i); - var inputDo = this.getInput('DO' + i); + for (let i = 1; this.getInput('IF' + i); i++) { + const inputIf = this.getInput('IF' + i); + const inputDo = this.getInput('DO' + i); valueConnections.push(inputIf.connection.targetConnection); statementConnections.push(inputDo.connection.targetConnection); - i++; } this.updateShape_(); - this.reconnectChildBlocks_(valueConnections, statementConnections, - elseStatementConnection); + this.reconnectChildBlocks_( + valueConnections, statementConnections, elseStatementConnection); }, /** * Modify this block to have the correct number of inputs. - * @this {Blockly.Block} + * @this {Block} * @private */ updateShape_: function() { @@ -452,103 +475,96 @@ Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN = { if (this.getInput('ELSE')) { this.removeInput('ELSE'); } - var i = 1; - while (this.getInput('IF' + i)) { + for (let i = 1; this.getInput('IF' + i); i++) { this.removeInput('IF' + i); this.removeInput('DO' + i); - i++; } // Rebuild block. - for (i = 1; i <= this.elseifCount_; i++) { - this.appendValueInput('IF' + i) - .setCheck('Boolean') - .appendField(Blockly.Msg['CONTROLS_IF_MSG_ELSEIF']); - this.appendStatementInput('DO' + i) - .appendField(Blockly.Msg['CONTROLS_IF_MSG_THEN']); + for (let i = 1; i <= this.elseifCount_; i++) { + this.appendValueInput('IF' + i).setCheck('Boolean').appendField( + Msg['CONTROLS_IF_MSG_ELSEIF']); + this.appendStatementInput('DO' + i).appendField( + Msg['CONTROLS_IF_MSG_THEN']); } if (this.elseCount_) { - this.appendStatementInput('ELSE') - .appendField(Blockly.Msg['CONTROLS_IF_MSG_ELSE']); + this.appendStatementInput('ELSE').appendField( + Msg['CONTROLS_IF_MSG_ELSE']); } }, /** * Reconnects child blocks. - * @param {!Array} valueConnections List of + * @param {!Array} valueConnections List of * value connections for 'if' input. - * @param {!Array} statementConnections List of + * @param {!Array} statementConnections List of * statement connections for 'do' input. - * @param {?Blockly.RenderedConnection} elseStatementConnection Statement + * @param {?RenderedConnection} elseStatementConnection Statement * connection for else input. - * @this {Blockly.Block} + * @this {Block} */ - reconnectChildBlocks_: function(valueConnections, statementConnections, - elseStatementConnection) { - for (var i = 1; i <= this.elseifCount_; i++) { - Blockly.Mutator.reconnect(valueConnections[i], this, 'IF' + i); - Blockly.Mutator.reconnect(statementConnections[i], this, 'DO' + i); + reconnectChildBlocks_: function( + valueConnections, statementConnections, elseStatementConnection) { + for (let i = 1; i <= this.elseifCount_; i++) { + Mutator.reconnect(valueConnections[i], this, 'IF' + i); + Mutator.reconnect(statementConnections[i], this, 'DO' + i); } - Blockly.Mutator.reconnect(elseStatementConnection, this, 'ELSE'); - } + Mutator.reconnect(elseStatementConnection, this, 'ELSE'); + }, }; -Blockly.Extensions.registerMutator('controls_if_mutator', - Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN, null, +Extensions.registerMutator( + 'controls_if_mutator', CONTROLS_IF_MUTATOR_MIXIN, null, ['controls_if_elseif', 'controls_if_else']); /** * "controls_if" extension function. Adds mutator, shape updating methods, and * dynamic tooltip to "controls_if" blocks. - * @this {Blockly.Block} - * @package + * @this {Block} */ -Blockly.Constants.Logic.CONTROLS_IF_TOOLTIP_EXTENSION = function() { - +const CONTROLS_IF_TOOLTIP_EXTENSION = function() { this.setTooltip(function() { if (!this.elseifCount_ && !this.elseCount_) { - return Blockly.Msg['CONTROLS_IF_TOOLTIP_1']; + return Msg['CONTROLS_IF_TOOLTIP_1']; } else if (!this.elseifCount_ && this.elseCount_) { - return Blockly.Msg['CONTROLS_IF_TOOLTIP_2']; + return Msg['CONTROLS_IF_TOOLTIP_2']; } else if (this.elseifCount_ && !this.elseCount_) { - return Blockly.Msg['CONTROLS_IF_TOOLTIP_3']; + return Msg['CONTROLS_IF_TOOLTIP_3']; } else if (this.elseifCount_ && this.elseCount_) { - return Blockly.Msg['CONTROLS_IF_TOOLTIP_4']; + return Msg['CONTROLS_IF_TOOLTIP_4']; } return ''; }.bind(this)); }; -Blockly.Extensions.register('controls_if_tooltip', - Blockly.Constants.Logic.CONTROLS_IF_TOOLTIP_EXTENSION); +Extensions.register('controls_if_tooltip', CONTROLS_IF_TOOLTIP_EXTENSION); /** * Adds dynamic type validation for the left and right sides of a logic_compare * block. * @mixin - * @augments Blockly.Block - * @package + * @augments Block * @readonly */ -Blockly.Constants.Logic.LOGIC_COMPARE_ONCHANGE_MIXIN = { +const LOGIC_COMPARE_ONCHANGE_MIXIN = { /** * Called whenever anything on the workspace changes. * Prevent mismatched types from being compared. - * @param {!Blockly.Events.Abstract} e Change event. - * @this {Blockly.Block} + * @param {!AbstractEvent} e Change event. + * @this {Block} */ onchange: function(e) { if (!this.prevBlocks_) { this.prevBlocks_ = [null, null]; } - var blockA = this.getInputTargetBlock('A'); - var blockB = this.getInputTargetBlock('B'); + const blockA = this.getInputTargetBlock('A'); + const blockB = this.getInputTargetBlock('B'); // Disconnect blocks that existed prior to this change if they don't match. if (blockA && blockB && - !this.workspace.connectionChecker.doTypeChecks( - blockA.outputConnection, blockB.outputConnection)) { + !this.workspace.connectionChecker.doTypeChecks( + blockA.outputConnection, blockB.outputConnection)) { // Mismatch between two inputs. Revert the block connections, // bumping away the newly connected block(s). - Blockly.Events.setGroup(e.group); - var prevA = this.prevBlocks_[0]; + Events.setGroup(e.group); + const prevA = this.prevBlocks_[0]; if (prevA !== blockA) { blockA.unplug(); if (prevA && !prevA.isDisposed() && !prevA.isShadow()) { @@ -556,7 +572,7 @@ Blockly.Constants.Logic.LOGIC_COMPARE_ONCHANGE_MIXIN = { this.getInput('A').connection.connect(prevA.outputConnection); } } - var prevB = this.prevBlocks_[1]; + const prevB = this.prevBlocks_[1]; if (prevB !== blockB) { blockB.unplug(); if (prevB && !prevB.isDisposed() && !prevB.isShadow()) { @@ -565,57 +581,54 @@ Blockly.Constants.Logic.LOGIC_COMPARE_ONCHANGE_MIXIN = { } } this.bumpNeighbours(); - Blockly.Events.setGroup(false); + Events.setGroup(false); } this.prevBlocks_[0] = this.getInputTargetBlock('A'); this.prevBlocks_[1] = this.getInputTargetBlock('B'); - } + }, }; /** * "logic_compare" extension function. Adds type left and right side type * checking to "logic_compare" blocks. - * @this {Blockly.Block} - * @package + * @this {Block} * @readonly */ -Blockly.Constants.Logic.LOGIC_COMPARE_EXTENSION = function() { +const LOGIC_COMPARE_EXTENSION = function() { // Add onchange handler to ensure types are compatible. - this.mixin(Blockly.Constants.Logic.LOGIC_COMPARE_ONCHANGE_MIXIN); + this.mixin(LOGIC_COMPARE_ONCHANGE_MIXIN); }; -Blockly.Extensions.register('logic_compare', - Blockly.Constants.Logic.LOGIC_COMPARE_EXTENSION); +Extensions.register('logic_compare', LOGIC_COMPARE_EXTENSION); /** * Adds type coordination between inputs and output. * @mixin - * @augments Blockly.Block - * @package + * @augments Block * @readonly */ -Blockly.Constants.Logic.LOGIC_TERNARY_ONCHANGE_MIXIN = { +const LOGIC_TERNARY_ONCHANGE_MIXIN = { prevParentConnection_: null, /** * Called whenever anything on the workspace changes. * Prevent mismatched types. - * @param {!Blockly.Events.Abstract} e Change event. - * @this {Blockly.Block} + * @param {!AbstractEvent} e Change event. + * @this {Block} */ onchange: function(e) { - var blockA = this.getInputTargetBlock('THEN'); - var blockB = this.getInputTargetBlock('ELSE'); - var parentConnection = this.outputConnection.targetConnection; + const blockA = this.getInputTargetBlock('THEN'); + const blockB = this.getInputTargetBlock('ELSE'); + const parentConnection = this.outputConnection.targetConnection; // Disconnect blocks that existed prior to this change if they don't match. if ((blockA || blockB) && parentConnection) { - for (var i = 0; i < 2; i++) { - var block = (i == 1) ? blockA : blockB; + for (let i = 0; i < 2; i++) { + const block = (i === 1) ? blockA : blockB; if (block && !block.workspace.connectionChecker.doTypeChecks( block.outputConnection, parentConnection)) { // Ensure that any disconnections are grouped with the causing event. - Blockly.Events.setGroup(e.group); + Events.setGroup(e.group); if (parentConnection === this.prevParentConnection_) { this.unplug(); parentConnection.getSourceBlock().bumpNeighbours(); @@ -623,13 +636,12 @@ Blockly.Constants.Logic.LOGIC_TERNARY_ONCHANGE_MIXIN = { block.unplug(); block.bumpNeighbours(); } - Blockly.Events.setGroup(false); + Events.setGroup(false); } } } this.prevParentConnection_ = parentConnection; - } + }, }; -Blockly.Extensions.registerMixin('logic_ternary', - Blockly.Constants.Logic.LOGIC_TERNARY_ONCHANGE_MIXIN); +Extensions.registerMixin('logic_ternary', LOGIC_TERNARY_ONCHANGE_MIXIN); diff --git a/blocks/loops.js b/blocks/loops.js index 772871dbaad..cc3c27d371f 100644 --- a/blocks/loops.js +++ b/blocks/loops.js @@ -6,317 +6,322 @@ /** * @fileoverview Loop blocks for Blockly. - * - * This file is scraped to extract a .json file of block definitions. The array - * passed to defineBlocksWithJsonArray(..) must be strict JSON: double quotes - * only, no outside references, no functions, no trailing commas, etc. The one - * exception is end-of-line comments, which the scraper will remove. - * @author fraser@google.com (Neil Fraser) + * @suppress {checkTypes} */ 'use strict'; -goog.provide('Blockly.Blocks.loops'); // Deprecated -goog.provide('Blockly.Constants.Loops'); +goog.module('Blockly.blocks.loops'); -goog.require('Blockly'); -goog.require('Blockly.Blocks'); +/* eslint-disable-next-line no-unused-vars */ +const AbstractEvent = goog.requireType('Blockly.Events.Abstract'); +const ContextMenu = goog.require('Blockly.ContextMenu'); +const Events = goog.require('Blockly.Events'); +const Extensions = goog.require('Blockly.Extensions'); +const Variables = goog.require('Blockly.Variables'); +const common = goog.require('Blockly.common'); +const xmlUtils = goog.require('Blockly.utils.xml'); +/* eslint-disable-next-line no-unused-vars */ +const {Block} = goog.requireType('Blockly.Block'); +const {Msg} = goog.require('Blockly.Msg'); +/** @suppress {extraRequire} */ goog.require('Blockly.FieldDropdown'); +/** @suppress {extraRequire} */ goog.require('Blockly.FieldLabel'); +/** @suppress {extraRequire} */ goog.require('Blockly.FieldNumber'); +/** @suppress {extraRequire} */ goog.require('Blockly.FieldVariable'); +/** @suppress {extraRequire} */ goog.require('Blockly.Warning'); -/** - * Unused constant for the common HSV hue for all blocks in this category. - * @deprecated Use Blockly.Msg['LOOPS_HUE']. (2018 April 5) - */ -Blockly.Constants.Loops.HUE = 120; - -Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT +common.defineBlocksWithJsonArray([ // Block for repeat n times (external number). { - "type": "controls_repeat_ext", - "message0": "%{BKY_CONTROLS_REPEAT_TITLE}", - "args0": [{ - "type": "input_value", - "name": "TIMES", - "check": "Number" + 'type': 'controls_repeat_ext', + 'message0': '%{BKY_CONTROLS_REPEAT_TITLE}', + 'args0': [{ + 'type': 'input_value', + 'name': 'TIMES', + 'check': 'Number', }], - "message1": "%{BKY_CONTROLS_REPEAT_INPUT_DO} %1", - "args1": [{ - "type": "input_statement", - "name": "DO" + 'message1': '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1', + 'args1': [{ + 'type': 'input_statement', + 'name': 'DO', }], - "previousStatement": null, - "nextStatement": null, - "style": "loop_blocks", - "tooltip": "%{BKY_CONTROLS_REPEAT_TOOLTIP}", - "helpUrl": "%{BKY_CONTROLS_REPEAT_HELPURL}" + 'previousStatement': null, + 'nextStatement': null, + 'style': 'loop_blocks', + 'tooltip': '%{BKY_CONTROLS_REPEAT_TOOLTIP}', + 'helpUrl': '%{BKY_CONTROLS_REPEAT_HELPURL}', }, // Block for repeat n times (internal number). // The 'controls_repeat_ext' block is preferred as it is more flexible. { - "type": "controls_repeat", - "message0": "%{BKY_CONTROLS_REPEAT_TITLE}", - "args0": [{ - "type": "field_number", - "name": "TIMES", - "value": 10, - "min": 0, - "precision": 1 + 'type': 'controls_repeat', + 'message0': '%{BKY_CONTROLS_REPEAT_TITLE}', + '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" + 'message1': '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1', + 'args1': [{ + 'type': 'input_statement', + 'name': 'DO', }], - "previousStatement": null, - "nextStatement": null, - "style": "loop_blocks", - "tooltip": "%{BKY_CONTROLS_REPEAT_TOOLTIP}", - "helpUrl": "%{BKY_CONTROLS_REPEAT_HELPURL}" + 'previousStatement': null, + 'nextStatement': null, + 'style': 'loop_blocks', + 'tooltip': '%{BKY_CONTROLS_REPEAT_TOOLTIP}', + 'helpUrl': '%{BKY_CONTROLS_REPEAT_HELPURL}', }, // Block for 'do while/until' loop. { - "type": "controls_whileUntil", - "message0": "%1 %2", - "args0": [ + 'type': 'controls_whileUntil', + 'message0': '%1 %2', + 'args0': [ { - "type": "field_dropdown", - "name": "MODE", - "options": [ - ["%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_WHILE}", "WHILE"], - ["%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_UNTIL}", "UNTIL"] - ] + 'type': 'field_dropdown', + 'name': 'MODE', + 'options': [ + ['%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_WHILE}', 'WHILE'], + ['%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_UNTIL}', 'UNTIL'], + ], }, { - "type": "input_value", - "name": "BOOL", - "check": "Boolean" - } + 'type': 'input_value', + 'name': 'BOOL', + 'check': 'Boolean', + }, ], - "message1": "%{BKY_CONTROLS_REPEAT_INPUT_DO} %1", - "args1": [{ - "type": "input_statement", - "name": "DO" + 'message1': '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1', + 'args1': [{ + 'type': 'input_statement', + 'name': 'DO', }], - "previousStatement": null, - "nextStatement": null, - "style": "loop_blocks", - "helpUrl": "%{BKY_CONTROLS_WHILEUNTIL_HELPURL}", - "extensions": ["controls_whileUntil_tooltip"] + 'previousStatement': null, + 'nextStatement': null, + 'style': 'loop_blocks', + 'helpUrl': '%{BKY_CONTROLS_WHILEUNTIL_HELPURL}', + 'extensions': ['controls_whileUntil_tooltip'], }, // Block for 'for' loop. { - "type": "controls_for", - "message0": "%{BKY_CONTROLS_FOR_TITLE}", - "args0": [ + 'type': 'controls_for', + 'message0': '%{BKY_CONTROLS_FOR_TITLE}', + 'args0': [ { - "type": "field_variable", - "name": "VAR", - "variable": null + 'type': 'field_variable', + 'name': 'VAR', + 'variable': null, }, { - "type": "input_value", - "name": "FROM", - "check": "Number", - "align": "RIGHT" + 'type': 'input_value', + 'name': 'FROM', + 'check': 'Number', + 'align': 'RIGHT', }, { - "type": "input_value", - "name": "TO", - "check": "Number", - "align": "RIGHT" + 'type': 'input_value', + 'name': 'TO', + 'check': 'Number', + 'align': 'RIGHT', }, { - "type": "input_value", - "name": "BY", - "check": "Number", - "align": "RIGHT" - } + 'type': 'input_value', + 'name': 'BY', + 'check': 'Number', + 'align': 'RIGHT', + }, ], - "message1": "%{BKY_CONTROLS_REPEAT_INPUT_DO} %1", - "args1": [{ - "type": "input_statement", - "name": "DO" + 'message1': '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1', + '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" - ] + 'inputsInline': true, + 'previousStatement': null, + 'nextStatement': null, + 'style': 'loop_blocks', + 'helpUrl': '%{BKY_CONTROLS_FOR_HELPURL}', + 'extensions': [ + 'contextMenu_newGetVariableBlock', + 'controls_for_tooltip', + ], }, // Block for 'for each' loop. { - "type": "controls_forEach", - "message0": "%{BKY_CONTROLS_FOREACH_TITLE}", - "args0": [ + 'type': 'controls_forEach', + 'message0': '%{BKY_CONTROLS_FOREACH_TITLE}', + 'args0': [ { - "type": "field_variable", - "name": "VAR", - "variable": null + 'type': 'field_variable', + 'name': 'VAR', + 'variable': null, }, { - "type": "input_value", - "name": "LIST", - "check": "Array" - } + 'type': 'input_value', + 'name': 'LIST', + 'check': 'Array', + }, ], - "message1": "%{BKY_CONTROLS_REPEAT_INPUT_DO} %1", - "args1": [{ - "type": "input_statement", - "name": "DO" + 'message1': '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1', + 'args1': [{ + 'type': 'input_statement', + 'name': 'DO', }], - "previousStatement": null, - "nextStatement": null, - "style": "loop_blocks", - "helpUrl": "%{BKY_CONTROLS_FOREACH_HELPURL}", - "extensions": [ - "contextMenu_newGetVariableBlock", - "controls_forEach_tooltip" - ] + 'previousStatement': null, + 'nextStatement': null, + 'style': 'loop_blocks', + 'helpUrl': '%{BKY_CONTROLS_FOREACH_HELPURL}', + 'extensions': [ + 'contextMenu_newGetVariableBlock', + 'controls_forEach_tooltip', + ], }, // Block for flow statements: continue, break. { - "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"] - ] + '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'], + ], }], - "previousStatement": null, - "style": "loop_blocks", - "helpUrl": "%{BKY_CONTROLS_FLOW_STATEMENTS_HELPURL}", - "extensions": [ - "controls_flow_tooltip", - "controls_flow_in_loop_check" - ] - } -]); // END JSON EXTRACT (Do not delete this comment.) + 'previousStatement': null, + 'style': 'loop_blocks', + 'helpUrl': '%{BKY_CONTROLS_FLOW_STATEMENTS_HELPURL}', + 'suppressPrefixSuffix': true, + 'extensions': [ + 'controls_flow_tooltip', + 'controls_flow_in_loop_check', + ], + }, +]); /** * Tooltips for the 'controls_whileUntil' block, keyed by MODE value. - * @see {Blockly.Extensions#buildTooltipForDropdown} - * @package + * @see {Extensions#buildTooltipForDropdown} * @readonly */ -Blockly.Constants.Loops.WHILE_UNTIL_TOOLTIPS = { +const WHILE_UNTIL_TOOLTIPS = { 'WHILE': '%{BKY_CONTROLS_WHILEUNTIL_TOOLTIP_WHILE}', - 'UNTIL': '%{BKY_CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL}' + 'UNTIL': '%{BKY_CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL}', }; -Blockly.Extensions.register('controls_whileUntil_tooltip', - Blockly.Extensions.buildTooltipForDropdown( - 'MODE', Blockly.Constants.Loops.WHILE_UNTIL_TOOLTIPS)); +Extensions.register( + 'controls_whileUntil_tooltip', + Extensions.buildTooltipForDropdown('MODE', WHILE_UNTIL_TOOLTIPS)); /** * Tooltips for the 'controls_flow_statements' block, keyed by FLOW value. - * @see {Blockly.Extensions#buildTooltipForDropdown} - * @package + * @see {Extensions#buildTooltipForDropdown} * @readonly */ -Blockly.Constants.Loops.BREAK_CONTINUE_TOOLTIPS = { +const BREAK_CONTINUE_TOOLTIPS = { 'BREAK': '%{BKY_CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK}', - 'CONTINUE': '%{BKY_CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE}' + 'CONTINUE': '%{BKY_CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE}', }; -Blockly.Extensions.register('controls_flow_tooltip', - Blockly.Extensions.buildTooltipForDropdown( - 'FLOW', Blockly.Constants.Loops.BREAK_CONTINUE_TOOLTIPS)); +Extensions.register( + 'controls_flow_tooltip', + Extensions.buildTooltipForDropdown('FLOW', BREAK_CONTINUE_TOOLTIPS)); /** * Mixin to add a context menu item to create a 'variables_get' block. * Used by blocks 'controls_for' and 'controls_forEach'. * @mixin - * @augments Blockly.Block + * @augments Block * @package * @readonly */ -Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN = { +const CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN = { /** * Add context menu option to create getter block for the loop's variable. * (customContextMenu support limited to web BlockSvg.) * @param {!Array} options List of menu options to add to. - * @this {Blockly.Block} + * @this {Block} */ customContextMenu: function(options) { if (this.isInFlyout) { return; } - var variable = this.getField('VAR').getVariable(); - var varName = variable.name; - if (!this.isCollapsed() && varName != null) { - var option = {enabled: true}; - option.text = - Blockly.Msg['VARIABLES_SET_CREATE_GET'].replace('%1', varName); - var xmlField = Blockly.Variables.generateVariableFieldDom(variable); - var xmlBlock = Blockly.utils.xml.createElement('block'); + const variable = this.getField('VAR').getVariable(); + const varName = variable.name; + if (!this.isCollapsed() && varName !== null) { + const option = {enabled: true}; + option.text = Msg['VARIABLES_SET_CREATE_GET'].replace('%1', varName); + const xmlField = Variables.generateVariableFieldDom(variable); + const xmlBlock = xmlUtils.createElement('block'); xmlBlock.setAttribute('type', 'variables_get'); xmlBlock.appendChild(xmlField); - option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock); + option.callback = ContextMenu.callbackFactory(this, xmlBlock); options.push(option); } - } + }, }; -Blockly.Extensions.registerMixin('contextMenu_newGetVariableBlock', - Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN); +Extensions.registerMixin( + 'contextMenu_newGetVariableBlock', + CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN); -Blockly.Extensions.register('controls_for_tooltip', - Blockly.Extensions.buildTooltipWithFieldText( - '%{BKY_CONTROLS_FOR_TOOLTIP}', 'VAR')); +Extensions.register( + 'controls_for_tooltip', + Extensions.buildTooltipWithFieldText('%{BKY_CONTROLS_FOR_TOOLTIP}', 'VAR')); -Blockly.Extensions.register('controls_forEach_tooltip', - Blockly.Extensions.buildTooltipWithFieldText( +Extensions.register( + 'controls_forEach_tooltip', + Extensions.buildTooltipWithFieldText( '%{BKY_CONTROLS_FOREACH_TOOLTIP}', 'VAR')); +/** + * List of block types that are loops and thus do not need warnings. + * To add a new loop type add this to your code: + * + * // If using the Blockly npm package and es6 import syntax: + * import {loopTypes} from 'blockly/blocks'; + * loopTypes.push('custom_loop'); + * + * // Else if using Closure Compiler and goog.modules: + * const {loopTypes} = goog.require('Blockly.blocks.loops'); + * loopTypes.push('custom_loop'); + * + * @type {!Array} + */ +const loopTypes = [ + 'controls_repeat', + 'controls_repeat_ext', + 'controls_forEach', + 'controls_for', + 'controls_whileUntil', +]; +exports.loopTypes = loopTypes; + /** * This mixin adds a check to make sure the 'controls_flow_statements' block * is contained in a loop. Otherwise a warning is added to the block. * @mixin - * @augments Blockly.Block - * @package + * @augments Block + * @public * @readonly */ -Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN = { +const CONTROL_FLOW_IN_LOOP_CHECK_MIXIN = { /** - * List of block types that are loops and thus do not need warnings. - * To add a new loop type add this to your code: - * Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.LOOP_TYPES.push('custom_loop'); + * Is this block enclosed (at any level) by a loop? + * @return {Block} The nearest surrounding loop, or null if none. + * @this {Block} */ - LOOP_TYPES: [ - 'controls_repeat', - 'controls_repeat_ext', - 'controls_forEach', - 'controls_for', - 'controls_whileUntil' - ], - - /** - * Don't automatically add STATEMENT_PREFIX and STATEMENT_SUFFIX to generated - * code. These will be handled manually in this block's generators. - */ - suppressPrefixSuffix: true, - - /** - * Is the given block enclosed (at any level) by a loop? - * @param {!Blockly.Block} block Current block. - * @return {Blockly.Block} The nearest surrounding loop, or null if none. - */ - getSurroundLoop: function(block) { - // Is the block nested in a loop? + getSurroundLoop: function() { + let block = this; do { - if (Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.LOOP_TYPES - .indexOf(block.type) != -1) { + if (loopTypes.includes(block.type)) { return block; } block = block.getSurroundParent(); @@ -327,30 +332,29 @@ Blockly.Constants.Loops.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. - * @param {!Blockly.Events.Abstract} e Change event. - * @this {Blockly.Block} + * @param {!AbstractEvent} e Change event. + * @this {Block} */ onchange: function(e) { // Don't change state if: // * It's at the start of a drag. // * It's not a move event. if (!this.workspace.isDragging || this.workspace.isDragging() || - e.type != Blockly.Events.BLOCK_MOVE) { + e.type !== Events.BLOCK_MOVE) { return; } - var enabled = Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN - .getSurroundLoop(this); - this.setWarningText(enabled ? null : - Blockly.Msg['CONTROLS_FLOW_STATEMENTS_WARNING']); + const enabled = this.getSurroundLoop(this); + this.setWarningText( + enabled ? null : Msg['CONTROLS_FLOW_STATEMENTS_WARNING']); if (!this.isInFlyout) { - var group = Blockly.Events.getGroup(); + const group = Events.getGroup(); // Makes it so the move and the disable event get undone together. - Blockly.Events.setGroup(e.group); + Events.setGroup(e.group); this.setEnabled(enabled); - Blockly.Events.setGroup(group); + Events.setGroup(group); } - } + }, }; -Blockly.Extensions.registerMixin('controls_flow_in_loop_check', - Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN); +Extensions.registerMixin( + 'controls_flow_in_loop_check', CONTROL_FLOW_IN_LOOP_CHECK_MIXIN); diff --git a/blocks/math.js b/blocks/math.js index b6827c5b121..003ff5bdc94 100644 --- a/blocks/math.js +++ b/blocks/math.js @@ -6,396 +6,393 @@ /** * @fileoverview Math blocks for Blockly. - * - * This file is scraped to extract a .json file of block definitions. The array - * passed to defineBlocksWithJsonArray(..) must be strict JSON: double quotes - * only, no outside references, no functions, no trailing commas, etc. The one - * exception is end-of-line comments, which the scraper will remove. - * @author q.neutron@gmail.com (Quynh Neutron) + * @suppress {checkTypes} */ 'use strict'; -goog.provide('Blockly.Blocks.math'); // Deprecated -goog.provide('Blockly.Constants.Math'); - -goog.require('Blockly'); -goog.require('Blockly.Blocks'); -goog.require('Blockly.FieldDropdown'); +goog.module('Blockly.blocks.math'); + +const Extensions = goog.require('Blockly.Extensions'); +// N.B.: Blockly.FieldDropdown needed for type AND side-effects. +/* eslint-disable-next-line no-unused-vars */ +const FieldDropdown = goog.require('Blockly.FieldDropdown'); +const xmlUtils = goog.require('Blockly.utils.xml'); +/* eslint-disable-next-line no-unused-vars */ +const {Block} = goog.requireType('Blockly.Block'); +/* eslint-disable-next-line no-unused-vars */ +const {defineBlocksWithJsonArray} = goog.require('Blockly.common'); +/** @suppress {extraRequire} */ goog.require('Blockly.FieldLabel'); +/** @suppress {extraRequire} */ goog.require('Blockly.FieldNumber'); +/** @suppress {extraRequire} */ goog.require('Blockly.FieldVariable'); -/** - * Unused constant for the common HSV hue for all blocks in this category. - * @deprecated Use Blockly.Msg['MATH_HUE']. (2018 April 5) - */ -Blockly.Constants.Math.HUE = 230; - -Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT +defineBlocksWithJsonArray([ // Block for numeric value. { - "type": "math_number", - "message0": "%1", - "args0": [{ - "type": "field_number", - "name": "NUM", - "value": 0 + 'type': 'math_number', + 'message0': '%1', + 'args0': [{ + 'type': 'field_number', + 'name': 'NUM', + 'value': 0, }], - "output": "Number", - "helpUrl": "%{BKY_MATH_NUMBER_HELPURL}", - "style": "math_blocks", - "tooltip": "%{BKY_MATH_NUMBER_TOOLTIP}", - "extensions": ["parent_tooltip_when_inline"] + 'output': 'Number', + 'helpUrl': '%{BKY_MATH_NUMBER_HELPURL}', + 'style': 'math_blocks', + 'tooltip': '%{BKY_MATH_NUMBER_TOOLTIP}', + 'extensions': ['parent_tooltip_when_inline'], }, // Block for basic arithmetic operator. { - "type": "math_arithmetic", - "message0": "%1 %2 %3", - "args0": [ + 'type': 'math_arithmetic', + 'message0': '%1 %2 %3', + 'args0': [ { - "type": "input_value", - "name": "A", - "check": "Number" + 'type': 'input_value', + 'name': 'A', + 'check': 'Number', }, { - "type": "field_dropdown", - "name": "OP", - "options": [ - ["%{BKY_MATH_ADDITION_SYMBOL}", "ADD"], - ["%{BKY_MATH_SUBTRACTION_SYMBOL}", "MINUS"], - ["%{BKY_MATH_MULTIPLICATION_SYMBOL}", "MULTIPLY"], - ["%{BKY_MATH_DIVISION_SYMBOL}", "DIVIDE"], - ["%{BKY_MATH_POWER_SYMBOL}", "POWER"] - ] + 'type': 'field_dropdown', + 'name': 'OP', + 'options': [ + ['%{BKY_MATH_ADDITION_SYMBOL}', 'ADD'], + ['%{BKY_MATH_SUBTRACTION_SYMBOL}', 'MINUS'], + ['%{BKY_MATH_MULTIPLICATION_SYMBOL}', 'MULTIPLY'], + ['%{BKY_MATH_DIVISION_SYMBOL}', 'DIVIDE'], + ['%{BKY_MATH_POWER_SYMBOL}', 'POWER'], + ], }, { - "type": "input_value", - "name": "B", - "check": "Number" - } + 'type': 'input_value', + 'name': 'B', + 'check': 'Number', + }, ], - "inputsInline": true, - "output": "Number", - "style": "math_blocks", - "helpUrl": "%{BKY_MATH_ARITHMETIC_HELPURL}", - "extensions": ["math_op_tooltip"] + 'inputsInline': true, + 'output': 'Number', + 'style': 'math_blocks', + 'helpUrl': '%{BKY_MATH_ARITHMETIC_HELPURL}', + 'extensions': ['math_op_tooltip'], }, // Block for advanced math operators with single operand. { - "type": "math_single", - "message0": "%1 %2", - "args0": [ + 'type': 'math_single', + 'message0': '%1 %2', + 'args0': [ { - "type": "field_dropdown", - "name": "OP", - "options": [ - ["%{BKY_MATH_SINGLE_OP_ROOT}", 'ROOT'], - ["%{BKY_MATH_SINGLE_OP_ABSOLUTE}", 'ABS'], + 'type': 'field_dropdown', + 'name': 'OP', + 'options': [ + ['%{BKY_MATH_SINGLE_OP_ROOT}', 'ROOT'], + ['%{BKY_MATH_SINGLE_OP_ABSOLUTE}', 'ABS'], ['-', 'NEG'], ['ln', 'LN'], ['log10', 'LOG10'], ['e^', 'EXP'], - ['10^', 'POW10'] - ] + ['10^', 'POW10'], + ], }, { - "type": "input_value", - "name": "NUM", - "check": "Number" - } + 'type': 'input_value', + 'name': 'NUM', + 'check': 'Number', + }, ], - "output": "Number", - "style": "math_blocks", - "helpUrl": "%{BKY_MATH_SINGLE_HELPURL}", - "extensions": ["math_op_tooltip"] + 'output': 'Number', + 'style': 'math_blocks', + 'helpUrl': '%{BKY_MATH_SINGLE_HELPURL}', + 'extensions': ['math_op_tooltip'], }, // Block for trigonometry operators. { - "type": "math_trig", - "message0": "%1 %2", - "args0": [ + 'type': 'math_trig', + 'message0': '%1 %2', + 'args0': [ { - "type": "field_dropdown", - "name": "OP", - "options": [ - ["%{BKY_MATH_TRIG_SIN}", "SIN"], - ["%{BKY_MATH_TRIG_COS}", "COS"], - ["%{BKY_MATH_TRIG_TAN}", "TAN"], - ["%{BKY_MATH_TRIG_ASIN}", "ASIN"], - ["%{BKY_MATH_TRIG_ACOS}", "ACOS"], - ["%{BKY_MATH_TRIG_ATAN}", "ATAN"] - ] + 'type': 'field_dropdown', + 'name': 'OP', + 'options': [ + ['%{BKY_MATH_TRIG_SIN}', 'SIN'], + ['%{BKY_MATH_TRIG_COS}', 'COS'], + ['%{BKY_MATH_TRIG_TAN}', 'TAN'], + ['%{BKY_MATH_TRIG_ASIN}', 'ASIN'], + ['%{BKY_MATH_TRIG_ACOS}', 'ACOS'], + ['%{BKY_MATH_TRIG_ATAN}', 'ATAN'], + ], }, { - "type": "input_value", - "name": "NUM", - "check": "Number" - } + 'type': 'input_value', + 'name': 'NUM', + 'check': 'Number', + }, ], - "output": "Number", - "style": "math_blocks", - "helpUrl": "%{BKY_MATH_TRIG_HELPURL}", - "extensions": ["math_op_tooltip"] + 'output': 'Number', + 'style': 'math_blocks', + 'helpUrl': '%{BKY_MATH_TRIG_HELPURL}', + 'extensions': ['math_op_tooltip'], }, // Block for constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY. { - "type": "math_constant", - "message0": "%1", - "args0": [ + 'type': 'math_constant', + 'message0': '%1', + 'args0': [ { - "type": "field_dropdown", - "name": "CONSTANT", - "options": [ - ["\u03c0", "PI"], - ["e", "E"], - ["\u03c6", "GOLDEN_RATIO"], - ["sqrt(2)", "SQRT2"], - ["sqrt(\u00bd)", "SQRT1_2"], - ["\u221e", "INFINITY"] - ] - } + 'type': 'field_dropdown', + 'name': 'CONSTANT', + 'options': [ + ['\u03c0', 'PI'], + ['e', 'E'], + ['\u03c6', 'GOLDEN_RATIO'], + ['sqrt(2)', 'SQRT2'], + ['sqrt(\u00bd)', 'SQRT1_2'], + ['\u221e', 'INFINITY'], + ], + }, ], - "output": "Number", - "style": "math_blocks", - "tooltip": "%{BKY_MATH_CONSTANT_TOOLTIP}", - "helpUrl": "%{BKY_MATH_CONSTANT_HELPURL}" + 'output': 'Number', + 'style': 'math_blocks', + 'tooltip': '%{BKY_MATH_CONSTANT_TOOLTIP}', + 'helpUrl': '%{BKY_MATH_CONSTANT_HELPURL}', }, // Block for checking if a number is even, odd, prime, whole, positive, // negative or if it is divisible by certain number. { - "type": "math_number_property", - "message0": "%1 %2", - "args0": [ + 'type': 'math_number_property', + 'message0': '%1 %2', + 'args0': [ { - "type": "input_value", - "name": "NUMBER_TO_CHECK", - "check": "Number" + 'type': 'input_value', + 'name': 'NUMBER_TO_CHECK', + 'check': 'Number', }, { - "type": "field_dropdown", - "name": "PROPERTY", - "options": [ - ["%{BKY_MATH_IS_EVEN}", "EVEN"], - ["%{BKY_MATH_IS_ODD}", "ODD"], - ["%{BKY_MATH_IS_PRIME}", "PRIME"], - ["%{BKY_MATH_IS_WHOLE}", "WHOLE"], - ["%{BKY_MATH_IS_POSITIVE}", "POSITIVE"], - ["%{BKY_MATH_IS_NEGATIVE}", "NEGATIVE"], - ["%{BKY_MATH_IS_DIVISIBLE_BY}", "DIVISIBLE_BY"] - ] - } + 'type': 'field_dropdown', + 'name': 'PROPERTY', + 'options': [ + ['%{BKY_MATH_IS_EVEN}', 'EVEN'], + ['%{BKY_MATH_IS_ODD}', 'ODD'], + ['%{BKY_MATH_IS_PRIME}', 'PRIME'], + ['%{BKY_MATH_IS_WHOLE}', 'WHOLE'], + ['%{BKY_MATH_IS_POSITIVE}', 'POSITIVE'], + ['%{BKY_MATH_IS_NEGATIVE}', 'NEGATIVE'], + ['%{BKY_MATH_IS_DIVISIBLE_BY}', 'DIVISIBLE_BY'], + ], + }, ], - "inputsInline": true, - "output": "Boolean", - "style": "math_blocks", - "tooltip": "%{BKY_MATH_IS_TOOLTIP}", - "mutator": "math_is_divisibleby_mutator" + 'inputsInline': true, + 'output': 'Boolean', + 'style': 'math_blocks', + 'tooltip': '%{BKY_MATH_IS_TOOLTIP}', + 'mutator': 'math_is_divisibleby_mutator', }, // Block for adding to a variable in place. { - "type": "math_change", - "message0": "%{BKY_MATH_CHANGE_TITLE}", - "args0": [ + 'type': 'math_change', + 'message0': '%{BKY_MATH_CHANGE_TITLE}', + 'args0': [ { - "type": "field_variable", - "name": "VAR", - "variable": "%{BKY_MATH_CHANGE_TITLE_ITEM}" + 'type': 'field_variable', + 'name': 'VAR', + 'variable': '%{BKY_MATH_CHANGE_TITLE_ITEM}', }, { - "type": "input_value", - "name": "DELTA", - "check": "Number" - } + 'type': 'input_value', + 'name': 'DELTA', + 'check': 'Number', + }, ], - "previousStatement": null, - "nextStatement": null, - "style": "variable_blocks", - "helpUrl": "%{BKY_MATH_CHANGE_HELPURL}", - "extensions": ["math_change_tooltip"] + 'previousStatement': null, + 'nextStatement': null, + 'style': 'variable_blocks', + 'helpUrl': '%{BKY_MATH_CHANGE_HELPURL}', + 'extensions': ['math_change_tooltip'], }, // Block for rounding functions. { - "type": "math_round", - "message0": "%1 %2", - "args0": [ + 'type': 'math_round', + 'message0': '%1 %2', + 'args0': [ { - "type": "field_dropdown", - "name": "OP", - "options": [ - ["%{BKY_MATH_ROUND_OPERATOR_ROUND}", "ROUND"], - ["%{BKY_MATH_ROUND_OPERATOR_ROUNDUP}", "ROUNDUP"], - ["%{BKY_MATH_ROUND_OPERATOR_ROUNDDOWN}", "ROUNDDOWN"] - ] + 'type': 'field_dropdown', + 'name': 'OP', + 'options': [ + ['%{BKY_MATH_ROUND_OPERATOR_ROUND}', 'ROUND'], + ['%{BKY_MATH_ROUND_OPERATOR_ROUNDUP}', 'ROUNDUP'], + ['%{BKY_MATH_ROUND_OPERATOR_ROUNDDOWN}', 'ROUNDDOWN'], + ], }, { - "type": "input_value", - "name": "NUM", - "check": "Number" - } + 'type': 'input_value', + 'name': 'NUM', + 'check': 'Number', + }, ], - "output": "Number", - "style": "math_blocks", - "helpUrl": "%{BKY_MATH_ROUND_HELPURL}", - "tooltip": "%{BKY_MATH_ROUND_TOOLTIP}" + 'output': 'Number', + 'style': 'math_blocks', + 'helpUrl': '%{BKY_MATH_ROUND_HELPURL}', + 'tooltip': '%{BKY_MATH_ROUND_TOOLTIP}', }, // Block for evaluating a list of numbers to return sum, average, min, max, // etc. Some functions also work on text (min, max, mode, median). { - "type": "math_on_list", - "message0": "%1 %2", - "args0": [ + 'type': 'math_on_list', + 'message0': '%1 %2', + 'args0': [ { - "type": "field_dropdown", - "name": "OP", - "options": [ - ["%{BKY_MATH_ONLIST_OPERATOR_SUM}", "SUM"], - ["%{BKY_MATH_ONLIST_OPERATOR_MIN}", "MIN"], - ["%{BKY_MATH_ONLIST_OPERATOR_MAX}", "MAX"], - ["%{BKY_MATH_ONLIST_OPERATOR_AVERAGE}", "AVERAGE"], - ["%{BKY_MATH_ONLIST_OPERATOR_MEDIAN}", "MEDIAN"], - ["%{BKY_MATH_ONLIST_OPERATOR_MODE}", "MODE"], - ["%{BKY_MATH_ONLIST_OPERATOR_STD_DEV}", "STD_DEV"], - ["%{BKY_MATH_ONLIST_OPERATOR_RANDOM}", "RANDOM"] - ] + 'type': 'field_dropdown', + 'name': 'OP', + 'options': [ + ['%{BKY_MATH_ONLIST_OPERATOR_SUM}', 'SUM'], + ['%{BKY_MATH_ONLIST_OPERATOR_MIN}', 'MIN'], + ['%{BKY_MATH_ONLIST_OPERATOR_MAX}', 'MAX'], + ['%{BKY_MATH_ONLIST_OPERATOR_AVERAGE}', 'AVERAGE'], + ['%{BKY_MATH_ONLIST_OPERATOR_MEDIAN}', 'MEDIAN'], + ['%{BKY_MATH_ONLIST_OPERATOR_MODE}', 'MODE'], + ['%{BKY_MATH_ONLIST_OPERATOR_STD_DEV}', 'STD_DEV'], + ['%{BKY_MATH_ONLIST_OPERATOR_RANDOM}', 'RANDOM'], + ], }, { - "type": "input_value", - "name": "LIST", - "check": "Array" - } + 'type': 'input_value', + 'name': 'LIST', + 'check': 'Array', + }, ], - "output": "Number", - "style": "math_blocks", - "helpUrl": "%{BKY_MATH_ONLIST_HELPURL}", - "mutator": "math_modes_of_list_mutator", - "extensions": ["math_op_tooltip"] + 'output': 'Number', + 'style': 'math_blocks', + 'helpUrl': '%{BKY_MATH_ONLIST_HELPURL}', + 'mutator': 'math_modes_of_list_mutator', + 'extensions': ['math_op_tooltip'], }, // Block for remainder of a division. { - "type": "math_modulo", - "message0": "%{BKY_MATH_MODULO_TITLE}", - "args0": [ + 'type': 'math_modulo', + 'message0': '%{BKY_MATH_MODULO_TITLE}', + 'args0': [ { - "type": "input_value", - "name": "DIVIDEND", - "check": "Number" + 'type': 'input_value', + 'name': 'DIVIDEND', + 'check': 'Number', }, { - "type": "input_value", - "name": "DIVISOR", - "check": "Number" - } + 'type': 'input_value', + 'name': 'DIVISOR', + 'check': 'Number', + }, ], - "inputsInline": true, - "output": "Number", - "style": "math_blocks", - "tooltip": "%{BKY_MATH_MODULO_TOOLTIP}", - "helpUrl": "%{BKY_MATH_MODULO_HELPURL}" + 'inputsInline': true, + 'output': 'Number', + 'style': 'math_blocks', + 'tooltip': '%{BKY_MATH_MODULO_TOOLTIP}', + 'helpUrl': '%{BKY_MATH_MODULO_HELPURL}', }, // Block for constraining a number between two limits. { - "type": "math_constrain", - "message0": "%{BKY_MATH_CONSTRAIN_TITLE}", - "args0": [ + 'type': 'math_constrain', + 'message0': '%{BKY_MATH_CONSTRAIN_TITLE}', + 'args0': [ { - "type": "input_value", - "name": "VALUE", - "check": "Number" + 'type': 'input_value', + 'name': 'VALUE', + 'check': 'Number', }, { - "type": "input_value", - "name": "LOW", - "check": "Number" + 'type': 'input_value', + 'name': 'LOW', + 'check': 'Number', }, { - "type": "input_value", - "name": "HIGH", - "check": "Number" - } + 'type': 'input_value', + 'name': 'HIGH', + 'check': 'Number', + }, ], - "inputsInline": true, - "output": "Number", - "style": "math_blocks", - "tooltip": "%{BKY_MATH_CONSTRAIN_TOOLTIP}", - "helpUrl": "%{BKY_MATH_CONSTRAIN_HELPURL}" + 'inputsInline': true, + 'output': 'Number', + 'style': 'math_blocks', + 'tooltip': '%{BKY_MATH_CONSTRAIN_TOOLTIP}', + 'helpUrl': '%{BKY_MATH_CONSTRAIN_HELPURL}', }, // Block for random integer between [X] and [Y]. { - "type": "math_random_int", - "message0": "%{BKY_MATH_RANDOM_INT_TITLE}", - "args0": [ + 'type': 'math_random_int', + 'message0': '%{BKY_MATH_RANDOM_INT_TITLE}', + 'args0': [ { - "type": "input_value", - "name": "FROM", - "check": "Number" + 'type': 'input_value', + 'name': 'FROM', + 'check': 'Number', }, { - "type": "input_value", - "name": "TO", - "check": "Number" - } + 'type': 'input_value', + 'name': 'TO', + 'check': 'Number', + }, ], - "inputsInline": true, - "output": "Number", - "style": "math_blocks", - "tooltip": "%{BKY_MATH_RANDOM_INT_TOOLTIP}", - "helpUrl": "%{BKY_MATH_RANDOM_INT_HELPURL}" + 'inputsInline': true, + 'output': 'Number', + 'style': 'math_blocks', + 'tooltip': '%{BKY_MATH_RANDOM_INT_TOOLTIP}', + 'helpUrl': '%{BKY_MATH_RANDOM_INT_HELPURL}', }, // Block for random integer between [X] and [Y]. { - "type": "math_random_float", - "message0": "%{BKY_MATH_RANDOM_FLOAT_TITLE_RANDOM}", - "output": "Number", - "style": "math_blocks", - "tooltip": "%{BKY_MATH_RANDOM_FLOAT_TOOLTIP}", - "helpUrl": "%{BKY_MATH_RANDOM_FLOAT_HELPURL}" + 'type': 'math_random_float', + 'message0': '%{BKY_MATH_RANDOM_FLOAT_TITLE_RANDOM}', + 'output': 'Number', + 'style': 'math_blocks', + 'tooltip': '%{BKY_MATH_RANDOM_FLOAT_TOOLTIP}', + 'helpUrl': '%{BKY_MATH_RANDOM_FLOAT_HELPURL}', }, // Block for calculating atan2 of [X] and [Y]. { - "type": "math_atan2", - "message0": "%{BKY_MATH_ATAN2_TITLE}", - "args0": [ + 'type': 'math_atan2', + 'message0': '%{BKY_MATH_ATAN2_TITLE}', + 'args0': [ { - "type": "input_value", - "name": "X", - "check": "Number" + 'type': 'input_value', + 'name': 'X', + 'check': 'Number', }, { - "type": "input_value", - "name": "Y", - "check": "Number" - } + 'type': 'input_value', + 'name': 'Y', + 'check': 'Number', + }, ], - "inputsInline": true, - "output": "Number", - "style": "math_blocks", - "tooltip": "%{BKY_MATH_ATAN2_TOOLTIP}", - "helpUrl": "%{BKY_MATH_ATAN2_HELPURL}" - } -]); // END JSON EXTRACT (Do not delete this comment.) + 'inputsInline': true, + 'output': 'Number', + 'style': 'math_blocks', + 'tooltip': '%{BKY_MATH_ATAN2_TOOLTIP}', + 'helpUrl': '%{BKY_MATH_ATAN2_HELPURL}', + }, +]); /** * Mapping of math block OP value to tooltip message for blocks * math_arithmetic, math_simple, math_trig, and math_on_lists. - * @see {Blockly.Extensions#buildTooltipForDropdown} + * @see {Extensions#buildTooltipForDropdown} * @package * @readonly */ -Blockly.Constants.Math.TOOLTIPS_BY_OP = { +const TOOLTIPS_BY_OP = { // math_arithmetic 'ADD': '%{BKY_MATH_ARITHMETIC_TOOLTIP_ADD}', 'MINUS': '%{BKY_MATH_ARITHMETIC_TOOLTIP_MINUS}', @@ -428,102 +425,115 @@ Blockly.Constants.Math.TOOLTIPS_BY_OP = { 'MEDIAN': '%{BKY_MATH_ONLIST_TOOLTIP_MEDIAN}', 'MODE': '%{BKY_MATH_ONLIST_TOOLTIP_MODE}', 'STD_DEV': '%{BKY_MATH_ONLIST_TOOLTIP_STD_DEV}', - 'RANDOM': '%{BKY_MATH_ONLIST_TOOLTIP_RANDOM}' + 'RANDOM': '%{BKY_MATH_ONLIST_TOOLTIP_RANDOM}', }; -Blockly.Extensions.register('math_op_tooltip', - Blockly.Extensions.buildTooltipForDropdown( - 'OP', Blockly.Constants.Math.TOOLTIPS_BY_OP)); +Extensions.register( + 'math_op_tooltip', + Extensions.buildTooltipForDropdown('OP', TOOLTIPS_BY_OP)); /** * Mixin for mutator functions in the 'math_is_divisibleby_mutator' * extension. * @mixin - * @augments Blockly.Block + * @augments Block * @package */ -Blockly.Constants.Math.IS_DIVISIBLEBY_MUTATOR_MIXIN = { +const IS_DIVISIBLEBY_MUTATOR_MIXIN = { /** * Create XML to represent whether the 'divisorInput' should be present. + * Backwards compatible serialization implementation. * @return {!Element} XML storage element. - * @this {Blockly.Block} + * @this {Block} */ mutationToDom: function() { - var container = Blockly.utils.xml.createElement('mutation'); - var divisorInput = (this.getFieldValue('PROPERTY') == 'DIVISIBLE_BY'); + const container = xmlUtils.createElement('mutation'); + const divisorInput = (this.getFieldValue('PROPERTY') === 'DIVISIBLE_BY'); container.setAttribute('divisor_input', divisorInput); return container; }, /** * Parse XML to restore the 'divisorInput'. + * Backwards compatible serialization implementation. * @param {!Element} xmlElement XML storage element. - * @this {Blockly.Block} + * @this {Block} */ domToMutation: function(xmlElement) { - var divisorInput = (xmlElement.getAttribute('divisor_input') == 'true'); + const divisorInput = (xmlElement.getAttribute('divisor_input') === 'true'); this.updateShape_(divisorInput); }, + + // This block does not need JSO serialization hooks (saveExtraState and + // loadExtraState) because the state of this object is already encoded in the + // dropdown values. + // XML hooks are kept for backwards compatibility. + /** * Modify this block to have (or not have) an input for 'is divisible by'. * @param {boolean} divisorInput True if this block has a divisor input. * @private - * @this {Blockly.Block} + * @this {Block} */ updateShape_: function(divisorInput) { // Add or remove a Value Input. - var inputExists = this.getInput('DIVISOR'); + const inputExists = this.getInput('DIVISOR'); if (divisorInput) { if (!inputExists) { - this.appendValueInput('DIVISOR') - .setCheck('Number'); + this.appendValueInput('DIVISOR').setCheck('Number'); } } else if (inputExists) { this.removeInput('DIVISOR'); } - } + }, }; /** * 'math_is_divisibleby_mutator' extension to the 'math_property' block that * can update the block shape (add/remove divisor input) based on whether * property is "divisible by". - * @this {Blockly.Block} + * @this {Block} * @package */ -Blockly.Constants.Math.IS_DIVISIBLE_MUTATOR_EXTENSION = function() { - this.getField('PROPERTY').setValidator(function(option) { - var divisorInput = (option == 'DIVISIBLE_BY'); - this.getSourceBlock().updateShape_(divisorInput); - }); +const IS_DIVISIBLE_MUTATOR_EXTENSION = function() { + this.getField('PROPERTY') + .setValidator( + /** + * @this {FieldDropdown} + * @param {*} option The selected dropdown option. + */ + function(option) { + const divisorInput = (option === 'DIVISIBLE_BY'); + this.getSourceBlock().updateShape_(divisorInput); + }); }; -Blockly.Extensions.registerMutator('math_is_divisibleby_mutator', - Blockly.Constants.Math.IS_DIVISIBLEBY_MUTATOR_MIXIN, - Blockly.Constants.Math.IS_DIVISIBLE_MUTATOR_EXTENSION); +Extensions.registerMutator( + 'math_is_divisibleby_mutator', IS_DIVISIBLEBY_MUTATOR_MIXIN, + IS_DIVISIBLE_MUTATOR_EXTENSION); // Update the tooltip of 'math_change' block to reference the variable. -Blockly.Extensions.register('math_change_tooltip', - Blockly.Extensions.buildTooltipWithFieldText( - '%{BKY_MATH_CHANGE_TOOLTIP}', 'VAR')); +Extensions.register( + 'math_change_tooltip', + Extensions.buildTooltipWithFieldText('%{BKY_MATH_CHANGE_TOOLTIP}', 'VAR')); /** * Mixin with mutator methods to support alternate output based if the * 'math_on_list' block uses the 'MODE' operation. * @mixin - * @augments Blockly.Block + * @augments Block * @package * @readonly */ -Blockly.Constants.Math.LIST_MODES_MUTATOR_MIXIN = { +const LIST_MODES_MUTATOR_MIXIN = { /** * Modify this block to have the correct output type. * @param {string} newOp Either 'MODE' or some op than returns a number. * @private - * @this {Blockly.Block} + * @this {Block} */ updateType_: function(newOp) { - if (newOp == 'MODE') { + if (newOp === 'MODE') { this.outputConnection.setCheck('Array'); } else { this.outputConnection.setCheck('Number'); @@ -531,36 +541,43 @@ Blockly.Constants.Math.LIST_MODES_MUTATOR_MIXIN = { }, /** * Create XML to represent the output type. + * Backwards compatible serialization implementation. * @return {!Element} XML storage element. - * @this {Blockly.Block} + * @this {Block} */ mutationToDom: function() { - var container = Blockly.utils.xml.createElement('mutation'); + const container = xmlUtils.createElement('mutation'); container.setAttribute('op', this.getFieldValue('OP')); return container; }, /** * Parse XML to restore the output type. + * Backwards compatible serialization implementation. * @param {!Element} xmlElement XML storage element. - * @this {Blockly.Block} + * @this {Block} */ domToMutation: function(xmlElement) { this.updateType_(xmlElement.getAttribute('op')); - } + }, + + // This block does not need JSO serialization hooks (saveExtraState and + // loadExtraState) because the state of this object is already encoded in the + // dropdown values. + // XML hooks are kept for backwards compatibility. }; /** * Extension to 'math_on_list' blocks that allows support of * modes operation (outputs a list of numbers). - * @this {Blockly.Block} + * @this {Block} * @package */ -Blockly.Constants.Math.LIST_MODES_MUTATOR_EXTENSION = function() { +const LIST_MODES_MUTATOR_EXTENSION = function() { this.getField('OP').setValidator(function(newOp) { this.updateType_(newOp); }.bind(this)); }; -Blockly.Extensions.registerMutator('math_modes_of_list_mutator', - Blockly.Constants.Math.LIST_MODES_MUTATOR_MIXIN, - Blockly.Constants.Math.LIST_MODES_MUTATOR_EXTENSION); +Extensions.registerMutator( + 'math_modes_of_list_mutator', LIST_MODES_MUTATOR_MIXIN, + LIST_MODES_MUTATOR_EXTENSION); diff --git a/blocks/procedures.js b/blocks/procedures.js index 93a5b2d49a2..b9fdaa69728 100644 --- a/blocks/procedures.js +++ b/blocks/procedures.js @@ -6,63 +6,59 @@ /** * @fileoverview Procedure blocks for Blockly. - * @author fraser@google.com (Neil Fraser) + * @suppress {checkTypes|visibility} */ 'use strict'; -goog.provide('Blockly.Blocks.procedures'); +goog.module('Blockly.blocks.procedures'); -goog.require('Blockly'); -goog.require('Blockly.Blocks'); +/* eslint-disable-next-line no-unused-vars */ +const AbstractEvent = goog.requireType('Blockly.Events.Abstract'); +const ContextMenu = goog.require('Blockly.ContextMenu'); +const Events = goog.require('Blockly.Events'); +const Procedures = goog.require('Blockly.Procedures'); +const Variables = goog.require('Blockly.Variables'); +const Xml = goog.require('Blockly.Xml'); +const internalConstants = goog.require('Blockly.internalConstants'); +const xmlUtils = goog.require('Blockly.utils.xml'); +const {Align} = goog.require('Blockly.Input'); +/* eslint-disable-next-line no-unused-vars */ +const {Block} = goog.requireType('Blockly.Block'); +const {Blocks} = goog.require('Blockly.blocks'); +/* eslint-disable-next-line no-unused-vars */ +const {FieldCheckbox} = goog.require('Blockly.FieldCheckbox'); +const {FieldLabel} = goog.require('Blockly.FieldLabel'); +const {FieldTextInput} = goog.require('Blockly.FieldTextInput'); +const {Msg} = goog.require('Blockly.Msg'); +const {Mutator} = goog.require('Blockly.Mutator'); +const {Names} = goog.require('Blockly.Names'); +/* eslint-disable-next-line no-unused-vars */ +const {VariableModel} = goog.requireType('Blockly.VariableModel'); +/* eslint-disable-next-line no-unused-vars */ +const {Workspace} = goog.requireType('Blockly.Workspace'); +/** @suppress {extraRequire} */ goog.require('Blockly.Comment'); -goog.require('Blockly.FieldCheckbox'); -goog.require('Blockly.FieldLabel'); -goog.require('Blockly.FieldTextInput'); -goog.require('Blockly.Mutator'); +/** @suppress {extraRequire} */ goog.require('Blockly.Warning'); -Blockly.Blocks['procedures_defnoreturn'] = { - /** - * Block for defining a procedure with no return value. - * @this {Blockly.Block} - */ - init: function() { - var initName = Blockly.Procedures.findLegalName('', this); - var nameField = new Blockly.FieldTextInput(initName, - Blockly.Procedures.rename); - nameField.setSpellcheck(false); - this.appendDummyInput() - .appendField(Blockly.Msg['PROCEDURES_DEFNORETURN_TITLE']) - .appendField(nameField, 'NAME') - .appendField('', 'PARAMS'); - this.setMutator(new Blockly.Mutator(['procedures_mutatorarg'])); - if ((this.workspace.options.comments || - (this.workspace.options.parentWorkspace && - this.workspace.options.parentWorkspace.options.comments)) && - Blockly.Msg['PROCEDURES_DEFNORETURN_COMMENT']) { - this.setCommentText(Blockly.Msg['PROCEDURES_DEFNORETURN_COMMENT']); - } - this.setStyle('procedure_blocks'); - this.setTooltip(Blockly.Msg['PROCEDURES_DEFNORETURN_TOOLTIP']); - this.setHelpUrl(Blockly.Msg['PROCEDURES_DEFNORETURN_HELPURL']); - this.arguments_ = []; - this.argumentVarModels_ = []; - this.setStatements_(true); - this.statementConnection_ = null; - }, +/** + * Common properties for the procedure_defnoreturn and + * procedure_defreturn blocks. + */ +const PROCEDURE_DEF_COMMON = { /** * Add or remove the statement block from this function definition. * @param {boolean} hasStatements True if a statement block is needed. - * @this {Blockly.Block} + * @this {Block} */ setStatements_: function(hasStatements) { if (this.hasStatements_ === hasStatements) { return; } if (hasStatements) { - this.appendStatementInput('STACK') - .appendField(Blockly.Msg['PROCEDURES_DEFNORETURN_DO']); + this.appendStatementInput('STACK').appendField( + Msg['PROCEDURES_DEFNORETURN_DO']); if (this.getInput('RETURN')) { this.moveInputBefore('STACK', 'RETURN'); } @@ -74,40 +70,40 @@ Blockly.Blocks['procedures_defnoreturn'] = { /** * Update the display of parameters for this procedure definition block. * @private - * @this {Blockly.Block} + * @this {Block} */ updateParams_: function() { - // Merge the arguments into a human-readable list. - var paramString = ''; + let paramString = ''; if (this.arguments_.length) { - paramString = Blockly.Msg['PROCEDURES_BEFORE_PARAMS'] + - ' ' + this.arguments_.join(', '); + paramString = + Msg['PROCEDURES_BEFORE_PARAMS'] + ' ' + this.arguments_.join(', '); } // The params field is deterministic based on the mutation, // no need to fire a change event. - Blockly.Events.disable(); + Events.disable(); try { this.setFieldValue(paramString, 'PARAMS'); } finally { - Blockly.Events.enable(); + Events.enable(); } }, /** * Create XML to represent the argument inputs. + * Backwards compatible serialization implementation. * @param {boolean=} opt_paramIds If true include the IDs of the parameter - * quarks. Used by Blockly.Procedures.mutateCallers for reconnection. + * quarks. Used by Procedures.mutateCallers for reconnection. * @return {!Element} XML storage element. - * @this {Blockly.Block} + * @this {Block} */ mutationToDom: function(opt_paramIds) { - var container = Blockly.utils.xml.createElement('mutation'); + const container = xmlUtils.createElement('mutation'); if (opt_paramIds) { container.setAttribute('name', this.getFieldValue('NAME')); } - for (var i = 0; i < this.argumentVarModels_.length; i++) { - var parameter = Blockly.utils.xml.createElement('arg'); - var argModel = this.argumentVarModels_[i]; + for (let i = 0; i < this.argumentVarModels_.length; i++) { + const parameter = xmlUtils.createElement('arg'); + const argModel = this.argumentVarModels_[i]; parameter.setAttribute('name', argModel.name); parameter.setAttribute('varid', argModel.getId()); if (opt_paramIds && this.paramIds_) { @@ -124,37 +120,89 @@ Blockly.Blocks['procedures_defnoreturn'] = { }, /** * Parse XML to restore the argument inputs. + * Backwards compatible serialization implementation. * @param {!Element} xmlElement XML storage element. - * @this {Blockly.Block} + * @this {Block} */ domToMutation: function(xmlElement) { this.arguments_ = []; this.argumentVarModels_ = []; - for (var i = 0, childNode; (childNode = xmlElement.childNodes[i]); i++) { - if (childNode.nodeName.toLowerCase() == 'arg') { - var varName = childNode.getAttribute('name'); - var varId = childNode.getAttribute('varid') || childNode.getAttribute('varId'); + for (let i = 0, childNode; (childNode = xmlElement.childNodes[i]); i++) { + if (childNode.nodeName.toLowerCase() === 'arg') { + const varName = childNode.getAttribute('name'); + const varId = + childNode.getAttribute('varid') || childNode.getAttribute('varId'); this.arguments_.push(varName); - var variable = Blockly.Variables.getOrCreateVariablePackage( + const variable = Variables.getOrCreateVariablePackage( this.workspace, varId, varName, ''); - if (variable != null) { + if (variable !== null) { this.argumentVarModels_.push(variable); } else { - console.log('Failed to create a variable with name ' + varName + ', ignoring.'); + console.log( + 'Failed to create a variable with name ' + varName + + ', ignoring.'); } } } this.updateParams_(); - Blockly.Procedures.mutateCallers(this); + Procedures.mutateCallers(this); // Show or hide the statement input. this.setStatements_(xmlElement.getAttribute('statements') !== 'false'); }, + /** + * Returns the state of this block as a JSON serializable object. + * @return {?{params: (!Array<{name: string, id: string}>|undefined), + * hasStatements: (boolean|undefined)}} The state of this block, eg the + * parameters and statements. + */ + saveExtraState: function() { + if (!this.argumentVarModels_.length && this.hasStatements_) { + return null; + } + const state = Object.create(null); + if (this.argumentVarModels_.length) { + state['params'] = []; + for (let i = 0; i < this.argumentVarModels_.length; i++) { + state['params'].push({ + // We don't need to serialize the name, but just in case we decide + // to separate params from variables. + 'name': this.argumentVarModels_[i].name, + 'id': this.argumentVarModels_[i].getId(), + }); + } + } + if (!this.hasStatements_) { + state['hasStatements'] = false; + } + return state; + }, + /** + * Applies the given state to this block. + * @param {*} state The state to apply to this block, eg the parameters and + * statements. + */ + loadExtraState: function(state) { + this.arguments_ = []; + this.argumentVarModels_ = []; + if (state['params']) { + for (let i = 0; i < state['params'].length; i++) { + const param = state['params'][i]; + const variable = Variables.getOrCreateVariablePackage( + this.workspace, param['id'], param['name'], ''); + this.arguments_.push(variable.name); + this.argumentVarModels_.push(variable); + } + } + this.updateParams_(); + Procedures.mutateCallers(this); + this.setStatements_(state['hasStatements'] === false ? false : true); + }, /** * Populate the mutator's dialog with this block's components. - * @param {!Blockly.Workspace} workspace Mutator's workspace. - * @return {!Blockly.Block} Root block in mutator. - * @this {Blockly.Block} + * @param {!Workspace} workspace Mutator's workspace. + * @return {!Block} Root block in mutator. + * @this {Block} */ decompose: function(workspace) { /* @@ -169,80 +217,80 @@ Blockly.Blocks['procedures_defnoreturn'] = { * */ - var containerBlockNode = Blockly.utils.xml.createElement('block'); + const containerBlockNode = xmlUtils.createElement('block'); containerBlockNode.setAttribute('type', 'procedures_mutatorcontainer'); - var statementNode = Blockly.utils.xml.createElement('statement'); + const statementNode = xmlUtils.createElement('statement'); statementNode.setAttribute('name', 'STACK'); containerBlockNode.appendChild(statementNode); - var node = statementNode; - for (var i = 0; i < this.arguments_.length; i++) { - var argBlockNode = Blockly.utils.xml.createElement('block'); + let node = statementNode; + for (let i = 0; i < this.arguments_.length; i++) { + const argBlockNode = xmlUtils.createElement('block'); argBlockNode.setAttribute('type', 'procedures_mutatorarg'); - var fieldNode = Blockly.utils.xml.createElement('field'); + const fieldNode = xmlUtils.createElement('field'); fieldNode.setAttribute('name', 'NAME'); - var argumentName = Blockly.utils.xml.createTextNode(this.arguments_[i]); + const argumentName = xmlUtils.createTextNode(this.arguments_[i]); fieldNode.appendChild(argumentName); argBlockNode.appendChild(fieldNode); - var nextNode = Blockly.utils.xml.createElement('next'); + const nextNode = xmlUtils.createElement('next'); argBlockNode.appendChild(nextNode); node.appendChild(argBlockNode); node = nextNode; } - var containerBlock = Blockly.Xml.domToBlock(containerBlockNode, workspace); + const containerBlock = Xml.domToBlock(containerBlockNode, workspace); - if (this.type == 'procedures_defreturn') { + if (this.type === 'procedures_defreturn') { containerBlock.setFieldValue(this.hasStatements_, 'STATEMENTS'); } else { containerBlock.removeInput('STATEMENT_INPUT'); } // Initialize procedure's callers with blank IDs. - Blockly.Procedures.mutateCallers(this); + Procedures.mutateCallers(this); return containerBlock; }, /** * Reconfigure this block based on the mutator dialog's components. - * @param {!Blockly.Block} containerBlock Root block in mutator. - * @this {Blockly.Block} + * @param {!Block} containerBlock Root block in mutator. + * @this {Block} */ compose: function(containerBlock) { // Parameter list. this.arguments_ = []; this.paramIds_ = []; this.argumentVarModels_ = []; - var paramBlock = containerBlock.getInputTargetBlock('STACK'); + let paramBlock = containerBlock.getInputTargetBlock('STACK'); while (paramBlock && !paramBlock.isInsertionMarker()) { - var varName = paramBlock.getFieldValue('NAME'); + const varName = paramBlock.getFieldValue('NAME'); this.arguments_.push(varName); - var variable = this.workspace.getVariable(varName, ''); + const variable = this.workspace.getVariable(varName, ''); this.argumentVarModels_.push(variable); this.paramIds_.push(paramBlock.id); - paramBlock = paramBlock.nextConnection && - paramBlock.nextConnection.targetBlock(); + paramBlock = + paramBlock.nextConnection && paramBlock.nextConnection.targetBlock(); } this.updateParams_(); - Blockly.Procedures.mutateCallers(this); + Procedures.mutateCallers(this); // Show/hide the statement input. - var hasStatements = containerBlock.getFieldValue('STATEMENTS'); + let hasStatements = containerBlock.getFieldValue('STATEMENTS'); if (hasStatements !== null) { - hasStatements = hasStatements == 'TRUE'; - if (this.hasStatements_ != hasStatements) { + hasStatements = hasStatements === 'TRUE'; + if (this.hasStatements_ !== hasStatements) { if (hasStatements) { this.setStatements_(true); // Restore the stack, if one was saved. - Blockly.Mutator.reconnect(this.statementConnection_, this, 'STACK'); + Mutator.reconnect(this.statementConnection_, this, 'STACK'); this.statementConnection_ = null; } else { // Save the stack, then disconnect it. - var stackConnection = this.getInput('STACK').connection; + const stackConnection = this.getInput('STACK').connection; this.statementConnection_ = stackConnection.targetConnection; if (this.statementConnection_) { - var stackBlock = stackConnection.targetBlock(); + const stackBlock = stackConnection.targetBlock(); stackBlock.unplug(); stackBlock.bumpNeighbours(); } @@ -251,29 +299,18 @@ Blockly.Blocks['procedures_defnoreturn'] = { } } }, - /** - * Return the signature of this procedure definition. - * @return {!Array} Tuple containing three elements: - * - the name of the defined procedure, - * - a list of all its arguments, - * - that it DOES NOT have a return value. - * @this {Blockly.Block} - */ - getProcedureDef: function() { - return [this.getFieldValue('NAME'), this.arguments_, false]; - }, /** * Return all variables referenced by this block. * @return {!Array} List of variable names. - * @this {Blockly.Block} + * @this {Block} */ getVars: function() { return this.arguments_; }, /** * Return all variables referenced by this block. - * @return {!Array} List of variable models. - * @this {Blockly.Block} + * @return {!Array} List of variable models. + * @this {Block} */ getVarModels: function() { return this.argumentVarModels_; @@ -286,20 +323,20 @@ Blockly.Blocks['procedures_defnoreturn'] = { * with an updated name. Guaranteed to be the same type as the old * variable. * @override - * @this {Blockly.Block} + * @this {Block} */ renameVarById: function(oldId, newId) { - var oldVariable = this.workspace.getVariableById(oldId); - if (oldVariable.type != '') { + const oldVariable = this.workspace.getVariableById(oldId); + if (oldVariable.type !== '') { // Procedure arguments always have the empty type. return; } - var oldName = oldVariable.name; - var newVar = this.workspace.getVariableById(newId); + const oldName = oldVariable.name; + const newVar = this.workspace.getVariableById(newId); - var change = false; - for (var i = 0; i < this.argumentVarModels_.length; i++) { - if (this.argumentVarModels_[i].getId() == oldId) { + let change = false; + for (let i = 0; i < this.argumentVarModels_.length; i++) { + if (this.argumentVarModels_[i].getId() === oldId) { this.arguments_[i] = newVar.name; this.argumentVarModels_[i] = newVar; change = true; @@ -307,30 +344,31 @@ Blockly.Blocks['procedures_defnoreturn'] = { } if (change) { this.displayRenamedVar_(oldName, newVar.name); - Blockly.Procedures.mutateCallers(this); + Procedures.mutateCallers(this); } }, /** * Notification that a variable is renaming but keeping the same ID. If the * variable is in use on this block, rerender to show the new name. - * @param {!Blockly.VariableModel} variable The variable being renamed. + * @param {!VariableModel} variable The variable being renamed. * @package * @override - * @this {Blockly.Block} + * @this {Block} */ updateVarName: function(variable) { - var newName = variable.name; - var change = false; - for (var i = 0; i < this.argumentVarModels_.length; i++) { - if (this.argumentVarModels_[i].getId() == variable.getId()) { - var oldName = this.arguments_[i]; + const newName = variable.name; + let change = false; + let oldName; + for (let i = 0; i < this.argumentVarModels_.length; i++) { + if (this.argumentVarModels_[i].getId() === variable.getId()) { + oldName = this.arguments_[i]; this.arguments_[i] = newName; change = true; } } if (change) { this.displayRenamedVar_(oldName, newName); - Blockly.Procedures.mutateCallers(this); + Procedures.mutateCallers(this); } }, /** @@ -338,16 +376,16 @@ Blockly.Blocks['procedures_defnoreturn'] = { * @param {string} oldName The old display name of the argument. * @param {string} newName The new display name of the argument. * @private - * @this {Blockly.Block} + * @this {Block} */ displayRenamedVar_: function(oldName, newName) { this.updateParams_(); // Update the mutator's variables if the mutator is open. if (this.mutator && this.mutator.isVisible()) { - var blocks = this.mutator.workspace_.getAllBlocks(false); - for (var i = 0, block; (block = blocks[i]); i++) { - if (block.type == 'procedures_mutatorarg' && - Blockly.Names.equals(oldName, block.getFieldValue('NAME'))) { + const blocks = this.mutator.workspace_.getAllBlocks(false); + for (let i = 0, block; (block = blocks[i]); i++) { + if (block.type === 'procedures_mutatorarg' && + Names.equals(oldName, block.getFieldValue('NAME'))) { block.setFieldValue(newName, 'NAME'); } } @@ -356,150 +394,180 @@ Blockly.Blocks['procedures_defnoreturn'] = { /** * Add custom menu options to this block's context menu. * @param {!Array} options List of menu options to add to. - * @this {Blockly.Block} + * @this {Block} */ customContextMenu: function(options) { if (this.isInFlyout) { return; } // Add option to create caller. - var option = {enabled: true}; - var name = this.getFieldValue('NAME'); - option.text = Blockly.Msg['PROCEDURES_CREATE_DO'].replace('%1', name); - var xmlMutation = Blockly.utils.xml.createElement('mutation'); + const option = {enabled: true}; + const name = this.getFieldValue('NAME'); + option.text = Msg['PROCEDURES_CREATE_DO'].replace('%1', name); + const xmlMutation = xmlUtils.createElement('mutation'); xmlMutation.setAttribute('name', name); - for (var i = 0; i < this.arguments_.length; i++) { - var xmlArg = Blockly.utils.xml.createElement('arg'); + for (let i = 0; i < this.arguments_.length; i++) { + const xmlArg = xmlUtils.createElement('arg'); xmlArg.setAttribute('name', this.arguments_[i]); xmlMutation.appendChild(xmlArg); } - var xmlBlock = Blockly.utils.xml.createElement('block'); + const xmlBlock = xmlUtils.createElement('block'); xmlBlock.setAttribute('type', this.callType_); xmlBlock.appendChild(xmlMutation); - option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock); + option.callback = ContextMenu.callbackFactory(this, xmlBlock); options.push(option); // Add options to create getters for each parameter. if (!this.isCollapsed()) { - for (var i = 0; i < this.argumentVarModels_.length; i++) { - var argOption = {enabled: true}; - var argVar = this.argumentVarModels_[i]; - argOption.text = Blockly.Msg['VARIABLES_SET_CREATE_GET'] - .replace('%1', argVar.name); + for (let i = 0; i < this.argumentVarModels_.length; i++) { + const argOption = {enabled: true}; + const argVar = this.argumentVarModels_[i]; + argOption.text = + Msg['VARIABLES_SET_CREATE_GET'].replace('%1', argVar.name); - var argXmlField = Blockly.Variables.generateVariableFieldDom(argVar); - var argXmlBlock = Blockly.utils.xml.createElement('block'); + const argXmlField = Variables.generateVariableFieldDom(argVar); + const argXmlBlock = xmlUtils.createElement('block'); argXmlBlock.setAttribute('type', 'variables_get'); argXmlBlock.appendChild(argXmlField); - argOption.callback = - Blockly.ContextMenu.callbackFactory(this, argXmlBlock); + argOption.callback = ContextMenu.callbackFactory(this, argXmlBlock); options.push(argOption); } } }, - callType_: 'procedures_callnoreturn' + callType_: 'procedures_callnoreturn', +}; + +Blocks['procedures_defnoreturn'] = { + ...PROCEDURE_DEF_COMMON, + /** + * Block for defining a procedure with no return value. + * @this {Block} + */ + init: function() { + const initName = Procedures.findLegalName('', this); + const nameField = new FieldTextInput(initName, Procedures.rename); + nameField.setSpellcheck(false); + this.appendDummyInput() + .appendField(Msg['PROCEDURES_DEFNORETURN_TITLE']) + .appendField(nameField, 'NAME') + .appendField('', 'PARAMS'); + this.setMutator(new Mutator(['procedures_mutatorarg'])); + if ((this.workspace.options.comments || + (this.workspace.options.parentWorkspace && + this.workspace.options.parentWorkspace.options.comments)) && + Msg['PROCEDURES_DEFNORETURN_COMMENT']) { + this.setCommentText(Msg['PROCEDURES_DEFNORETURN_COMMENT']); + } + this.setStyle('procedure_blocks'); + this.setTooltip(Msg['PROCEDURES_DEFNORETURN_TOOLTIP']); + this.setHelpUrl(Msg['PROCEDURES_DEFNORETURN_HELPURL']); + this.arguments_ = []; + this.argumentVarModels_ = []; + this.setStatements_(true); + this.statementConnection_ = null; + }, + /** + * Return the signature of this procedure definition. + * @return {!Array} Tuple containing three elements: + * - the name of the defined procedure, + * - a list of all its arguments, + * - that it DOES NOT have a return value. + * @this {Block} + */ + getProcedureDef: function() { + return [this.getFieldValue('NAME'), this.arguments_, false]; + }, }; -Blockly.Blocks['procedures_defreturn'] = { +Blocks['procedures_defreturn'] = { + ...PROCEDURE_DEF_COMMON, /** * Block for defining a procedure with a return value. - * @this {Blockly.Block} + * @this {Block} */ init: function() { - var initName = Blockly.Procedures.findLegalName('', this); - var nameField = new Blockly.FieldTextInput(initName, - Blockly.Procedures.rename); + const initName = Procedures.findLegalName('', this); + const nameField = new FieldTextInput(initName, Procedures.rename); nameField.setSpellcheck(false); this.appendDummyInput() - .appendField(Blockly.Msg['PROCEDURES_DEFRETURN_TITLE']) + .appendField(Msg['PROCEDURES_DEFRETURN_TITLE']) .appendField(nameField, 'NAME') .appendField('', 'PARAMS'); this.appendValueInput('RETURN') - .setAlign(Blockly.ALIGN_RIGHT) - .appendField(Blockly.Msg['PROCEDURES_DEFRETURN_RETURN']); - this.setMutator(new Blockly.Mutator(['procedures_mutatorarg'])); + .setAlign(Align.RIGHT) + .appendField(Msg['PROCEDURES_DEFRETURN_RETURN']); + this.setMutator(new Mutator(['procedures_mutatorarg'])); if ((this.workspace.options.comments || (this.workspace.options.parentWorkspace && this.workspace.options.parentWorkspace.options.comments)) && - Blockly.Msg['PROCEDURES_DEFRETURN_COMMENT']) { - this.setCommentText(Blockly.Msg['PROCEDURES_DEFRETURN_COMMENT']); + Msg['PROCEDURES_DEFRETURN_COMMENT']) { + this.setCommentText(Msg['PROCEDURES_DEFRETURN_COMMENT']); } this.setStyle('procedure_blocks'); - this.setTooltip(Blockly.Msg['PROCEDURES_DEFRETURN_TOOLTIP']); - this.setHelpUrl(Blockly.Msg['PROCEDURES_DEFRETURN_HELPURL']); + this.setTooltip(Msg['PROCEDURES_DEFRETURN_TOOLTIP']); + this.setHelpUrl(Msg['PROCEDURES_DEFRETURN_HELPURL']); this.arguments_ = []; this.argumentVarModels_ = []; this.setStatements_(true); this.statementConnection_ = null; }, - setStatements_: Blockly.Blocks['procedures_defnoreturn'].setStatements_, - updateParams_: Blockly.Blocks['procedures_defnoreturn'].updateParams_, - mutationToDom: Blockly.Blocks['procedures_defnoreturn'].mutationToDom, - domToMutation: Blockly.Blocks['procedures_defnoreturn'].domToMutation, - decompose: Blockly.Blocks['procedures_defnoreturn'].decompose, - compose: Blockly.Blocks['procedures_defnoreturn'].compose, /** * Return the signature of this procedure definition. * @return {!Array} Tuple containing three elements: * - the name of the defined procedure, * - a list of all its arguments, * - that it DOES have a return value. - * @this {Blockly.Block} + * @this {Block} */ getProcedureDef: function() { return [this.getFieldValue('NAME'), this.arguments_, true]; }, - getVars: Blockly.Blocks['procedures_defnoreturn'].getVars, - getVarModels: Blockly.Blocks['procedures_defnoreturn'].getVarModels, - renameVarById: Blockly.Blocks['procedures_defnoreturn'].renameVarById, - updateVarName: Blockly.Blocks['procedures_defnoreturn'].updateVarName, - displayRenamedVar_: Blockly.Blocks['procedures_defnoreturn'].displayRenamedVar_, - customContextMenu: Blockly.Blocks['procedures_defnoreturn'].customContextMenu, - callType_: 'procedures_callreturn' }; -Blockly.Blocks['procedures_mutatorcontainer'] = { +Blocks['procedures_mutatorcontainer'] = { /** * Mutator block for procedure container. - * @this {Blockly.Block} + * @this {Block} */ init: function() { - this.appendDummyInput() - .appendField(Blockly.Msg['PROCEDURES_MUTATORCONTAINER_TITLE']); + this.appendDummyInput().appendField( + Msg['PROCEDURES_MUTATORCONTAINER_TITLE']); this.appendStatementInput('STACK'); this.appendDummyInput('STATEMENT_INPUT') - .appendField(Blockly.Msg['PROCEDURES_ALLOW_STATEMENTS']) - .appendField(new Blockly.FieldCheckbox('TRUE'), 'STATEMENTS'); + .appendField(Msg['PROCEDURES_ALLOW_STATEMENTS']) + .appendField(new FieldCheckbox('TRUE'), 'STATEMENTS'); this.setStyle('procedure_blocks'); - this.setTooltip(Blockly.Msg['PROCEDURES_MUTATORCONTAINER_TOOLTIP']); + this.setTooltip(Msg['PROCEDURES_MUTATORCONTAINER_TOOLTIP']); this.contextMenu = false; }, }; -Blockly.Blocks['procedures_mutatorarg'] = { +Blocks['procedures_mutatorarg'] = { /** * Mutator block for procedure argument. - * @this {Blockly.Block} + * @this {Block} */ init: function() { - var field = new Blockly.FieldTextInput( - Blockly.Procedures.DEFAULT_ARG, this.validator_); + const field = new FieldTextInput(Procedures.DEFAULT_ARG, this.validator_); // Hack: override showEditor to do just a little bit more work. // We don't have a good place to hook into the start of a text edit. field.oldShowEditorFn_ = field.showEditor_; - var newShowEditorFn = function() { + /** + * @this {FieldTextInput} + */ + const newShowEditorFn = function() { this.createdVariables_ = []; this.oldShowEditorFn_(); }; field.showEditor_ = newShowEditorFn; this.appendDummyInput() - .appendField(Blockly.Msg['PROCEDURES_MUTATORARG_TITLE']) + .appendField(Msg['PROCEDURES_MUTATORARG_TITLE']) .appendField(field, 'NAME'); this.setPreviousStatement(true); this.setNextStatement(true); this.setStyle('procedure_blocks'); - this.setTooltip(Blockly.Msg['PROCEDURES_MUTATORARG_TOOLTIP']); + this.setTooltip(Msg['PROCEDURES_MUTATORARG_TOOLTIP']); this.contextMenu = false; // Create the default variable when we drag the block in from the flyout. @@ -519,28 +587,28 @@ Blockly.Blocks['procedures_mutatorarg'] = { * @param {string} varName User-supplied name. * @return {?string} Valid name, or null if a name was not specified. * @private - * @this Blockly.FieldTextInput + * @this {FieldTextInput} */ validator_: function(varName) { - var sourceBlock = this.getSourceBlock(); - var outerWs = Blockly.Mutator.findParentWs(sourceBlock.workspace); + const sourceBlock = this.getSourceBlock(); + const outerWs = Mutator.findParentWs(sourceBlock.workspace); varName = varName.replace(/[\s\xa0]+/g, ' ').replace(/^ | $/g, ''); if (!varName) { return null; } // Prevents duplicate parameter names in functions - var workspace = sourceBlock.workspace.targetWorkspace || - sourceBlock.workspace; - var blocks = workspace.getAllBlocks(false); - var caselessName = varName.toLowerCase(); - for (var i = 0; i < blocks.length; i++) { - if (blocks[i].id == this.getSourceBlock().id) { + const workspace = + sourceBlock.workspace.targetWorkspace || sourceBlock.workspace; + const blocks = workspace.getAllBlocks(false); + const caselessName = varName.toLowerCase(); + for (let i = 0; i < blocks.length; i++) { + if (blocks[i].id === this.getSourceBlock().id) { continue; } // Other blocks values may not be set yet when this is loaded. - var otherVar = blocks[i].getFieldValue('NAME'); - if (otherVar && otherVar.toLowerCase() == caselessName) { + const otherVar = blocks[i].getFieldValue('NAME'); + if (otherVar && otherVar.toLowerCase() === caselessName) { return null; } } @@ -551,8 +619,8 @@ Blockly.Blocks['procedures_mutatorarg'] = { return varName; } - var model = outerWs.getVariable(varName, ''); - if (model && model.name != varName) { + let model = outerWs.getVariable(varName, ''); + if (model && model.name !== varName) { // Rename the variable (case change) outerWs.renameVariableById(model.getId(), varName); } @@ -571,46 +639,31 @@ Blockly.Blocks['procedures_mutatorarg'] = { * variable name. * @param {string} newText The new variable name. * @private - * @this Blockly.FieldTextInput + * @this {FieldTextInput} */ deleteIntermediateVars_: function(newText) { - var outerWs = Blockly.Mutator.findParentWs(this.getSourceBlock().workspace); + const outerWs = Mutator.findParentWs(this.getSourceBlock().workspace); if (!outerWs) { return; } - for (var i = 0; i < this.createdVariables_.length; i++) { - var model = this.createdVariables_[i]; - if (model.name != newText) { + for (let i = 0; i < this.createdVariables_.length; i++) { + const model = this.createdVariables_[i]; + if (model.name !== newText) { outerWs.deleteVariableById(model.getId()); } } - } -}; - -Blockly.Blocks['procedures_callnoreturn'] = { - /** - * Block for calling a procedure with no return value. - * @this {Blockly.Block} - */ - init: function() { - this.appendDummyInput('TOPROW') - .appendField('', 'NAME'); - this.setPreviousStatement(true); - this.setNextStatement(true); - this.setStyle('procedure_blocks'); - // Tooltip is set in renameProcedure. - this.setHelpUrl(Blockly.Msg['PROCEDURES_CALLNORETURN_HELPURL']); - this.arguments_ = []; - this.argumentVarModels_ = []; - this.quarkConnections_ = {}; - this.quarkIds_ = null; - this.previousEnabledState_ = true; }, +}; +/** + * Common properties for the procedure_callnoreturn and + * procedure_callreturn blocks. + */ +const PROCEDURE_CALL_COMMON = { /** * Returns the name of the procedure this block calls. * @return {string} Procedure name. - * @this {Blockly.Block} + * @this {Block} */ getProcedureCall: function() { // The NAME field is guaranteed to exist, null will never be returned. @@ -621,14 +674,14 @@ Blockly.Blocks['procedures_callnoreturn'] = { * If the name matches this block's procedure, rename it. * @param {string} oldName Previous name of procedure. * @param {string} newName Renamed procedure. - * @this {Blockly.Block} + * @this {Block} */ renameProcedure: function(oldName, newName) { - if (Blockly.Names.equals(oldName, this.getProcedureCall())) { + if (Names.equals(oldName, this.getProcedureCall())) { this.setFieldValue(newName, 'NAME'); - var baseMsg = this.outputConnection ? - Blockly.Msg['PROCEDURES_CALLRETURN_TOOLTIP'] : - Blockly.Msg['PROCEDURES_CALLNORETURN_TOOLTIP']; + const baseMsg = this.outputConnection ? + Msg['PROCEDURES_CALLRETURN_TOOLTIP'] : + Msg['PROCEDURES_CALLNORETURN_TOOLTIP']; this.setTooltip(baseMsg.replace('%1', newName)); } }, @@ -639,22 +692,22 @@ Blockly.Blocks['procedures_callnoreturn'] = { * parameter through the life of a mutator, regardless of param renaming), * e.g. ['piua', 'f8b_', 'oi.o']. * @private - * @this {Blockly.Block} + * @this {Block} */ setProcedureParameters_: function(paramNames, paramIds) { // Data structures: // this.arguments = ['x', 'y'] // Existing param names. - // this.quarkConnections_ {piua: null, f8b_: Blockly.Connection} + // this.quarkConnections_ {piua: null, f8b_: Connection} // Look-up of paramIds to connections plugged into the call block. // this.quarkIds_ = ['piua', 'f8b_'] // Existing param IDs. // Note that quarkConnections_ may include IDs that no longer exist, but // which might reappear if a param is reattached in the mutator. - var defBlock = Blockly.Procedures.getDefinition(this.getProcedureCall(), - this.workspace); - var mutatorOpen = defBlock && defBlock.mutator && - defBlock.mutator.isVisible(); + const defBlock = + Procedures.getDefinition(this.getProcedureCall(), this.workspace); + const mutatorOpen = + defBlock && defBlock.mutator && defBlock.mutator.isVisible(); if (!mutatorOpen) { this.quarkConnections_ = {}; this.quarkIds_ = null; @@ -665,12 +718,12 @@ Blockly.Blocks['procedures_callnoreturn'] = { } // Test arguments (arrays of strings) for changes. '\n' is not a valid // argument name character, so it is a valid delimiter here. - if (paramNames.join('\n') == this.arguments_.join('\n')) { + if (paramNames.join('\n') === this.arguments_.join('\n')) { // No change. this.quarkIds_ = paramIds; return; } - if (paramIds.length != paramNames.length) { + if (paramIds.length !== paramNames.length) { throw RangeError('paramNames and paramIds must be the same length.'); } this.setCollapsed(false); @@ -680,16 +733,16 @@ Blockly.Blocks['procedures_callnoreturn'] = { this.quarkIds_ = []; } // Switch off rendering while the block is rebuilt. - var savedRendered = this.rendered; + const savedRendered = this.rendered; this.rendered = false; // Update the quarkConnections_ with existing connections. - for (var i = 0; i < this.arguments_.length; i++) { - var input = this.getInput('ARG' + i); + for (let i = 0; i < this.arguments_.length; i++) { + const input = this.getInput('ARG' + i); if (input) { - var connection = input.connection.targetConnection; + const connection = input.connection.targetConnection; this.quarkConnections_[this.quarkIds_[i]] = connection; if (mutatorOpen && connection && - paramIds.indexOf(this.quarkIds_[i]) == -1) { + paramIds.indexOf(this.quarkIds_[i]) === -1) { // This connection should no longer be attached to this block. connection.disconnect(); connection.getSourceBlock().bumpNeighbours(); @@ -700,8 +753,8 @@ Blockly.Blocks['procedures_callnoreturn'] = { this.arguments_ = [].concat(paramNames); // And rebuild the argument model list. this.argumentVarModels_ = []; - for (var i = 0; i < this.arguments_.length; i++) { - var variable = Blockly.Variables.getOrCreateVariablePackage( + for (let i = 0; i < this.arguments_.length; i++) { + const variable = Variables.getOrCreateVariablePackage( this.workspace, null, this.arguments_[i], ''); this.argumentVarModels_.push(variable); } @@ -710,11 +763,11 @@ Blockly.Blocks['procedures_callnoreturn'] = { this.quarkIds_ = paramIds; // Reconnect any child blocks. if (this.quarkIds_) { - for (var i = 0; i < this.arguments_.length; i++) { - var quarkId = this.quarkIds_[i]; + for (let i = 0; i < this.arguments_.length; i++) { + const quarkId = this.quarkIds_[i]; if (quarkId in this.quarkConnections_) { - var connection = this.quarkConnections_[quarkId]; - if (!Blockly.Mutator.reconnect(connection, this, 'ARG' + i)) { + const connection = this.quarkConnections_[quarkId]; + if (!Mutator.reconnect(connection, this, 'ARG' + i)) { // Block no longer exists or has been attached elsewhere. delete this.quarkConnections_[quarkId]; } @@ -730,41 +783,40 @@ Blockly.Blocks['procedures_callnoreturn'] = { /** * Modify this block to have the correct number of arguments. * @private - * @this {Blockly.Block} + * @this {Block} */ updateShape_: function() { - for (var i = 0; i < this.arguments_.length; i++) { - var field = this.getField('ARGNAME' + i); - if (field) { + for (let i = 0; i < this.arguments_.length; i++) { + const argField = this.getField('ARGNAME' + i); + if (argField) { // Ensure argument name is up to date. // The argument name field is deterministic based on the mutation, // no need to fire a change event. - Blockly.Events.disable(); + Events.disable(); try { - field.setValue(this.arguments_[i]); + argField.setValue(this.arguments_[i]); } finally { - Blockly.Events.enable(); + Events.enable(); } } else { // Add new input. - field = new Blockly.FieldLabel(this.arguments_[i]); - var input = this.appendValueInput('ARG' + i) - .setAlign(Blockly.ALIGN_RIGHT) - .appendField(field, 'ARGNAME' + i); + const newField = new FieldLabel(this.arguments_[i]); + const input = this.appendValueInput('ARG' + i) + .setAlign(Align.RIGHT) + .appendField(newField, 'ARGNAME' + i); input.init(); } } // Remove deleted inputs. - while (this.getInput('ARG' + i)) { + for (let i = this.arguments_.length; this.getInput('ARG' + i); i++) { this.removeInput('ARG' + i); - i++; } // Add 'with:' if there are parameters, remove otherwise. - var topRow = this.getInput('TOPROW'); + const topRow = this.getInput('TOPROW'); if (topRow) { if (this.arguments_.length) { if (!this.getField('WITH')) { - topRow.appendField(Blockly.Msg['PROCEDURES_CALL_BEFORE_PARAMS'], 'WITH'); + topRow.appendField(Msg['PROCEDURES_CALL_BEFORE_PARAMS'], 'WITH'); topRow.init(); } } else { @@ -776,14 +828,15 @@ Blockly.Blocks['procedures_callnoreturn'] = { }, /** * Create XML to represent the (non-editable) name and arguments. + * Backwards compatible serialization implementation. * @return {!Element} XML storage element. - * @this {Blockly.Block} + * @this {Block} */ mutationToDom: function() { - var container = Blockly.utils.xml.createElement('mutation'); + const container = xmlUtils.createElement('mutation'); container.setAttribute('name', this.getProcedureCall()); - for (var i = 0; i < this.arguments_.length; i++) { - var parameter = Blockly.utils.xml.createElement('arg'); + for (let i = 0; i < this.arguments_.length; i++) { + const parameter = xmlUtils.createElement('arg'); parameter.setAttribute('name', this.arguments_[i]); container.appendChild(parameter); } @@ -791,34 +844,63 @@ Blockly.Blocks['procedures_callnoreturn'] = { }, /** * Parse XML to restore the (non-editable) name and parameters. + * Backwards compatible serialization implementation. * @param {!Element} xmlElement XML storage element. - * @this {Blockly.Block} + * @this {Block} */ domToMutation: function(xmlElement) { - var name = xmlElement.getAttribute('name'); + const name = xmlElement.getAttribute('name'); this.renameProcedure(this.getProcedureCall(), name); - var args = []; - var paramIds = []; - for (var i = 0, childNode; (childNode = xmlElement.childNodes[i]); i++) { - if (childNode.nodeName.toLowerCase() == 'arg') { + const args = []; + const paramIds = []; + for (let i = 0, childNode; (childNode = xmlElement.childNodes[i]); i++) { + if (childNode.nodeName.toLowerCase() === 'arg') { args.push(childNode.getAttribute('name')); paramIds.push(childNode.getAttribute('paramId')); } } this.setProcedureParameters_(args, paramIds); }, + /** + * Returns the state of this block as a JSON serializable object. + * @return {{name: string, params:(!Array|undefined)}} The state of + * this block, ie the params and procedure name. + */ + saveExtraState: function() { + const state = Object.create(null); + state['name'] = this.getProcedureCall(); + if (this.arguments_.length) { + state['params'] = this.arguments_; + } + return state; + }, + /** + * Applies the given state to this block. + * @param {*} state The state to apply to this block, ie the params and + * procedure name. + */ + loadExtraState: function(state) { + this.renameProcedure(this.getProcedureCall(), state['name']); + const params = state['params']; + if (params) { + const ids = []; + ids.length = params.length; + ids.fill(null); + this.setProcedureParameters_(params, ids); + } + }, /** * Return all variables referenced by this block. * @return {!Array} List of variable names. - * @this {Blockly.Block} + * @this {Block} */ getVars: function() { return this.arguments_; }, /** * Return all variables referenced by this block. - * @return {!Array} List of variable models. - * @this {Blockly.Block} + * @return {!Array} List of variable models. + * @this {Block} */ getVarModels: function() { return this.argumentVarModels_; @@ -826,8 +908,8 @@ Blockly.Blocks['procedures_callnoreturn'] = { /** * Procedure calls cannot exist without the corresponding procedure * definition. Enforce this link whenever an event is fired. - * @param {!Blockly.Events.Abstract} event Change event. - * @this {Blockly.Block} + * @param {!AbstractEvent} event Change event. + * @this {Block} */ onchange: function(event) { if (!this.workspace || this.workspace.isFlyout) { @@ -838,20 +920,21 @@ Blockly.Blocks['procedures_callnoreturn'] = { // Events not generated by user. Skip handling. return; } - if (event.type == Blockly.Events.BLOCK_CREATE && - event.ids.indexOf(this.id) != -1) { + if (event.type === Events.BLOCK_CREATE && + event.ids.indexOf(this.id) !== -1) { // Look for the case where a procedure call was created (usually through // paste) and there is no matching definition. In this case, create // an empty definition block with the correct signature. - var name = this.getProcedureCall(); - var def = Blockly.Procedures.getDefinition(name, this.workspace); - if (def && (def.type != this.defType_ || - JSON.stringify(def.getVars()) != JSON.stringify(this.arguments_))) { + const name = this.getProcedureCall(); + let def = Procedures.getDefinition(name, this.workspace); + if (def && + (def.type !== this.defType_ || + JSON.stringify(def.getVars()) !== JSON.stringify(this.arguments_))) { // The signatures don't match. def = null; } if (!def) { - Blockly.Events.setGroup(event.group); + Events.setGroup(event.group); /** * Create matching definition block. * @@ -863,68 +946,70 @@ Blockly.Blocks['procedures_callnoreturn'] = { * * */ - var xml = Blockly.utils.xml.createElement('xml'); - var block = Blockly.utils.xml.createElement('block'); + const xml = xmlUtils.createElement('xml'); + const block = xmlUtils.createElement('block'); block.setAttribute('type', this.defType_); - var xy = this.getRelativeToSurfaceXY(); - var x = xy.x + Blockly.SNAP_RADIUS * (this.RTL ? -1 : 1); - var y = xy.y + Blockly.SNAP_RADIUS * 2; + const xy = this.getRelativeToSurfaceXY(); + const x = xy.x + internalConstants.SNAP_RADIUS * (this.RTL ? -1 : 1); + const y = xy.y + internalConstants.SNAP_RADIUS * 2; block.setAttribute('x', x); block.setAttribute('y', y); - var mutation = this.mutationToDom(); + const mutation = this.mutationToDom(); block.appendChild(mutation); - var field = Blockly.utils.xml.createElement('field'); + const field = xmlUtils.createElement('field'); field.setAttribute('name', 'NAME'); - var callName = this.getProcedureCall(); + let callName = this.getProcedureCall(); if (!callName) { // Rename if name is empty string. - callName = Blockly.Procedures.findLegalName('', this); + callName = Procedures.findLegalName('', this); this.renameProcedure('', callName); } - field.appendChild(Blockly.utils.xml.createTextNode(callName)); + field.appendChild(xmlUtils.createTextNode(callName)); block.appendChild(field); xml.appendChild(block); - Blockly.Xml.domToWorkspace(xml, this.workspace); - Blockly.Events.setGroup(false); + Xml.domToWorkspace(xml, this.workspace); + Events.setGroup(false); } - } else if (event.type == Blockly.Events.BLOCK_DELETE) { + } else if (event.type === Events.BLOCK_DELETE) { // Look for the case where a procedure definition has been deleted, // leaving this block (a procedure call) orphaned. In this case, delete // the orphan. - var name = this.getProcedureCall(); - var def = Blockly.Procedures.getDefinition(name, this.workspace); + const name = this.getProcedureCall(); + const def = Procedures.getDefinition(name, this.workspace); if (!def) { - Blockly.Events.setGroup(event.group); + Events.setGroup(event.group); this.dispose(true); - Blockly.Events.setGroup(false); + Events.setGroup(false); } - } else if (event.type == Blockly.Events.CHANGE && event.element == 'disabled') { - var name = this.getProcedureCall(); - var def = Blockly.Procedures.getDefinition(name, this.workspace); - if (def && def.id == event.blockId) { + } else if (event.type === Events.CHANGE && event.element === 'disabled') { + const name = this.getProcedureCall(); + const def = Procedures.getDefinition(name, this.workspace); + if (def && def.id === event.blockId) { // in most cases the old group should be '' - var oldGroup = Blockly.Events.getGroup(); + const oldGroup = Events.getGroup(); if (oldGroup) { - // This should only be possible programmatically and may indicate a problem - // with event grouping. If you see this message please investigate. If the - // use ends up being valid we may need to reorder events in the undo stack. - console.log('Saw an existing group while responding to a definition change'); + // This should only be possible programmatically and may indicate a + // problem with event grouping. If you see this message please + // investigate. If the use ends up being valid we may need to reorder + // events in the undo stack. + console.log( + 'Saw an existing group while responding to a definition change'); } - Blockly.Events.setGroup(event.group); + Events.setGroup(event.group); if (event.newValue) { this.previousEnabledState_ = this.isEnabled(); this.setEnabled(false); } else { this.setEnabled(this.previousEnabledState_); } - Blockly.Events.setGroup(oldGroup); + Events.setGroup(oldGroup); } } }, /** * Add menu option to find the definition block for this call. * @param {!Array} options List of menu options to add to. - * @this {Blockly.Block} + * @this {Block} */ customContextMenu: function(options) { if (!this.workspace.isMovable()) { @@ -933,12 +1018,13 @@ Blockly.Blocks['procedures_callnoreturn'] = { return; } - var option = {enabled: true}; - option.text = Blockly.Msg['PROCEDURES_HIGHLIGHT_DEF']; - var name = this.getProcedureCall(); - var workspace = this.workspace; + const option = {enabled: true}; + option.text = Msg['PROCEDURES_HIGHLIGHT_DEF']; + const name = this.getProcedureCall(); + const workspace = this.workspace; option.callback = function() { - var def = Blockly.Procedures.getDefinition(name, workspace); + let def = Procedures.getDefinition(name, workspace); + if (def) { if (!def.InActiveModule()) { workspace.getModuleManager().activateModule(workspace.getModuleManager().getModuleById(def.getModuleId())); @@ -950,21 +1036,43 @@ Blockly.Blocks['procedures_callnoreturn'] = { }; options.push(option); }, - defType_: 'procedures_defnoreturn' }; -Blockly.Blocks['procedures_callreturn'] = { +Blocks['procedures_callnoreturn'] = { + ...PROCEDURE_CALL_COMMON, + /** + * Block for calling a procedure with no return value. + * @this {Block} + */ + init: function() { + this.appendDummyInput('TOPROW').appendField('', 'NAME'); + this.setPreviousStatement(true); + this.setNextStatement(true); + this.setStyle('procedure_blocks'); + // Tooltip is set in renameProcedure. + this.setHelpUrl(Msg['PROCEDURES_CALLNORETURN_HELPURL']); + this.arguments_ = []; + this.argumentVarModels_ = []; + this.quarkConnections_ = {}; + this.quarkIds_ = null; + this.previousEnabledState_ = true; + }, + + defType_: 'procedures_defnoreturn', +}; + +Blocks['procedures_callreturn'] = { + ...PROCEDURE_CALL_COMMON, /** * Block for calling a procedure with a return value. - * @this {Blockly.Block} + * @this {Block} */ init: function() { - this.appendDummyInput('TOPROW') - .appendField('', 'NAME'); + this.appendDummyInput('TOPROW').appendField('', 'NAME'); this.setOutput(true); this.setStyle('procedure_blocks'); // Tooltip is set in domToMutation. - this.setHelpUrl(Blockly.Msg['PROCEDURES_CALLRETURN_HELPURL']); + this.setHelpUrl(Msg['PROCEDURES_CALLRETURN_HELPURL']); this.arguments_ = []; this.argumentVarModels_ = []; this.quarkConnections_ = {}; @@ -972,79 +1080,73 @@ Blockly.Blocks['procedures_callreturn'] = { this.previousEnabledState_ = true; }, - getProcedureCall: Blockly.Blocks['procedures_callnoreturn'].getProcedureCall, - renameProcedure: Blockly.Blocks['procedures_callnoreturn'].renameProcedure, - setProcedureParameters_: - Blockly.Blocks['procedures_callnoreturn'].setProcedureParameters_, - updateShape_: Blockly.Blocks['procedures_callnoreturn'].updateShape_, - mutationToDom: Blockly.Blocks['procedures_callnoreturn'].mutationToDom, - domToMutation: Blockly.Blocks['procedures_callnoreturn'].domToMutation, - getVars: Blockly.Blocks['procedures_callnoreturn'].getVars, - getVarModels: Blockly.Blocks['procedures_callnoreturn'].getVarModels, - onchange: Blockly.Blocks['procedures_callnoreturn'].onchange, - customContextMenu: - Blockly.Blocks['procedures_callnoreturn'].customContextMenu, - defType_: 'procedures_defreturn' + defType_: 'procedures_defreturn', }; -Blockly.Blocks['procedures_ifreturn'] = { +Blocks['procedures_ifreturn'] = { /** * Block for conditionally returning a value from a procedure. - * @this {Blockly.Block} + * @this {Block} */ init: function() { this.appendValueInput('CONDITION') .setCheck('Boolean') - .appendField(Blockly.Msg['CONTROLS_IF_MSG_IF']); - this.appendValueInput('VALUE') - .appendField(Blockly.Msg['PROCEDURES_DEFRETURN_RETURN']); + .appendField(Msg['CONTROLS_IF_MSG_IF']); + this.appendValueInput('VALUE').appendField( + Msg['PROCEDURES_DEFRETURN_RETURN']); this.setInputsInline(true); this.setPreviousStatement(true); this.setNextStatement(true); this.setStyle('procedure_blocks'); - this.setTooltip(Blockly.Msg['PROCEDURES_IFRETURN_TOOLTIP']); - this.setHelpUrl(Blockly.Msg['PROCEDURES_IFRETURN_HELPURL']); + this.setTooltip(Msg['PROCEDURES_IFRETURN_TOOLTIP']); + this.setHelpUrl(Msg['PROCEDURES_IFRETURN_HELPURL']); this.hasReturnValue_ = true; }, /** * Create XML to represent whether this block has a return value. * @return {!Element} XML storage element. - * @this {Blockly.Block} + * @this {Block} */ mutationToDom: function() { - var container = Blockly.utils.xml.createElement('mutation'); + const container = xmlUtils.createElement('mutation'); container.setAttribute('value', Number(this.hasReturnValue_)); return container; }, /** * Parse XML to restore whether this block has a return value. * @param {!Element} xmlElement XML storage element. - * @this {Blockly.Block} + * @this {Block} */ domToMutation: function(xmlElement) { - var value = xmlElement.getAttribute('value'); - this.hasReturnValue_ = (value == 1); + const value = xmlElement.getAttribute('value'); + this.hasReturnValue_ = (value === '1'); if (!this.hasReturnValue_) { this.removeInput('VALUE'); - this.appendDummyInput('VALUE') - .appendField(Blockly.Msg['PROCEDURES_DEFRETURN_RETURN']); + this.appendDummyInput('VALUE').appendField( + Msg['PROCEDURES_DEFRETURN_RETURN']); } }, + + // This block does not need JSO serialization hooks (saveExtraState and + // loadExtraState) because the state of this block is already encoded in the + // block's position in the workspace. + // XML hooks are kept for backwards compatibility. + /** * Called whenever anything on the workspace changes. * Add warning if this flow block is not nested inside a loop. - * @param {!Blockly.Events.Abstract} _e Change event. - * @this {Blockly.Block} + * @param {!AbstractEvent} _e Change event. + * @this {Block} */ onchange: function(_e) { - if (!this.workspace.isDragging || this.workspace.isDragging()) { + if (this.workspace.isDragging && this.workspace.isDragging()) { return; // Don't change state at the start of a drag. } - var legal = false; + let legal = false; // Is the block nested in a procedure? - var block = this; + let block = this; do { - if (this.FUNCTION_TYPES.indexOf(block.type) != -1) { + if (this.FUNCTION_TYPES.indexOf(block.type) !== -1) { legal = true; break; } @@ -1052,16 +1154,16 @@ Blockly.Blocks['procedures_ifreturn'] = { } while (block); if (legal) { // If needed, toggle whether this block has a return value. - if (block.type == 'procedures_defnoreturn' && this.hasReturnValue_) { + if (block.type === 'procedures_defnoreturn' && this.hasReturnValue_) { this.removeInput('VALUE'); - this.appendDummyInput('VALUE') - .appendField(Blockly.Msg['PROCEDURES_DEFRETURN_RETURN']); + this.appendDummyInput('VALUE').appendField( + Msg['PROCEDURES_DEFRETURN_RETURN']); this.hasReturnValue_ = false; - } else if (block.type == 'procedures_defreturn' && - !this.hasReturnValue_) { + } else if ( + block.type === 'procedures_defreturn' && !this.hasReturnValue_) { this.removeInput('VALUE'); - this.appendValueInput('VALUE') - .appendField(Blockly.Msg['PROCEDURES_DEFRETURN_RETURN']); + this.appendValueInput('VALUE').appendField( + Msg['PROCEDURES_DEFRETURN_RETURN']); this.hasReturnValue_ = true; } this.setWarningText(null); @@ -1069,7 +1171,7 @@ Blockly.Blocks['procedures_ifreturn'] = { this.setEnabled(true); } } else { - this.setWarningText(Blockly.Msg['PROCEDURES_IFRETURN_WARNING']); + this.setWarningText(Msg['PROCEDURES_IFRETURN_WARNING']); if (!this.isInFlyout && !this.getInheritedDisabled()) { this.setEnabled(false); } @@ -1078,7 +1180,7 @@ Blockly.Blocks['procedures_ifreturn'] = { /** * List of block types that are functions and thus do not need warnings. * To add a new function type add this to your code: - * Blockly.Blocks['procedures_ifreturn'].FUNCTION_TYPES.push('custom_func'); + * Blocks['procedures_ifreturn'].FUNCTION_TYPES.push('custom_func'); */ - FUNCTION_TYPES: ['procedures_defnoreturn', 'procedures_defreturn'] + FUNCTION_TYPES: ['procedures_defnoreturn', 'procedures_defreturn'], }; diff --git a/blocks/text.js b/blocks/text.js index 9e806be92f3..9072c7fa1c3 100644 --- a/blocks/text.js +++ b/blocks/text.js @@ -6,290 +6,309 @@ /** * @fileoverview Text blocks for Blockly. - * @author fraser@google.com (Neil Fraser) + * @suppress {checkTypes} */ 'use strict'; -goog.provide('Blockly.Blocks.texts'); // Deprecated -goog.provide('Blockly.Constants.Text'); - -goog.require('Blockly'); -goog.require('Blockly.Blocks'); -goog.require('Blockly.FieldDropdown'); -goog.require('Blockly.FieldImage'); +goog.module('Blockly.blocks.texts'); + +const Extensions = goog.require('Blockly.Extensions'); +const {Msg} = goog.require('Blockly.Msg'); +/* eslint-disable-next-line no-unused-vars */ +const xmlUtils = goog.require('Blockly.utils.xml'); +const {Align} = goog.require('Blockly.Input'); +/* eslint-disable-next-line no-unused-vars */ +const {Block} = goog.requireType('Blockly.Block'); +const {Blocks} = goog.require('Blockly.blocks'); +const {ConnectionType} = goog.require('Blockly.ConnectionType'); +const {FieldDropdown} = goog.require('Blockly.FieldDropdown'); +const {FieldImage} = goog.require('Blockly.FieldImage'); +const {FieldTextInput} = goog.require('Blockly.FieldTextInput'); +const {Mutator} = goog.require('Blockly.Mutator'); +/* eslint-disable-next-line no-unused-vars */ +const {Workspace} = goog.requireType('Blockly.Workspace'); +const {defineBlocksWithJsonArray} = goog.require('Blockly.common'); +/** @suppress {extraRequire} */ goog.require('Blockly.FieldMultilineInput'); -goog.require('Blockly.FieldTextInput'); +/** @suppress {extraRequire} */ goog.require('Blockly.FieldVariable'); -goog.require('Blockly.Mutator'); - -/** - * Unused constant for the common HSV hue for all blocks in this category. - * @deprecated Use Blockly.Msg['TEXTS_HUE']. (2018 April 5) - */ -Blockly.Constants.Text.HUE = 160; -Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT +defineBlocksWithJsonArray([ // Block for text value { - "type": "text", - "message0": "%1", - "args0": [{ - "type": "field_input", - "name": "TEXT", - "text": "" + 'type': 'text', + 'message0': '%1', + 'args0': [{ + 'type': 'field_input', + 'name': 'TEXT', + 'text': '', }], - "output": "String", - "style": "text_blocks", - "helpUrl": "%{BKY_TEXT_TEXT_HELPURL}", - "tooltip": "%{BKY_TEXT_TEXT_TOOLTIP}", - "extensions": [ - "text_quotes", - "parent_tooltip_when_inline" - ] + 'output': 'String', + 'style': 'text_blocks', + 'helpUrl': '%{BKY_TEXT_TEXT_HELPURL}', + 'tooltip': '%{BKY_TEXT_TEXT_TOOLTIP}', + 'extensions': [ + 'text_quotes', + 'parent_tooltip_when_inline', + ], }, { - "type": "text_multiline", - "message0": "%1 %2", - "args0": [{ - "type": "field_image", - "src": 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAARCAYAAADpP' + - 'U2iAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAdhgAAHYYBXaITgQAAABh0RVh0' + - 'U29mdHdhcmUAcGFpbnQubmV0IDQuMS42/U4J6AAAAP1JREFUOE+Vks0KQUEYhjm' + - 'RIja4ABtZ2dm5A3t3Ia6AUm7CylYuQRaUhZSlLZJiQbFAyRnPN33y01HOW08z88' + - '73zpwzM4F3GWOCruvGIE4/rLaV+Nq1hVGMBqzhqlxgCys4wJA65xnogMHsQ5luj' + - 'nYHTejBBCK2mE4abjCgMGhNxHgDFWjDSG07kdfVa2pZMf4ZyMAdWmpZMfYOsLiD' + - 'MYMjlMB+K613QISRhTnITnsYg5yUd0DETmEoMlkFOeIT/A58iyK5E18BuTBfgYX' + - 'fwNJv4P9/oEBerLylOnRhygmGdPpTTBZAPkde61lbQe4moWUvYUZYLfUNftIY4z' + - 'wA5X2Z9AYnQrEAAAAASUVORK5CYII=', - "width": 12, - "height": 17, - "alt": '\u00B6' - },{ - "type": "field_multilinetext", - "name": "TEXT", - "text": "" - }], - "output": "String", - "style": "text_blocks", - "helpUrl": "%{BKY_TEXT_TEXT_HELPURL}", - "tooltip": "%{BKY_TEXT_TEXT_TOOLTIP}", - "extensions": [ - "parent_tooltip_when_inline" - ] + 'type': 'text_multiline', + 'message0': '%1 %2', + 'args0': [ + { + 'type': 'field_image', + 'src': + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAARCAYAAADpP' + + 'U2iAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAdhgAAHYYBXaITgQAAABh0RVh0' + + 'U29mdHdhcmUAcGFpbnQubmV0IDQuMS42/U4J6AAAAP1JREFUOE+Vks0KQUEYhjm' + + 'RIja4ABtZ2dm5A3t3Ia6AUm7CylYuQRaUhZSlLZJiQbFAyRnPN33y01HOW08z88' + + '73zpwzM4F3GWOCruvGIE4/rLaV+Nq1hVGMBqzhqlxgCys4wJA65xnogMHsQ5luj' + + 'nYHTejBBCK2mE4abjCgMGhNxHgDFWjDSG07kdfVa2pZMf4ZyMAdWmpZMfYOsLiD' + + 'MYMjlMB+K613QISRhTnITnsYg5yUd0DETmEoMlkFOeIT/A58iyK5E18BuTBfgYX' + + 'fwNJv4P9/oEBerLylOnRhygmGdPpTTBZAPkde61lbQe4moWUvYUZYLfUNftIY4z' + + 'wA5X2Z9AYnQrEAAAAASUVORK5CYII=', + 'width': 12, + 'height': 17, + 'alt': '\u00B6', + }, + { + 'type': 'field_multilinetext', + 'name': 'TEXT', + 'text': '', + }, + ], + 'output': 'String', + 'style': 'text_blocks', + 'helpUrl': '%{BKY_TEXT_TEXT_HELPURL}', + 'tooltip': '%{BKY_TEXT_TEXT_TOOLTIP}', + 'extensions': [ + 'parent_tooltip_when_inline', + ], }, { - "type": "text_join", - "message0": "", - "output": "String", - "style": "text_blocks", - "helpUrl": "%{BKY_TEXT_JOIN_HELPURL}", - "tooltip": "%{BKY_TEXT_JOIN_TOOLTIP}", - "mutator": "text_join_mutator" + 'type': 'text_join', + 'message0': '', + 'output': 'String', + 'style': 'text_blocks', + 'helpUrl': '%{BKY_TEXT_JOIN_HELPURL}', + 'tooltip': '%{BKY_TEXT_JOIN_TOOLTIP}', + 'mutator': 'text_join_mutator', }, { - "type": "text_create_join_container", - "message0": "%{BKY_TEXT_CREATE_JOIN_TITLE_JOIN} %1 %2", - "args0": [{ - "type": "input_dummy" - }, - { - "type": "input_statement", - "name": "STACK" - }], - "style": "text_blocks", - "tooltip": "%{BKY_TEXT_CREATE_JOIN_TOOLTIP}", - "enableContextMenu": false + 'type': 'text_create_join_container', + 'message0': '%{BKY_TEXT_CREATE_JOIN_TITLE_JOIN} %1 %2', + 'args0': [ + { + 'type': 'input_dummy', + }, + { + 'type': 'input_statement', + 'name': 'STACK', + }, + ], + 'style': 'text_blocks', + 'tooltip': '%{BKY_TEXT_CREATE_JOIN_TOOLTIP}', + 'enableContextMenu': false, }, { - "type": "text_create_join_item", - "message0": "%{BKY_TEXT_CREATE_JOIN_ITEM_TITLE_ITEM}", - "previousStatement": null, - "nextStatement": null, - "style": "text_blocks", - "tooltip": "%{BKY_TEXT_CREATE_JOIN_ITEM_TOOLTIP}", - "enableContextMenu": false + 'type': 'text_create_join_item', + 'message0': '%{BKY_TEXT_CREATE_JOIN_ITEM_TITLE_ITEM}', + 'previousStatement': null, + 'nextStatement': null, + 'style': 'text_blocks', + 'tooltip': '%{BKY_TEXT_CREATE_JOIN_ITEM_TOOLTIP}', + 'enableContextMenu': false, }, { - "type": "text_append", - "message0": "%{BKY_TEXT_APPEND_TITLE}", - "args0": [{ - "type": "field_variable", - "name": "VAR", - "variable": "%{BKY_TEXT_APPEND_VARIABLE}" - }, - { - "type": "input_value", - "name": "TEXT" - }], - "previousStatement": null, - "nextStatement": null, - "style": "text_blocks", - "extensions": [ - "text_append_tooltip" - ] + 'type': 'text_append', + 'message0': '%{BKY_TEXT_APPEND_TITLE}', + 'args0': [ + { + 'type': 'field_variable', + 'name': 'VAR', + 'variable': '%{BKY_TEXT_APPEND_VARIABLE}', + }, + { + 'type': 'input_value', + 'name': 'TEXT', + }, + ], + 'previousStatement': null, + 'nextStatement': null, + 'style': 'text_blocks', + 'extensions': [ + 'text_append_tooltip', + ], }, { - "type": "text_length", - "message0": "%{BKY_TEXT_LENGTH_TITLE}", - "args0": [ + 'type': 'text_length', + 'message0': '%{BKY_TEXT_LENGTH_TITLE}', + 'args0': [ { - "type": "input_value", - "name": "VALUE", - "check": ['String', 'Array'] - } + 'type': 'input_value', + 'name': 'VALUE', + 'check': ['String', 'Array'], + }, ], - "output": 'Number', - "style": "text_blocks", - "tooltip": "%{BKY_TEXT_LENGTH_TOOLTIP}", - "helpUrl": "%{BKY_TEXT_LENGTH_HELPURL}" + 'output': 'Number', + 'style': 'text_blocks', + 'tooltip': '%{BKY_TEXT_LENGTH_TOOLTIP}', + 'helpUrl': '%{BKY_TEXT_LENGTH_HELPURL}', }, { - "type": "text_isEmpty", - "message0": "%{BKY_TEXT_ISEMPTY_TITLE}", - "args0": [ + 'type': 'text_isEmpty', + 'message0': '%{BKY_TEXT_ISEMPTY_TITLE}', + 'args0': [ { - "type": "input_value", - "name": "VALUE", - "check": ['String', 'Array'] - } + 'type': 'input_value', + 'name': 'VALUE', + 'check': ['String', 'Array'], + }, ], - "output": 'Boolean', - "style": "text_blocks", - "tooltip": "%{BKY_TEXT_ISEMPTY_TOOLTIP}", - "helpUrl": "%{BKY_TEXT_ISEMPTY_HELPURL}" + 'output': 'Boolean', + 'style': 'text_blocks', + 'tooltip': '%{BKY_TEXT_ISEMPTY_TOOLTIP}', + 'helpUrl': '%{BKY_TEXT_ISEMPTY_HELPURL}', }, { - "type": "text_indexOf", - "message0": "%{BKY_TEXT_INDEXOF_TITLE}", - "args0": [ + 'type': 'text_indexOf', + 'message0': '%{BKY_TEXT_INDEXOF_TITLE}', + 'args0': [ { - "type": "input_value", - "name": "VALUE", - "check": "String" + 'type': 'input_value', + 'name': 'VALUE', + 'check': 'String', }, { - "type": "field_dropdown", - "name": "END", - "options": [ + 'type': 'field_dropdown', + 'name': 'END', + 'options': [ [ - "%{BKY_TEXT_INDEXOF_OPERATOR_FIRST}", - "FIRST" + '%{BKY_TEXT_INDEXOF_OPERATOR_FIRST}', + 'FIRST', ], [ - "%{BKY_TEXT_INDEXOF_OPERATOR_LAST}", - "LAST" - ] - ] + '%{BKY_TEXT_INDEXOF_OPERATOR_LAST}', + 'LAST', + ], + ], }, { - "type": "input_value", - "name": "FIND", - "check": "String" - } + 'type': 'input_value', + 'name': 'FIND', + 'check': 'String', + }, + ], + 'output': 'Number', + 'style': 'text_blocks', + 'helpUrl': '%{BKY_TEXT_INDEXOF_HELPURL}', + 'inputsInline': true, + 'extensions': [ + 'text_indexOf_tooltip', ], - "output": "Number", - "style": "text_blocks", - "helpUrl": "%{BKY_TEXT_INDEXOF_HELPURL}", - "inputsInline": true, - "extensions": [ - "text_indexOf_tooltip" - ] }, { - "type": "text_charAt", - "message0": "%{BKY_TEXT_CHARAT_TITLE}", // "in text %1 %2" - "args0": [ + 'type': 'text_charAt', + 'message0': '%{BKY_TEXT_CHARAT_TITLE}', // "in text %1 %2" + 'args0': [ { - "type":"input_value", - "name": "VALUE", - "check": "String" + 'type': 'input_value', + 'name': 'VALUE', + 'check': 'String', }, { - "type": "field_dropdown", - "name": "WHERE", - "options": [ - ["%{BKY_TEXT_CHARAT_FROM_START}", "FROM_START"], - ["%{BKY_TEXT_CHARAT_FROM_END}", "FROM_END"], - ["%{BKY_TEXT_CHARAT_FIRST}", "FIRST"], - ["%{BKY_TEXT_CHARAT_LAST}", "LAST"], - ["%{BKY_TEXT_CHARAT_RANDOM}", "RANDOM"] - ] - } + 'type': 'field_dropdown', + 'name': 'WHERE', + 'options': [ + ['%{BKY_TEXT_CHARAT_FROM_START}', 'FROM_START'], + ['%{BKY_TEXT_CHARAT_FROM_END}', 'FROM_END'], + ['%{BKY_TEXT_CHARAT_FIRST}', 'FIRST'], + ['%{BKY_TEXT_CHARAT_LAST}', 'LAST'], + ['%{BKY_TEXT_CHARAT_RANDOM}', 'RANDOM'], + ], + }, ], - "output": "String", - "style": "text_blocks", - "helpUrl": "%{BKY_TEXT_CHARAT_HELPURL}", - "inputsInline": true, - "mutator": "text_charAt_mutator" - } -]); // END JSON EXTRACT (Do not delete this comment.) + 'output': 'String', + 'style': 'text_blocks', + 'helpUrl': '%{BKY_TEXT_CHARAT_HELPURL}', + 'inputsInline': true, + 'mutator': 'text_charAt_mutator', + }, +]); -Blockly.Blocks['text_getSubstring'] = { +Blocks['text_getSubstring'] = { /** * Block for getting substring. - * @this {Blockly.Block} + * @this {Block} */ init: function() { this['WHERE_OPTIONS_1'] = [ - [Blockly.Msg['TEXT_GET_SUBSTRING_START_FROM_START'], 'FROM_START'], - [Blockly.Msg['TEXT_GET_SUBSTRING_START_FROM_END'], 'FROM_END'], - [Blockly.Msg['TEXT_GET_SUBSTRING_START_FIRST'], 'FIRST'] + [Msg['TEXT_GET_SUBSTRING_START_FROM_START'], 'FROM_START'], + [Msg['TEXT_GET_SUBSTRING_START_FROM_END'], 'FROM_END'], + [Msg['TEXT_GET_SUBSTRING_START_FIRST'], 'FIRST'], ]; this['WHERE_OPTIONS_2'] = [ - [Blockly.Msg['TEXT_GET_SUBSTRING_END_FROM_START'], 'FROM_START'], - [Blockly.Msg['TEXT_GET_SUBSTRING_END_FROM_END'], 'FROM_END'], - [Blockly.Msg['TEXT_GET_SUBSTRING_END_LAST'], 'LAST'] + [Msg['TEXT_GET_SUBSTRING_END_FROM_START'], 'FROM_START'], + [Msg['TEXT_GET_SUBSTRING_END_FROM_END'], 'FROM_END'], + [Msg['TEXT_GET_SUBSTRING_END_LAST'], 'LAST'], ]; - this.setHelpUrl(Blockly.Msg['TEXT_GET_SUBSTRING_HELPURL']); + this.setHelpUrl(Msg['TEXT_GET_SUBSTRING_HELPURL']); this.setStyle('text_blocks'); - this.appendValueInput('STRING') - .setCheck('String') - .appendField(Blockly.Msg['TEXT_GET_SUBSTRING_INPUT_IN_TEXT']); + this.appendValueInput('STRING').setCheck('String').appendField( + Msg['TEXT_GET_SUBSTRING_INPUT_IN_TEXT']); this.appendDummyInput('AT1'); this.appendDummyInput('AT2'); - if (Blockly.Msg['TEXT_GET_SUBSTRING_TAIL']) { - this.appendDummyInput('TAIL') - .appendField(Blockly.Msg['TEXT_GET_SUBSTRING_TAIL']); + if (Msg['TEXT_GET_SUBSTRING_TAIL']) { + this.appendDummyInput('TAIL').appendField(Msg['TEXT_GET_SUBSTRING_TAIL']); } this.setInputsInline(true); this.setOutput(true, 'String'); this.updateAt_(1, true); this.updateAt_(2, true); - this.setTooltip(Blockly.Msg['TEXT_GET_SUBSTRING_TOOLTIP']); + this.setTooltip(Msg['TEXT_GET_SUBSTRING_TOOLTIP']); }, /** * Create XML to represent whether there are 'AT' inputs. + * Backwards compatible serialization implementation. * @return {!Element} XML storage element. - * @this {Blockly.Block} + * @this {Block} */ mutationToDom: function() { - var container = Blockly.utils.xml.createElement('mutation'); - var isAt1 = this.getInput('AT1').type == Blockly.INPUT_VALUE; + const container = xmlUtils.createElement('mutation'); + const isAt1 = this.getInput('AT1').type === ConnectionType.INPUT_VALUE; container.setAttribute('at1', isAt1); - var isAt2 = this.getInput('AT2').type == Blockly.INPUT_VALUE; + const isAt2 = this.getInput('AT2').type === ConnectionType.INPUT_VALUE; container.setAttribute('at2', isAt2); return container; }, /** * Parse XML to restore the 'AT' inputs. + * Backwards compatible serialization implementation. * @param {!Element} xmlElement XML storage element. - * @this {Blockly.Block} + * @this {Block} */ domToMutation: function(xmlElement) { - var isAt1 = (xmlElement.getAttribute('at1') == 'true'); - var isAt2 = (xmlElement.getAttribute('at2') == 'true'); + const isAt1 = (xmlElement.getAttribute('at1') === 'true'); + const isAt2 = (xmlElement.getAttribute('at2') === 'true'); this.updateAt_(1, isAt1); this.updateAt_(2, isAt2); }, + + // This block does not need JSO serialization hooks (saveExtraState and + // loadExtraState) because the state of this object is already encoded in the + // dropdown values. + // XML hooks are kept for backwards compatibility. + /** * Create or delete an input for a numeric index. * This block has two such inputs, independent of each other. * @param {number} n Specify first or second input (1 or 2). * @param {boolean} isAt True if the input should exist. * @private - * @this {Blockly.Block} + * @this {Block} */ updateAt_: function(n, isAt) { // Create or delete an input for the numeric index. @@ -299,26 +318,32 @@ Blockly.Blocks['text_getSubstring'] = { // Create either a value 'AT' input or a dummy input. if (isAt) { this.appendValueInput('AT' + n).setCheck('Number'); - if (Blockly.Msg['ORDINAL_NUMBER_SUFFIX']) { + if (Msg['ORDINAL_NUMBER_SUFFIX']) { this.appendDummyInput('ORDINAL' + n) - .appendField(Blockly.Msg['ORDINAL_NUMBER_SUFFIX']); + .appendField(Msg['ORDINAL_NUMBER_SUFFIX']); } } else { this.appendDummyInput('AT' + n); } // Move tail, if present, to end of block. - if (n == 2 && Blockly.Msg['TEXT_GET_SUBSTRING_TAIL']) { + if (n === 2 && Msg['TEXT_GET_SUBSTRING_TAIL']) { this.removeInput('TAIL', true); - this.appendDummyInput('TAIL') - .appendField(Blockly.Msg['TEXT_GET_SUBSTRING_TAIL']); + this.appendDummyInput('TAIL').appendField(Msg['TEXT_GET_SUBSTRING_TAIL']); } - var menu = new Blockly.FieldDropdown(this['WHERE_OPTIONS_' + n], + const menu = new FieldDropdown( + this['WHERE_OPTIONS_' + n], + /** + * @param {*} value The input value. + * @this {FieldDropdown} + * @returns {null|undefined} Null if the field has been replaced; + * otherwise undefined. + */ function(value) { - var newAt = (value == 'FROM_START') || (value == 'FROM_END'); + const newAt = (value === 'FROM_START') || (value === 'FROM_END'); // The 'isAt' variable is available due to this function being a // closure. - if (newAt != isAt) { - var block = this.getSourceBlock(); + if (newAt !== isAt) { + const block = this.getSourceBlock(); block.updateAt_(n, newAt); // This menu has been destroyed and replaced. // Update the replacement. @@ -328,288 +353,299 @@ Blockly.Blocks['text_getSubstring'] = { return undefined; }); - this.getInput('AT' + n) - .appendField(menu, 'WHERE' + n); - if (n == 1) { + this.getInput('AT' + n).appendField(menu, 'WHERE' + n); + if (n === 1) { this.moveInputBefore('AT1', 'AT2'); if (this.getInput('ORDINAL1')) { this.moveInputBefore('ORDINAL1', 'AT2'); } } - } + }, }; -Blockly.Blocks['text_changeCase'] = { +Blocks['text_changeCase'] = { /** * Block for changing capitalization. - * @this {Blockly.Block} + * @this {Block} */ init: function() { - var OPERATORS = [ - [Blockly.Msg['TEXT_CHANGECASE_OPERATOR_UPPERCASE'], 'UPPERCASE'], - [Blockly.Msg['TEXT_CHANGECASE_OPERATOR_LOWERCASE'], 'LOWERCASE'], - [Blockly.Msg['TEXT_CHANGECASE_OPERATOR_TITLECASE'], 'TITLECASE'] + const OPERATORS = [ + [Msg['TEXT_CHANGECASE_OPERATOR_UPPERCASE'], 'UPPERCASE'], + [Msg['TEXT_CHANGECASE_OPERATOR_LOWERCASE'], 'LOWERCASE'], + [Msg['TEXT_CHANGECASE_OPERATOR_TITLECASE'], 'TITLECASE'], ]; - this.setHelpUrl(Blockly.Msg['TEXT_CHANGECASE_HELPURL']); + this.setHelpUrl(Msg['TEXT_CHANGECASE_HELPURL']); this.setStyle('text_blocks'); - this.appendValueInput('TEXT') - .setCheck('String') - .appendField(new Blockly.FieldDropdown(OPERATORS), 'CASE'); + this.appendValueInput('TEXT').setCheck('String').appendField( + new FieldDropdown(OPERATORS), 'CASE'); this.setOutput(true, 'String'); - this.setTooltip(Blockly.Msg['TEXT_CHANGECASE_TOOLTIP']); - } + this.setTooltip(Msg['TEXT_CHANGECASE_TOOLTIP']); + }, }; -Blockly.Blocks['text_trim'] = { +Blocks['text_trim'] = { /** * Block for trimming spaces. - * @this {Blockly.Block} + * @this {Block} */ init: function() { - var OPERATORS = [ - [Blockly.Msg['TEXT_TRIM_OPERATOR_BOTH'], 'BOTH'], - [Blockly.Msg['TEXT_TRIM_OPERATOR_LEFT'], 'LEFT'], - [Blockly.Msg['TEXT_TRIM_OPERATOR_RIGHT'], 'RIGHT'] + const OPERATORS = [ + [Msg['TEXT_TRIM_OPERATOR_BOTH'], 'BOTH'], + [Msg['TEXT_TRIM_OPERATOR_LEFT'], 'LEFT'], + [Msg['TEXT_TRIM_OPERATOR_RIGHT'], 'RIGHT'], ]; - this.setHelpUrl(Blockly.Msg['TEXT_TRIM_HELPURL']); + this.setHelpUrl(Msg['TEXT_TRIM_HELPURL']); this.setStyle('text_blocks'); - this.appendValueInput('TEXT') - .setCheck('String') - .appendField(new Blockly.FieldDropdown(OPERATORS), 'MODE'); + this.appendValueInput('TEXT').setCheck('String').appendField( + new FieldDropdown(OPERATORS), 'MODE'); this.setOutput(true, 'String'); - this.setTooltip(Blockly.Msg['TEXT_TRIM_TOOLTIP']); - } + this.setTooltip(Msg['TEXT_TRIM_TOOLTIP']); + }, }; -Blockly.Blocks['text_print'] = { +Blocks['text_print'] = { /** * Block for print statement. - * @this {Blockly.Block} + * @this {Block} */ init: function() { this.jsonInit({ - "message0": Blockly.Msg['TEXT_PRINT_TITLE'], - "args0": [ + 'message0': Msg['TEXT_PRINT_TITLE'], + 'args0': [ { - "type": "input_value", - "name": "TEXT" - } + 'type': 'input_value', + 'name': 'TEXT', + }, ], - "previousStatement": null, - "nextStatement": null, - "style": "text_blocks", - "tooltip": Blockly.Msg['TEXT_PRINT_TOOLTIP'], - "helpUrl": Blockly.Msg['TEXT_PRINT_HELPURL'] + 'previousStatement': null, + 'nextStatement': null, + 'style': 'text_blocks', + 'tooltip': Msg['TEXT_PRINT_TOOLTIP'], + 'helpUrl': Msg['TEXT_PRINT_HELPURL'], }); - } + }, }; -Blockly.Blocks['text_prompt_ext'] = { - /** - * Block for prompt function (external message). - * @this {Blockly.Block} - */ - init: function() { - var TYPES = [ - [Blockly.Msg['TEXT_PROMPT_TYPE_TEXT'], 'TEXT'], - [Blockly.Msg['TEXT_PROMPT_TYPE_NUMBER'], 'NUMBER'] - ]; - this.setHelpUrl(Blockly.Msg['TEXT_PROMPT_HELPURL']); - this.setStyle('text_blocks'); - // Assign 'this' to a variable for use in the closures below. - var thisBlock = this; - var dropdown = new Blockly.FieldDropdown(TYPES, function(newOp) { - thisBlock.updateType_(newOp); - }); - this.appendValueInput('TEXT') - .appendField(dropdown, 'TYPE'); - this.setOutput(true, 'String'); - this.setTooltip(function() { - return (thisBlock.getFieldValue('TYPE') == 'TEXT') ? - Blockly.Msg['TEXT_PROMPT_TOOLTIP_TEXT'] : - Blockly.Msg['TEXT_PROMPT_TOOLTIP_NUMBER']; - }); - }, + +/** + * Common properties for the text_prompt_ext and text_prompt blocks + * definitions. + */ +const TEXT_PROMPT_COMMON = { /** * Modify this block to have the correct output type. * @param {string} newOp Either 'TEXT' or 'NUMBER'. * @private - * @this {Blockly.Block} + * @this {Block} */ updateType_: function(newOp) { - this.outputConnection.setCheck(newOp == 'NUMBER' ? 'Number' : 'String'); + this.outputConnection.setCheck(newOp === 'NUMBER' ? 'Number' : 'String'); }, /** * Create XML to represent the output type. + * Backwards compatible serialization implementation. * @return {!Element} XML storage element. - * @this {Blockly.Block} + * @this {Block} */ mutationToDom: function() { - var container = Blockly.utils.xml.createElement('mutation'); + const container = xmlUtils.createElement('mutation'); container.setAttribute('type', this.getFieldValue('TYPE')); return container; }, /** * Parse XML to restore the output type. + * Backwards compatible serialization implementation. * @param {!Element} xmlElement XML storage element. - * @this {Blockly.Block} + * @this {Block} */ domToMutation: function(xmlElement) { this.updateType_(xmlElement.getAttribute('type')); - } + }, +}; + +Blocks['text_prompt_ext'] = { + ...TEXT_PROMPT_COMMON, + /** + * Block for prompt function (external message). + * @this {Block} + */ + init: function() { + const TYPES = [ + [Msg['TEXT_PROMPT_TYPE_TEXT'], 'TEXT'], + [Msg['TEXT_PROMPT_TYPE_NUMBER'], 'NUMBER'], + ]; + this.setHelpUrl(Msg['TEXT_PROMPT_HELPURL']); + this.setStyle('text_blocks'); + // Assign 'this' to a variable for use in the closures below. + const thisBlock = this; + const dropdown = new FieldDropdown(TYPES, function(newOp) { + thisBlock.updateType_(newOp); + }); + this.appendValueInput('TEXT').appendField(dropdown, 'TYPE'); + this.setOutput(true, 'String'); + this.setTooltip(function() { + return (thisBlock.getFieldValue('TYPE') === 'TEXT') ? + Msg['TEXT_PROMPT_TOOLTIP_TEXT'] : + Msg['TEXT_PROMPT_TOOLTIP_NUMBER']; + }); + }, + + // This block does not need JSO serialization hooks (saveExtraState and + // loadExtraState) because the state of this object is already encoded in the + // dropdown values. + // XML hooks are kept for backwards compatibility. }; -Blockly.Blocks['text_prompt'] = { +Blocks['text_prompt'] = { + ...TEXT_PROMPT_COMMON, /** * Block for prompt function (internal message). * The 'text_prompt_ext' block is preferred as it is more flexible. - * @this {Blockly.Block} + * @this {Block} */ init: function() { - this.mixin(Blockly.Constants.Text.QUOTE_IMAGE_MIXIN); - var TYPES = [ - [Blockly.Msg['TEXT_PROMPT_TYPE_TEXT'], 'TEXT'], - [Blockly.Msg['TEXT_PROMPT_TYPE_NUMBER'], 'NUMBER'] + this.mixin(QUOTE_IMAGE_MIXIN); + const TYPES = [ + [Msg['TEXT_PROMPT_TYPE_TEXT'], 'TEXT'], + [Msg['TEXT_PROMPT_TYPE_NUMBER'], 'NUMBER'], ]; // Assign 'this' to a variable for use in the closures below. - var thisBlock = this; - this.setHelpUrl(Blockly.Msg['TEXT_PROMPT_HELPURL']); + const thisBlock = this; + this.setHelpUrl(Msg['TEXT_PROMPT_HELPURL']); this.setStyle('text_blocks'); - var dropdown = new Blockly.FieldDropdown(TYPES, function(newOp) { + const dropdown = new FieldDropdown(TYPES, function(newOp) { thisBlock.updateType_(newOp); }); this.appendDummyInput() .appendField(dropdown, 'TYPE') .appendField(this.newQuote_(true)) - .appendField(new Blockly.FieldTextInput(''), 'TEXT') + .appendField(new FieldTextInput(''), 'TEXT') .appendField(this.newQuote_(false)); this.setOutput(true, 'String'); this.setTooltip(function() { - return (thisBlock.getFieldValue('TYPE') == 'TEXT') ? - Blockly.Msg['TEXT_PROMPT_TOOLTIP_TEXT'] : - Blockly.Msg['TEXT_PROMPT_TOOLTIP_NUMBER']; + return (thisBlock.getFieldValue('TYPE') === 'TEXT') ? + Msg['TEXT_PROMPT_TOOLTIP_TEXT'] : + Msg['TEXT_PROMPT_TOOLTIP_NUMBER']; }); }, - updateType_: Blockly.Blocks['text_prompt_ext'].updateType_, - mutationToDom: Blockly.Blocks['text_prompt_ext'].mutationToDom, - domToMutation: Blockly.Blocks['text_prompt_ext'].domToMutation }; -Blockly.Blocks['text_count'] = { +Blocks['text_count'] = { /** * Block for counting how many times one string appears within another string. - * @this {Blockly.Block} + * @this {Block} */ init: function() { this.jsonInit({ - "message0": Blockly.Msg['TEXT_COUNT_MESSAGE0'], - "args0": [ + 'message0': Msg['TEXT_COUNT_MESSAGE0'], + 'args0': [ { - "type": "input_value", - "name": "SUB", - "check": "String" + 'type': 'input_value', + 'name': 'SUB', + 'check': 'String', }, { - "type": "input_value", - "name": "TEXT", - "check": "String" - } + 'type': 'input_value', + 'name': 'TEXT', + 'check': 'String', + }, ], - "output": "Number", - "inputsInline": true, - "style": "text_blocks", - "tooltip": Blockly.Msg['TEXT_COUNT_TOOLTIP'], - "helpUrl": Blockly.Msg['TEXT_COUNT_HELPURL'] + 'output': 'Number', + 'inputsInline': true, + 'style': 'text_blocks', + 'tooltip': Msg['TEXT_COUNT_TOOLTIP'], + 'helpUrl': Msg['TEXT_COUNT_HELPURL'], }); - } + }, }; -Blockly.Blocks['text_replace'] = { +Blocks['text_replace'] = { /** * Block for replacing one string with another in the text. - * @this {Blockly.Block} + * @this {Block} */ init: function() { this.jsonInit({ - "message0": Blockly.Msg['TEXT_REPLACE_MESSAGE0'], - "args0": [ + 'message0': Msg['TEXT_REPLACE_MESSAGE0'], + 'args0': [ { - "type": "input_value", - "name": "FROM", - "check": "String" + 'type': 'input_value', + 'name': 'FROM', + 'check': 'String', }, { - "type": "input_value", - "name": "TO", - "check": "String" + 'type': 'input_value', + 'name': 'TO', + 'check': 'String', }, { - "type": "input_value", - "name": "TEXT", - "check": "String" - } + 'type': 'input_value', + 'name': 'TEXT', + 'check': 'String', + }, ], - "output": "String", - "inputsInline": true, - "style": "text_blocks", - "tooltip": Blockly.Msg['TEXT_REPLACE_TOOLTIP'], - "helpUrl": Blockly.Msg['TEXT_REPLACE_HELPURL'] + 'output': 'String', + 'inputsInline': true, + 'style': 'text_blocks', + 'tooltip': Msg['TEXT_REPLACE_TOOLTIP'], + 'helpUrl': Msg['TEXT_REPLACE_HELPURL'], }); - } + }, }; -Blockly.Blocks['text_reverse'] = { +Blocks['text_reverse'] = { /** * Block for reversing a string. - * @this {Blockly.Block} + * @this {Block} */ init: function() { this.jsonInit({ - "message0": Blockly.Msg['TEXT_REVERSE_MESSAGE0'], - "args0": [ + 'message0': Msg['TEXT_REVERSE_MESSAGE0'], + 'args0': [ { - "type": "input_value", - "name": "TEXT", - "check": "String" - } + 'type': 'input_value', + 'name': 'TEXT', + 'check': 'String', + }, ], - "output": "String", - "inputsInline": true, - "style": "text_blocks", - "tooltip": Blockly.Msg['TEXT_REVERSE_TOOLTIP'], - "helpUrl": Blockly.Msg['TEXT_REVERSE_HELPURL'] + 'output': 'String', + 'inputsInline': true, + 'style': 'text_blocks', + 'tooltip': Msg['TEXT_REVERSE_TOOLTIP'], + 'helpUrl': Msg['TEXT_REVERSE_HELPURL'], }); - } + }, }; /** - * * @mixin * @package * @readonly */ -Blockly.Constants.Text.QUOTE_IMAGE_MIXIN = { +const QUOTE_IMAGE_MIXIN = { /** - * Image data URI of an LTR opening double quote (same as RTL closing double quote). + * Image data URI of an LTR opening double quote (same as RTL closing double + * quote). * @readonly */ QUOTE_IMAGE_LEFT_DATAURI: - 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAKCAQAAAAqJXdxAAAA' + - 'n0lEQVQI1z3OMa5BURSF4f/cQhAKjUQhuQmFNwGJEUi0RKN5rU7FHKhpjEH3TEMtkdBSCY' + - '1EIv8r7nFX9e29V7EBAOvu7RPjwmWGH/VuF8CyN9/OAdvqIXYLvtRaNjx9mMTDyo+NjAN1' + - 'HNcl9ZQ5oQMM3dgDUqDo1l8DzvwmtZN7mnD+PkmLa+4mhrxVA9fRowBWmVBhFy5gYEjKMf' + - 'z9AylsaRRgGzvZAAAAAElFTkSuQmCC', + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAKCAQAAAAqJXdxAAAA' + + 'n0lEQVQI1z3OMa5BURSF4f/cQhAKjUQhuQmFNwGJEUi0RKN5rU7FHKhpjEH3TEMtkdBSCY' + + '1EIv8r7nFX9e29V7EBAOvu7RPjwmWGH/VuF8CyN9/OAdvqIXYLvtRaNjx9mMTDyo+NjAN1' + + 'HNcl9ZQ5oQMM3dgDUqDo1l8DzvwmtZN7mnD+PkmLa+4mhrxVA9fRowBWmVBhFy5gYEjKMf' + + 'z9AylsaRRgGzvZAAAAAElFTkSuQmCC', /** - * Image data URI of an LTR closing double quote (same as RTL opening double quote). + * Image data URI of an LTR closing double quote (same as RTL opening double + * quote). * @readonly */ QUOTE_IMAGE_RIGHT_DATAURI: - 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAKCAQAAAAqJXdxAAAA' + - 'qUlEQVQI1z3KvUpCcRiA8ef9E4JNHhI0aFEacm1o0BsI0Slx8wa8gLauoDnoBhq7DcfWhg' + - 'gONDmJJgqCPA7neJ7p934EOOKOnM8Q7PDElo/4x4lFb2DmuUjcUzS3URnGib9qaPNbuXvB' + - 'O3sGPHJDRG6fGVdMSeWDP2q99FQdFrz26Gu5Tq7dFMzUvbXy8KXeAj57cOklgA+u1B5Aos' + - 'lLtGIHQMaCVnwDnADZIFIrXsoXrgAAAABJRU5ErkJggg==', + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAKCAQAAAAqJXdxAAAA' + + 'qUlEQVQI1z3KvUpCcRiA8ef9E4JNHhI0aFEacm1o0BsI0Slx8wa8gLauoDnoBhq7DcfWhg' + + 'gONDmJJgqCPA7neJ7p934EOOKOnM8Q7PDElo/4x4lFb2DmuUjcUzS3URnGib9qaPNbuXvB' + + 'O3sGPHJDRG6fGVdMSeWDP2q99FQdFrz26Gu5Tq7dFMzUvbXy8KXeAj57cOklgA+u1B5Aos' + + 'lLtGIHQMaCVnwDnADZIFIrXsoXrgAAAABJRU5ErkJggg==', /** * Pixel width of QUOTE_IMAGE_LEFT_DATAURI and QUOTE_IMAGE_RIGHT_DATAURI. * @readonly @@ -624,19 +660,20 @@ Blockly.Constants.Text.QUOTE_IMAGE_MIXIN = { /** * Inserts appropriate quote images before and after the named field. * @param {string} fieldName The name of the field to wrap with quotes. - * @this {Blockly.Block} + * @this {Block} */ quoteField_: function(fieldName) { - for (var i = 0, input; (input = this.inputList[i]); i++) { - for (var j = 0, field; (field = input.fieldRow[j]); j++) { - if (fieldName == field.name) { + for (let i = 0, input; (input = this.inputList[i]); i++) { + for (let j = 0, field; (field = input.fieldRow[j]); j++) { + if (fieldName === field.name) { input.insertFieldAt(j, this.newQuote_(true)); input.insertFieldAt(j + 2, this.newQuote_(false)); return; } } } - console.warn('field named "' + fieldName + '" not found in ' + this.toDevString()); + console.warn( + 'field named "' + fieldName + '" not found in ' + this.toDevString()); }, /** @@ -644,69 +681,85 @@ Blockly.Constants.Text.QUOTE_IMAGE_MIXIN = { * closing double quote. The selected quote will be adapted for RTL blocks. * @param {boolean} open If the image should be open quote (“ in LTR). * Otherwise, a closing quote is used (” in LTR). - * @return {!Blockly.FieldImage} The new field. - * @this {Blockly.Block} + * @return {!FieldImage} The new field. + * @this {Block} */ newQuote_: function(open) { - var isLeft = this.RTL ? !open : open; - var dataUri = isLeft ? - this.QUOTE_IMAGE_LEFT_DATAURI : - this.QUOTE_IMAGE_RIGHT_DATAURI; - return new Blockly.FieldImage( - dataUri, - this.QUOTE_IMAGE_WIDTH, - this.QUOTE_IMAGE_HEIGHT, + const isLeft = this.RTL ? !open : open; + const dataUri = + isLeft ? this.QUOTE_IMAGE_LEFT_DATAURI : this.QUOTE_IMAGE_RIGHT_DATAURI; + return new FieldImage( + dataUri, this.QUOTE_IMAGE_WIDTH, this.QUOTE_IMAGE_HEIGHT, isLeft ? '\u201C' : '\u201D'); - } + }, }; /** * Wraps TEXT field with images of double quote characters. - * @this {Blockly.Block} + * @this {Block} */ -Blockly.Constants.Text.TEXT_QUOTES_EXTENSION = function() { - this.mixin(Blockly.Constants.Text.QUOTE_IMAGE_MIXIN); +const TEXT_QUOTES_EXTENSION = function() { + this.mixin(QUOTE_IMAGE_MIXIN); this.quoteField_('TEXT'); }; /** * Mixin for mutator functions in the 'text_join_mutator' extension. * @mixin - * @augments Blockly.Block + * @augments Block * @package */ -Blockly.Constants.Text.TEXT_JOIN_MUTATOR_MIXIN = { +const TEXT_JOIN_MUTATOR_MIXIN = { /** * Create XML to represent number of text inputs. + * Backwards compatible serialization implementation. * @return {!Element} XML storage element. - * @this {Blockly.Block} + * @this {Block} */ mutationToDom: function() { - var container = Blockly.utils.xml.createElement('mutation'); + const container = xmlUtils.createElement('mutation'); container.setAttribute('items', this.itemCount_); return container; }, /** * Parse XML to restore the text inputs. + * Backwards compatible serialization implementation. * @param {!Element} xmlElement XML storage element. - * @this {Blockly.Block} + * @this {Block} */ domToMutation: function(xmlElement) { this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10); this.updateShape_(); }, + /** + * Returns the state of this block as a JSON serializable object. + * @return {{itemCount: number}} The state of this block, ie the item count. + */ + saveExtraState: function() { + return { + 'itemCount': this.itemCount_, + }; + }, + /** + * Applies the given state to this block. + * @param {*} state The state to apply to this block, ie the item count. + */ + loadExtraState: function(state) { + this.itemCount_ = state['itemCount']; + this.updateShape_(); + }, /** * Populate the mutator's dialog with this block's components. - * @param {!Blockly.Workspace} workspace Mutator's workspace. - * @return {!Blockly.Block} Root block in mutator. - * @this {Blockly.Block} + * @param {!Workspace} workspace Mutator's workspace. + * @return {!Block} Root block in mutator. + * @this {Block} */ decompose: function(workspace) { - var containerBlock = workspace.newBlock('text_create_join_container'); + const containerBlock = workspace.newBlock('text_create_join_container'); containerBlock.initSvg(); - var connection = containerBlock.getInput('STACK').connection; - for (var i = 0; i < this.itemCount_; i++) { - var itemBlock = workspace.newBlock('text_create_join_item'); + let connection = containerBlock.getInput('STACK').connection; + for (let i = 0; i < this.itemCount_; i++) { + const itemBlock = workspace.newBlock('text_create_join_item'); itemBlock.initSvg(); connection.connect(itemBlock.previousConnection); connection = itemBlock.nextConnection; @@ -715,52 +768,52 @@ Blockly.Constants.Text.TEXT_JOIN_MUTATOR_MIXIN = { }, /** * Reconfigure this block based on the mutator dialog's components. - * @param {!Blockly.Block} containerBlock Root block in mutator. - * @this {Blockly.Block} + * @param {!Block} containerBlock Root block in mutator. + * @this {Block} */ compose: function(containerBlock) { - var itemBlock = containerBlock.getInputTargetBlock('STACK'); + let itemBlock = containerBlock.getInputTargetBlock('STACK'); // Count number of inputs. - var connections = []; + const connections = []; while (itemBlock && !itemBlock.isInsertionMarker()) { connections.push(itemBlock.valueConnection_); - itemBlock = itemBlock.nextConnection && - itemBlock.nextConnection.targetBlock(); + itemBlock = + itemBlock.nextConnection && itemBlock.nextConnection.targetBlock(); } // Disconnect any children that don't belong. - for (var i = 0; i < this.itemCount_; i++) { - var connection = this.getInput('ADD' + i).connection.targetConnection; - if (connection && connections.indexOf(connection) == -1) { + for (let i = 0; i < this.itemCount_; i++) { + const connection = this.getInput('ADD' + i).connection.targetConnection; + if (connection && connections.indexOf(connection) === -1) { connection.disconnect(); } } this.itemCount_ = connections.length; this.updateShape_(); // Reconnect any child blocks. - for (var i = 0; i < this.itemCount_; i++) { - Blockly.Mutator.reconnect(connections[i], this, 'ADD' + i); + for (let i = 0; i < this.itemCount_; i++) { + Mutator.reconnect(connections[i], this, 'ADD' + i); } }, /** * Store pointers to any connected child blocks. - * @param {!Blockly.Block} containerBlock Root block in mutator. - * @this {Blockly.Block} + * @param {!Block} containerBlock Root block in mutator. + * @this {Block} */ saveConnections: function(containerBlock) { - var itemBlock = containerBlock.getInputTargetBlock('STACK'); - var i = 0; + let itemBlock = containerBlock.getInputTargetBlock('STACK'); + let i = 0; while (itemBlock) { - var input = this.getInput('ADD' + i); + const input = this.getInput('ADD' + i); itemBlock.valueConnection_ = input && input.connection.targetConnection; + itemBlock = + itemBlock.nextConnection && itemBlock.nextConnection.targetBlock(); i++; - itemBlock = itemBlock.nextConnection && - itemBlock.nextConnection.targetBlock(); } }, /** * Modify this block to have the correct number of inputs. * @private - * @this {Blockly.Block} + * @this {Block} */ updateShape_: function() { if (this.itemCount_ && this.getInput('EMPTY')) { @@ -771,88 +824,94 @@ Blockly.Constants.Text.TEXT_JOIN_MUTATOR_MIXIN = { .appendField(this.newQuote_(false)); } // Add new inputs. - for (var i = 0; i < this.itemCount_; i++) { + for (let i = 0; i < this.itemCount_; i++) { if (!this.getInput('ADD' + i)) { - var input = this.appendValueInput('ADD' + i) - .setAlign(Blockly.ALIGN_RIGHT); - if (i == 0) { - input.appendField(Blockly.Msg['TEXT_JOIN_TITLE_CREATEWITH']); + const input = this.appendValueInput('ADD' + i).setAlign(Align.RIGHT); + if (i === 0) { + input.appendField(Msg['TEXT_JOIN_TITLE_CREATEWITH']); } } } // Remove deleted inputs. - while (this.getInput('ADD' + i)) { + for (let i = this.itemCount_; this.getInput('ADD' + i); i++) { this.removeInput('ADD' + i); - i++; } - } + }, }; /** * Performs final setup of a text_join block. - * @this {Blockly.Block} + * @this {Block} */ -Blockly.Constants.Text.TEXT_JOIN_EXTENSION = function() { +const TEXT_JOIN_EXTENSION = function() { // Add the quote mixin for the itemCount_ = 0 case. - this.mixin(Blockly.Constants.Text.QUOTE_IMAGE_MIXIN); + this.mixin(QUOTE_IMAGE_MIXIN); // Initialize the mutator values. this.itemCount_ = 2; this.updateShape_(); // Configure the mutator UI. - this.setMutator(new Blockly.Mutator(['text_create_join_item'])); + this.setMutator(new Mutator(['text_create_join_item'])); }; // Update the tooltip of 'text_append' block to reference the variable. -Blockly.Extensions.register('text_append_tooltip', - Blockly.Extensions.buildTooltipWithFieldText( - '%{BKY_TEXT_APPEND_TOOLTIP}', 'VAR')); +Extensions.register( + 'text_append_tooltip', + Extensions.buildTooltipWithFieldText('%{BKY_TEXT_APPEND_TOOLTIP}', 'VAR')); /** * Update the tooltip of 'text_append' block to reference the variable. - * @this {Blockly.Block} + * @this {Block} */ -Blockly.Constants.Text.TEXT_INDEXOF_TOOLTIP_EXTENSION = function() { +const TEXT_INDEXOF_TOOLTIP_EXTENSION = function() { // Assign 'this' to a variable for use in the tooltip closure below. - var thisBlock = this; + const thisBlock = this; this.setTooltip(function() { - return Blockly.Msg['TEXT_INDEXOF_TOOLTIP'].replace('%1', - thisBlock.workspace.options.oneBasedIndex ? '0' : '-1'); + return Msg['TEXT_INDEXOF_TOOLTIP'].replace( + '%1', thisBlock.workspace.options.oneBasedIndex ? '0' : '-1'); }); }; /** * Mixin for mutator functions in the 'text_charAt_mutator' extension. * @mixin - * @augments Blockly.Block + * @augments Block * @package */ -Blockly.Constants.Text.TEXT_CHARAT_MUTATOR_MIXIN = { +const TEXT_CHARAT_MUTATOR_MIXIN = { /** * Create XML to represent whether there is an 'AT' input. + * Backwards compatible serialization implementation. * @return {!Element} XML storage element. - * @this {Blockly.Block} + * @this {Block} */ mutationToDom: function() { - var container = Blockly.utils.xml.createElement('mutation'); + const container = xmlUtils.createElement('mutation'); container.setAttribute('at', !!this.isAt_); return container; }, /** * Parse XML to restore the 'AT' input. + * Backwards compatible serialization implementation. * @param {!Element} xmlElement XML storage element. - * @this {Blockly.Block} + * @this {Block} */ domToMutation: function(xmlElement) { // Note: Until January 2013 this block did not have mutations, // so 'at' defaults to true. - var isAt = (xmlElement.getAttribute('at') != 'false'); + const isAt = (xmlElement.getAttribute('at') !== 'false'); this.updateAt_(isAt); }, + + // This block does not need JSO serialization hooks (saveExtraState and + // loadExtraState) because the state of this object is already encoded in the + // dropdown values. + // XML hooks are kept for backwards compatibility. + /** * Create or delete an input for the numeric index. * @param {boolean} isAt True if the input should exist. * @private - * @this {Blockly.Block} + * @this {Block} */ updateAt_: function(isAt) { // Destroy old 'AT' and 'ORDINAL' inputs. @@ -861,63 +920,64 @@ Blockly.Constants.Text.TEXT_CHARAT_MUTATOR_MIXIN = { // Create either a value 'AT' input or a dummy input. if (isAt) { this.appendValueInput('AT').setCheck('Number'); - if (Blockly.Msg['ORDINAL_NUMBER_SUFFIX']) { - this.appendDummyInput('ORDINAL') - .appendField(Blockly.Msg['ORDINAL_NUMBER_SUFFIX']); + if (Msg['ORDINAL_NUMBER_SUFFIX']) { + this.appendDummyInput('ORDINAL').appendField( + Msg['ORDINAL_NUMBER_SUFFIX']); } } - if (Blockly.Msg['TEXT_CHARAT_TAIL']) { + if (Msg['TEXT_CHARAT_TAIL']) { this.removeInput('TAIL', true); - this.appendDummyInput('TAIL') - .appendField(Blockly.Msg['TEXT_CHARAT_TAIL']); + this.appendDummyInput('TAIL').appendField(Msg['TEXT_CHARAT_TAIL']); } this.isAt_ = isAt; - } + }, }; /** * Does the initial mutator update of text_charAt and adds the tooltip - * @this {Blockly.Block} + * @this {Block} */ -Blockly.Constants.Text.TEXT_CHARAT_EXTENSION = function() { - var dropdown = this.getField('WHERE'); - dropdown.setValidator(function(value) { - var newAt = (value == 'FROM_START') || (value == 'FROM_END'); - if (newAt != this.isAt_) { - var block = this.getSourceBlock(); - block.updateAt_(newAt); - } - }); +const TEXT_CHARAT_EXTENSION = function() { + const dropdown = this.getField('WHERE'); + dropdown.setValidator( + /** + * @param {*} value The input value. + * @this {FieldDropdown} + */ + function(value) { + const newAt = (value === 'FROM_START') || (value === 'FROM_END'); + if (newAt !== this.isAt_) { + const block = this.getSourceBlock(); + block.updateAt_(newAt); + } + }); this.updateAt_(true); // Assign 'this' to a variable for use in the tooltip closure below. - var thisBlock = this; + const thisBlock = this; this.setTooltip(function() { - var where = thisBlock.getFieldValue('WHERE'); - var tooltip = Blockly.Msg['TEXT_CHARAT_TOOLTIP']; - if (where == 'FROM_START' || where == 'FROM_END') { - var msg = (where == 'FROM_START') ? - Blockly.Msg['LISTS_INDEX_FROM_START_TOOLTIP'] : - Blockly.Msg['LISTS_INDEX_FROM_END_TOOLTIP']; + const where = thisBlock.getFieldValue('WHERE'); + let tooltip = Msg['TEXT_CHARAT_TOOLTIP']; + if (where === 'FROM_START' || where === 'FROM_END') { + const msg = (where === 'FROM_START') ? + Msg['LISTS_INDEX_FROM_START_TOOLTIP'] : + Msg['LISTS_INDEX_FROM_END_TOOLTIP']; if (msg) { - tooltip += ' ' + msg.replace('%1', - thisBlock.workspace.options.oneBasedIndex ? '#1' : '#0'); + tooltip += ' ' + + msg.replace( + '%1', thisBlock.workspace.options.oneBasedIndex ? '#1' : '#0'); } } return tooltip; }); }; -Blockly.Extensions.register('text_indexOf_tooltip', - Blockly.Constants.Text.TEXT_INDEXOF_TOOLTIP_EXTENSION); +Extensions.register('text_indexOf_tooltip', TEXT_INDEXOF_TOOLTIP_EXTENSION); -Blockly.Extensions.register('text_quotes', - Blockly.Constants.Text.TEXT_QUOTES_EXTENSION); +Extensions.register('text_quotes', TEXT_QUOTES_EXTENSION); -Blockly.Extensions.registerMutator('text_join_mutator', - Blockly.Constants.Text.TEXT_JOIN_MUTATOR_MIXIN, - Blockly.Constants.Text.TEXT_JOIN_EXTENSION); +Extensions.registerMutator( + 'text_join_mutator', TEXT_JOIN_MUTATOR_MIXIN, TEXT_JOIN_EXTENSION); -Blockly.Extensions.registerMutator('text_charAt_mutator', - Blockly.Constants.Text.TEXT_CHARAT_MUTATOR_MIXIN, - Blockly.Constants.Text.TEXT_CHARAT_EXTENSION); +Extensions.registerMutator( + 'text_charAt_mutator', TEXT_CHARAT_MUTATOR_MIXIN, TEXT_CHARAT_EXTENSION); diff --git a/blocks/variables.js b/blocks/variables.js index 65967f921b1..c9e4720fafd 100644 --- a/blocks/variables.js +++ b/blocks/variables.js @@ -6,158 +6,158 @@ /** * @fileoverview Variable blocks for Blockly. - - * This file is scraped to extract a .json file of block definitions. The array - * passed to defineBlocksWithJsonArray(..) must be strict JSON: double quotes - * only, no outside references, no functions, no trailing commas, etc. The one - * exception is end-of-line comments, which the scraper will remove. - * @author fraser@google.com (Neil Fraser) + * @suppress {checkTypes} */ 'use strict'; -goog.provide('Blockly.Blocks.variables'); // Deprecated. -goog.provide('Blockly.Constants.Variables'); +goog.module('Blockly.blocks.variables'); -goog.require('Blockly'); -goog.require('Blockly.Blocks'); +const ContextMenu = goog.require('Blockly.ContextMenu'); +const Extensions = goog.require('Blockly.Extensions'); +const Variables = goog.require('Blockly.Variables'); +const xmlUtils = goog.require('Blockly.utils.xml'); +/* eslint-disable-next-line no-unused-vars */ +const {Block} = goog.requireType('Blockly.Block'); +const {Msg} = goog.require('Blockly.Msg'); +const {defineBlocksWithJsonArray} = goog.require('Blockly.common'); +/** @suppress {extraRequire} */ goog.require('Blockly.FieldLabel'); +/** @suppress {extraRequire} */ goog.require('Blockly.FieldVariable'); -/** - * Unused constant for the common HSV hue for all blocks in this category. - * @deprecated Use Blockly.Msg['VARIABLES_HUE']. (2018 April 5) - */ -Blockly.Constants.Variables.HUE = 330; - -Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT +defineBlocksWithJsonArray([ // Block for variable getter. { - "type": "variables_get", - "message0": "%1", - "args0": [ + 'type': 'variables_get', + 'message0': '%1', + 'args0': [ { - "type": "field_variable", - "name": "VAR", - "variable": "%{BKY_VARIABLES_DEFAULT_NAME}" - } + 'type': 'field_variable', + 'name': 'VAR', + 'variable': '%{BKY_VARIABLES_DEFAULT_NAME}', + }, ], - "output": null, - "style": "variable_blocks", - "helpUrl": "%{BKY_VARIABLES_GET_HELPURL}", - "tooltip": "%{BKY_VARIABLES_GET_TOOLTIP}", - "extensions": ["contextMenu_variableSetterGetter"] + 'output': null, + 'style': 'variable_blocks', + 'helpUrl': '%{BKY_VARIABLES_GET_HELPURL}', + 'tooltip': '%{BKY_VARIABLES_GET_TOOLTIP}', + 'extensions': ['contextMenu_variableSetterGetter'], }, // Block for variable setter. { - "type": "variables_set", - "message0": "%{BKY_VARIABLES_SET}", - "args0": [ + 'type': 'variables_set', + 'message0': '%{BKY_VARIABLES_SET}', + 'args0': [ { - "type": "field_variable", - "name": "VAR", - "variable": "%{BKY_VARIABLES_DEFAULT_NAME}" + 'type': 'field_variable', + 'name': 'VAR', + 'variable': '%{BKY_VARIABLES_DEFAULT_NAME}', }, { - "type": "input_value", - "name": "VALUE" - } + 'type': 'input_value', + 'name': 'VALUE', + }, ], - "previousStatement": null, - "nextStatement": null, - "style": "variable_blocks", - "tooltip": "%{BKY_VARIABLES_SET_TOOLTIP}", - "helpUrl": "%{BKY_VARIABLES_SET_HELPURL}", - "extensions": ["contextMenu_variableSetterGetter"] - } -]); // END JSON EXTRACT (Do not delete this comment.) + 'previousStatement': null, + 'nextStatement': null, + 'style': 'variable_blocks', + 'tooltip': '%{BKY_VARIABLES_SET_TOOLTIP}', + 'helpUrl': '%{BKY_VARIABLES_SET_HELPURL}', + 'extensions': ['contextMenu_variableSetterGetter'], + }, +]); /** * Mixin to add context menu items to create getter/setter blocks for this * setter/getter. * Used by blocks 'variables_set' and 'variables_get'. * @mixin - * @augments Blockly.Block + * @augments Block * @package * @readonly */ -Blockly.Constants.Variables.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { +const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { /** * Add menu option to create getter/setter block for this setter/getter. * @param {!Array} options List of menu options to add to. - * @this {Blockly.Block} + * @this {Block} */ customContextMenu: function(options) { if (!this.isInFlyout) { + let oppositeType; + let contextMenuMsg; // Getter blocks have the option to create a setter block, and vice versa. - if (this.type == 'variables_get') { - var opposite_type = 'variables_set'; - var contextMenuMsg = Blockly.Msg['VARIABLES_GET_CREATE_SET']; + if (this.type === 'variables_get') { + oppositeType = 'variables_set'; + contextMenuMsg = Msg['VARIABLES_GET_CREATE_SET']; } else { - var opposite_type = 'variables_get'; - var contextMenuMsg = Blockly.Msg['VARIABLES_SET_CREATE_GET']; + oppositeType = 'variables_get'; + contextMenuMsg = Msg['VARIABLES_SET_CREATE_GET']; } - var option = {enabled: this.workspace.remainingCapacity() > 0}; - var name = this.getField('VAR').getText(); + const option = {enabled: this.workspace.remainingCapacity() > 0}; + const name = this.getField('VAR').getText(); option.text = contextMenuMsg.replace('%1', name); - var xmlField = Blockly.utils.xml.createElement('field'); + const xmlField = xmlUtils.createElement('field'); xmlField.setAttribute('name', 'VAR'); - xmlField.appendChild(Blockly.utils.xml.createTextNode(name)); - var xmlBlock = Blockly.utils.xml.createElement('block'); - xmlBlock.setAttribute('type', opposite_type); + xmlField.appendChild(xmlUtils.createTextNode(name)); + const xmlBlock = xmlUtils.createElement('block'); + xmlBlock.setAttribute('type', oppositeType); xmlBlock.appendChild(xmlField); - option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock); + option.callback = ContextMenu.callbackFactory(this, xmlBlock); options.push(option); // Getter blocks have the option to rename or delete that variable. } else { - if (this.type == 'variables_get' || this.type == 'variables_get_reporter') { - var renameOption = { - text: Blockly.Msg.RENAME_VARIABLE, + if (this.type === 'variables_get' || + this.type === 'variables_get_reporter') { + const renameOption = { + text: Msg['RENAME_VARIABLE'], enabled: true, - callback: Blockly.Constants.Variables.RENAME_OPTION_CALLBACK_FACTORY(this) + callback: renameOptionCallbackFactory(this), }; - var name = this.getField('VAR').getText(); - var deleteOption = { - text: Blockly.Msg.DELETE_VARIABLE.replace('%1', name), + const name = this.getField('VAR').getText(); + const deleteOption = { + text: Msg['DELETE_VARIABLE'].replace('%1', name), enabled: true, - callback: Blockly.Constants.Variables.DELETE_OPTION_CALLBACK_FACTORY(this) + callback: deleteOptionCallbackFactory(this), }; options.unshift(renameOption); options.unshift(deleteOption); } } - } + }, }; /** - * Callback for rename variable dropdown menu option associated with a - * variable getter block. - * @param {!Blockly.Block} block The block with the variable to rename. - * @return {!function()} A function that renames the variable. - */ -Blockly.Constants.Variables.RENAME_OPTION_CALLBACK_FACTORY = function(block) { + * Factory for callbacks for rename variable dropdown menu option + * associated with a variable getter block. + * @param {!Block} block The block with the variable to rename. + * @return {!function()} A function that renames the variable. + */ +const renameOptionCallbackFactory = function(block) { return function() { - var workspace = block.workspace; - var variable = block.getField('VAR').getVariable(); - Blockly.Variables.renameVariable(workspace, variable); + const workspace = block.workspace; + const variable = block.getField('VAR').getVariable(); + Variables.renameVariable(workspace, variable); }; }; /** - * Callback for delete variable dropdown menu option associated with a - * variable getter block. - * @param {!Blockly.Block} block The block with the variable to delete. + * Factory for callbacks for delete variable dropdown menu option + * associated with a variable getter block. + * @param {!Block} block The block with the variable to delete. * @return {!function()} A function that deletes the variable. */ -Blockly.Constants.Variables.DELETE_OPTION_CALLBACK_FACTORY = function(block) { +const deleteOptionCallbackFactory = function(block) { return function() { - var workspace = block.workspace; - var variable = block.getField('VAR').getVariable(); + const workspace = block.workspace; + const variable = block.getField('VAR').getVariable(); workspace.deleteVariableById(variable.getId()); workspace.refreshToolboxSelection(); }; }; -Blockly.Extensions.registerMixin('contextMenu_variableSetterGetter', - Blockly.Constants.Variables.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN); +Extensions.registerMixin( + 'contextMenu_variableSetterGetter', + CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN); diff --git a/blocks/variables_dynamic.js b/blocks/variables_dynamic.js index de9a364fdd3..26a91af1ebc 100644 --- a/blocks/variables_dynamic.js +++ b/blocks/variables_dynamic.js @@ -6,124 +6,123 @@ /** * @fileoverview Variable blocks for Blockly. - - * This file is scraped to extract a .json file of block definitions. The array - * passed to defineBlocksWithJsonArray(..) must be strict JSON: double quotes - * only, no outside references, no functions, no trailing commas, etc. The one - * exception is end-of-line comments, which the scraper will remove. - * @author duzc2dtw@gmail.com (Du Tian Wei) + * @suppress {checkTypes} */ 'use strict'; -goog.provide('Blockly.Constants.VariablesDynamic'); +goog.module('Blockly.blocks.variablesDynamic'); -goog.require('Blockly'); -goog.require('Blockly.Blocks'); +/* eslint-disable-next-line no-unused-vars */ +const AbstractEvent = goog.requireType('Blockly.Events.Abstract'); +const ContextMenu = goog.require('Blockly.ContextMenu'); +const Extensions = goog.require('Blockly.Extensions'); +const Variables = goog.require('Blockly.Variables'); +const xml = goog.require('Blockly.utils.xml'); +/* eslint-disable-next-line no-unused-vars */ +const {Block} = goog.requireType('Blockly.Block'); +const {Msg} = goog.require('Blockly.Msg'); +const {defineBlocksWithJsonArray} = goog.require('Blockly.common'); +/** @suppress {extraRequire} */ goog.require('Blockly.FieldLabel'); +/** @suppress {extraRequire} */ goog.require('Blockly.FieldVariable'); -/** - * Unused constant for the common HSV hue for all blocks in this category. - * @deprecated Use Blockly.Msg['VARIABLES_DYNAMIC_HUE']. (2018 April 5) - */ -Blockly.Constants.VariablesDynamic.HUE = 310; - -Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT +defineBlocksWithJsonArray([ // Block for variable getter. { - "type": "variables_get_dynamic", - "message0": "%1", - "args0": [{ - "type": "field_variable", - "name": "VAR", - "variable": "%{BKY_VARIABLES_DEFAULT_NAME}" + 'type': 'variables_get_dynamic', + 'message0': '%1', + 'args0': [{ + 'type': 'field_variable', + 'name': 'VAR', + 'variable': '%{BKY_VARIABLES_DEFAULT_NAME}', }], - "output": null, - "style": "variable_dynamic_blocks", - "helpUrl": "%{BKY_VARIABLES_GET_HELPURL}", - "tooltip": "%{BKY_VARIABLES_GET_TOOLTIP}", - "extensions": ["contextMenu_variableDynamicSetterGetter"] + 'output': null, + 'style': 'variable_dynamic_blocks', + 'helpUrl': '%{BKY_VARIABLES_GET_HELPURL}', + 'tooltip': '%{BKY_VARIABLES_GET_TOOLTIP}', + 'extensions': ['contextMenu_variableDynamicSetterGetter'], }, // Block for variable setter. { - "type": "variables_set_dynamic", - "message0": "%{BKY_VARIABLES_SET}", - "args0": [{ - "type": "field_variable", - "name": "VAR", - "variable": "%{BKY_VARIABLES_DEFAULT_NAME}" - }, - { - "type": "input_value", - "name": "VALUE" - } + 'type': 'variables_set_dynamic', + 'message0': '%{BKY_VARIABLES_SET}', + 'args0': [ + { + 'type': 'field_variable', + 'name': 'VAR', + 'variable': '%{BKY_VARIABLES_DEFAULT_NAME}', + }, + { + 'type': 'input_value', + 'name': 'VALUE', + }, ], - "previousStatement": null, - "nextStatement": null, - "style": "variable_dynamic_blocks", - "tooltip": "%{BKY_VARIABLES_SET_TOOLTIP}", - "helpUrl": "%{BKY_VARIABLES_SET_HELPURL}", - "extensions": ["contextMenu_variableDynamicSetterGetter"] - } -]); // END JSON EXTRACT (Do not delete this comment.) + 'previousStatement': null, + 'nextStatement': null, + 'style': 'variable_dynamic_blocks', + 'tooltip': '%{BKY_VARIABLES_SET_TOOLTIP}', + 'helpUrl': '%{BKY_VARIABLES_SET_HELPURL}', + 'extensions': ['contextMenu_variableDynamicSetterGetter'], + }, +]); /** * Mixin to add context menu items to create getter/setter blocks for this * setter/getter. * Used by blocks 'variables_set_dynamic' and 'variables_get_dynamic'. * @mixin - * @augments Blockly.Block - * @package + * @augments Block * @readonly */ -Blockly.Constants.VariablesDynamic.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { +const CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { /** * Add menu option to create getter/setter block for this setter/getter. * @param {!Array} options List of menu options to add to. - * @this {Blockly.Block} + * @this {Block} */ customContextMenu: function(options) { // Getter blocks have the option to create a setter block, and vice versa. if (!this.isInFlyout) { - var opposite_type; - var contextMenuMsg; - var id = this.getFieldValue('VAR'); - var variableModel = this.workspace.getVariableById(id); - var varType = variableModel.type; - if (this.type == 'variables_get_dynamic') { - opposite_type = 'variables_set_dynamic'; - contextMenuMsg = Blockly.Msg['VARIABLES_GET_CREATE_SET']; + let oppositeType; + let contextMenuMsg; + const id = this.getFieldValue('VAR'); + const variableModel = this.workspace.getVariableById(id); + const varType = variableModel.type; + if (this.type === 'variables_get_dynamic') { + oppositeType = 'variables_set_dynamic'; + contextMenuMsg = Msg['VARIABLES_GET_CREATE_SET']; } else { - opposite_type = 'variables_get_dynamic'; - contextMenuMsg = Blockly.Msg['VARIABLES_SET_CREATE_GET']; + oppositeType = 'variables_get_dynamic'; + contextMenuMsg = Msg['VARIABLES_SET_CREATE_GET']; } - var option = {enabled: this.workspace.remainingCapacity() > 0}; - var name = this.getField('VAR').getText(); + const option = {enabled: this.workspace.remainingCapacity() > 0}; + const name = this.getField('VAR').getText(); option.text = contextMenuMsg.replace('%1', name); - var xmlField = Blockly.utils.xml.createElement('field'); + const xmlField = xml.createElement('field'); xmlField.setAttribute('name', 'VAR'); xmlField.setAttribute('variabletype', varType); - xmlField.appendChild(Blockly.utils.xml.createTextNode(name)); - var xmlBlock = Blockly.utils.xml.createElement('block'); - xmlBlock.setAttribute('type', opposite_type); + xmlField.appendChild(xml.createTextNode(name)); + const xmlBlock = xml.createElement('block'); + xmlBlock.setAttribute('type', oppositeType); xmlBlock.appendChild(xmlField); - option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock); + option.callback = ContextMenu.callbackFactory(this, xmlBlock); options.push(option); } else { - if (this.type == 'variables_get_dynamic' || - this.type == 'variables_get_reporter_dynamic') { - var renameOption = { - text: Blockly.Msg.RENAME_VARIABLE, + if (this.type === 'variables_get_dynamic' || + this.type === 'variables_get_reporter_dynamic') { + const renameOption = { + text: Msg['RENAME_VARIABLE'], enabled: true, - callback: Blockly.Constants.Variables.RENAME_OPTION_CALLBACK_FACTORY(this) + callback: renameOptionCallbackFactory(this), }; - var name = this.getField('VAR').getText(); - var deleteOption = { - text: Blockly.Msg.DELETE_VARIABLE.replace('%1', name), + const name = this.getField('VAR').getText(); + const deleteOption = { + text: Msg['DELETE_VARIABLE'].replace('%1', name), enabled: true, - callback: Blockly.Constants.Variables.DELETE_OPTION_CALLBACK_FACTORY(this) + callback: deleteOptionCallbackFactory(this), }; options.unshift(renameOption); options.unshift(deleteOption); @@ -133,48 +132,49 @@ Blockly.Constants.VariablesDynamic.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MI /** * Called whenever anything on the workspace changes. * Set the connection type for this block. - * @param {!Blockly.Events.Abstract} _e Change event. - * @this {Blockly.Block} + * @param {AbstractEvent} _e Change event. + * @this {Block} */ onchange: function(_e) { - var id = this.getFieldValue('VAR'); - var variableModel = Blockly.Variables.getVariable(this.workspace, id); - if (this.type == 'variables_get_dynamic') { + const id = this.getFieldValue('VAR'); + const variableModel = Variables.getVariable(this.workspace, id); + if (this.type === 'variables_get_dynamic') { this.outputConnection.setCheck(variableModel.type); } else { this.getInput('VALUE').connection.setCheck(variableModel.type); } - } + }, }; /** - * Callback for rename variable dropdown menu option associated with a - * variable getter block. - * @param {!Blockly.Block} block The block with the variable to rename. - * @return {!function()} A function that renames the variable. - */ -Blockly.Constants.VariablesDynamic.RENAME_OPTION_CALLBACK_FACTORY = function(block) { + * Factory for callbacks for rename variable dropdown menu option + * associated with a variable getter block. + * @param {!Block} block The block with the variable to rename. + * @return {!function()} A function that renames the variable. + */ +const renameOptionCallbackFactory = function(block) { return function() { - var workspace = block.workspace; - var variable = block.getField('VAR').getVariable(); - Blockly.Variables.renameVariable(workspace, variable); + const workspace = block.workspace; + const variable = block.getField('VAR').getVariable(); + Variables.renameVariable(workspace, variable); }; }; /** - * Callback for delete variable dropdown menu option associated with a - * variable getter block. - * @param {!Blockly.Block} block The block with the variable to delete. + * Factory for callbacks for delete variable dropdown menu option + * associated with a variable getter block. + * @param {!Block} block The block with the variable to delete. * @return {!function()} A function that deletes the variable. */ -Blockly.Constants.VariablesDynamic.DELETE_OPTION_CALLBACK_FACTORY = function(block) { +const deleteOptionCallbackFactory = function(block) { return function() { - var workspace = block.workspace; - var variable = block.getField('VAR').getVariable(); + const workspace = block.workspace; + const variable = block.getField('VAR').getVariable(); workspace.deleteVariableById(variable.getId()); workspace.refreshToolboxSelection(); }; }; -Blockly.Extensions.registerMixin('contextMenu_variableDynamicSetterGetter', - Blockly.Constants.VariablesDynamic.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN); +Extensions.registerMixin( + 'contextMenu_variableDynamicSetterGetter', + CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN); diff --git a/blocks_compressed.js b/blocks_compressed.js deleted file mode 100644 index 16024fdca26..00000000000 --- a/blocks_compressed.js +++ /dev/null @@ -1,193 +0,0 @@ -// Do not edit this file; automatically generated by gulp. - -/* eslint-disable */ -;(function(root, factory) { - if (typeof define === 'function' && define.amd) { // AMD - define(['./blockly_compressed.js'], factory); - } else if (typeof exports === 'object') { // Node.js - module.exports = factory(require('./blockly_compressed.js')); - } else { // Browser - root.Blockly.Blocks = factory(root.Blockly); - } -}(this, function(Blockly) { - /* - - Copyright 2012 Google LLC - SPDX-License-Identifier: Apache-2.0 -*/ -'use strict';Blockly.Blocks.colour={};Blockly.Constants={};Blockly.Constants.Colour={};Blockly.Constants.Colour.HUE=20; -Blockly.defineBlocksWithJsonArray([{type:"colour_picker",message0:"%1",args0:[{type:"field_colour",name:"COLOUR",colour:"#ff0000"}],output:"Colour",helpUrl:"%{BKY_COLOUR_PICKER_HELPURL}",style:"colour_blocks",tooltip:"%{BKY_COLOUR_PICKER_TOOLTIP}",extensions:["parent_tooltip_when_inline"]},{type:"colour_random",message0:"%{BKY_COLOUR_RANDOM_TITLE}",output:"Colour",helpUrl:"%{BKY_COLOUR_RANDOM_HELPURL}",style:"colour_blocks",tooltip:"%{BKY_COLOUR_RANDOM_TOOLTIP}"},{type:"colour_rgb",message0:"%{BKY_COLOUR_RGB_TITLE} %{BKY_COLOUR_RGB_RED} %1 %{BKY_COLOUR_RGB_GREEN} %2 %{BKY_COLOUR_RGB_BLUE} %3", -args0:[{type:"input_value",name:"RED",check:"Number",align:"RIGHT"},{type:"input_value",name:"GREEN",check:"Number",align:"RIGHT"},{type:"input_value",name:"BLUE",check:"Number",align:"RIGHT"}],output:"Colour",helpUrl:"%{BKY_COLOUR_RGB_HELPURL}",style:"colour_blocks",tooltip:"%{BKY_COLOUR_RGB_TOOLTIP}"},{type:"colour_blend",message0:"%{BKY_COLOUR_BLEND_TITLE} %{BKY_COLOUR_BLEND_COLOUR1} %1 %{BKY_COLOUR_BLEND_COLOUR2} %2 %{BKY_COLOUR_BLEND_RATIO} %3",args0:[{type:"input_value",name:"COLOUR1",check:"Colour", -align:"RIGHT"},{type:"input_value",name:"COLOUR2",check:"Colour",align:"RIGHT"},{type:"input_value",name:"RATIO",check:"Number",align:"RIGHT"}],output:"Colour",helpUrl:"%{BKY_COLOUR_BLEND_HELPURL}",style:"colour_blocks",tooltip:"%{BKY_COLOUR_BLEND_TOOLTIP}"}]);Blockly.Constants.Lists={};Blockly.Constants.Lists.HUE=260; -Blockly.defineBlocksWithJsonArray([{type:"lists_create_empty",message0:"%{BKY_LISTS_CREATE_EMPTY_TITLE}",output:"Array",style:"list_blocks",tooltip:"%{BKY_LISTS_CREATE_EMPTY_TOOLTIP}",helpUrl:"%{BKY_LISTS_CREATE_EMPTY_HELPURL}"},{type:"lists_repeat",message0:"%{BKY_LISTS_REPEAT_TITLE}",args0:[{type:"input_value",name:"ITEM"},{type:"input_value",name:"NUM",check:"Number"}],output:"Array",style:"list_blocks",tooltip:"%{BKY_LISTS_REPEAT_TOOLTIP}",helpUrl:"%{BKY_LISTS_REPEAT_HELPURL}"},{type:"lists_reverse", -message0:"%{BKY_LISTS_REVERSE_MESSAGE0}",args0:[{type:"input_value",name:"LIST",check:"Array"}],output:"Array",inputsInline:!0,style:"list_blocks",tooltip:"%{BKY_LISTS_REVERSE_TOOLTIP}",helpUrl:"%{BKY_LISTS_REVERSE_HELPURL}"},{type:"lists_isEmpty",message0:"%{BKY_LISTS_ISEMPTY_TITLE}",args0:[{type:"input_value",name:"VALUE",check:["String","Array"]}],output:"Boolean",style:"list_blocks",tooltip:"%{BKY_LISTS_ISEMPTY_TOOLTIP}",helpUrl:"%{BKY_LISTS_ISEMPTY_HELPURL}"},{type:"lists_length",message0:"%{BKY_LISTS_LENGTH_TITLE}", -args0:[{type:"input_value",name:"VALUE",check:["String","Array"]}],output:"Number",style:"list_blocks",tooltip:"%{BKY_LISTS_LENGTH_TOOLTIP}",helpUrl:"%{BKY_LISTS_LENGTH_HELPURL}"}]); -Blockly.Blocks.lists_create_with={init:function(){this.setHelpUrl(Blockly.Msg.LISTS_CREATE_WITH_HELPURL);this.setStyle("list_blocks");this.itemCount_=3;this.updateShape_();this.setOutput(!0,"Array");this.setMutator(new Blockly.Mutator(["lists_create_with_item"]));this.setTooltip(Blockly.Msg.LISTS_CREATE_WITH_TOOLTIP)},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation");a.setAttribute("items",this.itemCount_);return a},domToMutation:function(a){this.itemCount_=parseInt(a.getAttribute("items"), -10);this.updateShape_()},decompose:function(a){var b=a.newBlock("lists_create_with_container");b.initSvg();for(var c=b.getInput("STACK").connection,d=0;d","GT"],["\u200f\u2265","GTE"]]},{type:"input_value",name:"B"}],inputsInline:!0,output:"Boolean",style:"logic_blocks",helpUrl:"%{BKY_LOGIC_COMPARE_HELPURL}",extensions:["logic_compare", -"logic_op_tooltip"]},{type:"logic_operation",message0:"%1 %2 %3",args0:[{type:"input_value",name:"A",check:"Boolean"},{type:"field_dropdown",name:"OP",options:[["%{BKY_LOGIC_OPERATION_AND}","AND"],["%{BKY_LOGIC_OPERATION_OR}","OR"]]},{type:"input_value",name:"B",check:"Boolean"}],inputsInline:!0,output:"Boolean",style:"logic_blocks",helpUrl:"%{BKY_LOGIC_OPERATION_HELPURL}",extensions:["logic_op_tooltip"]},{type:"logic_negate",message0:"%{BKY_LOGIC_NEGATE_TITLE}",args0:[{type:"input_value",name:"BOOL", -check:"Boolean"}],output:"Boolean",style:"logic_blocks",tooltip:"%{BKY_LOGIC_NEGATE_TOOLTIP}",helpUrl:"%{BKY_LOGIC_NEGATE_HELPURL}"},{type:"logic_null",message0:"%{BKY_LOGIC_NULL}",output:null,style:"logic_blocks",tooltip:"%{BKY_LOGIC_NULL_TOOLTIP}",helpUrl:"%{BKY_LOGIC_NULL_HELPURL}"},{type:"logic_ternary",message0:"%{BKY_LOGIC_TERNARY_CONDITION} %1",args0:[{type:"input_value",name:"IF",check:"Boolean"}],message1:"%{BKY_LOGIC_TERNARY_IF_TRUE} %1",args1:[{type:"input_value",name:"THEN"}],message2:"%{BKY_LOGIC_TERNARY_IF_FALSE} %1", -args2:[{type:"input_value",name:"ELSE"}],output:null,style:"logic_blocks",tooltip:"%{BKY_LOGIC_TERNARY_TOOLTIP}",helpUrl:"%{BKY_LOGIC_TERNARY_HELPURL}",extensions:["logic_ternary"]}]); -Blockly.defineBlocksWithJsonArray([{type:"controls_if_if",message0:"%{BKY_CONTROLS_IF_IF_TITLE_IF}",nextStatement:null,enableContextMenu:!1,style:"logic_blocks",tooltip:"%{BKY_CONTROLS_IF_IF_TOOLTIP}"},{type:"controls_if_elseif",message0:"%{BKY_CONTROLS_IF_ELSEIF_TITLE_ELSEIF}",previousStatement:null,nextStatement:null,enableContextMenu:!1,style:"logic_blocks",tooltip:"%{BKY_CONTROLS_IF_ELSEIF_TOOLTIP}"},{type:"controls_if_else",message0:"%{BKY_CONTROLS_IF_ELSE_TITLE_ELSE}",previousStatement:null, -enableContextMenu:!1,style:"logic_blocks",tooltip:"%{BKY_CONTROLS_IF_ELSE_TOOLTIP}"}]);Blockly.Constants.Logic.TOOLTIPS_BY_OP={EQ:"%{BKY_LOGIC_COMPARE_TOOLTIP_EQ}",NEQ:"%{BKY_LOGIC_COMPARE_TOOLTIP_NEQ}",LT:"%{BKY_LOGIC_COMPARE_TOOLTIP_LT}",LTE:"%{BKY_LOGIC_COMPARE_TOOLTIP_LTE}",GT:"%{BKY_LOGIC_COMPARE_TOOLTIP_GT}",GTE:"%{BKY_LOGIC_COMPARE_TOOLTIP_GTE}",AND:"%{BKY_LOGIC_OPERATION_TOOLTIP_AND}",OR:"%{BKY_LOGIC_OPERATION_TOOLTIP_OR}"}; -Blockly.Extensions.register("logic_op_tooltip",Blockly.Extensions.buildTooltipForDropdown("OP",Blockly.Constants.Logic.TOOLTIPS_BY_OP)); -Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN={elseifCount_:0,elseCount_:0,suppressPrefixSuffix:!0,mutationToDom:function(){if(!this.elseifCount_&&!this.elseCount_)return null;var a=Blockly.utils.xml.createElement("mutation");this.elseifCount_&&a.setAttribute("elseif",this.elseifCount_);this.elseCount_&&a.setAttribute("else",1);return a},domToMutation:function(a){this.elseifCount_=parseInt(a.getAttribute("elseif"),10)||0;this.elseCount_=parseInt(a.getAttribute("else"),10)||0;this.rebuildShape_()}, -decompose:function(a){var b=a.newBlock("controls_if_if");b.initSvg();for(var c=b.nextConnection,d=1;d<=this.elseifCount_;d++){var e=a.newBlock("controls_if_elseif");e.initSvg();c.connect(e.previousConnection);c=e.nextConnection}this.elseCount_&&(a=a.newBlock("controls_if_else"),a.initSvg(),c.connect(a.previousConnection));return b},compose:function(a){a=a.nextConnection.targetBlock();this.elseCount_=this.elseifCount_=0;for(var b=[null],c=[null],d=null;a&&!a.isInsertionMarker();){switch(a.type){case "controls_if_elseif":this.elseifCount_++; -b.push(a.valueConnection_);c.push(a.statementConnection_);break;case "controls_if_else":this.elseCount_++;d=a.statementConnection_;break;default:throw TypeError("Unknown block type: "+a.type);}a=a.nextConnection&&a.nextConnection.targetBlock()}this.updateShape_();this.reconnectChildBlocks_(b,c,d)},saveConnections:function(a){a=a.nextConnection.targetBlock();for(var b=1;a;){switch(a.type){case "controls_if_elseif":var c=this.getInput("IF"+b),d=this.getInput("DO"+b);a.valueConnection_=c&&c.connection.targetConnection; -a.statementConnection_=d&&d.connection.targetConnection;b++;break;case "controls_if_else":d=this.getInput("ELSE");a.statementConnection_=d&&d.connection.targetConnection;break;default:throw TypeError("Unknown block type: "+a.type);}a=a.nextConnection&&a.nextConnection.targetBlock()}},rebuildShape_:function(){var a=[null],b=[null],c=null;this.getInput("ELSE")&&(c=this.getInput("ELSE").connection.targetConnection);for(var d=1;this.getInput("IF"+d);){var e=this.getInput("IF"+d),f=this.getInput("DO"+ -d);a.push(e.connection.targetConnection);b.push(f.connection.targetConnection);d++}this.updateShape_();this.reconnectChildBlocks_(a,b,c)},updateShape_:function(){this.getInput("ELSE")&&this.removeInput("ELSE");for(var a=1;this.getInput("IF"+a);)this.removeInput("IF"+a),this.removeInput("DO"+a),a++;for(a=1;a<=this.elseifCount_;a++)this.appendValueInput("IF"+a).setCheck("Boolean").appendField(Blockly.Msg.CONTROLS_IF_MSG_ELSEIF),this.appendStatementInput("DO"+a).appendField(Blockly.Msg.CONTROLS_IF_MSG_THEN); -this.elseCount_&&this.appendStatementInput("ELSE").appendField(Blockly.Msg.CONTROLS_IF_MSG_ELSE)},reconnectChildBlocks_:function(a,b,c){for(var d=1;d<=this.elseifCount_;d++)Blockly.Mutator.reconnect(a[d],this,"IF"+d),Blockly.Mutator.reconnect(b[d],this,"DO"+d);Blockly.Mutator.reconnect(c,this,"ELSE")}};Blockly.Extensions.registerMutator("controls_if_mutator",Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN,null,["controls_if_elseif","controls_if_else"]); -Blockly.Constants.Logic.CONTROLS_IF_TOOLTIP_EXTENSION=function(){this.setTooltip(function(){if(this.elseifCount_||this.elseCount_){if(!this.elseifCount_&&this.elseCount_)return Blockly.Msg.CONTROLS_IF_TOOLTIP_2;if(this.elseifCount_&&!this.elseCount_)return Blockly.Msg.CONTROLS_IF_TOOLTIP_3;if(this.elseifCount_&&this.elseCount_)return Blockly.Msg.CONTROLS_IF_TOOLTIP_4}else return Blockly.Msg.CONTROLS_IF_TOOLTIP_1;return""}.bind(this))};Blockly.Extensions.register("controls_if_tooltip",Blockly.Constants.Logic.CONTROLS_IF_TOOLTIP_EXTENSION); -Blockly.Constants.Logic.LOGIC_COMPARE_ONCHANGE_MIXIN={onchange:function(a){this.prevBlocks_||(this.prevBlocks_=[null,null]);var b=this.getInputTargetBlock("A"),c=this.getInputTargetBlock("B");b&&c&&!this.workspace.connectionChecker.doTypeChecks(b.outputConnection,c.outputConnection)&&(Blockly.Events.setGroup(a.group),a=this.prevBlocks_[0],a!==b&&(b.unplug(),!a||a.isDisposed()||a.isShadow()||this.getInput("A").connection.connect(a.outputConnection)),b=this.prevBlocks_[1],b!==c&&(c.unplug(),!b||b.isDisposed()|| -b.isShadow()||this.getInput("B").connection.connect(b.outputConnection)),this.bumpNeighbours(),Blockly.Events.setGroup(!1));this.prevBlocks_[0]=this.getInputTargetBlock("A");this.prevBlocks_[1]=this.getInputTargetBlock("B")}};Blockly.Constants.Logic.LOGIC_COMPARE_EXTENSION=function(){this.mixin(Blockly.Constants.Logic.LOGIC_COMPARE_ONCHANGE_MIXIN)};Blockly.Extensions.register("logic_compare",Blockly.Constants.Logic.LOGIC_COMPARE_EXTENSION); -Blockly.Constants.Logic.LOGIC_TERNARY_ONCHANGE_MIXIN={prevParentConnection_:null,onchange:function(a){var b=this.getInputTargetBlock("THEN"),c=this.getInputTargetBlock("ELSE"),d=this.outputConnection.targetConnection;if((b||c)&&d)for(var e=0;2>e;e++){var f=1==e?b:c;f&&!f.workspace.connectionChecker.doTypeChecks(f.outputConnection,d)&&(Blockly.Events.setGroup(a.group),d===this.prevParentConnection_?(this.unplug(),d.getSourceBlock().bumpNeighbours()):(f.unplug(),f.bumpNeighbours()),Blockly.Events.setGroup(!1))}this.prevParentConnection_= -d}};Blockly.Extensions.registerMixin("logic_ternary",Blockly.Constants.Logic.LOGIC_TERNARY_ONCHANGE_MIXIN);Blockly.Blocks.loops={};Blockly.Constants.Loops={};Blockly.Constants.Loops.HUE=120; -Blockly.defineBlocksWithJsonArray([{type:"controls_repeat_ext",message0:"%{BKY_CONTROLS_REPEAT_TITLE}",args0:[{type:"input_value",name:"TIMES",check:"Number"}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,style:"loop_blocks",tooltip:"%{BKY_CONTROLS_REPEAT_TOOLTIP}",helpUrl:"%{BKY_CONTROLS_REPEAT_HELPURL}"},{type:"controls_repeat",message0:"%{BKY_CONTROLS_REPEAT_TITLE}",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"}],previousStatement:null,nextStatement:null,style:"loop_blocks",tooltip:"%{BKY_CONTROLS_REPEAT_TOOLTIP}",helpUrl:"%{BKY_CONTROLS_REPEAT_HELPURL}"},{type:"controls_whileUntil",message0:"%1 %2",args0:[{type:"field_dropdown",name:"MODE",options:[["%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_WHILE}","WHILE"],["%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_UNTIL}","UNTIL"]]},{type:"input_value",name:"BOOL",check:"Boolean"}], -message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,style:"loop_blocks",helpUrl:"%{BKY_CONTROLS_WHILEUNTIL_HELPURL}",extensions:["controls_whileUntil_tooltip"]},{type:"controls_for",message0:"%{BKY_CONTROLS_FOR_TITLE}",args0:[{type:"field_variable",name:"VAR",variable:null},{type:"input_value",name:"FROM",check:"Number",align:"RIGHT"},{type:"input_value",name:"TO",check:"Number",align:"RIGHT"},{type:"input_value",name:"BY", -check:"Number",align:"RIGHT"}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],inputsInline:!0,previousStatement:null,nextStatement:null,style:"loop_blocks",helpUrl:"%{BKY_CONTROLS_FOR_HELPURL}",extensions:["contextMenu_newGetVariableBlock","controls_for_tooltip"]},{type:"controls_forEach",message0:"%{BKY_CONTROLS_FOREACH_TITLE}",args0:[{type:"field_variable",name:"VAR",variable:null},{type:"input_value",name:"LIST",check:"Array"}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1", -args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,style:"loop_blocks",helpUrl:"%{BKY_CONTROLS_FOREACH_HELPURL}",extensions:["contextMenu_newGetVariableBlock","controls_forEach_tooltip"]},{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"]]}],previousStatement:null,style:"loop_blocks",helpUrl:"%{BKY_CONTROLS_FLOW_STATEMENTS_HELPURL}", -extensions:["controls_flow_tooltip","controls_flow_in_loop_check"]}]);Blockly.Constants.Loops.WHILE_UNTIL_TOOLTIPS={WHILE:"%{BKY_CONTROLS_WHILEUNTIL_TOOLTIP_WHILE}",UNTIL:"%{BKY_CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL}"};Blockly.Extensions.register("controls_whileUntil_tooltip",Blockly.Extensions.buildTooltipForDropdown("MODE",Blockly.Constants.Loops.WHILE_UNTIL_TOOLTIPS));Blockly.Constants.Loops.BREAK_CONTINUE_TOOLTIPS={BREAK:"%{BKY_CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK}",CONTINUE:"%{BKY_CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE}"}; -Blockly.Extensions.register("controls_flow_tooltip",Blockly.Extensions.buildTooltipForDropdown("FLOW",Blockly.Constants.Loops.BREAK_CONTINUE_TOOLTIPS)); -Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN={customContextMenu:function(a){if(!this.isInFlyout){var b=this.getField("VAR").getVariable(),c=b.name;if(!this.isCollapsed()&&null!=c){var d={enabled:!0};d.text=Blockly.Msg.VARIABLES_SET_CREATE_GET.replace("%1",c);b=Blockly.Variables.generateVariableFieldDom(b);c=Blockly.utils.xml.createElement("block");c.setAttribute("type","variables_get");c.appendChild(b);d.callback=Blockly.ContextMenu.callbackFactory(this,c);a.push(d)}}}}; -Blockly.Extensions.registerMixin("contextMenu_newGetVariableBlock",Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN);Blockly.Extensions.register("controls_for_tooltip",Blockly.Extensions.buildTooltipWithFieldText("%{BKY_CONTROLS_FOR_TOOLTIP}","VAR"));Blockly.Extensions.register("controls_forEach_tooltip",Blockly.Extensions.buildTooltipWithFieldText("%{BKY_CONTROLS_FOREACH_TOOLTIP}","VAR")); -Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN={LOOP_TYPES:["controls_repeat","controls_repeat_ext","controls_forEach","controls_for","controls_whileUntil"],suppressPrefixSuffix:!0,getSurroundLoop:function(a){do{if(-1!=Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.LOOP_TYPES.indexOf(a.type))return a;a=a.getSurroundParent()}while(a);return null},onchange:function(a){if(this.workspace.isDragging&&!this.workspace.isDragging()&&a.type==Blockly.Events.BLOCK_MOVE){var b=Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.getSurroundLoop(this); -this.setWarningText(b?null:Blockly.Msg.CONTROLS_FLOW_STATEMENTS_WARNING);if(!this.isInFlyout){var c=Blockly.Events.getGroup();Blockly.Events.setGroup(a.group);this.setEnabled(b);Blockly.Events.setGroup(c)}}}};Blockly.Extensions.registerMixin("controls_flow_in_loop_check",Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN);Blockly.Blocks.math={};Blockly.Constants.Math={};Blockly.Constants.Math.HUE=230; -Blockly.defineBlocksWithJsonArray([{type:"math_number",message0:"%1",args0:[{type:"field_number",name:"NUM",value:0}],output:"Number",helpUrl:"%{BKY_MATH_NUMBER_HELPURL}",style:"math_blocks",tooltip:"%{BKY_MATH_NUMBER_TOOLTIP}",extensions:["parent_tooltip_when_inline"]},{type:"math_arithmetic",message0:"%1 %2 %3",args0:[{type:"input_value",name:"A",check:"Number"},{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_ADDITION_SYMBOL}","ADD"],["%{BKY_MATH_SUBTRACTION_SYMBOL}","MINUS"],["%{BKY_MATH_MULTIPLICATION_SYMBOL}", -"MULTIPLY"],["%{BKY_MATH_DIVISION_SYMBOL}","DIVIDE"],["%{BKY_MATH_POWER_SYMBOL}","POWER"]]},{type:"input_value",name:"B",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",helpUrl:"%{BKY_MATH_ARITHMETIC_HELPURL}",extensions:["math_op_tooltip"]},{type:"math_single",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_SINGLE_OP_ROOT}","ROOT"],["%{BKY_MATH_SINGLE_OP_ABSOLUTE}","ABS"],["-","NEG"],["ln","LN"],["log10","LOG10"],["e^","EXP"],["10^","POW10"]]}, -{type:"input_value",name:"NUM",check:"Number"}],output:"Number",style:"math_blocks",helpUrl:"%{BKY_MATH_SINGLE_HELPURL}",extensions:["math_op_tooltip"]},{type:"math_trig",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_TRIG_SIN}","SIN"],["%{BKY_MATH_TRIG_COS}","COS"],["%{BKY_MATH_TRIG_TAN}","TAN"],["%{BKY_MATH_TRIG_ASIN}","ASIN"],["%{BKY_MATH_TRIG_ACOS}","ACOS"],["%{BKY_MATH_TRIG_ATAN}","ATAN"]]},{type:"input_value",name:"NUM",check:"Number"}],output:"Number",style:"math_blocks", -helpUrl:"%{BKY_MATH_TRIG_HELPURL}",extensions:["math_op_tooltip"]},{type:"math_constant",message0:"%1",args0:[{type:"field_dropdown",name:"CONSTANT",options:[["\u03c0","PI"],["e","E"],["\u03c6","GOLDEN_RATIO"],["sqrt(2)","SQRT2"],["sqrt(\u00bd)","SQRT1_2"],["\u221e","INFINITY"]]}],output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_CONSTANT_TOOLTIP}",helpUrl:"%{BKY_MATH_CONSTANT_HELPURL}"},{type:"math_number_property",message0:"%1 %2",args0:[{type:"input_value",name:"NUMBER_TO_CHECK",check:"Number"}, -{type:"field_dropdown",name:"PROPERTY",options:[["%{BKY_MATH_IS_EVEN}","EVEN"],["%{BKY_MATH_IS_ODD}","ODD"],["%{BKY_MATH_IS_PRIME}","PRIME"],["%{BKY_MATH_IS_WHOLE}","WHOLE"],["%{BKY_MATH_IS_POSITIVE}","POSITIVE"],["%{BKY_MATH_IS_NEGATIVE}","NEGATIVE"],["%{BKY_MATH_IS_DIVISIBLE_BY}","DIVISIBLE_BY"]]}],inputsInline:!0,output:"Boolean",style:"math_blocks",tooltip:"%{BKY_MATH_IS_TOOLTIP}",mutator:"math_is_divisibleby_mutator"},{type:"math_change",message0:"%{BKY_MATH_CHANGE_TITLE}",args0:[{type:"field_variable", -name:"VAR",variable:"%{BKY_MATH_CHANGE_TITLE_ITEM}"},{type:"input_value",name:"DELTA",check:"Number"}],previousStatement:null,nextStatement:null,style:"variable_blocks",helpUrl:"%{BKY_MATH_CHANGE_HELPURL}",extensions:["math_change_tooltip"]},{type:"math_round",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_ROUND_OPERATOR_ROUND}","ROUND"],["%{BKY_MATH_ROUND_OPERATOR_ROUNDUP}","ROUNDUP"],["%{BKY_MATH_ROUND_OPERATOR_ROUNDDOWN}","ROUNDDOWN"]]},{type:"input_value",name:"NUM", -check:"Number"}],output:"Number",style:"math_blocks",helpUrl:"%{BKY_MATH_ROUND_HELPURL}",tooltip:"%{BKY_MATH_ROUND_TOOLTIP}"},{type:"math_on_list",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_ONLIST_OPERATOR_SUM}","SUM"],["%{BKY_MATH_ONLIST_OPERATOR_MIN}","MIN"],["%{BKY_MATH_ONLIST_OPERATOR_MAX}","MAX"],["%{BKY_MATH_ONLIST_OPERATOR_AVERAGE}","AVERAGE"],["%{BKY_MATH_ONLIST_OPERATOR_MEDIAN}","MEDIAN"],["%{BKY_MATH_ONLIST_OPERATOR_MODE}","MODE"],["%{BKY_MATH_ONLIST_OPERATOR_STD_DEV}", -"STD_DEV"],["%{BKY_MATH_ONLIST_OPERATOR_RANDOM}","RANDOM"]]},{type:"input_value",name:"LIST",check:"Array"}],output:"Number",style:"math_blocks",helpUrl:"%{BKY_MATH_ONLIST_HELPURL}",mutator:"math_modes_of_list_mutator",extensions:["math_op_tooltip"]},{type:"math_modulo",message0:"%{BKY_MATH_MODULO_TITLE}",args0:[{type:"input_value",name:"DIVIDEND",check:"Number"},{type:"input_value",name:"DIVISOR",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_MODULO_TOOLTIP}", -helpUrl:"%{BKY_MATH_MODULO_HELPURL}"},{type:"math_constrain",message0:"%{BKY_MATH_CONSTRAIN_TITLE}",args0:[{type:"input_value",name:"VALUE",check:"Number"},{type:"input_value",name:"LOW",check:"Number"},{type:"input_value",name:"HIGH",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_CONSTRAIN_TOOLTIP}",helpUrl:"%{BKY_MATH_CONSTRAIN_HELPURL}"},{type:"math_random_int",message0:"%{BKY_MATH_RANDOM_INT_TITLE}",args0:[{type:"input_value",name:"FROM",check:"Number"}, -{type:"input_value",name:"TO",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_RANDOM_INT_TOOLTIP}",helpUrl:"%{BKY_MATH_RANDOM_INT_HELPURL}"},{type:"math_random_float",message0:"%{BKY_MATH_RANDOM_FLOAT_TITLE_RANDOM}",output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_RANDOM_FLOAT_TOOLTIP}",helpUrl:"%{BKY_MATH_RANDOM_FLOAT_HELPURL}"},{type:"math_atan2",message0:"%{BKY_MATH_ATAN2_TITLE}",args0:[{type:"input_value",name:"X",check:"Number"},{type:"input_value", -name:"Y",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_ATAN2_TOOLTIP}",helpUrl:"%{BKY_MATH_ATAN2_HELPURL}"}]); -Blockly.Constants.Math.TOOLTIPS_BY_OP={ADD:"%{BKY_MATH_ARITHMETIC_TOOLTIP_ADD}",MINUS:"%{BKY_MATH_ARITHMETIC_TOOLTIP_MINUS}",MULTIPLY:"%{BKY_MATH_ARITHMETIC_TOOLTIP_MULTIPLY}",DIVIDE:"%{BKY_MATH_ARITHMETIC_TOOLTIP_DIVIDE}",POWER:"%{BKY_MATH_ARITHMETIC_TOOLTIP_POWER}",ROOT:"%{BKY_MATH_SINGLE_TOOLTIP_ROOT}",ABS:"%{BKY_MATH_SINGLE_TOOLTIP_ABS}",NEG:"%{BKY_MATH_SINGLE_TOOLTIP_NEG}",LN:"%{BKY_MATH_SINGLE_TOOLTIP_LN}",LOG10:"%{BKY_MATH_SINGLE_TOOLTIP_LOG10}",EXP:"%{BKY_MATH_SINGLE_TOOLTIP_EXP}",POW10:"%{BKY_MATH_SINGLE_TOOLTIP_POW10}", -SIN:"%{BKY_MATH_TRIG_TOOLTIP_SIN}",COS:"%{BKY_MATH_TRIG_TOOLTIP_COS}",TAN:"%{BKY_MATH_TRIG_TOOLTIP_TAN}",ASIN:"%{BKY_MATH_TRIG_TOOLTIP_ASIN}",ACOS:"%{BKY_MATH_TRIG_TOOLTIP_ACOS}",ATAN:"%{BKY_MATH_TRIG_TOOLTIP_ATAN}",SUM:"%{BKY_MATH_ONLIST_TOOLTIP_SUM}",MIN:"%{BKY_MATH_ONLIST_TOOLTIP_MIN}",MAX:"%{BKY_MATH_ONLIST_TOOLTIP_MAX}",AVERAGE:"%{BKY_MATH_ONLIST_TOOLTIP_AVERAGE}",MEDIAN:"%{BKY_MATH_ONLIST_TOOLTIP_MEDIAN}",MODE:"%{BKY_MATH_ONLIST_TOOLTIP_MODE}",STD_DEV:"%{BKY_MATH_ONLIST_TOOLTIP_STD_DEV}",RANDOM:"%{BKY_MATH_ONLIST_TOOLTIP_RANDOM}"}; -Blockly.Extensions.register("math_op_tooltip",Blockly.Extensions.buildTooltipForDropdown("OP",Blockly.Constants.Math.TOOLTIPS_BY_OP)); -Blockly.Constants.Math.IS_DIVISIBLEBY_MUTATOR_MIXIN={mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation"),b="DIVISIBLE_BY"==this.getFieldValue("PROPERTY");a.setAttribute("divisor_input",b);return a},domToMutation:function(a){a="true"==a.getAttribute("divisor_input");this.updateShape_(a)},updateShape_:function(a){var b=this.getInput("DIVISOR");a?b||this.appendValueInput("DIVISOR").setCheck("Number"):b&&this.removeInput("DIVISOR")}}; -Blockly.Constants.Math.IS_DIVISIBLE_MUTATOR_EXTENSION=function(){this.getField("PROPERTY").setValidator(function(a){a="DIVISIBLE_BY"==a;this.getSourceBlock().updateShape_(a)})};Blockly.Extensions.registerMutator("math_is_divisibleby_mutator",Blockly.Constants.Math.IS_DIVISIBLEBY_MUTATOR_MIXIN,Blockly.Constants.Math.IS_DIVISIBLE_MUTATOR_EXTENSION);Blockly.Extensions.register("math_change_tooltip",Blockly.Extensions.buildTooltipWithFieldText("%{BKY_MATH_CHANGE_TOOLTIP}","VAR")); -Blockly.Constants.Math.LIST_MODES_MUTATOR_MIXIN={updateType_:function(a){"MODE"==a?this.outputConnection.setCheck("Array"):this.outputConnection.setCheck("Number")},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation");a.setAttribute("op",this.getFieldValue("OP"));return a},domToMutation:function(a){this.updateType_(a.getAttribute("op"))}};Blockly.Constants.Math.LIST_MODES_MUTATOR_EXTENSION=function(){this.getField("OP").setValidator(function(a){this.updateType_(a)}.bind(this))}; -Blockly.Extensions.registerMutator("math_modes_of_list_mutator",Blockly.Constants.Math.LIST_MODES_MUTATOR_MIXIN,Blockly.Constants.Math.LIST_MODES_MUTATOR_EXTENSION);Blockly.Blocks.procedures={}; -Blockly.Blocks.procedures_defnoreturn={init:function(){var a=Blockly.Procedures.findLegalName("",this);a=new Blockly.FieldTextInput(a,Blockly.Procedures.rename);a.setSpellcheck(!1);this.appendDummyInput().appendField(Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE).appendField(a,"NAME").appendField("","PARAMS");this.setMutator(new Blockly.Mutator(["procedures_mutatorarg"]));(this.workspace.options.comments||this.workspace.options.parentWorkspace&&this.workspace.options.parentWorkspace.options.comments)&&Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT&& -this.setCommentText(Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT);this.setStyle("procedure_blocks");this.setTooltip(Blockly.Msg.PROCEDURES_DEFNORETURN_TOOLTIP);this.setHelpUrl(Blockly.Msg.PROCEDURES_DEFNORETURN_HELPURL);this.arguments_=[];this.argumentVarModels_=[];this.setStatements_(!0);this.statementConnection_=null},setStatements_:function(a){this.hasStatements_!==a&&(a?(this.appendStatementInput("STACK").appendField(Blockly.Msg.PROCEDURES_DEFNORETURN_DO),this.getInput("RETURN")&&this.moveInputBefore("STACK", -"RETURN")):this.removeInput("STACK",!0),this.hasStatements_=a)},updateParams_:function(){var a="";this.arguments_.length&&(a=Blockly.Msg.PROCEDURES_BEFORE_PARAMS+" "+this.arguments_.join(", "));Blockly.Events.disable();try{this.setFieldValue(a,"PARAMS")}finally{Blockly.Events.enable()}},mutationToDom:function(a){var b=Blockly.utils.xml.createElement("mutation");a&&b.setAttribute("name",this.getFieldValue("NAME"));for(var c=0;c + * var CLOSURE_UNCOMPILED_DEFINES = {'goog.DEBUG': false}; + * + * + * @type {Object|undefined} + */ +goog.global.CLOSURE_UNCOMPILED_DEFINES; + + +/** + * A hook for overriding the define values in uncompiled or compiled mode, + * like CLOSURE_UNCOMPILED_DEFINES but effective in compiled code. In + * uncompiled code CLOSURE_UNCOMPILED_DEFINES takes precedence. + * + * Also unlike CLOSURE_UNCOMPILED_DEFINES the values must be number, boolean or + * string literals or the compiler will emit an error. + * + * While any @define value may be set, only those set with goog.define will be + * effective for uncompiled code. + * + * Example: + *
+ *   var CLOSURE_DEFINES = {'goog.DEBUG': false} ;
+ * 
+ * + * @type {Object|undefined} + */ +goog.global.CLOSURE_DEFINES; + + /** * Builds an object structure for the provided namespace path, ensuring that * names that already exist are not overwritten. For example: * "a.b.c" -> a = {};a.b={};a.b.c={}; * Used by goog.provide and goog.exportSymbol. - * @param {string} name name of the object that this file defines. + * @param {string} name The name of the object that this file defines. + * @param {*=} object The object to expose at the end of the path. + * @param {boolean=} overwriteImplicit If object is set and a previous call + * implicitly constructed the namespace given by name, this parameter + * controls whether object should overwrite the implicitly constructed + * namespace or be merged into it. Defaults to false. + * @param {?Object=} objectToExportTo The object to add the path to; if this + * field is not specified, its value defaults to `goog.global`. * @private */ -goog.exportPath_ = function(name) { +goog.exportPath_ = function(name, object, overwriteImplicit, objectToExportTo) { var parts = name.split('.'); - var cur = goog.global; + var cur = objectToExportTo || goog.global; // Internet Explorer exhibits strange behavior when throwing errors from // methods externed in this manner. See the testExportSymbolExceptions in @@ -77,7 +123,24 @@ goog.exportPath_ = function(name) { } for (var part; parts.length && (part = parts.shift());) { - if (cur[part] && cur[part] !== Object.prototype[part]) { + if (!parts.length && object !== undefined) { + if (!overwriteImplicit && goog.isObject(object) && + goog.isObject(cur[part])) { + // Merge properties on object (the input parameter) with the existing + // implicitly defined namespace, so as to not clobber previously + // defined child namespaces. + for (var prop in object) { + if (object.hasOwnProperty(prop)) { + cur[part][prop] = object[prop]; + } + } + } else { + // Either there is no existing implicit namespace, or overwriteImplicit + // is set to true, so directly assign object (the input parameter) to + // the namespace. + cur[part] = object; + } + } else if (cur[part] && cur[part] !== Object.prototype[part]) { cur = cur[part]; } else { cur = cur[part] = {}; @@ -85,6 +148,152 @@ goog.exportPath_ = function(name) { } }; + +/** + * Defines a named value. In uncompiled mode, the value is retrieved from + * CLOSURE_DEFINES or CLOSURE_UNCOMPILED_DEFINES if the object is defined and + * has the property specified, and otherwise used the defined defaultValue. + * When compiled the default can be overridden using the compiler options or the + * value set in the CLOSURE_DEFINES object. Returns the defined value so that it + * can be used safely in modules. Note that the value type MUST be either + * boolean, number, or string. + * + * @param {string} name The distinguished name to provide. + * @param {T} defaultValue + * @return {T} The defined value. + * @template T + */ +goog.define = function(name, defaultValue) { + var value = defaultValue; + if (!COMPILED) { + var uncompiledDefines = goog.global.CLOSURE_UNCOMPILED_DEFINES; + var defines = goog.global.CLOSURE_DEFINES; + if (uncompiledDefines && + // Anti DOM-clobbering runtime check (b/37736576). + /** @type {?} */ (uncompiledDefines).nodeType === undefined && + Object.prototype.hasOwnProperty.call(uncompiledDefines, name)) { + value = uncompiledDefines[name]; + } else if ( + defines && + // Anti DOM-clobbering runtime check (b/37736576). + /** @type {?} */ (defines).nodeType === undefined && + Object.prototype.hasOwnProperty.call(defines, name)) { + value = defines[name]; + } + } + return value; +}; + + +/** + * @define {number} Integer year indicating the set of browser features that are + * guaranteed to be present. This is defined to include exactly features that + * work correctly on all "modern" browsers that are stable on January 1 of the + * specified year. For example, + * ```js + * if (goog.FEATURESET_YEAR >= 2019) { + * // use APIs known to be available on all major stable browsers Jan 1, 2019 + * } else { + * // polyfill for older browsers + * } + * ``` + * This is intended to be the primary define for removing + * unnecessary browser compatibility code (such as ponyfills and workarounds), + * and should inform the default value for most other defines: + * ```js + * const ASSUME_NATIVE_PROMISE = + * goog.define('ASSUME_NATIVE_PROMISE', goog.FEATURESET_YEAR >= 2016); + * ``` + * + * The default assumption is that IE9 is the lowest supported browser, which was + * first available Jan 1, 2012. + * + * TODO(user): Reference more thorough documentation when it's available. + */ +goog.FEATURESET_YEAR = goog.define('goog.FEATURESET_YEAR', 2012); + + +/** + * @define {boolean} DEBUG is provided as a convenience so that debugging code + * that should not be included in a production. It can be easily stripped + * by specifying --define goog.DEBUG=false to the Closure Compiler aka + * JSCompiler. For example, most toString() methods should be declared inside an + * "if (goog.DEBUG)" conditional because they are generally used for debugging + * purposes and it is difficult for the JSCompiler to statically determine + * whether they are used. + */ +goog.DEBUG = goog.define('goog.DEBUG', true); + + +/** + * @define {string} LOCALE defines the locale being used for compilation. It is + * used to select locale specific data to be compiled in js binary. BUILD rule + * can specify this value by "--define goog.LOCALE=" as a compiler + * option. + * + * Take into account that the locale code format is important. You should use + * the canonical Unicode format with hyphen as a delimiter. Language must be + * lowercase, Language Script - Capitalized, Region - UPPERCASE. + * There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN. + * + * See more info about locale codes here: + * http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers + * + * For language codes you should use values defined by ISO 693-1. See it here + * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from + * this rule: the Hebrew language. For legacy reasons the old code (iw) should + * be used instead of the new code (he). + * + */ +goog.LOCALE = goog.define('goog.LOCALE', 'en'); // default to en + + +/** + * This method is intended to be used for bookkeeping purposes. We would + * like to distinguish uses of goog.LOCALE used for code stripping purposes + * and uses of goog.LOCALE for other uses (such as URL parameters). + * + * This allows us to ban direct uses of goog.LOCALE and to ensure that all + * code has been transformed to our new localization build scheme. + * + * @return {string} + * + */ +goog.getLocale = function() { + return goog.LOCALE; +}; + + +/** + * @define {boolean} Whether this code is running on trusted sites. + * + * On untrusted sites, several native functions can be defined or overridden by + * external libraries like Prototype, Datejs, and JQuery and setting this flag + * to false forces closure to use its own implementations when possible. + * + * If your JavaScript can be loaded by a third party site and you are wary about + * relying on non-standard implementations, specify + * "--define goog.TRUSTED_SITE=false" to the compiler. + */ +goog.TRUSTED_SITE = goog.define('goog.TRUSTED_SITE', true); + + +/** + * @define {boolean} Whether code that calls {@link goog.setTestOnly} should + * be disallowed in the compilation unit. + */ +goog.DISALLOW_TEST_ONLY_CODE = + goog.define('goog.DISALLOW_TEST_ONLY_CODE', COMPILED && !goog.DEBUG); + + +/** + * @define {boolean} Whether to use a Chrome app CSP-compliant method for + * loading scripts via goog.require. @see appendScriptSrcNode_. + */ +goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING = + goog.define('goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING', false); + + /** * Defines a namespace in Closure. * @@ -106,27 +315,199 @@ goog.exportPath_ = function(name) { * @see goog.module * @param {string} name Namespace provided by this file in the form * "goog.package.part". + * deprecated Use goog.module (see b/159289405) */ goog.provide = function(name) { - // Ensure that the same namespace isn't provided twice. - // A goog.module/goog.provide maps a goog.require to a specific file - if (goog.isProvided_(name)) { - throw Error('Namespace "' + name + '" already declared.'); + if (goog.isInModuleLoader_()) { + throw new Error('goog.provide cannot be used within a module.'); + } + if (!COMPILED) { + // Ensure that the same namespace isn't provided twice. + // A goog.module/goog.provide maps a goog.require to a specific file + if (goog.isProvided_(name)) { + throw new Error('Namespace "' + name + '" already declared.'); + } + } + + goog.constructNamespace_(name); +}; + + +/** + * @param {string} name Namespace provided by this file in the form + * "goog.package.part". + * @param {?Object=} object The object to embed in the namespace. + * @param {boolean=} overwriteImplicit If object is set and a previous call + * implicitly constructed the namespace given by name, this parameter + * controls whether opt_obj should overwrite the implicitly constructed + * namespace or be merged into it. Defaults to false. + * @private + */ +goog.constructNamespace_ = function(name, object, overwriteImplicit) { + if (!COMPILED) { + delete goog.implicitNamespaces_[name]; + + var namespace = name; + while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) { + if (goog.getObjectByName(namespace)) { + break; + } + goog.implicitNamespaces_[namespace] = true; + } + } + + goog.exportPath_(name, object, overwriteImplicit); +}; + + +/** + * According to the CSP3 spec a nonce must be a valid base64 string. + * @see https://www.w3.org/TR/CSP3/#grammardef-base64-value + * @private @const + */ +goog.NONCE_PATTERN_ = /^[\w+/_-]+[=]{0,2}$/; + + +/** + * Returns CSP nonce, if set for any script tag. + * @param {?Window=} opt_window The window context used to retrieve the nonce. + * Defaults to global context. + * @return {string} CSP nonce or empty string if no nonce is present. + * @private + */ +goog.getScriptNonce_ = function(opt_window) { + var doc = (opt_window || goog.global).document; + var script = doc.querySelector && doc.querySelector('script[nonce]'); + if (script) { + // Try to get the nonce from the IDL property first, because browsers that + // implement additional nonce protection features (currently only Chrome) to + // prevent nonce stealing via CSS do not expose the nonce via attributes. + // See https://github.com/whatwg/html/issues/2369 + var nonce = script['nonce'] || script.getAttribute('nonce'); + if (nonce && goog.NONCE_PATTERN_.test(nonce)) { + return nonce; + } + } + return ''; +}; + + +/** + * Module identifier validation regexp. + * Note: This is a conservative check, it is very possible to be more lenient, + * the primary exclusion here is "/" and "\" and a leading ".", these + * restrictions are intended to leave the door open for using goog.require + * with relative file paths rather than module identifiers. + * @private + */ +goog.VALID_MODULE_RE_ = /^[a-zA-Z_$][a-zA-Z0-9._$]*$/; + + +/** + * Defines a module in Closure. + * + * Marks that this file must be loaded as a module and claims the namespace. + * + * A namespace may only be defined once in a codebase. It may be defined using + * goog.provide() or goog.module(). + * + * goog.module() has three requirements: + * - goog.module may not be used in the same file as goog.provide. + * - goog.module must be the first statement in the file. + * - only one goog.module is allowed per file. + * + * When a goog.module annotated file is loaded, it is enclosed in + * a strict function closure. This means that: + * - any variables declared in a goog.module file are private to the file + * (not global), though the compiler is expected to inline the module. + * - The code must obey all the rules of "strict" JavaScript. + * - the file will be marked as "use strict" + * + * NOTE: unlike goog.provide, goog.module does not declare any symbols by + * itself. If declared symbols are desired, use + * goog.module.declareLegacyNamespace(). + * + * + * See the public goog.module proposal: http://goo.gl/Va1hin + * + * @param {string} name Namespace provided by this file in the form + * "goog.package.part", is expected but not required. + * @return {void} + */ +goog.module = function(name) { + if (typeof name !== 'string' || !name || + name.search(goog.VALID_MODULE_RE_) == -1) { + throw new Error('Invalid module identifier'); + } + if (!goog.isInGoogModuleLoader_()) { + throw new Error( + 'Module ' + name + ' has been loaded incorrectly. Note, ' + + 'modules cannot be loaded as normal scripts. They require some kind of ' + + 'pre-processing step. You\'re likely trying to load a module via a ' + + 'script tag or as a part of a concatenated bundle without rewriting the ' + + 'module. For more info see: ' + + 'https://github.com/google/closure-library/wiki/goog.module:-an-ES6-module-like-alternative-to-goog.provide.'); } + if (goog.moduleLoaderState_.moduleName) { + throw new Error('goog.module may only be called once per module.'); + } + + // Store the module name for the loader. + goog.moduleLoaderState_.moduleName = name; + if (!COMPILED) { + // Ensure that the same namespace isn't provided twice. + // A goog.module/goog.provide maps a goog.require to a specific file + if (goog.isProvided_(name)) { + throw new Error('Namespace "' + name + '" already declared.'); + } + delete goog.implicitNamespaces_[name]; + } +}; + + +/** + * @param {string} name The module identifier. + * @return {?} The module exports for an already loaded module or null. + * + * Note: This is not an alternative to goog.require, it does not + * indicate a hard dependency, instead it is used to indicate + * an optional dependency or to access the exports of a module + * that has already been loaded. + * @suppress {missingProvide} + */ +goog.module.get = function(name) { + return goog.module.getInternal_(name); +}; - delete goog.implicitNamespaces_[name]; - var namespace = name; - while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) { - if (goog.getObjectByName(namespace)) { - break; +/** + * @param {string} name The module identifier. + * @return {?} The module exports for an already loaded module or null. + * @private + */ +goog.module.getInternal_ = function(name) { + if (!COMPILED) { + if (name in goog.loadedModules_) { + return goog.loadedModules_[name].exports; + } else if (!goog.implicitNamespaces_[name]) { + var ns = goog.getObjectByName(name); + return ns != null ? ns : null; } - goog.implicitNamespaces_[namespace] = true; } + return null; +}; - goog.exportPath_(name); + +/** + * Types of modules the debug loader can load. + * @enum {string} + */ +goog.ModuleType = { + ES6: 'es6', + GOOG: 'goog' }; + /** * @private {?{ * moduleName: (string|undefined), @@ -136,32 +517,210 @@ goog.provide = function(name) { */ goog.moduleLoaderState_ = null; + /** - * Check if the given name has been goog.provided. This will return false for - * names that are available only as implicit namespaces. - * @param {string} name name of the object to look for. - * @return {boolean} Whether the name has been provided. * @private + * @return {boolean} Whether a goog.module or an es6 module is currently being + * initialized. */ -goog.isProvided_ = function(name) { - return (!goog.implicitNamespaces_[name] && - goog.isDefAndNotNull(goog.getObjectByName(name))); +goog.isInModuleLoader_ = function() { + return goog.isInGoogModuleLoader_() || goog.isInEs6ModuleLoader_(); }; + +/** + * @private + * @return {boolean} Whether a goog.module is currently being initialized. + */ +goog.isInGoogModuleLoader_ = function() { + return !!goog.moduleLoaderState_ && + goog.moduleLoaderState_.type == goog.ModuleType.GOOG; +}; + + /** - * Namespaces implicitly defined by goog.provide. For example, - * goog.provide('goog.events.Event') implicitly declares that 'goog' and - * 'goog.events' must be namespaces. - * - * @type {!Object} * @private + * @return {boolean} Whether an es6 module is currently being initialized. + */ +goog.isInEs6ModuleLoader_ = function() { + var inLoader = !!goog.moduleLoaderState_ && + goog.moduleLoaderState_.type == goog.ModuleType.ES6; + + if (inLoader) { + return true; + } + + var jscomp = goog.global['$jscomp']; + + if (jscomp) { + // jscomp may not have getCurrentModulePath if this is a compiled bundle + // that has some of the runtime, but not all of it. This can happen if + // optimizations are turned on so the unused runtime is removed but renaming + // and Closure pass are off (so $jscomp is still named $jscomp and the + // goog.provide/require calls still exist). + if (typeof jscomp.getCurrentModulePath != 'function') { + return false; + } + + // Bundled ES6 module. + return !!jscomp.getCurrentModulePath(); + } + + return false; +}; + + +/** + * Provide the module's exports as a globally accessible object under the + * module's declared name. This is intended to ease migration to goog.module + * for files that have existing usages. + * @suppress {missingProvide} + */ +goog.module.declareLegacyNamespace = function() { + if (!COMPILED && !goog.isInGoogModuleLoader_()) { + throw new Error( + 'goog.module.declareLegacyNamespace must be called from ' + + 'within a goog.module'); + } + if (!COMPILED && !goog.moduleLoaderState_.moduleName) { + throw new Error( + 'goog.module must be called prior to ' + + 'goog.module.declareLegacyNamespace.'); + } + goog.moduleLoaderState_.declareLegacyNamespace = true; +}; + + +/** + * Associates an ES6 module with a Closure module ID so that is available via + * goog.require. The associated ID acts like a goog.module ID - it does not + * create any global names, it is merely available via goog.require / + * goog.module.get / goog.forwardDeclare / goog.requireType. goog.require and + * goog.module.get will return the entire module as if it was import *'d. This + * allows Closure files to reference ES6 modules for the sake of migration. + * + * @param {string} namespace + * @suppress {missingProvide} + */ +goog.declareModuleId = function(namespace) { + if (!COMPILED) { + if (!goog.isInEs6ModuleLoader_()) { + throw new Error( + 'goog.declareModuleId may only be called from ' + + 'within an ES6 module'); + } + if (goog.moduleLoaderState_ && goog.moduleLoaderState_.moduleName) { + throw new Error( + 'goog.declareModuleId may only be called once per module.'); + } + if (namespace in goog.loadedModules_) { + throw new Error( + 'Module with namespace "' + namespace + '" already exists.'); + } + } + if (goog.moduleLoaderState_) { + // Not bundled - debug loading. + goog.moduleLoaderState_.moduleName = namespace; + } else { + // Bundled - not debug loading, no module loader state. + var jscomp = goog.global['$jscomp']; + if (!jscomp || typeof jscomp.getCurrentModulePath != 'function') { + throw new Error( + 'Module with namespace "' + namespace + + '" has been loaded incorrectly.'); + } + var exports = jscomp.require(jscomp.getCurrentModulePath()); + goog.loadedModules_[namespace] = { + exports: exports, + type: goog.ModuleType.ES6, + moduleId: namespace + }; + } +}; + + +/** + * Marks that the current file should only be used for testing, and never for + * live code in production. + * + * In the case of unit tests, the message may optionally be an exact namespace + * for the test (e.g. 'goog.stringTest'). The linter will then ignore the extra + * provide (if not explicitly defined in the code). + * + * @param {string=} opt_message Optional message to add to the error that's + * raised when used in production code. + */ +goog.setTestOnly = function(opt_message) { + if (goog.DISALLOW_TEST_ONLY_CODE) { + opt_message = opt_message || ''; + throw new Error( + 'Importing test-only code into non-debug environment' + + (opt_message ? ': ' + opt_message : '.')); + } +}; + + +/** + * Forward declares a symbol. This is an indication to the compiler that the + * symbol may be used in the source yet is not required and may not be provided + * in compilation. + * + * The most common usage of forward declaration is code that takes a type as a + * function parameter but does not need to require it. By forward declaring + * instead of requiring, no hard dependency is made, and (if not required + * elsewhere) the namespace may never be required and thus, not be pulled + * into the JavaScript binary. If it is required elsewhere, it will be type + * checked as normal. + * + * Before using goog.forwardDeclare, please read the documentation at + * https://github.com/google/closure-compiler/wiki/Bad-Type-Annotation to + * understand the options and tradeoffs when working with forward declarations. + * + * @param {string} name The namespace to forward declare in the form of + * "goog.package.part". + * @deprecated See go/noforwarddeclaration, Use `goog.requireType` instead. */ -goog.implicitNamespaces_ = {}; +goog.forwardDeclare = function(name) {}; -// NOTE: We add goog.module as an implicit namespace as goog.module is defined -// here and because the existing module package has not been moved yet out of -// the goog.module namespace. This satisfies both the debug loader and -// ahead-of-time dependency management. + +/** + * Forward declare type information. Used to assign types to goog.global + * referenced object that would otherwise result in unknown type references + * and thus block property disambiguation. + */ +goog.forwardDeclare('Document'); +goog.forwardDeclare('HTMLScriptElement'); +goog.forwardDeclare('XMLHttpRequest'); + + +if (!COMPILED) { + /** + * Check if the given name has been goog.provided. This will return false for + * names that are available only as implicit namespaces. + * @param {string} name name of the object to look for. + * @return {boolean} Whether the name has been provided. + * @private + */ + goog.isProvided_ = function(name) { + return (name in goog.loadedModules_) || + (!goog.implicitNamespaces_[name] && goog.getObjectByName(name) != null); + }; + + /** + * Namespaces implicitly defined by goog.provide. For example, + * goog.provide('goog.events.Event') implicitly declares that 'goog' and + * 'goog.events' must be namespaces. + * + * @type {!Object} + * @private + */ + goog.implicitNamespaces_ = {'goog.module': true}; + + // NOTE: We add goog.module as an implicit namespace as goog.module is defined + // here and because the existing module package has not been moved yet out of + // the goog.module namespace. This satisifies both the debug loader and + // ahead-of-time dependency management. +} /** @@ -180,7 +739,7 @@ goog.getObjectByName = function(name, opt_obj) { var cur = opt_obj || goog.global; for (var i = 0; i < parts.length; i++) { cur = cur[parts[i]]; - if (!goog.isDefAndNotNull(cur)) { + if (cur == null) { return null; } } @@ -195,14 +754,18 @@ goog.getObjectByName = function(name, opt_obj) { * the names of the objects this file provides. * @param {!Array} requires An array of strings with * the names of the objects this file requires. + * @param {boolean|!Object=} opt_loadFlags Parameters indicating + * how the file must be loaded. The boolean 'true' is equivalent + * to {'module': 'goog'} for backwards-compatibility. Valid properties + * and values include {'module': 'goog'} and {'lang': 'es6'}. */ -goog.addDependency = function(relPath, provides, requires) { - goog.debugLoader_.addDependency(relPath, provides, requires); +goog.addDependency = function(relPath, provides, requires, opt_loadFlags) { + if (!COMPILED && goog.DEPENDENCIES_ENABLED) { + goog.debugLoader_.addDependency(relPath, provides, requires, opt_loadFlags); + } }; - - // NOTE(nnaze): The debug DOM loader was included in base.js as an original way // to do "debug-mode" development. The dependency system can sometimes be // confusing, as can the debug DOM loader's asynchronous nature. @@ -222,6 +785,30 @@ goog.addDependency = function(relPath, provides, requires) { // for example). See bootstrap/ for more information. +/** + * @define {boolean} Whether to enable the debug loader. + * + * If enabled, a call to goog.require() will attempt to load the namespace by + * appending a script tag to the DOM (if the namespace has been registered). + * + * If disabled, goog.require() will simply assert that the namespace has been + * provided (and depend on the fact that some outside tool correctly ordered + * the script). + */ +goog.ENABLE_DEBUG_LOADER = goog.define('goog.ENABLE_DEBUG_LOADER', true); + + +/** + * @param {string} msg + * @private + */ +goog.logToConsole_ = function(msg) { + if (goog.global.console) { + goog.global.console['error'](msg); + } +}; + + /** * Implements a system for the dynamic resolution of dependencies that works in * parallel with the BUILD system. @@ -236,20 +823,32 @@ goog.addDependency = function(relPath, provides, requires) { * namespace or module otherwise null. */ goog.require = function(namespace) { - // If the object already exists we do not need to do anything. - if (!goog.isProvided_(namespace)) { - var moduleLoaderState = goog.moduleLoaderState_; - goog.moduleLoaderState_ = null; - try { - goog.debugLoader_.load_(namespace); - } finally { - goog.moduleLoaderState_ = moduleLoaderState; + if (!COMPILED) { + // Might need to lazy load on old IE. + if (goog.ENABLE_DEBUG_LOADER) { + goog.debugLoader_.requested(namespace); } - } - return null; + // If the object already exists we do not need to do anything. + if (goog.isProvided_(namespace)) { + if (goog.isInModuleLoader_()) { + return goog.module.getInternal_(namespace); + } + } else if (goog.ENABLE_DEBUG_LOADER) { + var moduleLoaderState = goog.moduleLoaderState_; + goog.moduleLoaderState_ = null; + try { + goog.debugLoader_.load_(namespace); + } finally { + goog.moduleLoaderState_ = moduleLoaderState; + } + } + + return null; + } }; + /** * Requires a symbol for its type information. This is an indication to the * compiler that the symbol may appear in type annotations, yet it is not @@ -273,259 +872,3011 @@ goog.requireType = function(namespace) { return {}; }; + /** * Path for included scripts. * @type {string} */ goog.basePath = ''; + /** - * Normalize a file path by removing redundant ".." and extraneous "." file - * path components. - * @param {string} path - * @return {string} - * @private + * A hook for overriding the base path. + * @type {string|undefined} */ -goog.normalizePath_ = function(path) { - var components = path.split('/'); - var i = 0; - while (i < components.length) { - if (components[i] == '.') { - components.splice(i, 1); - } else if ( - i && components[i] == '..' && components[i - 1] && - components[i - 1] != '..') { - components.splice(--i, 2); - } else { - i++; - } - } - return components.join('/'); -}; +goog.global.CLOSURE_BASE_PATH; -//============================================================================== -// Language Enhancements -//============================================================================== +/** + * Whether to attempt to load Closure's deps file. By default, when uncompiled, + * deps files will attempt to be loaded. + * @type {boolean|undefined} + */ +goog.global.CLOSURE_NO_DEPS; /** - * Returns true if the specified value is defined and not null. - * @param {?} val Variable to test. - * @return {boolean} Whether variable is defined and not null. + * A function to import a single script. This is meant to be overridden when + * Closure is being run in non-HTML contexts, such as web workers. It's defined + * in the global scope so that it can be set before base.js is loaded, which + * allows deps.js to be imported properly. + * + * The first parameter the script source, which is a relative URI. The second, + * optional parameter is the script contents, in the event the script needed + * transformation. It should return true if the script was imported, false + * otherwise. + * @type {(function(string, string=): boolean)|undefined} + */ +goog.global.CLOSURE_IMPORT_SCRIPT; + + +/** + * Null function used for default values of callbacks, etc. + * @return {void} Nothing. + * @deprecated use '()=>{}' or 'function(){}' instead. + */ +goog.nullFunction = function() {}; + + +/** + * When defining a class Foo with an abstract method bar(), you can do: + * Foo.prototype.bar = goog.abstractMethod + * + * Now if a subclass of Foo fails to override bar(), an error will be thrown + * when bar() is invoked. + * + * @type {!Function} + * @throws {Error} when invoked to indicate the method should be overridden. + * @deprecated Use "@abstract" annotation instead of goog.abstractMethod in new + * code. See + * https://github.com/google/closure-compiler/wiki/@abstract-classes-and-methods */ -goog.isDefAndNotNull = function(val) { - // Note that undefined == null. - return val != null; +goog.abstractMethod = function() { + throw new Error('unimplemented abstract method'); }; -//============================================================================== -// goog.defineClass implementation -//============================================================================== +/** + * Adds a `getInstance` static method that always returns the same + * instance object. + * @param {!Function} ctor The constructor for the class to add the static + * method to. + * @suppress {missingProperties} 'instance_' isn't a property on 'Function' + * but we don't have a better type to use here. + */ +goog.addSingletonGetter = function(ctor) { + // instance_ is immediately set to prevent issues with sealed constructors + // such as are encountered when a constructor is returned as the export object + // of a goog.module in unoptimized code. + // Delcare type to avoid conformance violations that ctor.instance_ is unknown + /** @type {undefined|!Object} @suppress {underscore} */ + ctor.instance_ = undefined; + ctor.getInstance = function() { + if (ctor.instance_) { + return ctor.instance_; + } + if (goog.DEBUG) { + // NOTE: JSCompiler can't optimize away Array#push. + goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor; + } + // Cast to avoid conformance violations that ctor.instance_ is unknown + return /** @type {!Object|undefined} */ (ctor.instance_) = new ctor; + }; +}; -// There's a bug in the compiler where without collapse properties the -// Closure namespace defines do not guard code correctly. To help reduce code -// size also check for !COMPILED even though it redundant until this is fixed. /** - * Tries to detect the base path of base.js script that bootstraps Closure. + * All singleton classes that have been instantiated, for testing. Don't read + * it directly, use the `goog.testing.singleton` module. The compiler + * removes this variable if unused. + * @type {!Array} * @private */ -goog.findBasePath_ = function() { - /** @type {!Document} */ - var doc = goog.global.document; - // If we have a currentScript available, use it exclusively. - var currentScript = doc.currentScript; - if (currentScript) { - var scripts = [currentScript]; - } else { - var scripts = doc.getElementsByTagName('SCRIPT'); - } - // Search backwards since the current script is in almost all cases the one - // that has base.js. - for (var i = scripts.length - 1; i >= 0; --i) { - var script = /** @type {!HTMLScriptElement} */ (scripts[i]); - var src = script.src; - var qmark = src.lastIndexOf('?'); - var l = qmark == -1 ? src.length : qmark; - if (src.substr(l - 7, 7) == 'base.js') { - goog.basePath = src.substr(0, l - 7); - return; +goog.instantiatedSingletons_ = []; + + +/** + * @define {boolean} Whether to load goog.modules using `eval` when using + * the debug loader. This provides a better debugging experience as the + * source is unmodified and can be edited using Chrome Workspaces or similar. + * However in some environments the use of `eval` is banned + * so we provide an alternative. + */ +goog.LOAD_MODULE_USING_EVAL = goog.define('goog.LOAD_MODULE_USING_EVAL', true); + + +/** + * @define {boolean} Whether the exports of goog.modules should be sealed when + * possible. + */ +goog.SEAL_MODULE_EXPORTS = goog.define('goog.SEAL_MODULE_EXPORTS', goog.DEBUG); + + +/** + * The registry of initialized modules: + * The module identifier or path to module exports map. + * @private @const {!Object} + */ +goog.loadedModules_ = {}; + + +/** + * True if the debug loader enabled and used. + * @const {boolean} + */ +goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER; + + +/** + * @define {string} How to decide whether to transpile. Valid values + * are 'always', 'never', and 'detect'. The default ('detect') is to + * use feature detection to determine which language levels need + * transpilation. + */ +// NOTE(sdh): we could expand this to accept a language level to bypass +// detection: e.g. goog.TRANSPILE == 'es5' would transpile ES6 files but +// would leave ES3 and ES5 files alone. +goog.TRANSPILE = goog.define('goog.TRANSPILE', 'detect'); + +/** + * @define {boolean} If true assume that ES modules have already been + * transpiled by the jscompiler (in the same way that transpile.js would + * transpile them - to jscomp modules). Useful only for servers that wish to use + * the debug loader and transpile server side. Thus this is only respected if + * goog.TRANSPILE is "never". + */ +goog.ASSUME_ES_MODULES_TRANSPILED = + goog.define('goog.ASSUME_ES_MODULES_TRANSPILED', false); + + +/** + * @define {string} If a file needs to be transpiled what the output language + * should be. By default this is the highest language level this file detects + * the current environment supports. Generally this flag should not be set, but + * it could be useful to override. Example: If the current environment supports + * ES6 then by default ES7+ files will be transpiled to ES6, unless this is + * overridden. + * + * Valid values include: es3, es5, es6, es7, and es8. Anything not recognized + * is treated as es3. + * + * Note that setting this value does not force transpilation. Just if + * transpilation occurs this will be the output. So this is most useful when + * goog.TRANSPILE is set to 'always' and then forcing the language level to be + * something lower than what the environment detects. + */ +goog.TRANSPILE_TO_LANGUAGE = goog.define('goog.TRANSPILE_TO_LANGUAGE', ''); + + +/** + * @define {string} Path to the transpiler. Executing the script at this + * path (relative to base.js) should define a function $jscomp.transpile. + */ +goog.TRANSPILER = goog.define('goog.TRANSPILER', 'transpile.js'); + + +/** + * @define {string} Trusted Types policy name. If non-empty then Closure will + * use Trusted Types. + */ +goog.TRUSTED_TYPES_POLICY_NAME = + goog.define('goog.TRUSTED_TYPES_POLICY_NAME', 'goog'); + + +/** + * @package {?boolean} + * Visible for testing. + */ +goog.hasBadLetScoping = null; + + +/** + * @param {function(?):?|string} moduleDef The module definition. + */ +goog.loadModule = function(moduleDef) { + // NOTE: we allow function definitions to be either in the from + // of a string to eval (which keeps the original source intact) or + // in a eval forbidden environment (CSP) we allow a function definition + // which in its body must call `goog.module`, and return the exports + // of the module. + var previousState = goog.moduleLoaderState_; + try { + goog.moduleLoaderState_ = { + moduleName: '', + declareLegacyNamespace: false, + type: goog.ModuleType.GOOG + }; + var origExports = {}; + var exports = origExports; + if (typeof moduleDef === 'function') { + exports = moduleDef.call(undefined, exports); + } else if (typeof moduleDef === 'string') { + exports = goog.loadModuleFromSource_.call(undefined, exports, moduleDef); + } else { + throw new Error('Invalid module definition'); + } + + var moduleName = goog.moduleLoaderState_.moduleName; + if (typeof moduleName === 'string' && moduleName) { + // Don't seal legacy namespaces as they may be used as a parent of + // another namespace + if (goog.moduleLoaderState_.declareLegacyNamespace) { + // Whether exports was overwritten via default export assignment. + // This is important for legacy namespaces as it dictates whether + // previously a previously loaded implicit namespace should be clobbered + // or not. + var isDefaultExport = origExports !== exports; + goog.constructNamespace_(moduleName, exports, isDefaultExport); + } else if ( + goog.SEAL_MODULE_EXPORTS && Object.seal && + typeof exports == 'object' && exports != null) { + Object.seal(exports); + } + + var data = { + exports: exports, + type: goog.ModuleType.GOOG, + moduleId: goog.moduleLoaderState_.moduleName + }; + goog.loadedModules_[moduleName] = data; + } else { + throw new Error('Invalid module name \"' + moduleName + '\"'); } + } finally { + goog.moduleLoaderState_ = previousState; } }; -goog.findBasePath_(); /** - * A debug loader is responsible for downloading and executing javascript - * files in an unbundled, uncompiled environment. - * - * @struct @constructor @final @private + * @private @const + */ +goog.loadModuleFromSource_ = + /** @type {function(!Object, string):?} */ (function(exports) { + // NOTE: we avoid declaring parameters or local variables here to avoid + // masking globals or leaking values into the module definition. + 'use strict'; + eval(goog.CLOSURE_EVAL_PREFILTER_.createScript(arguments[1])); + return exports; + }); + + +/** + * Normalize a file path by removing redundant ".." and extraneous "." file + * path components. + * @param {string} path + * @return {string} + * @private */ -goog.DebugLoader_ = function() { - /** @private @const {!Object} */ - this.dependencies_ = {}; - /** @private @const {!Object} */ - this.idToPath_ = {}; - /** @private @const {!Object} */ - this.written_ = {}; - /** @private {!Array} */ - this.depsToLoad_ = []; +goog.normalizePath_ = function(path) { + var components = path.split('/'); + var i = 0; + while (i < components.length) { + if (components[i] == '.') { + components.splice(i, 1); + } else if ( + i && components[i] == '..' && components[i - 1] && + components[i - 1] != '..') { + components.splice(--i, 2); + } else { + i++; + } + } + return components.join('/'); }; /** - * Travserses the dependency graph and queues the given dependency, and all of - * its transitive dependencies, for loading and then starts loading if not - * paused. + * Provides a hook for loading a file when using Closure's goog.require() API + * with goog.modules. In particular this hook is provided to support Node.js. * - * @param {string} namespace - * @private + * @type {(function(string):string)|undefined} */ -goog.DebugLoader_.prototype.load_ = function(namespace) { - if (!this.getPathFromDeps_(namespace)) { - throw Error('goog.require could not find: ' + namespace); - } else { - var loader = this; +goog.global.CLOSURE_LOAD_FILE_SYNC; - var deps = []; - /** @param {string} namespace */ - var visit = function(namespace) { - var path = loader.getPathFromDeps_(namespace); +/** + * Loads file by synchronous XHR. Should not be used in production environments. + * @param {string} src Source URL. + * @return {?string} File contents, or null if load failed. + * @private + */ +goog.loadFileSync_ = function(src) { + if (goog.global.CLOSURE_LOAD_FILE_SYNC) { + return goog.global.CLOSURE_LOAD_FILE_SYNC(src); + } else { + try { + /** @type {XMLHttpRequest} */ + var xhr = new goog.global['XMLHttpRequest'](); + xhr.open('get', src, false); + xhr.send(); + // NOTE: Successful http: requests have a status of 200, but successful + // file: requests may have a status of zero. Any other status, or a + // thrown exception (particularly in case of file: requests) indicates + // some sort of error, which we treat as a missing or unavailable file. + return xhr.status == 0 || xhr.status == 200 ? xhr.responseText : null; + } catch (err) { + // No need to rethrow or log, since errors should show up on their own. + return null; + } + } +}; - if (!path) { - throw Error('Bad dependency path or symbol: ' + namespace); - } - if (loader.written_[path]) { - return; +/** + * Lazily retrieves the transpiler and applies it to the source. + * @param {string} code JS code. + * @param {string} path Path to the code. + * @param {string} target Language level output. + * @return {string} The transpiled code. + * @private + */ +goog.transpile_ = function(code, path, target) { + var jscomp = goog.global['$jscomp']; + if (!jscomp) { + goog.global['$jscomp'] = jscomp = {}; + } + var transpile = jscomp.transpile; + if (!transpile) { + var transpilerPath = goog.basePath + goog.TRANSPILER; + var transpilerCode = goog.loadFileSync_(transpilerPath); + if (transpilerCode) { + // This must be executed synchronously, since by the time we know we + // need it, we're about to load and write the ES6 code synchronously, + // so a normal script-tag load will be too slow. Wrapped in a function + // so that code is eval'd in the global scope. + (function() { + (0, eval)(transpilerCode + '\n//# sourceURL=' + transpilerPath); + }).call(goog.global); + // Even though the transpiler is optional, if $gwtExport is found, it's + // a sign the transpiler was loaded and the $jscomp.transpile *should* + // be there. + if (goog.global['$gwtExport'] && goog.global['$gwtExport']['$jscomp'] && + !goog.global['$gwtExport']['$jscomp']['transpile']) { + throw new Error( + 'The transpiler did not properly export the "transpile" ' + + 'method. $gwtExport: ' + JSON.stringify(goog.global['$gwtExport'])); } + // transpile.js only exports a single $jscomp function, transpile. We + // grab just that and add it to the existing definition of $jscomp which + // contains the polyfills. + goog.global['$jscomp'].transpile = + goog.global['$gwtExport']['$jscomp']['transpile']; + jscomp = goog.global['$jscomp']; + transpile = jscomp.transpile; + } + } + if (!transpile) { + // The transpiler is an optional component. If it's not available then + // replace it with a pass-through function that simply logs. + var suffix = ' requires transpilation but no transpiler was found.'; + transpile = jscomp.transpile = function(code, path) { + // TODO(sdh): figure out some way to get this error to show up + // in test results, noting that the failure may occur in many + // different ways, including in loadModule() before the test + // runner even comes up. + goog.logToConsole_(path + suffix); + return code; + }; + } + // Note: any transpilation errors/warnings will be logged to the console. + return transpile(code, path, target); +}; - loader.written_[path] = true; +//============================================================================== +// Language Enhancements +//============================================================================== - var dep = loader.dependencies_[path]; - for (var i = 0; i < dep.requires.length; i++) { - if (!goog.isProvided_(dep.requires[i])) { - visit(dep.requires[i]); - } - } - deps.push(dep); - }; +/** + * This is a "fixed" version of the typeof operator. It differs from the typeof + * operator in such a way that null returns 'null' and arrays return 'array'. + * @param {?} value The value to get the type of. + * @return {string} The name of the type. + */ +goog.typeOf = function(value) { + var s = typeof value; - visit(namespace); + if (s != 'object') { + return s; + } - var wasLoading = !!this.depsToLoad_.length; - this.depsToLoad_ = this.depsToLoad_.concat(deps); + if (!value) { + return 'null'; + } - if (!wasLoading) { - this.loadDeps_(); - } + if (Array.isArray(value)) { + return 'array'; } + return s; +}; + + +/** + * Returns true if the object looks like an array. To qualify as array like + * the value needs to be either a NodeList or an object with a Number length + * property. Note that for this function neither strings nor functions are + * considered "array-like". + * + * @param {?} val Variable to test. + * @return {boolean} Whether variable is an array. + */ +goog.isArrayLike = function(val) { + var type = goog.typeOf(val); + // We do not use goog.isObject here in order to exclude function values. + return type == 'array' || type == 'object' && typeof val.length == 'number'; +}; + + +/** + * Returns true if the object looks like a Date. To qualify as Date-like the + * value needs to be an object and have a getFullYear() function. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is a like a Date. + */ +goog.isDateLike = function(val) { + return goog.isObject(val) && typeof val.getFullYear == 'function'; +}; + + +/** + * Returns true if the specified value is an object. This includes arrays and + * functions. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is an object. + */ +goog.isObject = function(val) { + var type = typeof val; + return type == 'object' && val != null || type == 'function'; + // return Object(val) === val also works, but is slower, especially if val is + // not an object. +}; + + +/** + * Gets a unique ID for an object. This mutates the object so that further calls + * with the same object as a parameter returns the same value. The unique ID is + * guaranteed to be unique across the current session amongst objects that are + * passed into `getUid`. There is no guarantee that the ID is unique or + * consistent across sessions. It is unsafe to generate unique ID for function + * prototypes. + * + * @param {Object} obj The object to get the unique ID for. + * @return {number} The unique ID for the object. + */ +goog.getUid = function(obj) { + // TODO(arv): Make the type stricter, do not accept null. + return Object.prototype.hasOwnProperty.call(obj, goog.UID_PROPERTY_) && + obj[goog.UID_PROPERTY_] || + (obj[goog.UID_PROPERTY_] = ++goog.uidCounter_); }; /** - * Loads any queued dependencies until they are all loaded or paused. + * Whether the given object is already assigned a unique ID. + * + * This does not modify the object. * + * @param {!Object} obj The object to check. + * @return {boolean} Whether there is an assigned unique id for the object. + */ +goog.hasUid = function(obj) { + return !!obj[goog.UID_PROPERTY_]; +}; + + +/** + * Removes the unique ID from an object. This is useful if the object was + * previously mutated using `goog.getUid` in which case the mutation is + * undone. + * @param {Object} obj The object to remove the unique ID field from. + */ +goog.removeUid = function(obj) { + // TODO(arv): Make the type stricter, do not accept null. + + // In IE, DOM nodes are not instances of Object and throw an exception if we + // try to delete. Instead we try to use removeAttribute. + if (obj !== null && 'removeAttribute' in obj) { + obj.removeAttribute(goog.UID_PROPERTY_); + } + + try { + delete obj[goog.UID_PROPERTY_]; + } catch (ex) { + } +}; + + +/** + * Name for unique ID property. Initialized in a way to help avoid collisions + * with other closure JavaScript on the same page. + * @type {string} * @private */ -goog.DebugLoader_.prototype.loadDeps_ = function() { - var loader = this; +goog.UID_PROPERTY_ = 'closure_uid_' + ((Math.random() * 1e9) >>> 0); - while (this.depsToLoad_.length) { - (function() { - var loadCallDone = false; - var dep = loader.depsToLoad_.shift(); - try { - dep.load(); - } finally { - loadCallDone = true; - } - })(); +/** + * Counter for UID. + * @type {number} + * @private + */ +goog.uidCounter_ = 0; + + +/** + * Clones a value. The input may be an Object, Array, or basic type. Objects and + * arrays will be cloned recursively. + * + * WARNINGS: + * goog.cloneObject does not detect reference loops. Objects that + * refer to themselves will cause infinite recursion. + * + * goog.cloneObject is unaware of unique identifiers, and copies + * UIDs created by getUid into cloned results. + * + * @param {*} obj The value to clone. + * @return {*} A clone of the input value. + * @deprecated goog.cloneObject is unsafe. Prefer the goog.object methods. + */ +goog.cloneObject = function(obj) { + var type = goog.typeOf(obj); + if (type == 'object' || type == 'array') { + if (typeof obj.clone === 'function') { + return obj.clone(); + } + if (typeof Map !== 'undefined' && obj instanceof Map) { + return new Map(obj); + } else if (typeof Set !== 'undefined' && obj instanceof Set) { + return new Set(obj); + } + var clone = type == 'array' ? [] : {}; + for (var key in obj) { + clone[key] = goog.cloneObject(obj[key]); + } + return clone; } + + return obj; +}; + + +/** + * A native implementation of goog.bind. + * @param {?function(this:T, ...)} fn A function to partially apply. + * @param {T} selfObj Specifies the object which this should point to when the + * function is run. + * @param {...*} var_args Additional arguments that are partially applied to the + * function. + * @return {!Function} A partially-applied form of the function goog.bind() was + * invoked as a method of. + * @template T + * @private + */ +goog.bindNative_ = function(fn, selfObj, var_args) { + return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments)); }; /** - * @param {string} absPathOrId - * @return {?string} + * A pure-JS implementation of goog.bind. + * @param {?function(this:T, ...)} fn A function to partially apply. + * @param {T} selfObj Specifies the object which this should point to when the + * function is run. + * @param {...*} var_args Additional arguments that are partially applied to the + * function. + * @return {!Function} A partially-applied form of the function goog.bind() was + * invoked as a method of. + * @template T * @private */ -goog.DebugLoader_.prototype.getPathFromDeps_ = function(absPathOrId) { - return this.idToPath_[absPathOrId]; +goog.bindJs_ = function(fn, selfObj, var_args) { + if (!fn) { + throw new Error(); + } + + if (arguments.length > 2) { + var boundArgs = Array.prototype.slice.call(arguments, 2); + return function() { + // Prepend the bound arguments to the current arguments. + var newArgs = Array.prototype.slice.call(arguments); + Array.prototype.unshift.apply(newArgs, boundArgs); + return fn.apply(selfObj, newArgs); + }; + + } else { + return function() { + return fn.apply(selfObj, arguments); + }; + } }; /** - * Basic super class for all dependencies Closure Library can load. + * Partially applies this function to a particular 'this object' and zero or + * more arguments. The result is a new function with some arguments of the first + * function pre-filled and the value of this 'pre-specified'. + * + * Remaining arguments specified at call-time are appended to the pre-specified + * ones. * - * This default implementation is designed to load untranspiled, non-module - * scripts in a web broswer. + * Also see: {@link #partial}. * - * For transpiled non-goog.module files {@see goog.TranspiledDependency}. - * For goog.modules see {@see goog.GoogModuleDependency}. - * For untranspiled ES6 modules {@see goog.Es6ModuleDependency}. + * Usage: + *
var barMethBound = goog.bind(myFunction, myObj, 'arg1', 'arg2');
+ * barMethBound('arg3', 'arg4');
* - * @param {string} path Absolute path of this script. - * @param {!Array} requires goog symbols or relative paths to Closure - * this depends on. - * @struct @constructor + * @param {?function(this:T, ...)} fn A function to partially apply. + * @param {T} selfObj Specifies the object which this should point to when the + * function is run. + * @param {...*} var_args Additional arguments that are partially applied to the + * function. + * @return {!Function} A partially-applied form of the function goog.bind() was + * invoked as a method of. + * @template T + * @suppress {deprecated} See above. + * @deprecated use `=> {}` or Function.prototype.bind instead. */ -goog.Dependency = function(path, requires) { - /** @const */ - this.path = path; - /** @const */ - this.requires = requires; +goog.bind = function(fn, selfObj, var_args) { + // TODO(nicksantos): narrow the type signature. + if (Function.prototype.bind && + // NOTE(nicksantos): Somebody pulled base.js into the default Chrome + // extension environment. This means that for Chrome extensions, they get + // the implementation of Function.prototype.bind that calls goog.bind + // instead of the native one. Even worse, we don't want to introduce a + // circular dependency between goog.bind and Function.prototype.bind, so + // we have to hack this to make sure it works correctly. + Function.prototype.bind.toString().indexOf('native code') != -1) { + goog.bind = goog.bindNative_; + } else { + goog.bind = goog.bindJs_; + } + return goog.bind.apply(null, arguments); }; + /** - * Map of script ready / state change callbacks. Old IE cannot handle putting - * these properties on goog.global. + * Like goog.bind(), except that a 'this object' is not required. Useful when + * the target function is already bound. + * + * Usage: + * var g = goog.partial(f, arg1, arg2); + * g(arg3, arg4); * - * @private @const {!Object} + * @param {Function} fn A function to partially apply. + * @param {...*} var_args Additional arguments that are partially applied to fn. + * @return {!Function} A partially-applied form of the function goog.partial() + * was invoked as a method of. */ -goog.Dependency.callbackMap_ = {}; +goog.partial = function(fn, var_args) { + var args = Array.prototype.slice.call(arguments, 1); + return function() { + // Clone the array (with slice()) and append additional arguments + // to the existing arguments. + var newArgs = args.slice(); + newArgs.push.apply(newArgs, arguments); + return fn.apply(/** @type {?} */ (this), newArgs); + }; +}; /** - * Starts loading this dependency. This dependency can pause loading if it - * needs to and resume it later via the controller interface. + * Copies all the members of a source object to a target object. This method + * does not work on all browsers for all objects that contain keys such as + * toString or hasOwnProperty. Use goog.object.extend for this purpose. + * + * NOTE: Some have advocated for the use of goog.mixin to setup classes + * with multiple inheritence (traits, mixins, etc). However, as it simply + * uses "for in", this is not compatible with ES6 classes whose methods are + * non-enumerable. Changing this, would break cases where non-enumerable + * properties are not expected. * - * When this is loaded it should call controller.loaded(). Note that this will - * end up calling the loaded method of this dependency; there is no need to - * call it explicitly. + * @param {Object} target Target. + * @param {Object} source Source. + * @deprecated Prefer Object.assign */ -goog.Dependency.prototype.load = function() { - /** @type {!HTMLDocument} */ - var doc = goog.global.document; - doc.write(''); - document.write(''); + return closureCompiler({...defaultOptions, ...options}, {platform}); } -`; - -let deps = []; -return gulp.src(maybeAddClosureLibrary(['core/**/**/*.js'])) - .pipe(through2.obj((file, _enc, cb) => { - const result = closureDeps.parser.parseFile(file.path); - for (const dep of result.dependencies) { - deps.push(dep); - } - cb(null); - })) - .on('end', () => { - // Update the path to closure for any files that we don't know the full path - // of (parsed from a goog.addDependency call). - for (const dep of deps) { - dep.setClosurePath(closurePath); - } - - const addDependency = closureDeps.depFile - .getDepFileText(closurePath, deps) - .replace(/\\/g, '\/'); - - const requires = `goog.addDependency("base.js", [], []); - -// Load Blockly. -goog.require('Blockly.requires'); -`; - fs.writeFileSync('blockly_uncompressed.js', - header + - addDependency + - requires + - footer); - }); -}; /** - * This task builds Blockly's lang files. - * msg/*.js + * This task compiles the core library, blocks and generators, creating + * blockly_compressed.js, blocks_compressed.js, etc. + * + * The deps.js file must be up-to-date. */ -function buildLangfiles(done) { - // Run js_to_json.py - const jsToJsonCmd = `python ./scripts/i18n/js_to_json.py \ ---input_file ${path.join('msg', 'messages.js')} \ ---output_dir ${path.join('msg', 'json')} \ ---quiet`; - execSync(jsToJsonCmd, { stdio: 'inherit' }); - - // Run create_messages.py - let json_files = fs.readdirSync(path.join('msg', 'json')); - json_files = json_files.filter(file => file.endsWith('json') && - !(new RegExp(/(keys|synonyms|qqq|constants)\.json$/).test(file))); - json_files = json_files.map(file => path.join('msg', 'json', file)); - const createMessagesCmd = `python ./scripts/i18n/create_messages.py \ - --source_lang_file ${path.join('msg', 'json', 'en.json')} \ - --source_synonym_file ${path.join('msg', 'json', 'synonyms.json')} \ - --source_constants_file ${path.join('msg', 'json', 'constants.json')} \ - --key_file ${path.join('msg', 'json', 'keys.json')} \ - --output_dir ${path.join('msg', 'js')} \ - --quiet ${json_files.join(' ')}`; - execSync(createMessagesCmd, { stdio: 'inherit' }); - - done(); +function buildCompiled() { + // Get chunking. + const chunkOptions = getChunkOptions(); + // Closure Compiler options. + const packageJson = getPackageJson(); // For version number. + const options = { + define: 'Blockly.VERSION="' + packageJson.version + '"', + chunk: chunkOptions.chunk, + chunk_wrapper: chunkOptions.chunk_wrapper, + rename_prefix_namespace: NAMESPACE_OBJECT, + // Don't supply the list of source files in chunkOptions.js as an + // option to Closure Compiler; instead feed them as input via gulp.src. + }; + + // Fire up compilation pipline. + return gulp.src(chunkOptions.js, {base: './'}) + .pipe(stripApacheLicense()) + .pipe(gulp.sourcemaps.init()) + .pipe(gulp.rename(flattenCorePaths)) + .pipe(compile(options)) + .pipe(gulp.rename({suffix: COMPILED_SUFFIX})) + .pipe(gulp.sourcemaps.mapSources(unflattenCorePaths)) + .pipe( + gulp.sourcemaps.write('.', {includeContent: false, sourceRoot: './'})) + .pipe(gulp.dest(BUILD_DIR)); }; /** @@ -443,55 +541,29 @@ function buildLangfiles(done) { */ function buildAdvancedCompilationTest() { const srcs = [ - 'tests/compile/main.js', 'tests/compile/test_blocks.js', 'core/**/**/*.js', - 'blocks/*.js', 'generators/**/*.js' + 'closure/goog/base_minimal.js', + 'core/**/*.js', 'blocks/**/*.js', 'generators/**/*.js', + 'tests/compile/main.js', 'tests/compile/test_blocks.js', ]; - return gulp.src(maybeAddClosureLibrary(srcs), {base: './'}) + + // Closure Compiler options. + const options = { + dependency_mode: 'PRUNE', + compilation_level: 'ADVANCED_OPTIMIZATIONS', + entry_point: './tests/compile/main.js', + js_output_file: 'main_compressed.js', + }; + return gulp.src(srcs, {base: './'}) .pipe(stripApacheLicense()) .pipe(gulp.sourcemaps.init()) - // Directories in Blockly are used to group similar files together - // but are not used to limit access with @package, instead the - // method means something is internal to Blockly and not a public - // API. - // Flatten all files so they're in the same directory, but ensure that - // files with the same name don't conflict. - .pipe(gulp.rename(function(p) { - if (p.dirname.indexOf('core') === 0) { - var dirname = p.dirname.replace( - new RegExp(path.sep.replace(/\\/, '\\\\'), "g"), "-"); - p.dirname = ""; - p.basename = dirname + "-" + p.basename; - } - })) - .pipe(compile( - { - dependency_mode: 'PRUNE', - compilation_level: 'ADVANCED_OPTIMIZATIONS', - entry_point: './tests/compile/main.js', - js_output_file: 'main_compressed.js', - externs: ['./externs/svg-externs.js', './externs/goog-externs.js'], - }, - argv.verbose, argv.strict)) - .pipe(gulp.sourcemaps.mapSources(function(sourcePath, file) { - return sourcePath.replace(/-/g, '/'); - })) + .pipe(gulp.rename(flattenCorePaths)) + .pipe(compile(options)) + .pipe(gulp.sourcemaps.mapSources(unflattenCorePaths)) .pipe(gulp.sourcemaps.write( '.', {includeContent: false, sourceRoot: '../../'})) .pipe(gulp.dest('./tests/compile/')); } -/** - * This tasks builds Blockly's core files: - * blockly_compressed.js - * blocks_compressed.js - * blockly_uncompressed.js - */ -const buildCore = gulp.parallel( - buildCompressed, - buildBlocks, - buildUncompressed -); - /** * This task builds all of Blockly: * blockly_compressed.js @@ -503,20 +575,54 @@ const buildCore = gulp.parallel( * dart_compressed.js * blockly_uncompressed.js * msg/json/*.js + * test/deps*.js */ const build = gulp.parallel( - buildCore, - buildGenerators, - buildLangfiles -); + gulp.series(buildDeps, buildCompiled), + buildLangfiles, + ); + +/** + * This task copies built files from BUILD_DIR back to the repository + * so they can be committed to git. + */ +function checkinBuilt() { + return gulp.src([ + `${BUILD_DIR}/**.js`, + `${BUILD_DIR}/**.js.map`, + `${BUILD_DIR}/**/**.js`, + `${BUILD_DIR}/**/**.js.map`, + ]).pipe(gulp.dest('.')); +}; + +/** + * This task cleans the build directory (by deleting it). + */ +function cleanBuildDir(done) { + // Sanity check. + if (BUILD_DIR === '.' || BUILD_DIR === '/') { + throw new Error(`Refusing to rm -rf ${BUILD_DIR}`); + } + rimraf(BUILD_DIR, done); +} + +/** + * Runs clang format on all files in the core directory. + */ +function format() { + return gulp.src(['core/**/*.js'], {base: '.'}) + .pipe(clangFormatter.format('file', clangFormat)) + .pipe(gulp.dest('.')); +}; module.exports = { build: build, - core: buildCore, - blocks: buildBlocks, + deps: buildDeps, + generateLangfiles: generateLangfiles, langfiles: buildLangfiles, - uncompressed: buildUncompressed, - compressed: buildCompressed, - generators: buildGenerators, + compiled: buildCompiled, + format: format, + checkinBuilt: checkinBuilt, + cleanBuildDir: cleanBuildDir, advancedCompilationTest: buildAdvancedCompilationTest, } diff --git a/scripts/gulpfiles/chunks.json b/scripts/gulpfiles/chunks.json new file mode 100644 index 00000000000..4ebb9e9e347 --- /dev/null +++ b/scripts/gulpfiles/chunks.json @@ -0,0 +1,345 @@ +{ + "chunk": [ + "blockly:267", + "all:10:blockly", + "all1:11:blockly", + "all2:11:blockly", + "all3:11:blockly", + "all4:11:blockly", + "all5:11:blockly" + ], + "js": [ + ".\\core\\inject.js", + ".\\core\\flyout_vertical.js", + ".\\core\\toolbox\\toolbox.js", + ".\\core\\interfaces\\i_styleable.js", + ".\\core\\flyout_horizontal.js", + ".\\core\\generator.js", + ".\\core\\flyout_base.js", + ".\\core\\flyout_metrics_manager.js", + ".\\core\\field_variable.js", + ".\\core\\field_number.js", + ".\\core\\field_multilineinput.js", + ".\\core\\field_label_serializable.js", + ".\\core\\field_dropdown.js", + ".\\core\\field_colour.js", + ".\\core\\field_checkbox.js", + ".\\core\\field_angle.js", + ".\\core\\toolbox\\collapsible_category.js", + ".\\core\\renderers\\zelos\\measurables\\top_row.js", + ".\\core\\renderers\\zelos\\measurables\\inputs.js", + ".\\core\\renderers\\zelos\\measurables\\row_elements.js", + ".\\core\\renderers\\zelos\\marker_svg.js", + ".\\core\\renderers\\zelos\\renderer.js", + ".\\core\\field_textinput.js", + ".\\core\\field_image.js", + ".\\core\\renderers\\zelos\\info.js", + ".\\core\\renderers\\zelos\\path_object.js", + ".\\core\\renderers\\zelos\\drawer.js", + ".\\core\\renderers\\zelos\\constants.js", + ".\\core\\renderers\\zelos\\measurables\\bottom_row.js", + ".\\core\\renderers\\zelos\\zelos.js", + ".\\core\\renderers\\thrasos\\renderer.js", + ".\\core\\renderers\\thrasos\\info.js", + ".\\core\\renderers\\thrasos\\thrasos.js", + ".\\core\\serialization\\workspaces.js", + ".\\core\\serialization\\variables.js", + ".\\core\\renderers\\minimalist\\renderer.js", + ".\\core\\renderers\\minimalist\\info.js", + ".\\core\\renderers\\minimalist\\drawer.js", + ".\\core\\renderers\\minimalist\\constants.js", + ".\\core\\renderers\\minimalist\\minimalist.js", + ".\\core\\renderers\\geras\\measurables\\statement_input.js", + ".\\core\\renderers\\geras\\path_object.js", + ".\\core\\renderers\\geras\\renderer.js", + ".\\core\\renderers\\geras\\measurables\\inline_input.js", + ".\\core\\renderers\\geras\\info.js", + ".\\core\\renderers\\geras\\highlight_constants.js", + ".\\core\\renderers\\geras\\highlighter.js", + ".\\core\\renderers\\geras\\drawer.js", + ".\\core\\renderers\\geras\\constants.js", + ".\\core\\renderers\\geras\\geras.js", + ".\\core\\theme\\zelos.js", + ".\\core\\theme\\themes.js", + ".\\core\\shortcut_items.js", + ".\\core\\contextmenu_items.js", + ".\\core\\widgetdiv.js", + ".\\core\\clipboard.js", + ".\\core\\menuitem.js", + ".\\core\\menu.js", + ".\\core\\contextmenu.js", + ".\\core\\blocks.js", + ".\\core\\utils\\global.js", + ".\\core\\utils\\useragent.js", + ".\\core\\utils\\svg.js", + ".\\core\\utils\\dom.js", + ".\\core\\connection_checker.js", + ".\\core\\events\\events_ui.js", + ".\\core\\events\\events_toolbox_item_select.js", + ".\\core\\events\\events_move_block_to_module.js", + ".\\core\\events\\events_module_move.js", + ".\\core\\events\\events_module_rename.js", + ".\\core\\events\\events_module_activate.js", + ".\\core\\events\\events_module_delete.js", + ".\\core\\events\\events_module_base.js", + ".\\core\\events\\events_module_create.js", + ".\\core\\events\\workspace_events.js", + ".\\core\\events\\events.js", + ".\\core\\module_model.js", + ".\\core\\module_manager.js", + ".\\core\\keyboard_nav\\ast_node.js", + ".\\core\\keyboard_nav\\cursor.js", + ".\\core\\registry.js", + ".\\core\\utils\\math.js", + ".\\core\\utils\\idgenerator.js", + ".\\core\\utils\\array.js", + ".\\core\\workspace.js", + ".\\core\\utils\\object.js", + ".\\core\\events\\events_block_delete.js", + ".\\core\\toolbox\\separator.js", + ".\\core\\toolbox\\toolbox_item.js", + ".\\core\\keyboard_nav\\basic_cursor.js", + ".\\core\\keyboard_nav\\tab_navigate_cursor.js", + ".\\core\\warning.js", + ".\\core\\events\\events_bubble_open.js", + ".\\core\\comment.js", + ".\\core\\events\\events_block_move.js", + ".\\core\\events\\events_block_drag.js", + ".\\core\\bump_objects.js", + ".\\core\\block_dragger.js", + ".\\core\\workspace_dragger.js", + ".\\core\\interfaces\\i_block_dragger.js", + ".\\core\\events\\events_viewport.js", + ".\\core\\events\\events_theme_change.js", + ".\\core\\events\\events_block_create.js", + ".\\core\\modulebar.js", + ".\\core\\events\\events_click.js", + ".\\core\\zoom_controls.js", + ".\\core\\workspace_drag_surface_svg.js", + ".\\core\\events\\events_selected.js", + ".\\core\\events\\events_comment_move.js", + ".\\core\\events\\events_comment_delete.js", + ".\\core\\events\\events_comment_create.js", + ".\\core\\events\\events_comment_base.js", + ".\\core\\events\\events_comment_change.js", + ".\\core\\workspace_comment.js", + ".\\core\\interfaces\\i_movable.js", + ".\\core\\interfaces\\i_selectable.js", + ".\\core\\interfaces\\i_copyable.js", + ".\\core\\workspace_comment_svg.js", + ".\\core\\workspace_audio.js", + ".\\core\\events\\events_trashcan_open.js", + ".\\core\\drag_target.js", + ".\\core\\delete_area.js", + ".\\core\\positionable_helpers.js", + ".\\core\\trashcan.js", + ".\\core\\touch_gesture.js", + ".\\core\\theme_manager.js", + ".\\core\\scrollbar_pair.js", + ".\\core\\options.js", + ".\\core\\interfaces\\i_bounded_element.js", + ".\\core\\grid.js", + ".\\core\\flyout_button.js", + ".\\core\\contextmenu_registry.js", + ".\\core\\theme\\classic.js", + ".\\core\\blockly_options.js", + ".\\core\\utils.js", + ".\\core\\renderers\\measurables\\top_row.js", + ".\\core\\renderers\\measurables\\statement_input.js", + ".\\core\\renderers\\measurables\\square_corner.js", + ".\\core\\renderers\\measurables\\spacer_row.js", + ".\\core\\renderers\\measurables\\round_corner.js", + ".\\core\\renderers\\common\\path_object.js", + ".\\core\\interfaces\\i_positionable.js", + ".\\core\\interfaces\\i_drag_target.js", + ".\\core\\interfaces\\i_delete_area.js", + ".\\core\\interfaces\\i_component.js", + ".\\core\\interfaces\\i_autohideable.js", + ".\\core\\component_manager.js", + ".\\core\\insertion_marker_manager.js", + ".\\core\\renderers\\common\\i_path_object.js", + ".\\core\\renderers\\common\\drawer.js", + ".\\core\\renderers\\common\\renderer.js", + ".\\core\\renderers\\measurables\\previous_connection.js", + ".\\core\\renderers\\measurables\\output_connection.js", + ".\\core\\renderers\\measurables\\jagged_edge.js", + ".\\core\\renderers\\measurables\\input_row.js", + ".\\core\\renderers\\measurables\\inline_input.js", + ".\\core\\scrollbar.js", + ".\\core\\utils\\metrics.js", + ".\\core\\interfaces\\i_metrics_manager.js", + ".\\core\\metrics_manager.js", + ".\\core\\interfaces\\i_deletable.js", + ".\\core\\interfaces\\i_draggable.js", + ".\\core\\interfaces\\i_contextmenu.js", + ".\\core\\interfaces\\i_bubble.js", + ".\\core\\block_drag_surface.js", + ".\\core\\bubble.js", + ".\\core\\icon.js", + ".\\core\\renderers\\measurables\\icon.js", + ".\\core\\renderers\\measurables\\hat.js", + ".\\core\\renderers\\measurables\\field.js", + ".\\core\\renderers\\measurables\\external_value_input.js", + ".\\core\\renderers\\common\\info.js", + ".\\core\\renderers\\common\\debugger.js", + ".\\core\\renderers\\measurables\\input_connection.js", + ".\\core\\renderers\\measurables\\in_row_spacer.js", + ".\\core\\renderers\\measurables\\row.js", + ".\\core\\renderers\\measurables\\types.js", + ".\\core\\renderers\\measurables\\base.js", + ".\\core\\renderers\\measurables\\connection.js", + ".\\core\\renderers\\measurables\\next_connection.js", + ".\\core\\renderers\\measurables\\bottom_row.js", + ".\\core\\renderers\\common\\debug.js", + ".\\core\\renderers\\common\\block_rendering.js", + ".\\core\\variables_dynamic.js", + ".\\core\\events\\events_var_rename.js", + ".\\core\\events\\events_var_delete.js", + ".\\core\\variable_map.js", + ".\\core\\names.js", + ".\\core\\events\\events_block_base.js", + ".\\core\\events\\events_block_change.js", + ".\\core\\events\\events_ui_base.js", + ".\\core\\events\\events_marker_move.js", + ".\\core\\renderers\\common\\marker_svg.js", + ".\\core\\keyboard_nav\\marker.js", + ".\\core\\marker_manager.js", + ".\\core\\field_label.js", + ".\\core\\interfaces\\i_registrable_field.js", + ".\\core\\field_registry.js", + ".\\core\\input.js", + ".\\core\\interfaces\\i_registrable.js", + ".\\core\\utils\\keycodes.js", + ".\\core\\shortcut_registry.js", + ".\\core\\interfaces\\i_keyboard_accessible.js", + ".\\core\\interfaces\\i_ast_node_location_with_block.js", + ".\\core\\interfaces\\i_ast_node_location.js", + ".\\core\\interfaces\\i_ast_node_location_svg.js", + ".\\core\\dropdowndiv.js", + ".\\core\\theme.js", + ".\\core\\constants.js", + ".\\core\\interfaces\\i_connection_checker.js", + ".\\core\\connection_db.js", + ".\\core\\rendered_connection.js", + ".\\core\\utils\\svg_paths.js", + ".\\core\\renderers\\common\\constants.js", + ".\\core\\field.js", + ".\\core\\procedures.js", + ".\\core\\workspace_svg.js", + ".\\core\\utils\\rect.js", + ".\\core\\utils\\coordinate.js", + ".\\core\\utils\\style.js", + ".\\core\\utils\\svg_math.js", + ".\\core\\bubble_dragger.js", + ".\\core\\block_animations.js", + ".\\core\\gesture.js", + ".\\core\\touch.js", + ".\\core\\browser_events.js", + ".\\core\\tooltip.js", + ".\\core\\block_svg.js", + ".\\core\\interfaces\\i_flyout.js", + ".\\core\\interfaces\\i_toolbox.js", + ".\\core\\interfaces\\i_toolbox_item.js", + ".\\core\\interfaces\\i_selectable_toolbox_item.js", + ".\\core\\interfaces\\i_collapsible_toolbox_item.js", + ".\\core\\utils\\aria.js", + ".\\core\\utils\\deprecation.js", + ".\\core\\css.js", + ".\\core\\toolbox\\category.js", + ".\\core\\input_types.js", + ".\\core\\utils\\size.js", + ".\\core\\serialization\\exceptions.js", + ".\\core\\interfaces\\i_serializer.js", + ".\\core\\serialization\\registry.js", + ".\\core\\serialization\\priorities.js", + ".\\core\\serialization\\blocks.js", + ".\\core\\utils\\toolbox.js", + ".\\core\\connection_type.js", + ".\\core\\internal_constants.js", + ".\\core\\mutator.js", + ".\\core\\msg.js", + ".\\core\\utils\\colour.js", + ".\\core\\utils\\parsing.js", + ".\\core\\extensions.js", + ".\\core\\block.js", + ".\\core\\utils\\string.js", + ".\\core\\dialog.js", + ".\\core\\utils\\xml.js", + ".\\core\\events\\events_var_base.js", + ".\\core\\events\\events_var_create.js", + ".\\core\\variable_model.js", + ".\\core\\variables.js", + ".\\core\\events\\events_abstract.js", + ".\\core\\events\\utils.js", + ".\\core\\xml.js", + ".\\core\\connection.js", + ".\\core\\common.js", + ".\\closure\\goog\\base_minimal.js", + ".\\core\\blockly.js", + ".\\blocks\\variables_dynamic.js", + ".\\blocks\\variables.js", + ".\\blocks\\text.js", + ".\\blocks\\procedures.js", + ".\\blocks\\math.js", + ".\\blocks\\loops.js", + ".\\blocks\\logic.js", + ".\\blocks\\lists.js", + ".\\blocks\\colour.js", + ".\\blocks\\all.js", + ".\\generators\\javascript\\variables_dynamic.js", + ".\\generators\\javascript\\variables.js", + ".\\generators\\javascript\\text.js", + ".\\generators\\javascript\\procedures.js", + ".\\generators\\javascript\\math.js", + ".\\generators\\javascript\\loops.js", + ".\\generators\\javascript\\logic.js", + ".\\generators\\javascript\\lists.js", + ".\\generators\\javascript.js", + ".\\generators\\javascript\\colour.js", + ".\\generators\\javascript\\all.js", + ".\\generators\\python\\variables_dynamic.js", + ".\\generators\\python\\variables.js", + ".\\generators\\python\\text.js", + ".\\generators\\python\\procedures.js", + ".\\generators\\python\\math.js", + ".\\generators\\python\\loops.js", + ".\\generators\\python\\logic.js", + ".\\generators\\python\\lists.js", + ".\\generators\\python.js", + ".\\generators\\python\\colour.js", + ".\\generators\\python\\all.js", + ".\\generators\\php\\variables_dynamic.js", + ".\\generators\\php\\variables.js", + ".\\generators\\php\\text.js", + ".\\generators\\php\\procedures.js", + ".\\generators\\php\\math.js", + ".\\generators\\php\\loops.js", + ".\\generators\\php\\logic.js", + ".\\generators\\php\\lists.js", + ".\\generators\\php.js", + ".\\generators\\php\\colour.js", + ".\\generators\\php\\all.js", + ".\\generators\\lua\\variables_dynamic.js", + ".\\generators\\lua\\variables.js", + ".\\generators\\lua\\text.js", + ".\\generators\\lua\\procedures.js", + ".\\generators\\lua\\math.js", + ".\\generators\\lua\\loops.js", + ".\\generators\\lua\\logic.js", + ".\\generators\\lua\\lists.js", + ".\\generators\\lua.js", + ".\\generators\\lua\\colour.js", + ".\\generators\\lua\\all.js", + ".\\generators\\dart\\variables_dynamic.js", + ".\\generators\\dart\\variables.js", + ".\\generators\\dart\\text.js", + ".\\generators\\dart\\procedures.js", + ".\\generators\\dart\\math.js", + ".\\generators\\dart\\loops.js", + ".\\generators\\dart\\logic.js", + ".\\generators\\dart\\lists.js", + ".\\generators\\dart.js", + ".\\generators\\dart\\colour.js", + ".\\generators\\dart\\all.js" + ] +} diff --git a/scripts/gulpfiles/cleanup_tasks.js b/scripts/gulpfiles/cleanup_tasks.js index 3387eab65bd..e6c8501ff51 100644 --- a/scripts/gulpfiles/cleanup_tasks.js +++ b/scripts/gulpfiles/cleanup_tasks.js @@ -44,10 +44,10 @@ function sortRequires() { const requireTypesList = []; let firstIndex; let lastIndex; - while ((match = re.exec(contents)) != null) { - if (match[1] == 'require') requiresList.push(match[2]); - if (match[1] == 'requireType') requireTypesList.push(match[2]); - if (firstIndex == undefined) { + while ((match = re.exec(contents)) !== null) { + if (match[1] === 'require') requiresList.push(match[2]); + if (match[1] === 'requireType') requireTypesList.push(match[2]); + if (firstIndex === undefined) { firstIndex = match.index; } else { lastIndex = re.lastIndex; @@ -70,7 +70,7 @@ function sortRequires() { ''; const requires = `${requiresSection}${requireTypesSection}\n`; - if (firstIndex != undefined & lastIndex != undefined) { + if (firstIndex !== undefined & lastIndex !== undefined) { file.contents = Buffer.from( contents.substring(0, firstIndex) + requires + contents.substring(lastIndex).trimStart()); diff --git a/scripts/gulpfiles/config.js b/scripts/gulpfiles/config.js new file mode 100644 index 00000000000..1c6e87bc6ed --- /dev/null +++ b/scripts/gulpfiles/config.js @@ -0,0 +1,38 @@ +/** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Common configuration for Gulp scripts. + */ + +var path = require('path'); + +// Paths are all relative to the repository root. Do not include +// trailing slash. +// +// TODO(#5007): If you modify these values, you must also modify the +// corresponding values in the following files: +// +// - tests/scripts/compile_typings.sh +// - tests/scripts/check_metadata.sh + +let buildDir = 'build' +const outputArgIndex = process.argv.indexOf("--output"); +if (outputArgIndex > -1 && process.argv[outputArgIndex + 1]) { + buildDir = process.argv[outputArgIndex + 1]; +} + +module.exports = { + // Directory to write compiled output to. + BUILD_DIR: buildDir, + + // Directory in which to assemble (and from which to publish) the + // blockly npm package. + RELEASE_DIR: 'dist', + + // Directory to write typings output to. + TYPINGS_BUILD_DIR: path.join('build', 'typings'), +}; diff --git a/scripts/gulpfiles/git_tasks.js b/scripts/gulpfiles/git_tasks.js index f08ab9e0063..bfc28df4c29 100644 --- a/scripts/gulpfiles/git_tasks.js +++ b/scripts/gulpfiles/git_tasks.js @@ -103,7 +103,9 @@ const updateGithubPages = gulp.series( execSync('git reset --hard upstream/develop', { stdio: 'inherit' }); done(); }, + buildTasks.cleanBuildDir, buildTasks.build, + buildTasks.checkinBuilt, function(done) { execSync('git commit -am "Rebuild"', { stdio: 'inherit' }); execSync('git push ' + upstream_url + ' gh-pages --force', { stdio: 'inherit' }); diff --git a/scripts/gulpfiles/package_tasks.js b/scripts/gulpfiles/package_tasks.js index e0b48ad3685..3bb334fba7e 100644 --- a/scripts/gulpfiles/package_tasks.js +++ b/scripts/gulpfiles/package_tasks.js @@ -17,18 +17,21 @@ gulp.umd = require('gulp-umd'); var path = require('path'); var fs = require('fs'); -var { getPackageJson } = require('./helper_tasks'); - -const blocklyRoot = '../../'; +var rimraf = require('rimraf'); +var {getPackageJson} = require('./helper_tasks'); +var {BUILD_DIR, RELEASE_DIR, TYPINGS_BUILD_DIR} = require('./config'); // The destination path where all the NPM distribution files will go. -let packageDistribution = 'dist'; +let releaseDir = RELEASE_DIR; const outputArgIndex = process.argv.indexOf("--output"); if (outputArgIndex > -1 && process.argv[outputArgIndex + 1]) { - packageDistribution = process.argv[outputArgIndex + 1]; + releaseDir = process.argv[outputArgIndex + 1]; } +// Path to template files for gulp-umd. +const TEMPLATE_DIR = 'scripts/package/templates'; + /** * A helper method for wrapping a file into a Universal Module Definition. * @param {string} namespace The export namespace. @@ -39,7 +42,7 @@ function packageUMD(namespace, dependencies) { dependencies: function () { return dependencies; }, namespace: function () { return namespace; }, exports: function () { return namespace; }, - template: path.join(__dirname, `${blocklyRoot}/scripts/package/templates/umd.template`) + template: path.join(TEMPLATE_DIR, 'umd.template') }); }; @@ -53,26 +56,52 @@ function packageCommonJS(namespace, dependencies) { dependencies: function () { return dependencies; }, namespace: function () { return namespace; }, exports: function () { return namespace; }, - template: path.join(__dirname, `${blocklyRoot}/scripts/package/templates/node.template`) + template: path.join(TEMPLATE_DIR, 'node.template') }); }; +// Sanity check that the BUILD_DIR directory exists, and that certain +// files are in it. +function checkBuildDir(done) { + // Check that directory exists. + if (!fs.existsSync(BUILD_DIR)) { + done(new Error(`The ${BUILD_DIR} directory does not exist. ` + + 'Has packageTasks.build been run?')); + return; + } + // Check files built by buildTasks.build exist in BUILD_DIR. + for (const fileName of [ + 'blockly_compressed.js', // buildTasks.buildCompressed + 'blocks_compressed.js', // buildTasks.buildBlocks + 'javascript_compressed.js', // buildTasks.buildGenerators + 'msg/js/en.js', // buildTaks.buildLangfiles + ]) { + if (!fs.existsSync(`${BUILD_DIR}/${fileName}`)) { + done(new Error( + `Your ${BUILD_DIR} directory does not contain ${fileName}. ` + + 'Has packageTasks.build been run? Try "npm run build".')); + return; + } + } + done(); +} + /** - * This task copies source files into the distribution directory. + * This task copies source files into the release directory. */ function packageSources() { return gulp.src(['core/**/**.js', 'blocks/**.js', 'generators/**/**.js'], {base: '.'}) - .pipe(gulp.dest(packageDistribution)); + .pipe(gulp.dest(releaseDir)); }; /** - * This task copies the compressed files and their source maps into the - * distribution directory. + * This task copies the compressed files and their source maps into + * the release directory. */ function packageCompressed() { - return gulp.src('*_compressed.js?(.map)') - .pipe(gulp.dest(packageDistribution)); + return gulp.src('*_compressed.js?(.map)', {cwd: BUILD_DIR}) + .pipe(gulp.dest(releaseDir)); }; /** @@ -87,7 +116,7 @@ function packageBlockly() { cjs: './blockly_compressed', }])) .pipe(gulp.rename('blockly.js')) - .pipe(gulp.dest(packageDistribution)); + .pipe(gulp.dest(releaseDir)); }; /** @@ -96,13 +125,13 @@ function packageBlockly() { */ function packageBlocks() { return gulp.src('scripts/package/blocks.js') - .pipe(packageUMD('Blockly.Blocks', [{ - name: 'Blockly', + .pipe(packageUMD('BlocklyBlocks', [{ + name: 'BlocklyBlocks', amd: './blocks_compressed', cjs: './blocks_compressed', }])) .pipe(gulp.rename('blocks.js')) - .pipe(gulp.dest(packageDistribution)); + .pipe(gulp.dest(releaseDir)); }; /** @@ -119,7 +148,7 @@ function packageIndex() { cjs: './node', }])) .pipe(gulp.rename('index.js')) - .pipe(gulp.dest(packageDistribution)); + .pipe(gulp.dest(releaseDir)); }; /** @@ -151,7 +180,7 @@ function packageBrowser() { cjs: './javascript', }])) .pipe(gulp.rename('browser.js')) - .pipe(gulp.dest(packageDistribution)); + .pipe(gulp.dest(releaseDir)); }; /** @@ -170,7 +199,7 @@ function packageCore() { cjs: './blockly', }])) .pipe(gulp.rename('core-browser.js')) - .pipe(gulp.dest(packageDistribution)); + .pipe(gulp.dest(releaseDir)); }; /** @@ -209,7 +238,7 @@ function packageNode() { cjs: './dart', }])) .pipe(gulp.rename('node.js')) - .pipe(gulp.dest(packageDistribution)); + .pipe(gulp.dest(releaseDir)); }; /** @@ -228,7 +257,7 @@ function packageNodeCore() { cjs: './blockly', }])) .pipe(gulp.rename('core.js')) - .pipe(gulp.dest(packageDistribution)); + .pipe(gulp.dest(releaseDir)); }; /** @@ -249,7 +278,7 @@ function packageGenerator(file, rename, namespace) { cjs: `./${file}`, }])) .pipe(gulp.rename(rename)) - .pipe(gulp.dest(packageDistribution)); + .pipe(gulp.dest(releaseDir)); }; /** @@ -293,12 +322,12 @@ function packagePHP() { }; /** - * This task wraps each of the msg/js/* files into a UMD module. + * This task wraps each of the ${BUILD_DIR}/msg/js/* files into a UMD module. * @example import * as En from 'blockly/msg/en'; */ function packageLocales() { // Remove references to goog.provide and goog.require. - return gulp.src('msg/js/*.js') + return gulp.src(`${BUILD_DIR}/msg/js/*.js`) .pipe(gulp.replace(/goog\.[^\n]+/g, '')) .pipe(gulp.insert.prepend(` var Blockly = {};Blockly.Msg={};`)) @@ -307,7 +336,7 @@ function packageLocales() { amd: '../core', cjs: '../core', }])) - .pipe(gulp.dest(`${packageDistribution}/msg`)); + .pipe(gulp.dest(`${releaseDir}/msg`)); }; /** @@ -318,84 +347,109 @@ function packageLocales() { */ function packageUMDBundle() { var srcs = [ - 'blockly_compressed.js', - 'msg/js/en.js', - 'blocks_compressed.js', - 'javascript_compressed.js' + `${BUILD_DIR}/blockly_compressed.js`, + `${BUILD_DIR}/msg/js/en.js`, + `${BUILD_DIR}/blocks_compressed.js`, + `${BUILD_DIR}/javascript_compressed.js`, ]; return gulp.src(srcs) - .pipe(gulp.concat('blockly.min.js')) - .pipe(gulp.dest(`${packageDistribution}`)) + .pipe(gulp.concat('blockly.min.js')) + .pipe(gulp.dest(`${releaseDir}`)); }; /** - * This task copies all the media/* files into the distribution directory. + * This task copies all the media/* files into the release directory. */ function packageMedia() { - return gulp.src('./media/*') - .pipe(gulp.dest(`${packageDistribution}/media`)); + return gulp.src('media/*') + .pipe(gulp.dest(`${releaseDir}/media`)); }; /** - * This task copies the package.json file into the distribution directory. + * This task copies the package.json file into the release directory. */ function packageJSON(cb) { const packageJson = getPackageJson(); const json = Object.assign({}, packageJson); delete json['scripts']; - if (!fs.existsSync(packageDistribution)) { - fs.mkdirSync(packageDistribution); + if (!fs.existsSync(releaseDir)) { + fs.mkdirSync(releaseDir, {recursive: true}); } - fs.writeFileSync(`${packageDistribution}/package.json`, + fs.writeFileSync(`${releaseDir}/package.json`, JSON.stringify(json, null, 2)); cb(); }; /** - * This task copies the scripts/package/README.md file into the distribution directory. - * This file is what developers will see at https://www.npmjs.com/package/blockly. + * This task copies the scripts/package/README.md file into the + * release directory. This file is what developers will see at + * https://www.npmjs.com/package/blockly . */ function packageReadme() { - return gulp.src('./scripts/package/README.md') - .pipe(gulp.dest(`${packageDistribution}`)); + return gulp.src('scripts/package/README.md') + .pipe(gulp.dest(releaseDir)); }; /** - * This task copies the typings/blockly.d.ts TypeScript definition file into the - * distribution directory. - * The bundled declaration file is referenced in package.json in the types property. + * This task copies the typings/blockly.d.ts TypeScript definition + * file into the release directory. The bundled declaration file is + * referenced in package.json in the types property. + * As of Q4 2021 this simply copies the existing ts definition files, since + * generation through typescript-closure-tools does not work with goog.module. + * TODO(5621): Regenerate definition files and copy them into the release dir as + * needed. */ function packageDTS() { - return gulp.src(['./typings/*.d.ts', './typings/msg/*.d.ts'], {base: './typings'}) - .pipe(gulp.dest(`${packageDistribution}`)); + const handwrittenSrcs = [ + 'typings/*.d.ts', + 'typings/msg/msg.d.ts', + ]; + return gulp.src(handwrittenSrcs, {base: 'typings'}) + .pipe(gulp.dest(releaseDir)); }; /** - * This task prepares the NPM distribution files under the /dist directory. + * This task cleans the release directory (by deleting it). + */ +function cleanReleaseDir(done) { + // Sanity check. + if (releaseDir === '.' || releaseDir === '/') { + throw new Error(`Refusing to rm -rf ${releaseDir}`); + } + rimraf(releaseDir, done); +} + +/** + * This task prepares the files to be included in the NPM by copying + * them into the release directory. */ -const package = gulp.parallel( - packageIndex, - packageSources, - packageCompressed, - packageBrowser, - packageNode, - packageCore, - packageNodeCore, - packageBlockly, - packageBlocks, - packageJavascript, - packagePython, - packageLua, - packageDart, - packagePHP, - packageLocales, - packageMedia, - packageUMDBundle, - packageJSON, - packageReadme, - packageDTS -); +const package = gulp.series( + checkBuildDir, + cleanReleaseDir, + gulp.parallel( + packageIndex, + packageSources, + packageCompressed, + packageBrowser, + packageNode, + packageCore, + packageNodeCore, + packageBlockly, + packageBlocks, + packageJavascript, + packagePython, + packageLua, + packageDart, + packagePHP, + packageLocales, + packageMedia, + packageUMDBundle, + packageJSON, + packageReadme, + packageDTS) + ); module.exports = { + cleanReleaseDir: cleanReleaseDir, package: package, }; diff --git a/scripts/gulpfiles/release_tasks.js b/scripts/gulpfiles/release_tasks.js index 5e14fc08c7c..d44928ca34d 100644 --- a/scripts/gulpfiles/release_tasks.js +++ b/scripts/gulpfiles/release_tasks.js @@ -12,14 +12,13 @@ var execSync = require('child_process').execSync; var fs = require('fs'); var gulp = require('gulp'); var readlineSync = require('readline-sync'); -var typings = require('./typings'); var buildTasks = require('./build_tasks'); var gitTasks = require('./git_tasks'); var packageTasks = require('./package_tasks'); -var { getPackageJson } = require('./helper_tasks'); +var {getPackageJson} = require('./helper_tasks'); +var {RELEASE_DIR} = require('./config'); -const RELEASE_DIR = 'dist'; // Gets the current major version. function getMajorVersion() { @@ -80,20 +79,25 @@ function checkBranch(done) { } -// Sanity check that the dist folder exists, and that certain files are in the dist folder. -function checkDist(done) { - const sanityFiles = ['blockly_compressed.js', 'blocks_compressed.js', 'core', 'blocks', 'generators']; - // Check that dist exists. +// Sanity check that the RELASE_DIR directory exists, and that certain +// files are in it. +function checkReleaseDir(done) { + const sanityFiles = ['blockly_compressed.js', 'blocks_compressed.js', + 'core', 'blocks', 'generators']; + // Check that directory exists. if (fs.existsSync(RELEASE_DIR)) { - // Sanity check that certain files exist in dist. + // Sanity check that certain files exist in RELASE_DIR. sanityFiles.forEach((fileName) => { if (!fs.existsSync(`${RELEASE_DIR}/${fileName}`)) { - done(new Error(`Your dist folder does not contain:${fileName}`)); + done(new Error( + `Your ${RELEASE_DIR} directory does not contain ${fileName}`)); + return; } }); done(); } else { - done(new Error('The dist directory does not exist. Is packageTasks.package being run?')); + done(new Error(`The ${RELEASE_DIR} directory does not exist. ` + + 'Has packageTasks.package been run?')); } } @@ -144,36 +148,48 @@ function updateBetaVersion(done) { done(); } +// Build Blockly and prepare to check in the resulting built files. +const rebuildAll = gulp.series( + buildTasks.cleanBuildDir, + buildTasks.build, + buildTasks.checkinBuilt, + // TODO(5621): Re-enable once typings generation is fixed. + // typings.typings, + // typings.msgTypings, + // typings.checkinTypings, + ); + // Package and publish to npm. const publish = gulp.series( + rebuildAll, packageTasks.package, checkBranch, - checkDist, + checkReleaseDir, loginAndPublish ); // Publish a beta version of Blockly. const publishBeta = gulp.series( updateBetaVersion, - buildTasks.build, + rebuildAll, packageTasks.package, checkBranch, - checkDist, + checkReleaseDir, loginAndPublishBeta ); -// Switch to a new branch, update the version number, and build Blockly. -const recompile = gulp.series( +// Switch to a new branch, update the version number, build Blockly +// and check in the resulting built files. +const recompileDevelop = gulp.series( gitTasks.syncDevelop(), gitTasks.createRebuildBranch, updateVersionPrompt, - buildTasks.build, - typings.typings, + rebuildAll, gitTasks.pushRebuildBranch ); module.exports = { - recompile: recompile, + recompile: recompileDevelop, publishBeta: publishBeta, publish: publish } diff --git a/scripts/gulpfiles/typings.js b/scripts/gulpfiles/typings.js deleted file mode 100644 index 67b94f59c52..00000000000 --- a/scripts/gulpfiles/typings.js +++ /dev/null @@ -1,121 +0,0 @@ -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @fileoverview Gulp script to generate the Typescript definition file (d.ts) - * for Blockly. - */ - -var gulp = require('gulp'); -gulp.concat = require('gulp-concat'); - -var path = require('path'); -var fs = require('fs'); -var rimraf = require('rimraf'); -var execSync = require('child_process').execSync; - -/** - * Recursively generates a list of file paths with the specified extension - * contained within the specified basePath. - * @param {string} basePath The base path to use. - * @param {string} filter The extension name to filter for. - * @param {Array} excludePaths The paths to exclude from search. - * @return {Array} The generated file paths. - */ -function getFilePath(basePath, filter, excludePaths) { - const files = []; - const dirContents = fs.readdirSync(basePath); - dirContents.forEach((fn) => { - const filePath = path.join(basePath, fn); - const excluded = - !excludePaths.every((exPath) => !filePath.startsWith(exPath)); - if (excluded) { - return; - } - const stat = fs.lstatSync(filePath); - if (stat.isDirectory()) { - files.push(...getFilePath(filePath, filter, excludePaths)); - } else if (filePath.endsWith(filter)) { - files.push(filePath); - } - }); - return files; -} - -// Generates the TypeScript definition file (d.ts) for Blockly. -// As well as generating the typings of each of the files under core/ and msg/, -// the script also pulls in a number of part files from typings/parts. -// This includes the header (incl License), additional useful interfaces -// including Blockly Options and Google Closure typings. -function typings() { - const tmpDir = './typings/tmp'; - - // Clean directory if exists. - if (fs.existsSync(tmpDir)) { - rimraf.sync(tmpDir); - } - fs.mkdirSync(tmpDir); - - const excludePaths = [ - "core/renderers/geras", - "core/renderers/minimalist", - "core/renderers/thrasos", - "core/renderers/zelos", - ]; - const blocklySrcs = [ - 'core', - 'msg' - ] - // Find all files that will be included in the typings file. - let files = []; - blocklySrcs.forEach((basePath) => { - files.push(...getFilePath(basePath, '.js', excludePaths)); - }); - - // Generate typings file for each file. - files.forEach((file) => { - const typescriptFileName = `${path.join(tmpDir, file)}.d.ts`; - if (file.indexOf('core/msg.js') > -1) { - return; - } - const cmd = `node ./node_modules/typescript-closure-tools/definition-generator/src/main.js ${file} ${typescriptFileName}`; - console.log(`Generating typings for ${file}`); - execSync(cmd, { stdio: 'inherit' }); - }); - - const srcs = [ - 'typings/templates/blockly-header.template', - 'typings/templates/blockly-interfaces.template', - `${tmpDir}/core/**/*`, - `${tmpDir}/msg/**` - ]; - return gulp.src(srcs) - .pipe(gulp.concat('blockly.d.ts')) - .pipe(gulp.dest('typings')) - .on('end', function () { - // Clean up tmp directory. - if (fs.existsSync(tmpDir)) { - rimraf.sync(tmpDir); - } - }); -}; - -// Generates the TypeScript definition files (d.ts) for Blockly locales. -function msgTypings(cb) { - const template = fs.readFileSync(path.join('typings/templates/msg.template'), 'utf-8'); - const msgFiles = fs.readdirSync(path.join('msg', 'json')); - msgFiles.forEach(msg => { - const localeName = msg.substring(0, msg.indexOf('.json')); - const msgTypings = template.slice().replace(/<%= locale %>/gi, localeName); - fs.writeFileSync(path.join('typings', 'msg', localeName + '.d.ts'), msgTypings, 'utf-8'); - }) - cb(); -} - -module.exports = { - typings: typings, - msgTypings: msgTypings -}; diff --git a/scripts/i18n/common.py b/scripts/i18n/common.py index 7e568a5f6a7..0ac733dcade 100644 --- a/scripts/i18n/common.py +++ b/scripts/i18n/common.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # Code shared by translation conversion scripts. # diff --git a/scripts/i18n/create_messages.py b/scripts/i18n/create_messages.py index dba3b35fff1..70ce61de3e3 100755 --- a/scripts/i18n/create_messages.py +++ b/scripts/i18n/create_messages.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # Generate .js files defining Blockly core and language messages. # @@ -155,7 +155,7 @@ def main(): outfile.write(constants_text) if not args.quiet: - print('Created {0}.'.format(outname)) + print('Created {0}'.format(outname)) if __name__ == '__main__': diff --git a/scripts/i18n/dedup_json.py b/scripts/i18n/dedup_json.py index 1329e47580c..ccdddf54ab0 100755 --- a/scripts/i18n/dedup_json.py +++ b/scripts/i18n/dedup_json.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # Consolidates duplicate key-value pairs in a JSON file. # If the same key is used with different values, no warning is given, diff --git a/scripts/i18n/js_to_json.py b/scripts/i18n/js_to_json.py index cd3d4dc2b88..c073163de2d 100755 --- a/scripts/i18n/js_to_json.py +++ b/scripts/i18n/js_to_json.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # Gives the translation status of the specified apps and languages. # @@ -35,8 +35,6 @@ "Blockly.SOME_KEY", "Here is a description of the following message.", Commas would of course be omitted for the final entry of each value. - -@author Ellen Spertus (ellen.spertus@gmail.com) """ import argparse diff --git a/scripts/i18n/tests.py b/scripts/i18n/tests.py index 2fd3bc76a84..684d731058e 100644 --- a/scripts/i18n/tests.py +++ b/scripts/i18n/tests.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/python3 # -*- coding: utf-8 -*- # Tests of i18n scripts. diff --git a/scripts/migration/renamings.js b/scripts/migration/renamings.js new file mode 100644 index 00000000000..688e8ba4a05 --- /dev/null +++ b/scripts/migration/renamings.js @@ -0,0 +1,1016 @@ +/** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @fileoverview Collected information about modules and module + * exports that have been renamed between versions. + * + * For now this is a node module, not a goog.module. + */ +'use strict'; + +/** + * Map from Blockly core version number to table of renamings made + * *since* that version was released (since we don't know for sure + * what the version number of the release that will incorporate those + * renamings will be yet). + * @type {Object} + */ +const renamings = { + // Example entry: + '0.0.0': { + // These renaming were made after version 0.0.0 was published. + // Each entry is keyed by the original module name. + 'module.name.Old': { + // If the module has been renamed, its new name will be listed. + module: 'module.name.new', + + // Modules which had a default export that has been turned into + // a named export the new name will be given. + export: 'defaultName', + + // For backward-compatibility reasons, we may choose to continue + // to reexport default exports in the same place in the Blockly + // tree that they were previously (or nearby). If so, the full + // path to the former default export will be listed. This path + // is only relevant to code that accesses the export via the + // namespace tree rather than by a named import. + // + // The given example implies that: + // module.name.Old === + // goog.require('module.name.new').defaultExport + // which may not be what what one expects but is very handy. + path: 'module.name.Old', + // If path is not given explicitly then it can be assumed to be + // `${module}.${export}`. + + // Modules which already had named exports can instead list + // information about exports which have been renamed or moved in + // the exports subsection, which is a map from old export name + // to object with info about the new export name. + exports: { + oldExportName: { + // No need to quote this. + + // If the export has been moved to a different module, that + // module is listed. + module: 'module.name.other', + + // If the export has been given a new name, that is listed. + export: 'newExportName', + + // As with default exports, if a named export has been + // reexported on the namespace tree somewhere other than at + // `${module}.${export}` then that can specified explicitly. + path: 'module.name.Old.oldExportName', + // This given example implies that code that previously + // accessed this export via module.name.Old.oldExportName + // can continue to do so; only code using named requires + // needs to update from + // goog.require('module.name.Old').oldExportName + // to + // goog.require('module.name.new').newExportName + }, + // More individual exports in this module can be listed here. + }, + }, + // More modules with renamings can be listed here. + }, + + '4.20201217.0': { + 'Blockly': { + exports: { + // bind/unbind events functions. See PR #4642 + EventData: {module: 'Blockly.eventHandling', export: 'Data'}, + bindEvent_: {module: 'Blockly.browserEvents', export: 'bind'}, + unbindEvent_: {module: 'Blockly.browserEvents', export: 'unbind'}, + bindEventWithChecks_: { + module: 'Blockly.browserEvents', + export: 'conditionalBind', + }, + }, + }, + }, + '6.20210701.0': { + 'Blockly': { + exports: { + // Align. + ALIGN_LEFT: { + module: 'Blockly.Input', + export: 'Align.LEFT', + path: 'Blockly.ALIGN_LEFT', + }, + ALIGN_CENTRE: { + module: 'Blockly.Input', + export: 'Align.CENTRE', + path: 'Blockly.ALIGN_CENTRE', + }, + ALIGN_RIGHT: { + module: 'Blockly.Input', + export: 'Align.RIGHT', + path: 'Blockly.ALIGN_RIGHT', + }, + // Clipboard. See PR #5237. + clipboardXml_: {module: 'Blockly.clipboard', export: 'xml'}, + clipboardSource_: {module: 'Blockly.clipboard', export: 'source'}, + clipboardTypeCounts_: { + module: 'Blockly.clipboard', + export: 'typeCounts', + }, + copy: {module: 'Blockly.clipboard'}, + paste: {module: 'Blockly.clipboard'}, + duplicate: {module: 'Blockly.clipboard'}, + + // mainWorkspace. See PR #5244. + mainWorkspace: { + module: 'Blockly.common', + get: 'getMainWorkspace', + set: 'setMainWorkspace', + }, + getMainWorkspace: {module: 'Blockly.common'}, + + // parentContainer, draggingConnections. See PR #5262. + parentContainer: { + module: 'Blockly.common', + get: 'getParentContainer', + set: 'setParentContainer', + }, + setParentContainer: {module: 'Blockly.common'}, + draggingConnections: {module: 'Blockly.common'}, + // Dialogs. See PR #5457. + alert: { + module: 'Blockly.dialog', + export: 'alert', + set: 'setAlert', + }, + confirm: { + module: 'Blockly.dialog', + export: 'confirm', + set: 'setConfirm', + }, + prompt: { + module: 'Blockly.dialog', + export: 'prompt', + set: 'setPrompt', + }, + // hueToHex. See PR #5462. + hueToHex: {module: 'Blockly.utils.colour'}, + // Blockly.hideChaff() became + // Blockly.common.getMainWorkspace().hideChaff(). See PR #5460. + + // selected. See PR #5489. + selected: { + module: 'Blockly.common', + get: 'getSelected', + set: 'setSelected', + }, + }, + }, + 'Blockly.Blocks': { + module: 'Blockly.blocks', + export: 'Blocks', // Previous default export now named. + path: 'Blockly.Blocks', // But still on tree with original name. + }, + 'Blockly.ContextMenu': { + exports: { + currentBlock: {get: 'getCurrentBlock', set: 'setCurrentBlock'}, + }, + }, + 'Blockly.Events': { + exports: { + recordUndo: {get: 'getRecordUndo', set: 'setRecordUndo'}, + }, + }, + 'Blockly.Tooltip': { + exports: { + DIV: {get: 'getDiv', set: 'setDiv'}, + visible: {get: 'isVisible'}, + }, + }, + 'Blockly.WidgetDiv': { + exports: { + DIV: {get: 'getDiv'}, + }, + }, + 'Blockly.connectionTypes': { + module: 'Blockly.ConnectionType', + export: 'ConnectionType', // Previous default export now named. + path: 'Blockly.ConnectionType', // Type reexported directly. + }, + 'Blockly.utils': { + exports: { + genUid: {module: 'Blockly.utils.idGenerator'}, + getScrollDelta: {module: 'Blockly.utils.browserEvents'}, + isTargetInput: {module: 'Blockly.utils.browserEvents'}, + isRightButton: {module: 'Blockly.utils.browserEvents'}, + mouseToSvg: {module: 'Blockly.utils.browserEvents'}, + }, + }, + 'Blockly.utils.global': { + export: 'globalThis', // Previous default export now named. + path: 'Blockly.utils.global', // But still exported under original name. + }, + 'Blockly.utils.IdGenerator': { + module: 'Blockly.utils.idGenerator', + }, + 'Blockly.utils.xml': { + exports: { + // document was a function before, too - not a static property + // or get accessor. + document: {export: 'getDocument'}, + }, + }, + }, + '7.20211209.0-beta.0': { + 'Blockly.Blocks.colour': {module: 'Blockly.blocks.colour'}, + // Blockly.Blocks.lists not previously provided. + 'Blockly.Blocks.logic': {module: 'Blockly.blocks.logic'}, + 'Blockly.Blocks.loops': {module: 'Blockly.blocks.loops'}, + 'Blockly.Blocks.math': {module: 'Blockly.blocks.math'}, + 'Blockly.Blocks.procedures': {module: 'Blockly.blocks.procedures'}, + 'Blockly.Blocks.texts': {module: 'Blockly.blocks.texts'}, + 'Blockly.Blocks.variables': {module: 'Blockly.blocks.variables'}, + // Blockly.Blocks.variablesDynamic not previously provided. + 'Blockly.utils': { + exports: { + screenToWsCoordinates: {module: 'Blockly.utils.svgMath'}, + getDocumentScroll: {module: 'Blockly.utils.svgMath'}, + getViewportBBox: {module: 'Blockly.utils.svgMath'}, + is3dSupported: {module: 'Blockly.utils.svgMath'}, + getRelativeXY: {module: 'Blockly.utils.svgMath'}, + getInjectionDivXY_: + {module: 'Blockly.utils.svgMath', export: 'getInjectionDivXY'}, + parseBlockColour: {module: 'Blockly.utils.parsing'}, + checkMessageReferences: {module: 'Blockly.utils.parsing'}, + replaceMessageReferences: {module: 'Blockly.utils.parsing'}, + tokenizeInterpolation: {module: 'Blockly.utils.parsing'}, + arrayRemove: {module: 'Blockly.utils.array', export: 'removeElem'}, + getBlockTypeCounts: + {module: 'Blockly.common', export: 'getBlockTypeCounts'}, + runAfterPageLoad: + {module: 'Blockly.Extensions', export: 'runAfterPageLoad'}, + }, + }, + 'Blockly.Events.Abstract': { + export: 'Abstract', + path: 'Blockly.Events.Abstract', + }, + 'Blockly.Events.BlockBase': { + export: 'BlockBase', + path: 'Blockly.Events.BlockBase', + }, + 'Blockly.Events.BlockChange': { + export: 'BlockChange', + path: 'Blockly.Events.BlockChange', + }, + 'Blockly.Events.BlockCreate': { + export: 'BlockCreate', + path: 'Blockly.Events.BlockCreate', + }, + 'Blockly.Events.BlockDelete': { + export: 'BlockDelete', + path: 'Blockly.Events.BlockDelete', + }, + 'Blockly.Events.BlockDrag': { + export: 'BlockDrag', + path: 'Blockly.Events.BlockDrag', + }, + 'Blockly.Events.BlockMove': { + export: 'BlockMove', + path: 'Blockly.Events.BlockMove', + }, + 'Blockly.Events.BubbleOpen': { + export: 'BubbleOpen', + path: 'Blockly.Events.BubbleOpen', + }, + 'Blockly.Events.Click': { + export: 'Click', + path: 'Blockly.Events.Click', + }, + 'Blockly.Events.CommentBase': { + export: 'CommentBase', + path: 'Blockly.Events.CommentBase', + }, + 'Blockly.Events.CommentChange': { + export: 'CommentChange', + path: 'Blockly.Events.CommentChange', + }, + 'Blockly.Events.CommentCreate': { + export: 'CommentCreate', + path: 'Blockly.Events.CommentCreate', + }, + 'Blockly.Events.CommentDelete': { + export: 'CommentDelete', + path: 'Blockly.Events.CommentDelete', + }, + 'Blockly.Events.CommentMove': { + export: 'CommentMove', + path: 'Blockly.Events.CommentMove', + }, + 'Blockly.Events.MarkerMove': { + export: 'MarkerMove', + path: 'Blockly.Events.MarkerMove', + }, + 'Blockly.Events.Selected': { + export: 'Selected', + path: 'Blockly.Events.Selected', + }, + 'Blockly.Events.ThemeChange': { + export: 'ThemeChange', + path: 'Blockly.Events.ThemeChange', + }, + 'Blockly.Events.ToolboxItemSelect': { + export: 'ToolboxItemSelect', + path: 'Blockly.Events.ToolboxItemSelect', + }, + 'Blockly.Events.TrashcanOpen': { + export: 'TrashcanOpen', + path: 'Blockly.Events.TrashcanOpen', + }, + 'Blockly.Events.Ui': { + export: 'Ui', + path: 'Blockly.Events.Ui', + }, + 'Blockly.Events.UiBase': { + export: 'UiBase', + path: 'Blockly.Events.UiBase', + }, + 'Blockly.Events.VarBase': { + export: 'VarBase', + path: 'Blockly.Events.VarBase', + }, + 'Blockly.Events.VarCreate': { + export: 'VarCreate', + path: 'Blockly.Events.VarCreate', + }, + 'Blockly.Events.VarDelete': { + export: 'VarDelete', + path: 'Blockly.Events.VarDelete', + }, + 'Blockly.Events.VarRename': { + export: 'VarRename', + path: 'Blockly.Events.VarRename', + }, + 'Blockly.Events.ViewportChange': { + export: 'ViewportChange', + path: 'Blockly.Events.ViewportChange', + }, + 'Blockly.Events.FinishedLoading': { + export: 'FinishedLoading', + path: 'Blockly.Events.FinishedLoading', + }, + 'Blockly.IASTNodeLocation': { + export: 'IASTNodeLocation', + path: 'Blockly.IASTNodeLocation', + }, + 'Blockly.IASTNodeLocationSvg': { + export: 'IASTNodeLocationSvg', + path: 'Blockly.IASTNodeLocationSvg', + }, + 'Blockly.IASTNodeLocationWithBlock': { + export: 'IASTNodeLocationWithBlock', + path: 'Blockly.IASTNodeLocationWithBlock', + }, + 'Blockly.IAutoHideable': { + export: 'IAutoHideable', + path: 'Blockly.IAutoHideable', + }, + 'Blockly.IBlockDragger': { + export: 'IBlockDragger', + path: 'Blockly.IBlockDragger', + }, + 'Blockly.IBoundedElement': { + export: 'IBoundedElement', + path: 'Blockly.IBoundedElement', + }, + 'Blockly.IBubble': { + export: 'IBubble', + path: 'Blockly.IBubble', + }, + 'Blockly.ICollapsibleToolboxItem': { + export: 'ICollapsibleToolboxItem', + path: 'Blockly.ICollapsibleToolboxItem', + }, + 'Blockly.IComponent': { + export: 'IComponent', + path: 'Blockly.IComponent', + }, + 'Blockly.IConnectionChecker': { + export: 'IConnectionChecker', + path: 'Blockly.IConnectionChecker', + }, + 'Blockly.IContextMenu': { + export: 'IContextMenu', + path: 'Blockly.IContextMenu', + }, + 'Blockly.ICopyable': { + export: 'ICopyable', + path: 'Blockly.ICopyable', + }, + 'Blockly.IDeletable': { + export: 'IDeletable', + path: 'Blockly.IDeletable', + }, + 'Blockly.IDeleteArea': { + export: 'IDeleteArea', + path: 'Blockly.IDeleteArea', + }, + 'Blockly.IDragTarget': { + export: 'IDragTarget', + path: 'Blockly.IDragTarget', + }, + 'Blockly.IDraggable': { + export: 'IDraggable', + path: 'Blockly.IDraggable', + }, + 'Blockly.IFlyout': { + export: 'IFlyout', + path: 'Blockly.IFlyout', + }, + 'Blockly.IKeyboardAccessible': { + export: 'IKeyboardAccessible', + path: 'Blockly.IKeyboardAccessible', + }, + 'Blockly.IMetricsManager': { + export: 'IMetricsManager', + path: 'Blockly.IMetricsManager', + }, + 'Blockly.IMovable': { + export: 'IMovable', + path: 'Blockly.IMovable', + }, + 'Blockly.IPositionable': { + export: 'IPositionable', + path: 'Blockly.IPositionable', + }, + 'Blockly.IRegistrable': { + export: 'IRegistrable', + path: 'Blockly.IRegistrable', + }, + 'Blockly.IRegistrableField': { + export: 'IRegistrableField', + path: 'Blockly.IRegistrableField', + }, + 'Blockly.ISelectable': { + export: 'ISelectable', + path: 'Blockly.ISelectable', + }, + 'Blockly.ISelectableToolboxItem': { + export: 'ISelectableToolboxItem', + path: 'Blockly.ISelectableToolboxItem', + }, + 'Blockly.IStyleable': { + export: 'IStyleable', + path: 'Blockly.IStyleable', + }, + 'Blockly.IToolbox': { + export: 'IToolbox', + path: 'Blockly.IToolbox', + }, + 'Blockly.IToolboxItem': { + export: 'IToolboxItem', + path: 'Blockly.IToolboxItem', + }, + 'Blockly.blockRendering.ConstantProvider': { + export: 'ConstantProvider', + path: 'Blockly.blockRendering.ConstantProvider', + }, + 'Blockly.blockRendering.Debug': { + export: 'Debug', + path: 'Blockly.blockRendering.Debug', + }, + 'Blockly.blockRendering.Drawer': { + export: 'Drawer', + path: 'Blockly.blockRendering.Drawer', + }, + 'Blockly.blockRendering.IPathObject': { + export: 'IPathObject', + path: 'Blockly.blockRendering.IPathObject', + }, + 'Blockly.blockRendering.RenderInfo': { + export: 'RenderInfo', + path: 'Blockly.blockRendering.RenderInfo', + }, + 'Blockly.blockRendering.MarkerSvg': { + export: 'MarkerSvg', + path: 'Blockly.blockRendering.MarkerSvg', + }, + 'Blockly.blockRendering.PathObject': { + export: 'PathObject', + path: 'Blockly.blockRendering.PathObject', + }, + 'Blockly.blockRendering.Renderer': { + export: 'Renderer', + path: 'Blockly.blockRendering.Renderer', + }, + 'Blockly.geras.InlineInput': { + export: 'InlineInput', + path: 'Blockly.geras.InlineInput', + }, + 'Blockly.geras.StatementInput': { + export: 'StatementInput', + path: 'Blockly.geras.StatementInput', + }, + 'Blockly.geras.ConstantProvider': { + export: 'ConstantProvider', + path: 'Blockly.geras.ConstantProvider', + }, + 'Blockly.geras.Drawer': { + export: 'Drawer', + path: 'Blockly.geras.Drawer', + }, + 'Blockly.geras.HighlightConstantProvider': { + export: 'HighlightConstantProvider', + path: 'Blockly.geras.HighlightConstantProvider', + }, + 'Blockly.geras.Highlighter': { + export: 'Highlighter', + path: 'Blockly.geras.Highlighter', + }, + 'Blockly.geras.RenderInfo': { + export: 'RenderInfo', + path: 'Blockly.geras.RenderInfo', + }, + 'Blockly.geras.PathObject': { + export: 'PathObject', + path: 'Blockly.geras.PathObject', + }, + 'Blockly.geras.Renderer': { + export: 'Renderer', + path: 'Blockly.geras.Renderer', + }, + 'Blockly.blockRendering.Measurable': { + export: 'Measurable', + path: 'Blockly.blockRendering.Measurable', + }, + 'Blockly.blockRendering.BottomRow': { + export: 'BottomRow', + path: 'Blockly.blockRendering.BottomRow', + }, + 'Blockly.blockRendering.Connection': { + export: 'Connection', + path: 'Blockly.blockRendering.Connection', + }, + 'Blockly.blockRendering.ExternalValueInput': { + export: 'ExternalValueInput', + path: 'Blockly.blockRendering.ExternalValueInput', + }, + 'Blockly.blockRendering.Field': { + export: 'Field', + path: 'Blockly.blockRendering.Field', + }, + 'Blockly.blockRendering.Hat': { + export: 'Hat', + path: 'Blockly.blockRendering.Hat', + }, + 'Blockly.blockRendering.Icon': { + export: 'Icon', + path: 'Blockly.blockRendering.Icon', + }, + 'Blockly.blockRendering.InRowSpacer': { + export: 'InRowSpacer', + path: 'Blockly.blockRendering.InRowSpacer', + }, + 'Blockly.blockRendering.InlineInput': { + export: 'InlineInput', + path: 'Blockly.blockRendering.InlineInput', + }, + 'Blockly.blockRendering.InputConnection': { + export: 'InputConnection', + path: 'Blockly.blockRendering.InputConnection', + }, + 'Blockly.blockRendering.InputRow': { + export: 'InputRow', + path: 'Blockly.blockRendering.InputRow', + }, + 'Blockly.blockRendering.JaggedEdge': { + export: 'JaggedEdge', + path: 'Blockly.blockRendering.JaggedEdge', + }, + 'Blockly.blockRendering.NextConnection': { + export: 'NextConnection', + path: 'Blockly.blockRendering.NextConnection', + }, + 'Blockly.blockRendering.OutputConnection': { + export: 'OutputConnection', + path: 'Blockly.blockRendering.OutputConnection', + }, + 'Blockly.blockRendering.PreviousConnection': { + export: 'PreviousConnection', + path: 'Blockly.blockRendering.PreviousConnection', + }, + 'Blockly.blockRendering.RoundCorner': { + export: 'RoundCorner', + path: 'Blockly.blockRendering.RoundCorner', + }, + 'Blockly.blockRendering.Row': { + export: 'Row', + path: 'Blockly.blockRendering.Row', + }, + 'Blockly.blockRendering.SpacerRow': { + export: 'SpacerRow', + path: 'Blockly.blockRendering.SpacerRow', + }, + 'Blockly.blockRendering.SquareCorner': { + export: 'SquareCorner', + path: 'Blockly.blockRendering.SquareCorner', + }, + 'Blockly.blockRendering.StatementInput': { + export: 'StatementInput', + path: 'Blockly.blockRendering.StatementInput', + }, + 'Blockly.blockRendering.TopRow': { + export: 'TopRow', + path: 'Blockly.blockRendering.TopRow', + }, + 'Blockly.blockRendering.Types': { + export: 'Types', + path: 'Blockly.blockRendering.Types', + }, + 'Blockly.minimalist.ConstantProvider': { + export: 'ConstantProvider', + path: 'Blockly.minimalist.ConstantProvider', + }, + 'Blockly.minimalist.Drawer': { + export: 'Drawer', + path: 'Blockly.minimalist.Drawer', + }, + 'Blockly.minimalist.RenderInfo': { + export: 'RenderInfo', + path: 'Blockly.minimalist.RenderInfo', + }, + 'Blockly.minimalist.Renderer': { + export: 'Renderer', + path: 'Blockly.minimalist.Renderer', + }, + 'Blockly.thrasos.RenderInfo': { + export: 'RenderInfo', + path: 'Blockly.thrasos.RenderInfo', + }, + 'Blockly.thrasos.Renderer': { + export: 'Renderer', + path: 'Blockly.thrasos.Renderer', + }, + 'Blockly.zelos.BottomRow': { + export: 'BottomRow', + path: 'Blockly.zelos.BottomRow', + }, + 'Blockly.zelos.StatementInput': { + export: 'StatementInput', + path: 'Blockly.zelos.StatementInput', + }, + 'Blockly.zelos.RightConnectionShape': { + export: 'RightConnectionShape', + path: 'Blockly.zelos.RightConnectionShape', + }, + 'Blockly.zelos.TopRow': { + export: 'TopRow', + path: 'Blockly.zelos.TopRow', + }, + 'Blockly.zelos.ConstantProvider': { + export: 'ConstantProvider', + path: 'Blockly.zelos.ConstantProvider', + }, + 'Blockly.zelos.Drawer': { + export: 'Drawer', + path: 'Blockly.zelos.Drawer', + }, + 'Blockly.zelos.RenderInfo': { + export: 'RenderInfo', + path: 'Blockly.zelos.RenderInfo', + }, + 'Blockly.zelos.MarkerSvg': { + export: 'MarkerSvg', + path: 'Blockly.zelos.MarkerSvg', + }, + 'Blockly.zelos.PathObject': { + export: 'PathObject', + path: 'Blockly.zelos.PathObject', + }, + 'Blockly.zelos.Renderer': { + export: 'Renderer', + path: 'Blockly.zelos.Renderer', + }, + 'Blockly.Themes.Classic': { + export: 'Classic', + path: 'Blockly.Themes.Classic', + }, + 'Blockly.Themes.Zelos': { + export: 'Zelos', + path: 'Blockly.Themes.Zelos', + }, + 'Blockly.ToolboxCategory': { + export: 'ToolboxCategory', + path: 'Blockly.ToolboxCategory', + }, + 'Blockly.CollapsibleToolboxCategory': { + export: 'CollapsibleToolboxCategory', + path: 'Blockly.CollapsibleToolboxCategory', + }, + 'Blockly.ToolboxSeparator': { + export: 'ToolboxSeparator', + path: 'Blockly.ToolboxSeparator', + }, + 'Blockly.Toolbox': { + export: 'Toolbox', + path: 'Blockly.Toolbox', + }, + 'Blockly.ToolboxItem': { + export: 'ToolboxItem', + path: 'Blockly.ToolboxItem', + }, + 'Blockly.utils.Coordinate': { + export: 'Coordinate', + path: 'Blockly.utils.Coordinate', + }, + 'Blockly.utils.KeyCodes': { + export: 'KeyCodes', + path: 'Blockly.utils.KeyCodes', + }, + 'Blockly.utils.Metrics': { + export: 'Metrics', + path: 'Blockly.utils.Metrics', + }, + 'Blockly.utils.Rect': { + export: 'Rect', + path: 'Blockly.utils.Rect', + }, + 'Blockly.utils.Size': { + export: 'Size', + path: 'Blockly.utils.Size', + }, + 'Blockly.utils.Svg': { + export: 'Svg', + path: 'Blockly.utils.Svg', + }, + 'Blockly.BlocklyOptions': { + export: 'BlocklyOptions', + path: 'Blockly.BlocklyOptions', + }, + 'Blockly.Bubble': { + export: 'Bubble', + path: 'Blockly.Bubble', + }, + 'Blockly.BubbleDragger': { + export: 'BubbleDragger', + path: 'Blockly.BubbleDragger', + }, + 'Blockly.Comment': { + export: 'Comment', + path: 'Blockly.Comment', + }, + 'Blockly.ComponentManager': { + export: 'ComponentManager', + path: 'Blockly.ComponentManager', + }, + 'Blockly.Connection': { + export: 'Connection', + path: 'Blockly.Connection', + }, + 'Blockly.ConnectionChecker': { + export: 'ConnectionChecker', + path: 'Blockly.ConnectionChecker', + }, + 'Blockly.ConnectionDB': { + export: 'ConnectionDB', + path: 'Blockly.ConnectionDB', + }, + 'Blockly.ContextMenuRegistry': { + export: 'ContextMenuRegistry', + path: 'Blockly.ContextMenuRegistry', + }, + 'Blockly.DeleteArea': { + export: 'DeleteArea', + path: 'Blockly.DeleteArea', + }, + 'Blockly.DragTarget': { + export: 'DragTarget', + path: 'Blockly.DragTarget', + }, + 'Blockly.DropDownDiv': { + export: 'DropDownDiv', + path: 'Blockly.DropDownDiv', + }, + 'Blockly.Field': { + export: 'Field', + path: 'Blockly.Field', + }, + 'Blockly.FieldAngle': { + export: 'FieldAngle', + path: 'Blockly.FieldAngle', + }, + 'Blockly.FieldCheckbox': { + export: 'FieldCheckbox', + path: 'Blockly.FieldCheckbox', + }, + 'Blockly.FieldColour': { + export: 'FieldColour', + path: 'Blockly.FieldColour', + }, + 'Blockly.FieldDropdown': { + export: 'FieldDropdown', + path: 'Blockly.FieldDropdown', + }, + 'Blockly.FieldImage': { + export: 'FieldImage', + path: 'Blockly.FieldImage', + }, + 'Blockly.FieldLabel': { + export: 'FieldLabel', + path: 'Blockly.FieldLabel', + }, + 'Blockly.FieldLabelSerializable': { + export: 'FieldLabelSerializable', + path: 'Blockly.FieldLabelSerializable', + }, + 'Blockly.FieldMultilineInput': { + export: 'FieldMultilineInput', + path: 'Blockly.FieldMultilineInput', + }, + 'Blockly.FieldNumber': { + export: 'FieldNumber', + path: 'Blockly.FieldNumber', + }, + 'Blockly.FieldTextInput': { + export: 'FieldTextInput', + path: 'Blockly.FieldTextInput', + }, + 'Blockly.FieldVariable': { + export: 'FieldVariable', + path: 'Blockly.FieldVariable', + }, + 'Blockly.Flyout': { + export: 'Flyout', + path: 'Blockly.Flyout', + }, + 'Blockly.FlyoutButton': { + export: 'FlyoutButton', + path: 'Blockly.FlyoutButton', + }, + 'Blockly.HorizontalFlyout': { + export: 'HorizontalFlyout', + path: 'Blockly.HorizontalFlyout', + }, + 'Blockly.FlyoutMetricsManager': { + export: 'FlyoutMetricsManager', + path: 'Blockly.FlyoutMetricsManager', + }, + 'Blockly.VerticalFlyout': { + export: 'VerticalFlyout', + path: 'Blockly.VerticalFlyout', + }, + 'Blockly.Generator': { + export: 'Generator', + path: 'Blockly.Generator', + }, + 'Blockly.Gesture': { + export: 'Gesture', + path: 'Blockly.Gesture', + }, + 'Blockly.Grid': { + export: 'Grid', + path: 'Blockly.Grid', + }, + 'Blockly.Icon': { + export: 'Icon', + path: 'Blockly.Icon', + }, + 'Blockly.inject': { + export: 'inject', + path: 'Blockly.inject', + }, + 'Blockly.Input': { + export: 'Input', + path: 'Blockly.Input', + }, + 'Blockly.inputTypes': { + export: 'inputTypes', + path: 'Blockly.inputTypes', + }, + 'Blockly.InsertionMarkerManager': { + export: 'InsertionMarkerManager', + path: 'Blockly.InsertionMarkerManager', + }, + 'Blockly.MarkerManager': { + export: 'MarkerManager', + path: 'Blockly.MarkerManager', + }, + 'Blockly.Menu': { + export: 'Menu', + path: 'Blockly.Menu', + }, + 'Blockly.MenuItem': { + export: 'MenuItem', + path: 'Blockly.MenuItem', + }, + 'Blockly.MetricsManager': { + export: 'MetricsManager', + path: 'Blockly.MetricsManager', + }, + 'Blockly.Msg': { + export: 'Msg', + path: 'Blockly.Msg', + }, + 'Blockly.Mutator': { + export: 'Mutator', + path: 'Blockly.Mutator', + }, + 'Blockly.Names': { + export: 'Names', + path: 'Blockly.Names', + }, + 'Blockly.Options': { + export: 'Options', + path: 'Blockly.Options', + }, + 'Blockly.RenderedConnection': { + export: 'RenderedConnection', + path: 'Blockly.RenderedConnection', + }, + 'Blockly.Scrollbar': { + export: 'Scrollbar', + path: 'Blockly.Scrollbar', + }, + 'Blockly.ScrollbarPair': { + export: 'ScrollbarPair', + path: 'Blockly.ScrollbarPair', + }, + 'Blockly.ShortcutRegistry': { + export: 'ShortcutRegistry', + path: 'Blockly.ShortcutRegistry', + }, + 'Blockly.Theme': { + export: 'Theme', + path: 'Blockly.Theme', + }, + 'Blockly.ThemeManager': { + export: 'ThemeManager', + path: 'Blockly.ThemeManager', + }, + 'Blockly.TouchGesture': { + export: 'TouchGesture', + path: 'Blockly.TouchGesture', + }, + 'Blockly.Trashcan': { + export: 'Trashcan', + path: 'Blockly.Trashcan', + }, + 'Blockly.VariableMap': { + export: 'VariableMap', + path: 'Blockly.VariableMap', + }, + 'Blockly.VariableModel': { + export: 'VariableModel', + path: 'Blockly.VariableModel', + }, + 'Blockly.Warning': { + export: 'Warning', + path: 'Blockly.Warning', + }, + 'Blockly.Workspace': { + export: 'Workspace', + path: 'Blockly.Workspace', + }, + 'Blockly.WorkspaceAudio': { + export: 'WorkspaceAudio', + path: 'Blockly.WorkspaceAudio', + }, + 'Blockly.WorkspaceComment': { + export: 'WorkspaceComment', + path: 'Blockly.WorkspaceComment', + }, + 'Blockly.WorkspaceCommentSvg': { + export: 'WorkspaceCommentSvg', + path: 'Blockly.WorkspaceCommentSvg', + }, + 'Blockly.WorkspaceDragSurfaceSvg': { + export: 'WorkspaceDragSurfaceSvg', + path: 'Blockly.WorkspaceDragSurfaceSvg', + }, + 'Blockly.WorkspaceDragger': { + export: 'WorkspaceDragger', + path: 'Blockly.WorkspaceDragger', + }, + 'Blockly.WorkspaceSvg': { + export: 'WorkspaceSvg', + path: 'Blockly.WorkspaceSvg', + }, + 'Blockly.ZoomControls': { + export: 'ZoomControls', + path: 'Blockly.ZoomControls', + }, + 'Blockly': { + exports: { + svgSize: {module: 'Blockly.utils.svgMath'}, + resizeSvgContents: {module: 'Blockly.WorkspaecSvg'}, + defineBlocksWithJsonArray: {module: 'Blockly.common'}, + isNumber: {module: 'Blockly.utils.string'}, + }, + }, + }, +}; + +exports.renamings = renamings; diff --git a/scripts/package/blockly.js b/scripts/package/blockly.js index 55bc791af5f..77021b3d63c 100644 --- a/scripts/package/blockly.js +++ b/scripts/package/blockly.js @@ -5,8 +5,5 @@ */ /** - * @fileoverview Blockly module. + * @fileoverview Blockly module; just a wrapper for blockly_compressed.js. */ - -/* eslint-disable */ -'use strict'; diff --git a/scripts/package/blocks.js b/scripts/package/blocks.js index 8e0d4ab79f2..66ae806bffa 100644 --- a/scripts/package/blocks.js +++ b/scripts/package/blocks.js @@ -5,10 +5,5 @@ */ /** - * @fileoverview Blockly Blocks module. + * @fileoverview Blockly blocks module; just a wrapper for blocks_compressed.js. */ - -/* eslint-disable */ -'use strict'; - -Blockly.Blocks = {}; diff --git a/scripts/package/browser/core.js b/scripts/package/browser/core.js index 19463cd7e96..a0b0d92593a 100644 --- a/scripts/package/browser/core.js +++ b/scripts/package/browser/core.js @@ -14,7 +14,6 @@ // Add a helper method to set the Blockly locale. Blockly.setLocale = function (locale) { - Blockly.Msg = Blockly.Msg || {}; Object.keys(locale).forEach(function (k) { Blockly.Msg[k] = locale[k]; }); diff --git a/scripts/package/browser/index.js b/scripts/package/browser/index.js index 1b2f05e5131..36d0c325bf2 100644 --- a/scripts/package/browser/index.js +++ b/scripts/package/browser/index.js @@ -15,10 +15,3 @@ // Include the EN Locale by default. Blockly.setLocale(En); - -Blockly.Blocks = Blockly.Blocks || {}; -Object.keys(BlocklyBlocks).forEach(function (k) { - Blockly.Blocks[k] = BlocklyBlocks[k]; -}); - -Blockly.JavaScript = BlocklyJS; \ No newline at end of file diff --git a/scripts/package/dart.js b/scripts/package/dart.js index ea4e8338a30..d59060bded7 100644 --- a/scripts/package/dart.js +++ b/scripts/package/dart.js @@ -5,10 +5,5 @@ */ /** - * @fileoverview Dart Generator module. + * @fileoverview Dart generator module; just a wrapper for dart_compressed.js. */ - -/* eslint-disable */ -'use strict'; - -Blockly.Dart = BlocklyDart; diff --git a/scripts/package/index.js b/scripts/package/index.js index f88c16e7378..0264fd446b5 100644 --- a/scripts/package/index.js +++ b/scripts/package/index.js @@ -5,8 +5,7 @@ */ /** - * @fileoverview Blockly module. + * @fileoverview Blockly module; this is a wrapper which selects + * either browser.js or node.js, depending on which environment we + * are running in. */ - -/* eslint-disable */ -'use strict'; \ No newline at end of file diff --git a/scripts/package/javascript.js b/scripts/package/javascript.js index e0ba813f276..e4adba09fd2 100644 --- a/scripts/package/javascript.js +++ b/scripts/package/javascript.js @@ -5,10 +5,6 @@ */ /** - * @fileoverview JavaScript Generator module. + * @fileoverview JavaScript Generator module; just a wrapper for + * javascript_compressed.js. */ - -/* eslint-disable */ -'use strict'; - -Blockly.JavaScript = BlocklyJavaScript; diff --git a/scripts/package/lua.js b/scripts/package/lua.js index 4094782a199..ca37b44f995 100644 --- a/scripts/package/lua.js +++ b/scripts/package/lua.js @@ -5,10 +5,5 @@ */ /** - * @fileoverview Lua Generator module. + * @fileoverview Lua generator module; just a wrapper for lua_compressed.js. */ - -/* eslint-disable */ -'use strict'; - -Blockly.Lua = BlocklyLua; diff --git a/scripts/package/node/core.js b/scripts/package/node/core.js index 7935f0cc5e9..36a5addd21a 100644 --- a/scripts/package/node/core.js +++ b/scripts/package/node/core.js @@ -14,7 +14,6 @@ // Add a helper method to set the Blockly locale. Blockly.setLocale = function (locale) { - Blockly.Msg = Blockly.Msg || {}; Object.keys(locale).forEach(function (k) { Blockly.Msg[k] = locale[k]; }); @@ -22,12 +21,12 @@ Blockly.setLocale = function (locale) { // Override textToDomDocument and provide Node.js alternatives to DOMParser and // XMLSerializer. -if (typeof Blockly.utils.global.document !== 'object') { - Blockly.utils.global.DOMParser = require('jsdom/lib/jsdom/living').DOMParser; - Blockly.utils.global.XMLSerializer = require('jsdom/lib/jsdom/living').XMLSerializer; - var doc = Blockly.utils.xml.textToDomDocument( - ''); - Blockly.utils.xml.document = function() { - return doc; - }; +const globalThis = Blockly.utils.global; +if (typeof globalThis.document !== 'object') { + const jsdom = require('jsdom/lib/jsdom/living'); + globalThis.DOMParser = jsdom.DOMParser; + globalThis.XMLSerializer = jsdom.XMLSerializer; + const xmlDocument = Blockly.utils.xml.textToDomDocument( + ``); + Blockly.utils.xml.setDocument(xmlDocument); } diff --git a/scripts/package/node/index.js b/scripts/package/node/index.js index d50a92d60e3..33c322a98e6 100644 --- a/scripts/package/node/index.js +++ b/scripts/package/node/index.js @@ -14,18 +14,3 @@ // Include the EN Locale by default. Blockly.setLocale(En); - -Blockly.Blocks = Blockly.Blocks || {}; -Object.keys(BlocklyBlocks).forEach(function (k) { - Blockly.Blocks[k] = BlocklyBlocks[k]; -}); - -Blockly.JavaScript = BlocklyJS; - -Blockly.Python = BlocklyPython; - -Blockly.Lua = BlocklyLua; - -Blockly.PHP = BlocklyPHP; - -Blockly.Dart = BlocklyDart; \ No newline at end of file diff --git a/scripts/package/php.js b/scripts/package/php.js index 361dd97ecdb..a6ed3fa6d3b 100644 --- a/scripts/package/php.js +++ b/scripts/package/php.js @@ -5,10 +5,5 @@ */ /** - * @fileoverview PHP Generator module. + * @fileoverview PHP generator module; just a wrapper for php_compressed.js. */ - -/* eslint-disable */ -'use strict'; - -Blockly.PHP = BlocklyPHP; diff --git a/scripts/package/python.js b/scripts/package/python.js index 3634e41bfb0..0bb64c06f7d 100644 --- a/scripts/package/python.js +++ b/scripts/package/python.js @@ -5,10 +5,6 @@ */ /** - * @fileoverview Python Generator module. + * @fileoverview Python generator module; just a wrapper for + * python_compressed.js. */ - -/* eslint-disable */ -'use strict'; - -Blockly.Python = BlocklyPython; diff --git a/tests/compile/index.html b/tests/compile/index.html index 8ffadca8893..97fb520316f 100644 --- a/tests/compile/index.html +++ b/tests/compile/index.html @@ -22,9 +22,8 @@

Blockly: Advanced Compilation Test

-

To run this test manually, download - closure-compiler-vxxxxxxxx.jar, - place it in this directory, then run `npm run test:compile:advanced` from the command line.

+

To run this test manually, run `npm run test:compile:advanced` + from the command line, then open this file in your web browser.

Measure the size of main_compressed.js (295kb as of October 2017), then reload this page and see if Blockly works.

diff --git a/tests/compile/main.js b/tests/compile/main.js index ae71831e718..f7ab99e7a76 100644 --- a/tests/compile/main.js +++ b/tests/compile/main.js @@ -11,16 +11,8 @@ goog.require('Blockly'); goog.require('Blockly.geras.Renderer'); goog.require('Blockly.VerticalFlyout'); // Blocks -goog.require('Blockly.Constants.Logic'); -goog.require('Blockly.Constants.Loops'); -goog.require('Blockly.Constants.Math'); -goog.require('Blockly.Constants.TestBlocks'); -goog.require('Blockly.Constants.Text'); -goog.require('Blockly.Constants.Lists'); -goog.require('Blockly.Constants.Colour'); -goog.require('Blockly.Constants.Variables'); -goog.require('Blockly.Constants.VariablesDynamic'); -goog.require('Blockly.Blocks.procedures'); +goog.require('Blockly.blocks.all'); +goog.require('Blockly.blocks.testBlocks'); Main.init = function() { Blockly.inject('blocklyDiv', { diff --git a/tests/compile/test_blocks.js b/tests/compile/test_blocks.js index e5fface57a4..9cf7efd6895 100644 --- a/tests/compile/test_blocks.js +++ b/tests/compile/test_blocks.js @@ -9,11 +9,9 @@ */ 'use strict'; -goog.provide('Blockly.Constants.TestBlocks'); +goog.provide('Blockly.blocks.testBlocks'); goog.require('Blockly'); -goog.require('Blockly.Blocks'); - Blockly.defineBlocksWithJsonArray([ { diff --git a/tests/deps.js b/tests/deps.js new file mode 100644 index 00000000000..06a663a0b23 --- /dev/null +++ b/tests/deps.js @@ -0,0 +1,334 @@ +goog.addDependency('../../blocks/all.js', ['Blockly.blocks.all'], ['Blockly.blocks.colour', 'Blockly.blocks.lists', 'Blockly.blocks.logic', 'Blockly.blocks.loops', 'Blockly.blocks.math', 'Blockly.blocks.procedures', 'Blockly.blocks.texts', 'Blockly.blocks.variables', 'Blockly.blocks.variablesDynamic'], {'module': 'goog'}); +goog.addDependency('../../blocks/colour.js', ['Blockly.blocks.colour'], ['Blockly.FieldColour', 'Blockly.common'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../blocks/lists.js', ['Blockly.blocks.lists'], ['Blockly.ConnectionType', 'Blockly.FieldDropdown', 'Blockly.FieldDropdown', 'Blockly.Input', 'Blockly.Msg', 'Blockly.Mutator', 'Blockly.blocks', 'Blockly.common', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../blocks/logic.js', ['Blockly.blocks.logic'], ['Blockly.Events', 'Blockly.Extensions', 'Blockly.FieldDropdown', 'Blockly.FieldLabel', 'Blockly.Msg', 'Blockly.Mutator', 'Blockly.common', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../blocks/loops.js', ['Blockly.blocks.loops'], ['Blockly.ContextMenu', 'Blockly.Events', 'Blockly.Extensions', 'Blockly.FieldDropdown', 'Blockly.FieldLabel', 'Blockly.FieldNumber', 'Blockly.FieldVariable', 'Blockly.Msg', 'Blockly.Variables', 'Blockly.Warning', 'Blockly.common', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../blocks/math.js', ['Blockly.blocks.math'], ['Blockly.Extensions', 'Blockly.FieldDropdown', 'Blockly.FieldLabel', 'Blockly.FieldNumber', 'Blockly.FieldVariable', 'Blockly.common', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../blocks/procedures.js', ['Blockly.blocks.procedures'], ['Blockly.Comment', 'Blockly.ContextMenu', 'Blockly.Events', 'Blockly.FieldCheckbox', 'Blockly.FieldLabel', 'Blockly.FieldTextInput', 'Blockly.Input', 'Blockly.Msg', 'Blockly.Mutator', 'Blockly.Names', 'Blockly.Procedures', 'Blockly.Variables', 'Blockly.Warning', 'Blockly.Xml', 'Blockly.blocks', 'Blockly.internalConstants', 'Blockly.utils.xml'], {'lang': 'es9', 'module': 'goog'}); +goog.addDependency('../../blocks/text.js', ['Blockly.blocks.texts'], ['Blockly.ConnectionType', 'Blockly.Extensions', 'Blockly.FieldDropdown', 'Blockly.FieldImage', 'Blockly.FieldMultilineInput', 'Blockly.FieldTextInput', 'Blockly.FieldVariable', 'Blockly.Input', 'Blockly.Msg', 'Blockly.Mutator', 'Blockly.blocks', 'Blockly.common', 'Blockly.utils.xml'], {'lang': 'es9', 'module': 'goog'}); +goog.addDependency('../../blocks/variables.js', ['Blockly.blocks.variables'], ['Blockly.ContextMenu', 'Blockly.Extensions', 'Blockly.FieldLabel', 'Blockly.FieldVariable', 'Blockly.Msg', 'Blockly.Variables', 'Blockly.common', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../blocks/variables_dynamic.js', ['Blockly.blocks.variablesDynamic'], ['Blockly.ContextMenu', 'Blockly.Extensions', 'Blockly.FieldLabel', 'Blockly.FieldVariable', 'Blockly.Msg', 'Blockly.Variables', 'Blockly.common', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../closure/goog/base.js', [], []); +goog.addDependency('../../closure/goog/base_minimal.js', [], []); +goog.addDependency('../../core/block.js', ['Blockly.Block'], ['Blockly.ASTNode', 'Blockly.Connection', 'Blockly.ConnectionType', 'Blockly.Events.BlockChange', 'Blockly.Events.BlockCreate', 'Blockly.Events.BlockDelete', 'Blockly.Events.BlockMove', 'Blockly.Events.utils', 'Blockly.Extensions', 'Blockly.IASTNodeLocation', 'Blockly.IDeletable', 'Blockly.Input', 'Blockly.Tooltip', 'Blockly.blocks', 'Blockly.common', 'Blockly.constants', 'Blockly.fieldRegistry', 'Blockly.inputTypes', 'Blockly.utils.Coordinate', 'Blockly.utils.Size', 'Blockly.utils.array', 'Blockly.utils.idGenerator', 'Blockly.utils.object', 'Blockly.utils.parsing'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/block_animations.js', ['Blockly.blockAnimations'], ['Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/block_drag_surface.js', ['Blockly.BlockDragSurfaceSvg'], ['Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.svgMath'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/block_dragger.js', ['Blockly.BlockDragger'], ['Blockly.Events.BlockDrag', 'Blockly.Events.BlockMove', 'Blockly.Events.utils', 'Blockly.IBlockDragger', 'Blockly.InsertionMarkerManager', 'Blockly.blockAnimations', 'Blockly.bumpObjects', 'Blockly.common', 'Blockly.registry', 'Blockly.utils.Coordinate', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/block_svg.js', ['Blockly.BlockSvg'], ['Blockly.ASTNode', 'Blockly.Block', 'Blockly.ConnectionType', 'Blockly.ContextMenu', 'Blockly.ContextMenuRegistry', 'Blockly.Events.BlockMove', 'Blockly.Events.Selected', 'Blockly.Events.utils', 'Blockly.FieldLabel', 'Blockly.IASTNodeLocationSvg', 'Blockly.IBoundedElement', 'Blockly.ICopyable', 'Blockly.IDraggable', 'Blockly.MarkerManager', 'Blockly.Msg', 'Blockly.RenderedConnection', 'Blockly.TabNavigateCursor', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.blockAnimations', 'Blockly.browserEvents', 'Blockly.common', 'Blockly.constants', 'Blockly.internalConstants', 'Blockly.serialization.blocks', 'Blockly.utils.Coordinate', 'Blockly.utils.Rect', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.svgMath', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/blockly.js', ['Blockly'], ['Blockly.ASTNode', 'Blockly.BasicCursor', 'Blockly.Block', 'Blockly.BlockDragSurfaceSvg', 'Blockly.BlockDragger', 'Blockly.BlockSvg', 'Blockly.BlocklyOptions', 'Blockly.Bubble', 'Blockly.BubbleDragger', 'Blockly.CollapsibleToolboxCategory', 'Blockly.Comment', 'Blockly.ComponentManager', 'Blockly.Connection', 'Blockly.ConnectionChecker', 'Blockly.ConnectionDB', 'Blockly.ConnectionType', 'Blockly.ContextMenu', 'Blockly.ContextMenuItems', 'Blockly.ContextMenuRegistry', 'Blockly.Css', 'Blockly.Cursor', 'Blockly.DeleteArea', 'Blockly.DragTarget', 'Blockly.DropDownDiv', 'Blockly.Events', 'Blockly.Events.BlockCreate', 'Blockly.Events.FinishedLoading', 'Blockly.Events.Ui', 'Blockly.Events.UiBase', 'Blockly.Events.VarCreate', 'Blockly.Extensions', 'Blockly.Field', 'Blockly.FieldAngle', 'Blockly.FieldCheckbox', 'Blockly.FieldColour', 'Blockly.FieldDropdown', 'Blockly.FieldImage', 'Blockly.FieldLabel', 'Blockly.FieldLabelSerializable', 'Blockly.FieldMultilineInput', 'Blockly.FieldNumber', 'Blockly.FieldTextInput', 'Blockly.FieldVariable', 'Blockly.Flyout', 'Blockly.FlyoutButton', 'Blockly.FlyoutMetricsManager', 'Blockly.Generator', 'Blockly.Gesture', 'Blockly.Grid', 'Blockly.HorizontalFlyout', 'Blockly.IASTNodeLocation', 'Blockly.IASTNodeLocationSvg', 'Blockly.IASTNodeLocationWithBlock', 'Blockly.IAutoHideable', 'Blockly.IBlockDragger', 'Blockly.IBoundedElement', 'Blockly.IBubble', 'Blockly.ICollapsibleToolboxItem', 'Blockly.IComponent', 'Blockly.IConnectionChecker', 'Blockly.IContextMenu', 'Blockly.ICopyable', 'Blockly.IDeletable', 'Blockly.IDeleteArea', 'Blockly.IDragTarget', 'Blockly.IDraggable', 'Blockly.IFlyout', 'Blockly.IKeyboardAccessible', 'Blockly.IMetricsManager', 'Blockly.IMovable', 'Blockly.IPositionable', 'Blockly.IRegistrable', 'Blockly.IRegistrableField', 'Blockly.ISelectable', 'Blockly.ISelectableToolboxItem', 'Blockly.IStyleable', 'Blockly.IToolbox', 'Blockly.IToolboxItem', 'Blockly.Icon', 'Blockly.Input', 'Blockly.InsertionMarkerManager', 'Blockly.Marker', 'Blockly.MarkerManager', 'Blockly.Menu', 'Blockly.MenuItem', 'Blockly.MetricsManager', 'Blockly.Msg', 'Blockly.Mutator', 'Blockly.Names', 'Blockly.Options', 'Blockly.Procedures', 'Blockly.RenderedConnection', 'Blockly.Scrollbar', 'Blockly.ScrollbarPair', 'Blockly.ShortcutItems', 'Blockly.ShortcutRegistry', 'Blockly.TabNavigateCursor', 'Blockly.Theme', 'Blockly.ThemeManager', 'Blockly.Themes', 'Blockly.Toolbox', 'Blockly.ToolboxCategory', 'Blockly.ToolboxItem', 'Blockly.ToolboxSeparator', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.TouchGesture', 'Blockly.Trashcan', 'Blockly.VariableMap', 'Blockly.VariableModel', 'Blockly.Variables', 'Blockly.VariablesDynamic', 'Blockly.VerticalFlyout', 'Blockly.Warning', 'Blockly.WidgetDiv', 'Blockly.Workspace', 'Blockly.WorkspaceAudio', 'Blockly.WorkspaceComment', 'Blockly.WorkspaceCommentSvg', 'Blockly.WorkspaceDragSurfaceSvg', 'Blockly.WorkspaceDragger', 'Blockly.WorkspaceSvg', 'Blockly.Xml', 'Blockly.ZoomControls', 'Blockly.blockAnimations', 'Blockly.blockRendering', 'Blockly.blocks', 'Blockly.browserEvents', 'Blockly.bumpObjects', 'Blockly.clipboard', 'Blockly.common', 'Blockly.constants', 'Blockly.dialog', 'Blockly.fieldRegistry', 'Blockly.geras', 'Blockly.inject', 'Blockly.inputTypes', 'Blockly.internalConstants', 'Blockly.minimalist', 'Blockly.registry', 'Blockly.serialization.ISerializer', 'Blockly.serialization.blocks', 'Blockly.serialization.exceptions', 'Blockly.serialization.priorities', 'Blockly.serialization.registry', 'Blockly.serialization.variables', 'Blockly.serialization.workspaces', 'Blockly.thrasos', 'Blockly.uiPosition', 'Blockly.utils', 'Blockly.utils.colour', 'Blockly.utils.deprecation', 'Blockly.utils.global', 'Blockly.utils.svgMath', 'Blockly.utils.toolbox', 'Blockly.zelos'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/blockly_options.js', ['Blockly.BlocklyOptions'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/blocks.js', ['Blockly.blocks'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/browser_events.js', ['Blockly.browserEvents'], ['Blockly.Touch', 'Blockly.internalConstants', 'Blockly.utils.global', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/bubble.js', ['Blockly.Bubble'], ['Blockly.IBubble', 'Blockly.Scrollbar', 'Blockly.Touch', 'Blockly.Workspace', 'Blockly.browserEvents', 'Blockly.utils.Coordinate', 'Blockly.utils.Size', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.math', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/bubble_dragger.js', ['Blockly.BubbleDragger'], ['Blockly.Bubble', 'Blockly.ComponentManager', 'Blockly.Events.CommentMove', 'Blockly.Events.utils', 'Blockly.constants', 'Blockly.utils.Coordinate', 'Blockly.utils.svgMath'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/bump_objects.js', ['Blockly.bumpObjects'], ['Blockly.Events.utils', 'Blockly.utils.math'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/clipboard.js', ['Blockly.clipboard'], ['Blockly.Events.utils'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/comment.js', ['Blockly.Comment'], ['Blockly.Bubble', 'Blockly.Css', 'Blockly.Events.BlockChange', 'Blockly.Events.BubbleOpen', 'Blockly.Events.utils', 'Blockly.Icon', 'Blockly.Warning', 'Blockly.browserEvents', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/common.js', ['Blockly.common'], ['Blockly.blocks'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/component_manager.js', ['Blockly.ComponentManager'], ['Blockly.utils.array'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/connection.js', ['Blockly.Connection'], ['Blockly.ConnectionType', 'Blockly.Events.BlockMove', 'Blockly.Events.utils', 'Blockly.IASTNodeLocationWithBlock', 'Blockly.Xml', 'Blockly.constants', 'Blockly.serialization.blocks'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/connection_checker.js', ['Blockly.ConnectionChecker'], ['Blockly.Connection', 'Blockly.ConnectionType', 'Blockly.IConnectionChecker', 'Blockly.common', 'Blockly.internalConstants', 'Blockly.registry'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/connection_db.js', ['Blockly.ConnectionDB'], ['Blockly.ConnectionType', 'Blockly.constants'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/connection_type.js', ['Blockly.ConnectionType'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/constants.js', ['Blockly.constants'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/contextmenu.js', ['Blockly.ContextMenu'], ['Blockly.Events.BlockCreate', 'Blockly.Events.utils', 'Blockly.Menu', 'Blockly.MenuItem', 'Blockly.Msg', 'Blockly.WidgetDiv', 'Blockly.Xml', 'Blockly.browserEvents', 'Blockly.clipboard', 'Blockly.internalConstants', 'Blockly.utils.Coordinate', 'Blockly.utils.Rect', 'Blockly.utils.aria', 'Blockly.utils.deprecation', 'Blockly.utils.dom', 'Blockly.utils.svgMath', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/contextmenu_items.js', ['Blockly.ContextMenuItems'], ['Blockly.ContextMenuRegistry', 'Blockly.Events', 'Blockly.Events.utils', 'Blockly.Msg', 'Blockly.clipboard', 'Blockly.dialog', 'Blockly.inputTypes', 'Blockly.utils.idGenerator', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/contextmenu_registry.js', ['Blockly.ContextMenuRegistry'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/css.js', ['Blockly.Css'], ['Blockly.utils.deprecation'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/delete_area.js', ['Blockly.DeleteArea'], ['Blockly.BlockSvg', 'Blockly.DragTarget', 'Blockly.IDeleteArea', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/dialog.js', ['Blockly.dialog'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/drag_target.js', ['Blockly.DragTarget'], ['Blockly.IDragTarget'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/dropdowndiv.js', ['Blockly.DropDownDiv'], ['Blockly.common', 'Blockly.utils.Rect', 'Blockly.utils.dom', 'Blockly.utils.math', 'Blockly.utils.style'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events.js', ['Blockly.Events'], ['Blockly.Events.Abstract', 'Blockly.Events.BlockBase', 'Blockly.Events.BlockChange', 'Blockly.Events.BlockCreate', 'Blockly.Events.BlockDelete', 'Blockly.Events.BlockDrag', 'Blockly.Events.BlockMove', 'Blockly.Events.BubbleOpen', 'Blockly.Events.Click', 'Blockly.Events.CommentBase', 'Blockly.Events.CommentChange', 'Blockly.Events.CommentCreate', 'Blockly.Events.CommentDelete', 'Blockly.Events.CommentMove', 'Blockly.Events.FinishedLoading', 'Blockly.Events.MarkerMove', 'Blockly.Events.ModuleActivate', 'Blockly.Events.ModuleCreate', 'Blockly.Events.ModuleDelete', 'Blockly.Events.ModuleMove', 'Blockly.Events.ModuleRename', 'Blockly.Events.MoveBlockToModule', 'Blockly.Events.Selected', 'Blockly.Events.ThemeChange', 'Blockly.Events.ToolboxItemSelect', 'Blockly.Events.TrashcanOpen', 'Blockly.Events.Ui', 'Blockly.Events.UiBase', 'Blockly.Events.VarBase', 'Blockly.Events.VarCreate', 'Blockly.Events.VarDelete', 'Blockly.Events.VarRename', 'Blockly.Events.ViewportChange', 'Blockly.Events.utils', 'Blockly.utils.deprecation'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_abstract.js', ['Blockly.Events.Abstract'], ['Blockly.Events.utils'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_block_base.js', ['Blockly.Events.BlockBase'], ['Blockly.Events.Abstract', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_block_change.js', ['Blockly.Events.BlockChange'], ['Blockly.Events.BlockBase', 'Blockly.Events.utils', 'Blockly.Xml', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_block_create.js', ['Blockly.Events.BlockCreate'], ['Blockly.Events.BlockBase', 'Blockly.Events.utils', 'Blockly.Xml', 'Blockly.registry', 'Blockly.serialization.blocks', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_block_delete.js', ['Blockly.Events.BlockDelete'], ['Blockly.Events.BlockBase', 'Blockly.Events.utils', 'Blockly.Xml', 'Blockly.registry', 'Blockly.serialization.blocks', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_block_drag.js', ['Blockly.Events.BlockDrag'], ['Blockly.Events.UiBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_block_move.js', ['Blockly.Events.BlockMove'], ['Blockly.ConnectionType', 'Blockly.Events.BlockBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.Coordinate', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_bubble_open.js', ['Blockly.Events.BubbleOpen'], ['Blockly.Events.UiBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_click.js', ['Blockly.Events.Click'], ['Blockly.Events.UiBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_comment_base.js', ['Blockly.Events.CommentBase'], ['Blockly.Events.Abstract', 'Blockly.Events.utils', 'Blockly.Xml', 'Blockly.utils.object', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_comment_change.js', ['Blockly.Events.CommentChange'], ['Blockly.Events.CommentBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_comment_create.js', ['Blockly.Events.CommentCreate'], ['Blockly.Events.CommentBase', 'Blockly.Events.utils', 'Blockly.Xml', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_comment_delete.js', ['Blockly.Events.CommentDelete'], ['Blockly.Events.CommentBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_comment_move.js', ['Blockly.Events.CommentMove'], ['Blockly.Events.CommentBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.Coordinate', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_marker_move.js', ['Blockly.Events.MarkerMove'], ['Blockly.ASTNode', 'Blockly.Events.UiBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_module_activate.js', ['Blockly.Events.ModuleActivate'], ['Blockly.Events.Abstract', 'Blockly.Events.ModuleBase', 'Blockly.Events.ModuleDelete', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_module_base.js', ['Blockly.Events.ModuleBase'], ['Blockly.Events.Abstract', 'Blockly.Events.utils', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_module_create.js', ['Blockly.Events.ModuleCreate'], ['Blockly.Events.Abstract', 'Blockly.Events.ModuleBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_module_delete.js', ['Blockly.Events.ModuleDelete'], ['Blockly.Events.Abstract', 'Blockly.Events.ModuleBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_module_move.js', ['Blockly.Events.ModuleMove'], ['Blockly.Events.Abstract', 'Blockly.Events.ModuleBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_module_rename.js', ['Blockly.Events.ModuleRename'], ['Blockly.Events.Abstract', 'Blockly.Events.ModuleBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_move_block_to_module.js', ['Blockly.Events.MoveBlockToModule'], ['Blockly.Events.Abstract', 'Blockly.Events.ModuleBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_selected.js', ['Blockly.Events.Selected'], ['Blockly.Events.UiBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_theme_change.js', ['Blockly.Events.ThemeChange'], ['Blockly.Events.UiBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_toolbox_item_select.js', ['Blockly.Events.ToolboxItemSelect'], ['Blockly.Events.UiBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_trashcan_open.js', ['Blockly.Events.TrashcanOpen'], ['Blockly.Events.UiBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_ui.js', ['Blockly.Events.Ui'], ['Blockly.Events.UiBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_ui_base.js', ['Blockly.Events.UiBase'], ['Blockly.Events.Abstract', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_var_base.js', ['Blockly.Events.VarBase'], ['Blockly.Events.Abstract', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_var_create.js', ['Blockly.Events.VarCreate'], ['Blockly.Events.VarBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_var_delete.js', ['Blockly.Events.VarDelete'], ['Blockly.Events.VarBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_var_rename.js', ['Blockly.Events.VarRename'], ['Blockly.Events.VarBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/events_viewport.js', ['Blockly.Events.ViewportChange'], ['Blockly.Events.UiBase', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/utils.js', ['Blockly.Events.utils'], ['Blockly.registry', 'Blockly.utils.idGenerator'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/events/workspace_events.js', ['Blockly.Events.FinishedLoading'], ['Blockly.Events.Abstract', 'Blockly.Events.utils', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/extensions.js', ['Blockly.Extensions'], ['Blockly.utils.parsing'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/field.js', ['Blockly.Field'], ['Blockly.DropDownDiv', 'Blockly.Events.BlockChange', 'Blockly.Events.utils', 'Blockly.Gesture', 'Blockly.IASTNodeLocationSvg', 'Blockly.IASTNodeLocationWithBlock', 'Blockly.IKeyboardAccessible', 'Blockly.IRegistrable', 'Blockly.MarkerManager', 'Blockly.Tooltip', 'Blockly.WidgetDiv', 'Blockly.Xml', 'Blockly.browserEvents', 'Blockly.utils.Rect', 'Blockly.utils.Size', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.parsing', 'Blockly.utils.style', 'Blockly.utils.userAgent', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/field_angle.js', ['Blockly.FieldAngle'], ['Blockly.Css', 'Blockly.DropDownDiv', 'Blockly.FieldTextInput', 'Blockly.WidgetDiv', 'Blockly.browserEvents', 'Blockly.fieldRegistry', 'Blockly.utils.KeyCodes', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.math', 'Blockly.utils.object', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/field_checkbox.js', ['Blockly.FieldCheckbox'], ['Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.fieldRegistry', 'Blockly.utils.dom', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/field_colour.js', ['Blockly.FieldColour'], ['Blockly.Css', 'Blockly.DropDownDiv', 'Blockly.Events.BlockChange', 'Blockly.Field', 'Blockly.browserEvents', 'Blockly.fieldRegistry', 'Blockly.utils.KeyCodes', 'Blockly.utils.Size', 'Blockly.utils.aria', 'Blockly.utils.colour', 'Blockly.utils.dom', 'Blockly.utils.idGenerator', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/field_dropdown.js', ['Blockly.FieldDropdown'], ['Blockly.DropDownDiv', 'Blockly.Field', 'Blockly.Menu', 'Blockly.MenuItem', 'Blockly.fieldRegistry', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.parsing', 'Blockly.utils.string', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/field_image.js', ['Blockly.FieldImage'], ['Blockly.Field', 'Blockly.fieldRegistry', 'Blockly.utils.Size', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.parsing'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/field_label.js', ['Blockly.FieldLabel'], ['Blockly.Field', 'Blockly.fieldRegistry', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.parsing'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/field_label_serializable.js', ['Blockly.FieldLabelSerializable'], ['Blockly.FieldLabel', 'Blockly.fieldRegistry', 'Blockly.utils.object', 'Blockly.utils.parsing'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/field_multilineinput.js', ['Blockly.FieldMultilineInput'], ['Blockly.Css', 'Blockly.Field', 'Blockly.FieldTextInput', 'Blockly.WidgetDiv', 'Blockly.fieldRegistry', 'Blockly.utils.KeyCodes', 'Blockly.utils.Svg', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.parsing', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/field_number.js', ['Blockly.FieldNumber'], ['Blockly.FieldTextInput', 'Blockly.fieldRegistry', 'Blockly.utils.aria', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/field_registry.js', ['Blockly.fieldRegistry'], ['Blockly.registry'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/field_textinput.js', ['Blockly.FieldTextInput'], ['Blockly.DropDownDiv', 'Blockly.Events.BlockChange', 'Blockly.Events.utils', 'Blockly.Field', 'Blockly.Msg', 'Blockly.WidgetDiv', 'Blockly.browserEvents', 'Blockly.dialog', 'Blockly.fieldRegistry', 'Blockly.utils.Coordinate', 'Blockly.utils.KeyCodes', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.parsing', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/field_variable.js', ['Blockly.FieldVariable'], ['Blockly.Events.BlockChange', 'Blockly.FieldDropdown', 'Blockly.Msg', 'Blockly.VariableModel', 'Blockly.Variables', 'Blockly.Xml', 'Blockly.fieldRegistry', 'Blockly.internalConstants', 'Blockly.utils.Size', 'Blockly.utils.object', 'Blockly.utils.parsing'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/flyout_base.js', ['Blockly.Flyout'], ['Blockly.ComponentManager', 'Blockly.DeleteArea', 'Blockly.Events.BlockCreate', 'Blockly.Events.VarCreate', 'Blockly.Events.utils', 'Blockly.FlyoutMetricsManager', 'Blockly.Gesture', 'Blockly.IFlyout', 'Blockly.ScrollbarPair', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.Variables', 'Blockly.WorkspaceSvg', 'Blockly.Xml', 'Blockly.blockRendering', 'Blockly.browserEvents', 'Blockly.common', 'Blockly.serialization.blocks', 'Blockly.utils.Coordinate', 'Blockly.utils.Rect', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.idGenerator', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/flyout_button.js', ['Blockly.FlyoutButton'], ['Blockly.Css', 'Blockly.browserEvents', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.parsing', 'Blockly.utils.style'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/flyout_horizontal.js', ['Blockly.HorizontalFlyout'], ['Blockly.DropDownDiv', 'Blockly.Flyout', 'Blockly.Scrollbar', 'Blockly.WidgetDiv', 'Blockly.browserEvents', 'Blockly.registry', 'Blockly.utils.Rect', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/flyout_metrics_manager.js', ['Blockly.FlyoutMetricsManager'], ['Blockly.MetricsManager', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/flyout_vertical.js', ['Blockly.VerticalFlyout'], ['Blockly.Block', 'Blockly.DropDownDiv', 'Blockly.Flyout', 'Blockly.Scrollbar', 'Blockly.WidgetDiv', 'Blockly.browserEvents', 'Blockly.constants', 'Blockly.registry', 'Blockly.utils.Rect', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/generator.js', ['Blockly.Generator'], ['Blockly.Names', 'Blockly.common', 'Blockly.utils.deprecation'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/gesture.js', ['Blockly.Gesture'], ['Blockly.BlockDragger', 'Blockly.BubbleDragger', 'Blockly.Events.Click', 'Blockly.Events.utils', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.Workspace', 'Blockly.WorkspaceDragger', 'Blockly.blockAnimations', 'Blockly.browserEvents', 'Blockly.common', 'Blockly.internalConstants', 'Blockly.registry', 'Blockly.utils.Coordinate'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/grid.js', ['Blockly.Grid'], ['Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/icon.js', ['Blockly.Icon'], ['Blockly.browserEvents', 'Blockly.utils.Coordinate', 'Blockly.utils.Size', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.svgMath'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/inject.js', ['Blockly.inject'], ['Blockly.BlockDragSurfaceSvg', 'Blockly.Css', 'Blockly.DropDownDiv', 'Blockly.Grid', 'Blockly.Msg', 'Blockly.Options', 'Blockly.ScrollbarPair', 'Blockly.ShortcutRegistry', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.WidgetDiv', 'Blockly.Workspace', 'Blockly.WorkspaceDragSurfaceSvg', 'Blockly.WorkspaceSvg', 'Blockly.browserEvents', 'Blockly.bumpObjects', 'Blockly.common', 'Blockly.utils.Svg', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/input.js', ['Blockly.Input'], ['Blockly.FieldLabel', 'Blockly.fieldRegistry', 'Blockly.inputTypes'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/input_types.js', ['Blockly.inputTypes'], ['Blockly.ConnectionType'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/insertion_marker_manager.js', ['Blockly.InsertionMarkerManager'], ['Blockly.ComponentManager', 'Blockly.ConnectionType', 'Blockly.Events.utils', 'Blockly.blockAnimations', 'Blockly.common', 'Blockly.constants', 'Blockly.internalConstants'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_ast_node_location.js', ['Blockly.IASTNodeLocation'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_ast_node_location_svg.js', ['Blockly.IASTNodeLocationSvg'], ['Blockly.IASTNodeLocation'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_ast_node_location_with_block.js', ['Blockly.IASTNodeLocationWithBlock'], ['Blockly.IASTNodeLocation'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_autohideable.js', ['Blockly.IAutoHideable'], ['Blockly.IComponent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_block_dragger.js', ['Blockly.IBlockDragger'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_bounded_element.js', ['Blockly.IBoundedElement'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_bubble.js', ['Blockly.IBubble'], ['Blockly.IContextMenu', 'Blockly.IDraggable'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_collapsible_toolbox_item.js', ['Blockly.ICollapsibleToolboxItem'], ['Blockly.ISelectableToolboxItem'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_component.js', ['Blockly.IComponent'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_connection_checker.js', ['Blockly.IConnectionChecker'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_contextmenu.js', ['Blockly.IContextMenu'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_copyable.js', ['Blockly.ICopyable'], ['Blockly.ISelectable'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_deletable.js', ['Blockly.IDeletable'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_delete_area.js', ['Blockly.IDeleteArea'], ['Blockly.IDragTarget'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_drag_target.js', ['Blockly.IDragTarget'], ['Blockly.IComponent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_draggable.js', ['Blockly.IDraggable'], ['Blockly.IDeletable'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_flyout.js', ['Blockly.IFlyout'], ['Blockly.IRegistrable'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_keyboard_accessible.js', ['Blockly.IKeyboardAccessible'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_metrics_manager.js', ['Blockly.IMetricsManager'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_movable.js', ['Blockly.IMovable'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_positionable.js', ['Blockly.IPositionable'], ['Blockly.IComponent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_registrable.js', ['Blockly.IRegistrable'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_registrable_field.js', ['Blockly.IRegistrableField'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_selectable.js', ['Blockly.ISelectable'], ['Blockly.IDeletable', 'Blockly.IMovable'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_selectable_toolbox_item.js', ['Blockly.ISelectableToolboxItem'], ['Blockly.IToolboxItem'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_serializer.js', ['Blockly.serialization.ISerializer'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_styleable.js', ['Blockly.IStyleable'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_toolbox.js', ['Blockly.IToolbox'], ['Blockly.IRegistrable'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/interfaces/i_toolbox_item.js', ['Blockly.IToolboxItem'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/internal_constants.js', ['Blockly.internalConstants'], ['Blockly.ConnectionType'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/keyboard_nav/ast_node.js', ['Blockly.ASTNode'], ['Blockly.ConnectionType', 'Blockly.utils.Coordinate'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/keyboard_nav/basic_cursor.js', ['Blockly.BasicCursor'], ['Blockly.ASTNode', 'Blockly.Cursor', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/keyboard_nav/cursor.js', ['Blockly.Cursor'], ['Blockly.ASTNode', 'Blockly.Marker', 'Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/keyboard_nav/marker.js', ['Blockly.Marker'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/keyboard_nav/tab_navigate_cursor.js', ['Blockly.TabNavigateCursor'], ['Blockly.ASTNode', 'Blockly.BasicCursor', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/marker_manager.js', ['Blockly.MarkerManager'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/menu.js', ['Blockly.Menu'], ['Blockly.browserEvents', 'Blockly.utils.Coordinate', 'Blockly.utils.KeyCodes', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.style'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/menuitem.js', ['Blockly.MenuItem'], ['Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.idGenerator'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/metrics_manager.js', ['Blockly.MetricsManager'], ['Blockly.IMetricsManager', 'Blockly.registry', 'Blockly.utils.Size', 'Blockly.utils.toolbox'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/module_manager.js', ['Blockly.ModuleManager'], ['Blockly.Events', 'Blockly.Events.ModuleDelete', 'Blockly.Events.ModuleRename', 'Blockly.ModuleBar', 'Blockly.ModuleModel', 'Blockly.Msg', 'Blockly.utils', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/module_model.js', ['Blockly.ModuleModel'], ['Blockly.Events', 'Blockly.Events.ModuleCreate', 'Blockly.utils'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/modulebar.js', ['Blockly.ModuleBar'], ['Blockly.Css', 'Blockly.utils', 'Blockly.utils.aria', 'Blockly.utils.colour', 'Blockly.utils.dom', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/msg.js', ['Blockly.Msg'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/mutator.js', ['Blockly.Mutator'], ['Blockly.Bubble', 'Blockly.Events.BlockChange', 'Blockly.Events.BubbleOpen', 'Blockly.Events.utils', 'Blockly.Icon', 'Blockly.Options', 'Blockly.WorkspaceSvg', 'Blockly.internalConstants', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/names.js', ['Blockly.Names'], ['Blockly.Msg', 'Blockly.Variables'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/options.js', ['Blockly.Options'], ['Blockly.Theme', 'Blockly.Themes.Classic', 'Blockly.registry', 'Blockly.utils.idGenerator', 'Blockly.utils.toolbox'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/positionable_helpers.js', ['Blockly.uiPosition'], ['Blockly.Scrollbar', 'Blockly.utils.Rect', 'Blockly.utils.toolbox'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/procedures.js', ['Blockly.Procedures'], ['Blockly.Events.BlockChange', 'Blockly.Events.utils', 'Blockly.Msg', 'Blockly.Names', 'Blockly.Variables', 'Blockly.Workspace', 'Blockly.Xml', 'Blockly.blocks', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/registry.js', ['Blockly.registry'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/rendered_connection.js', ['Blockly.RenderedConnection'], ['Blockly.Connection', 'Blockly.ConnectionType', 'Blockly.Events.utils', 'Blockly.common', 'Blockly.internalConstants', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.svgMath', 'Blockly.utils.svgPaths'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/common/block_rendering.js', ['Blockly.blockRendering'], ['Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.Connection', 'Blockly.blockRendering.ConstantProvider', 'Blockly.blockRendering.Debug', 'Blockly.blockRendering.Drawer', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.Field', 'Blockly.blockRendering.Hat', 'Blockly.blockRendering.IPathObject', 'Blockly.blockRendering.Icon', 'Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.InlineInput', 'Blockly.blockRendering.InputConnection', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.JaggedEdge', 'Blockly.blockRendering.MarkerSvg', 'Blockly.blockRendering.Measurable', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PathObject', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Renderer', 'Blockly.blockRendering.RoundCorner', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.SquareCorner', 'Blockly.blockRendering.StatementInput', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types', 'Blockly.blockRendering.debug', 'Blockly.registry', 'Blockly.utils.deprecation'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/common/constants.js', ['Blockly.blockRendering.ConstantProvider'], ['Blockly.ConnectionType', 'Blockly.utils.Svg', 'Blockly.utils.colour', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.parsing', 'Blockly.utils.svgPaths', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/common/debug.js', ['Blockly.blockRendering.debug'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/common/debugger.js', ['Blockly.blockRendering.Debug'], ['Blockly.ConnectionType', 'Blockly.FieldLabel', 'Blockly.blockRendering.Types', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/common/drawer.js', ['Blockly.blockRendering.Drawer'], ['Blockly.blockRendering.Types', 'Blockly.blockRendering.debug', 'Blockly.utils.svgPaths'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/common/i_path_object.js', ['Blockly.blockRendering.IPathObject'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/common/info.js', ['Blockly.blockRendering.RenderInfo'], ['Blockly.Input', 'Blockly.blockRendering.BottomRow', 'Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.Field', 'Blockly.blockRendering.Hat', 'Blockly.blockRendering.Icon', 'Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.InlineInput', 'Blockly.blockRendering.InputRow', 'Blockly.blockRendering.JaggedEdge', 'Blockly.blockRendering.NextConnection', 'Blockly.blockRendering.OutputConnection', 'Blockly.blockRendering.PreviousConnection', 'Blockly.blockRendering.RoundCorner', 'Blockly.blockRendering.SpacerRow', 'Blockly.blockRendering.SquareCorner', 'Blockly.blockRendering.StatementInput', 'Blockly.blockRendering.TopRow', 'Blockly.blockRendering.Types', 'Blockly.inputTypes'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/common/marker_svg.js', ['Blockly.blockRendering.MarkerSvg'], ['Blockly.ASTNode', 'Blockly.ConnectionType', 'Blockly.Events.MarkerMove', 'Blockly.Events.utils', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.svgPaths'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/common/path_object.js', ['Blockly.blockRendering.PathObject'], ['Blockly.blockRendering.IPathObject', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/common/renderer.js', ['Blockly.blockRendering.Renderer'], ['Blockly.Connection', 'Blockly.ConnectionType', 'Blockly.IRegistrable', 'Blockly.InsertionMarkerManager', 'Blockly.blockRendering.ConstantProvider', 'Blockly.blockRendering.Drawer', 'Blockly.blockRendering.MarkerSvg', 'Blockly.blockRendering.PathObject', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.debug', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/geras/constants.js', ['Blockly.geras.ConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/geras/drawer.js', ['Blockly.geras.Drawer'], ['Blockly.blockRendering.Drawer', 'Blockly.blockRendering.debug', 'Blockly.geras.Highlighter', 'Blockly.utils.object', 'Blockly.utils.svgPaths'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/geras/geras.js', ['Blockly.geras'], ['Blockly.geras.ConstantProvider', 'Blockly.geras.Drawer', 'Blockly.geras.HighlightConstantProvider', 'Blockly.geras.Highlighter', 'Blockly.geras.InlineInput', 'Blockly.geras.PathObject', 'Blockly.geras.RenderInfo', 'Blockly.geras.Renderer', 'Blockly.geras.StatementInput'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/geras/highlight_constants.js', ['Blockly.geras.HighlightConstantProvider'], ['Blockly.utils.svgPaths'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/geras/highlighter.js', ['Blockly.geras.Highlighter'], ['Blockly.blockRendering.Types', 'Blockly.utils.svgPaths'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/geras/info.js', ['Blockly.geras.RenderInfo'], ['Blockly.blockRendering.ExternalValueInput', 'Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Types', 'Blockly.geras.InlineInput', 'Blockly.geras.StatementInput', 'Blockly.inputTypes', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/geras/measurables/inline_input.js', ['Blockly.geras.InlineInput'], ['Blockly.blockRendering.InlineInput', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/geras/measurables/statement_input.js', ['Blockly.geras.StatementInput'], ['Blockly.blockRendering.StatementInput', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/geras/path_object.js', ['Blockly.geras.PathObject'], ['Blockly.blockRendering.PathObject', 'Blockly.utils.Svg', 'Blockly.utils.colour', 'Blockly.utils.dom', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/geras/renderer.js', ['Blockly.geras.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.geras.ConstantProvider', 'Blockly.geras.Drawer', 'Blockly.geras.HighlightConstantProvider', 'Blockly.geras.PathObject', 'Blockly.geras.RenderInfo', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/base.js', ['Blockly.blockRendering.Measurable'], ['Blockly.blockRendering.Types'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/bottom_row.js', ['Blockly.blockRendering.BottomRow'], ['Blockly.blockRendering.Row', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/connection.js', ['Blockly.blockRendering.Connection'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/external_value_input.js', ['Blockly.blockRendering.ExternalValueInput'], ['Blockly.blockRendering.InputConnection', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/field.js', ['Blockly.blockRendering.Field'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/hat.js', ['Blockly.blockRendering.Hat'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/icon.js', ['Blockly.blockRendering.Icon'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/in_row_spacer.js', ['Blockly.blockRendering.InRowSpacer'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/inline_input.js', ['Blockly.blockRendering.InlineInput'], ['Blockly.blockRendering.InputConnection', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/input_connection.js', ['Blockly.blockRendering.InputConnection'], ['Blockly.blockRendering.Connection', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/input_row.js', ['Blockly.blockRendering.InputRow'], ['Blockly.blockRendering.Row', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/jagged_edge.js', ['Blockly.blockRendering.JaggedEdge'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/next_connection.js', ['Blockly.blockRendering.NextConnection'], ['Blockly.blockRendering.Connection', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/output_connection.js', ['Blockly.blockRendering.OutputConnection'], ['Blockly.blockRendering.Connection', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/previous_connection.js', ['Blockly.blockRendering.PreviousConnection'], ['Blockly.blockRendering.Connection', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/round_corner.js', ['Blockly.blockRendering.RoundCorner'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/row.js', ['Blockly.blockRendering.Row'], ['Blockly.blockRendering.Types'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/spacer_row.js', ['Blockly.blockRendering.SpacerRow'], ['Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.Row', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/square_corner.js', ['Blockly.blockRendering.SquareCorner'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/statement_input.js', ['Blockly.blockRendering.StatementInput'], ['Blockly.blockRendering.InputConnection', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/top_row.js', ['Blockly.blockRendering.TopRow'], ['Blockly.blockRendering.Row', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/measurables/types.js', ['Blockly.blockRendering.Types'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/minimalist/constants.js', ['Blockly.minimalist.ConstantProvider'], ['Blockly.blockRendering.ConstantProvider', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/minimalist/drawer.js', ['Blockly.minimalist.Drawer'], ['Blockly.blockRendering.Drawer', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/minimalist/info.js', ['Blockly.minimalist.RenderInfo'], ['Blockly.blockRendering.RenderInfo', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/minimalist/minimalist.js', ['Blockly.minimalist'], ['Blockly.minimalist.ConstantProvider', 'Blockly.minimalist.Drawer', 'Blockly.minimalist.RenderInfo', 'Blockly.minimalist.Renderer'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/minimalist/renderer.js', ['Blockly.minimalist.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.minimalist.ConstantProvider', 'Blockly.minimalist.Drawer', 'Blockly.minimalist.RenderInfo', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/thrasos/info.js', ['Blockly.thrasos.RenderInfo'], ['Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/thrasos/renderer.js', ['Blockly.thrasos.Renderer'], ['Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.thrasos.RenderInfo', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/thrasos/thrasos.js', ['Blockly.thrasos'], ['Blockly.thrasos.RenderInfo', 'Blockly.thrasos.Renderer'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/zelos/constants.js', ['Blockly.zelos.ConstantProvider'], ['Blockly.ConnectionType', 'Blockly.blockRendering.ConstantProvider', 'Blockly.utils.Svg', 'Blockly.utils.colour', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.svgPaths'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/zelos/drawer.js', ['Blockly.zelos.Drawer'], ['Blockly.blockRendering.Drawer', 'Blockly.blockRendering.debug', 'Blockly.utils.object', 'Blockly.utils.svgPaths'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/zelos/info.js', ['Blockly.zelos.RenderInfo'], ['Blockly.FieldImage', 'Blockly.FieldLabel', 'Blockly.FieldTextInput', 'Blockly.Input', 'Blockly.blockRendering.InRowSpacer', 'Blockly.blockRendering.RenderInfo', 'Blockly.blockRendering.Types', 'Blockly.inputTypes', 'Blockly.utils.object', 'Blockly.zelos.BottomRow', 'Blockly.zelos.RightConnectionShape', 'Blockly.zelos.StatementInput', 'Blockly.zelos.TopRow'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/zelos/marker_svg.js', ['Blockly.zelos.MarkerSvg'], ['Blockly.blockRendering.MarkerSvg', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/zelos/measurables/bottom_row.js', ['Blockly.zelos.BottomRow'], ['Blockly.blockRendering.BottomRow', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/zelos/measurables/inputs.js', ['Blockly.zelos.StatementInput'], ['Blockly.blockRendering.StatementInput', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/zelos/measurables/row_elements.js', ['Blockly.zelos.RightConnectionShape'], ['Blockly.blockRendering.Measurable', 'Blockly.blockRendering.Types', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/zelos/measurables/top_row.js', ['Blockly.zelos.TopRow'], ['Blockly.blockRendering.TopRow', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/zelos/path_object.js', ['Blockly.zelos.PathObject'], ['Blockly.blockRendering.PathObject', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/zelos/renderer.js', ['Blockly.zelos.Renderer'], ['Blockly.ConnectionType', 'Blockly.InsertionMarkerManager', 'Blockly.blockRendering', 'Blockly.blockRendering.Renderer', 'Blockly.utils.object', 'Blockly.zelos.ConstantProvider', 'Blockly.zelos.Drawer', 'Blockly.zelos.MarkerSvg', 'Blockly.zelos.PathObject', 'Blockly.zelos.RenderInfo'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/renderers/zelos/zelos.js', ['Blockly.zelos'], ['Blockly.zelos.BottomRow', 'Blockly.zelos.ConstantProvider', 'Blockly.zelos.Drawer', 'Blockly.zelos.MarkerSvg', 'Blockly.zelos.PathObject', 'Blockly.zelos.RenderInfo', 'Blockly.zelos.Renderer', 'Blockly.zelos.RightConnectionShape', 'Blockly.zelos.StatementInput', 'Blockly.zelos.TopRow'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/scrollbar.js', ['Blockly.Scrollbar'], ['Blockly.Touch', 'Blockly.browserEvents', 'Blockly.utils.Coordinate', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.svgMath'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/scrollbar_pair.js', ['Blockly.ScrollbarPair'], ['Blockly.Events.utils', 'Blockly.Scrollbar', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/serialization/blocks.js', ['Blockly.serialization.blocks'], ['Blockly.Events.utils', 'Blockly.Xml', 'Blockly.inputTypes', 'Blockly.serialization.ISerializer', 'Blockly.serialization.exceptions', 'Blockly.serialization.priorities', 'Blockly.serialization.registry', 'Blockly.utils.Size'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/serialization/exceptions.js', ['Blockly.serialization.exceptions'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/serialization/priorities.js', ['Blockly.serialization.priorities'], [], {'module': 'goog'}); +goog.addDependency('../../core/serialization/registry.js', ['Blockly.serialization.registry'], ['Blockly.registry'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/serialization/variables.js', ['Blockly.serialization.variables'], ['Blockly.serialization.ISerializer', 'Blockly.serialization.priorities', 'Blockly.serialization.registry'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/serialization/workspaces.js', ['Blockly.serialization.workspaces'], ['Blockly.Events.utils', 'Blockly.Workspace', 'Blockly.registry', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/shortcut_items.js', ['Blockly.ShortcutItems'], ['Blockly.Gesture', 'Blockly.ShortcutRegistry', 'Blockly.clipboard', 'Blockly.common', 'Blockly.utils.KeyCodes'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/shortcut_registry.js', ['Blockly.ShortcutRegistry'], ['Blockly.utils.KeyCodes', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/theme.js', ['Blockly.Theme'], ['Blockly.registry', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/theme/classic.js', ['Blockly.Themes.Classic'], ['Blockly.Theme'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/theme/themes.js', ['Blockly.Themes'], ['Blockly.Themes.Classic', 'Blockly.Themes.Zelos'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/theme/zelos.js', ['Blockly.Themes.Zelos'], ['Blockly.Theme'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/theme_manager.js', ['Blockly.ThemeManager'], ['Blockly.utils.array', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/toolbox/category.js', ['Blockly.ToolboxCategory'], ['Blockly.Css', 'Blockly.ISelectableToolboxItem', 'Blockly.ToolboxItem', 'Blockly.registry', 'Blockly.utils.aria', 'Blockly.utils.colour', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.parsing', 'Blockly.utils.toolbox'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/toolbox/collapsible_category.js', ['Blockly.CollapsibleToolboxCategory'], ['Blockly.ICollapsibleToolboxItem', 'Blockly.ToolboxCategory', 'Blockly.ToolboxSeparator', 'Blockly.registry', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/toolbox/separator.js', ['Blockly.ToolboxSeparator'], ['Blockly.Css', 'Blockly.ToolboxItem', 'Blockly.registry', 'Blockly.utils.dom', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/toolbox/toolbox.js', ['Blockly.Toolbox'], ['Blockly.BlockSvg', 'Blockly.CollapsibleToolboxCategory', 'Blockly.ComponentManager', 'Blockly.Css', 'Blockly.DeleteArea', 'Blockly.Events.ToolboxItemSelect', 'Blockly.Events.utils', 'Blockly.IAutoHideable', 'Blockly.IKeyboardAccessible', 'Blockly.IStyleable', 'Blockly.IToolbox', 'Blockly.Options', 'Blockly.Touch', 'Blockly.browserEvents', 'Blockly.common', 'Blockly.registry', 'Blockly.utils.KeyCodes', 'Blockly.utils.Rect', 'Blockly.utils.aria', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/toolbox/toolbox_item.js', ['Blockly.ToolboxItem'], ['Blockly.IToolboxItem', 'Blockly.utils.idGenerator'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/tooltip.js', ['Blockly.Tooltip'], ['Blockly.browserEvents', 'Blockly.common', 'Blockly.utils.deprecation', 'Blockly.utils.string'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/touch.js', ['Blockly.Touch'], ['Blockly.internalConstants', 'Blockly.utils.global', 'Blockly.utils.string'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/touch_gesture.js', ['Blockly.TouchGesture'], ['Blockly.Gesture', 'Blockly.Touch', 'Blockly.browserEvents', 'Blockly.utils.Coordinate', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/trashcan.js', ['Blockly.Trashcan'], ['Blockly.ComponentManager', 'Blockly.DeleteArea', 'Blockly.Events.TrashcanOpen', 'Blockly.Events.utils', 'Blockly.IAutoHideable', 'Blockly.IPositionable', 'Blockly.Options', 'Blockly.browserEvents', 'Blockly.internalConstants', 'Blockly.registry', 'Blockly.uiPosition', 'Blockly.utils.Rect', 'Blockly.utils.Size', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.toolbox'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils.js', ['Blockly.utils'], ['Blockly.Extensions', 'Blockly.browserEvents', 'Blockly.common', 'Blockly.utils.Coordinate', 'Blockly.utils.KeyCodes', 'Blockly.utils.Metrics', 'Blockly.utils.Rect', 'Blockly.utils.Size', 'Blockly.utils.Svg', 'Blockly.utils.aria', 'Blockly.utils.array', 'Blockly.utils.colour', 'Blockly.utils.deprecation', 'Blockly.utils.dom', 'Blockly.utils.global', 'Blockly.utils.idGenerator', 'Blockly.utils.math', 'Blockly.utils.object', 'Blockly.utils.parsing', 'Blockly.utils.string', 'Blockly.utils.style', 'Blockly.utils.svgMath', 'Blockly.utils.svgPaths', 'Blockly.utils.toolbox', 'Blockly.utils.userAgent', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/aria.js', ['Blockly.utils.aria'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/array.js', ['Blockly.utils.array'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/colour.js', ['Blockly.utils.colour'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/coordinate.js', ['Blockly.utils.Coordinate'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/deprecation.js', ['Blockly.utils.deprecation'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/dom.js', ['Blockly.utils.dom'], ['Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/global.js', ['Blockly.utils.global'], [], {'module': 'goog'}); +goog.addDependency('../../core/utils/idgenerator.js', ['Blockly.utils.idGenerator'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/keycodes.js', ['Blockly.utils.KeyCodes'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/math.js', ['Blockly.utils.math'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/metrics.js', ['Blockly.utils.Metrics'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/object.js', ['Blockly.utils.object'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/parsing.js', ['Blockly.utils.parsing'], ['Blockly.Msg', 'Blockly.utils.colour', 'Blockly.utils.string'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/rect.js', ['Blockly.utils.Rect'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/size.js', ['Blockly.utils.Size'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/string.js', ['Blockly.utils.string'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/style.js', ['Blockly.utils.style'], ['Blockly.utils.Coordinate', 'Blockly.utils.Size'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/svg.js', ['Blockly.utils.Svg'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/svg_math.js', ['Blockly.utils.svgMath'], ['Blockly.utils.Coordinate', 'Blockly.utils.Rect', 'Blockly.utils.Size', 'Blockly.utils.deprecation', 'Blockly.utils.global', 'Blockly.utils.style', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/svg_paths.js', ['Blockly.utils.svgPaths'], [], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/toolbox.js', ['Blockly.utils.toolbox'], ['Blockly.Xml', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/useragent.js', ['Blockly.utils.userAgent'], ['Blockly.utils.global'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/utils/xml.js', ['Blockly.utils.xml'], ['Blockly.utils.global'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/variable_map.js', ['Blockly.VariableMap'], ['Blockly.Events.VarDelete', 'Blockly.Events.VarRename', 'Blockly.Events.utils', 'Blockly.Msg', 'Blockly.Names', 'Blockly.VariableModel', 'Blockly.dialog', 'Blockly.utils.array', 'Blockly.utils.idGenerator', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/variable_model.js', ['Blockly.VariableModel'], ['Blockly.Events.VarCreate', 'Blockly.Events.utils', 'Blockly.utils.idGenerator'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/variables.js', ['Blockly.Variables'], ['Blockly.Msg', 'Blockly.VariableModel', 'Blockly.Xml', 'Blockly.blocks', 'Blockly.dialog', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/variables_dynamic.js', ['Blockly.VariablesDynamic'], ['Blockly.Msg', 'Blockly.VariableModel', 'Blockly.Variables', 'Blockly.blocks', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/warning.js', ['Blockly.Warning'], ['Blockly.Bubble', 'Blockly.Events.BubbleOpen', 'Blockly.Events.utils', 'Blockly.Icon', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/widgetdiv.js', ['Blockly.WidgetDiv'], ['Blockly.common', 'Blockly.utils.deprecation', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/workspace.js', ['Blockly.Workspace'], ['Blockly.ConnectionChecker', 'Blockly.Events.utils', 'Blockly.IASTNodeLocation', 'Blockly.ModuleManager', 'Blockly.Options', 'Blockly.VariableMap', 'Blockly.registry', 'Blockly.utils.array', 'Blockly.utils.idGenerator', 'Blockly.utils.math'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/workspace_audio.js', ['Blockly.WorkspaceAudio'], ['Blockly.internalConstants', 'Blockly.utils.global', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/workspace_comment.js', ['Blockly.WorkspaceComment'], ['Blockly.Events.CommentChange', 'Blockly.Events.CommentCreate', 'Blockly.Events.CommentDelete', 'Blockly.Events.CommentMove', 'Blockly.Events.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.idGenerator', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/workspace_comment_svg.js', ['Blockly.WorkspaceCommentSvg'], ['Blockly.ContextMenu', 'Blockly.Css', 'Blockly.Events.CommentCreate', 'Blockly.Events.CommentDelete', 'Blockly.Events.CommentMove', 'Blockly.Events.Selected', 'Blockly.Events.utils', 'Blockly.IBoundedElement', 'Blockly.IBubble', 'Blockly.ICopyable', 'Blockly.Touch', 'Blockly.WorkspaceComment', 'Blockly.browserEvents', 'Blockly.common', 'Blockly.utils.Coordinate', 'Blockly.utils.Rect', 'Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.svgMath'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/workspace_drag_surface_svg.js', ['Blockly.WorkspaceDragSurfaceSvg'], ['Blockly.utils.Svg', 'Blockly.utils.dom', 'Blockly.utils.svgMath'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/workspace_dragger.js', ['Blockly.WorkspaceDragger'], ['Blockly.common', 'Blockly.utils.Coordinate'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/workspace_svg.js', ['Blockly.WorkspaceSvg'], ['Blockly.BlockSvg', 'Blockly.ComponentManager', 'Blockly.ConnectionDB', 'Blockly.ContextMenu', 'Blockly.ContextMenuRegistry', 'Blockly.DropDownDiv', 'Blockly.Events.BlockCreate', 'Blockly.Events.ThemeChange', 'Blockly.Events.ViewportChange', 'Blockly.Events.utils', 'Blockly.Gesture', 'Blockly.Grid', 'Blockly.IASTNodeLocationSvg', 'Blockly.MarkerManager', 'Blockly.MetricsManager', 'Blockly.ModuleBar', 'Blockly.Msg', 'Blockly.Options', 'Blockly.ThemeManager', 'Blockly.Themes.Classic', 'Blockly.Tooltip', 'Blockly.TouchGesture', 'Blockly.WidgetDiv', 'Blockly.Workspace', 'Blockly.WorkspaceAudio', 'Blockly.Xml', 'Blockly.blockRendering', 'Blockly.browserEvents', 'Blockly.common', 'Blockly.internalConstants', 'Blockly.registry', 'Blockly.serialization.blocks', 'Blockly.utils', 'Blockly.utils.Coordinate', 'Blockly.utils.Rect', 'Blockly.utils.Size', 'Blockly.utils.Svg', 'Blockly.utils.array', 'Blockly.utils.dom', 'Blockly.utils.object', 'Blockly.utils.svgMath', 'Blockly.utils.toolbox', 'Blockly.utils.userAgent'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/xml.js', ['Blockly.Xml'], ['Blockly.Events.utils', 'Blockly.inputTypes', 'Blockly.utils.Size', 'Blockly.utils.dom', 'Blockly.utils.xml'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../core/zoom_controls.js', ['Blockly.ZoomControls'], ['Blockly.ComponentManager', 'Blockly.Css', 'Blockly.Events.Click', 'Blockly.Events.utils', 'Blockly.IPositionable', 'Blockly.Touch', 'Blockly.browserEvents', 'Blockly.internalConstants', 'Blockly.uiPosition', 'Blockly.utils.Rect', 'Blockly.utils.Size', 'Blockly.utils.Svg', 'Blockly.utils.dom'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/dart.js', ['Blockly.Dart'], ['Blockly.Generator', 'Blockly.Names', 'Blockly.Variables', 'Blockly.inputTypes', 'Blockly.utils.string'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/dart/all.js', ['Blockly.Dart.all'], ['Blockly.Dart.colour', 'Blockly.Dart.lists', 'Blockly.Dart.logic', 'Blockly.Dart.loops', 'Blockly.Dart.math', 'Blockly.Dart.procedures', 'Blockly.Dart.texts', 'Blockly.Dart.variables', 'Blockly.Dart.variablesDynamic'], {'module': 'goog'}); +goog.addDependency('../../generators/dart/colour.js', ['Blockly.Dart.colour'], ['Blockly.Dart'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/dart/lists.js', ['Blockly.Dart.lists'], ['Blockly.Dart', 'Blockly.Names'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/dart/logic.js', ['Blockly.Dart.logic'], ['Blockly.Dart'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/dart/loops.js', ['Blockly.Dart.loops'], ['Blockly.Dart', 'Blockly.Names', 'Blockly.utils.string'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/dart/math.js', ['Blockly.Dart.math'], ['Blockly.Dart', 'Blockly.Names'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/dart/procedures.js', ['Blockly.Dart.procedures'], ['Blockly.Dart', 'Blockly.Names'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/dart/text.js', ['Blockly.Dart.texts'], ['Blockly.Dart', 'Blockly.Names'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/dart/variables.js', ['Blockly.Dart.variables'], ['Blockly.Dart', 'Blockly.Names'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/dart/variables_dynamic.js', ['Blockly.Dart.variablesDynamic'], ['Blockly.Dart', 'Blockly.Dart.variables'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/javascript.js', ['Blockly.JavaScript'], ['Blockly.Generator', 'Blockly.Names', 'Blockly.Variables', 'Blockly.inputTypes', 'Blockly.utils.global', 'Blockly.utils.object', 'Blockly.utils.string'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/javascript/all.js', ['Blockly.JavaScript.all'], ['Blockly.JavaScript.colour', 'Blockly.JavaScript.lists', 'Blockly.JavaScript.logic', 'Blockly.JavaScript.loops', 'Blockly.JavaScript.math', 'Blockly.JavaScript.procedures', 'Blockly.JavaScript.texts', 'Blockly.JavaScript.variables', 'Blockly.JavaScript.variablesDynamic'], {'module': 'goog'}); +goog.addDependency('../../generators/javascript/colour.js', ['Blockly.JavaScript.colour'], ['Blockly.JavaScript'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/javascript/lists.js', ['Blockly.JavaScript.lists'], ['Blockly.JavaScript', 'Blockly.Names'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/javascript/logic.js', ['Blockly.JavaScript.logic'], ['Blockly.JavaScript'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/javascript/loops.js', ['Blockly.JavaScript.loops'], ['Blockly.JavaScript', 'Blockly.Names', 'Blockly.utils.string'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/javascript/math.js', ['Blockly.JavaScript.math'], ['Blockly.JavaScript', 'Blockly.Names'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/javascript/procedures.js', ['Blockly.JavaScript.procedures'], ['Blockly.JavaScript', 'Blockly.Names'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/javascript/text.js', ['Blockly.JavaScript.texts'], ['Blockly.JavaScript', 'Blockly.Names'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/javascript/variables.js', ['Blockly.JavaScript.variables'], ['Blockly.JavaScript', 'Blockly.Names'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/javascript/variables_dynamic.js', ['Blockly.JavaScript.variablesDynamic'], ['Blockly.JavaScript', 'Blockly.JavaScript.variables'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/lua.js', ['Blockly.Lua'], ['Blockly.Generator', 'Blockly.Names', 'Blockly.inputTypes', 'Blockly.utils.object', 'Blockly.utils.string'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/lua/all.js', ['Blockly.Lua.all'], ['Blockly.Lua.colour', 'Blockly.Lua.lists', 'Blockly.Lua.logic', 'Blockly.Lua.loops', 'Blockly.Lua.math', 'Blockly.Lua.procedures', 'Blockly.Lua.texts', 'Blockly.Lua.variables', 'Blockly.Lua.variablesDynamic'], {'module': 'goog'}); +goog.addDependency('../../generators/lua/colour.js', ['Blockly.Lua.colour'], ['Blockly.Lua'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/lua/lists.js', ['Blockly.Lua.lists'], ['Blockly.Lua', 'Blockly.Names'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/lua/logic.js', ['Blockly.Lua.logic'], ['Blockly.Lua'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/lua/loops.js', ['Blockly.Lua.loops'], ['Blockly.Lua', 'Blockly.Names', 'Blockly.utils.string'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/lua/math.js', ['Blockly.Lua.math'], ['Blockly.Lua', 'Blockly.Names'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/lua/procedures.js', ['Blockly.Lua.procedures'], ['Blockly.Lua', 'Blockly.Names'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/lua/text.js', ['Blockly.Lua.texts'], ['Blockly.Lua', 'Blockly.Names'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/lua/variables.js', ['Blockly.Lua.variables'], ['Blockly.Lua', 'Blockly.Names'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/lua/variables_dynamic.js', ['Blockly.Lua.variablesDynamic'], ['Blockly.Lua', 'Blockly.Lua.variables'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/php.js', ['Blockly.PHP'], ['Blockly.Generator', 'Blockly.Names', 'Blockly.inputTypes', 'Blockly.utils.object', 'Blockly.utils.string'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/php/all.js', ['Blockly.PHP.all'], ['Blockly.PHP.colour', 'Blockly.PHP.lists', 'Blockly.PHP.logic', 'Blockly.PHP.loops', 'Blockly.PHP.math', 'Blockly.PHP.procedures', 'Blockly.PHP.texts', 'Blockly.PHP.variables', 'Blockly.PHP.variablesDynamic'], {'module': 'goog'}); +goog.addDependency('../../generators/php/colour.js', ['Blockly.PHP.colour'], ['Blockly.PHP'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/php/lists.js', ['Blockly.PHP.lists'], ['Blockly.Names', 'Blockly.PHP', 'Blockly.utils.string'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/php/logic.js', ['Blockly.PHP.logic'], ['Blockly.PHP'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/php/loops.js', ['Blockly.PHP.loops'], ['Blockly.Names', 'Blockly.PHP', 'Blockly.utils.string'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/php/math.js', ['Blockly.PHP.math'], ['Blockly.Names', 'Blockly.PHP'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/php/procedures.js', ['Blockly.PHP.procedures'], ['Blockly.Names', 'Blockly.PHP', 'Blockly.Variables'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/php/text.js', ['Blockly.PHP.texts'], ['Blockly.Names', 'Blockly.PHP'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/php/variables.js', ['Blockly.PHP.variables'], ['Blockly.Names', 'Blockly.PHP'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/php/variables_dynamic.js', ['Blockly.PHP.variablesDynamic'], ['Blockly.PHP', 'Blockly.PHP.variables'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/python.js', ['Blockly.Python'], ['Blockly.Generator', 'Blockly.Names', 'Blockly.Variables', 'Blockly.inputTypes', 'Blockly.utils.string'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/python/all.js', ['Blockly.Python.all'], ['Blockly.Python.colour', 'Blockly.Python.lists', 'Blockly.Python.logic', 'Blockly.Python.loops', 'Blockly.Python.math', 'Blockly.Python.procedures', 'Blockly.Python.texts', 'Blockly.Python.variables', 'Blockly.Python.variablesDynamic'], {'module': 'goog'}); +goog.addDependency('../../generators/python/colour.js', ['Blockly.Python.colour'], ['Blockly.Python'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/python/lists.js', ['Blockly.Python.lists'], ['Blockly.Names', 'Blockly.Python', 'Blockly.utils.string'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/python/logic.js', ['Blockly.Python.logic'], ['Blockly.Python'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/python/loops.js', ['Blockly.Python.loops'], ['Blockly.Names', 'Blockly.Python', 'Blockly.utils.string'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/python/math.js', ['Blockly.Python.math'], ['Blockly.Names', 'Blockly.Python'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/python/procedures.js', ['Blockly.Python.procedures'], ['Blockly.Names', 'Blockly.Python', 'Blockly.Variables'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/python/text.js', ['Blockly.Python.texts'], ['Blockly.Names', 'Blockly.Python', 'Blockly.utils.string'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/python/variables.js', ['Blockly.Python.variables'], ['Blockly.Names', 'Blockly.Python'], {'lang': 'es6', 'module': 'goog'}); +goog.addDependency('../../generators/python/variables_dynamic.js', ['Blockly.Python.variablesDynamic'], ['Blockly.Python', 'Blockly.Python.variables'], {'lang': 'es6', 'module': 'goog'}); + diff --git a/tests/deps.mocha.js b/tests/deps.mocha.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/generators/golden/generated.js b/tests/generators/golden/generated.js index d6361ae1ac6..01e9bd311a6 100644 --- a/tests/generators/golden/generated.js +++ b/tests/generators/golden/generated.js @@ -38,11 +38,11 @@ function assertEquals(actual, expected, message) { function equals(a, b) { if (a === b) { return true; - } else if ((typeof a == "number") && (typeof b == "number") && + } else if ((typeof a === "number") && (typeof b === "number") && (a.toPrecision(15) == b.toPrecision(15))) { return true; } else if (a instanceof Array && b instanceof Array) { - if (a.length != b.length) { + if (a.length !== b.length) { return false; } for (var i = 0; i < a.length; i++) { @@ -177,7 +177,7 @@ function test_foreach() { function test_repeat() { count = 0; for (var count2 = 0; count2 < 10; count2++) { - count = (typeof count == 'number' ? count : 0) + 1; + count = (typeof count === 'number' ? count : 0) + 1; } assertEquals(count, 10, 'repeat 10'); } @@ -192,12 +192,12 @@ function test_while() { } count = 1; while (count != 10) { - count = (typeof count == 'number' ? count : 0) + 1; + count = (typeof count === 'number' ? count : 0) + 1; } assertEquals(count, 10, 'while 10'); count = 1; while (!(count == 10)) { - count = (typeof count == 'number' ? count : 0) + 1; + count = (typeof count === 'number' ? count : 0) + 1; } assertEquals(count, 10, 'until 10'); } @@ -206,7 +206,7 @@ function test_while() { function test_repeat_ext() { count = 0; for (var count3 = 0; count3 < 10; count3++) { - count = (typeof count == 'number' ? count : 0) + 1; + count = (typeof count === 'number' ? count : 0) + 1; } assertEquals(count, 10, 'repeat 10'); } @@ -304,7 +304,7 @@ function test_continue() { log = ''; count = 0; while (count != 8) { - count = (typeof count == 'number' ? count : 0) + 1; + count = (typeof count === 'number' ? count : 0) + 1; if (count == 5) { continue; } @@ -314,7 +314,7 @@ function test_continue() { log = ''; count = 0; while (!(count == 8)) { - count = (typeof count == 'number' ? count : 0) + 1; + count = (typeof count === 'number' ? count : 0) + 1; if (count == 5) { continue; } @@ -348,7 +348,7 @@ function test_break() { if (count == 5) { break; } - count = (typeof count == 'number' ? count : 0) + 1; + count = (typeof count === 'number' ? count : 0) + 1; } assertEquals(count, 5, 'while break'); count = 1; @@ -356,7 +356,7 @@ function test_break() { if (count == 5) { break; } - count = (typeof count == 'number' ? count : 0) + 1; + count = (typeof count === 'number' ? count : 0) + 1; } assertEquals(count, 5, 'until break'); log = ''; @@ -432,12 +432,12 @@ function mathIsPrime(n) { } // False if n is NaN, negative, is 1, or not whole. // And false if n is divisible by 2 or 3. - if (isNaN(n) || n <= 1 || n % 1 != 0 || n % 2 == 0 || n % 3 == 0) { + if (isNaN(n) || n <= 1 || n % 1 !== 0 || n % 2 === 0 || n % 3 === 0) { return false; } // Check all the numbers of form 6k +/- 1, up to sqrt(n). for (var x = 6; x <= Math.sqrt(n) + 1; x += 6) { - if (n % (x - 1) == 0 || n % (x + 1) == 0) { + if (n % (x - 1) === 0 || n % (x + 1) === 0) { return false; } } @@ -446,15 +446,15 @@ function mathIsPrime(n) { // Tests the "number property" blocks. function test_number_properties() { - assertEquals(42 % 2 == 0, true, 'even'); - assertEquals(42.1 % 2 == 1, false, 'odd'); + assertEquals(42 % 2 === 0, true, 'even'); + assertEquals(42.1 % 2 === 1, false, 'odd'); assertEquals(mathIsPrime(5), true, 'prime 5'); assertEquals(mathIsPrime(25), false, 'prime 25'); assertEquals(mathIsPrime(-31.1), false, 'prime negative'); - assertEquals(Math.PI % 1 == 0, false, 'whole'); + assertEquals(Math.PI % 1 === 0, false, 'whole'); assertEquals(Infinity > 0, true, 'positive'); assertEquals(-42 < 0, true, 'negative'); - assertEquals(42 % 2 == 0, true, 'divisible'); + assertEquals(42 % 2 === 0, true, 'divisible'); } // Tests the "round" block. @@ -467,7 +467,7 @@ function test_round() { // Tests the "change" block. function test_change() { varToChange = 100; - varToChange = (typeof varToChange == 'number' ? varToChange : 0) + 42; + varToChange = (typeof varToChange === 'number' ? varToChange : 0) + 42; assertEquals(varToChange, 142, 'change'); } @@ -476,10 +476,10 @@ function mathMean(myList) { } function mathMedian(myList) { - var localList = myList.filter(function (x) {return typeof x == 'number';}); + var localList = myList.filter(function (x) {return typeof x === 'number';}); if (!localList.length) return null; localList.sort(function(a, b) {return b - a;}); - if (localList.length % 2 == 0) { + if (localList.length % 2 === 0) { return (localList[localList.length / 2 - 1] + localList[localList.length / 2]) / 2; } else { return localList[(localList.length - 1) / 2]; @@ -508,7 +508,7 @@ function mathModes(values) { maxCount = Math.max(thisCount, maxCount); } for (var j = 0; j < counts.length; j++) { - if (counts[j][1] == maxCount) { + if (counts[j][1] === maxCount) { modes.push(counts[j][0]); } } @@ -569,7 +569,7 @@ function mathRandomInt(a, b) { function test_random_integer() { rand = mathRandomInt(5, 10); assertEquals(rand >= 5 && rand <= 10, true, 'randRange'); - assertEquals(rand % 1 == 0, true, 'randInteger'); + assertEquals(rand % 1 === 0, true, 'randInteger'); } // Tests the "random fraction" block. @@ -645,7 +645,7 @@ function test_find_text_simple() { // Creates a string for use with the find test. function get_fruit() { - number_of_calls = (typeof number_of_calls == 'number' ? number_of_calls : 0) + 1; + number_of_calls = (typeof number_of_calls === 'number' ? number_of_calls : 0) + 1; return 'Banana'; } @@ -691,7 +691,7 @@ function test_get_text_simple() { // Creates a string for use with the get test. function get_Blockly() { - number_of_calls = (typeof number_of_calls == 'number' ? number_of_calls : 0) + 1; + number_of_calls = (typeof number_of_calls === 'number' ? number_of_calls : 0) + 1; return 'Blockly'; } @@ -733,7 +733,7 @@ function test_get_text_complex() { // Creates a string for use with the substring test. function get_numbers() { - number_of_calls = (typeof number_of_calls == 'number' ? number_of_calls : 0) + 1; + number_of_calls = (typeof number_of_calls === 'number' ? number_of_calls : 0) + 1; return '123456789'; } @@ -984,7 +984,7 @@ function test_find_lists_simple() { // Creates a list for use with the find test. function get_names() { - number_of_calls = (typeof number_of_calls == 'number' ? number_of_calls : 0) + 1; + number_of_calls = (typeof number_of_calls === 'number' ? number_of_calls : 0) + 1; return ['Alice', 'Eve', 'Bob', 'Eve']; } @@ -1046,7 +1046,7 @@ function test_get_lists_create_list() { // Creates a list for use with the get test. function get_star_wars() { - number_of_calls = (typeof number_of_calls == 'number' ? number_of_calls : 0) + 1; + number_of_calls = (typeof number_of_calls === 'number' ? number_of_calls : 0) + 1; return ['Kirk', 'Spock', 'McCoy']; } @@ -1263,7 +1263,7 @@ function test_sublist_simple() { // Creates a list for use with the sublist test. function get_space_shuttles() { - number_of_calls = (typeof number_of_calls == 'number' ? number_of_calls : 0) + 1; + number_of_calls = (typeof number_of_calls === 'number' ? number_of_calls : 0) + 1; return ['Columbia', 'Challenger', 'Discovery', 'Atlantis', 'Endeavour']; } @@ -1476,7 +1476,7 @@ function function_1(func_x, func_y) { // Describe this function... function function_2(func_a) { - func_a = (typeof func_a == 'number' ? func_a : 0) + 1; + func_a = (typeof func_a === 'number' ? func_a : 0) + 1; return String(func_a) + String(func_c); } diff --git a/tests/generators/index.html b/tests/generators/index.html index 7ce86c72476..b258ad18dc0 100644 --- a/tests/generators/index.html +++ b/tests/generators/index.html @@ -4,73 +4,23 @@ Blockly Generator Tests + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - + + + +
+ +

Blockly Playground

+ +

Show + - Hide + - Advanced

+ +
+ + +
+

+ + + +
+ + + + + +
+ +

+ +

+ Stress test:   + + + +

+
    +
  • + + +
  • +
  • + + +
  • +
+ + + + + + + + + + + + + + diff --git a/tests/playgrounds/multi_compressed.html b/tests/playgrounds/multi_compressed.html new file mode 100644 index 00000000000..29f0ae3838d --- /dev/null +++ b/tests/playgrounds/multi_compressed.html @@ -0,0 +1,470 @@ + + + + +Multi-toolbox Compressed Playground + + + + + + + + +

Blockly Multi Playground

+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LTR, Vertical, StartRTL, Vertical, Start
LTR, Vertical, EndRTL, Vertical, End
LTR, Horizontal, StartRTL, Horizontal, Start
LTR, Horizontal, EndRTL, Horizontal, End
+ + + + + + diff --git a/tests/playgrounds/screenshot.js b/tests/playgrounds/screenshot.js index 07521b31037..a1ee9a204c8 100644 --- a/tests/playgrounds/screenshot.js +++ b/tests/playgrounds/screenshot.js @@ -6,7 +6,6 @@ /** * @fileoverview Download screenshot. - * @author samelh@google.com (Sam El-Husseini) */ 'use strict'; @@ -18,18 +17,18 @@ * @param {!Function} callback Callback. */ function svgToPng_(data, width, height, callback) { - var canvas = document.createElement("canvas"); - var context = canvas.getContext("2d"); - var img = new Image(); + const canvas = document.createElement("canvas"); + const context = canvas.getContext("2d"); + const img = new Image(); - var pixelDensity = 10; + const pixelDensity = 10; canvas.width = width * pixelDensity; canvas.height = height * pixelDensity; img.onload = function() { context.drawImage( img, 0, 0, width, height, 0, 0, canvas.width, canvas.height); try { - var dataUri = canvas.toDataURL('image/png'); + const dataUri = canvas.toDataURL('image/png'); callback(dataUri); } catch (err) { console.warn('Error converting the workspace svg to a png'); @@ -46,24 +45,23 @@ function svgToPng_(data, width, height, callback) { * @param {string=} customCss Custom CSS to append to the SVG. */ function workspaceToSvg_(workspace, callback, customCss) { - // Go through all text areas and set their value. - var textAreas = document.getElementsByTagName("textarea"); - for (var i = 0; i < textAreas.length; i++) { + const textAreas = document.getElementsByTagName("textarea"); + for (let i = 0; i < textAreas.length; i++) { textAreas[i].innerHTML = textAreas[i].value; } - var bBox = workspace.getBlocksBoundingBox(); - var x = bBox.x || bBox.left; - var y = bBox.y || bBox.top; - var width = bBox.width || bBox.right - x; - var height = bBox.height || bBox.bottom - y; + const bBox = workspace.getBlocksBoundingBox(); + const x = bBox.x || bBox.left; + const y = bBox.y || bBox.top; + const width = bBox.width || bBox.right - x; + const height = bBox.height || bBox.bottom - y; - var blockCanvas = workspace.getCanvas(); - var clone = blockCanvas.cloneNode(true); + const blockCanvas = workspace.getCanvas(); + const clone = blockCanvas.cloneNode(true); clone.removeAttribute('transform'); - - var svg = document.createElementNS('http://www.w3.org/2000/svg','svg'); + + const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); svg.appendChild(clone); svg.setAttribute('viewBox', @@ -76,17 +74,19 @@ function workspaceToSvg_(workspace, callback, customCss) { svg.setAttribute('height', height); svg.setAttribute("style", 'background-color: transparent'); - var css = [].slice.call(document.head.querySelectorAll('style')) - .filter(function(el) { return /\.blocklySvg/.test(el.innerText) || - (el.id.indexOf('blockly-') === 0); }).map(function(el) { - return el.innerText; }).join('\n'); - var style = document.createElement('style'); + const css = [].slice.call(document.head.querySelectorAll('style')) + .filter( + (el) => /\.blocklySvg/.test(el.innerText) || + (el.id.indexOf('blockly-') === 0)) + .map((el) => el.innerText) + .join('\n'); + const style = document.createElement('style'); style.innerHTML = css + '\n' + customCss; svg.insertBefore(style, svg.firstChild); - var svgAsXML = (new XMLSerializer).serializeToString(svg); + let svgAsXML = (new XMLSerializer).serializeToString(svg); svgAsXML = svgAsXML.replace(/ /g, ' '); - var data = 'data:image/svg+xml,' + encodeURIComponent(svgAsXML); + const data = 'data:image/svg+xml,' + encodeURIComponent(svgAsXML); svgToPng_(data, width, height, callback); } @@ -97,7 +97,7 @@ function workspaceToSvg_(workspace, callback, customCss) { */ Blockly.downloadScreenshot = function(workspace) { workspaceToSvg_(workspace, function(datauri) { - var a = document.createElement('a'); + const a = document.createElement('a'); a.download = 'screenshot.png'; a.target = '_self'; a.href = datauri; diff --git a/tests/run_all_tests.sh b/tests/run_all_tests.sh index 5107a6ebf8d..9318ede6a78 100755 --- a/tests/run_all_tests.sh +++ b/tests/run_all_tests.sh @@ -49,16 +49,18 @@ run_test_command () { } # Lint the codebase. -run_test_command "eslint" "eslint ." - -# Run the closure compiler. -run_test_command "compile" "npm run build" +# Skip for CI environments, because linting is run separately. +if [ -z $CI ]; then + run_test_command "eslint" "eslint ." +fi -# Run the closure compiler ensuring there are no compiler warnings / errors. -run_test_command "compile:warnings" "npm run build:debug" +# Run the full usual build process, checking to ensure there are no +# closure compiler warnings / errors. +run_test_command "build-debug" "npm run build-debug" # Generate TypeScript typings and ensure there are no errors. -run_test_command "typings" "tests/scripts/compile_typings.sh" +# TODO(5621): Re-enable this test once typings generation is fixed. +# run_test_command "typings" "npm run typings" # Check the sizes of built files for unexpected growth. run_test_command "metadata" "tests/scripts/check_metadata.sh" @@ -69,11 +71,14 @@ run_test_command "mocha" "node tests/mocha/run_mocha_tests_in_browser.js" # Run generator tests inside a browser and check the results. run_test_command "generators" "tests/scripts/run_generators.sh" +# Run the package build process, as Node tests depend on it. +run_test_command "package" "npm run package" + # Run Node tests. run_test_command "node" "./node_modules/.bin/mocha tests/node --config tests/node/.mocharc.js" -# # Attempt advanced compilation of a Blockly app. -# run_test_command "advanced_compile" "npm run test:compile:advanced" +# Attempt advanced compilation of a Blockly app. +run_test_command "advanced_compile" "npm run test:compile:advanced" # End of tests. diff --git a/tests/scripts/check_metadata.sh b/tests/scripts/check_metadata.sh index 8dbae6ccc39..02195205c8d 100755 --- a/tests/scripts/check_metadata.sh +++ b/tests/scripts/check_metadata.sh @@ -1,9 +1,19 @@ #!/bin/bash # Checks the size of generated files and verifies they aren't growing -# unreasonably. +# unreasonably. Assumes the compressed files have already been built. -# These values should be updated with each release +# The ..._EXPECTED values should be updated with each release. +# Run this script to get the new values. + +# Location of the pre-built compressed files. +# +# (TODO(#5007): Should fetch this from scripts/gulpfiles/config.js +# instead of hardcoding it here. +readonly BUILD_DIR='build' + +# These values should be updated with each release. (Note that the +# historic values are tab-delimited.) # Size of blockly_compressed.js # Q2 2019 2.20190722.0 812688 @@ -12,9 +22,13 @@ # Q1 2020 3.20200402.0 619341 # Q2 2020 3.20200625.0 621811 # Q3 2020 3.20200924.0 641216 -# Q4 2020 4.20201217.0 653624 -# Q1 2021 5.20210325.0 653957 -blockly_size_expected=653957 +# Q4 2020 4.20201217.0 653624 +# Q1 2021 5.20210325.0 653957 +# Q2 2021 6.20210701.0 664497 +# Q3 2021 6.20210701.0 731695 (mid-quarter goog.module conversion) +# Q3 2021 6.20210701.0 808807 (late-quarter goog.module conversion) +# Q4 2021 7.20211209.0-beta.0 920002 +readonly BLOCKLY_SIZE_EXPECTED=920002 # Size of blocks_compressed.js # Q2 2019 2.20190722.0 75618 @@ -23,9 +37,12 @@ blockly_size_expected=653957 # Q1 2020 3.20200402.0 75805 # Q2 2020 3.20200625.0 76360 # Q3 2020 3.20200924.0 76429 -# Q4 2020 4.20201217.0 76693 -# Q1 2021 5.20210325.0 76693 -blocks_size_expected=76693 +# Q4 2020 4.20201217.0 76693 +# Q1 2021 5.20210325.0 76693 +# Q2 2021 6.20210701.0 76669 +# Q3 2021 6.20210701.0 76669 +# Q4 2021 7.20211209.0-beta.0 82054 +readonly BLOCKS_SIZE_EXPECTED=82054 # Size of blockly_compressed.js.gz # Q2 2019 2.20190722.0 180925 @@ -34,9 +51,13 @@ blocks_size_expected=76693 # Q1 2020 3.20200402.0 134133 # Q2 2020 3.20200625.0 135181 # Q3 2020 3.20200924.0 138003 -# Q4 2020 4.20201217.0 138115 -# Q1 2021 5.20210325.0 136118 -blockly_gz_size_expected=136118 +# Q4 2020 4.20201217.0 138115 +# Q1 2021 5.20210325.0 136118 +# Q2 2021 6.20210701.0 142112 +# Q3 2021 6.20210701.0 147476 (mid-quarter goog.module conversion) +# Q3 2021 6.20210701.0 152025 (late-quarter goog.module conversion) +# Q4 2021 7.20211209.0-beta.0 169863 +readonly BLOCKLY_GZ_SIZE_EXPECTED=169863 # Size of blocks_compressed.js.gz # Q2 2019 2.20190722.0 14552 @@ -45,49 +66,57 @@ blockly_gz_size_expected=136118 # Q1 2020 3.20200402.0 14966 # Q2 2020 3.20200625.0 15195 # Q3 2020 3.20200924.0 15231 -# Q4 2020 4.20201217.0 15224 -# Q1 2021 5.20210325.0 15285 -blocks_gz_size_expected=15285 +# Q4 2020 4.20201217.0 15224 +# Q1 2021 5.20210325.0 15285 +# Q2 2021 6.20210701.0 15275 +# Q3 2021 6.20210701.0 15284 +# Q4 2021 7.20211209.0-beta.0 16616 +readonly BLOCKS_GZ_SIZE_EXPECTED=16616 # ANSI colors -BOLD_GREEN='\033[1;32m' -BOLD_RED='\033[1;31m' -ANSI_RESET='\033[0m' - -# Build the compressed files for core and blocks -echo "Building files" -npm install -gulp buildCompressed -gulp buildBlocks +readonly BOLD_GREEN='\033[1;32m' +readonly BOLD_RED='\033[1;31m' +readonly ANSI_RESET='\033[0m' + +# Terminate immediately with non-zero status if any command exits +# with non-zero status, printing a nice message. +set -e +function fail { + echo -e "${BOLD_RED}Error while checking metadata.${ANSI_RESET}" >&2 +} +trap fail ERR -# GZip them for additional size comparisons +# GZip them for additional size comparisons (keep originals, force +# overwite previously-gzipped copies). echo "Zipping the compressed files" -gzip -c blockly_compressed.js > blockly_compressed.js.gz -gzip -c blocks_compressed.js > blocks_compressed.js.gz +gzip -kf "${BUILD_DIR}/blockly_compressed.js" +gzip -kf "${BUILD_DIR}/blocks_compressed.js" # Check the sizes of the files has_failed=0 compare_size() { - local name=$1 - local expected=$2 - local compare=$(echo "$expected * 1.1 / 1" | bc) - - local size=$(wc -c <"$name") - - if (( $size > $compare)) - then - echo -e "${BOLD_RED}Failed: Size of $name has grown more than 10%. $size vs $expected ${ANSI_RESET}" >&2 - has_failed=1 - else - echo -e "${BOLD_GREEN}Size of $name at $size compared to previous $expected.${ANSI_RESET}" - fi + local name="$1" + local expected="$2" + local compare=$(echo "${expected} * 1.1 / 1" | bc) + + local size=$(wc -c <"${name}") + + if (( $size > $compare)) + then + echo -ne "${BOLD_RED}Failed: Size of ${name} has grown more than 10%. " >&2 + echo -e "${size} vs ${expected} ${ANSI_RESET}" >&2 + has_failed=1 + else + echo -ne "${BOLD_GREEN}Size of ${name} at ${size} compared to previous " >&2 + echo -e "${expected}.${ANSI_RESET}" + fi } -compare_size "blockly_compressed.js" $blockly_size_expected -compare_size "blocks_compressed.js" $blocks_size_expected -compare_size "blockly_compressed.js.gz" $blockly_gz_size_expected -compare_size "blocks_compressed.js.gz" $blocks_gz_size_expected +compare_size "${BUILD_DIR}/blockly_compressed.js" $BLOCKLY_SIZE_EXPECTED +compare_size "${BUILD_DIR}/blocks_compressed.js" $BLOCKS_SIZE_EXPECTED +compare_size "${BUILD_DIR}/blockly_compressed.js.gz" $BLOCKLY_GZ_SIZE_EXPECTED +compare_size "${BUILD_DIR}/blocks_compressed.js.gz" $BLOCKS_GZ_SIZE_EXPECTED exit $has_failed diff --git a/tests/scripts/compile_typings.sh b/tests/scripts/compile_typings.sh index ee16beb5629..35e9d56dc53 100755 --- a/tests/scripts/compile_typings.sh +++ b/tests/scripts/compile_typings.sh @@ -1,13 +1,23 @@ #!/bin/bash +# Location that npm run typings will write .d.ts files to. +# +# (TODO(#5007): Should fetch this from scripts/gulpfiles/config.js +# instead of hardcoding it here. +readonly BUILD_DIR='build' + # ANSI colors BOLD_GREEN='\033[1;32m' BOLD_RED='\033[1;31m' ANSI_RESET='\033[0m' -# Download TypeScript to obtain the compiler. -echo "Downloading TypeScript" -npm install typescript +# Terminate immediately with non-zero status if any command exits +# with non-zero status, printing a nice message. +set -e +function fail { + echo -e "${BOLD_RED}Failed to compile TypeScript typings.${ANSI_RESET}" >&2 +} +trap fail ERR # Generate Blockly typings. echo "Generating Blockly typings" @@ -15,15 +25,8 @@ npm run typings # Use the TypeScript compiler to compile the generated typings. echo "Compiling typings" -cd typings -../node_modules/.bin/tsc blockly.d.ts +cd "${BUILD_DIR}" +../node_modules/.bin/tsc blockly.d.ts -if [ $? -eq 0 ] -then - echo -e "${BOLD_GREEN}TypeScript typings compiled successfully.${ANSI_RESET}" - exit 0 -else - echo -e "${BOLD_RED}Failed to compile TypeScript typings.${ANSI_RESET}" >&2 - exit 1 -fi +echo -e "${BOLD_GREEN}TypeScript typings compiled successfully.${ANSI_RESET}" diff --git a/tests/scripts/run_generators.sh b/tests/scripts/run_generators.sh index 9d556acad82..8c461f8e085 100755 --- a/tests/scripts/run_generators.sh +++ b/tests/scripts/run_generators.sh @@ -18,7 +18,7 @@ check_result() { if [ -f $tmp_filename ]; then local golden_filename="${GOLDEN_DIR}generated.$suffix" if [ -f $golden_filename ]; then - if cmp --silent $tmp_filename $golden_filename; then + if cmp $tmp_filename $golden_filename; then echo -e "$SUCCESS_PREFIX $suffix: $tmp_filename matches $golden_filename" else echo -e "$FAILURE_PREFIX $suffix: $tmp_filename does not match $golden_filename" diff --git a/tests/scripts/update_metadata.sh b/tests/scripts/update_metadata.sh new file mode 100755 index 00000000000..c6afc8ad00d --- /dev/null +++ b/tests/scripts/update_metadata.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# Determines the size of generated files and updates check_metadata.sh to +# reflect the new values. + +gzip -k build/blockly_compressed.js +gzip -k build/blocks_compressed.js + +blockly_size=$(wc -c < "build/blockly_compressed.js") +blocks_size=$(wc -c < "build/blocks_compressed.js") +blockly_gz_size=$(wc -c < "build/blockly_compressed.js.gz") +blocks_gz_size=$(wc -c < "build/blocks_compressed.js.gz") +quarters=(1 1 1 2 2 2 3 3 3 4 4 4) +month=$(date +%-m) +quarter=$(echo Q${quarters[$month - 1]} $(date +%Y)) +version=$(npx -c 'echo "$npm_package_version"') + +replacement="# ${quarter}\t${version}\t${blockly_size}\n" +replacement+="readonly BLOCKLY_SIZE_EXPECTED=${blockly_size}" +sed -ri.bak "s/readonly BLOCKLY_SIZE_EXPECTED=[0-9]+/${replacement}/g" \ + tests/scripts/check_metadata.sh + +replacement="# ${quarter}\t${version}\t${blocks_size}\n" +replacement+="readonly BLOCKS_SIZE_EXPECTED=${blocks_size}" +sed -ri.bak "s/readonly BLOCKS_SIZE_EXPECTED=[0-9]+/${replacement}/g" \ + tests/scripts/check_metadata.sh + +replacement="# ${quarter}\t${version}\t${blockly_gz_size}\n" +replacement+="readonly BLOCKLY_GZ_SIZE_EXPECTED=${blockly_gz_size}" +sed -ri.bak "s/readonly BLOCKLY_GZ_SIZE_EXPECTED=[0-9]+/${replacement}/g" \ + tests/scripts/check_metadata.sh + +replacement="# ${quarter}\t${version}\t${blocks_gz_size}\n" +replacement+="readonly BLOCKS_GZ_SIZE_EXPECTED=${blocks_gz_size}" +sed -ri.bak "s/readonly BLOCKS_GZ_SIZE_EXPECTED=[0-9]+/${replacement}/g" \ + tests/scripts/check_metadata.sh + +rm tests/scripts/check_metadata.sh.bak \ No newline at end of file diff --git a/typings/blockly.d.ts b/typings/blockly.d.ts index e26d24f7137..291f472fdfc 100644 --- a/typings/blockly.d.ts +++ b/typings/blockly.d.ts @@ -1,23281 +1,25596 @@ -/** - * @license - * Copyright 2019 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @fileoverview Type definitions for Blockly. - * @author samelh@google.com (Sam El-Husseini) - */ - -export = Blockly; - -declare module Blockly { - - interface BlocklyOptions { - toolbox?: Blockly.utils.toolbox.ToolboxDefinition; - readOnly?: boolean; - trashcan?: boolean; - maxInstances?: {[type: string]: number;}; - maxTrashcanContents?: number; - collapse?: boolean; - comments?: boolean; - disable?: boolean; - sounds?: boolean; - rtl?: boolean; - horizontalLayout?: boolean; - toolboxPosition?: string; - css?: boolean; - oneBasedIndex?: boolean; - media?: string; - theme?: Blockly.Theme | BlocklyThemeOptions; - move?: { - scrollbars?: boolean; - drag?: boolean; - wheel?: boolean; - }; - grid?: { - spacing?: number; - colour?: string; - length?: number; - snap?: boolean; - }; - zoom?: { - controls?: boolean; - wheel?: boolean; - startScale?: number; - maxScale?: number; - minScale?: number; - scaleSpeed?: number; - pinch?: boolean; +declare module "utils/idgenerator" { + namespace internal { + /** + * Generate a random unique ID. This should be globally unique. + * 87 characters ^ 20 length > 128 bits (better than a UUID). + * @return {string} A globally unique ID string. + */ + function genUid(): string; + } + /** + * Generate the next unique element IDs. + * IDs are compatible with the HTML4 id attribute restrictions: + * Use only ASCII letters, digits, '_', '-' and '.' + * + * For UUIDs use genUid (below) instead; this ID generator should + * primarily be used for IDs that end up in the DOM. + * + * @return {string} The next unique identifier. + * @alias Blockly.utils.idGenerator.getNextUniqueId + */ + export function getNextUniqueId(): string; + /** + * Generate a random unique ID. + * @see internal.genUid + * @return {string} A globally unique ID string. + * @alias Blockly.utils.idGenerator.genUid + */ + export function genUid(): string; + export { internal as TEST_ONLY }; +} +declare module "utils/object" { + /** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + /** + * @fileoverview Utility methods for objects. + */ + /** + * Utility methods for objects. + * @namespace Blockly.utils.object + */ + /** + * Inherit the prototype methods from one constructor into another. + * @param {!Function} childCtor Child class. + * @param {!Function} parentCtor Parent class. + * @suppress {strictMissingProperties} superClass_ is not defined on Function. + * @alias Blockly.utils.object.inherits + */ + export function inherits(childCtor: Function, parentCtor: Function): void; + /** + * Copies all the members of a source object to a target object. + * @param {!Object} target Target. + * @param {!Object} source Source. + * @alias Blockly.utils.object.mixin + */ + export function mixin(target: any, source: any): void; + /** + * Complete a deep merge of all members of a source object with a target object. + * @param {!Object} target Target. + * @param {!Object} source Source. + * @return {!Object} The resulting object. + * @alias Blockly.utils.object.deepMerge + */ + export function deepMerge(target: any, source: any): any; + /** + * Returns an array of a given object's own enumerable property values. + * @param {!Object} obj Object containing values. + * @return {!Array} Array of values. + * @alias Blockly.utils.object.values + */ + export function values(obj: any): any[]; +} +declare module "connection_type" { + /** + * * + */ + export type ConnectionType = number; + export namespace ConnectionType { + const INPUT_VALUE: number; + const OUTPUT_VALUE: number; + const NEXT_STATEMENT: number; + const PREVIOUS_STATEMENT: number; + } +} +declare module "utils/global" { + /** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + /** + * @fileoverview Provides a reference to the global object. + */ + /** + * Provides a reference to the global object. + * @namespace Blockly.utils.global + */ + /** + * Reference to the global object. + * + * More info on this implementation here: + * https://docs.google.com/document/d/1NAeW4Wk7I7FV0Y2tcUFvQdGMc89k2vdgSXInw8_nvCI + */ + export const globalThis: any; +} +declare module "utils/useragent" { + /** + * The raw useragent string. + * @type {string} + */ + let rawUserAgent: string; + /** @type {boolean} */ + let isIe: boolean; + /** @type {boolean} */ + let isEdge: boolean; + /** @type {boolean} */ + let isJavaFx: boolean; + /** @type {boolean} */ + let isChrome: boolean; + /** @type {boolean} */ + let isWebKit: boolean; + /** @type {boolean} */ + let isGecko: boolean; + /** @type {boolean} */ + let isAndroid: boolean; + /** @type {boolean} */ + let isIPad: boolean; + /** @type {boolean} */ + let isIPod: boolean; + /** @type {boolean} */ + let isIPhone: boolean; + /** @type {boolean} */ + let isMac: boolean; + /** @type {boolean} */ + let isTablet: boolean; + /** @type {boolean} */ + let isMobile: boolean; + export { rawUserAgent as raw, isIe as IE, isEdge as EDGE, isJavaFx as JavaFx, isChrome as CHROME, isWebKit as WEBKIT, isGecko as GECKO, isAndroid as ANDROID, isIPad as IPAD, isIPod as IPOD, isIPhone as IPHONE, isMac as MAC, isTablet as TABLET, isMobile as MOBILE }; +} +declare module "utils/svg" { + /** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + /** + * @fileoverview Defines the Svg class. Its constants enumerate + * all SVG tag names used by Blockly. + */ + /** + * Defines the Svg class. Its constants enumerate + * all SVG tag names used by Blockly. + * @class + */ + /** + * A name with the type of the SVG element stored in the generic. + * @param {string} tagName The SVG element tag name. + * @constructor + * @template T + * @private + * @alias Blockly.utils.Svg + */ + export class Svg { + constructor(tagName: any); + /** + * @type {string} + * @private + */ + private tagName_; + /** + * Returns the SVG element tag name. + * @return {string} The name. + * @override + */ + toString(): string; + } + export namespace Svg { + const ANIMATE: Svg; + const CIRCLE: Svg; + const CLIPPATH: Svg; + const DEFS: Svg; + const FECOMPOSITE: Svg; + const FECOMPONENTTRANSFER: Svg; + const FEFLOOD: Svg; + const FEFUNCA: Svg; + const FEGAUSSIANBLUR: Svg; + const FEPOINTLIGHT: Svg; + const FESPECULARLIGHTING: Svg; + const FILTER: Svg; + const FOREIGNOBJECT: Svg; + const G: Svg; + const IMAGE: Svg; + const LINE: Svg; + const PATH: Svg; + const PATTERN: Svg; + const POLYGON: Svg; + const RECT: Svg; + const SVG: Svg; + const TEXT: Svg; + const TSPAN: Svg; + } +} +declare module "utils/dom" { + /** + * Required name space for SVG elements. + * @const + * @alias Blockly.utils.dom.SVG_NS + */ + export const SVG_NS: "http://www.w3.org/2000/svg"; + /** + * Required name space for HTML elements. + * @const + * @alias Blockly.utils.dom.HTML_NS + */ + export const HTML_NS: "http://www.w3.org/1999/xhtml"; + /** + * Required name space for XLINK elements. + * @const + * @alias Blockly.utils.dom.XLINK_NS + */ + export const XLINK_NS: "http://www.w3.org/1999/xlink"; + /** + * * + */ + export type NodeType = number; + export namespace NodeType { + const ELEMENT_NODE: number; + const TEXT_NODE: number; + const COMMENT_NODE: number; + const DOCUMENT_POSITION_CONTAINED_BY: number; + } + /** + * Helper method for creating SVG elements. + * @param {string|Svg} name Element's tag name. + * @param {!Object} attrs Dictionary of attribute names and values. + * @param {Element=} opt_parent Optional parent on which to append the element. + * @return {T} Newly created SVG element. The return type is {!SVGElement} if + * name is a string or a more specific type if it a member of Svg. + * @template T + * @alias Blockly.utils.dom.createSvgElement + */ + export function createSvgElement(name: string | Svg, attrs: any, opt_parent?: Element | undefined): T; + /** + * Add a CSS class to a element. + * Similar to Closure's goog.dom.classes.add, except it handles SVG elements. + * @param {!Element} element DOM element to add class to. + * @param {string} className Name of class to add. + * @return {boolean} True if class was added, false if already present. + * @alias Blockly.utils.dom.addClass + */ + export function addClass(element: Element, className: string): boolean; + /** + * Removes multiple calsses from an element. + * @param {!Element} element DOM element to remove classes from. + * @param {string} classNames A string of one or multiple class names for an + * element. + * @alias Blockly.utils.dom.removeClasses + */ + export function removeClasses(element: Element, classNames: string): void; + /** + * Remove a CSS class from a element. + * Similar to Closure's goog.dom.classes.remove, except it handles SVG elements. + * @param {!Element} element DOM element to remove class from. + * @param {string} className Name of class to remove. + * @return {boolean} True if class was removed, false if never present. + * @alias Blockly.utils.dom.removeClass + */ + export function removeClass(element: Element, className: string): boolean; + /** + * Checks if an element has the specified CSS class. + * Similar to Closure's goog.dom.classes.has, except it handles SVG elements. + * @param {!Element} element DOM element to check. + * @param {string} className Name of class to check. + * @return {boolean} True if class exists, false otherwise. + * @alias Blockly.utils.dom.hasClass + */ + export function hasClass(element: Element, className: string): boolean; + /** + * Removes a node from its parent. No-op if not attached to a parent. + * @param {?Node} node The node to remove. + * @return {?Node} The node removed if removed; else, null. + * @alias Blockly.utils.dom.removeNode + */ + export function removeNode(node: Node | null): Node | null; + /** + * Insert a node after a reference node. + * Contrast with node.insertBefore function. + * @param {!Element} newNode New element to insert. + * @param {!Element} refNode Existing element to precede new node. + * @alias Blockly.utils.dom.insertAfter + */ + export function insertAfter(newNode: Element, refNode: Element): void; + /** + * Whether a node contains another node. + * @param {!Node} parent The node that should contain the other node. + * @param {!Node} descendant The node to test presence of. + * @return {boolean} Whether the parent node contains the descendant node. + * @alias Blockly.utils.dom.containsNode + */ + export function containsNode(parent: Node, descendant: Node): boolean; + /** + * Sets the CSS transform property on an element. This function sets the + * non-vendor-prefixed and vendor-prefixed versions for backwards compatibility + * with older browsers. See https://caniuse.com/#feat=transforms2d + * @param {!Element} element Element to which the CSS transform will be applied. + * @param {string} transform The value of the CSS `transform` property. + * @alias Blockly.utils.dom.setCssTransform + */ + export function setCssTransform(element: Element, transform: string): void; + /** + * Start caching text widths. Every call to this function MUST also call + * stopTextWidthCache. Caches must not survive between execution threads. + * @alias Blockly.utils.dom.startTextWidthCache + */ + export function startTextWidthCache(): void; + /** + * Stop caching field widths. Unless caching was already on when the + * corresponding call to startTextWidthCache was made. + * @alias Blockly.utils.dom.stopTextWidthCache + */ + export function stopTextWidthCache(): void; + /** + * Gets the width of a text element, caching it in the process. + * @param {!Element} textElement An SVG 'text' element. + * @return {number} Width of element. + * @alias Blockly.utils.dom.getTextWidth + */ + export function getTextWidth(textElement: Element): number; + /** + * Gets the width of a text element using a faster method than `getTextWidth`. + * This method requires that we know the text element's font family and size in + * advance. Similar to `getTextWidth`, we cache the width we compute. + * @param {!Element} textElement An SVG 'text' element. + * @param {number} fontSize The font size to use. + * @param {string} fontWeight The font weight to use. + * @param {string} fontFamily The font family to use. + * @return {number} Width of element. + * @alias Blockly.utils.dom.getFastTextWidth + */ + export function getFastTextWidth(textElement: Element, fontSize: number, fontWeight: string, fontFamily: string): number; + /** + * Gets the width of a text element using a faster method than `getTextWidth`. + * This method requires that we know the text element's font family and size in + * advance. Similar to `getTextWidth`, we cache the width we compute. + * This method is similar to ``getFastTextWidth`` but expects the font size + * parameter to be a string. + * @param {!Element} textElement An SVG 'text' element. + * @param {string} fontSize The font size to use. + * @param {string} fontWeight The font weight to use. + * @param {string} fontFamily The font family to use. + * @return {number} Width of element. + * @alias Blockly.utils.dom.getFastTextWidthWithSizeString + */ + export function getFastTextWidthWithSizeString(textElement: Element, fontSize: string, fontWeight: string, fontFamily: string): number; + /** + * Measure a font's metrics. The height and baseline values. + * @param {string} text Text to measure the font dimensions of. + * @param {string} fontSize The font size to use. + * @param {string} fontWeight The font weight to use. + * @param {string} fontFamily The font family to use. + * @return {{height: number, baseline: number}} Font measurements. + * @alias Blockly.utils.dom.measureFontMetrics + */ + export function measureFontMetrics(text: string, fontSize: string, fontWeight: string, fontFamily: string): { + height: number; + baseline: number; }; - renderer?: string; - parentWorkspace?: Blockly.WorkspaceSvg; - } - - interface BlocklyThemeOptions { - base?: string; - blockStyles?: {[blocks: string]: Blockly.Theme.BlockStyle;}; - categoryStyles?: {[category: string]: Blockly.Theme.CategoryStyle;}; - componentStyles?: {[component: string]: any;}; - fontStyle?: Blockly.Theme.FontStyle; - startHats?: boolean; - } - - /** - * Set the Blockly locale. - * Note: this method is only available in the npm release of Blockly. - * @param {!Object} msg An object of Blockly message strings in the desired - * language. - */ - function setLocale(msg: {[key: string]: string;}): void; + import { Svg } from "utils/svg"; } - -declare module Blockly.utils { - interface Metrics { - viewHeight: number; - viewWidth: number; - contentHeight: number; - contentWidth: number; - viewTop: number; - viewLeft: number; - contentTop: number; - contentLeft: number; - absoluteTop: number; - absoluteLeft: number; - svgHeight?: number; - svgWidth?: number; - toolboxWidth?: number; - toolboxHeight?: number; - flyoutWidth?: number; - flyoutHeight?: number; - toolboxPosition?: number; - } +declare module "utils/xml" { + /** + * Namespace for Blockly's XML. + * @alias Blockly.utils.xml.NAME_SPACE + */ + export const NAME_SPACE: "https://developers.google.com/blockly/xml"; + /** + * Get the document object to use for XML serialization. + * @return {!Document} The document object. + * @alias Blockly.utils.xml.getDocument + */ + export function getDocument(): Document; + /** + * Get the document object to use for XML serialization. + * @param {!Document} document The document object to use. + * @alias Blockly.utils.xml.setDocument + */ + export function setDocument(document: Document): void; + /** + * Create DOM element for XML. + * @param {string} tagName Name of DOM element. + * @return {!Element} New DOM element. + * @alias Blockly.utils.xml.createElement + */ + export function createElement(tagName: string): Element; + /** + * Create text element for XML. + * @param {string} text Text content. + * @return {!Text} New DOM text node. + * @alias Blockly.utils.xml.createTextNode + */ + export function createTextNode(text: string): Text; + /** + * Converts an XML string into a DOM tree. + * @param {string} text XML string. + * @return {Document} The DOM document. + * @throws if XML doesn't parse. + * @alias Blockly.utils.xml.textToDomDocument + */ + export function textToDomDocument(text: string): Document; + /** + * Converts a DOM structure into plain text. + * Currently the text format is fairly ugly: all one line with no whitespace. + * @param {!Node} dom A tree of XML nodes. + * @return {string} Text representation. + * @alias Blockly.utils.xml.domToText + */ + export function domToText(dom: Node): string; } +declare module "utils/string" { + /** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + /** + * @fileoverview Utility methods for string manipulation. + * These methods are not specific to Blockly, and could be factored out into + * a JavaScript framework such as Closure. + */ + /** + * Utility methods for string manipulation. + * These methods are not specific to Blockly, and could be factored out into + * a JavaScript framework such as Closure. + * @namespace Blockly.utils.string + */ + /** + * Fast prefix-checker. + * Copied from Closure's goog.string.startsWith. + * @param {string} str The string to check. + * @param {string} prefix A string to look for at the start of `str`. + * @return {boolean} True if `str` begins with `prefix`. + * @alias Blockly.utils.string.startsWith + */ + export function startsWith(str: string, prefix: string): boolean; + /** + * Given an array of strings, return the length of the shortest one. + * @param {!Array} array Array of strings. + * @return {number} Length of shortest string. + * @alias Blockly.utils.string.shortestStringLength + */ + export function shortestStringLength(array: Array): number; + /** + * Given an array of strings, return the length of the common prefix. + * Words may not be split. Any space after a word is included in the length. + * @param {!Array} array Array of strings. + * @param {number=} opt_shortest Length of shortest string. + * @return {number} Length of common prefix. + * @alias Blockly.utils.string.commonWordPrefix + */ + export function commonWordPrefix(array: Array, opt_shortest?: number | undefined): number; + /** + * Given an array of strings, return the length of the common suffix. + * Words may not be split. Any space after a word is included in the length. + * @param {!Array} array Array of strings. + * @param {number=} opt_shortest Length of shortest string. + * @return {number} Length of common suffix. + * @alias Blockly.utils.string.commonWordSuffix + */ + export function commonWordSuffix(array: Array, opt_shortest?: number | undefined): number; + /** + * Wrap text to the specified width. + * @param {string} text Text to wrap. + * @param {number} limit Width to wrap each line. + * @return {string} Wrapped text. + * @alias Blockly.utils.string.wrap + */ + export function wrap(text: string, limit: number): string; + /** + * Is the given string a number (includes negative and decimals). + * @param {string} str Input string. + * @return {boolean} True if number, false otherwise. + * @alias Blockly.utils.string.isNumber + */ + export function isNumber(str: string): boolean; +} +declare module "internal_constants" { + /** + * The multiplier for scroll wheel deltas using the line delta mode. + * @type {number} + * @alias Blockly.internalConstants.LINE_MODE_MULTIPLIER + */ + export const LINE_MODE_MULTIPLIER: number; + /** + * The multiplier for scroll wheel deltas using the page delta mode. + * @type {number} + * @alias Blockly.internalConstants.PAGE_MODE_MULTIPLIER + */ + export const PAGE_MODE_MULTIPLIER: number; + /** + * Number of pixels the mouse must move before a drag starts. + * @alias Blockly.internalConstants.DRAG_RADIUS + */ + export const DRAG_RADIUS: 5; + /** + * Number of pixels the mouse must move before a drag/scroll starts from the + * flyout. Because the drag-intention is determined when this is reached, it is + * larger than DRAG_RADIUS so that the drag-direction is clearer. + * @alias Blockly.internalConstants.FLYOUT_DRAG_RADIUS + */ + export const FLYOUT_DRAG_RADIUS: 10; + /** + * Maximum misalignment between connections for them to snap together. + * @alias Blockly.internalConstants.SNAP_RADIUS + */ + export const SNAP_RADIUS: 28; + /** + * Maximum misalignment between connections for them to snap together, + * when a connection is already highlighted. + * @alias Blockly.internalConstants.CONNECTING_SNAP_RADIUS + */ + export const CONNECTING_SNAP_RADIUS: 28; + /** + * How much to prefer staying connected to the current connection over moving to + * a new connection. The current previewed connection is considered to be this + * much closer to the matching connection on the block than it actually is. + * @alias Blockly.internalConstants.CURRENT_CONNECTION_PREFERENCE + */ + export const CURRENT_CONNECTION_PREFERENCE: 8; + /** + * Delay in ms between trigger and bumping unconnected block out of alignment. + * @alias Blockly.internalConstants.BUMP_DELAY + */ + export const BUMP_DELAY: 250; + /** + * Maximum randomness in workspace units for bumping a block. + * @alias Blockly.internalConstants.BUMP_RANDOMNESS + */ + export const BUMP_RANDOMNESS: 10; + /** + * Number of characters to truncate a collapsed block to. + * @alias Blockly.internalConstants.COLLAPSE_CHARS + */ + export const COLLAPSE_CHARS: 30; + /** + * Length in ms for a touch to become a long press. + * @alias Blockly.internalConstants.LONGPRESS + */ + export const LONGPRESS: 750; + /** + * Prevent a sound from playing if another sound preceded it within this many + * milliseconds. + * @alias Blockly.internalConstants.SOUND_LIMIT + */ + export const SOUND_LIMIT: 100; + /** + * When dragging a block out of a stack, split the stack in two (true), or drag + * out the block healing the stack (false). + * @alias Blockly.internalConstants.DRAG_STACK + */ + export const DRAG_STACK: true; + /** + * The richness of block colours, regardless of the hue. + * Must be in the range of 0 (inclusive) to 1 (exclusive). + * @alias Blockly.internalConstants.HSV_SATURATION + */ + export const HSV_SATURATION: 0.45; + /** + * The intensity of block colours, regardless of the hue. + * Must be in the range of 0 (inclusive) to 1 (exclusive). + * @alias Blockly.internalConstants.HSV_VALUE + */ + export const HSV_VALUE: 0.65; + export namespace SPRITE { + const width: number; + const height: number; + const url: string; + } + /** + * ENUM for no drag operation. + * @const + * @alias Blockly.internalConstants.DRAG_NONE + */ + export const DRAG_NONE: 0; + /** + * ENUM for inside the sticky DRAG_RADIUS. + * @const + * @alias Blockly.internalConstants.DRAG_STICKY + */ + export const DRAG_STICKY: 1; + /** + * ENUM for inside the non-sticky DRAG_RADIUS, for differentiating between + * clicks and drags. + * @const + * @alias Blockly.internalConstants.DRAG_BEGIN + */ + export const DRAG_BEGIN: 1; + /** + * ENUM for freely draggable (outside the DRAG_RADIUS, if one applies). + * @const + * @alias Blockly.internalConstants.DRAG_FREE + */ + export const DRAG_FREE: 2; + /** + * Lookup table for determining the opposite type of a connection. + * @const + * @alias Blockly.internalConstants.OPPOSITE_TYPE + */ + export const OPPOSITE_TYPE: any[]; + /** + * String for use in the "custom" attribute of a category in toolbox XML. + * This string indicates that the category should be dynamically populated with + * variable blocks. + * @const {string} + * @alias Blockly.internalConstants.VARIABLE_CATEGORY_NAME + */ + export const VARIABLE_CATEGORY_NAME: "VARIABLE"; + /** + * String for use in the "custom" attribute of a category in toolbox XML. + * This string indicates that the category should be dynamically populated with + * variable blocks. + * @const {string} + * @alias Blockly.internalConstants.VARIABLE_DYNAMIC_CATEGORY_NAME + */ + export const VARIABLE_DYNAMIC_CATEGORY_NAME: "VARIABLE_DYNAMIC"; + /** + * String for use in the "custom" attribute of a category in toolbox XML. + * This string indicates that the category should be dynamically populated with + * procedure blocks. + * @const {string} + * @alias Blockly.internalConstants.PROCEDURE_CATEGORY_NAME + */ + export const PROCEDURE_CATEGORY_NAME: "PROCEDURE"; + /** + * String for use in the dropdown created in field_variable. + * This string indicates that this option in the dropdown is 'Rename + * variable...' and if selected, should trigger the prompt to rename a variable. + * @const {string} + * @alias Blockly.internalConstants.RENAME_VARIABLE_ID + */ + export const RENAME_VARIABLE_ID: "RENAME_VARIABLE_ID"; + /** + * String for use in the dropdown created in field_variable. + * This string indicates that this option in the dropdown is 'Delete the "%1" + * variable' and if selected, should trigger the prompt to delete a variable. + * @const {string} + * @alias Blockly.internalConstants.DELETE_VARIABLE_ID + */ + export const DELETE_VARIABLE_ID: "DELETE_VARIABLE_ID"; +} +declare module "msg" { + export {}; +} +declare module "blocks" { + /** + * @license + * Copyright 2013 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + /** + * @fileoverview A mapping of block type names to block prototype objects. + */ + /** + * A mapping of block type names to block prototype objects. + * @namespace Blockly.blocks + */ + /** + * A mapping of block type names to block prototype objects. + * @type {!Object} + * @alias Blockly.blocks.Blocks + */ + export const Blocks: { + [x: string]: any; + }; +} +declare module "interfaces/i_deletable" { + /** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + /** + * @fileoverview The interface for an object that is deletable. + */ + /** + * The interface for an object that is deletable. + * @namespace Blockly.IDeletable + */ + /** + * The interface for an object that can be deleted. + * @interface + * @alias Blockly.IDeletable + */ + export class IDeletable { + } +} +declare module "interfaces/i_movable" { + /** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + /** + * @fileoverview The interface for an object that is movable. + */ + /** + * The interface for an object that is movable. + * @namespace Blockly.IMovable + */ + /** + * The interface for an object that is movable. + * @interface + * @alias Blockly.IMovable + */ + export class IMovable { + } +} +declare module "interfaces/i_selectable" { + /** + * The interface for an object that is selectable. + * @extends {IDeletable} + * @extends {IMovable} + * @interface + * @alias Blockly.ISelectable + */ + export class ISelectable { + /** + * @type {string} + */ + id: string; + } +} +declare module "dialog" { + /** + * Wrapper to window.alert() that app developers may override via setAlert to + * provide alternatives to the modal browser window. + * @param {string} message The message to display to the user. + * @param {function()=} opt_callback The callback when the alert is dismissed. + * @alias Blockly.dialog.alert + */ + export function alert(message: string, opt_callback?: (() => any) | undefined): void; + /** + * Sets the function to be run when Blockly.dialog.alert() is called. + * @param {!function(string, function()=)} alertFunction The function to be run. + * @see Blockly.dialog.alert + * @alias Blockly.dialog.setAlert + */ + export function setAlert(alertFunction: (arg0: string, arg1: (() => any) | undefined) => any): void; + /** + * Wrapper to window.confirm() that app developers may override via setConfirm + * to provide alternatives to the modal browser window. + * @param {string} message The message to display to the user. + * @param {!function(boolean)} callback The callback for handling user response. + * @alias Blockly.dialog.confirm + */ + export function confirm(message: string, callback: (arg0: boolean) => any): void; + /** + * Sets the function to be run when Blockly.dialog.confirm() is called. + * @param {!function(string, !function(boolean))} confirmFunction The function + * to be run. + * @see Blockly.dialog.confirm + * @alias Blockly.dialog.setConfirm + */ + export function setConfirm(confirmFunction: (arg0: string, arg1: (arg0: boolean) => any) => any): void; + /** + * Wrapper to window.prompt() that app developers may override via setPrompt to + * provide alternatives to the modal browser window. Built-in browser prompts + * are often used for better text input experience on mobile device. We strongly + * recommend testing mobile when overriding this. + * @param {string} message The message to display to the user. + * @param {string} defaultValue The value to initialize the prompt with. + * @param {!function(?string)} callback The callback for handling user response. + * @alias Blockly.dialog.prompt + */ + export function prompt(message: string, defaultValue: string, callback: (arg0: string | null) => any): void; + /** + * Sets the function to be run when Blockly.dialog.prompt() is called. + * @param {!function(string, string, !function(?string))} promptFunction The + * function to be run. + * @see Blockly.dialog.prompt + * @alias Blockly.dialog.setPrompt + */ + export function setPrompt(promptFunction: (arg0: string, arg1: string, arg2: (arg0: string | null) => any) => any): void; +} +declare module "utils/array" { + /** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + /** + * @fileoverview Utility methods related to arrays. + */ + /** + * @namespace Blockly.utils.array + */ + /** + * Removes the first occurrence of a particular value from an array. + * @param {!Array} arr Array from which to remove value. + * @param {*} value Value to remove. + * @return {boolean} True if an element was removed. + * @alias Blockly.array.removeElem + * @package + */ + export function removeElem(arr: any[], value: any): boolean; +} +declare module "utils/math" { + /** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + /** + * @fileoverview Utility methods for math. + * These methods are not specific to Blockly, and could be factored out into + * a JavaScript framework such as Closure. + */ + /** + * Utility methods for math. + * These methods are not specific to Blockly, and could be factored out into + * a JavaScript framework such as Closure. + * @namespace Blockly.utils.math + */ + /** + * Converts degrees to radians. + * Copied from Closure's goog.math.toRadians. + * @param {number} angleDegrees Angle in degrees. + * @return {number} Angle in radians. + * @alias Blockly.utils.math.toRadians + */ + export function toRadians(angleDegrees: number): number; + /** + * Converts radians to degrees. + * Copied from Closure's goog.math.toDegrees. + * @param {number} angleRadians Angle in radians. + * @return {number} Angle in degrees. + * @alias Blockly.utils.math.toDegrees + */ + export function toDegrees(angleRadians: number): number; + /** + * Clamp the provided number between the lower bound and the upper bound. + * @param {number} lowerBound The desired lower bound. + * @param {number} number The number to clamp. + * @param {number} upperBound The desired upper bound. + * @return {number} The clamped number. + * @alias Blockly.utils.math.clamp + */ + export function clamp(lowerBound: number, number: number, upperBound: number): number; +} +declare module "serialization/priorities" { + /** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + /** + * @fileoverview The top level namespace for priorities of plugin serializers. + * Includes constants for the priorities of different plugin + * serializers. Higher priorities are deserialized first. + */ + /** + * The top level namespace for priorities of plugin serializers. + * Includes constants for the priorities of different plugin serializers. Higher + * priorities are deserialized first. + * @namespace Blockly.serialization.priorities + */ + /** + * The priority for deserializing variables. + * @type {number} + * @const + * @alias Blockly.serialization.priorities.VARIABLES + */ + export const VARIABLES: number; + /** + * The priority for deserializing blocks. + * @type {number} + * @const + * @alias Blockly.serialization.priorities.BLOCKS + */ + export const BLOCKS: number; +} +declare module "interfaces/i_serializer" { + /** + * Serializes and deserializes a plugin or system. + * @interface + * @alias Blockly.serialization.ISerializer.ISerializer + */ + export class ISerializer { + /** + * A priority value used to determine the order of deserializing state. + * More positive priorities are deserialized before less positive + * priorities. Eg if you have priorities (0, -10, 10, 100) the order of + * deserialiation will be (100, 10, 0, -10). + * If two serializers have the same priority, they are deserialized in an + * arbitrary order relative to each other. + * @type {number} + */ + priority: number; + /** + * Saves the state of the plugin or system. + * @param {!Workspace} workspace The workspace the system to serialize is + * associated with. + * @return {?} A JS object containing the system's state, or null if + * there is no state to record. + */ + save(workspace: Workspace): unknown; + /** + * Loads the state of the plugin or system. + * @param {?} state The state of the system to deserialize. This will always + * be non-null. + * @param {!Workspace} workspace The workspace the system to deserialize is + * associated with. + */ + load(state: unknown, workspace: Workspace): void; + /** + * Clears the state of the plugin or system. + * @param {!Workspace} workspace The workspace the system to clear the state + * of is associated with. + */ + clear(workspace: Workspace): void; + } + import { Workspace } from "workspace"; +} +declare module "serialization/registry" { + /** + * Registers the given serializer so that it can be used for serialization and + * deserialization. + * @param {string} name The name of the serializer to register. + * @param {ISerializer} serializer The serializer to register. + * @alias Blockly.serialization.registry.register + */ + export function register(name: string, serializer: ISerializer): void; + /** + * Unregisters the serializer associated with the given name. + * @param {string} name The name of the serializer to unregister. + * @alias Blockly.serialization.registry.unregister + */ + export function unregister(name: string): void; + import { ISerializer } from "interfaces/i_serializer"; +} +declare module "serialization/exceptions" { + /** + * @alias Blockly.serialization.exceptions.DeserializationError + */ + export class DeserializationError extends Error { + } + /** + * Represents an error where the serialized state is expected to provide a + * block type, but it is not provided. + * @alias Blockly.serialization.exceptions.MissingBlockType + */ + export class MissingBlockType extends DeserializationError { + /** + * @param {!State} state The state object which is missing the block type. + * @package + */ + constructor(state: any); + /** + * The state object containing the bad name. + * @type {!State} + */ + state: any; + } + /** + * Represents an error where deserialization encountered a block that did + * not have a connection that was defined in the serialized state. + * @alias Blockly.serialization.exceptions.MissingConnection + */ + export class MissingConnection extends DeserializationError { + /** + * @param {string} connection The name of the connection that is missing. E.g. + * 'IF0', or 'next'. + * @param {!Block} block The block missing the connection. + * @param {!State} state The state object containing the bad connection. + * @package + */ + constructor(connection: string, block: Block, state: any); + /** + * The block missing the connection. + * @type {!Block} + */ + block: Block; + /** + * The state object containing the bad name. + * @type {!State} + */ + state: any; + } + /** + * Represents an error where deserialization tried to connect two connections + * that were not compatible. + * @alias Blockly.serialization.exceptions.BadConnectionCheck + */ + export class BadConnectionCheck extends DeserializationError { + /** + * @param {string} reason The reason the connections were not compatible. + * @param {string} childConnection The name of the incompatible child + * connection. E.g. 'output' or 'previous'. + * @param {!Block} childBlock The child block that could not connect + * to its parent. + * @param {!State} childState The state object representing the child block. + * @package + */ + constructor(reason: string, childConnection: string, childBlock: Block, childState: any); + /** + * The block that could not connect to its parent. + * @type {!Block} + */ + childBlock: Block; + /** + * The state object representing the block that could not connect to its + * parent. + * @type {!State} + */ + childState: any; + } + /** + * Represents an error where deserialization encountered a real block as it + * was deserializing children of a shadow. + * This is an error because it is an invariant of Blockly that shadow blocks + * do not have real children. + * @alias Blockly.serialization.exceptions.RealChildOfShadow + */ + export class RealChildOfShadow extends DeserializationError { + /** + * @param {!State} state The state object representing the real block. + * @package + */ + constructor(state: any); + /** + * The state object representing the real block. + * @type {!State} + */ + state: any; + } + import { Block } from "block"; +} +declare module "utils/size" { + /** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + /** + * @fileoverview Utility methods for size calculation. + * These methods are not specific to Blockly, and could be factored out into + * a JavaScript framework such as Closure. + */ + /** + * Utility methods for size calculation. + * These methods are not specific to Blockly, and could be factored out into + * a JavaScript framework such as Closure. + * @class + */ + /** + * Class for representing sizes consisting of a width and height. + * @param {number} width Width. + * @param {number} height Height. + * @struct + * @constructor + * @alias Blockly.utils.Size + */ + export class Size { + /** + * Compares sizes for equality. + * @param {?Size} a A Size. + * @param {?Size} b A Size. + * @return {boolean} True iff the sizes have equal widths and equal + * heights, or if both are null. + */ + static equals(a: Size | null, b: Size | null): boolean; + constructor(width: any, height: any); + /** + * Width + * @type {number} + */ + width: number; + /** + * Height + * @type {number} + */ + height: number; + } +} +declare module "input_types" { + /** + * * + */ + export type inputTypes = number; + export namespace inputTypes { + const VALUE: number; + const STATEMENT: number; + const DUMMY: number; + } +} +declare module "serialization/blocks" { + /** + * Represents the state of a connection. + */ + export type ConnectionState = { + shadow: (any | undefined); + block: (any | undefined); + }; + /** + * Represents the state of a connection. + * @typedef {{ + * shadow: (!State|undefined), + * block: (!State|undefined) + * }} + * @alias Blockly.serialization.blocks.ConnectionState + */ + export let ConnectionState: any; + /** + * Represents the state of a given block. + * @typedef {{ + * type: string, + * id: (string|undefined), + * x: (number|undefined), + * y: (number|undefined), + * collapsed: (boolean|undefined), + * enabled: (boolean|undefined), + * inline: (boolean|undefined), + * data: (string|undefined), + * extra-state: (*|undefined), + * icons: (!Object|undefined), + * fields: (!Object|undefined), + * inputs: (!Object|undefined), + * next: (!ConnectionState|undefined) + * }} + * @alias Blockly.serialization.blocks.State + */ + export let State: any; + /** + * Returns the state of the given block as a plain JavaScript object. + * @param {!Block} block The block to serialize. + * @param {{addCoordinates: (boolean|undefined), addInputBlocks: + * (boolean|undefined), addNextBlocks: (boolean|undefined), + * doFullSerialization: (boolean|undefined)}=} param1 + * addCoordinates: If true, the coordinates of the block are added to the + * serialized state. False by default. + * addinputBlocks: If true, children of the block which are connected to + * inputs will be serialized. True by default. + * addNextBlocks: If true, children of the block which are connected to the + * block's next connection (if it exists) will be serialized. + * True by default. + * doFullSerialization: If true, fields that normally just save a reference + * to some external state (eg variables) will instead serialize all of the + * info about that state. This supports deserializing the block into a + * workspace where that state doesn't yet exist. True by default. + * @return {?State} The serialized state of the block, or null if the block + * could not be serialied (eg it was an insertion marker). + * @alias Blockly.serialization.blocks.save + */ + export function save(block: Block, { addCoordinates, addInputBlocks, addNextBlocks, doFullSerialization, }?: { + addCoordinates: (boolean | undefined); + addInputBlocks: (boolean | undefined); + addNextBlocks: (boolean | undefined); + doFullSerialization: (boolean | undefined); + } | undefined): any | null; + /** + * Loads the block represented by the given state into the given workspace. + * @param {!State} state The state of a block to deserialize into the workspace. + * @param {!Workspace} workspace The workspace to add the block to. + * @param {{recordUndo: (boolean|undefined)}=} param1 + * recordUndo: If true, events triggered by this function will be undo-able + * by the user. False by default. + * @return {!Block} The block that was just loaded. + * @alias Blockly.serialization.blocks.append + */ + export function append(state: any, workspace: Workspace, { recordUndo }?: { + recordUndo: (boolean | undefined); + } | undefined): Block; + /** + * Loads the block represented by the given state into the given workspace. + * This is defined internally so that the extra parameters don't clutter our + * external API. + * But it is exported so that other places within Blockly can call it directly + * with the extra parameters. + * @param {!State} state The state of a block to deserialize into the workspace. + * @param {!Workspace} workspace The workspace to add the block to. + * @param {{parentConnection: (!Connection|undefined), isShadow: + * (boolean|undefined), recordUndo: (boolean|undefined)}=} param1 + * parentConnection: If provided, the system will attempt to connect the + * block to this connection after it is created. Undefined by default. + * isShadow: If true, the block will be set to a shadow block after it is + * created. False by default. + * recordUndo: If true, events triggered by this function will be undo-able + * by the user. False by default. + * @return {!Block} The block that was just appended. + * @alias Blockly.serialization.blocks.appendInternal + * @package + */ + export function appendInternal(state: any, workspace: Workspace, { parentConnection, isShadow, recordUndo, }?: { + parentConnection: (Connection | undefined); + isShadow: (boolean | undefined); + recordUndo: (boolean | undefined); + } | undefined): Block; + import { Block } from "block"; + import { Workspace } from "workspace"; + import { Connection } from "connection"; +} +declare module "utils/deprecation" { + /** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + /** + * @fileoverview Helper function for warning developers about deprecations. + * This method is not specific to Blockly. + */ + /** + * Helper function for warning developers about deprecations. + * This method is not specific to Blockly. + * @namespace Blockly.utils.deprecation + */ + /** + * Warn developers that a function or property is deprecated. + * @param {string} name The name of the function or property. + * @param {string} deprecationDate The date of deprecation. + * Prefer 'month yyyy' or 'quarter yyyy' format. + * @param {string} deletionDate The date of deletion, in the same format as the + * deprecation date. + * @param {string=} opt_use The name of a function or property to use instead, + * if any. + * @alias Blockly.utils.deprecation.warn + * @package + */ + export function warn(name: string, deprecationDate: string, deletionDate: string, opt_use?: string | undefined): void; +} +declare module "css" { + /** + * Add some CSS to the blob that will be injected later. Allows optional + * components such as fields and the toolbox to store separate CSS. + * @param {string|!Array} cssContent Multiline CSS string or an array of + * single lines of CSS. + * @alias Blockly.Css.register + */ + export function register(cssContent: string | Array): void; + /** + * Inject the CSS into the DOM. This is preferable over using a regular CSS + * file since: + * a) It loads synchronously and doesn't force a redraw later. + * b) It speeds up loading by not blocking on a separate HTTP transfer. + * c) The CSS content may be made dynamic depending on init options. + * @param {boolean} hasCss If false, don't inject CSS + * (providing CSS becomes the document's responsibility). + * @param {string} pathToMedia Path from page to the Blockly media directory. + * @alias Blockly.Css.inject + */ + export function inject(hasCss: boolean, pathToMedia: string): void; + /** + * The CSS content for Blockly. + * @alias Blockly.Css.content + */ + export let content: string; +} +declare module "utils/aria" { + /** + * * + */ + export type Role = string; + export namespace Role { + const GRID: string; + const GRIDCELL: string; + const GROUP: string; + const LISTBOX: string; + const MENU: string; + const MENUITEM: string; + const MENUITEMCHECKBOX: string; + const OPTION: string; + const PRESENTATION: string; + const ROW: string; + const TREE: string; + const TREEITEM: string; + } + /** + * * + */ + export type State = string; + export namespace State { + const ACTIVEDESCENDANT: string; + const COLCOUNT: string; + const DISABLED: string; + const EXPANDED: string; + const INVALID: string; + const LABEL: string; + const LABELLEDBY: string; + const LEVEL: string; + const ORIENTATION: string; + const POSINSET: string; + const ROWCOUNT: string; + const SELECTED: string; + const SETSIZE: string; + const VALUEMAX: string; + const VALUEMIN: string; + } + /** + * Sets the role of an element. + * + * Similar to Closure's goog.a11y.aria + * + * @param {!Element} element DOM node to set role of. + * @param {!Role} roleName Role name. + * @alias Blockly.utils.aria.setRole + */ + export function setRole(element: Element, roleName: Role): void; + /** + * Sets the state or property of an element. + * Copied from Closure's goog.a11y.aria + * @param {!Element} element DOM node where we set state. + * @param {!State} stateName State attribute being set. + * Automatically adds prefix 'aria-' to the state name if the attribute is + * not an extra attribute. + * @param {string|boolean|number|!Array} value Value + * for the state attribute. + * @alias Blockly.utils.aria.setState + */ + export function setState(element: Element, stateName: State, value: string | boolean | number | Array): void; +} +declare module "utils/colour" { + /** + * Parses a colour from a string. + * .parse('red') -> '#ff0000' + * .parse('#f00') -> '#ff0000' + * .parse('#ff0000') -> '#ff0000' + * .parse('0xff0000') -> '#ff0000' + * .parse('rgb(255, 0, 0)') -> '#ff0000' + * @param {string|number} str Colour in some CSS format. + * @return {?string} A string containing a hex representation of the colour, + * or null if can't be parsed. + * @alias Blockly.utils.colour.parse + */ + export function parse(str: string | number): string | null; + /** + * Converts a colour from RGB to hex representation. + * @param {number} r Amount of red, int between 0 and 255. + * @param {number} g Amount of green, int between 0 and 255. + * @param {number} b Amount of blue, int between 0 and 255. + * @return {string} Hex representation of the colour. + * @alias Blockly.utils.colour.rgbToHex + */ + export function rgbToHex(r: number, g: number, b: number): string; + /** + * Converts a colour to RGB. + * @param {string} colour String representing colour in any + * colour format ('#ff0000', 'red', '0xff000', etc). + * @return {!Array} RGB representation of the colour. + * @alias Blockly.utils.colour.hexToRgb + */ + export function hexToRgb(colour: string): Array; + /** + * Converts an HSV triplet to hex representation. + * @param {number} h Hue value in [0, 360]. + * @param {number} s Saturation value in [0, 1]. + * @param {number} v Brightness in [0, 255]. + * @return {string} Hex representation of the colour. + * @alias Blockly.utils.colour.hsvToHex + */ + export function hsvToHex(h: number, s: number, v: number): string; + /** + * Blend two colours together, using the specified factor to indicate the + * weight given to the first colour. + * @param {string} colour1 First colour. + * @param {string} colour2 Second colour. + * @param {number} factor The weight to be given to colour1 over colour2. + * Values should be in the range [0, 1]. + * @return {?string} Combined colour represented in hex. + * @alias Blockly.utils.colour.blend + */ + export function blend(colour1: string, colour2: string, factor: number): string | null; + /** + * A map that contains the 16 basic colour keywords as defined by W3C: + * https://www.w3.org/TR/2018/REC-css-color-3-20180619/#html4 + * The keys of this map are the lowercase "readable" names of the colours, + * while the values are the "hex" values. + * + * @type {!Object} + * @alias Blockly.utils.colour.names + */ + export const names: { + [x: string]: string; + }; + /** + * Convert a hue (HSV model) into an RGB hex triplet. + * @param {number} hue Hue on a colour wheel (0-360). + * @return {string} RGB code, e.g. '#5ba65b'. + * @alias Blockly.utils.colour.hueToHex + */ + export function hueToHex(hue: number): string; +} +declare module "utils/parsing" { + /** + * Parse a string with any number of interpolation tokens (%1, %2, ...). + * It will also replace string table references (e.g., %{bky_my_msg} and + * %{BKY_MY_MSG} will both be replaced with the value in + * Msg['MY_MSG']). Percentage sign characters '%' may be self-escaped + * (e.g., '%%'). + * @param {string} message Text which might contain string table references and + * interpolation tokens. + * @return {!Array} Array of strings and numbers. + * @alias Blockly.parsing.tokenizeInterpolation + */ + export function tokenizeInterpolation(message: string): Array; + /** + * Replaces string table references in a message, if the message is a string. + * For example, "%{bky_my_msg}" and "%{BKY_MY_MSG}" will both be replaced with + * the value in Msg['MY_MSG']. + * @param {string|?} message Message, which may be a string that contains + * string table references. + * @return {string} String with message references replaced. + * @alias Blockly.parsing.replaceMessageReferences + */ + export function replaceMessageReferences(message: string | unknown): string; + /** + * Validates that any %{MSG_KEY} references in the message refer to keys of + * the Msg string table. + * @param {string} message Text which might contain string table references. + * @return {boolean} True if all message references have matching values. + * Otherwise, false. + * @alias Blockly.parsing.checkMessageReferences + */ + export function checkMessageReferences(message: string): boolean; + /** + * Parse a block colour from a number or string, as provided in a block + * definition. + * @param {number|string} colour HSV hue value (0 to 360), #RRGGBB string, + * or a message reference string pointing to one of those two values. + * @return {{hue: ?number, hex: string}} An object containing the colour as + * a #RRGGBB string, and the hue if the input was an HSV hue value. + * @throws {Error} If the colour cannot be parsed. + * @alias Blockly.parsing.parseBlockColour + */ + export function parseBlockColour(colour: number | string): { + hue: number | null; + hex: string; + }; +} +declare module "interfaces/i_toolbox_item" { + /** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + /** + * @fileoverview The interface for a toolbox item. + */ + /** + * The interface for a toolbox item. + * @namespace Blockly.IToolboxItem + */ + /** + * Interface for an item in the toolbox. + * @interface + * @alias Blockly.IToolboxItem + */ + export class IToolboxItem { + } +} +declare module "interfaces/i_selectable_toolbox_item" { + /** + * Interface for an item in the toolbox that can be selected. + * @extends {IToolboxItem} + * @interface + * @alias Blockly.ISelectableToolboxItem + */ + export class ISelectableToolboxItem { + } +} +declare module "interfaces/i_collapsible_toolbox_item" { + /** + * Interface for an item in the toolbox that can be collapsed. + * @extends {ISelectableToolboxItem} + * @interface + * @alias Blockly.ICollapsibleToolboxItem + */ + export class ICollapsibleToolboxItem { + } +} +declare module "utils/coordinate" { + /** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + /** + * @fileoverview Utility methods for coordinate manipulation. + * These methods are not specific to Blockly, and could be factored out into + * a JavaScript framework such as Closure. + */ + /** + * Utility methods for coordinate manipulation. + * These methods are not specific to Blockly, and could be factored out into + * a JavaScript framework such as Closure. + * @class + */ + /** + * Class for representing coordinates and positions. + * @param {number} x Left. + * @param {number} y Top. + * @struct + * @constructor + * @alias Blockly.utils.Coordinate + */ + export class Coordinate { + /** + * Compares coordinates for equality. + * @param {?Coordinate} a A Coordinate. + * @param {?Coordinate} b A Coordinate. + * @return {boolean} True iff the coordinates are equal, or if both are null. + */ + static equals(a: Coordinate | null, b: Coordinate | null): boolean; + /** + * Returns the distance between two coordinates. + * @param {!Coordinate} a A Coordinate. + * @param {!Coordinate} b A Coordinate. + * @return {number} The distance between `a` and `b`. + */ + static distance(a: Coordinate, b: Coordinate): number; + /** + * Returns the magnitude of a coordinate. + * @param {!Coordinate} a A Coordinate. + * @return {number} The distance between the origin and `a`. + */ + static magnitude(a: Coordinate): number; + /** + * Returns the difference between two coordinates as a new + * Coordinate. + * @param {!Coordinate|!SVGPoint} a An x/y coordinate. + * @param {!Coordinate|!SVGPoint} b An x/y coordinate. + * @return {!Coordinate} A Coordinate representing the difference + * between `a` and `b`. + */ + static difference(a: Coordinate | SVGPoint, b: Coordinate | SVGPoint): Coordinate; + /** + * Returns the sum of two coordinates as a new Coordinate. + * @param {!Coordinate|!SVGPoint} a An x/y coordinate. + * @param {!Coordinate|!SVGPoint} b An x/y coordinate. + * @return {!Coordinate} A Coordinate representing the sum of + * the two coordinates. + */ + static sum(a: Coordinate | SVGPoint, b: Coordinate | SVGPoint): Coordinate; + constructor(x: any, y: any); + /** + * X-value + * @type {number} + */ + x: number; + /** + * Y-value + * @type {number} + */ + y: number; + /** + * Creates a new copy of this coordinate. + * @return {!Coordinate} A copy of this coordinate. + */ + clone(): Coordinate; + /** + * Scales this coordinate by the given scale factor. + * @param {number} s The scale factor to use for both x and y dimensions. + * @return {!Coordinate} This coordinate after scaling. + */ + scale(s: number): Coordinate; + /** + * Translates this coordinate by the given offsets. + * respectively. + * @param {number} tx The value to translate x by. + * @param {number} ty The value to translate y by. + * @return {!Coordinate} This coordinate after translating. + */ + translate(tx: number, ty: number): Coordinate; + } +} +declare module "interfaces/i_registrable" { + /** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + /** + * @fileoverview The interface for a Blockly component that can be registered. + * (Ex. Toolbox, Fields, Renderers) + */ + /** + * The interface for a Blockly component that can be registered. + * (Ex. Toolbox, Fields, Renderers) + * @namespace Blockly.IRegistrable + */ + /** + * The interface for a Blockly component that can be registered. + * @interface + * @alias Blockly.IRegistrable + */ + export class IRegistrable { + } +} +declare module "interfaces/i_flyout" { + /** + * Interface for a flyout. + * @extends {IRegistrable} + * @interface + * @alias Blockly.IFlyout + */ + export class IFlyout { + /** + * Whether the flyout is laid out horizontally or not. + * @type {boolean} + */ + horizontalLayout: boolean; + /** + * Is RTL vs LTR. + * @type {boolean} + */ + RTL: boolean; + /** + * The target workspace + * @type {?WorkspaceSvg} + */ + targetWorkspace: WorkspaceSvg | null; + /** + * Margin around the edges of the blocks in the flyout. + * @type {number} + * @const + */ + MARGIN: number; + /** + * Does the flyout automatically close when a block is created? + * @type {boolean} + */ + autoClose: boolean; + /** + * Corner radius of the flyout background. + * @type {number} + * @const + */ + CORNER_RADIUS: number; + } + import { WorkspaceSvg } from "workspace_svg"; +} +declare module "interfaces/i_toolbox" { + /** + * Interface for a toolbox. + * @extends {IRegistrable} + * @interface + * @alias Blockly.IToolbox + */ + export class IToolbox { + } +} +declare module "toolbox/toolbox_item" { + /** + * Class for an item in the toolbox. + * @param {!toolbox.ToolboxItemInfo} toolboxItemDef The JSON defining the + * toolbox item. + * @param {!IToolbox} toolbox The toolbox that holds the toolbox item. + * @param {ICollapsibleToolboxItem=} opt_parent The parent toolbox item + * or null if the category does not have a parent. + * @constructor + * @implements {IToolboxItem} + * @alias Blockly.ToolboxItem + */ + export class ToolboxItem implements IToolboxItem { + constructor(toolboxItemDef: any, toolbox: any, opt_parent: any); + /** + * The id for the category. + * @type {string} + * @protected + */ + protected id_: string; + /** + * The parent of the category. + * @type {?ICollapsibleToolboxItem} + * @protected + */ + protected parent_: ICollapsibleToolboxItem | null; + /** + * The level that the category is nested at. + * @type {number} + * @protected + */ + protected level_: number; + /** + * The JSON definition of the toolbox item. + * @type {!toolbox.ToolboxItemInfo} + * @protected + */ + protected toolboxItemDef_: any; + /** + * The toolbox this category belongs to. + * @type {!IToolbox} + * @protected + */ + protected parentToolbox_: IToolbox; + /** + * The workspace of the parent toolbox. + * @type {!WorkspaceSvg} + * @protected + */ + protected workspace_: WorkspaceSvg; + /** + * Initializes the toolbox item. + * This includes creating the DOM and updating the state of any items based + * on the info object. + * @public + */ + public init(): void; + /** + * Gets the div for the toolbox item. + * @return {?Element} The div for the toolbox item. + * @public + */ + public getDiv(): Element | null; + /** + * Gets a unique identifier for this toolbox item. + * @return {string} The ID for the toolbox item. + * @public + */ + public getId(): string; + /** + * Gets the parent if the toolbox item is nested. + * @return {?IToolboxItem} The parent toolbox item, or null if + * this toolbox item is not nested. + * @public + */ + public getParent(): IToolboxItem | null; + /** + * Gets the nested level of the category. + * @return {number} The nested level of the category. + * @package + */ + getLevel(): number; + /** + * Whether the toolbox item is selectable. + * @return {boolean} True if the toolbox item can be selected. + * @public + */ + public isSelectable(): boolean; + /** + * Whether the toolbox item is collapsible. + * @return {boolean} True if the toolbox item is collapsible. + * @public + */ + public isCollapsible(): boolean; + /** + * Dispose of this toolbox item. No-op by default. + * @public + */ + public dispose(): void; + } + import { IToolboxItem } from "interfaces/i_toolbox_item"; + import { ICollapsibleToolboxItem } from "interfaces/i_collapsible_toolbox_item"; + import { IToolbox } from "interfaces/i_toolbox"; + import { WorkspaceSvg } from "workspace_svg"; +} +declare module "toolbox/category" { + import { ToolboxItem } from "toolbox/toolbox_item"; + export class ToolboxCategory extends ToolboxItem { + /** + * Class for a category in a toolbox. + * @param {!toolbox.CategoryInfo} categoryDef The information needed + * to create a category in the toolbox. + * @param {!IToolbox} toolbox The parent toolbox for the category. + * @param {ICollapsibleToolboxItem=} opt_parent The parent category or null if + * the category does not have a parent. + * @constructor + * @extends {ToolboxItem} + * @implements {ISelectableToolboxItem} + * @alias Blockly.ToolboxCategory + */ + constructor(categoryDef: toolbox.CategoryInfo, toolbox: IToolbox, opt_parent?: ICollapsibleToolboxItem | undefined); + /** + * The name that will be displayed on the category. + * @type {string} + * @protected + */ + protected name_: string; + /** + * The colour of the category. + * @type {string} + * @protected + */ + protected colour_: string; + /** + * The html container for the category. + * @type {?Element} + * @protected + */ + protected htmlDiv_: Element | null; + /** + * The html element for the category row. + * @type {?Element} + * @protected + */ + protected rowDiv_: Element | null; + /** + * The html element that holds children elements of the category row. + * @type {?Element} + * @protected + */ + protected rowContents_: Element | null; + /** + * The html element for the toolbox icon. + * @type {?Element} + * @protected + */ + protected iconDom_: Element | null; + /** + * The html element for the toolbox label. + * @type {?Element} + * @protected + */ + protected labelDom_: Element | null; + /** + * All the css class names that are used to create a category. + * @type {!ToolboxCategory.CssConfig} + * @protected + */ + protected cssConfig_: ToolboxCategory.CssConfig; + /** + * True if the category is meant to be hidden, false otherwise. + * @type {boolean} + * @protected + */ + protected isHidden_: boolean; + /** + * True if this category is disabled, false otherwise. + * @type {boolean} + * @protected + */ + protected isDisabled_: boolean; + /** + * The flyout items for this category. + * @type {string|!toolbox.FlyoutItemInfoArray} + * @protected + */ + protected flyoutItems_: string | toolbox.FlyoutItemInfoArray; + /** + * Creates an object holding the default classes for a category. + * @return {!ToolboxCategory.CssConfig} The configuration object holding + * all the CSS classes for a category. + * @protected + */ + protected makeDefaultCssConfig_(): ToolboxCategory.CssConfig; + /** + * Parses the contents array depending on if the category is a dynamic category, + * or if its contents are meant to be shown in the flyout. + * @param {!toolbox.CategoryInfo} categoryDef The information needed + * to create a category. + * @protected + */ + protected parseContents_(categoryDef: toolbox.CategoryInfo): void; + /** + * @override + */ + override init(): void; + /** + * Creates the DOM for the category. + * @return {!Element} The parent element for the category. + * @protected + */ + protected createDom_(): Element; + /** + * Creates the container that holds the row and any subcategories. + * @return {!Element} The div that holds the icon and the label. + * @protected + */ + protected createContainer_(): Element; + /** + * Creates the parent of the contents container. All clicks will happen on this + * div. + * @return {!Element} The div that holds the contents container. + * @protected + */ + protected createRowContainer_(): Element; + /** + * Creates the container for the label and icon. + * This is necessary so we can set all subcategory pointer events to none. + * @return {!Element} The div that holds the icon and the label. + * @protected + */ + protected createRowContentsContainer_(): Element; + /** + * Creates the span that holds the category icon. + * @return {!Element} The span that holds the category icon. + * @protected + */ + protected createIconDom_(): Element; + /** + * Creates the span that holds the category label. + * This should have an ID for accessibility purposes. + * @param {string} name The name of the category. + * @return {!Element} The span that holds the category label. + * @protected + */ + protected createLabelDom_(name: string): Element; + /** + * Updates the colour for this category. + * @public + */ + public refreshTheme(): void; + /** + * Add the strip of colour to the toolbox category. + * @param {string} colour The category colour. + * @protected + */ + protected addColourBorder_(colour: string): void; + /** + * Gets either the colour or the style for a category. + * @param {!toolbox.CategoryInfo} categoryDef The object holding + * information on the category. + * @return {string} The hex colour for the category. + * @protected + */ + protected getColour_(categoryDef: toolbox.CategoryInfo): string; + /** + * Sets the colour for the category using the style name and returns the new + * colour as a hex string. + * @param {string} styleName Name of the style. + * @return {string} The hex colour for the category. + * @private + */ + private getColourfromStyle_; + /** + * Gets the HTML element that is clickable. + * The parent toolbox element receives clicks. The parent toolbox will add an ID + * to this element so it can pass the onClick event to the correct toolboxItem. + * @return {!Element} The HTML element that receives clicks. + * @public + */ + public getClickTarget(): Element; + /** + * Parses the colour on the category. + * @param {number|string} colourValue HSV hue value (0 to 360), #RRGGBB string, + * or a message reference string pointing to one of those two values. + * @return {string} The hex colour for the category. + * @private + */ + private parseColour_; + /** + * Adds appropriate classes to display an open icon. + * @param {?Element} iconDiv The div that holds the icon. + * @protected + */ + protected openIcon_(iconDiv: Element | null): void; + /** + * Adds appropriate classes to display a closed icon. + * @param {?Element} iconDiv The div that holds the icon. + * @protected + */ + protected closeIcon_(iconDiv: Element | null): void; + /** + * Sets whether the category is visible or not. + * For a category to be visible its parent category must also be expanded. + * @param {boolean} isVisible True if category should be visible. + * @protected + */ + protected setVisible_(isVisible: boolean): void; + /** + * Hide the category. + */ + hide(): void; + /** + * Show the category. Category will only appear if its parent category is also + * expanded. + */ + show(): void; + /** + * Whether the category is visible. + * A category is only visible if all of its ancestors are expanded and isHidden_ + * is false. + * @return {boolean} True if the category is visible, false otherwise. + * @public + */ + public isVisible(): boolean; + /** + * Whether all ancestors of a category (parent and parent's parent, etc.) are + * expanded. + * @return {boolean} True only if every ancestor is expanded + * @protected + */ + protected allAncestorsExpanded_(): boolean; + /** + * @override + */ + override isSelectable(): boolean; + /** + * Handles when the toolbox item is clicked. + * @param {!Event} _e Click event to handle. + * @public + */ + public onClick(_e: Event): void; + /** + * Sets the current category as selected. + * @param {boolean} isSelected True if this category is selected, false + * otherwise. + * @public + */ + public setSelected(isSelected: boolean): void; + /** + * Sets whether the category is disabled. + * @param {boolean} isDisabled True to disable the category, false otherwise. + */ + setDisabled(isDisabled: boolean): void; + /** + * Gets the name of the category. Used for emitting events. + * @return {string} The name of the toolbox item. + * @public + */ + public getName(): string; + /** + * @override + */ + override getParent(): any; + /** + * @override + */ + override getDiv(): Element; + /** + * Gets the contents of the category. These are items that are meant to be + * displayed in the flyout. + * @return {!toolbox.FlyoutItemInfoArray|string} The definition + * of items to be displayed in the flyout. + * @public + */ + public getContents(): any | string; + /** + * Updates the contents to be displayed in the flyout. + * If the flyout is open when the contents are updated, refreshSelection on the + * toolbox must also be called. + * @param {!toolbox.FlyoutDefinition|string} contents The contents + * to be displayed in the flyout. A string can be supplied to create a + * dynamic category. + * @public + */ + public updateFlyoutContents(contents: toolbox.FlyoutDefinition | string): void; + /** + * @override + */ + override dispose(): void; + } + export namespace ToolboxCategory { + const registrationName: string; + const nestedPadding: number; + const borderWidth: number; + const defaultBackgroundColour: string; + /** + * All the CSS class names that are used to create a category. + */ + type CssConfig = { + container: (string | undefined); + row: (string | undefined); + rowcontentcontainer: (string | undefined); + icon: (string | undefined); + label: (string | undefined); + selected: (string | undefined); + openicon: (string | undefined); + closedicon: (string | undefined); + }; + } + import * as toolbox from "utils/toolbox"; + import { IToolbox } from "interfaces/i_toolbox"; + import { ICollapsibleToolboxItem } from "interfaces/i_collapsible_toolbox_item"; +} +declare module "toolbox/separator" { + import { ToolboxItem } from "toolbox/toolbox_item"; + /** + * Class for a toolbox separator. This is the thin visual line that appears on + * the toolbox. This item is not interactable. + * @param {!toolbox.SeparatorInfo} separatorDef The information + * needed to create a separator. + * @param {!IToolbox} toolbox The parent toolbox for the separator. + * @constructor + * @extends {ToolboxItem} + * @alias Blockly.ToolboxSeparator + */ + export class ToolboxSeparator extends ToolboxItem { + constructor(separatorDef: any, toolbox: any); + /** + * All the CSS class names that are used to create a separator. + * @type {!ToolboxSeparator.CssConfig} + * @protected + */ + protected cssConfig_: ToolboxSeparator.CssConfig; + /** + * @override + */ + override init(): void; + /** + * Creates the DOM for a separator. + * @return {!Element} The parent element for the separator. + * @protected + */ + protected createDom_(): Element; + htmlDiv_: HTMLDivElement; + /** + * @override + */ + override getDiv(): HTMLDivElement; + /** + * @override + */ + override dispose(): void; + } + export namespace ToolboxSeparator { + const registrationName: string; + /** + * All the CSS class names that are used to create a separator. + */ + type CssConfig = { + container: (string | undefined); + }; + } +} +declare module "utils/toolbox" { +/** + * The information needed to create a block in the toolbox. + * Note that disabled has a different type for backwards compatibility. + * @typedef {{ + * kind:string, + * blockxml:(string|!Node|undefined), + * type:(string|undefined), + * gap:(string|number|undefined), + * disabled: (string|boolean|undefined), + * enabled: (boolean|undefined), + * id: (string|undefined), + * x: (number|undefined), + * y: (number|undefined), + * collapsed: (boolean|undefined), + * inline: (boolean|undefined), + * data: (string|undefined), + * extra-state: (*|undefined), + * icons: (!Object|undefined), + * fields: (!Object|undefined), + * inputs: (!Object|undefined), + * next: (!ConnectionState|undefined) + * }} + * @alias Blockly.utils.toolbox.BlockInfo + */ +export type BlockInfo = any; + /** + * An array holding flyout items. + * @typedef { + * Array + * } + * @alias Blockly.utils.toolbox.FlyoutItemInfoArray + */ + export type FlyoutItemInfoArray = any; + /** + * The information needed to create a separator in the toolbox. + */ + export type SeparatorInfo = { + kind: string; + id: (string | undefined); + gap: (number | undefined); + cssconfig: (ToolboxSeparator.CssConfig | undefined); + }; + /** + * The information needed to create a separator in the toolbox. + * @typedef {{ + * kind:string, + * id:(string|undefined), + * gap:(number|undefined), + * cssconfig:(!ToolboxSeparator.CssConfig|undefined) + * }} + * @alias Blockly.utils.toolbox.SeparatorInfo + */ + export let SeparatorInfo: any; + /** + * The information needed to create a button in the toolbox. + */ + export type ButtonInfo = { + kind: string; + text: string; + callbackkey: string; + }; + /** + * The information needed to create a button in the toolbox. + * @typedef {{ + * kind:string, + * text:string, + * callbackkey:string + * }} + * @alias Blockly.utils.toolbox.ButtonInfo + */ + export let ButtonInfo: any; + /** + * The information needed to create a label in the toolbox. + */ + export type LabelInfo = { + kind: string; + text: string; + id: (string | undefined); + }; + /** + * The information needed to create a label in the toolbox. + * @typedef {{ + * kind:string, + * text:string, + * id:(string|undefined) + * }} + * @alias Blockly.utils.toolbox.LabelInfo + */ + export let LabelInfo: any; + /** + * The information needed to create either a button or a label in the flyout. + */ + export type ButtonOrLabelInfo = ButtonInfo | LabelInfo; + /** + * The information needed to create either a button or a label in the flyout. + * @typedef {ButtonInfo| + * LabelInfo} + * @alias Blockly.utils.toolbox.ButtonOrLabelInfo + */ + export let ButtonOrLabelInfo: any; + /** + * The information needed to create a category in the toolbox. + */ + export type StaticCategoryInfo = { + kind: string; + name: string; + contents: Array; + id: (string | undefined); + categorystyle: (string | undefined); + colour: (string | undefined); + cssconfig: (ToolboxCategory.CssConfig | undefined); + hidden: (string | undefined); + }; + /** + * The information needed to create a category in the toolbox. + * @typedef {{ + * kind:string, + * name:string, + * contents:!Array, + * id:(string|undefined), + * categorystyle:(string|undefined), + * colour:(string|undefined), + * cssconfig:(!ToolboxCategory.CssConfig|undefined), + * hidden:(string|undefined) + * }} + * @alias Blockly.utils.toolbox.StaticCategoryInfo + */ + export let StaticCategoryInfo: any; + /** + * The information needed to create a custom category. + */ + export type DynamicCategoryInfo = { + kind: string; + custom: string; + id: (string | undefined); + categorystyle: (string | undefined); + colour: (string | undefined); + cssconfig: (ToolboxCategory.CssConfig | undefined); + hidden: (string | undefined); + }; + /** + * The information needed to create a custom category. + * @typedef {{ + * kind:string, + * custom:string, + * id:(string|undefined), + * categorystyle:(string|undefined), + * colour:(string|undefined), + * cssconfig:(!ToolboxCategory.CssConfig|undefined), + * hidden:(string|undefined) + * }} + * @alias Blockly.utils.toolbox.DynamicCategoryInfo + */ + export let DynamicCategoryInfo: any; + /** + * The information needed to create either a dynamic or static category. + */ + export type CategoryInfo = StaticCategoryInfo | DynamicCategoryInfo; + /** + * The information needed to create either a dynamic or static category. + * @typedef {StaticCategoryInfo| + * DynamicCategoryInfo} + * @alias Blockly.utils.toolbox.CategoryInfo + */ + export let CategoryInfo: any; + /** + * Any information that can be used to create an item in the toolbox. + */ + export type ToolboxItemInfo = FlyoutItemInfo | StaticCategoryInfo; + /** + * Any information that can be used to create an item in the toolbox. + * @typedef {FlyoutItemInfo| + * StaticCategoryInfo} + * @alias Blockly.utils.toolbox.ToolboxItemInfo + */ + export let ToolboxItemInfo: any; + /** + * All the different types that can be displayed in a flyout. + */ + export type FlyoutItemInfo = any | SeparatorInfo | ButtonInfo | LabelInfo | DynamicCategoryInfo; + /** + * All the different types that can be displayed in a flyout. + * @typedef {BlockInfo| + * SeparatorInfo| + * ButtonInfo| + * LabelInfo| + * DynamicCategoryInfo} + * @alias Blockly.utils.toolbox.FlyoutItemInfo + */ + export let FlyoutItemInfo: any; + /** + * The JSON definition of a toolbox. + */ + export type ToolboxInfo = { + kind: (string | undefined); + contents: Array; + }; + /** + * The JSON definition of a toolbox. + * @typedef {{ + * kind:(string|undefined), + * contents:!Array + * }} + * @alias Blockly.utils.toolbox.ToolboxInfo + */ + export let ToolboxInfo: any; + /** + * An array holding flyout items. + * @typedef { + * Array + * } + * @alias Blockly.utils.toolbox.FlyoutItemInfoArray + */ + export let FlyoutItemInfoArray: any; + /** + * All of the different types that can create a toolbox. + */ + export type ToolboxDefinition = Node | ToolboxInfo | string; + /** + * All of the different types that can create a toolbox. + * @typedef {Node| + * ToolboxInfo| + * string} + * @alias Blockly.utils.toolbox.ToolboxDefinition + */ + export let ToolboxDefinition: any; + /** + * All of the different types that can be used to show items in a flyout. + */ + export type FlyoutDefinition = any | NodeList | ToolboxInfo | Array; + /** + * All of the different types that can be used to show items in a flyout. + * @typedef {FlyoutItemInfoArray| + * NodeList| + * ToolboxInfo| + * Array} + * @alias Blockly.utils.toolbox.FlyoutDefinition + */ + export let FlyoutDefinition: any; + /** + * * + */ + export type Position = number; + export namespace Position { + const TOP: number; + const BOTTOM: number; + const LEFT: number; + const RIGHT: number; + } + /** + * Converts the toolbox definition into toolbox JSON. + * @param {?ToolboxDefinition} toolboxDef The definition + * of the toolbox in one of its many forms. + * @return {?ToolboxInfo} Object holding information + * for creating a toolbox. + * @alias Blockly.utils.toolbox.convertToolboxDefToJson + * @package + */ + export function convertToolboxDefToJson(toolboxDef: ToolboxDefinition | null): ToolboxInfo | null; + /** + * Converts the flyout definition into a list of flyout items. + * @param {?FlyoutDefinition} flyoutDef The definition of + * the flyout in one of its many forms. + * @return {!FlyoutItemInfoArray} A list of flyout items. + * @alias Blockly.utils.toolbox.convertFlyoutDefToJsonArray + * @package + */ + export function convertFlyoutDefToJsonArray(flyoutDef: FlyoutDefinition | null): any; + /** + * Whether or not the toolbox definition has categories. + * @param {?ToolboxInfo} toolboxJson Object holding + * information for creating a toolbox. + * @return {boolean} True if the toolbox has categories. + * @alias Blockly.utils.toolbox.hasCategories + * @package + */ + export function hasCategories(toolboxJson: ToolboxInfo | null): boolean; + /** + * Whether or not the category is collapsible. + * @param {!CategoryInfo} categoryInfo Object holing + * information for creating a category. + * @return {boolean} True if the category has subcategories. + * @alias Blockly.utils.toolbox.isCategoryCollapsible + * @package + */ + export function isCategoryCollapsible(categoryInfo: CategoryInfo): boolean; + /** + * Parse the provided toolbox tree into a consistent DOM format. + * @param {?Node|?string} toolboxDef DOM tree of blocks, or text representation + * of same. + * @return {?Node} DOM tree of blocks, or null. + * @alias Blockly.utils.toolbox.parseToolboxTree + */ + export function parseToolboxTree(toolboxDef: (Node | (string | null)) | null): Node | null; + import { ToolboxSeparator } from "toolbox/separator"; + import { ToolboxCategory } from "toolbox/category"; +} +declare module "blockly_options" { + /** + * @license + * Copyright 2016 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + /** + * @fileoverview Object that defines user-specified options for the workspace. + */ + /** + * Object that defines user-specified options for the workspace. + * @namespace Blockly.BlocklyOptions + */ + /** + * Blockly options. + * This interface is further described in + * `typings/parts/blockly-interfaces.d.ts`. + * @interface + * @alias Blockly.BlocklyOptions + */ + export class BlocklyOptions { + } +} +declare module "utils/svg_paths" { + /** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + /** + * @fileoverview Methods for creating parts of SVG path strings. See + * developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths + */ + /** + * Methods for creating parts of SVG path strings. See + * @namespace Blockly.utils.svgPaths + */ + /** + * Create a string representing the given x, y pair. It does not matter whether + * the coordinate is relative or absolute. The result has leading + * and trailing spaces, and separates the x and y coordinates with a comma but + * no space. + * @param {number} x The x coordinate. + * @param {number} y The y coordinate. + * @return {string} A string of the format ' x,y ' + * @alias Blockly.utils.svgPaths.point + */ + export function point(x: number, y: number): string; + /** + * Draw a cubic or quadratic curve. See + * developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d#Cubic_B%C3%A9zier_Curve + * These coordinates are unitless and hence in the user coordinate system. + * @param {string} command The command to use. + * Should be one of: c, C, s, S, q, Q. + * @param {!Array} points An array containing all of the points to pass + * to the curve command, in order. The points are represented as strings of + * the format ' x, y '. + * @return {string} A string defining one or more Bezier curves. See the MDN + * documentation for exact format. + * @alias Blockly.utils.svgPaths.curve + */ + export function curve(command: string, points: Array): string; + /** + * Move the cursor to the given position without drawing a line. + * The coordinates are absolute. + * These coordinates are unitless and hence in the user coordinate system. + * See developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths#Line_commands + * @param {number} x The absolute x coordinate. + * @param {number} y The absolute y coordinate. + * @return {string} A string of the format ' M x,y ' + * @alias Blockly.utils.svgPaths.moveTo + */ + export function moveTo(x: number, y: number): string; + /** + * Move the cursor to the given position without drawing a line. + * Coordinates are relative. + * These coordinates are unitless and hence in the user coordinate system. + * See developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths#Line_commands + * @param {number} dx The relative x coordinate. + * @param {number} dy The relative y coordinate. + * @return {string} A string of the format ' m dx,dy ' + * @alias Blockly.utils.svgPaths.moveBy + */ + export function moveBy(dx: number, dy: number): string; + /** + * Draw a line from the current point to the end point, which is the current + * point shifted by dx along the x-axis and dy along the y-axis. + * These coordinates are unitless and hence in the user coordinate system. + * See developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths#Line_commands + * @param {number} dx The relative x coordinate. + * @param {number} dy The relative y coordinate. + * @return {string} A string of the format ' l dx,dy ' + * @alias Blockly.utils.svgPaths.lineTo + */ + export function lineTo(dx: number, dy: number): string; + /** + * Draw multiple lines connecting all of the given points in order. This is + * equivalent to a series of 'l' commands. + * These coordinates are unitless and hence in the user coordinate system. + * See developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths#Line_commands + * @param {!Array} points An array containing all of the points to + * draw lines to, in order. The points are represented as strings of the + * format ' dx,dy '. + * @return {string} A string of the format ' l (dx,dy)+ ' + * @alias Blockly.utils.svgPaths.line + */ + export function line(points: Array): string; + /** + * Draw a horizontal or vertical line. + * The first argument specifies the direction and whether the given position is + * relative or absolute. + * These coordinates are unitless and hence in the user coordinate system. + * See developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d#LineTo_path_commands + * @param {string} command The command to prepend to the coordinate. This + * should be one of: V, v, H, h. + * @param {number} val The coordinate to pass to the command. It may be + * absolute or relative. + * @return {string} A string of the format ' command val ' + * @alias Blockly.utils.svgPaths.lineOnAxis + */ + export function lineOnAxis(command: string, val: number): string; + /** + * Draw an elliptical arc curve. + * These coordinates are unitless and hence in the user coordinate system. + * See developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d#Elliptical_Arc_Curve + * @param {string} command The command string. Either 'a' or 'A'. + * @param {string} flags The flag string. See the MDN documentation for a + * description and examples. + * @param {number} radius The radius of the arc to draw. + * @param {string} point The point to move the cursor to after drawing the arc, + * specified either in absolute or relative coordinates depending on the + * command. + * @return {string} A string of the format 'command radius radius flags point' + * @alias Blockly.utils.svgPaths.arc + */ + export function arc(command: string, flags: string, radius: number, point: string): string; +} +declare module "utils/style" { + /** + * Gets the height and width of an element. + * Similar to Closure's goog.style.getSize + * @param {!Element} element Element to get size of. + * @return {!Size} Object with width/height properties. + * @alias Blockly.utils.style.getSize + */ + export function getSize(element: Element): Size; + /** + * Retrieves a computed style value of a node. It returns empty string if the + * value cannot be computed (which will be the case in Internet Explorer) or + * "none" if the property requested is an SVG one and it has not been + * explicitly set (firefox and webkit). + * + * Copied from Closure's goog.style.getComputedStyle + * + * @param {!Element} element Element to get style of. + * @param {string} property Property to get (camel-case). + * @return {string} Style value. + * @alias Blockly.utils.style.getComputedStyle + */ + export function getComputedStyle(element: Element, property: string): string; + /** + * Gets the cascaded style value of a node, or null if the value cannot be + * computed (only Internet Explorer can do this). + * + * Copied from Closure's goog.style.getCascadedStyle + * + * @param {!Element} element Element to get style of. + * @param {string} style Property to get (camel-case). + * @return {string} Style value. + * @alias Blockly.utils.style.getCascadedStyle + */ + export function getCascadedStyle(element: Element, style: string): string; + /** + * Returns a Coordinate object relative to the top-left of the HTML document. + * Similar to Closure's goog.style.getPageOffset + * @param {!Element} el Element to get the page offset for. + * @return {!Coordinate} The page offset. + * @alias Blockly.utils.style.getPageOffset + */ + export function getPageOffset(el: Element): Coordinate; + /** + * Calculates the viewport coordinates relative to the document. + * Similar to Closure's goog.style.getViewportPageOffset + * @return {!Coordinate} The page offset of the viewport. + * @alias Blockly.utils.style.getViewportPageOffset + */ + export function getViewportPageOffset(): Coordinate; + /** + * Shows or hides an element from the page. Hiding the element is done by + * setting the display property to "none", removing the element from the + * rendering hierarchy so it takes up no space. To show the element, the default + * inherited display property is restored (defined either in stylesheets or by + * the browser's default style rules). + * Copied from Closure's goog.style.getViewportPageOffset + * + * @param {!Element} el Element to show or hide. + * @param {*} isShown True to render the element in its default style, + * false to disable rendering the element. + * @alias Blockly.utils.style.setElementShown + */ + export function setElementShown(el: Element, isShown: any): void; + /** + * Returns true if the element is using right to left (RTL) direction. + * Copied from Closure's goog.style.isRightToLeft + * + * @param {!Element} el The element to test. + * @return {boolean} True for right to left, false for left to right. + * @alias Blockly.utils.style.isRightToLeft + */ + export function isRightToLeft(el: Element): boolean; + /** + * Gets the computed border widths (on all sides) in pixels + * Copied from Closure's goog.style.getBorderBox + * @param {!Element} element The element to get the border widths for. + * @return {!Object} The computed border widths. + * @alias Blockly.utils.style.getBorderBox + */ + export function getBorderBox(element: Element): any; + /** + * Changes the scroll position of `container` with the minimum amount so + * that the content and the borders of the given `element` become visible. + * If the element is bigger than the container, its top left corner will be + * aligned as close to the container's top left corner as possible. + * Copied from Closure's goog.style.scrollIntoContainerView + * + * @param {!Element} element The element to make visible. + * @param {!Element} container The container to scroll. If not set, then the + * document scroll element will be used. + * @param {boolean=} opt_center Whether to center the element in the container. + * Defaults to false. + * @alias Blockly.utils.style.scrollIntoContainerView + */ + export function scrollIntoContainerView(element: Element, container: Element, opt_center?: boolean | undefined): void; + /** + * Calculate the scroll position of `container` with the minimum amount so + * that the content and the borders of the given `element` become visible. + * If the element is bigger than the container, its top left corner will be + * aligned as close to the container's top left corner as possible. + * Copied from Closure's goog.style.getContainerOffsetToScrollInto + * + * @param {!Element} element The element to make visible. + * @param {!Element} container The container to scroll. If not set, then the + * document scroll element will be used. + * @param {boolean=} opt_center Whether to center the element in the container. + * Defaults to false. + * @return {!Coordinate} The new scroll position of the container, + * in form of goog.math.Coordinate(scrollLeft, scrollTop). + * @alias Blockly.utils.style.getContainerOffsetToScrollInto + */ + export function getContainerOffsetToScrollInto(element: Element, container: Element, opt_center?: boolean | undefined): Coordinate; + import { Size } from "utils/size"; + import { Coordinate } from "utils/coordinate"; +} +declare module "utils/rect" { + /** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + /** + * @fileoverview Utility methods for rectangle manipulation. + * These methods are not specific to Blockly, and could be factored out into + * a JavaScript framework such as Closure. + */ + /** + * Utility methods for rectangle manipulation. + * These methods are not specific to Blockly, and could be factored out into + * a JavaScript framework such as Closure. + * @class + */ + /** + * Class for representing rectangular regions. + * @param {number} top Top. + * @param {number} bottom Bottom. + * @param {number} left Left. + * @param {number} right Right. + * @struct + * @constructor + * @alias Blockly.utils.Rect + */ + export class Rect { + constructor(top: any, bottom: any, left: any, right: any); + /** @type {number} */ + top: number; + /** @type {number} */ + bottom: number; + /** @type {number} */ + left: number; + /** @type {number} */ + right: number; + /** + * Tests whether this rectangle contains a x/y coordinate. + * + * @param {number} x The x coordinate to test for containment. + * @param {number} y The y coordinate to test for containment. + * @return {boolean} Whether this rectangle contains given coordinate. + */ + contains(x: number, y: number): boolean; + /** + * Tests whether this rectangle intersects the provided rectangle. + * Assumes that the coordinate system increases going down and left. + * @param {!Rect} other The other rectangle to check for + * intersection with. + * @return {boolean} Whether this rectangle intersects the provided rectangle. + */ + intersects(other: Rect): boolean; + } +} +declare module "utils/svg_math" { + export namespace TEST_ONLY { + export { XY_REGEX }; + export { XY_STYLE_REGEX }; + } + /** + * Return the coordinates of the top-left corner of this element relative to + * its parent. Only for SVG elements and children (e.g. rect, g, path). + * @param {!Element} element SVG element to find the coordinates of. + * @return {!Coordinate} Object with .x and .y properties. + * @alias Blockly.svgMath.getRelativeXY + */ + export function getRelativeXY(element: Element): Coordinate; + /** + * Return the coordinates of the top-left corner of this element relative to + * the div Blockly was injected into. + * @param {!Element} element SVG element to find the coordinates of. If this is + * not a child of the div Blockly was injected into, the behaviour is + * undefined. + * @return {!Coordinate} Object with .x and .y properties. + * @alias Blockly.svgMath.getInjectionDivXY + */ + export function getInjectionDivXY(element: Element): Coordinate; + /** + * Check if 3D transforms are supported by adding an element + * and attempting to set the property. + * @return {boolean} True if 3D transforms are supported. + * @alias Blockly.svgMath.is3dSupported + */ + export function is3dSupported(): boolean; + /** + * Get the position of the current viewport in window coordinates. This takes + * scroll into account. + * @return {!Rect} An object containing window width, height, and + * scroll position in window coordinates. + * @alias Blockly.svgMath.getViewportBBox + * @package + */ + export function getViewportBBox(): Rect; + /** + * Gets the document scroll distance as a coordinate object. + * Copied from Closure's goog.dom.getDocumentScroll. + * @return {!Coordinate} Object with values 'x' and 'y'. + * @alias Blockly.svgMath.getDocumentScroll + */ + export function getDocumentScroll(): Coordinate; + /** + * Converts screen coordinates to workspace coordinates. + * @param {!WorkspaceSvg} ws The workspace to find the coordinates on. + * @param {!Coordinate} screenCoordinates The screen coordinates to + * be converted to workspace coordinates + * @return {!Coordinate} The workspace coordinates. + * @alias Blockly.svgMath.screenToWsCoordinates + */ + export function screenToWsCoordinates(ws: WorkspaceSvg, screenCoordinates: Coordinate): Coordinate; + /** + * Returns the dimensions of the specified SVG image. + * @param {!SVGElement} svg SVG image. + * @return {!Size} Contains width and height properties. + * @deprecated Use workspace.getCachedParentSvgSize. (2021 March 5) + * @alias Blockly.utils.svgMath.svgSize + */ + export function svgSize(svg: SVGElement): Size; + /** + * Static regex to pull the x,y values out of an SVG translate() directive. + * Note that Firefox and IE (9,10) return 'translate(12)' instead of + * 'translate(12, 0)'. + * Note that IE (9,10) returns 'translate(16 8)' instead of 'translate(16, 8)'. + * Note that IE has been reported to return scientific notation (0.123456e-42). + * @type {!RegExp} + */ + const XY_REGEX: RegExp; + /** + * Static regex to pull the x,y values out of a translate() or translate3d() + * style property. + * Accounts for same exceptions as XY_REGEX. + * @type {!RegExp} + */ + const XY_STYLE_REGEX: RegExp; + import { Coordinate } from "utils/coordinate"; + import { Rect } from "utils/rect"; + import { WorkspaceSvg } from "workspace_svg"; + import { Size } from "utils/size"; + export {}; +} +declare module "rendered_connection" { + export class RenderedConnection extends Connection { + /** + * Class for a connection between blocks that may be rendered on screen. + * @param {!BlockSvg} source The block establishing this connection. + * @param {number} type The type of the connection. + * @extends {Connection} + * @constructor + * @alias Blockly.RenderedConnection + */ + constructor(source: BlockSvg, type: number); + /** + * Connection database for connections of this type on the current workspace. + * @const {!ConnectionDB} + * @private + */ + private db_; + /** + * Connection database for connections compatible with this type on the + * current workspace. + * @const {!ConnectionDB} + * @private + */ + private dbOpposite_; + /** + * Workspace units, (0, 0) is top left of block. + * @type {!Coordinate} + * @private + */ + private offsetInBlock_; + /** + * Describes the state of this connection's tracked-ness. + * @type {RenderedConnection.TrackedState} + * @private + */ + private trackedState_; + /** + * Connection this connection connects to. Null if not connected. + * @type {RenderedConnection} + */ + targetConnection: RenderedConnection; + /** + * Dispose of this connection. Remove it from the database (if it is + * tracked) and call the super-function to deal with connected blocks. + * @override + * @package + */ + override dispose(): void; + /** + * Get the source block for this connection. + * @return {!BlockSvg} The source block. + * @override + */ + override getSourceBlock(): BlockSvg; + /** + * Returns the block that this connection connects to. + * @return {?BlockSvg} The connected block or null if none is connected. + * @override + */ + override targetBlock(): BlockSvg | null; + /** + * Returns the distance between this connection and another connection in + * workspace units. + * @param {!Connection} otherConnection The other connection to measure + * the distance to. + * @return {number} The distance between connections, in workspace units. + */ + distanceFrom(otherConnection: Connection): number; + /** + * Move the block(s) belonging to the connection to a point where they don't + * visually interfere with the specified connection. + * @param {!Connection} staticConnection The connection to move away + * from. + * @package + */ + bumpAwayFrom(staticConnection: Connection): void; + /** + * Change the connection's coordinates. + * @param {number} x New absolute x coordinate, in workspace coordinates. + * @param {number} y New absolute y coordinate, in workspace coordinates. + */ + moveTo(x: number, y: number): void; + x: number; + y: number; + /** + * Change the connection's coordinates. + * @param {number} dx Change to x coordinate, in workspace units. + * @param {number} dy Change to y coordinate, in workspace units. + */ + moveBy(dx: number, dy: number): void; + /** + * Move this connection to the location given by its offset within the block and + * the location of the block's top left corner. + * @param {!Coordinate} blockTL The location of the top left + * corner of the block, in workspace coordinates. + */ + moveToOffset(blockTL: Coordinate): void; + /** + * Set the offset of this connection relative to the top left of its block. + * @param {number} x The new relative x, in workspace units. + * @param {number} y The new relative y, in workspace units. + */ + setOffsetInBlock(x: number, y: number): void; + /** + * Get the offset of this connection relative to the top left of its block. + * @return {!Coordinate} The offset of the connection. + * @package + */ + getOffsetInBlock(): Coordinate; + /** + * Move the blocks on either side of this connection right next to each other. + * @package + */ + tighten(): void; + /** + * Find the closest compatible connection to this connection. + * All parameters are in workspace units. + * @param {number} maxLimit The maximum radius to another connection. + * @param {!Coordinate} dxy Offset between this connection's location + * in the database and the current location (as a result of dragging). + * @return {!{connection: ?Connection, radius: number}} Contains two + * properties: 'connection' which is either another connection or null, + * and 'radius' which is the distance. + */ + closest(maxLimit: number, dxy: Coordinate): { + connection: Connection | null; + radius: number; + }; + /** + * Add highlighting around this connection. + */ + highlight(): void; + /** + * Remove the highlighting around this connection. + */ + unhighlight(): void; + /** + * Set whether this connections is tracked in the database or not. + * @param {boolean} doTracking If true, start tracking. If false, stop tracking. + * @package + */ + setTracking(doTracking: boolean): void; + /** + * Stop tracking this connection, as well as all down-stream connections on + * any block attached to this connection. This happens when a block is + * collapsed. + * + * Also closes down-stream icons/bubbles. + * @package + */ + stopTrackingAll(): void; + /** + * Start tracking this connection, as well as all down-stream connections on + * any block attached to this connection. This happens when a block is expanded. + * @return {!Array} List of blocks to render. + */ + startTrackingAll(): Array; + /** + * Check if the two connections can be dragged to connect to each other. + * @param {!Connection} candidate A nearby connection to check. + * @param {number=} maxRadius The maximum radius allowed for connections, in + * workspace units. + * @return {boolean} True if the connection is allowed, false otherwise. + * @deprecated July 2020 + */ + isConnectionAllowed(candidate: Connection, maxRadius?: number | undefined): boolean; + /** + * Behavior after a connection attempt fails. + * Bumps this connection away from the other connection. Called when an + * attempted connection fails. + * @param {!Connection} otherConnection Connection that this connection + * failed to connect to. + * @package + */ + onFailedConnect(otherConnection: Connection): void; + /** + * Disconnect two blocks that are connected by this connection. + * @param {!Block} parentBlock The superior block. + * @param {!Block} childBlock The inferior block. + * @protected + * @override + */ + protected override disconnectInternal_(parentBlock: Block, childBlock: Block): void; + /** + * Respawn the shadow block if there was one connected to the this connection. + * Render/rerender blocks as needed. + * @protected + * @override + */ + protected override respawnShadow_(): void; + /** + * Find all nearby compatible connections to this connection. + * Type checking does not apply, since this function is used for bumping. + * @param {number} maxLimit The maximum radius to another connection, in + * workspace units. + * @return {!Array} List of connections. + * @package + */ + neighbours(maxLimit: number): Array; + /** + * Connect two connections together. This is the connection on the superior + * block. Rerender blocks as needed. + * @param {!Connection} childConnection Connection on inferior block. + * @protected + */ + protected connect_(childConnection: Connection): void; + /** + * Function to be called when this connection's compatible types have changed. + * @protected + */ + protected onCheckChanged_(): void; + } + export namespace RenderedConnection { + namespace TrackedState { + const WILL_TRACK: number; + const UNTRACKED: number; + const TRACKED: number; + } + /** + * Enum for different kinds of tracked states. + * + * WILL_TRACK means that this connection will add itself to + * the db on the next moveTo call it receives. + * + * UNTRACKED means that this connection will not add + * itself to the database until setTracking(true) is explicitly called. + * + * TRACKED means that this connection is currently being tracked. + */ + type TrackedState = number; + } + import { BlockSvg } from "block_svg"; + import { Connection } from "connection"; + import { Coordinate } from "utils/coordinate"; + import { Block } from "block"; +} +declare module "interfaces/i_connection_checker" { + /** + * Class for connection type checking logic. + * @interface + * @alias Blockly.IConnectionChecker + */ + export class IConnectionChecker { + } +} +declare module "constants" { + /** + * * + */ + export type ALIGN = number; + export namespace ALIGN { + const LEFT: number; + const CENTRE: number; + const RIGHT: number; + } + /** + * The language-neutral ID given to the collapsed input. + * @const {string} + * @alias Blockly.constants.COLLAPSED_INPUT_NAME + */ + export const COLLAPSED_INPUT_NAME: "_TEMP_COLLAPSED_INPUT"; + /** + * The language-neutral ID given to the collapsed field. + * @const {string} + * @alias Blockly.constants.COLLAPSED_FIELD_NAME + */ + export const COLLAPSED_FIELD_NAME: "_TEMP_COLLAPSED_FIELD"; +} +declare module "connection_db" { + export class ConnectionDB { + /** + * Initialize a set of connection DBs for a workspace. + * @param {!IConnectionChecker} checker The workspace's + * connection checker, used to decide if connections are valid during a + * drag. + * @return {!Array} Array of databases. + */ + static init(checker: IConnectionChecker): Array; + /** + * Database of connections. + * Connections are stored in order of their vertical component. This way + * connections in an area may be looked up quickly using a binary search. + * @param {!IConnectionChecker} checker The workspace's + * connection type checker, used to decide if connections are valid during a + * drag. + * @constructor + * @alias Blockly.ConnectionDB + */ + constructor(checker: IConnectionChecker); + /** + * Array of connections sorted by y position in workspace units. + * @type {!Array} + * @private + */ + private connections_; + /** + * The workspace's connection type checker, used to decide if connections are + * valid during a drag. + * @type {!IConnectionChecker} + * @private + */ + private connectionChecker_; + /** + * Add a connection to the database. Should not already exist in the database. + * @param {!RenderedConnection} connection The connection to be added. + * @param {number} yPos The y position used to decide where to insert the + * connection. + * @package + */ + addConnection(connection: RenderedConnection, yPos: number): void; + /** + * Finds the index of the given connection. + * + * Starts by doing a binary search to find the approximate location, then + * linearly searches nearby for the exact connection. + * @param {!RenderedConnection} conn The connection to find. + * @param {number} yPos The y position used to find the index of the connection. + * @return {number} The index of the connection, or -1 if the connection was + * not found. + * @private + */ + private findIndexOfConnection_; + /** + * Finds the correct index for the given y position. + * @param {number} yPos The y position used to decide where to + * insert the connection. + * @return {number} The candidate index. + * @private + */ + private calculateIndexForYPos_; + /** + * Remove a connection from the database. Must already exist in DB. + * @param {!RenderedConnection} connection The connection to be removed. + * @param {number} yPos The y position used to find the index of the connection. + * @throws {Error} If the connection cannot be found in the database. + */ + removeConnection(connection: RenderedConnection, yPos: number): void; + /** + * Find all nearby connections to the given connection. + * Type checking does not apply, since this function is used for bumping. + * @param {!RenderedConnection} connection The connection whose + * neighbours should be returned. + * @param {number} maxRadius The maximum radius to another connection. + * @return {!Array} List of connections. + */ + getNeighbours(connection: RenderedConnection, maxRadius: number): Array; + /** + * Is the candidate connection close to the reference connection. + * Extremely fast; only looks at Y distance. + * @param {number} index Index in database of candidate connection. + * @param {number} baseY Reference connection's Y value. + * @param {number} maxRadius The maximum radius to another connection. + * @return {boolean} True if connection is in range. + * @private + */ + private isInYRange_; + /** + * Find the closest compatible connection to this connection. + * @param {!RenderedConnection} conn The connection searching for a compatible + * mate. + * @param {number} maxRadius The maximum radius to another connection. + * @param {!Coordinate} dxy Offset between this connection's + * location in the database and the current location (as a result of + * dragging). + * @return {!{connection: RenderedConnection, radius: number}} + * Contains two properties: 'connection' which is either another + * connection or null, and 'radius' which is the distance. + */ + searchForClosest(conn: RenderedConnection, maxRadius: number, dxy: Coordinate): { + connection: RenderedConnection; + radius: number; + }; + } + import { RenderedConnection } from "rendered_connection"; + import { Coordinate } from "utils/coordinate"; + import { IConnectionChecker } from "interfaces/i_connection_checker"; +} +declare module "interfaces/i_ast_node_location" { + /** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + /** + * @fileoverview The interface for an AST node location. + */ + /** + * The interface for an AST node location. + * @namespace Blockly.IASTNodeLocation + */ + /** + * An AST node location interface. + * @interface + * @alias Blockly.IASTNodeLocation + */ + export class IASTNodeLocation { + } +} +declare module "theme" { + /** + * Class for a theme. + * @param {string} name Theme name. + * @param {!Object=} opt_blockStyles A map + * from style names (strings) to objects with style attributes for blocks. + * @param {!Object=} opt_categoryStyles A + * map from style names (strings) to objects with style attributes for + * categories. + * @param {!Theme.ComponentStyle=} opt_componentStyles A map of Blockly + * component names to style value. + * @constructor + * @alias Blockly.Theme + */ + export class Theme { + /** + * Define a new Blockly theme. + * @param {string} name The name of the theme. + * @param {!Object} themeObj An object containing theme properties. + * @return {!Theme} A new Blockly theme. + */ + static defineTheme(name: string, themeObj: any): Theme; + constructor(name: any, opt_blockStyles: any, opt_categoryStyles: any, opt_componentStyles: any); + /** + * The theme name. This can be used to reference a specific theme in CSS. + * @type {string} + */ + name: string; + /** + * The block styles map. + * @type {!Object} + * @package + */ + blockStyles: { + [x: string]: Theme.BlockStyle; + }; + /** + * The category styles map. + * @type {!Object} + * @package + */ + categoryStyles: { + [x: string]: Theme.CategoryStyle; + }; + /** + * The UI components styles map. + * @type {!Theme.ComponentStyle} + * @package + */ + componentStyles: Theme.ComponentStyle; + /** + * The font style. + * @type {!Theme.FontStyle} + * @package + */ + fontStyle: Theme.FontStyle; + /** + * Whether or not to add a 'hat' on top of all blocks with no previous or + * output connections. + * @type {?boolean} + * @package + */ + startHats: boolean | null; + /** + * Gets the class name that identifies this theme. + * @return {string} The CSS class name. + * @package + */ + getClassName(): string; + /** + * Overrides or adds a style to the blockStyles map. + * @param {string} blockStyleName The name of the block style. + * @param {Theme.BlockStyle} blockStyle The block style. + */ + setBlockStyle(blockStyleName: string, blockStyle: Theme.BlockStyle): void; + /** + * Overrides or adds a style to the categoryStyles map. + * @param {string} categoryStyleName The name of the category style. + * @param {Theme.CategoryStyle} categoryStyle The category style. + */ + setCategoryStyle(categoryStyleName: string, categoryStyle: Theme.CategoryStyle): void; + /** + * Gets the style for a given Blockly UI component. If the style value is a + * string, we attempt to find the value of any named references. + * @param {string} componentName The name of the component. + * @return {?string} The style value. + */ + getComponentStyle(componentName: string): string | null; + /** + * Configure a specific Blockly UI component with a style value. + * @param {string} componentName The name of the component. + * @param {*} styleValue The style value. + */ + setComponentStyle(componentName: string, styleValue: any): void; + /** + * Configure a theme's font style. + * @param {Theme.FontStyle} fontStyle The font style. + */ + setFontStyle(fontStyle: Theme.FontStyle): void; + /** + * Configure a theme's start hats. + * @param {boolean} startHats True if the theme enables start hats, false + * otherwise. + */ + setStartHats(startHats: boolean): void; + } + export namespace Theme { + /** + * A block style. + */ + type BlockStyle = { + colourPrimary: string; + colourSecondary: string; + colourTertiary: string; + hat: string; + }; + /** + * A category style. + */ + type CategoryStyle = { + colour: string; + }; + /** + * A component style. + */ + type ComponentStyle = { + workspaceBackgroundColour: string | null; + toolboxBackgroundColour: string | null; + toolboxForegroundColour: string | null; + flyoutBackgroundColour: string | null; + flyoutForegroundColour: string | null; + flyoutOpacity: number | null; + scrollbarColour: string | null; + scrollbarOpacity: number | null; + insertionMarkerColour: string | null; + insertionMarkerOpacity: number | null; + markerColour: string | null; + cursorColour: string | null; + selectedGlowColour: string | null; + selectedGlowOpacity: number | null; + replacementGlowColour: string | null; + replacementGlowOpacity: number | null; + }; + /** + * A font style. + */ + type FontStyle = { + family: string | null; + weight: string | null; + size: number | null; + }; + } +} +declare module "theme/classic" { + /** + * Classic theme. + * Contains multi-coloured border to create shadow effect. + * @type {Theme} + * @alias Blockly.Themes.Classic + */ + export const Classic: Theme; + import { Theme } from "theme"; +} +declare module "utils/metrics" { + /** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + /** + * @fileoverview Workspace metrics definitions. + */ + /** + * Workspace metrics definitions. + * @namespace Blockly.utils.Metrics + */ + /** + * @record + * @alias Blockly.utils.Metrics + */ + export class Metrics { + /** + * Height of the visible portion of the workspace. + * @type {number} + */ + viewHeight: number; + /** + * Width of the visible portion of the workspace. + * @type {number} + */ + viewWidth: number; + /** + * Height of the content. + * @type {number} + */ + contentHeight: number; + /** + * Width of the content. + * @type {number} + */ + contentWidth: number; + /** + * Height of the scroll area. + * @type {number} + */ + scrollHeight: number; + /** + * Width of the scroll area. + * @type {number} + */ + scrollWidth: number; + /** + * Top-edge of the visible portion of the workspace, relative to the workspace + * origin. + * @type {number} + */ + viewTop: number; + /** + * Left-edge of the visible portion of the workspace, relative to the workspace + * origin. + * @type {number} + */ + viewLeft: number; + /** + * Top-edge of the content, relative to the workspace origin. + * @type {number} + */ + contentTop: number; + /** + * Left-edge of the content relative to the workspace origin. + * @type {number} + */ + contentLeft: number; + /** + * Top-edge of the scroll area, relative to the workspace origin. + * @type {number} + */ + scrollTop: number; + /** + * Left-edge of the scroll area relative to the workspace origin. + * @type {number} + */ + scrollLeft: number; + /** + * Top-edge of the visible portion of the workspace, relative to the blocklyDiv. + * @type {number} + */ + absoluteTop: number; + /** + * Left-edge of the visible portion of the workspace, relative to the + * blocklyDiv. + * @type {number} + */ + absoluteLeft: number; + /** + * Height of the Blockly div (the view + the toolbox, simple of otherwise). + * @type {number} + */ + svgHeight: number; + /** + * Width of the Blockly div (the view + the toolbox, simple or otherwise). + * @type {number} + */ + svgWidth: number; + /** + * Width of the toolbox, if it exists. Otherwise zero. + * @type {number} + */ + toolboxWidth: number; + /** + * Height of the toolbox, if it exists. Otherwise zero. + * @type {number} + */ + toolboxHeight: number; + /** + * Top, bottom, left or right. Use TOOLBOX_AT constants to compare. + * @type {number} + */ + toolboxPosition: number; + /** + * Width of the flyout if it is always open. Otherwise zero. + * @type {number} + */ + flyoutWidth: number; + /** + * Height of the flyout if it is always open. Otherwise zero. + * @type {number} + */ + flyoutHeight: number; + } +} +declare module "options" { + export class Options { + /** + * Parse the user-specified move options, using reasonable defaults where + * behaviour is unspecified. + * @param {!Object} options Dictionary of options. + * @param {boolean} hasCategories Whether the workspace has categories or not. + * @return {!Options.MoveOptions} Normalized move options. + * @private + */ + private static parseMoveOptions_; + /** + * Parse the user-specified zoom options, using reasonable defaults where + * behaviour is unspecified. See zoom documentation: + * https://developers.google.com/blockly/guides/configure/web/zoom + * @param {!Object} options Dictionary of options. + * @return {!Options.ZoomOptions} Normalized zoom options. + * @private + */ + private static parseZoomOptions_; + /** + * Parse the user-specified grid options, using reasonable defaults where + * behaviour is unspecified. See grid documentation: + * https://developers.google.com/blockly/guides/configure/web/grid + * @param {!Object} options Dictionary of options. + * @return {!Options.GridOptions} Normalized grid options. + * @private + */ + private static parseGridOptions_; + /** + * Parse the user-specified theme options, using the classic theme as a default. + * https://developers.google.com/blockly/guides/configure/web/themes + * @param {!Object} options Dictionary of options. + * @return {!Theme} A Blockly Theme. + * @private + */ + private static parseThemeOptions_; + /** + * Parse the user-specified options, using reasonable defaults where behaviour + * is unspecified. + * @param {!BlocklyOptions} options Dictionary of options. + * Specification: + * https://developers.google.com/blockly/guides/get-started/web#configuration + * @constructor + * @alias Blockly.Options + */ + constructor(options: BlocklyOptions); + /** @type {boolean} */ + RTL: boolean; + /** @type {boolean} */ + oneBasedIndex: boolean; + /** @type {boolean} */ + collapse: boolean; + /** @type {boolean} */ + comments: boolean; + /** @type {boolean} */ + disable: boolean; + /** @type {boolean} */ + readOnly: boolean; + /** @type {number} */ + maxBlocks: number; + /** @type {?Object} */ + maxInstances: { + [x: string]: number; + } | null; + /** @type {string} */ + pathToMedia: string; + /** @type {boolean} */ + hasCategories: boolean; + /** @type {!Options.MoveOptions} */ + moveOptions: Options.MoveOptions; + /** @deprecated January 2019 */ + hasScrollbars: boolean; + /** @type {boolean} */ + hasTrashcan: boolean; + /** @type {number} */ + maxTrashcanContents: number; + /** @type {boolean} */ + hasSounds: boolean; + /** @type {boolean} */ + hasCss: boolean; + /** @type {boolean} */ + horizontalLayout: boolean; + /** @type {?toolbox.ToolboxInfo} */ + languageTree: toolbox.ToolboxInfo | null; + /** @type {!Options.GridOptions} */ + gridOptions: Options.GridOptions; + /** @type {!Options.ZoomOptions} */ + zoomOptions: Options.ZoomOptions; + /** @type {!toolbox.Position} */ + toolboxPosition: toolbox.Position; + /** @type {!Theme} */ + theme: Theme; + /** @type {string} */ + renderer: string; + /** @type {?Object} */ + rendererOverrides: any | null; + /** + * The SVG element for the grid pattern. + * Created during injection. + * @type {?SVGElement} + */ + gridPattern: SVGElement | null; + /** + * The parent of the current workspace, or null if there is no parent + * workspace. We can assert that this is of type WorkspaceSvg as opposed to + * Workspace as this is only used in a rendered workspace. + * @type {WorkspaceSvg} + */ + parentWorkspace: WorkspaceSvg; + /** + * Map of plugin type to name of registered plugin or plugin class. + * @type {!Object} + */ + plugins: { + [x: string]: ((new (...args: unknown[]) => unknown) | string); + }; + } + export namespace Options { + /** + * Grid Options. + */ + type GridOptions = { + colour: string; + length: number; + snap: boolean; + spacing: number; + }; + /** + * Move Options. + */ + type MoveOptions = { + drag: boolean; + scrollbars: (boolean | Options.ScrollbarOptions); + wheel: boolean; + }; + /** + * Scrollbar Options. + */ + type ScrollbarOptions = { + horizontal: boolean; + vertical: boolean; + }; + /** + * Zoom Options. + */ + type ZoomOptions = { + controls: boolean; + maxScale: number; + minScale: number; + pinch: boolean; + scaleSpeed: number; + startScale: number; + wheel: boolean; + }; + } + import * as toolbox from "utils/toolbox"; + import { Theme } from "theme"; + import { WorkspaceSvg } from "workspace_svg"; + import { BlocklyOptions } from "blockly_options"; +} +declare module "names" { + export class Names { + /** + * Do the given two entity names refer to the same entity? + * Blockly names are case-insensitive. + * @param {string} name1 First name. + * @param {string} name2 Second name. + * @return {boolean} True if names are the same. + */ + static equals(name1: string, name2: string): boolean; + /** + * Class for a database of entity names (variables, procedures, etc). + * @param {string} reservedWords A comma-separated string of words that are + * illegal for use as names in a language (e.g. 'new,if,this,...'). + * @param {string=} opt_variablePrefix Some languages need a '$' or a namespace + * before all variable names (but not procedure names). + * @constructor + * @alias Blockly.Names + */ + constructor(reservedWords: string, opt_variablePrefix?: string | undefined); + variablePrefix_: string; + reservedDict_: any; + /** + * When JavaScript (or most other languages) is generated, variable 'foo' and + * procedure 'foo' would collide. However, Blockly has no such problems since + * variable get 'foo' and procedure call 'foo' are unambiguous. + * Therefore, Blockly keeps a separate realm name to disambiguate. + * getName('foo', 'VARIABLE') -> 'foo' + * getName('foo', 'PROCEDURE') -> 'foo2' + */ + /** + * Empty the database and start from scratch. The reserved words are kept. + */ + reset(): void; + db_: any; + dbReverse_: any; + variableMap_: VariableMap; + /** + * Set the variable map that maps from variable name to variable object. + * @param {!VariableMap} map The map to track. + */ + setVariableMap(map: VariableMap): void; + /** + * Get the name for a user-defined variable, based on its ID. + * This should only be used for variables of realm + * internalConstants.VARIABLE_CATEGORY_NAME. + * @param {string} id The ID to look up in the variable map. + * @return {?string} The name of the referenced variable, or null if there was + * no variable map or the variable was not found in the map. + * @private + */ + private getNameForUserVariable_; + /** + * Generate names for user variables, but only ones that are being used. + * @param {!Workspace} workspace Workspace to generate variables from. + */ + populateVariables(workspace: Workspace): void; + /** + * Generate names for procedures. + * @param {!Workspace} workspace Workspace to generate procedures from. + */ + populateProcedures(workspace: Workspace): void; + /** + * Convert a Blockly entity name to a legal exportable entity name. + * @param {string} nameOrId The Blockly entity name (no constraints) or + * variable ID. + * @param {string} realm The realm of entity in Blockly + * ('VARIABLE', 'PROCEDURE', 'DEVELOPER_VARIABLE', etc...). + * @return {string} An entity name that is legal in the exported language. + */ + getName(nameOrId: string, realm: string): string; + /** + * Return a list of all known user-created names in a specified realm. + * @param {string} realm The realm of entity in Blockly + * ('VARIABLE', 'PROCEDURE', 'DEVELOPER_VARIABLE', etc...). + * @return {!Array} A list of Blockly entity names (no constraints). + */ + getUserNames(realm: string): Array; + /** + * Convert a Blockly entity name to a legal exportable entity name. + * Ensure that this is a new name not overlapping any previously defined name. + * Also check against list of reserved words for the current language and + * ensure name doesn't collide. + * @param {string} name The Blockly entity name (no constraints). + * @param {string} realm The realm of entity in Blockly + * ('VARIABLE', 'PROCEDURE', 'DEVELOPER_VARIABLE', etc...). + * @return {string} An entity name that is legal in the exported language. + */ + getDistinctName(name: string, realm: string): string; + /** + * Given a proposed entity name, generate a name that conforms to the + * [_A-Za-z][_A-Za-z0-9]* format that most languages consider legal for + * variable and function names. + * @param {string} name Potentially illegal entity name. + * @return {string} Safe entity name. + * @private + */ + private safeName_; + } + export namespace Names { + const DEVELOPER_VARIABLE_TYPE: string; + } + import { VariableMap } from "variable_map"; + import { Workspace } from "workspace"; +} +declare module "events/events_var_base" { + /** + * Abstract class for a variable event. + * @param {!VariableModel=} opt_variable The variable this event + * corresponds to. Undefined for a blank event. + * @extends {Abstract} + * @constructor + * @alias Blockly.Events.VarBase + */ + export class VarBase { + constructor(opt_variable: any); + isBlank: boolean; + /** + * The variable id for the variable this event pertains to. + * @type {string} + */ + varId: string; + /** + * The workspace identifier for this event. + * @type {string} + */ + workspaceId: string; + /** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ + toJson(): any; + /** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ + fromJson(json: any): void; + } +} +declare module "events/events_var_delete" { + /** + * Class for a variable deletion event. + * @param {!VariableModel=} opt_variable The deleted variable. Undefined + * for a blank event. + * @extends {VarBase} + * @constructor + * @alias Blockly.Events.VarDelete + */ + export class VarDelete { + constructor(opt_variable: any); + varType: any; + varName: any; + /** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ + toJson(): any; + /** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ + fromJson(json: any): void; + /** + * Run a variable deletion event. + * @param {boolean} forward True if run forward, false if run backward (undo). + */ + run(forward: boolean): void; + /** + * Type of this event. + * @type {string} + */ + type: string; + } +} +declare module "events/events_var_rename" { + /** + * Class for a variable rename event. + * @param {!VariableModel=} opt_variable The renamed variable. Undefined + * for a blank event. + * @param {string=} newName The new name the variable will be changed to. + * @extends {VarBase} + * @constructor + * @alias Blockly.Events.VarRename + */ + export class VarRename { + constructor(opt_variable: any, newName: any); + oldName: any; + newName: any; + /** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ + toJson(): any; + /** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ + fromJson(json: any): void; + /** + * Run a variable rename event. + * @param {boolean} forward True if run forward, false if run backward (undo). + */ + run(forward: boolean): void; + /** + * Type of this event. + * @type {string} + */ + type: string; + } +} +declare module "variable_map" { + export class VariableMap { + /** + * Class for a variable map. This contains a dictionary data structure with + * variable types as keys and lists of variables as values. The list of + * variables are the type indicated by the key. + * @param {!Workspace} workspace The workspace this map belongs to. + * @constructor + * @alias Blockly.VariableMap + */ + constructor(workspace: Workspace); + /** + * A map from variable type to list of variable names. The lists contain all + * of the named variables in the workspace, including variables + * that are not currently in use. + * @type {!Object>} + * @private + */ + private variableMap_; + /** + * The workspace this map belongs to. + * @type {!Workspace} + */ + workspace: Workspace; + /** + * Clear the variable map. + */ + clear(): void; + /** + * Rename the given variable by updating its name in the variable map. + * @param {!VariableModel} variable Variable to rename. + * @param {string} newName New variable name. + * @package + */ + renameVariable(variable: VariableModel, newName: string): void; + /** + * Rename a variable by updating its name in the variable map. Identify the + * variable to rename with the given ID. + * @param {string} id ID of the variable to rename. + * @param {string} newName New variable name. + */ + renameVariableById(id: string, newName: string): void; + /** + * Update the name of the given variable and refresh all references to it. + * The new name must not conflict with any existing variable names. + * @param {!VariableModel} variable Variable to rename. + * @param {string} newName New variable name. + * @param {!Array} blocks The list of all blocks in the + * workspace. + * @private + */ + private renameVariableAndUses_; + /** + * Update the name of the given variable to the same name as an existing + * variable. The two variables are coalesced into a single variable with the ID + * of the existing variable that was already using newName. + * Refresh all references to the variable. + * @param {!VariableModel} variable Variable to rename. + * @param {string} newName New variable name. + * @param {!VariableModel} conflictVar The variable that was already + * using newName. + * @param {!Array} blocks The list of all blocks in the + * workspace. + * @private + */ + private renameVariableWithConflict_; + /** + * Create a variable with a given name, optional type, and optional ID. + * @param {string} name The name of the variable. This must be unique across + * variables and procedures. + * @param {?string=} opt_type The type of the variable like 'int' or 'string'. + * Does not need to be unique. Field_variable can filter variables based on + * their type. This will default to '' which is a specific type. + * @param {?string=} opt_id The unique ID of the variable. This will default to + * a UUID. + * @return {!VariableModel} The newly created variable. + */ + createVariable(name: string, opt_type?: (string | null) | undefined, opt_id?: (string | null) | undefined): VariableModel; + /** + * Delete a variable. + * @param {!VariableModel} variable Variable to delete. + */ + deleteVariable(variable: VariableModel): void; + /** + * Delete a variables by the passed in ID and all of its uses from this + * workspace. May prompt the user for confirmation. + * @param {string} id ID of variable to delete. + */ + deleteVariableById(id: string): void; + /** + * Deletes a variable and all of its uses from this workspace without asking the + * user for confirmation. + * @param {!VariableModel} variable Variable to delete. + * @param {!Array} uses An array of uses of the variable. + * @package + */ + deleteVariableInternal(variable: VariableModel, uses: Array): void; + /** + * Find the variable by the given name and type and return it. Return null if + * it is not found. + * @param {string} name The name to check for. + * @param {?string=} opt_type The type of the variable. If not provided it + * defaults to the empty string, which is a specific type. + * @return {?VariableModel} The variable with the given name, or null if + * it was not found. + */ + getVariable(name: string, opt_type?: (string | null) | undefined): VariableModel | null; + /** + * Find the variable by the given ID and return it. Return null if not found. + * @param {string} id The ID to check for. + * @return {?VariableModel} The variable with the given ID. + */ + getVariableById(id: string): VariableModel | null; + /** + * Get a list containing all of the variables of a specified type. If type is + * null, return list of variables with empty string type. + * @param {?string} type Type of the variables to find. + * @return {!Array} The sought after variables of the + * passed in type. An empty array if none are found. + */ + getVariablesOfType(type: string | null): Array; + /** + * Return all variable and potential variable types. This list always contains + * the empty string. + * @param {?Workspace} ws The workspace used to look for potential + * variables. This can be different than the workspace stored on this object + * if the passed in ws is a flyout workspace. + * @return {!Array} List of variable types. + * @package + */ + getVariableTypes(ws: Workspace | null): Array; + /** + * Return all variables of all types. + * @return {!Array} List of variable models. + */ + getAllVariables(): Array; + /** + * Returns all of the variable names of all types. + * @return {!Array} All of the variable names of all types. + */ + getAllVariableNames(): Array; + /** + * Find all the uses of a named variable. + * @param {string} id ID of the variable to find. + * @return {!Array} Array of block usages. + */ + getVariableUsesById(id: string): Array; + } + import { Workspace } from "workspace"; + import { VariableModel } from "variable_model"; + import { Block } from "block"; +} +declare module "events/events_comment_create" { + /** + * Class for a comment creation event. + * @param {!WorkspaceComment=} opt_comment The created comment. + * Undefined for a blank event. + * @extends {CommentBase} + * @constructor + * @alias Blockly.Events.CommentCreate + */ + export class CommentCreate { + constructor(opt_comment: any); + xml: any; + /** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ + toJson(): any; + /** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ + fromJson(json: any): void; + /** + * Run a creation event. + * @param {boolean} forward True if run forward, false if run backward (undo). + */ + run(forward: boolean): void; + /** + * Type of this event. + * @type {string} + */ + type: string; + } +} +declare module "events/events_comment_delete" { + /** + * Class for a comment deletion event. + * @param {!WorkspaceComment=} opt_comment The deleted comment. + * Undefined for a blank event. + * @extends {CommentBase} + * @constructor + * @alias Blockly.Events.CommentDelete + */ + export class CommentDelete { + constructor(opt_comment: any); + xml: any; + /** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ + toJson(): any; + /** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ + fromJson(json: any): void; + /** + * Run a creation event. + * @param {boolean} forward True if run forward, false if run backward (undo). + */ + run(forward: boolean): void; + /** + * Type of this event. + * @type {string} + */ + type: string; + } +} +declare module "events/events_comment_base" { + /** + * Abstract class for a comment event. + * @param {!WorkspaceComment=} opt_comment The comment this event + * corresponds to. Undefined for a blank event. + * @extends {AbstractEvents} + * @constructor + * @alias Blockly.Events.CommentBase + */ + export class CommentBase { + /** + * Helper function for Comment[Create|Delete] + * @param {!CommentCreate|!CommentDelete} event + * The event to run. + * @param {boolean} create if True then Create, if False then Delete + */ + static CommentCreateDeleteHelper(event: CommentCreate | CommentDelete, create: boolean): void; + constructor(opt_comment: any); + /** + * Whether or not an event is blank. + * @type {boolean} + */ + isBlank: boolean; + /** + * The ID of the comment this event pertains to. + * @type {string} + */ + commentId: string; + /** + * The workspace identifier for this event. + * @type {string} + */ + workspaceId: string; + /** + * The event group id for the group this event belongs to. Groups define + * events that should be treated as an single action from the user's + * perspective, and should be undone together. + * @type {string} + */ + group: string; + /** + * Sets whether the event should be added to the undo stack. + * @type {boolean} + */ + recordUndo: boolean; + /** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ + toJson(): any; + /** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ + fromJson(json: any): void; + } + import { CommentCreate } from "events/events_comment_create"; + import { CommentDelete } from "events/events_comment_delete"; +} +declare module "events/events_comment_change" { + /** + * Class for a comment change event. + * @param {!WorkspaceComment=} opt_comment The comment that is being + * changed. Undefined for a blank event. + * @param {string=} opt_oldContents Previous contents of the comment. + * @param {string=} opt_newContents New contents of the comment. + * @extends {CommentBase} + * @constructor + * @alias Blockly.Events.CommentChange + */ + export class CommentChange { + constructor(opt_comment: any, opt_oldContents: any, opt_newContents: any); + oldContents_: any; + newContents_: any; + /** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ + toJson(): any; + /** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ + fromJson(json: any): void; + /** + * Does this event record any change of state? + * @return {boolean} False if something changed. + */ + isNull(): boolean; + /** + * Run a change event. + * @param {boolean} forward True if run forward, false if run backward (undo). + */ + run(forward: boolean): void; + /** + * Type of this event. + * @type {string} + */ + type: string; + } +} +declare module "events/events_comment_move" { + /** + * Class for a comment move event. Created before the move. + * @param {!WorkspaceComment=} opt_comment The comment that is being + * moved. Undefined for a blank event. + * @extends {CommentBase} + * @constructor + * @alias Blockly.Events.CommentMove + */ + export class CommentMove { + constructor(opt_comment: any); + /** + * The comment that is being moved. Will be cleared after recording the new + * location. + * @type {WorkspaceComment} + */ + comment_: WorkspaceComment; + /** + * The location before the move, in workspace coordinates. + * @type {!Coordinate} + */ + oldCoordinate_: Coordinate; + /** + * The location after the move, in workspace coordinates. + * @type {Coordinate} + */ + newCoordinate_: Coordinate; + /** + * Record the comment's new location. Called after the move. Can only be + * called once. + */ + recordNew(): void; + /** + * Override the location before the move. Use this if you don't create the + * event until the end of the move, but you know the original location. + * @param {!Coordinate} xy The location before the move, + * in workspace coordinates. + */ + setOldCoordinate(xy: Coordinate): void; + /** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ + toJson(): any; + /** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ + fromJson(json: any): void; + /** + * Does this event record any change of state? + * @return {boolean} False if something changed. + */ + isNull(): boolean; + /** + * Run a move event. + * @param {boolean} forward True if run forward, false if run backward (undo). + */ + run(forward: boolean): void; + /** + * Type of this event. + * @type {string} + */ + type: string; + } + import { WorkspaceComment } from "workspace_comment"; + import { Coordinate } from "utils/coordinate"; +} +declare module "workspace_comment" { + /** + * Class for a workspace comment. + * @param {!Workspace} workspace The block's workspace. + * @param {string} content The content of this workspace comment. + * @param {number} height Height of the comment. + * @param {number} width Width of the comment. + * @param {string=} opt_id Optional ID. Use this ID if provided, otherwise + * create a new ID. + * @constructor + * @alias Blockly.WorkspaceComment + */ + export class WorkspaceComment { + /** + * Fire a create event for the given workspace comment, if comments are enabled. + * @param {!WorkspaceComment} comment The comment that was just created. + * @package + */ + static fireCreateEvent(comment: WorkspaceComment): void; + /** + * Decode an XML comment tag and create a comment on the workspace. + * @param {!Element} xmlComment XML comment element. + * @param {!Workspace} workspace The workspace. + * @return {!WorkspaceComment} The created workspace comment. + * @package + */ + static fromXml(xmlComment: Element, workspace: Workspace): WorkspaceComment; + /** + * Decode an XML comment tag and return the results in an object. + * @param {!Element} xml XML comment element. + * @return {{w: number, h: number, x: number, y: number, content: string}} An + * object containing the id, size, position, and comment string. + * @package + */ + static parseAttributes(xml: Element): { + w: number; + h: number; + x: number; + y: number; + content: string; + }; + constructor(workspace: any, content: any, height: any, width: any, opt_id: any); + /** @type {string} */ + id: string; + /** + * The comment's position in workspace units. (0, 0) is at the workspace's + * origin; scale does not change this value. + * @type {!Coordinate} + * @protected + */ + protected xy_: Coordinate; + /** + * The comment's height in workspace units. Scale does not change this value. + * @type {number} + * @protected + */ + protected height_: number; + /** + * The comment's width in workspace units. Scale does not change this value. + * @type {number} + * @protected + */ + protected width_: number; + /** + * @type {!Workspace} + */ + workspace: Workspace; + /** + * @protected + * @type {boolean} + */ + protected RTL: boolean; + /** + * @type {boolean} + * @private + */ + private deletable_; + /** + * @type {boolean} + * @private + */ + private movable_; + /** + * @type {boolean} + * @private + */ + private editable_; + /** + * @protected + * @type {string} + */ + protected content_: string; + /** + * Whether this comment has been disposed. + * @protected + * @type {boolean} + */ + protected disposed_: boolean; + /** + * @package + * @type {boolean} + */ + isComment: boolean; + /** + * Dispose of this comment. + * @package + */ + dispose(): void; + /** + * Get comment height. + * @return {number} Comment height. + * @package + */ + getHeight(): number; + /** + * Set comment height. + * @param {number} height Comment height. + * @package + */ + setHeight(height: number): void; + /** + * Get comment width. + * @return {number} Comment width. + * @package + */ + getWidth(): number; + /** + * Set comment width. + * @param {number} width comment width. + * @package + */ + setWidth(width: number): void; + /** + * Get stored location. + * @return {!Coordinate} The comment's stored location. + * This is not valid if the comment is currently being dragged. + * @package + */ + getXY(): Coordinate; + /** + * Move a comment by a relative offset. + * @param {number} dx Horizontal offset, in workspace units. + * @param {number} dy Vertical offset, in workspace units. + * @package + */ + moveBy(dx: number, dy: number): void; + /** + * Get whether this comment is deletable or not. + * @return {boolean} True if deletable. + * @package + */ + isDeletable(): boolean; + /** + * Set whether this comment is deletable or not. + * @param {boolean} deletable True if deletable. + * @package + */ + setDeletable(deletable: boolean): void; + /** + * Get whether this comment is movable or not. + * @return {boolean} True if movable. + * @package + */ + isMovable(): boolean; + /** + * Set whether this comment is movable or not. + * @param {boolean} movable True if movable. + * @package + */ + setMovable(movable: boolean): void; + /** + * Get whether this comment is editable or not. + * @return {boolean} True if editable. + */ + isEditable(): boolean; + /** + * Set whether this comment is editable or not. + * @param {boolean} editable True if editable. + */ + setEditable(editable: boolean): void; + /** + * Returns this comment's text. + * @return {string} Comment text. + * @package + */ + getContent(): string; + /** + * Set this comment's content. + * @param {string} content Comment content. + * @package + */ + setContent(content: string): void; + /** + * Encode a comment subtree as XML with XY coordinates. + * @param {boolean=} opt_noId True if the encoder should skip the comment ID. + * @return {!Element} Tree of XML elements. + * @package + */ + toXmlWithXY(opt_noId?: boolean | undefined): Element; + /** + * Encode a comment subtree as XML, but don't serialize the XY coordinates. + * This method avoids some expensive metrics-related calls that are made in + * toXmlWithXY(). + * @param {boolean=} opt_noId True if the encoder should skip the comment ID. + * @return {!Element} Tree of XML elements. + * @package + */ + toXml(opt_noId?: boolean | undefined): Element; + } + import { Coordinate } from "utils/coordinate"; + import { Workspace } from "workspace"; +} +declare module "connection_checker" { + /** + * Class for connection type checking logic. + * @implements {IConnectionChecker} + * @constructor + * @alias Blockly.ConnectionChecker + */ + export class ConnectionChecker implements IConnectionChecker { + /** + * Check whether the current connection can connect with the target + * connection. + * @param {Connection} a Connection to check compatibility with. + * @param {Connection} b Connection to check compatibility with. + * @param {boolean} isDragging True if the connection is being made by dragging + * a block. + * @param {number=} opt_distance The max allowable distance between the + * connections for drag checks. + * @return {boolean} Whether the connection is legal. + * @public + */ + public canConnect(a: Connection, b: Connection, isDragging: boolean, opt_distance?: number | undefined): boolean; + /** + * Checks whether the current connection can connect with the target + * connection, and return an error code if there are problems. + * @param {Connection} a Connection to check compatibility with. + * @param {Connection} b Connection to check compatibility with. + * @param {boolean} isDragging True if the connection is being made by dragging + * a block. + * @param {number=} opt_distance The max allowable distance between the + * connections for drag checks. + * @return {number} Connection.CAN_CONNECT if the connection is legal, + * an error code otherwise. + * @public + */ + public canConnectWithReason(a: Connection, b: Connection, isDragging: boolean, opt_distance?: number | undefined): number; + /** + * Helper method that translates a connection error code into a string. + * @param {number} errorCode The error code. + * @param {Connection} a One of the two connections being checked. + * @param {Connection} b The second of the two connections being + * checked. + * @return {string} A developer-readable error string. + * @public + */ + public getErrorMessage(errorCode: number, a: Connection, b: Connection): string; + /** + * Check that connecting the given connections is safe, meaning that it would + * not break any of Blockly's basic assumptions (e.g. no self connections). + * @param {Connection} a The first of the connections to check. + * @param {Connection} b The second of the connections to check. + * @return {number} An enum with the reason this connection is safe or unsafe. + * @public + */ + public doSafetyChecks(a: Connection, b: Connection): number; + /** + * Check whether this connection is compatible with another connection with + * respect to the value type system. E.g. square_root("Hello") is not + * compatible. + * @param {!Connection} a Connection to compare. + * @param {!Connection} b Connection to compare against. + * @return {boolean} True if the connections share a type. + * @public + */ + public doTypeChecks(a: Connection, b: Connection): boolean; + /** + * Check whether this connection can be made by dragging. + * @param {!RenderedConnection} a Connection to compare. + * @param {!RenderedConnection} b Connection to compare against. + * @param {number} distance The maximum allowable distance between connections. + * @return {boolean} True if the connection is allowed during a drag. + * @public + */ + public doDragChecks(a: RenderedConnection, b: RenderedConnection, distance: number): boolean; + /** + * Helper function for drag checking. + * @param {!Connection} a The connection to check, which must be a + * statement input or next connection. + * @param {!Connection} b A nearby connection to check, which + * must be a previous connection. + * @return {boolean} True if the connection is allowed, false otherwise. + * @protected + */ + protected canConnectToPrevious_(a: Connection, b: Connection): boolean; + } + import { IConnectionChecker } from "interfaces/i_connection_checker"; + import { Connection } from "connection"; + import { RenderedConnection } from "rendered_connection"; +} +declare module "workspace" { + export class Workspace { + /** + * Find the workspace with the specified ID. + * @param {string} id ID of workspace to find. + * @return {?Workspace} The sought after workspace or null if not found. + */ + static getById(id: string): Workspace | null; + /** + * Find all workspaces. + * @return {!Array} Array of workspaces. + */ + static getAll(): Array; + /** + * Class for a workspace. This is a data structure that contains blocks. + * There is no UI, and can be created headlessly. + * @param {!Options=} opt_options Dictionary of options. + * @constructor + * @implements {IASTNodeLocation} + * @alias Blockly.Workspace + */ + constructor(opt_options?: Options | undefined); + /** @type {string} */ + id: string; + /** @type {!Options} */ + options: Options; + /** @type {boolean} */ + RTL: boolean; + /** @type {boolean} */ + horizontalLayout: boolean; + /** @type {toolbox.Position} */ + toolboxPosition: toolbox.Position; + /** + * An object that encapsulates logic for safety, type, and dragging checks. + * @type {!IConnectionChecker} + */ + connectionChecker: IConnectionChecker; + /** + * @type {!Array} + * @private + */ + private topBlocks_; + /** + * @type {!Array} + * @private + */ + private topComments_; + /** + * @type {!Object} + * @private + */ + private commentDB_; + /** + * @type {!Array} + * @private + */ + private listeners_; + /** + * @type {!Array} + * @protected + */ + protected undoStack_: Array; + /** + * @type {!Array} + * @protected + */ + protected redoStack_: Array; + /** + * @type {!Object} + * @private + */ + private blockDB_; + /** + * @type {!Object} + * @private + */ + private typedBlocksDB_; + /** + * A map from variable type to list of variable names. The lists contain all + * of the named variables in the workspace, including variables + * that are not currently in use. + * @type {!VariableMap} + * @private + */ + private variableMap_; + /** + * Blocks in the flyout can refer to variables that don't exist in the main + * workspace. For instance, the "get item in list" block refers to an "item" + * variable regardless of whether the variable has been created yet. + * A FieldVariable must always refer to a VariableModel. We reconcile + * these by tracking "potential" variables in the flyout. These variables + * become real when references to them are dragged into the main workspace. + * @type {?VariableMap} + * @private + */ + private potentialVariableMap_; + /** + * Dispose of this workspace. + * Unlink from all DOM elements to prevent memory leaks. + * @suppress {checkTypes} + */ + dispose(): void; + /** + * Compare function for sorting objects (blocks, comments, etc) by position; + * top to bottom (with slight LTR or RTL bias). + * @param {!Block | !WorkspaceComment} a The first object to + * compare. + * @param {!Block | !WorkspaceComment} b The second object to + * compare. + * @return {number} The comparison value. This tells Array.sort() how to change + * object a's index. + * @private + */ + private sortObjects_; + /** + * Adds a block to the list of top blocks. + * @param {!Block} block Block to add. + */ + addTopBlock(block: Block): void; + /** + * Removes a block from the list of top blocks. + * @param {!Block} block Block to remove. + */ + removeTopBlock(block: Block): void; + /** + * Finds the top-level blocks and returns them. Blocks are optionally sorted + * by position; top to bottom (with slight LTR or RTL bias). + * @param {boolean} ordered Sort the list if true. + * @return {!Array} The top-level block objects. + */ + getTopBlocks(ordered: boolean): Array; + /** + * Add a block to the list of blocks keyed by type. + * @param {!Block} block Block to add. + */ + addTypedBlock(block: Block): void; + /** + * Remove a block from the list of blocks keyed by type. + * @param {!Block} block Block to remove. + */ + removeTypedBlock(block: Block): void; + /** + * Finds the blocks with the associated type and returns them. Blocks are + * optionally sorted by position; top to bottom (with slight LTR or RTL bias). + * @param {string} type The type of block to search for. + * @param {boolean} ordered Sort the list if true. + * @return {!Array} The blocks of the given type. + */ + getBlocksByType(type: string, ordered: boolean): Array; + /** + * Adds a comment to the list of top comments. + * @param {!WorkspaceComment} comment comment to add. + * @package + */ + addTopComment(comment: WorkspaceComment): void; + /** + * Removes a comment from the list of top comments. + * @param {!WorkspaceComment} comment comment to remove. + * @package + */ + removeTopComment(comment: WorkspaceComment): void; + /** + * Finds the top-level comments and returns them. Comments are optionally + * sorted by position; top to bottom (with slight LTR or RTL bias). + * @param {boolean} ordered Sort the list if true. + * @return {!Array} The top-level comment objects. + * @package + */ + getTopComments(ordered: boolean): Array; + /** + * Find all blocks in workspace. Blocks are optionally sorted + * by position; top to bottom (with slight LTR or RTL bias). + * @param {boolean} ordered Sort the list if true. + * @return {!Array} Array of blocks. + */ + getAllBlocks(ordered: boolean): Array; + /** + * Dispose of all blocks and comments in workspace. + */ + clear(): void; + isClearing: boolean; + /** + * Rename a variable by updating its name in the variable map. Identify the + * variable to rename with the given ID. + * @param {string} id ID of the variable to rename. + * @param {string} newName New variable name. + */ + renameVariableById(id: string, newName: string): void; + /** + * Create a variable with a given name, optional type, and optional ID. + * @param {string} name The name of the variable. This must be unique across + * variables and procedures. + * @param {?string=} opt_type The type of the variable like 'int' or 'string'. + * Does not need to be unique. Field_variable can filter variables based on + * their type. This will default to '' which is a specific type. + * @param {?string=} opt_id The unique ID of the variable. This will default to + * a UUID. + * @return {!VariableModel} The newly created variable. + */ + createVariable(name: string, opt_type?: (string | null) | undefined, opt_id?: (string | null) | undefined): VariableModel; + /** + * Find all the uses of the given variable, which is identified by ID. + * @param {string} id ID of the variable to find. + * @return {!Array} Array of block usages. + */ + getVariableUsesById(id: string): Array; + /** + * Delete a variables by the passed in ID and all of its uses from this + * workspace. May prompt the user for confirmation. + * @param {string} id ID of variable to delete. + */ + deleteVariableById(id: string): void; + /** + * Find the variable by the given name and return it. Return null if not found. + * @param {string} name The name to check for. + * @param {string=} opt_type The type of the variable. If not provided it + * defaults to the empty string, which is a specific type. + * @return {?VariableModel} The variable with the given name. + */ + getVariable(name: string, opt_type?: string | undefined): VariableModel | null; + /** + * Find the variable by the given ID and return it. Return null if not found. + * @param {string} id The ID to check for. + * @return {?VariableModel} The variable with the given ID. + */ + getVariableById(id: string): VariableModel | null; + /** + * Find the variable with the specified type. If type is null, return list of + * variables with empty string type. + * @param {?string} type Type of the variables to find. + * @return {!Array} The sought after variables of the + * passed in type. An empty array if none are found. + */ + getVariablesOfType(type: string | null): Array; + /** + * Return all variable types. + * @return {!Array} List of variable types. + * @package + */ + getVariableTypes(): Array; + /** + * Return all variables of all types. + * @return {!Array} List of variable models. + */ + getAllVariables(): Array; + /** + * Returns all variable names of all types. + * @return {!Array} List of all variable names of all types. + */ + getAllVariableNames(): Array; + /** + * Returns the horizontal offset of the workspace. + * Intended for LTR/RTL compatibility in XML. + * Not relevant for a headless workspace. + * @return {number} Width. + */ + getWidth(): number; + /** + * Obtain a newly created block. + * @param {!string} prototypeName Name of the language object containing + * type-specific functions for this block. + * @param {string=} opt_id Optional ID. Use this ID if provided, otherwise + * create a new ID. + * @return {!Block} The created block. + */ + newBlock(prototypeName: string, opt_id?: string | undefined): Block; + /** + * The number of blocks that may be added to the workspace before reaching + * the maxBlocks. + * @return {number} Number of blocks left. + */ + remainingCapacity(): number; + /** + * The number of blocks of the given type that may be added to the workspace + * before reaching the maxInstances allowed for that type. + * @param {string} type Type of block to return capacity for. + * @return {number} Number of blocks of type left. + */ + remainingCapacityOfType(type: string): number; + /** + * Check if there is remaining capacity for blocks of the given counts to be + * created. If the total number of blocks represented by the map is more than + * the total remaining capacity, it returns false. If a type count is more + * than the remaining capacity for that type, it returns false. + * @param {!Object} typeCountsMap A map of types to counts (usually representing + * blocks to be created). + * @return {boolean} True if there is capacity for the given map, + * false otherwise. + */ + isCapacityAvailable(typeCountsMap: any): boolean; + /** + * Checks if the workspace has any limits on the maximum number of blocks, + * or the maximum number of blocks of specific types. + * @return {boolean} True if it has block limits, false otherwise. + */ + hasBlockLimits(): boolean; + /** + * Gets the undo stack for workplace. + * @return {!Array} undo stack + * @package + */ + getUndoStack(): Array; + /** + * Gets the redo stack for workplace. + * @return {!Array} redo stack + * @package + */ + getRedoStack(): Array; + /** + * Undo or redo the previous action. + * @param {boolean} redo False if undo, true if redo. + */ + undo(redo: boolean): void; + /** + * Clear the undo/redo stacks. + */ + clearUndo(): void; + /** + * When something in this workspace changes, call a function. + * Note that there may be a few recent events already on the stack. Thus the + * new change listener might be called with events that occurred a few + * milliseconds before the change listener was added. + * @param {!Function} func Function to call. + * @return {!Function} Obsolete return value, ignore. + */ + addChangeListener(func: Function): Function; + /** + * Stop listening for this workspace's changes. + * @param {!Function} func Function to stop calling. + */ + removeChangeListener(func: Function): void; + /** + * Fire a change event. + * @param {!Abstract} event Event to fire. + */ + fireChangeListener(event: typeof Abstract): void; + /** + * Find the block on this workspace with the specified ID. + * @param {string} id ID of block to find. + * @return {?Block} The sought after block, or null if not found. + */ + getBlockById(id: string): Block | null; + /** + * Set a block on this workspace with the specified ID. + * @param {string} id ID of block to set. + * @param {Block} block The block to set. + * @package + */ + setBlockById(id: string, block: Block): void; + /** + * Delete a block off this workspace with the specified ID. + * @param {string} id ID of block to delete. + * @package + */ + removeBlockById(id: string): void; + /** + * Find the comment on this workspace with the specified ID. + * @param {string} id ID of comment to find. + * @return {?WorkspaceComment} The sought after comment, or null if not + * found. + * @package + */ + getCommentById(id: string): WorkspaceComment | null; + /** + * Checks whether all value and statement inputs in the workspace are filled + * with blocks. + * @param {boolean=} opt_shadowBlocksAreFilled An optional argument controlling + * whether shadow blocks are counted as filled. Defaults to true. + * @return {boolean} True if all inputs are filled, false otherwise. + */ + allInputsFilled(opt_shadowBlocksAreFilled?: boolean | undefined): boolean; + /** + * Return the variable map that contains "potential" variables. + * These exist in the flyout but not in the workspace. + * @return {?VariableMap} The potential variable map. + * @package + */ + getPotentialVariableMap(): VariableMap | null; + /** + * Create and store the potential variable map for this workspace. + * @package + */ + createPotentialVariableMap(): void; + /** + * Return the map of all variables on the workspace. + * @return {!VariableMap} The variable map. + */ + getVariableMap(): VariableMap; + /** + * Set the map of all variables on the workspace. + * @param {!VariableMap} variableMap The variable map. + * @package + */ + setVariableMap(variableMap: VariableMap): void; + /** + * Returns `true` if the workspace is visible and `false` if it's headless. + * @type {boolean} + */ + rendered: boolean; + /** + * Maximum number of undo events in stack. `0` turns off undo, `Infinity` sets + * it to unlimited. + * @type {number} + */ + MAX_UNDO: number; + /** + * Set of databases for rapid lookup of connection locations. + * @type {Array} + */ + connectionDBList: Array; + } + export namespace Workspace { + const SCAN_ANGLE: number; + } + import { Options } from "options"; + import * as toolbox from "utils/toolbox"; + import { IConnectionChecker } from "interfaces/i_connection_checker"; + import * as Abstract from "events/events_abstract"; + import { Block } from "block"; + import { WorkspaceComment } from "workspace_comment"; + import { VariableModel } from "variable_model"; + import { VariableMap } from "variable_map"; + import { ConnectionDB } from "connection_db"; +} +declare module "events/events_var_create" { + /** + * Class for a variable creation event. + * @param {!VariableModel=} opt_variable The created variable. Undefined + * for a blank event. + * @extends {VarBase} + * @constructor + * @alias Blockly.Events.VarCreate + */ + export class VarCreate { + constructor(opt_variable: any); + varType: any; + varName: any; + /** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ + toJson(): any; + /** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ + fromJson(json: any): void; + /** + * Run a variable creation event. + * @param {boolean} forward True if run forward, false if run backward (undo). + */ + run(forward: boolean): void; + /** + * Type of this event. + * @type {string} + */ + type: string; + } +} +declare module "variable_model" { + /** + * Class for a variable model. + * Holds information for the variable including name, ID, and type. + * @param {!Workspace} workspace The variable's workspace. + * @param {string} name The name of the variable. This is the user-visible name + * (e.g. 'my var' or '私の変数'), not the generated name. + * @param {string=} opt_type The type of the variable like 'int' or 'string'. + * Does not need to be unique. Field_variable can filter variables based on + * their type. This will default to '' which is a specific type. + * @param {string=} opt_id The unique ID of the variable. This will default to + * a UUID. + * @see {Blockly.FieldVariable} + * @constructor + * @alias Blockly.VariableModel + */ + export class VariableModel { + /** + * A custom compare function for the VariableModel objects. + * @param {VariableModel} var1 First variable to compare. + * @param {VariableModel} var2 Second variable to compare. + * @return {number} -1 if name of var1 is less than name of var2, 0 if equal, + * and 1 if greater. + * @package + */ + static compareByName(var1: VariableModel, var2: VariableModel): number; + constructor(workspace: any, name: any, opt_type: any, opt_id: any); + /** + * The workspace the variable is in. + * @type {!Workspace} + */ + workspace: Workspace; + /** + * The name of the variable, typically defined by the user. It may be + * changed by the user. + * @type {string} + */ + name: string; + /** + * The type of the variable, such as 'int' or 'sound_effect'. This may be + * used to build a list of variables of a specific type. By default this is + * the empty string '', which is a specific type. + * @see {Blockly.FieldVariable} + * @type {string} + */ + type: string; + /** + * A unique ID for the variable. This should be defined at creation and + * not change, even if the name changes. In most cases this should be a + * UUID. + * @type {string} + * @private + */ + private id_; + /** + * @return {string} The ID for the variable. + */ + getId(): string; + } + import { Workspace } from "workspace"; +} +declare module "variables" { + /** + * Find all user-created variables that are in use in the workspace. + * For use by generators. + * To get a list of all variables on a workspace, including unused variables, + * call Workspace.getAllVariables. + * @param {!Workspace} ws The workspace to search for variables. + * @return {!Array} Array of variable models. + * @alias Blockly.Variables.allUsedVarModels + */ + export function allUsedVarModels(ws: Workspace): Array; + /** + * Find all developer variables used by blocks in the workspace. + * Developer variables are never shown to the user, but are declared as global + * variables in the generated code. + * To declare developer variables, define the getDeveloperVariables function on + * your block and return a list of variable names. + * For use by generators. + * @param {!Workspace} workspace The workspace to search. + * @return {!Array} A list of non-duplicated variable names. + * @alias Blockly.Variables.allDeveloperVariables + */ + export function allDeveloperVariables(workspace: Workspace): Array; + /** + * Construct the elements (blocks and button) required by the flyout for the + * variable category. + * @param {!Workspace} workspace The workspace containing variables. + * @return {!Array} Array of XML elements. + * @alias Blockly.Variables.flyoutCategory + */ + export function flyoutCategory(workspace: Workspace): Array; + /** + * Construct the blocks required by the flyout for the variable category. + * @param {!Workspace} workspace The workspace containing variables. + * @return {!Array} Array of XML block elements. + * @alias Blockly.Variables.flyoutCategoryBlocks + */ + export function flyoutCategoryBlocks(workspace: Workspace): Array; + /** + * @alias Blockly.Variables.VAR_LETTER_OPTIONS + */ + export const VAR_LETTER_OPTIONS: "ijkmnopqrstuvwxyzabcdefgh"; + /** + * Return a new variable name that is not yet being used. This will try to + * generate single letter variable names in the range 'i' to 'z' to start with. + * If no unique name is located it will try 'i' to 'z', 'a' to 'h', + * then 'i2' to 'z2' etc. Skip 'l'. + * @param {!Workspace} workspace The workspace to be unique in. + * @return {string} New variable name. + * @alias Blockly.Variables.generateUniqueName + */ + export function generateUniqueName(workspace: Workspace): string; + /** + * Returns a unique name that is not present in the usedNames array. This + * will try to generate single letter names in the range a -> z (skip l). It + * will start with the character passed to startChar. + * @param {string} startChar The character to start the search at. + * @param {!Array} usedNames A list of all of the used names. + * @return {string} A unique name that is not present in the usedNames array. + * @alias Blockly.Variables.generateUniqueNameFromOptions + */ + export function generateUniqueNameFromOptions(startChar: string, usedNames: Array): string; + /** + * Handles "Create Variable" button in the default variables toolbox category. + * It will prompt the user for a variable name, including re-prompts if a name + * is already in use among the workspace's variables. + * + * Custom button handlers can delegate to this function, allowing variables + * types and after-creation processing. More complex customization (e.g., + * prompting for variable type) is beyond the scope of this function. + * + * @param {!Workspace} workspace The workspace on which to create the + * variable. + * @param {function(?string=)=} opt_callback A callback. It will be passed an + * acceptable new variable name, or null if change is to be aborted (cancel + * button), or undefined if an existing variable was chosen. + * @param {string=} opt_type The type of the variable like 'int', 'string', or + * ''. This will default to '', which is a specific type. + * @alias Blockly.Variables.createVariableButtonHandler + */ + export function createVariableButtonHandler(workspace: Workspace, opt_callback?: ((arg0: (string | null) | undefined) => any) | undefined, opt_type?: string | undefined): void; + /** + * Opens a prompt that allows the user to enter a new name for a variable. + * Triggers a rename if the new name is valid. Or re-prompts if there is a + * collision. + * @param {!Workspace} workspace The workspace on which to rename the + * variable. + * @param {!VariableModel} variable Variable to rename. + * @param {function(?string=)=} opt_callback A callback. It will + * be passed an acceptable new variable name, or null if change is to be + * aborted (cancel button), or undefined if an existing variable was chosen. + * @alias Blockly.Variables.renameVariable + */ + export function renameVariable(workspace: Workspace, variable: VariableModel, opt_callback?: ((arg0: (string | null) | undefined) => any) | undefined): void; + /** + * Prompt the user for a new variable name. + * @param {string} promptText The string of the prompt. + * @param {string} defaultText The default value to show in the prompt's field. + * @param {function(?string)} callback A callback. It will return the new + * variable name, or null if the user picked something illegal. + * @alias Blockly.Variables.promptName + */ + export function promptName(promptText: string, defaultText: string, callback: (arg0: string | null) => any): void; + /** + * Check whether there exists a variable with the given name of any type. + * @param {string} name The name to search for. + * @param {!Workspace} workspace The workspace to search for the + * variable. + * @return {?VariableModel} The variable with the given name, + * or null if none was found. + * @alias Blockly.Variables.nameUsedWithAnyType + */ + export function nameUsedWithAnyType(name: string, workspace: Workspace): VariableModel | null; + /** + * Generate DOM objects representing a variable field. + * @param {!VariableModel} variableModel The variable model to + * represent. + * @return {?Element} The generated DOM. + * @alias Blockly.Variables.generateVariableFieldDom + */ + export function generateVariableFieldDom(variableModel: VariableModel): Element | null; + /** + * Helper function to look up or create a variable on the given workspace. + * If no variable exists, creates and returns it. + * @param {!Workspace} workspace The workspace to search for the + * variable. It may be a flyout workspace or main workspace. + * @param {?string} id The ID to use to look up or create the variable, or null. + * @param {string=} opt_name The string to use to look up or create the + * variable. + * @param {string=} opt_type The type to use to look up or create the variable. + * @return {!VariableModel} The variable corresponding to the given ID + * or name + type combination. + * @alias Blockly.Variables.getOrCreateVariablePackage + */ + export function getOrCreateVariablePackage(workspace: Workspace, id: string | null, opt_name?: string | undefined, opt_type?: string | undefined): VariableModel; + /** + * Look up a variable on the given workspace. + * Always looks in the main workspace before looking in the flyout workspace. + * Always prefers lookup by ID to lookup by name + type. + * @param {!Workspace} workspace The workspace to search for the + * variable. It may be a flyout workspace or main workspace. + * @param {?string} id The ID to use to look up the variable, or null. + * @param {string=} opt_name The string to use to look up the variable. + * Only used if lookup by ID fails. + * @param {string=} opt_type The type to use to look up the variable. + * Only used if lookup by ID fails. + * @return {?VariableModel} The variable corresponding to the given ID + * or name + type combination, or null if not found. + * @alias Blockly.Variables.getVariable + */ + export function getVariable(workspace: Workspace, id: string | null, opt_name?: string | undefined, opt_type?: string | undefined): VariableModel | null; + /** + * Helper function to get the list of variables that have been added to the + * workspace after adding a new block, using the given list of variables that + * were in the workspace before the new block was added. + * @param {!Workspace} workspace The workspace to inspect. + * @param {!Array} originalVariables The array of + * variables that existed in the workspace before adding the new block. + * @return {!Array} The new array of variables that + * were freshly added to the workspace after creating the new block, + * or [] if no new variables were added to the workspace. + * @alias Blockly.Variables.getAddedVariables + * @package + */ + export function getAddedVariables(workspace: Workspace, originalVariables: Array): Array; + import { Workspace } from "workspace"; + import { VariableModel } from "variable_model"; +} +declare module "events/events_block_base" { + /** + * Abstract class for a block event. + * @param {!Block=} opt_block The block this event corresponds to. + * Undefined for a blank event. + * @extends {Abstract} + * @constructor + * @alias Blockly.Events.BlockBase + */ + export class BlockBase { + constructor(opt_block: any); + isBlank: boolean; + /** + * The block ID for the block this event pertains to + * @type {string} + */ + blockId: string; + /** + * The workspace identifier for this event. + * @type {string} + */ + workspaceId: string; + /** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ + toJson(): any; + /** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ + fromJson(json: any): void; + } +} +declare module "events/events_block_change" { + export class BlockChange { + /** + * Returns the extra state of the given block (either as XML or a JSO, depending + * on the block's definition). + * @param {!BlockSvg} block The block to get the extra state of. + * @return {string} A stringified version of the extra state of the given block. + * @package + */ + static getExtraBlockState_(block: BlockSvg): string; + /** + * Class for a block change event. + * @param {!Block=} opt_block The changed block. Undefined for a blank + * event. + * @param {string=} opt_element One of 'field', 'comment', 'disabled', etc. + * @param {?string=} opt_name Name of input or field affected, or null. + * @param {*=} opt_oldValue Previous value of element. + * @param {*=} opt_newValue New value of element. + * @extends {BlockBase} + * @constructor + * @alias Blockly.Events.BlockChange + */ + constructor(opt_block?: Block | undefined, opt_element?: string | undefined, opt_name?: (string | null) | undefined, opt_oldValue?: any | undefined, opt_newValue?: any | undefined); + element: string; + name: string; + oldValue: any; + newValue: any; + /** + * Encode the event as JSON. + * @return {!Object} JSON representation. + */ + toJson(): any; + /** + * Decode the JSON event. + * @param {!Object} json JSON representation. + */ + fromJson(json: any): void; + /** + * Does this event record any change of state? + * @return {boolean} False if something changed. + */ + isNull(): boolean; + /** + * Run a change event. + * @param {boolean} forward True if run forward, false if run backward (undo). + */ + run(forward: boolean): void; + /** + * Type of this event. + * @type {string} + */ + type: string; + } + import { BlockSvg } from "block_svg"; + import { Block } from "block"; +} +declare module "procedures" { + /** + * The default argument for a procedures_mutatorarg block. + * @type {string} + * @alias Blockly.Procedures.DEFAULT_ARG + */ + export const DEFAULT_ARG: string; + /** + * Procedure block type. + */ + export type ProcedureBlock = { + getProcedureCall: () => string; + renameProcedure: (arg0: string, arg1: string) => any; + getProcedureDef: () => any[]; + }; + /** + * Procedure block type. + * @typedef {{ + * getProcedureCall: function():string, + * renameProcedure: function(string,string), + * getProcedureDef: function():!Array + * }} + * @alias Blockly.Procedures.ProcedureBlock + */ + export let ProcedureBlock: any; + /** + * Find all user-created procedure definitions in a workspace. + * @param {!Workspace} root Root workspace. + * @return {!Array>} Pair of arrays, the + * first contains procedures without return variables, the second with. + * Each procedure is defined by a three-element list of name, parameter + * list, and return value boolean. + * @alias Blockly.Procedures.allProcedures + */ + export function allProcedures(root: Workspace): Array>; + /** + * Ensure two identically-named procedures don't exist. + * Take the proposed procedure name, and return a legal name i.e. one that + * is not empty and doesn't collide with other procedures. + * @param {string} name Proposed procedure name. + * @param {!Block} block Block to disambiguate. + * @return {string} Non-colliding name. + * @alias Blockly.Procedures.findLegalName + */ + export function findLegalName(name: string, block: Block): string; + /** + * Return if the given name is already a procedure name. + * @param {string} name The questionable name. + * @param {!Workspace} workspace The workspace to scan for collisions. + * @param {Block=} opt_exclude Optional block to exclude from + * comparisons (one doesn't want to collide with oneself). + * @return {boolean} True if the name is used, otherwise return false. + * @alias Blockly.Procedures.isNameUsed + */ + export function isNameUsed(name: string, workspace: Workspace, opt_exclude?: Block | undefined): boolean; + /** + * Rename a procedure. Called by the editable field. + * @param {string} name The proposed new name. + * @return {string} The accepted name. + * @this {Field} + * @alias Blockly.Procedures.rename + */ + export function rename(name: string): string; + /** + * Construct the blocks required by the flyout for the procedure category. + * @param {!Workspace} workspace The workspace containing procedures. + * @return {!Array} Array of XML block elements. + * @alias Blockly.Procedures.flyoutCategory + */ + export function flyoutCategory(workspace: Workspace): Array; + /** + * Listens for when a procedure mutator is opened. Then it triggers a flyout + * update and adds a mutator change listener to the mutator workspace. + * @param {!Abstract} e The event that triggered this listener. + * @alias Blockly.Procedures.mutatorOpenListener + * @package + */ + export function mutatorOpenListener(e: typeof Abstract): void; + /** + * Find all the callers of a named procedure. + * @param {string} name Name of procedure. + * @param {!Workspace} workspace The workspace to find callers in. + * @return {!Array} Array of caller blocks. + * @alias Blockly.Procedures.getCallers + */ + export function getCallers(name: string, workspace: Workspace): Array; + /** + * When a procedure definition changes its parameters, find and edit all its + * callers. + * @param {!Block} defBlock Procedure definition block. + * @alias Blockly.Procedures.mutateCallers + */ + export function mutateCallers(defBlock: Block): void; + /** + * Find the definition block for the named procedure. + * @param {string} name Name of procedure. + * @param {!Workspace} workspace The workspace to search. + * @return {?Block} The procedure definition block, or null not found. + * @alias Blockly.Procedures.getDefinition + */ + export function getDefinition(name: string, workspace: Workspace): Block | null; + import { Workspace } from "workspace"; + import { Block } from "block"; + import * as Abstract from "events/events_abstract"; +} +declare module "variables_dynamic" { + function stringButtonClickHandler(button: any): void; + function numberButtonClickHandler(button: any): void; + function colourButtonClickHandler(button: any): void; + /** + * Construct the elements (blocks and button) required by the flyout for the + * variable category. + * @param {!Workspace} workspace The workspace containing variables. + * @return {!Array} Array of XML elements. + * @alias Blockly.VariablesDynamic.flyoutCategory + */ + export function flyoutCategory(workspace: Workspace): Array; + /** + * Construct the blocks required by the flyout for the variable category. + * @param {!Workspace} workspace The workspace containing variables. + * @return {!Array} Array of XML block elements. + * @alias Blockly.VariablesDynamic.flyoutCategoryBlocks + */ + export function flyoutCategoryBlocks(workspace: Workspace): Array; + import { Workspace } from "workspace"; + export { stringButtonClickHandler as onCreateVariableButtonClick_String, numberButtonClickHandler as onCreateVariableButtonClick_Number, colourButtonClickHandler as onCreateVariableButtonClick_Colour }; +} +declare module "renderers/common/debug" { + /** + * Returns whether the debugger is turned on. + * @return {boolean} Whether the debugger is turned on. + * @alias Blockly.blockRendering.debug.isDebuggerEnabled + * @package + */ + export function isDebuggerEnabled(): boolean; + /** + * Turn on the blocks debugger. + * @package + * @alias Blockly.blockRendering.debug.startDebugger + */ + export function startDebugger(): void; + /** + * Turn off the blocks debugger. + * @package + * @alias Blockly.blockRendering.debug.stopDebugger + */ + export function stopDebugger(): void; +} +declare module "renderers/common/constants" { + /** + * An object that provides constants for rendering blocks. + * @constructor + * @package + * @alias Blockly.blockRendering.ConstantProvider + */ + export class ConstantProvider { + /** + * The size of an empty spacer. + * @type {number} + */ + NO_PADDING: number; + /** + * The size of small padding. + * @type {number} + */ + SMALL_PADDING: number; + /** + * The size of medium padding. + * @type {number} + */ + MEDIUM_PADDING: number; + /** + * The size of medium-large padding. + * @type {number} + */ + MEDIUM_LARGE_PADDING: number; + /** + * The size of large padding. + * @type {number} + */ + LARGE_PADDING: number; + /** + * Offset from the top of the row for placing fields on inline input rows + * and statement input rows. + * Matches existing rendering (in 2019). + * @type {number} + */ + TALL_INPUT_FIELD_OFFSET_Y: number; + /** + * The height of the puzzle tab used for input and output connections. + * @type {number} + */ + TAB_HEIGHT: number; + /** + * The offset from the top of the block at which a puzzle tab is positioned. + * @type {number} + */ + TAB_OFFSET_FROM_TOP: number; + /** + * Vertical overlap of the puzzle tab, used to make it look more like a puzzle + * piece. + * @type {number} + */ + TAB_VERTICAL_OVERLAP: number; + /** + * The width of the puzzle tab used for input and output connections. + * @type {number} + */ + TAB_WIDTH: number; + /** + * The width of the notch used for previous and next connections. + * @type {number} + */ + NOTCH_WIDTH: number; + /** + * The height of the notch used for previous and next connections. + * @type {number} + */ + NOTCH_HEIGHT: number; + /** + * The minimum width of the block. + * @type {number} + */ + MIN_BLOCK_WIDTH: number; + EMPTY_BLOCK_SPACER_HEIGHT: number; + /** + * The minimum height of a dummy input row. + * @type {number} + */ + DUMMY_INPUT_MIN_HEIGHT: number; + /** + * The minimum height of a dummy input row in a shadow block. + * @type {number} + */ + DUMMY_INPUT_SHADOW_MIN_HEIGHT: number; + /** + * Rounded corner radius. + * @type {number} + */ + CORNER_RADIUS: number; + /** + * Offset from the left side of a block or the inside of a statement input to + * the left side of the notch. + * @type {number} + */ + NOTCH_OFFSET_LEFT: number; + /** + * Additional offset added to the statement input's width to account for the + * notch. + * @type {number} + */ + STATEMENT_INPUT_NOTCH_OFFSET: number; + STATEMENT_BOTTOM_SPACER: number; + STATEMENT_INPUT_PADDING_LEFT: number; + /** + * Vertical padding between consecutive statement inputs. + * @type {number} + */ + BETWEEN_STATEMENT_PADDING_Y: number; + /** + * The top row's minimum height. + * @type {number} + */ + TOP_ROW_MIN_HEIGHT: number; + /** + * The top row's minimum height if it precedes a statement. + * @type {number} + */ + TOP_ROW_PRECEDES_STATEMENT_MIN_HEIGHT: number; + /** + * The bottom row's minimum height. + * @type {number} + */ + BOTTOM_ROW_MIN_HEIGHT: number; + /** + * The bottom row's minimum height if it follows a statement input. + * @type {number} + */ + BOTTOM_ROW_AFTER_STATEMENT_MIN_HEIGHT: number; + /** + * Whether to add a 'hat' on top of all blocks with no previous or output + * connections. Can be overridden by 'hat' property on Theme.BlockStyle. + * @type {boolean} + */ + ADD_START_HATS: boolean; + /** + * Height of the top hat. + * @type {number} + */ + START_HAT_HEIGHT: number; + /** + * Width of the top hat. + * @type {number} + */ + START_HAT_WIDTH: number; + SPACER_DEFAULT_HEIGHT: number; + MIN_BLOCK_HEIGHT: number; + EMPTY_INLINE_INPUT_PADDING: number; + /** + * The height of an empty inline input. + * @type {number} + */ + EMPTY_INLINE_INPUT_HEIGHT: number; + EXTERNAL_VALUE_INPUT_PADDING: number; + /** + * The height of an empty statement input. Note that in the old rendering + * this varies slightly depending on whether the block has external or inline + * inputs. In the new rendering this is consistent. It seems unlikely that + * the old behaviour was intentional. + * @type {number} + */ + EMPTY_STATEMENT_INPUT_HEIGHT: number; + START_POINT: string; + /** + * Height of SVG path for jagged teeth at the end of collapsed blocks. + * @type {number} + */ + JAGGED_TEETH_HEIGHT: number; + /** + * Width of SVG path for jagged teeth at the end of collapsed blocks. + * @type {number} + */ + JAGGED_TEETH_WIDTH: number; + /** + * Point size of text. + * @type {number} + */ + FIELD_TEXT_FONTSIZE: number; + /** + * Text font weight. + * @type {string} + */ + FIELD_TEXT_FONTWEIGHT: string; + /** + * Text font family. + * @type {string} + */ + FIELD_TEXT_FONTFAMILY: string; + /** + * Height of text. This constant is dynamically set in ``setFontConstants_`` + * to be the height of the text based on the font used. + * @type {number} + */ + FIELD_TEXT_HEIGHT: number; + /** + * Text baseline. This constant is dynamically set in ``setFontConstants_`` + * to be the baseline of the text based on the font used. + * @type {number} + */ + FIELD_TEXT_BASELINE: number; + /** + * A field's border rect corner radius. + * @type {number} + */ + FIELD_BORDER_RECT_RADIUS: number; + /** + * A field's border rect default height. + * @type {number} + */ + FIELD_BORDER_RECT_HEIGHT: number; + /** + * A field's border rect X padding. + * @type {number} + */ + FIELD_BORDER_RECT_X_PADDING: number; + /** + * A field's border rect Y padding. + * @type {number} + */ + FIELD_BORDER_RECT_Y_PADDING: number; + /** + * The backing colour of a field's border rect. + * @type {string} + * @package + */ + FIELD_BORDER_RECT_COLOUR: string; + /** + * A field's text element's dominant baseline. + * @type {boolean} + */ + FIELD_TEXT_BASELINE_CENTER: boolean; + /** + * A dropdown field's border rect height. + * @type {number} + */ + FIELD_DROPDOWN_BORDER_RECT_HEIGHT: number; + /** + * Whether or not a dropdown field should add a border rect when in a shadow + * block. + * @type {boolean} + */ + FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW: boolean; + /** + * Whether or not a dropdown field's div should be coloured to match the + * block colours. + * @type {boolean} + */ + FIELD_DROPDOWN_COLOURED_DIV: boolean; + /** + * Whether or not a dropdown field uses a text or SVG arrow. + * @type {boolean} + */ + FIELD_DROPDOWN_SVG_ARROW: boolean; + /** + * A dropdown field's SVG arrow padding. + * @type {number} + */ + FIELD_DROPDOWN_SVG_ARROW_PADDING: number; + /** + * A dropdown field's SVG arrow size. + * @type {number} + */ + FIELD_DROPDOWN_SVG_ARROW_SIZE: number; + /** + * A dropdown field's SVG arrow datauri. + * @type {string} + */ + FIELD_DROPDOWN_SVG_ARROW_DATAURI: string; + /** + * Whether or not to show a box shadow around the widget div. This is only a + * feature of full block fields. + * @type {boolean} + */ + FIELD_TEXTINPUT_BOX_SHADOW: boolean; + /** + * Whether or not the colour field should display its colour value on the + * entire block. + * @type {boolean} + */ + FIELD_COLOUR_FULL_BLOCK: boolean; + /** + * A colour field's default width. + * @type {number} + */ + FIELD_COLOUR_DEFAULT_WIDTH: number; + /** + * A colour field's default height. + * @type {number} + */ + FIELD_COLOUR_DEFAULT_HEIGHT: number; + /** + * A checkbox field's X offset. + * @type {number} + */ + FIELD_CHECKBOX_X_OFFSET: number; + /** + * A random identifier used to ensure a unique ID is used for each + * filter/pattern for the case of multiple Blockly instances on a page. + * @type {string} + * @package + */ + randomIdentifier: string; + /** + * The defs tag that contains all filters and patterns for this Blockly + * instance. + * @type {?SVGElement} + * @private + */ + private defs_; + /** + * The ID of the emboss filter, or the empty string if no filter is set. + * @type {string} + * @package + */ + embossFilterId: string; + /** + * The element to use for highlighting, or null if not set. + * @type {SVGElement} + * @private + */ + private embossFilter_; + /** + * The ID of the disabled pattern, or the empty string if no pattern is set. + * @type {string} + * @package + */ + disabledPatternId: string; + /** + * The element to use for disabled blocks, or null if not set. + * @type {SVGElement} + * @private + */ + private disabledPattern_; + /** + * The ID of the debug filter, or the empty string if no pattern is set. + * @type {string} + * @package + */ + debugFilterId: string; + /** + * The element to use for a debug highlight, or null if not set. + * @type {SVGElement} + * @private + */ + private debugFilter_; + /** + * The