diff --git a/.eslintrc b/.eslintrc index 9b01970189..cad8fcb744 100644 --- a/.eslintrc +++ b/.eslintrc @@ -57,5 +57,8 @@ "Blockly": true, # Blockly global "goog": true, # goog closure libraries/includes }, - "extends": "eslint:recommended" + "extends": "eslint:recommended", + "parserOptions": { + "sourceType": "module", + } } diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index cd39592905..0f3931edd4 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -6,7 +6,6 @@ jobs: setup: runs-on: ubuntu-latest env: - JVM_OPTS: -Xmx3200m PROJECT_PATH: ./scratch-blocks steps: - run: | @@ -16,32 +15,24 @@ jobs: - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3 - name: Check Python version run: python --version - - name: Setup Java - uses: actions/setup-java@0ab4596768b603586c0de567f2430c30f5b0d2b0 # v3 - with: - distribution: 'temurin' - java-version: 17 - name: Setup Node uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3 with: node-version-file: '.nvmrc' + # TODO: tests - name: Install Node Dependencies - run: npm ci - - name: Lint - run: npm run test:lint - - name: Run Tests - run: npm run test:messages - - name: Run Unit Tests - run: DISPLAY=:99 npm run test:unit - - name: Remove Closure App - run: rm -rf gh-pages/closure-library/scripts/ci/CloseAdobeDialog.exe - - name: Deploy playground to GitHub Pages - uses: peaceiris/actions-gh-pages@373f7f263a76c20808c831209c920827a82a2847 # v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./gh-pages - full_commit_message: "Build for ${{ github.sha }} ${{ github.event.head_commit.message }}" - enable_jekyll: true + # The --legacy-peer-deps flag is a TEMPORARY fix during the Blockly 12 beta. + # It should be removed once the @blockly/* packages support Blockly 12. + run: npm ci --legacy-peer-deps + - name: Build + run: npm run build + # - name: Deploy playground to GitHub Pages + # uses: peaceiris/actions-gh-pages@373f7f263a76c20808c831209c920827a82a2847 # v3 + # with: + # github_token: ${{ secrets.GITHUB_TOKEN }} + # publish_dir: ./gh-pages + # full_commit_message: "Build for ${{ github.sha }} ${{ github.event.head_commit.message }}" + # enable_jekyll: true - name: Run semantic-release env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/signature-assistant.yml b/.github/workflows/signature-assistant.yml new file mode 100644 index 0000000000..4820e949ab --- /dev/null +++ b/.github/workflows/signature-assistant.yml @@ -0,0 +1,37 @@ +name: "Signature Assistant" +on: + issue_comment: + types: [created] + pull_request_target: + types: [opened, closed, synchronize] + +permissions: + actions: write + contents: read + pull-requests: write + statuses: write + +jobs: + CLA-Assistant: + if: github.event_name == 'pull_request_target' || + ( + github.event.comment.body == 'recheck' || + github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA' + ) + runs-on: ubuntu-latest + steps: + - uses: scratchfoundation/scratch-agreements/.github/actions/cla-allowlist@main + id: cla-allowlist + - name: "CLA Assistant" + uses: contributor-assistant/github-action@ca4a40a7d1004f18d9960b404b97e5f30a505a08 # v2.6.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # the below token should have repo scope and must be manually added by you in the repository's secrets + PERSONAL_ACCESS_TOKEN: ${{ secrets.GHA_AGREEMENTS_PAT }} + with: + remote-organization-name: "scratchfoundation" + remote-repository-name: "scratch-agreements" + path-to-signatures: "signatures/version1/cla.json" + path-to-document: "https://github.com/scratchfoundation/scratch-agreements/blob/main/CLA.md" + branch: "main" + allowlist: ${{ steps.cla-allowlist.outputs.allowlist }} diff --git a/.gitignore b/.gitignore index ca5e13595f..b85810585b 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ python_compressed.js .vscode /accessible/* +/build /dist /msg/js/* !/msg/js/en.js diff --git a/.husky/commit-msg b/.husky/commit-msg index 80416c7b17..70bd3dd23d 100755 --- a/.husky/commit-msg +++ b/.husky/commit-msg @@ -1,4 +1 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - npx --no-install commitlint --edit "$1" diff --git a/CHANGELOG.md b/CHANGELOG.md index ba82b7a33f..392ba9d206 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,162 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.0.0-spork.5](https://github.com/scratchfoundation/scratch-blocks/compare/v2.0.0-spork.4...v2.0.0-spork.5) (2025-08-26) + + +### Bug Fixes + +* **deps:** use blockly 12.3 beta ([76981e1](https://github.com/scratchfoundation/scratch-blocks/commit/76981e1bc22ba3de6e234885bfcee97ca202b7f5)) +* fix bug that could cause the wrong toolbox category to appear selected ([#232](https://github.com/scratchfoundation/scratch-blocks/issues/232)) ([ec918e2](https://github.com/scratchfoundation/scratch-blocks/commit/ec918e2b7c1f8ac05cfe1c93bf37b458bb4767b1)) +* fix bug that could clear the values of procedure block caller arguments ([#247](https://github.com/scratchfoundation/scratch-blocks/issues/247)) ([611e440](https://github.com/scratchfoundation/scratch-blocks/commit/611e44066daa3e2a1201856503b868ced6a2bb1f)) +* fix bug that prevented moving block comments ([#248](https://github.com/scratchfoundation/scratch-blocks/issues/248)) ([dc17159](https://github.com/scratchfoundation/scratch-blocks/commit/dc171596aed0b248086d9110ec08370ed026bf4a)) +* fix deserialization of local/cloud variables ([#245](https://github.com/scratchfoundation/scratch-blocks/issues/245)) ([09bb237](https://github.com/scratchfoundation/scratch-blocks/commit/09bb2379318d107ca473bf7f3cdb797b69c5c42c)) +* fix ephemeral focus handling for `FieldAngle` and `FieldNote` ([#244](https://github.com/scratchfoundation/scratch-blocks/issues/244)) ([8b758db](https://github.com/scratchfoundation/scratch-blocks/commit/8b758db2c4cde80dd977bec05207f855a9d2f742)) +* fix positioning of block comments in RTL mode ([#249](https://github.com/scratchfoundation/scratch-blocks/issues/249)) ([9f1da96](https://github.com/scratchfoundation/scratch-blocks/commit/9f1da96cb250374a367ae4005512b7717a292e9a)) +* improve styling, especially in high contrast mode ([#246](https://github.com/scratchfoundation/scratch-blocks/issues/246)) ([abd9a40](https://github.com/scratchfoundation/scratch-blocks/commit/abd9a40709f615e1a737bddb3906cdf4744b4389)) +* make Duplicate context menu item duplicate subsequent blocks ([#250](https://github.com/scratchfoundation/scratch-blocks/issues/250)) ([3a43e6b](https://github.com/scratchfoundation/scratch-blocks/commit/3a43e6b47b8f7d3cfe99a02eb0e3a6b5d6f59503)) +* make insertion markers reflect the actual size of the block ([#251](https://github.com/scratchfoundation/scratch-blocks/issues/251)) ([c824cc1](https://github.com/scratchfoundation/scratch-blocks/commit/c824cc12617337680d40214c89bf622089da46ac)) +* prevent toolbox categories from becoming unselected ([#233](https://github.com/scratchfoundation/scratch-blocks/issues/233)) ([af66069](https://github.com/scratchfoundation/scratch-blocks/commit/af66069493bea8b17658cf1221d42c71fb2531b3)) + +# [2.0.0-spork.4](https://github.com/scratchfoundation/scratch-blocks/compare/v2.0.0-spork.3...v2.0.0-spork.4) (2025-02-11) + + +### Bug Fixes + +* new beta release for better Blockly v12 beta compatibility ([127ac8b](https://github.com/scratchfoundation/scratch-blocks/commit/127ac8b7b14e3ee86e4c6d038cb1759495f93001)) + +# [2.0.0-spork.3](https://github.com/scratchfoundation/scratch-blocks/compare/v2.0.0-spork.2...v2.0.0-spork.3) (2024-12-10) + + +### Bug Fixes + +* rename mouseDownWrapper fields for Blockly v12 compatibility ([adaf531](https://github.com/scratchfoundation/scratch-blocks/commit/adaf531cadf51f797cf6c33050df2380bb3cf372)) + +# [2.0.0-spork.2](https://github.com/scratchfoundation/scratch-blocks/compare/v2.0.0-spork.1...v2.0.0-spork.2) (2024-12-10) + + +### Bug Fixes + +* **deps:** use Blockly 12 beta instead of RC ([92a8a01](https://github.com/scratchfoundation/scratch-blocks/commit/92a8a010c496569ba04ca077744582e0ab71cd93)) + +# [2.0.0-spork.1](https://github.com/scratchfoundation/scratch-blocks/compare/v1.1.86...v2.0.0-spork.1) (2024-10-21) + + +### Bug Fixes + +* Add support for flyout labels with status indicators ([#212](https://github.com/scratchfoundation/scratch-blocks/issues/212)) ([665d196](https://github.com/scratchfoundation/scratch-blocks/commit/665d1963759caab39c1a0078a81e5e3b63734f05)) +* add support for Scratch-style block comments ([#83](https://github.com/scratchfoundation/scratch-blocks/issues/83)) ([8902091](https://github.com/scratchfoundation/scratch-blocks/commit/8902091c2da8ab68aeac6e2a3954741197a50b37)) +* add support for Scratch-style procedures ([#39](https://github.com/scratchfoundation/scratch-blocks/issues/39)) ([13647eb](https://github.com/scratchfoundation/scratch-blocks/commit/13647eb36439926e82cbe847a643f592702690ab)) +* add zoom controls config ([#126](https://github.com/scratchfoundation/scratch-blocks/issues/126)) ([a09ae24](https://github.com/scratchfoundation/scratch-blocks/commit/a09ae248a1c3aa422942b1f9d21487c79c0c4b86)) +* allow focusing fields in the flyout on mobile ([#184](https://github.com/scratchfoundation/scratch-blocks/issues/184)) ([6c9d3a6](https://github.com/scratchfoundation/scratch-blocks/commit/6c9d3a6026f67fd4a173c718d7a2ba66b69922f6)) +* allow specifying the function to be used for prompting about variable creation/edits ([#106](https://github.com/scratchfoundation/scratch-blocks/issues/106)) ([4cfe66f](https://github.com/scratchfoundation/scratch-blocks/commit/4cfe66fa0419e6140ff6f7cad1c1bfc884917a72)) +* clean up data block definitions ([#90](https://github.com/scratchfoundation/scratch-blocks/issues/90)) ([9ea96e2](https://github.com/scratchfoundation/scratch-blocks/commit/9ea96e27f43688bcf81bdc006adac129a1b286c8)) +* correctly align extension block icons ([#182](https://github.com/scratchfoundation/scratch-blocks/issues/182)) ([e3dbad1](https://github.com/scratchfoundation/scratch-blocks/commit/e3dbad1feb19c5e72ac66ba9aa9e6f47580a414b)) +* delete context menu to display the correct number of blocks ([#127](https://github.com/scratchfoundation/scratch-blocks/issues/127)) ([a65d24a](https://github.com/scratchfoundation/scratch-blocks/commit/a65d24a38daf8a740d5230ee9a7fbac5ae70e2af)) +* **deps:** after installing deps, replace Blockly v11 with v12 RC ([677ff6f](https://github.com/scratchfoundation/scratch-blocks/commit/677ff6f9589ef84b849822e17a705583a55e50db)) +* **deps:** clone Blockly RC over HTTP instead of SSH ([8a861f0](https://github.com/scratchfoundation/scratch-blocks/commit/8a861f0aac2737de08cc3e1c65942a99d21f1ba8)) +* display icons in the toolbox for extension categories ([#47](https://github.com/scratchfoundation/scratch-blocks/issues/47)) ([b53eadd](https://github.com/scratchfoundation/scratch-blocks/commit/b53eaddcf2ca261ea5798f342e0ec600bf60ca19)) +* don't hide the drag surface ([#38](https://github.com/scratchfoundation/scratch-blocks/issues/38)) ([7f70f09](https://github.com/scratchfoundation/scratch-blocks/commit/7f70f09e11972be8a6ee512323a0a39a52ef0452)) +* don't include Blockly RC source in npm package ([21b15d1](https://github.com/scratchfoundation/scratch-blocks/commit/21b15d1d8e29eef836615ac45488d856b6a22d01)) +* don't show global/local options when renaming a variable ([#123](https://github.com/scratchfoundation/scratch-blocks/issues/123)) ([22a6b73](https://github.com/scratchfoundation/scratch-blocks/commit/22a6b733636b738d200c67ae53caee3e072e043a)) +* don't show scope options when renaming a variable from the variable getter context menu ([#139](https://github.com/scratchfoundation/scratch-blocks/issues/139)) ([375e56d](https://github.com/scratchfoundation/scratch-blocks/commit/375e56db6918dc3475d5c8f29f53cd03f138e4a7)) +* don't warn about procedure references when moving the definition on the workspace ([#131](https://github.com/scratchfoundation/scratch-blocks/issues/131)) ([cda58cc](https://github.com/scratchfoundation/scratch-blocks/commit/cda58ccfa799a0d301d6ba842c6546c1eb9f5614)) +* enable and style workspace comments ([#82](https://github.com/scratchfoundation/scratch-blocks/issues/82)) ([98ccb62](https://github.com/scratchfoundation/scratch-blocks/commit/98ccb62d7280d2cc711337581464138a281453e3)) +* enable dragging arguments out of procedure blocks ([#119](https://github.com/scratchfoundation/scratch-blocks/issues/119)) ([0ca0620](https://github.com/scratchfoundation/scratch-blocks/commit/0ca0620aad343cdbb1d632ddf3ae73a70085aa03)) +* Export colours. ([7c346fa](https://github.com/scratchfoundation/scratch-blocks/commit/7c346fa2424962d7796e42232d9e7db5899dfd5a)) +* Export Scratch messages. ([09326a1](https://github.com/scratchfoundation/scratch-blocks/commit/09326a1c82e5dfa4316d426cd745a8f1c52315b9)) +* fix a crash when adding a broadcast message ([#150](https://github.com/scratchfoundation/scratch-blocks/issues/150)) ([8e165ce](https://github.com/scratchfoundation/scratch-blocks/commit/8e165cec9a45f5f9d17f2255ed3fd58f834881f7)) +* fix alignment of "define" text baseline on custom blocks ([#220](https://github.com/scratchfoundation/scratch-blocks/issues/220)) ([cc4d9f9](https://github.com/scratchfoundation/scratch-blocks/commit/cc4d9f9bc7520313145c4c0cb9978c4dfc6b3bfc)) +* fix bug that caused the number/string input in the custom block editor to have square corners ([#213](https://github.com/scratchfoundation/scratch-blocks/issues/213)) ([c3ee958](https://github.com/scratchfoundation/scratch-blocks/commit/c3ee958a5b5b8cf1ae375f82b5dce88a81ee62dd)) +* fix bug that could cause duplicated procedure argument blocks to create more duplicates on drag ([#217](https://github.com/scratchfoundation/scratch-blocks/issues/217)) ([6a1c8a9](https://github.com/scratchfoundation/scratch-blocks/commit/6a1c8a9e745d77f2aec49c3cae06dd6836bd58ef)) +* fix bug that prevented modal dialogs from appearing on mobile ([#183](https://github.com/scratchfoundation/scratch-blocks/issues/183)) ([37e0f10](https://github.com/scratchfoundation/scratch-blocks/commit/37e0f10f4a59e1bb32734b955b7f2f17e4f2fca0)) +* fix bug that prevented showing the contextual menu on blocks ([#176](https://github.com/scratchfoundation/scratch-blocks/issues/176)) ([2e98ff1](https://github.com/scratchfoundation/scratch-blocks/commit/2e98ff15a149b0ff44e30e7023b36d1605030311)) +* fix color of block reporter dropdown text ([#205](https://github.com/scratchfoundation/scratch-blocks/issues/205)) ([73d978e](https://github.com/scratchfoundation/scratch-blocks/commit/73d978e6c98d546774398e4d48faa7c181fdac11)) +* fix dropdown menu metrics ([#148](https://github.com/scratchfoundation/scratch-blocks/issues/148)) ([40eee91](https://github.com/scratchfoundation/scratch-blocks/commit/40eee91711fb2cc76494221ce2f8b08fc5d9f868)) +* fix exception when editing custom blocks ([#105](https://github.com/scratchfoundation/scratch-blocks/issues/105)) ([7478546](https://github.com/scratchfoundation/scratch-blocks/commit/747854658b5db2f4cfc00d827ef00c1081ab3c28)) +* fix positioning of categories when scrolling via the toolbox ([#186](https://github.com/scratchfoundation/scratch-blocks/issues/186)) ([6d14530](https://github.com/scratchfoundation/scratch-blocks/commit/6d1453082c3ed220c0ff09bf5b785a49df9f4312)) +* fix styling of dropdown menus ([#152](https://github.com/scratchfoundation/scratch-blocks/issues/152)) ([0e80277](https://github.com/scratchfoundation/scratch-blocks/commit/0e802773ce3749a3f74f5fec986aba54312a56cd)) +* fix the color of procedure argument blocks ([#216](https://github.com/scratchfoundation/scratch-blocks/issues/216)) ([88c700e](https://github.com/scratchfoundation/scratch-blocks/commit/88c700e40ea9ed05d3a237936ae44d2b62b1424f)) +* fix the colors of the angle picker dropdown ([#179](https://github.com/scratchfoundation/scratch-blocks/issues/179)) ([59896d2](https://github.com/scratchfoundation/scratch-blocks/commit/59896d2f55e12f5f28bb8bc615f4b5eb186e0ae6)) +* fix the flyout width at 250 pixels ([#168](https://github.com/scratchfoundation/scratch-blocks/issues/168)) ([a47aba6](https://github.com/scratchfoundation/scratch-blocks/commit/a47aba6189fbbb825ad24bd05ab3c8ecdc3dc972)) +* fix the styling of contextual menus ([#147](https://github.com/scratchfoundation/scratch-blocks/issues/147)) ([6fbc2e5](https://github.com/scratchfoundation/scratch-blocks/commit/6fbc2e5ef4d24279be3073021319e717963d76e4)) +* fix toolbox category selection ([#141](https://github.com/scratchfoundation/scratch-blocks/issues/141)) ([d3e1a1b](https://github.com/scratchfoundation/scratch-blocks/commit/d3e1a1b39099ec8a8ca72f3cac21fac5d588d449)) +* fix wrapping of long category labels ([#166](https://github.com/scratchfoundation/scratch-blocks/issues/166)) ([7b39ac1](https://github.com/scratchfoundation/scratch-blocks/commit/7b39ac141bf353edf0cc5379289a33dd3a3ecba8)) +* fixed bug where broadcast messages would appear in the variable dropdown list ([#124](https://github.com/scratchfoundation/scratch-blocks/issues/124)) ([b1e67f6](https://github.com/scratchfoundation/scratch-blocks/commit/b1e67f62dcedbd35e1b92d1ec1c773ff74b2ef56)) +* hide disable and inline inputs contextual menu items ([#35](https://github.com/scratchfoundation/scratch-blocks/issues/35)) ([c548298](https://github.com/scratchfoundation/scratch-blocks/commit/c548298f3b9cf6baa0c27fdb54b5336538d4abf0)) +* improve positioning of newly created procedure blocks ([#121](https://github.com/scratchfoundation/scratch-blocks/issues/121)) ([84a9e5b](https://github.com/scratchfoundation/scratch-blocks/commit/84a9e5b8e9aba2688fd10672a852827311f32259)) +* improve reliability of block value reporting ([#77](https://github.com/scratchfoundation/scratch-blocks/issues/77)) ([cb5b068](https://github.com/scratchfoundation/scratch-blocks/commit/cb5b068afa6e781e649754b55fc013a02112431e)) +* improve sizing and rendering of comments ([#219](https://github.com/scratchfoundation/scratch-blocks/issues/219)) ([1279c0a](https://github.com/scratchfoundation/scratch-blocks/commit/1279c0aacefb8bbfaaa9786531a56c5a45cf24ee)) +* load CSS and fix up UI appearance ([#33](https://github.com/scratchfoundation/scratch-blocks/issues/33)) ([1645129](https://github.com/scratchfoundation/scratch-blocks/commit/1645129950797408d01e3966a9d1b7bdf6223226)) +* load the continuous toolbox ([#31](https://github.com/scratchfoundation/scratch-blocks/issues/31)) ([ea68b1c](https://github.com/scratchfoundation/scratch-blocks/commit/ea68b1c25f055288e2ee39f846c17e5861f1a01f)) +* make block images work in all contexts ([#30](https://github.com/scratchfoundation/scratch-blocks/issues/30)) ([920febf](https://github.com/scratchfoundation/scratch-blocks/commit/920febf5c3304bcf3a561b1e7c0e016540399540)) +* make FieldNumber a subclass of FieldTextInput ([#214](https://github.com/scratchfoundation/scratch-blocks/issues/214)) ([3ae2235](https://github.com/scratchfoundation/scratch-blocks/commit/3ae22356f5ee9ed5da55b744657a033e43ad1ce0)) +* make variable names case-sensitive ([#122](https://github.com/scratchfoundation/scratch-blocks/issues/122)) ([46854cd](https://github.com/scratchfoundation/scratch-blocks/commit/46854cdb7c9fe92b8621a9e36acd6f94e9383a9f)) +* match Scratch behaviors around dragging and connection stickiness ([#80](https://github.com/scratchfoundation/scratch-blocks/issues/80)) ([fd1bc58](https://github.com/scratchfoundation/scratch-blocks/commit/fd1bc58bdef66697b0630d1c67f5a04d01dfa716)) +* miscellaneous UI fixits ([#41](https://github.com/scratchfoundation/scratch-blocks/issues/41)) ([300a1ce](https://github.com/scratchfoundation/scratch-blocks/commit/300a1ce564a55cb65ef2e01792a0ba1149621c50)) +* modernize and reenable the colour slider field ([#42](https://github.com/scratchfoundation/scratch-blocks/issues/42)) ([4f97982](https://github.com/scratchfoundation/scratch-blocks/commit/4f979828f1122d5cf314a8de464457ae8b925ed7)) +* more closely align flyout layout with Scratch ([#45](https://github.com/scratchfoundation/scratch-blocks/issues/45)) ([49663ed](https://github.com/scratchfoundation/scratch-blocks/commit/49663ed2f823ea45a04c0201d467d4e06e0d7078)) +* prevent deleting procedure definition blocks with references by dragging to the flyout ([#120](https://github.com/scratchfoundation/scratch-blocks/issues/120)) ([fa9367d](https://github.com/scratchfoundation/scratch-blocks/commit/fa9367d975e8c3fd45321248eb365c9cd0477dbb)) +* prevent dragging blocks into the slot occupied by the procedure definition block's example caller block ([#118](https://github.com/scratchfoundation/scratch-blocks/issues/118)) ([453ffa9](https://github.com/scratchfoundation/scratch-blocks/commit/453ffa9654927be023571f5bb496a1d0ff36aad9)) +* re-export scratch-blocks utility functions ([#26](https://github.com/scratchfoundation/scratch-blocks/issues/26)) ([685ecfc](https://github.com/scratchfoundation/scratch-blocks/commit/685ecfc0ce9f3c9335393f5af71eb3755aabedd4)) +* readd the control blocks ([#22](https://github.com/scratchfoundation/scratch-blocks/issues/22)) ([f69d4ac](https://github.com/scratchfoundation/scratch-blocks/commit/f69d4ac8ebd128b318919f4a3ac5222d211adfbb)) +* readd the data blocks ([#29](https://github.com/scratchfoundation/scratch-blocks/issues/29)) ([fafed65](https://github.com/scratchfoundation/scratch-blocks/commit/fafed65e73f59877200741c36244d082f2b3aad1)) +* readd the event blocks ([#21](https://github.com/scratchfoundation/scratch-blocks/issues/21)) ([4de530f](https://github.com/scratchfoundation/scratch-blocks/commit/4de530f00310ee27c0ad55940c9cb6bf1208df91)) +* readd the looks blocks ([#23](https://github.com/scratchfoundation/scratch-blocks/issues/23)) ([34f07c0](https://github.com/scratchfoundation/scratch-blocks/commit/34f07c0ed7982963ded8563b5f45d374e740d98c)) +* readd the motion blocks ([#20](https://github.com/scratchfoundation/scratch-blocks/issues/20)) ([79398c2](https://github.com/scratchfoundation/scratch-blocks/commit/79398c2ebae7329d0d428bab5870e559423ab36b)) +* readd the operator blocks and dependencies ([#19](https://github.com/scratchfoundation/scratch-blocks/issues/19)) ([8024e9f](https://github.com/scratchfoundation/scratch-blocks/commit/8024e9fd638586d26e6b47424e0067c0387af8cd)) +* readd the sensing blocks ([#27](https://github.com/scratchfoundation/scratch-blocks/issues/27)) ([9f5f135](https://github.com/scratchfoundation/scratch-blocks/commit/9f5f1351be5818fb9392214b8047acace8333a65)) +* readd the sound blocks ([#24](https://github.com/scratchfoundation/scratch-blocks/issues/24)) ([6837513](https://github.com/scratchfoundation/scratch-blocks/commit/6837513d3f217e94522a3ce756273d6ff8e7538c)) +* reenable reporting block values ([#55](https://github.com/scratchfoundation/scratch-blocks/issues/55)) ([70c8cfd](https://github.com/scratchfoundation/scratch-blocks/commit/70c8cfd73ee1081eafcb2773fe56a4e078cb2bea)) +* reenable shadows for blocks being dragged ([#79](https://github.com/scratchfoundation/scratch-blocks/issues/79)) ([94d2a2c](https://github.com/scratchfoundation/scratch-blocks/commit/94d2a2ca55c0d0265a79237610bcf8c246d74a9b)) +* reenable support for checkboxes in the flyout ([#43](https://github.com/scratchfoundation/scratch-blocks/issues/43)) ([e603c67](https://github.com/scratchfoundation/scratch-blocks/commit/e603c67cf7c55ff09c292991ef6b9f9ac3aae0f9)) +* reenable support for dragging blocks between sprites ([#130](https://github.com/scratchfoundation/scratch-blocks/issues/130)) ([3d8b998](https://github.com/scratchfoundation/scratch-blocks/commit/3d8b998f3be7e76adf313a60b81f29b871266c94)) +* reenable the matrix field ([#49](https://github.com/scratchfoundation/scratch-blocks/issues/49)) ([aa3341b](https://github.com/scratchfoundation/scratch-blocks/commit/aa3341b69e51de847f177124660a304e2bca8446)) +* reenable the mobile numpad field ([#54](https://github.com/scratchfoundation/scratch-blocks/issues/54)) ([003afd0](https://github.com/scratchfoundation/scratch-blocks/commit/003afd04b98ae1d3688b5356885e27c116fc61ac)) +* reenable the note block and picker field ([#48](https://github.com/scratchfoundation/scratch-blocks/issues/48)) ([de62d77](https://github.com/scratchfoundation/scratch-blocks/commit/de62d7752eb80f67a654ae2e87820ad4183e833d)) +* reenable the vertical separator field ([#46](https://github.com/scratchfoundation/scratch-blocks/issues/46)) ([48e931f](https://github.com/scratchfoundation/scratch-blocks/commit/48e931fa9de3e31b18f088557c2cbfd0738b4bc6)) +* remove canvas transition ([#129](https://github.com/scratchfoundation/scratch-blocks/issues/129)) ([ff4b115](https://github.com/scratchfoundation/scratch-blocks/commit/ff4b1151d702be0be5dabf1f1032fd08b8d58e3e)) +* remove underscore from a few createProcedureDefCallback calls ([#40](https://github.com/scratchfoundation/scratch-blocks/issues/40)) ([4e794f6](https://github.com/scratchfoundation/scratch-blocks/commit/4e794f6503dfba1d12f9e99905660d305d34e309)) +* render the procedure definition block like Scratch ([#115](https://github.com/scratchfoundation/scratch-blocks/issues/115)) ([2a543f5](https://github.com/scratchfoundation/scratch-blocks/commit/2a543f56f6d45bcc970cfe56dbfdea6ff26c833a)) +* resolve error when adding the stop block to the workspace ([#56](https://github.com/scratchfoundation/scratch-blocks/issues/56)) ([f3e059c](https://github.com/scratchfoundation/scratch-blocks/commit/f3e059cbf5cf3ce4e063871632a213c8d58f1000)) +* resolve various UI issues ([#117](https://github.com/scratchfoundation/scratch-blocks/issues/117)) ([4b74d5c](https://github.com/scratchfoundation/scratch-blocks/commit/4b74d5ca4ffaf7dfd7c040bce5cfd725c96d848a)) +* select new variable blocks' monitor checkboxes after creation ([#140](https://github.com/scratchfoundation/scratch-blocks/issues/140)) ([3811d93](https://github.com/scratchfoundation/scratch-blocks/commit/3811d93f57c2a72a8a830a7326e2d27694bec7b7)) +* show connection highlights for boolean inputs ([#181](https://github.com/scratchfoundation/scratch-blocks/issues/181)) ([303611a](https://github.com/scratchfoundation/scratch-blocks/commit/303611a5475b4c5914d14c7fa04915d0cb0a0d03)) +* show the glow only when blocks are running ([#57](https://github.com/scratchfoundation/scratch-blocks/issues/57)) ([33e9e91](https://github.com/scratchfoundation/scratch-blocks/commit/33e9e91be1210c1699bc3491df5d9c7c191bc30d)) +* show the name of the list in the list getter block context menu ([#132](https://github.com/scratchfoundation/scratch-blocks/issues/132)) ([eb839fc](https://github.com/scratchfoundation/scratch-blocks/commit/eb839fcef9e8befad591cf35c00044adff9269f6)) +* update the flyout for compatibility with the new flyout API ([#209](https://github.com/scratchfoundation/scratch-blocks/issues/209)) ([7ce9991](https://github.com/scratchfoundation/scratch-blocks/commit/7ce9991b6f1b6a504847097ebd098f0ec1e50e52)) +* use non-deprecated input type constants ([#78](https://github.com/scratchfoundation/scratch-blocks/issues/78)) ([1f1c859](https://github.com/scratchfoundation/scratch-blocks/commit/1f1c8598a5dac8bb42dd82fff7edc7eda7fe4225)) +* use Scratch-style text blocks ([#37](https://github.com/scratchfoundation/scratch-blocks/issues/37)) ([6bbbdf7](https://github.com/scratchfoundation/scratch-blocks/commit/6bbbdf763ba43d3dc0af7bad15478f848455f4df)) +* use Scratch's FieldAngle ([#138](https://github.com/scratchfoundation/scratch-blocks/issues/138)) ([ef7911c](https://github.com/scratchfoundation/scratch-blocks/commit/ef7911cf7fd12d6370733bdd3778560925bd050c)) + + +* fix!: bump to v2.0 to reflect Blockly un-forking ([899a981](https://github.com/scratchfoundation/scratch-blocks/commit/899a981fb55c60e4b7822e9a77ebb0a65819d094)) + + +### Features + +* add a block inflater that supports recycling ([#207](https://github.com/scratchfoundation/scratch-blocks/issues/207)) ([0701679](https://github.com/scratchfoundation/scratch-blocks/commit/07016799832530cf851c14be484158e58bdaa9dd)) +* add bubbles/icons for block flyout checkboxes ([#208](https://github.com/scratchfoundation/scratch-blocks/issues/208)) ([39b2162](https://github.com/scratchfoundation/scratch-blocks/commit/39b2162db62973860c904f0b78f1d7f27abebc99)) +* add custom Scratch variable model and creation event classes ([#86](https://github.com/scratchfoundation/scratch-blocks/issues/86)) ([2598ede](https://github.com/scratchfoundation/scratch-blocks/commit/2598ede046de74164922bd919695eae65bd0c9b2)) +* clean up and export Scratch's variables.js ([#88](https://github.com/scratchfoundation/scratch-blocks/issues/88)) ([5c1acfe](https://github.com/scratchfoundation/scratch-blocks/commit/5c1acfe3dc1bb0bf406ba20ffd1e25b22356fbd7)) +* readd support for the custom Data toolbox category ([#87](https://github.com/scratchfoundation/scratch-blocks/issues/87)) ([dcfbf39](https://github.com/scratchfoundation/scratch-blocks/commit/dcfbf391cfcf2dbdf04c84b7e5c4d1463c18ed1d)) +* reenable Scratch's FieldVariable subclass ([#91](https://github.com/scratchfoundation/scratch-blocks/issues/91)) ([7c891e3](https://github.com/scratchfoundation/scratch-blocks/commit/7c891e35207b6f561917f27739917cb84755d57c)) + + +### Reverts + +* Revert "fix: add zoom controls config (#126)" (#128) ([8e1dc14](https://github.com/scratchfoundation/scratch-blocks/commit/8e1dc14483d6e012734a30b666af7aa5427c58f9)), closes [#126](https://github.com/scratchfoundation/scratch-blocks/issues/126) [#128](https://github.com/scratchfoundation/scratch-blocks/issues/128) + + +### BREAKING CHANGES + +* scratch-blocks is no longer a divergent fork of +Blockly, and instead depends on Blockly as a regular node_modules +dependency. + +Thanks, @gonfunko and everyone else at Google who helped with this +effort! + ## [1.1.86](https://github.com/scratchfoundation/scratch-blocks/compare/v1.1.85...v1.1.86) (2024-04-12) diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000000..0e190ba3e7 --- /dev/null +++ b/TODO.md @@ -0,0 +1,16 @@ +# To-Do list for scratch-blocks un-fork + +Before this un-forked version of `scratch-blocks` is fully ready for release, we need to: + +- [ ] Hook up i18n / l10n +- [x] Fix or remove Husky +- [x] Set up CI & CD +- [ ] Wait for Blockly v12 release, then update Blockly dependencies accordingly + +Things we need to do before we consider this project "done" but could wait until after the initial release: + +- [ ] Decide what we want to do about tests, then do it +- [ ] Pick linting rules, apply them, and enforce them +- [ ] Make sure the gh-pages setup is doing what we want, or remove it +- [ ] ??? +- [ ] Remove this TODO file once it's empty :) diff --git a/blocks_common/math.js b/blocks_common/math.js deleted file mode 100644 index 63a95e9887..0000000000 --- a/blocks_common/math.js +++ /dev/null @@ -1,159 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Math blocks for Blockly. - * @author q.neutron@gmail.com (Quynh Neutron) - */ -'use strict'; - -goog.provide('Blockly.Blocks.math'); - -goog.require('Blockly.Blocks'); - -goog.require('Blockly.Colours'); - -goog.require('Blockly.constants'); - -Blockly.Blocks['math_number'] = { - /** - * Block for generic numeric value. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "args0": [ - { - "type": "field_number", - "name": "NUM", - "value": "0" - } - ], - "output": "Number", - "outputShape": Blockly.OUTPUT_SHAPE_ROUND, - "colour": Blockly.Colours.textField, - "colourSecondary": Blockly.Colours.textField, - "colourTertiary": Blockly.Colours.textField, - "colourQuaternary": Blockly.Colours.textField - }); - } -}; - -Blockly.Blocks['math_integer'] = { - /** - * Block for integer value (no decimal, + or -). - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "args0": [ - { - "type": "field_number", - "name": "NUM", - "precision": 1 - } - ], - "output": "Number", - "outputShape": Blockly.OUTPUT_SHAPE_ROUND, - "colour": Blockly.Colours.textField, - "colourSecondary": Blockly.Colours.textField, - "colourTertiary": Blockly.Colours.textField, - "colourQuaternary": Blockly.Colours.textField - }); - } -}; - -Blockly.Blocks['math_whole_number'] = { - /** - * Block for whole number value, no negatives or decimals. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "args0": [ - { - "type": "field_number", - "name": "NUM", - "min": 0, - "precision": 1 - } - ], - "output": "Number", - "outputShape": Blockly.OUTPUT_SHAPE_ROUND, - "colour": Blockly.Colours.textField, - "colourSecondary": Blockly.Colours.textField, - "colourTertiary": Blockly.Colours.textField, - "colourQuaternary": Blockly.Colours.textField - }); - } -}; - -Blockly.Blocks['math_positive_number'] = { - /** - * Block for positive number value, with decimal. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "args0": [ - { - "type": "field_number", - "name": "NUM", - "min": 0 - } - ], - "output": "Number", - "outputShape": Blockly.OUTPUT_SHAPE_ROUND, - "colour": Blockly.Colours.textField, - "colourSecondary": Blockly.Colours.textField, - "colourTertiary": Blockly.Colours.textField, - "colourQuaternary": Blockly.Colours.textField - }); - } -}; - -Blockly.Blocks['math_angle'] = { - /** - * Block for angle picker. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "args0": [ - { - "type": "field_angle", - "name": "NUM", - "value": 90 - } - ], - "output": "Number", - "outputShape": Blockly.OUTPUT_SHAPE_ROUND, - "colour": Blockly.Colours.textField, - "colourSecondary": Blockly.Colours.textField, - "colourTertiary": Blockly.Colours.textField, - "colourQuaternary": Blockly.Colours.textField - }); - } -}; diff --git a/blocks_horizontal/control.js b/blocks_horizontal/control.js deleted file mode 100644 index 8fd7cf20a2..0000000000 --- a/blocks_horizontal/control.js +++ /dev/null @@ -1,212 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Massachusetts Institute of Technology - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Control blocks for Scratch (Horizontal) - * @author ascii@media.mit.edu - */ -'use strict'; - -goog.provide('Blockly.Blocks.control'); - -goog.require('Blockly.Blocks'); - -goog.require('Blockly.Colours'); - -Blockly.Blocks['control_repeat'] = { - /** - * Block for repeat n times (external number). - * https://blockly-demo.appspot.com/static/demos/blockfactory/index.html#so57n9 - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "control_repeat", - "message0": "%1 %2 %3", - "args0": [ - { - "type": "input_statement", - "name": "SUBSTACK" - }, - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "icons/control_repeat.svg", - "width": 40, - "height": 40, - "alt": "*", - "flip_rtl": true - }, - { - "type": "input_value", - "name": "TIMES", - "check": "Number" - } - ], - "inputsInline": true, - "previousStatement": null, - "nextStatement": null, - "category": Blockly.Categories.control, - "colour": Blockly.Colours.control.primary, - "colourSecondary": Blockly.Colours.control.secondary, - "colourTertiary": Blockly.Colours.control.tertiary, - "colourQuaternary": Blockly.Colours.control.quaternary - }); - } -}; - -Blockly.Blocks['control_forever'] = { - /** - * Block for repeat n times (external number). - * https://blockly-demo.appspot.com/static/demos/blockfactory/index.html#5eke39 - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "control_forever", - "message0": "%1 %2", - "args0": [ - { - "type": "input_statement", - "name": "SUBSTACK" - }, - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "icons/control_forever.svg", - "width": 40, - "height": 40, - "alt": "*", - "flip_rtl": true - } - ], - "inputsInline": true, - "previousStatement": null, - "category": Blockly.Categories.control, - "colour": Blockly.Colours.control.primary, - "colourSecondary": Blockly.Colours.control.secondary, - "colourTertiary": Blockly.Colours.control.tertiary, - "colourQuaternary": Blockly.Colours.control.quaternary - }); - } -}; - -Blockly.Blocks['control_repeat'] = { - /** - * Block for repeat n times (external number). - * https://blockly-demo.appspot.com/static/demos/blockfactory/index.html#so57n9 - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "control_repeat", - "message0": "%1 %2 %3", - "args0": [ - { - "type": "input_statement", - "name": "SUBSTACK" - }, - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "icons/control_repeat.svg", - "width": 40, - "height": 40, - "alt": "*", - "flip_rtl": true - }, - { - "type": "input_value", - "name": "TIMES", - "check": "Number" - } - ], - "inputsInline": true, - "previousStatement": null, - "nextStatement": null, - "category": Blockly.Categories.control, - "colour": Blockly.Colours.control.primary, - "colourSecondary": Blockly.Colours.control.secondary, - "colourTertiary": Blockly.Colours.control.tertiary, - "colourQuaternary": Blockly.Colours.control.quaternary - }); - } -}; - -Blockly.Blocks['control_stop'] = { - /** - * Block for stop all scripts. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "control_stop", - "message0": "%1", - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "icons/control_stop.svg", - "width": 40, - "height": 40, - "alt": "Stop" - } - ], - "inputsInline": true, - "previousStatement": null, - "category": Blockly.Categories.control, - "colour": Blockly.Colours.control.primary, - "colourSecondary": Blockly.Colours.control.secondary, - "colourTertiary": Blockly.Colours.control.tertiary, - "colourQuaternary": Blockly.Colours.control.quaternary - }); - } -}; - -Blockly.Blocks['control_wait'] = { - /** - * Block to wait (pause) stack. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "control_wait", - "message0": "%1 %2", - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "icons/control_wait.svg", - "width": 40, - "height": 40, - "alt": "Wait" - }, - { - "type": "input_value", - "name": "DURATION", - "check": "Number" - } - ], - "inputsInline": true, - "previousStatement": null, - "nextStatement": null, - "category": Blockly.Categories.control, - "colour": Blockly.Colours.control.primary, - "colourSecondary": Blockly.Colours.control.secondary, - "colourTertiary": Blockly.Colours.control.tertiary, - "colourQuaternary": Blockly.Colours.control.quaternary - }); - } -}; diff --git a/blocks_horizontal/default_toolbox.js b/blocks_horizontal/default_toolbox.js deleted file mode 100644 index f5fd741c52..0000000000 --- a/blocks_horizontal/default_toolbox.js +++ /dev/null @@ -1,139 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Massachusetts Institute of Technology - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -goog.provide('Blockly.Blocks.defaultToolbox'); - -goog.require('Blockly.Blocks'); - -/** - * @fileoverview Provide a default toolbox XML. - */ - -Blockly.Blocks.defaultToolbox = ''; - -Blockly.Blocks.defaultToolboxSimple = ''; diff --git a/blocks_horizontal/event.js b/blocks_horizontal/event.js deleted file mode 100644 index faada4fbfd..0000000000 --- a/blocks_horizontal/event.js +++ /dev/null @@ -1,190 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Massachusetts Institute of Technology - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Control blocks for Scratch (Horizontal) - * @author ascii@media.mit.edu - */ -'use strict'; - -goog.provide('Blockly.Blocks.event'); - -goog.require('Blockly.Blocks'); - -goog.require('Blockly.Colours'); - -Blockly.Blocks['event_whenflagclicked'] = { - /** - * Block for when flag clicked. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "event_whenflagclicked", - "message0": "%1", - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "icons/event_whenflagclicked.svg", - "width": 40, - "height": 40, - "alt": "When green flag clicked", - "flip_rtl": true - } - ], - "inputsInline": true, - "nextStatement": null, - "category": Blockly.Categories.event, - "colour": Blockly.Colours.event.primary, - "colourSecondary": Blockly.Colours.event.secondary, - "colourTertiary": Blockly.Colours.event.tertiary, - "colourQuaternary": Blockly.Colours.event.quaternary - }); - } -}; - -Blockly.Blocks['dropdown_whenbroadcast'] = { - /** - * Block for when broadcast dropdown (used for shadow). - * @this Blockly.Block - */ - init: function() { - this.appendDummyInput() - .appendField(new Blockly.FieldIconMenu( - [ - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_when-broadcast-received_blue.svg', - value: 'blue', width: 48, height: 48, alt: 'Blue'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_when-broadcast-received_green.svg', - value: 'green', width: 48, height: 48, alt: 'Green'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_when-broadcast-received_coral.svg', - value: 'coral', width: 48, height: 48, alt: 'Coral'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_when-broadcast-received_magenta.svg', - value: 'magenta', width: 48, height: 48, alt: 'Magenta'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_when-broadcast-received_orange.svg', - value: 'orange', width: 48, height: 48, alt: 'Orange'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_when-broadcast-received_purple.svg', - value: 'purple', width: 48, height: 48, alt: 'Purple'} - ]), 'CHOICE'); - this.setOutput(true); - this.setColour(Blockly.Colours.event.primary, - Blockly.Colours.event.secondary, - Blockly.Colours.event.tertiary, - Blockly.Colours.event.quaternary - ); - } -}; - -Blockly.Blocks['event_whenbroadcastreceived'] = { - /** - * Block for when broadcast received. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "event_whenbroadcastreceived", - "message0": "%1 %2", - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "icons/event_when-broadcast-received_blue.svg", - "width": 40, - "height": 40, - "alt": "Broadcast received" - }, - { - "type": "input_value", - "name": "CHOICE" - } - ], - "inputsInline": true, - "nextStatement": null, - "category": Blockly.Categories.event, - "colour": Blockly.Colours.event.primary, - "colourSecondary": Blockly.Colours.event.secondary, - "colourTertiary": Blockly.Colours.event.tertiary, - "colourQuaternary": Blockly.Colours.event.quaternary - }); - } -}; - -Blockly.Blocks['dropdown_broadcast'] = { - /** - * Block for broadcast dropdown (used for shadow). - * @this Blockly.Block - */ - init: function() { - this.appendDummyInput() - .appendField(new Blockly.FieldIconMenu( - [ - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_broadcast_blue.svg', - value: 'blue', width: 48, height: 48, alt: 'Blue'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_broadcast_green.svg', - value: 'green', width: 48, height: 48, alt: 'Green'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_broadcast_coral.svg', - value: 'coral', width: 48, height: 48, alt: 'Coral'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_broadcast_magenta.svg', - value: 'magenta', width: 48, height: 48, alt: 'Magenta'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_broadcast_orange.svg', - value: 'orange', width: 48, height: 48, alt: 'Orange'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/event_broadcast_purple.svg', - value: 'purple', width: 48, height: 48, alt: 'Purple'} - ]), 'CHOICE'); - this.setOutput(true); - this.setColour(Blockly.Colours.event.primary, - Blockly.Colours.event.secondary, - Blockly.Colours.event.tertiary, - Blockly.Colours.event.quaternary - ); - } -}; - -Blockly.Blocks['event_broadcast'] = { - /** - * Block to send a broadcast. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "event_broadcast", - "message0": "%1 %2", - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "icons/event_broadcast_blue.svg", - "width": 40, - "height": 40, - "alt": "Broadcast" - }, - { - "type": "input_value", - "name": "CHOICE" - } - ], - "inputsInline": true, - "previousStatement": null, - "nextStatement": null, - "category": Blockly.Categories.event, - "colour": Blockly.Colours.event.primary, - "colourSecondary": Blockly.Colours.event.secondary, - "colourTertiary": Blockly.Colours.event.tertiary, - "colourQuaternary": Blockly.Colours.event.quaternary - }); - } -}; diff --git a/blocks_horizontal/wedo.js b/blocks_horizontal/wedo.js deleted file mode 100644 index 723fa9db65..0000000000 --- a/blocks_horizontal/wedo.js +++ /dev/null @@ -1,325 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Massachusetts Institute of Technology - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Wedo blocks for Scratch (Horizontal) - * @author ascii@media.mit.edu - */ -'use strict'; - -goog.provide('Blockly.Blocks.wedo'); - -goog.require('Blockly.Blocks'); - -goog.require('Blockly.Colours'); - -Blockly.Blocks['dropdown_wedo_setcolor'] = { - /** - * Block for set color drop-down (used for shadow). - * @this Blockly.Block - */ - init: function() { - this.appendDummyInput() - .appendField(new Blockly.FieldIconMenu( - [ - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_mystery.svg', - value: 'mystery', width: 48, height: 48, alt: 'Mystery'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_yellow.svg', - value: 'yellow', width: 48, height: 48, alt: 'Yellow'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_orange.svg', - value: 'orange', width: 48, height: 48, alt: 'Orange'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_coral.svg', - value: 'coral', width: 48, height: 48, alt: 'Coral'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_magenta.svg', - value: 'magenta', width: 48, height: 48, alt: 'Magenta'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_purple.svg', - value: 'purple', width: 48, height: 48, alt: 'Purple'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_blue.svg', - value: 'blue', width: 48, height: 48, alt: 'Blue'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_green.svg', - value: 'green', width: 48, height: 48, alt: 'Green'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/set-led_white.svg', - value: 'white', width: 48, height: 48, alt: 'White'} - ]), 'CHOICE'); - this.setOutput(true); - this.setColour(Blockly.Colours.looks.primary, - Blockly.Colours.looks.secondary, - Blockly.Colours.looks.tertiary, - Blockly.Colours.looks.quaternary - ); - } -}; - -Blockly.Blocks['wedo_setcolor'] = { - /** - * Block to set color of LED - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "wedo_setcolor", - "message0": "%1 %2", - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "icons/set-led_blue.svg", - "width": 40, - "height": 40, - "alt": "Set LED Color" - }, - { - "type": "input_value", - "name": "CHOICE" - } - ], - "inputsInline": true, - "previousStatement": null, - "nextStatement": null, - "category": Blockly.Categories.looks, - "colour": Blockly.Colours.looks.primary, - "colourSecondary": Blockly.Colours.looks.secondary, - "colourTertiary": Blockly.Colours.looks.tertiary, - "colourQuaternary": Blockly.Colours.looks.quaternary - }); - } -}; - -Blockly.Blocks['wedo_motorclockwise'] = { - /** - * Block to spin motor clockwise. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "wedo_motorclockwise", - "message0": "%1 %2", - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "icons/wedo_motor-clockwise.svg", - "width": 40, - "height": 40, - "alt": "Turn motor clockwise" - }, - { - "type": "input_value", - "name": "DURATION", - "check": "Number" - } - ], - "inputsInline": true, - "previousStatement": null, - "nextStatement": null, - "category": Blockly.Categories.motion, - "colour": Blockly.Colours.motion.primary, - "colourSecondary": Blockly.Colours.motion.secondary, - "colourTertiary": Blockly.Colours.motion.tertiary, - "colourQuaternary": Blockly.Colours.motion.quaternary - }); - } -}; - -Blockly.Blocks['wedo_motorcounterclockwise'] = { - /** - * Block to spin motor counter-clockwise. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "wedo_motorcounterclockwise", - "message0": "%1 %2", - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "icons/wedo_motor-counterclockwise.svg", - "width": 40, - "height": 40, - "alt": "Turn motor counter-clockwise" - }, - { - "type": "input_value", - "name": "DURATION", - "check": "Number" - } - ], - "inputsInline": true, - "previousStatement": null, - "nextStatement": null, - "category": Blockly.Categories.motion, - "colour": Blockly.Colours.motion.primary, - "colourSecondary": Blockly.Colours.motion.secondary, - "colourTertiary": Blockly.Colours.motion.tertiary, - "colourQuaternary": Blockly.Colours.motion.quaternary - }); - } -}; - -Blockly.Blocks['dropdown_wedo_motorspeed'] = { - /** - * Block for motor speed drop-down (used for shadow). - * @this Blockly.Block - */ - init: function() { - this.appendDummyInput() - .appendField(new Blockly.FieldIconMenu( - [ - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_motor-speed_slow.svg', - value: 'slow', width: 48, height: 48, alt: 'Slow'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_motor-speed_med.svg', - value: 'medium', width: 48, height: 48, alt: 'Medium'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_motor-speed_fast.svg', - value: 'fast', width: 48, height: 48, alt: 'Fast'} - ]), 'CHOICE'); - this.setOutput(true); - this.setColour(Blockly.Colours.motion.primary, - Blockly.Colours.motion.secondary, - Blockly.Colours.motion.tertiary, - Blockly.Colours.motion.quaternary - ); - } -}; - -Blockly.Blocks['wedo_motorspeed'] = { - /** - * Block to set motor speed. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "wedo_motorspeed", - "message0": "%1 %2", - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "icons/wedo_motor-speed_fast.svg", - "width": 40, - "height": 40, - "alt": "Motor Speed" - }, - { - "type": "input_value", - "name": "CHOICE" - } - ], - "inputsInline": true, - "previousStatement": null, - "nextStatement": null, - "category": Blockly.Categories.motion, - "colour": Blockly.Colours.motion.primary, - "colourSecondary": Blockly.Colours.motion.secondary, - "colourTertiary": Blockly.Colours.motion.tertiary, - "colourQuaternary": Blockly.Colours.motion.quaternary - }); - } -}; - -Blockly.Blocks['dropdown_wedo_whentilt'] = { - /** - * Block for when tilt drop-down (used for shadow). - * @this Blockly.Block - */ - init: function() { - this.appendDummyInput() - .appendField(new Blockly.FieldIconMenu( - [ - {type: 'placeholder', width: 48, height: 48}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_when-tilt-forward.svg', - value: 'forward', width: 48, height: 48, alt: 'Tilt forward'}, - {type: 'placeholder', width: 48, height: 48}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_when-tilt-left.svg', - value: 'left', width: 48, height: 48, alt: 'Tilt left'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_when-tilt.svg', - value: 'any', width: 48, height: 48, alt: 'Tilt any'}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_when-tilt-right.svg', - value: 'right', width: 48, height: 48, alt: 'Tilt right'}, - {type: 'placeholder', width: 48, height: 48}, - {src: Blockly.mainWorkspace.options.pathToMedia + 'icons/wedo_when-tilt-backward.svg', - value: 'backward', width: 48, height: 48, alt: 'Tilt backward'} - ]), 'CHOICE'); - this.setOutput(true); - this.setColour(Blockly.Colours.event.primary, - Blockly.Colours.event.secondary, - Blockly.Colours.event.tertiary, - Blockly.Colours.event.quaternary - ); - } -}; - -Blockly.Blocks['wedo_whentilt'] = { - /** - * Block for when tilted. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "wedo_whentilt", - "message0": "%1 %2", - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "icons/wedo_when-tilt.svg", - "width": 40, - "height": 40, - "alt": "When tilted" - }, - { - "type": "input_value", - "name": "CHOICE" - } - ], - "inputsInline": true, - "nextStatement": null, - "category": Blockly.Categories.event, - "colour": Blockly.Colours.event.primary, - "colourSecondary": Blockly.Colours.event.secondary, - "colourTertiary": Blockly.Colours.event.tertiary, - "colourQuaternary": Blockly.Colours.event.quaternary - }); - } -}; - -Blockly.Blocks['wedo_whendistanceclose'] = { - /** - * Block for when distance sensor is close. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "wedo_whendistanceclose", - "message0": "%1", - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "icons/wedo_when-distance_close.svg", - "width": 40, - "height": 40, - "alt": "When distance close" - } - ], - "inputsInline": true, - "nextStatement": null, - "category": Blockly.Categories.event, - "colour": Blockly.Colours.event.primary, - "colourSecondary": Blockly.Colours.event.secondary, - "colourTertiary": Blockly.Colours.event.tertiary, - "colourQuaternary": Blockly.Colours.event.quaternary - }); - } -}; diff --git a/blocks_vertical/control.js b/blocks_vertical/control.js deleted file mode 100644 index 3418745882..0000000000 --- a/blocks_vertical/control.js +++ /dev/null @@ -1,532 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Massachusetts Institute of Technology - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -goog.provide('Blockly.Blocks.control'); - -goog.require('Blockly.Blocks'); -goog.require('Blockly.Colours'); -goog.require('Blockly.ScratchBlocks.VerticalExtensions'); - - -Blockly.Blocks['control_forever'] = { - /** - * Block for repeat n times (external number). - * https://blockly-demo.appspot.com/static/demos/blockfactory/index.html#5eke39 - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "control_forever", - "message0": Blockly.Msg.CONTROL_FOREVER, - "message1": "%1", // Statement - "message2": "%1", // Icon - "lastDummyAlign2": "RIGHT", - "args1": [ - { - "type": "input_statement", - "name": "SUBSTACK" - } - ], - "args2": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "repeat.svg", - "width": 24, - "height": 24, - "alt": "*", - "flip_rtl": true - } - ], - "category": Blockly.Categories.control, - "extensions": ["colours_control", "shape_end"] - }); - } -}; - -Blockly.Blocks['control_repeat'] = { - /** - * Block for repeat n times (external number). - * https://blockly-demo.appspot.com/static/demos/blockfactory/index.html#so57n9 - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "control_repeat", - "message0": Blockly.Msg.CONTROL_REPEAT, - "message1": "%1", // Statement - "message2": "%1", // Icon - "lastDummyAlign2": "RIGHT", - "args0": [ - { - "type": "input_value", - "name": "TIMES" - } - ], - "args1": [ - { - "type": "input_statement", - "name": "SUBSTACK" - } - ], - "args2": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "repeat.svg", - "width": 24, - "height": 24, - "alt": "*", - "flip_rtl": true - } - ], - "category": Blockly.Categories.control, - "extensions": ["colours_control", "shape_statement"] - }); - } -}; - -Blockly.Blocks['control_if'] = { - /** - * Block for if-then. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "type": "control_if", - "message0": Blockly.Msg.CONTROL_IF, - "message1": "%1", // Statement - "args0": [ - { - "type": "input_value", - "name": "CONDITION", - "check": "Boolean" - } - ], - "args1": [ - { - "type": "input_statement", - "name": "SUBSTACK" - } - ], - "category": Blockly.Categories.control, - "extensions": ["colours_control", "shape_statement"] - }); - } -}; - -Blockly.Blocks['control_if_else'] = { - /** - * Block for if-else. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "type": "control_if_else", - "message0": Blockly.Msg.CONTROL_IF, - "message1": "%1", - "message2": Blockly.Msg.CONTROL_ELSE, - "message3": "%1", - "args0": [ - { - "type": "input_value", - "name": "CONDITION", - "check": "Boolean" - } - ], - "args1": [ - { - "type": "input_statement", - "name": "SUBSTACK" - } - ], - "args3": [ - { - "type": "input_statement", - "name": "SUBSTACK2" - } - ], - "category": Blockly.Categories.control, - "extensions": ["colours_control", "shape_statement"] - }); - } -}; - -Blockly.Blocks['control_stop'] = { - /** - * Block for stop all scripts. - * @this Blockly.Block - */ - init: function() { - var ALL_SCRIPTS = 'all'; - var THIS_SCRIPT = 'this script'; - var OTHER_SCRIPTS = 'other scripts in sprite'; - var stopDropdown = new Blockly.FieldDropdown(function() { - if (this.sourceBlock_ && - this.sourceBlock_.nextConnection && - this.sourceBlock_.nextConnection.isConnected()) { - return [ - [Blockly.Msg.CONTROL_STOP_OTHER, OTHER_SCRIPTS] - ]; - } - return [[Blockly.Msg.CONTROL_STOP_ALL, ALL_SCRIPTS], - [Blockly.Msg.CONTROL_STOP_THIS, THIS_SCRIPT], - [Blockly.Msg.CONTROL_STOP_OTHER, OTHER_SCRIPTS] - ]; - }, function(option) { - // Create an event group to keep field value and mutator in sync - // Return null at the end because setValue is called here already. - Blockly.Events.setGroup(true); - var oldMutation = Blockly.Xml.domToText(this.sourceBlock_.mutationToDom()); - this.sourceBlock_.setNextStatement(option == OTHER_SCRIPTS); - var newMutation = Blockly.Xml.domToText(this.sourceBlock_.mutationToDom()); - Blockly.Events.fire(new Blockly.Events.BlockChange(this.sourceBlock_, - 'mutation', null, oldMutation, newMutation)); - this.setValue(option); - Blockly.Events.setGroup(false); - return null; - }); - this.appendDummyInput() - .appendField(Blockly.Msg.CONTROL_STOP) - .appendField(stopDropdown, 'STOP_OPTION'); - this.setCategory(Blockly.Categories.control); - this.setColour(Blockly.Colours.control.primary, - Blockly.Colours.control.secondary, - Blockly.Colours.control.tertiary, - Blockly.Colours.control.quaternary - ); - this.setPreviousStatement(true); - }, - mutationToDom: function() { - var container = document.createElement('mutation'); - container.setAttribute('hasnext', this.nextConnection != null); - return container; - }, - domToMutation: function(xmlElement) { - var hasNext = (xmlElement.getAttribute('hasnext') == 'true'); - this.setNextStatement(hasNext); - } -}; - -Blockly.Blocks['control_wait'] = { - /** - * Block to wait (pause) stack. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "control_wait", - "message0": Blockly.Msg.CONTROL_WAIT, - "args0": [ - { - "type": "input_value", - "name": "DURATION" - } - ], - "category": Blockly.Categories.control, - "extensions": ["colours_control", "shape_statement"] - }); - } -}; - -Blockly.Blocks['control_wait_until'] = { - /** - * Block to wait until a condition becomes true. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.CONTROL_WAITUNTIL, - "args0": [ - { - "type": "input_value", - "name": "CONDITION", - "check": "Boolean" - } - ], - "category": Blockly.Categories.control, - "extensions": ["colours_control", "shape_statement"] - }); - } -}; - -Blockly.Blocks['control_repeat_until'] = { - /** - * Block to repeat until a condition becomes true. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.CONTROL_REPEATUNTIL, - "message1": "%1", - "message2": "%1", - "lastDummyAlign2": "RIGHT", - "args0": [ - { - "type": "input_value", - "name": "CONDITION", - "check": "Boolean" - } - ], - "args1": [ - { - "type": "input_statement", - "name": "SUBSTACK" - } - ], - "args2": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "repeat.svg", - "width": 24, - "height": 24, - "alt": "*", - "flip_rtl": true - } - ], - "category": Blockly.Categories.control, - "extensions": ["colours_control", "shape_statement"] - }); - } -}; - -Blockly.Blocks['control_while'] = { - /** - * Block to repeat until a condition becomes false. - * (This is an obsolete "hacked" block, for compatibility with 2.0.) - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.CONTROL_WHILE, - "message1": "%1", - "message2": "%1", - "lastDummyAlign2": "RIGHT", - "args0": [ - { - "type": "input_value", - "name": "CONDITION", - "check": "Boolean" - } - ], - "args1": [ - { - "type": "input_statement", - "name": "SUBSTACK" - } - ], - "args2": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "repeat.svg", - "width": 24, - "height": 24, - "alt": "*", - "flip_rtl": true - } - ], - "category": Blockly.Categories.control, - "extensions": ["colours_control", "shape_statement"] - }); - } -}; - -Blockly.Blocks['control_for_each'] = { - /** - * Block for for-each. This is an obsolete block that is implemented for - * compatibility with Scratch 2.0 projects. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "type": "control_for_each", - "message0": Blockly.Msg.CONTROL_FOREACH, - "message1": "%1", - "args0": [ - { - "type": "field_variable", - "name": "VARIABLE" - }, - { - "type": "input_value", - "name": "VALUE" - } - ], - "args1": [ - { - "type": "input_statement", - "name": "SUBSTACK" - } - ], - "category": Blockly.Categories.control, - "extensions": ["colours_control", "shape_statement"] - }); - } -}; - -Blockly.Blocks['control_start_as_clone'] = { - /** - * Block for "when I start as a clone" hat. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "control_start_as_clone", - "message0": Blockly.Msg.CONTROL_STARTASCLONE, - "args0": [ - ], - "category": Blockly.Categories.control, - "extensions": ["colours_control", "shape_hat"] - }); - } -}; - -Blockly.Blocks['control_create_clone_of_menu'] = { - /** - * Create-clone drop-down menu. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "CLONE_OPTION", - "options": [ - [Blockly.Msg.CONTROL_CREATECLONEOF_MYSELF, '_myself_'] - ] - } - ], - "extensions": ["colours_control", "output_string"] - }); - } -}; - -Blockly.Blocks['control_create_clone_of'] = { - /** - * Block for "create clone of..." - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "control_start_as_clone", - "message0": Blockly.Msg.CONTROL_CREATECLONEOF, - "args0": [ - { - "type": "input_value", - "name": "CLONE_OPTION" - } - ], - "category": Blockly.Categories.control, - "extensions": ["colours_control", "shape_statement"] - }); - } -}; - -Blockly.Blocks['control_delete_this_clone'] = { - /** - * Block for "delete this clone." - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.CONTROL_DELETETHISCLONE, - "args0": [ - ], - "category": Blockly.Categories.control, - "extensions": ["colours_control", "shape_end"] - }); - } -}; - -Blockly.Blocks['control_get_counter'] = { - /** - * Block to get the counter value. This is an obsolete block that is - * implemented for compatibility with Scratch 2.0 projects. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.CONTROL_COUNTER, - "category": Blockly.Categories.control, - "extensions": ["colours_control", "output_number"] - }); - } -}; - -Blockly.Blocks['control_incr_counter'] = { - /** - * Block to add one to the counter value. This is an obsolete block that is - * implemented for compatibility with Scratch 2.0 projects. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.CONTROL_INCRCOUNTER, - "category": Blockly.Categories.control, - "extensions": ["colours_control", "shape_statement"] - }); - } -}; - -Blockly.Blocks['control_clear_counter'] = { - /** - * Block to clear the counter value. This is an obsolete block that is - * implemented for compatibility with Scratch 2.0 projects. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.CONTROL_CLEARCOUNTER, - "category": Blockly.Categories.control, - "extensions": ["colours_control", "shape_statement"] - }); - } -}; - -Blockly.Blocks['control_all_at_once'] = { - /** - * Block to run the contained script. This is an obsolete block that is - * implemented for compatibility with Scratch 2.0 projects. Note that - * this was originally designed to run all of the contained blocks - * (sequentially, like normal) within a single frame, but this feature - * was removed in place of custom blocks marked "run without screen - * refresh". The "all at once" block was changed to run the contained - * blocks ordinarily, functioning the same way as an "if" block with a - * reporter that is always true (e.g. "if 1 = 1"). Also note that the - * Scratch 2.0 spec for this block is "warpSpeed", but the label shows - * "all at once". - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.CONTROL_ALLATONCE, - "message1": "%1", // Statement - "args1": [ - { - "type": "input_statement", - "name": "SUBSTACK" - } - ], - "category": Blockly.Categories.control, - "extensions": ["colours_control", "shape_statement"] - }); - } -}; diff --git a/blocks_vertical/data.js b/blocks_vertical/data.js deleted file mode 100644 index 726697d47d..0000000000 --- a/blocks_vertical/data.js +++ /dev/null @@ -1,666 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Massachusetts Institute of Technology - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -goog.provide('Blockly.Blocks.data'); -goog.provide('Blockly.Constants.Data'); - -goog.require('Blockly.Blocks'); -goog.require('Blockly.Colours'); -goog.require('Blockly.constants'); -goog.require('Blockly.ScratchBlocks.VerticalExtensions'); - - -Blockly.Blocks['data_variable'] = { - /** - * Block of Variables - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "lastDummyAlign0": "CENTRE", - "args0": [ - { - "type": "field_variable_getter", - "text": "", - "name": "VARIABLE", - "variableType": "" - } - ], - "category": Blockly.Categories.data, - "checkboxInFlyout": true, - "extensions": ["contextMenu_getVariableBlock", "colours_data", "output_string"] - }); - } -}; - -Blockly.Blocks['data_setvariableto'] = { - /** - * Block to set variable to a certain value - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.DATA_SETVARIABLETO, - "args0": [ - { - "type": "field_variable", - "name": "VARIABLE" - }, - { - "type": "input_value", - "name": "VALUE" - } - ], - "category": Blockly.Categories.data, - "extensions": ["colours_data", "shape_statement"] - }); - } -}; - -Blockly.Blocks['data_changevariableby'] = { - /** - * Block to change variable by a certain value - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.DATA_CHANGEVARIABLEBY, - "args0": [ - { - "type": "field_variable", - "name": "VARIABLE" - }, - { - "type": "input_value", - "name": "VALUE" - } - ], - "category": Blockly.Categories.data, - "extensions": ["colours_data", "shape_statement"] - }); - } -}; - -Blockly.Blocks['data_showvariable'] = { - /** - * Block to show a variable - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.DATA_SHOWVARIABLE, - "args0": [ - { - "type": "field_variable", - "name": "VARIABLE" - } - ], - "previousStatement": null, - "nextStatement": null, - "category": Blockly.Categories.data, - "extensions": ["colours_data"] - }); - } -}; - -Blockly.Blocks['data_hidevariable'] = { - /** - * Block to hide a variable - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.DATA_HIDEVARIABLE, - "args0": [ - { - "type": "field_variable", - "name": "VARIABLE" - } - ], - "previousStatement": null, - "nextStatement": null, - "category": Blockly.Categories.data, - "extensions": ["colours_data"] - }); - } -}; - -Blockly.Blocks['data_listcontents'] = { - /** - * List reporter. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "args0": [ - { - "type": "field_variable_getter", - "text": "", - "name": "LIST", - "variableType": Blockly.LIST_VARIABLE_TYPE - } - ], - "category": Blockly.Categories.dataLists, - "extensions": ["contextMenu_getListBlock", "colours_data_lists", "output_string"], - "checkboxInFlyout": true - }); - } -}; - -Blockly.Blocks['data_listindexall'] = { - /** - * List index menu, with all option. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "args0": [ - { - "type": "field_numberdropdown", - "name": "INDEX", - "value": "1", - "min": 1, - "precision": 1, - "options": [ - ["1", "1"], - [Blockly.Msg.DATA_INDEX_LAST, "last"], - [Blockly.Msg.DATA_INDEX_ALL, "all"] - ] - } - ], - "category": Blockly.Categories.data, - "extensions": ["colours_textfield", "output_string"] - }); - } -}; - -Blockly.Blocks['data_listindexrandom'] = { - /** - * List index menu, with random option. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "args0": [ - { - "type": "field_numberdropdown", - "name": "INDEX", - "value": "1", - "min": 1, - "precision": 1, - "options": [ - ["1", "1"], - [Blockly.Msg.DATA_INDEX_LAST, "last"], - [Blockly.Msg.DATA_INDEX_RANDOM, "random"] - ] - } - ], - "category": Blockly.Categories.data, - "extensions": ["colours_textfield", "output_string"] - }); - } -}; - -Blockly.Blocks['data_addtolist'] = { - /** - * Block to add item to list. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.DATA_ADDTOLIST, - "args0": [ - { - "type": "input_value", - "name": "ITEM" - }, - { - "type": "field_variable", - "name": "LIST", - "variableTypes": [Blockly.LIST_VARIABLE_TYPE] - } - ], - "category": Blockly.Categories.dataLists, - "extensions": ["colours_data_lists", "shape_statement"] - }); - } -}; - -Blockly.Blocks['data_deleteoflist'] = { - /** - * Block to delete item from list. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.DATA_DELETEOFLIST, - "args0": [ - { - "type": "input_value", - "name": "INDEX" - }, - { - "type": "field_variable", - "name": "LIST", - "variableTypes": [Blockly.LIST_VARIABLE_TYPE] - } - ], - "category": Blockly.Categories.dataLists, - "extensions": ["colours_data_lists", "shape_statement"] - }); - } -}; - -Blockly.Blocks['data_deletealloflist'] = { - /** - * Block to delete all items from list. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.DATA_DELETEALLOFLIST, - "args0": [ - { - "type": "field_variable", - "name": "LIST", - "variableTypes": [Blockly.LIST_VARIABLE_TYPE] - } - ], - "category": Blockly.Categories.dataLists, - "extensions": ["colours_data_lists", "shape_statement"] - }); - } -}; - -Blockly.Blocks['data_insertatlist'] = { - /** - * Block to insert item to list. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.DATA_INSERTATLIST, - "args0": [ - { - "type": "input_value", - "name": "ITEM" - }, - { - "type": "input_value", - "name": "INDEX" - }, - { - "type": "field_variable", - "name": "LIST", - "variableTypes": [Blockly.LIST_VARIABLE_TYPE] - } - ], - "category": Blockly.Categories.dataLists, - "extensions": ["colours_data_lists", "shape_statement"] - }); - } -}; - -Blockly.Blocks['data_replaceitemoflist'] = { - /** - * Block to insert item to list. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.DATA_REPLACEITEMOFLIST, - "args0": [ - { - "type": "input_value", - "name": "INDEX" - }, - { - "type": "field_variable", - "name": "LIST", - "variableTypes": [Blockly.LIST_VARIABLE_TYPE] - }, - { - "type": "input_value", - "name": "ITEM" - } - ], - "category": Blockly.Categories.dataLists, - "extensions": ["colours_data_lists", "shape_statement"] - }); - } -}; - -Blockly.Blocks['data_itemoflist'] = { - /** - * Block for reporting item of list. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.DATA_ITEMOFLIST, - "args0": [ - { - "type": "input_value", - "name": "INDEX" - }, - { - "type": "field_variable", - "name": "LIST", - "variableTypes": [Blockly.LIST_VARIABLE_TYPE] - } - ], - "output": null, - "category": Blockly.Categories.dataLists, - "extensions": ["colours_data_lists"], - "outputShape": Blockly.OUTPUT_SHAPE_ROUND - }); - } -}; - -Blockly.Blocks['data_itemnumoflist'] = { - /** - * Block for reporting the item # of a string in a list. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.DATA_ITEMNUMOFLIST, - "args0": [ - { - "type": "input_value", - "name": "ITEM" - }, - { - "type": "field_variable", - "name": "LIST", - "variableTypes": [Blockly.LIST_VARIABLE_TYPE] - } - ], - "output": null, - "category": Blockly.Categories.dataLists, - "extensions": ["colours_data_lists"], - "outputShape": Blockly.OUTPUT_SHAPE_ROUND - }); - } -}; - -Blockly.Blocks['data_lengthoflist'] = { - /** - * Block for reporting length of list. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.DATA_LENGTHOFLIST, - "args0": [ - { - "type": "field_variable", - "name": "LIST", - "variableTypes": [Blockly.LIST_VARIABLE_TYPE] - } - ], - "category": Blockly.Categories.dataLists, - "extensions": ["colours_data_lists", "output_number"] - }); - } -}; - -Blockly.Blocks['data_listcontainsitem'] = { - /** - * Block to report whether list contains item. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.DATA_LISTCONTAINSITEM, - "args0": [ - { - "type": "field_variable", - "name": "LIST", - "variableTypes": [Blockly.LIST_VARIABLE_TYPE] - }, - { - "type": "input_value", - "name": "ITEM" - } - ], - "category": Blockly.Categories.dataLists, - "extensions": ["colours_data_lists", "output_boolean"] - }); - } -}; - -Blockly.Blocks['data_showlist'] = { - /** - * Block to show a list. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.DATA_SHOWLIST, - "args0": [ - { - "type": "field_variable", - "name": "LIST", - "variableTypes": [Blockly.LIST_VARIABLE_TYPE] - } - ], - "category": Blockly.Categories.dataLists, - "extensions": ["colours_data_lists", "shape_statement"] - }); - } -}; - -Blockly.Blocks['data_hidelist'] = { - /** - * Block to hide a list. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.DATA_HIDELIST, - "args0": [ - { - "type": "field_variable", - "name": "LIST", - "variableTypes": [Blockly.LIST_VARIABLE_TYPE] - } - ], - "category": Blockly.Categories.dataLists, - "extensions": ["colours_data_lists", "shape_statement"] - }); - } -}; - -/** - * Mixin to add a context menu for a data_variable block. It adds one item for - * each variable defined on the workspace. - * @mixin - * @augments Blockly.Block - * @package - * @readonly - */ -Blockly.Constants.Data.CUSTOM_CONTEXT_MENU_GET_VARIABLE_MIXIN = { - /** - * Add context menu option to change the selected variable. - * @param {!Array} options List of menu options to add to. - * @this Blockly.Block - */ - customContextMenu: function(options) { - var fieldName = 'VARIABLE'; - if (this.isCollapsed()) { - return; - } - var currentVarName = this.getField(fieldName).text_; - if (!this.isInFlyout) { - var variablesList = this.workspace.getVariablesOfType(''); - variablesList.sort(function(a, b) { - return Blockly.scratchBlocksUtils.compareStrings(a.name, b.name); - }); - for (var i = 0; i < variablesList.length; i++) { - var varName = variablesList[i].name; - if (varName == currentVarName) continue; - - var option = {enabled: true}; - option.text = varName; - - option.callback = - Blockly.Constants.Data.VARIABLE_OPTION_CALLBACK_FACTORY(this, - variablesList[i].getId(), fieldName); - options.push(option); - } - } else { - var renameOption = { - text: Blockly.Msg.RENAME_VARIABLE, - enabled: true, - callback: Blockly.Constants.Data.RENAME_OPTION_CALLBACK_FACTORY(this, - fieldName) - }; - var deleteOption = { - text: Blockly.Msg.DELETE_VARIABLE.replace('%1', currentVarName), - enabled: true, - callback: Blockly.Constants.Data.DELETE_OPTION_CALLBACK_FACTORY(this, - fieldName) - }; - options.push(renameOption); - options.push(deleteOption); - } - } -}; - -Blockly.Extensions.registerMixin('contextMenu_getVariableBlock', - Blockly.Constants.Data.CUSTOM_CONTEXT_MENU_GET_VARIABLE_MIXIN); - -/** - * Mixin to add a context menu for a data_listcontents block. It adds one item for - * each list defined on the workspace. - * @mixin - * @augments Blockly.Block - * @package - * @readonly - */ -Blockly.Constants.Data.CUSTOM_CONTEXT_MENU_GET_LIST_MIXIN = { - /** - * Add context menu option to change the selected list. - * @param {!Array} options List of menu options to add to. - * @this Blockly.Block - */ - customContextMenu: function(options) { - var fieldName = 'LIST'; - if (this.isCollapsed()) { - return; - } - var currentVarName = this.getField(fieldName).text_; - if (!this.isInFlyout) { - var variablesList = this.workspace.getVariablesOfType('list'); - variablesList.sort(function(a, b) { - return Blockly.scratchBlocksUtils.compareStrings(a.name, b.name); - }); - for (var i = 0; i < variablesList.length; i++) { - var varName = variablesList[i].name; - if (varName == currentVarName) continue; - - var option = {enabled: true}; - option.text = varName; - - option.callback = - Blockly.Constants.Data.VARIABLE_OPTION_CALLBACK_FACTORY(this, - variablesList[i].getId(), fieldName); - options.push(option); - } - } else { - var renameOption = { - text: Blockly.Msg.RENAME_LIST, - enabled: true, - callback: Blockly.Constants.Data.RENAME_OPTION_CALLBACK_FACTORY(this, - fieldName) - }; - var deleteOption = { - text: Blockly.Msg.DELETE_LIST.replace('%1', currentVarName), - enabled: true, - callback: Blockly.Constants.Data.DELETE_OPTION_CALLBACK_FACTORY(this, - fieldName) - }; - options.push(renameOption); - options.push(deleteOption); - } - } -}; -Blockly.Extensions.registerMixin('contextMenu_getListBlock', - Blockly.Constants.Data.CUSTOM_CONTEXT_MENU_GET_LIST_MIXIN); - -/** - * Callback factory for dropdown menu options associated with a variable getter - * block. Each variable on the workspace gets its own item in the dropdown - * menu, and clicking on that item changes the text of the field on the source - * block. - * @param {!Blockly.Block} block The block to update. - * @param {string} id The id of the variable to set on this block. - * @param {string} fieldName The name of the field to update on the block. - * @return {!function()} A function that updates the block with the new name. - */ -Blockly.Constants.Data.VARIABLE_OPTION_CALLBACK_FACTORY = function(block, - id, fieldName) { - return function() { - var variableField = block.getField(fieldName); - if (!variableField) { - console.log("Tried to get a variable field on the wrong type of block."); - } - variableField.setValue(id); - }; -}; - -/** - * Callback for rename variable dropdown menu option associated with a - * variable getter block. - * @param {!Blockly.Block} block The block with the variable to rename. - * @param {string} fieldName The name of the field to inspect on the block. - * @return {!function()} A function that renames the variable. - */ -Blockly.Constants.Data.RENAME_OPTION_CALLBACK_FACTORY = function(block, - fieldName) { - return function() { - var workspace = block.workspace; - var variable = block.getField(fieldName).getVariable(); - Blockly.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. - * @param {string} fieldName The name of the field to inspect on the block. - * @return {!function()} A function that deletes the variable. - */ -Blockly.Constants.Data.DELETE_OPTION_CALLBACK_FACTORY = function(block, - fieldName) { - return function() { - var workspace = block.workspace; - var variable = block.getField(fieldName).getVariable(); - workspace.deleteVariableById(variable.getId()); - }; -}; diff --git a/blocks_vertical/default_toolbox.js b/blocks_vertical/default_toolbox.js deleted file mode 100644 index 8c5624fad4..0000000000 --- a/blocks_vertical/default_toolbox.js +++ /dev/null @@ -1,564 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Massachusetts Institute of Technology - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -goog.provide('Blockly.Blocks.defaultToolbox'); - -goog.require('Blockly.Blocks'); - -/** - * @fileoverview Provide a default toolbox XML. - */ - -Blockly.Blocks.defaultToolbox = ''; diff --git a/blocks_vertical/event.js b/blocks_vertical/event.js deleted file mode 100644 index 5694893cf3..0000000000 --- a/blocks_vertical/event.js +++ /dev/null @@ -1,329 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Massachusetts Institute of Technology - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -goog.provide('Blockly.Blocks.event'); - -goog.require('Blockly.Blocks'); -goog.require('Blockly.Colours'); -goog.require('Blockly.constants'); -goog.require('Blockly.ScratchBlocks.VerticalExtensions'); - -Blockly.Blocks['event_whentouchingobject'] = { - /** - * Block for when a sprite is touching an object. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.EVENT_WHENTOUCHINGOBJECT, - "args0": [ - { - "type": "input_value", - "name": "TOUCHINGOBJECTMENU" - } - ], - "category": Blockly.Categories.event, - "extensions": ["colours_event", "shape_hat"] - }); - } -}; - -Blockly.Blocks['event_touchingobjectmenu'] = { - /** - * "Touching [Object]" Block Menu. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "TOUCHINGOBJECTMENU", - "options": [ - [Blockly.Msg.SENSING_TOUCHINGOBJECT_POINTER, '_mouse_'], - [Blockly.Msg.SENSING_TOUCHINGOBJECT_EDGE, '_edge_'] - ] - } - ], - "extensions": ["colours_event", "output_string"] - }); - } -}; - -Blockly.Blocks['event_whenflagclicked'] = { - /** - * Block for when flag clicked. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "event_whenflagclicked", - "message0": Blockly.Msg.EVENT_WHENFLAGCLICKED, - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "green-flag.svg", - "width": 24, - "height": 24, - "alt": "flag" - } - ], - "category": Blockly.Categories.event, - "extensions": ["colours_event", "shape_hat"] - }); - } -}; - -Blockly.Blocks['event_whenthisspriteclicked'] = { - /** - * Block for when this sprite clicked. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.EVENT_WHENTHISSPRITECLICKED, - "category": Blockly.Categories.event, - "extensions": ["colours_event", "shape_hat"] - }); - } - -}; - -Blockly.Blocks['event_whenstageclicked'] = { - /** - * Block for when the stage is clicked. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.EVENT_WHENSTAGECLICKED, - "category": Blockly.Categories.event, - "extensions": ["colours_event", "shape_hat"] - }); - } -}; - -Blockly.Blocks['event_whenbroadcastreceived'] = { - /** - * Block for when broadcast received. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "event_whenbroadcastreceived", - "message0": Blockly.Msg.EVENT_WHENBROADCASTRECEIVED, - "args0": [ - { - "type": "field_variable", - "name": "BROADCAST_OPTION", - "variableTypes": [Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE], - "variable": Blockly.Msg.DEFAULT_BROADCAST_MESSAGE_NAME - } - ], - "category": Blockly.Categories.event, - "extensions": ["colours_event", "shape_hat"] - }); - } -}; - -Blockly.Blocks['event_whenbackdropswitchesto'] = { - /** - * Block for when the current backdrop switched to a selected backdrop. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.EVENT_WHENBACKDROPSWITCHESTO, - "args0": [ - { - "type": "field_dropdown", - "name": "BACKDROP", - "options": [ - ['backdrop1', 'BACKDROP1'] - ] - } - ], - "category": Blockly.Categories.event, - "extensions": ["colours_event", "shape_hat"] - }); - } -}; - -Blockly.Blocks['event_whengreaterthan'] = { - /** - * Block for when loudness/timer/video motion is greater than the value. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.EVENT_WHENGREATERTHAN, - "args0": [ - { - "type": "field_dropdown", - "name": "WHENGREATERTHANMENU", - "options": [ - [Blockly.Msg.EVENT_WHENGREATERTHAN_LOUDNESS, 'LOUDNESS'], - [Blockly.Msg.EVENT_WHENGREATERTHAN_TIMER, 'TIMER'] - ] - }, - { - "type": "input_value", - "name": "VALUE" - } - ], - "category": Blockly.Categories.event, - "extensions": ["colours_event", "shape_hat"] - }); - } -}; - -Blockly.Blocks['event_broadcast_menu'] = { - /** - * Broadcast drop-down menu. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "args0": [ - { - "type": "field_variable", - "name": "BROADCAST_OPTION", - "variableTypes":[Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE], - "variable": Blockly.Msg.DEFAULT_BROADCAST_MESSAGE_NAME - } - ], - "colour": Blockly.Colours.event.secondary, - "colourSecondary": Blockly.Colours.event.secondary, - "colourTertiary": Blockly.Colours.event.tertiary, - "colourQuaternary": Blockly.Colours.event.quaternary, - "extensions": ["output_string"] - }); - } -}; - -Blockly.Blocks['event_broadcast'] = { - /** - * Block to send a broadcast. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "event_broadcast", - "message0": Blockly.Msg.EVENT_BROADCAST, - "args0": [ - { - "type": "input_value", - "name": "BROADCAST_INPUT" - } - ], - "category": Blockly.Categories.event, - "extensions": ["colours_event", "shape_statement"] - }); - } -}; - -Blockly.Blocks['event_broadcastandwait'] = { - /** - * Block to send a broadcast. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.EVENT_BROADCASTANDWAIT, - "args0": [ - { - "type":"input_value", - "name":"BROADCAST_INPUT" - } - ], - "category": Blockly.Categories.event, - "extensions": ["colours_event", "shape_statement"] - }); - } -}; - -Blockly.Blocks['event_whenkeypressed'] = { - /** - * Block to send a broadcast. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "event_whenkeypressed", - "message0": Blockly.Msg.EVENT_WHENKEYPRESSED, - "args0": [ - { - "type": "field_dropdown", - "name": "KEY_OPTION", - "options": [ - [Blockly.Msg.EVENT_WHENKEYPRESSED_SPACE, 'space'], - [Blockly.Msg.EVENT_WHENKEYPRESSED_UP, 'up arrow'], - [Blockly.Msg.EVENT_WHENKEYPRESSED_DOWN, 'down arrow'], - [Blockly.Msg.EVENT_WHENKEYPRESSED_RIGHT, 'right arrow'], - [Blockly.Msg.EVENT_WHENKEYPRESSED_LEFT, 'left arrow'], - [Blockly.Msg.EVENT_WHENKEYPRESSED_ANY, 'any'], - ['a', 'a'], - ['b', 'b'], - ['c', 'c'], - ['d', 'd'], - ['e', 'e'], - ['f', 'f'], - ['g', 'g'], - ['h', 'h'], - ['i', 'i'], - ['j', 'j'], - ['k', 'k'], - ['l', 'l'], - ['m', 'm'], - ['n', 'n'], - ['o', 'o'], - ['p', 'p'], - ['q', 'q'], - ['r', 'r'], - ['s', 's'], - ['t', 't'], - ['u', 'u'], - ['v', 'v'], - ['w', 'w'], - ['x', 'x'], - ['y', 'y'], - ['z', 'z'], - ['0', '0'], - ['1', '1'], - ['2', '2'], - ['3', '3'], - ['4', '4'], - ['5', '5'], - ['6', '6'], - ['7', '7'], - ['8', '8'], - ['9', '9'] - ] - } - ], - "category": Blockly.Categories.event, - "extensions": ["colours_event", "shape_hat"] - }); - } -}; diff --git a/blocks_vertical/extensions.js b/blocks_vertical/extensions.js deleted file mode 100644 index 499a3961d0..0000000000 --- a/blocks_vertical/extensions.js +++ /dev/null @@ -1,294 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Massachusetts Institute of Technology - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -goog.provide('Blockly.Blocks.extensions'); - -goog.require('Blockly.Blocks'); -goog.require('Blockly.Colours'); -goog.require('Blockly.constants'); -goog.require('Blockly.ScratchBlocks.VerticalExtensions'); - -Blockly.Blocks['extension_pen_down'] = { - /** - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1 %2 pen down", - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "extensions/pen-block-icon.svg", - "width": 40, - "height": 40 - }, - { - "type": "field_vertical_separator" - } - ], - "category": Blockly.Categories.more, - "extensions": ["colours_more", "shape_statement", "scratch_extension"] - }); - } -}; - -Blockly.Blocks['extension_music_drum'] = { - /** - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1 %2 play drum %3", - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "extensions/music-block-icon.svg", - "width": 40, - "height": 40 - }, - { - "type": "field_vertical_separator" - }, - { - "type": "input_value", - "name": "NUMBER" - } - ], - "category": Blockly.Categories.more, - "extensions": ["colours_more", "shape_statement", "scratch_extension"] - }); - } -}; - -Blockly.Blocks['extension_wedo_motor'] = { - /** - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1 %2 turn a motor %3", - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "extensions/wedo2-block-icon.svg", - "width": 40, - "height": 40 - }, - { - "type": "field_vertical_separator" - }, - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "rotate-right.svg", - "width": 24, - "height": 24 - } - ], - "category": Blockly.Categories.more, - "extensions": ["colours_more", "shape_statement", "scratch_extension"] - }); - } -}; - -Blockly.Blocks['extension_wedo_hat'] = { - /** - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1 %2 when I am wearing a hat", - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "extensions/wedo2-block-icon.svg", - "width": 40, - "height": 40 - }, - { - "type": "field_vertical_separator" - } - ], - "category": Blockly.Categories.more, - "extensions": ["colours_more", "shape_hat", "scratch_extension"] - }); - } -}; - -Blockly.Blocks['extension_wedo_boolean'] = { - /** - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1 %2 O RLY?", - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "extensions/wedo2-block-icon.svg", - "width": 40, - "height": 40 - }, - { - "type": "field_vertical_separator" - } - ], - "category": Blockly.Categories.more, - "extensions": ["colours_more", "output_boolean", "scratch_extension"] - }); - } -}; - -Blockly.Blocks['extension_wedo_tilt_reporter'] = { - /** - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1 %2 tilt angle %3", - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "extensions/wedo2-block-icon.svg", - "width": 40, - "height": 40 - }, - { - "type": "field_vertical_separator" - }, - { - "type": "input_value", - "name": "TILT" - } - ], - "category": Blockly.Categories.more, - "extensions": ["colours_more", "output_number", "scratch_extension"] - }); - } -}; - -Blockly.Blocks['extension_wedo_tilt_menu'] = { - /** - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "TILT", - "options": [ - ['Any', 'Any'], - ['Whirl', 'Whirl'], - ['South', 'South'], - ['Back in time', 'Back in time'] - ] - } - ], - "extensions": ["colours_more", "output_string"] - }); - } -}; - -Blockly.Blocks['extension_music_reporter'] = { - /** - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1 %2 hey now, you're an all-star", - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "extensions/music-block-icon.svg", - "width": 40, - "height": 40 - }, - { - "type": "field_vertical_separator" - } - ], - "category": Blockly.Categories.more, - "extensions": ["colours_more", "output_number", "scratch_extension"] - }); - } -}; - -Blockly.Blocks['extension_microbit_display'] = { - /** - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1 %2 display %3", - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "extensions/microbit-block-icon.svg", - "width": 40, - "height": 40 - }, - { - "type": "field_vertical_separator" - }, - { - "type": "input_value", - "name": "MATRIX" - }, - ], - "category": Blockly.Categories.pen, - "extensions": ["colours_pen", "shape_statement", "scratch_extension"] - }); - } -}; - -Blockly.Blocks['extension_music_play_note'] = { - /** - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1 %2 play note %3 for %4 beats", - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "extensions/music-block-icon.svg", - "width": 40, - "height": 40 - }, - { - "type": "field_vertical_separator" - }, - { - "type": "input_value", - "name": "NOTE" - }, - { - "type": "input_value", - "name": "BEATS" - } - ], - "category": Blockly.Categories.pen, - "extensions": ["colours_pen", "shape_statement", "scratch_extension"] - }); - } -}; diff --git a/blocks_vertical/looks.js b/blocks_vertical/looks.js deleted file mode 100644 index 66482f0ef3..0000000000 --- a/blocks_vertical/looks.js +++ /dev/null @@ -1,591 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Massachusetts Institute of Technology - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -goog.provide('Blockly.Blocks.looks'); - -goog.require('Blockly.Blocks'); -goog.require('Blockly.Colours'); -goog.require('Blockly.constants'); -goog.require('Blockly.ScratchBlocks.VerticalExtensions'); - - -Blockly.Blocks['looks_sayforsecs'] = { - /** - * Block to say for some time. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_SAYFORSECS, - "args0": [ - { - "type": "input_value", - "name": "MESSAGE" - }, - { - "type": "input_value", - "name": "SECS" - } - ], - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); - } -}; - -Blockly.Blocks['looks_say'] = { - /** - * Block to say. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_SAY, - "args0": [ - { - "type": "input_value", - "name": "MESSAGE" - } - ], - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); - } -}; - -Blockly.Blocks['looks_thinkforsecs'] = { - /** - * Block to think for some time. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_THINKFORSECS, - "args0": [ - { - "type": "input_value", - "name": "MESSAGE" - }, - { - "type": "input_value", - "name": "SECS" - } - ], - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); - } -}; - -Blockly.Blocks['looks_think'] = { - /** - * Block to think. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_THINK, - "args0": [ - { - "type": "input_value", - "name": "MESSAGE" - } - ], - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); - } -}; - -Blockly.Blocks['looks_show'] = { - /** - * Show block. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_SHOW, - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); - } -}; - -Blockly.Blocks['looks_hide'] = { - /** - * Hide block. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_HIDE, - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); - } -}; - -Blockly.Blocks['looks_hideallsprites'] = { - /** - * Hide-all-sprites block. Does not actually do anything. This is an - * obsolete block that is implemented for compatibility with Scratch 2.0 - * projects. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_HIDEALLSPRITES, - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); - } -}; - -Blockly.Blocks['looks_changeeffectby'] = { - /** - * Block to change graphic effect. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_CHANGEEFFECTBY, - "args0": [ - { - "type": "field_dropdown", - "name": "EFFECT", - "options": [ - [Blockly.Msg.LOOKS_EFFECT_COLOR, 'COLOR'], - [Blockly.Msg.LOOKS_EFFECT_FISHEYE, 'FISHEYE'], - [Blockly.Msg.LOOKS_EFFECT_WHIRL, 'WHIRL'], - [Blockly.Msg.LOOKS_EFFECT_PIXELATE, 'PIXELATE'], - [Blockly.Msg.LOOKS_EFFECT_MOSAIC, 'MOSAIC'], - [Blockly.Msg.LOOKS_EFFECT_BRIGHTNESS, 'BRIGHTNESS'], - [Blockly.Msg.LOOKS_EFFECT_GHOST, 'GHOST'] - ] - }, - { - "type": "input_value", - "name": "CHANGE" - } - ], - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); - } -}; - -Blockly.Blocks['looks_seteffectto'] = { - /** - * Block to set graphic effect. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_SETEFFECTTO, - "args0": [ - { - "type": "field_dropdown", - "name": "EFFECT", - "options": [ - [Blockly.Msg.LOOKS_EFFECT_COLOR, 'COLOR'], - [Blockly.Msg.LOOKS_EFFECT_FISHEYE, 'FISHEYE'], - [Blockly.Msg.LOOKS_EFFECT_WHIRL, 'WHIRL'], - [Blockly.Msg.LOOKS_EFFECT_PIXELATE, 'PIXELATE'], - [Blockly.Msg.LOOKS_EFFECT_MOSAIC, 'MOSAIC'], - [Blockly.Msg.LOOKS_EFFECT_BRIGHTNESS, 'BRIGHTNESS'], - [Blockly.Msg.LOOKS_EFFECT_GHOST, 'GHOST'] - ] - }, - { - "type": "input_value", - "name": "VALUE" - } - ], - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); - } -}; - -Blockly.Blocks['looks_cleargraphiceffects'] = { - /** - * Block to clear graphic effects. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_CLEARGRAPHICEFFECTS, - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); - } -}; - -Blockly.Blocks['looks_changesizeby'] = { - /** - * Block to change size - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_CHANGESIZEBY, - "args0": [ - { - "type": "input_value", - "name": "CHANGE" - } - ], - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); - } -}; - -Blockly.Blocks['looks_setsizeto'] = { - /** - * Block to set size - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_SETSIZETO, - "args0": [ - { - "type": "input_value", - "name": "SIZE" - } - ], - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); - } -}; - -Blockly.Blocks['looks_size'] = { - /** - * Block to report size - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_SIZE, - "category": Blockly.Categories.looks, - "checkboxInFlyout": true, - "extensions": ["colours_looks", "output_number"] - }); - } -}; - -Blockly.Blocks['looks_changestretchby'] = { - /** - * Block to change stretch. Does not actually do anything. This is an - * obsolete block that is implemented for compatibility with Scratch 1.4 - * projects as well as 2.0 projects that still have the block. - * The "stretch" blocks were introduced in very early versions of Scratch, - * but their functionality was removed shortly later. They still appeared - * correctly up until (and including) Scratch 1.4 - as "change stretch by" - * and "set stretch to" - but were removed altogether in Scratch 2.0, and - * displayed as red "undefined" blocks. Some Scratch projects still contain - * these blocks, however, and they don't open in 3.0 unless the blocks - * actually exist (though they still don't funcitonally do anything). - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_CHANGESTRETCHBY, - "args0": [ - { - "type": "input_value", - "name": "CHANGE" - } - ], - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); - } -}; - -Blockly.Blocks['looks_setstretchto'] = { - /** - * Block to set stretch. Does not actually do anything. This is an obsolete - * block that is implemented for compatibility with Scratch 1.4 projects - * (see looks_changestretchby). - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_SETSTRETCHTO, - "args0": [ - { - "type": "input_value", - "name": "STRETCH" - } - ], - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); - } -}; - -Blockly.Blocks['looks_costume'] = { - /** - * Costumes drop-down menu. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "COSTUME", - "options": [ - ['costume1', 'COSTUME1'], - ['costume2', 'COSTUME2'] - ] - } - ], - "colour": Blockly.Colours.looks.secondary, - "colourSecondary": Blockly.Colours.looks.secondary, - "colourTertiary": Blockly.Colours.looks.tertiary, - "colourQuaternary": Blockly.Colours.looks.quaternary, - "extensions": ["output_string"] - }); - } -}; - -Blockly.Blocks['looks_switchcostumeto'] = { - /** - * Block to switch the sprite's costume to the selected one. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_SWITCHCOSTUMETO, - "args0": [ - { - "type": "input_value", - "name": "COSTUME" - } - ], - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); - } -}; - -Blockly.Blocks['looks_nextcostume'] = { - /** - * Block to switch the sprite's costume to the next one. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_NEXTCOSTUME, - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); - } -}; - -Blockly.Blocks['looks_switchbackdropto'] = { - /** - * Block to switch the backdrop to the selected one. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_SWITCHBACKDROPTO, - "args0": [ - { - "type": "input_value", - "name": "BACKDROP" - } - ], - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); - } -}; - -Blockly.Blocks['looks_backdrops'] = { - /** - * Backdrop list - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "id": "looks_backdrops", - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "BACKDROP", - "options": [ - ['backdrop1', 'BACKDROP1'] - ] - } - ], - "colour": Blockly.Colours.looks.secondary, - "colourSecondary": Blockly.Colours.looks.secondary, - "colourTertiary": Blockly.Colours.looks.tertiary, - "colourQuaternary": Blockly.Colours.looks.quaternary, - "extensions": ["output_string"] - }); - } -}; - -Blockly.Blocks['looks_gotofrontback'] = { - /** - * "Go to front/back" Block. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_GOTOFRONTBACK, - "args0": [ - { - "type": "field_dropdown", - "name": "FRONT_BACK", - "options": [ - [Blockly.Msg.LOOKS_GOTOFRONTBACK_FRONT, 'front'], - [Blockly.Msg.LOOKS_GOTOFRONTBACK_BACK, 'back'] - ] - } - ], - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); - } -}; - -Blockly.Blocks['looks_goforwardbackwardlayers'] = { - /** - * "Go forward/backward [Number] Layers" Block. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_GOFORWARDBACKWARDLAYERS, - "args0": [ - { - "type": "field_dropdown", - "name": "FORWARD_BACKWARD", - "options": [ - [Blockly.Msg.LOOKS_GOFORWARDBACKWARDLAYERS_FORWARD, 'forward'], - [Blockly.Msg.LOOKS_GOFORWARDBACKWARDLAYERS_BACKWARD, 'backward'] - ] - }, - { - "type": "input_value", - "name": "NUM" - } - ], - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); - } -}; - -Blockly.Blocks['looks_backdropnumbername'] = { - /** - * Block to report backdrop's number or name - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_BACKDROPNUMBERNAME, - "args0": [ - { - "type": "field_dropdown", - "name": "NUMBER_NAME", - "options": [ - [Blockly.Msg.LOOKS_NUMBERNAME_NUMBER, 'number'], - [Blockly.Msg.LOOKS_NUMBERNAME_NAME, 'name'] - ] - } - ], - "category": Blockly.Categories.looks, - "checkboxInFlyout": true, - "extensions": ["colours_looks", "output_number"] - }); - } -}; - -Blockly.Blocks['looks_costumenumbername'] = { - /** - * Block to report costume's number or name - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_COSTUMENUMBERNAME, - "args0": [ - { - "type": "field_dropdown", - "name": "NUMBER_NAME", - "options": [ - [Blockly.Msg.LOOKS_NUMBERNAME_NUMBER, 'number'], - [Blockly.Msg.LOOKS_NUMBERNAME_NAME, 'name'] - ] - } - ], - "category": Blockly.Categories.looks, - "checkboxInFlyout": true, - "extensions": ["colours_looks", "output_number"] - }); - } -}; - -Blockly.Blocks['looks_switchbackdroptoandwait'] = { - /** - * Block to switch the backdrop to the selected one and wait. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_SWITCHBACKDROPTOANDWAIT, - "args0": [ - { - "type": "input_value", - "name": "BACKDROP" - } - ], - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); - } -}; - -Blockly.Blocks['looks_nextbackdrop'] = { - /** - * Block to switch the backdrop to the next one. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.LOOKS_NEXTBACKDROP_BLOCK, - "category": Blockly.Categories.looks, - "extensions": ["colours_looks", "shape_statement"] - }); - } -}; diff --git a/blocks_vertical/motion.js b/blocks_vertical/motion.js deleted file mode 100644 index b4b5969231..0000000000 --- a/blocks_vertical/motion.js +++ /dev/null @@ -1,587 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Massachusetts Institute of Technology - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -goog.provide('Blockly.Blocks.motion'); - -goog.require('Blockly.Blocks'); -goog.require('Blockly.Colours'); -goog.require('Blockly.constants'); -goog.require('Blockly.ScratchBlocks.VerticalExtensions'); - - -Blockly.Blocks['motion_movesteps'] = { - /** - * Block to move steps. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_MOVESTEPS, - "args0": [ - { - "type": "input_value", - "name": "STEPS" - } - ], - "category": Blockly.Categories.motion, - "extensions": ["colours_motion", "shape_statement"] - }); - } -}; - -Blockly.Blocks['motion_turnright'] = { - /** - * Block to turn right. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_TURNRIGHT, - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "rotate-right.svg", - "width": 24, - "height": 24 - }, - { - "type": "input_value", - "name": "DEGREES" - } - ], - "category": Blockly.Categories.motion, - "extensions": ["colours_motion", "shape_statement"] - }); - } -}; - -Blockly.Blocks['motion_turnleft'] = { - /** - * Block to turn left. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_TURNLEFT, - "args0": [ - { - "type": "field_image", - "src": Blockly.mainWorkspace.options.pathToMedia + "rotate-left.svg", - "width": 24, - "height": 24 - }, - { - "type": "input_value", - "name": "DEGREES" - } - ], - "category": Blockly.Categories.motion, - "extensions": ["colours_motion", "shape_statement"] - }); - } -}; - -Blockly.Blocks['motion_pointindirection'] = { - /** - * Block to point in direction. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_POINTINDIRECTION, - "args0": [ - { - "type": "input_value", - "name": "DIRECTION" - } - ], - "category": Blockly.Categories.motion, - "extensions": ["colours_motion", "shape_statement"] - }); - } -}; - -Blockly.Blocks['motion_pointtowards_menu'] = { - /** - * Point towards drop-down menu. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "TOWARDS", - "options": [ - [Blockly.Msg.MOTION_POINTTOWARDS_POINTER, '_mouse_'], - [Blockly.Msg.MOTION_POINTTOWARDS_RANDOM, '_random_'] - ] - } - ], - "colour": Blockly.Colours.motion.secondary, - "colourSecondary": Blockly.Colours.motion.secondary, - "colourTertiary": Blockly.Colours.motion.tertiary, - "colourQuaternary": Blockly.Colours.motion.quaternary, - "extensions": ["output_string"] - }); - } -}; - -Blockly.Blocks['motion_pointtowards'] = { - /** - * Block to point in direction. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_POINTTOWARDS, - "args0": [ - { - "type": "input_value", - "name": "TOWARDS" - } - ], - "category": Blockly.Categories.motion, - "extensions": ["colours_motion", "shape_statement"] - }); - } -}; - -Blockly.Blocks['motion_goto_menu'] = { - /** - * Go to drop-down menu. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "TO", - "options": [ - [Blockly.Msg.MOTION_GOTO_POINTER, '_mouse_'], - [Blockly.Msg.MOTION_GOTO_RANDOM, '_random_'] - ] - } - ], - "colour": Blockly.Colours.motion.secondary, - "colourSecondary": Blockly.Colours.motion.secondary, - "colourTertiary": Blockly.Colours.motion.tertiary, - "colourQuaternary": Blockly.Colours.motion.quaternary, - "extensions": ["output_string"] - }); - } -}; - -Blockly.Blocks['motion_gotoxy'] = { - /** - * Block to go to X, Y. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_GOTOXY, - "args0": [ - { - "type": "input_value", - "name": "X" - }, - { - "type": "input_value", - "name": "Y" - } - ], - "category": Blockly.Categories.motion, - "extensions": ["colours_motion", "shape_statement"] - }); - } -}; - -Blockly.Blocks['motion_goto'] = { - /** - * Block to go to a menu item. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_GOTO, - "args0": [ - { - "type": "input_value", - "name": "TO" - } - ], - "category": Blockly.Categories.motion, - "extensions": ["colours_motion", "shape_statement"] - }); - } -}; - -Blockly.Blocks['motion_glidesecstoxy'] = { - /** - * Block to glide for a specified time. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_GLIDESECSTOXY, - "args0": [ - { - "type": "input_value", - "name": "SECS" - }, - { - "type": "input_value", - "name": "X" - }, - { - "type": "input_value", - "name": "Y" - } - ], - "category": Blockly.Categories.motion, - "extensions": ["colours_motion", "shape_statement"] - }); - } -}; - -Blockly.Blocks['motion_glideto_menu'] = { - /** - * Glide to drop-down menu - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "TO", - "options": [ - [Blockly.Msg.MOTION_GLIDETO_POINTER, '_mouse_'], - [Blockly.Msg.MOTION_GLIDETO_RANDOM, '_random_'] - ] - } - ], - "colour": Blockly.Colours.motion.secondary, - "colourSecondary": Blockly.Colours.motion.secondary, - "colourTertiary": Blockly.Colours.motion.tertiary, - "colourQuaternary": Blockly.Colours.motion.quaternary, - "extensions": ["output_string"] - }); - } -}; - -Blockly.Blocks['motion_glideto'] = { - /** - * Block to glide to a menu item - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_GLIDETO, - "args0": [ - { - "type": "input_value", - "name": "SECS" - }, - { - "type": "input_value", - "name": "TO" - } - ], - "category": Blockly.Categories.motion, - "extensions": ["colours_motion", "shape_statement"] - }); - } -}; - -Blockly.Blocks['motion_changexby'] = { - /** - * Block to change X. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_CHANGEXBY, - "args0": [ - { - "type": "input_value", - "name": "DX" - } - ], - "category": Blockly.Categories.motion, - "extensions": ["colours_motion", "shape_statement"] - }); - } -}; - -Blockly.Blocks['motion_setx'] = { - /** - * Block to set X. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_SETX, - "args0": [ - { - "type": "input_value", - "name": "X" - } - ], - "category": Blockly.Categories.motion, - "extensions": ["colours_motion", "shape_statement"] - }); - } -}; - -Blockly.Blocks['motion_changeyby'] = { - /** - * Block to change Y. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_CHANGEYBY, - "args0": [ - { - "type": "input_value", - "name": "DY" - } - ], - "category": Blockly.Categories.motion, - "extensions": ["colours_motion", "shape_statement"] - }); - } -}; - -Blockly.Blocks['motion_sety'] = { - /** - * Block to set Y. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_SETY, - "args0": [ - { - "type": "input_value", - "name": "Y" - } - ], - "category": Blockly.Categories.motion, - "extensions": ["colours_motion", "shape_statement"] - }); - } -}; - -Blockly.Blocks['motion_ifonedgebounce'] = { - /** - * Block to bounce on edge. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_IFONEDGEBOUNCE, - "category": Blockly.Categories.motion, - "extensions": ["colours_motion", "shape_statement"] - }); - } -}; - -Blockly.Blocks['motion_setrotationstyle'] = { - /** - * Block to set rotation style. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_SETROTATIONSTYLE, - "args0": [ - { - "type": "field_dropdown", - "name": "STYLE", - "options": [ - [Blockly.Msg.MOTION_SETROTATIONSTYLE_LEFTRIGHT, 'left-right'], - [Blockly.Msg.MOTION_SETROTATIONSTYLE_DONTROTATE, 'don\'t rotate'], - [Blockly.Msg.MOTION_SETROTATIONSTYLE_ALLAROUND, 'all around'] - ] - } - ], - "category": Blockly.Categories.motion, - "extensions": ["colours_motion", "shape_statement"] - }); - } -}; - -Blockly.Blocks['motion_xposition'] = { - /** - * Block to report X. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_XPOSITION, - "category": Blockly.Categories.motion, - "checkboxInFlyout": true, - "extensions": ["colours_motion", "output_number"] - }); - } -}; - -Blockly.Blocks['motion_yposition'] = { - /** - * Block to report Y. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_YPOSITION, - "category": Blockly.Categories.motion, - "checkboxInFlyout": true, - "extensions": ["colours_motion", "output_number"] - }); - } -}; - -Blockly.Blocks['motion_direction'] = { - /** - * Block to report direction. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_DIRECTION, - "category": Blockly.Categories.motion, - "checkboxInFlyout": true, - "extensions": ["colours_motion", "output_number"] - }); - } -}; - -Blockly.Blocks['motion_scroll_right'] = { - /** - * Block to scroll the stage right. Does not actually do anything. This is - * an obsolete block that is implemented for compatibility with Scratch 2.0 - * projects. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_SCROLLRIGHT, - "args0": [ - { - "type": "input_value", - "name": "DISTANCE" - } - ], - "category": Blockly.Categories.motion, - "extensions": ["colours_motion", "shape_statement"] - }); - } -}; - -Blockly.Blocks['motion_scroll_up'] = { - /** - * Block to scroll the stage up. Does not actually do anything. This is an - * obsolete block that is implemented for compatibility with Scratch 2.0 - * projects. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_SCROLLUP, - "args0": [ - { - "type": "input_value", - "name": "DISTANCE" - } - ], - "category": Blockly.Categories.motion, - "extensions": ["colours_motion", "shape_statement"] - }); - } -}; - -Blockly.Blocks['motion_align_scene'] = { - /** - * Block to change the stage's scrolling alignment. Does not actually do - * anything. This is an obsolete block that is implemented for compatibility - * with Scratch 2.0 projects. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_ALIGNSCENE, - "args0": [ - { - "type": "field_dropdown", - "name": "ALIGNMENT", - "options": [ - [Blockly.Msg.MOTION_ALIGNSCENE_BOTTOMLEFT, 'bottom-left'], - [Blockly.Msg.MOTION_ALIGNSCENE_BOTTOMRIGHT, 'bottom-right'], - [Blockly.Msg.MOTION_ALIGNSCENE_MIDDLE, 'middle'], - [Blockly.Msg.MOTION_ALIGNSCENE_TOPLEFT, 'top-left'], - [Blockly.Msg.MOTION_ALIGNSCENE_TOPRIGHT, 'top-right'] - ] - } - ], - "category": Blockly.Categories.motion, - "extensions": ["colours_motion", "shape_statement"] - }); - } -}; - -Blockly.Blocks['motion_xscroll'] = { - /** - * Block to report the stage's scroll position's X value. Does not actually - * do anything. This is an obsolete block that is implemented for - * compatibility with Scratch 2.0 projects. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_XSCROLL, - "category": Blockly.Categories.motion, - "extensions": ["colours_motion", "output_number"] - }); - } -}; - -Blockly.Blocks['motion_yscroll'] = { - /** - * Block to report the stage's scroll position's Y value. Does not actually - * do anything. This is an obsolete block that is implemented for - * compatibility with Scratch 2.0 projects. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.MOTION_YSCROLL, - "category": Blockly.Categories.motion, - "extensions": ["colours_motion", "output_number"] - }); - } -}; diff --git a/blocks_vertical/operators.js b/blocks_vertical/operators.js deleted file mode 100644 index 1caa04481c..0000000000 --- a/blocks_vertical/operators.js +++ /dev/null @@ -1,470 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -goog.provide('Blockly.Blocks.operators'); - -goog.require('Blockly.Blocks'); -goog.require('Blockly.Colours'); -goog.require('Blockly.constants'); -goog.require('Blockly.ScratchBlocks.VerticalExtensions'); - - -Blockly.Blocks['operator_add'] = { - /** - * Block for adding two numbers. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.OPERATORS_ADD, - "args0": [ - { - "type": "input_value", - "name": "NUM1" - }, - { - "type": "input_value", - "name": "NUM2" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_number"] - }); - } -}; - -Blockly.Blocks['operator_subtract'] = { - /** - * Block for subtracting two numbers. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.OPERATORS_SUBTRACT, - "args0": [ - { - "type": "input_value", - "name": "NUM1" - }, - { - "type": "input_value", - "name": "NUM2" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_number"] - }); - } -}; - -Blockly.Blocks['operator_multiply'] = { - /** - * Block for multiplying two numbers. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.OPERATORS_MULTIPLY, - "args0": [ - { - "type": "input_value", - "name": "NUM1" - }, - { - "type": "input_value", - "name": "NUM2" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_number"] - }); - } -}; - -Blockly.Blocks['operator_divide'] = { - /** - * Block for dividing two numbers. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.OPERATORS_DIVIDE, - "args0": [ - { - "type": "input_value", - "name": "NUM1" - }, - { - "type": "input_value", - "name": "NUM2" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_number"] - }); - } -}; - -Blockly.Blocks['operator_random'] = { - /** - * Block for picking a random number. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.OPERATORS_RANDOM, - "args0": [ - { - "type": "input_value", - "name": "FROM" - }, - { - "type": "input_value", - "name": "TO" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_number"] - }); - } -}; - -Blockly.Blocks['operator_lt'] = { - /** - * Block for less than comparator. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.OPERATORS_LT, - "args0": [ - { - "type": "input_value", - "name": "OPERAND1" - }, - { - "type": "input_value", - "name": "OPERAND2" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_boolean"] - }); - } -}; - -Blockly.Blocks['operator_equals'] = { - /** - * Block for equals comparator. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.OPERATORS_EQUALS, - "args0": [ - { - "type": "input_value", - "name": "OPERAND1" - }, - { - "type": "input_value", - "name": "OPERAND2" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_boolean"] - }); - } -}; - -Blockly.Blocks['operator_gt'] = { - /** - * Block for greater than comparator. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.OPERATORS_GT, - "args0": [ - { - "type": "input_value", - "name": "OPERAND1" - }, - { - "type": "input_value", - "name": "OPERAND2" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_boolean"] - }); - } -}; - -Blockly.Blocks['operator_and'] = { - /** - * Block for "and" boolean comparator. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.OPERATORS_AND, - "args0": [ - { - "type": "input_value", - "name": "OPERAND1", - "check": "Boolean" - }, - { - "type": "input_value", - "name": "OPERAND2", - "check": "Boolean" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_boolean"] - }); - } -}; - -Blockly.Blocks['operator_or'] = { - /** - * Block for "or" boolean comparator. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.OPERATORS_OR, - "args0": [ - { - "type": "input_value", - "name": "OPERAND1", - "check": "Boolean" - }, - { - "type": "input_value", - "name": "OPERAND2", - "check": "Boolean" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_boolean"] - }); - } -}; - -Blockly.Blocks['operator_not'] = { - /** - * Block for "not" unary boolean operator. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.OPERATORS_NOT, - "args0": [ - { - "type": "input_value", - "name": "OPERAND", - "check": "Boolean" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_boolean"] - }); - } -}; - -Blockly.Blocks['operator_join'] = { - /** - * Block for string join operator. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.OPERATORS_JOIN, - "args0": [ - { - "type": "input_value", - "name": "STRING1" - }, - { - "type": "input_value", - "name": "STRING2" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_string"] - }); - } -}; - -Blockly.Blocks['operator_letter_of'] = { - /** - * Block for "letter _ of _" operator. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.OPERATORS_LETTEROF, - "args0": [ - { - "type": "input_value", - "name": "LETTER" - }, - { - "type": "input_value", - "name": "STRING" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_string"] - }); - } -}; - -Blockly.Blocks['operator_length'] = { - /** - * Block for string length operator. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.OPERATORS_LENGTH, - "args0": [ - { - "type": "input_value", - "name": "STRING" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_string"] - }); - } -}; - -Blockly.Blocks['operator_contains'] = { - /** - * Block for _ contains _ operator - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.OPERATORS_CONTAINS, - "args0": [ - { - "type": "input_value", - "name": "STRING1" - }, - { - "type": "input_value", - "name": "STRING2" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_boolean"] - }); - } -}; - -Blockly.Blocks['operator_mod'] = { - /** - * Block for mod two numbers. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.OPERATORS_MOD, - "args0": [ - { - "type": "input_value", - "name": "NUM1" - }, - { - "type": "input_value", - "name": "NUM2" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_number"] - }); - } -}; - -Blockly.Blocks['operator_round'] = { - /** - * Block for rounding a numbers. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.OPERATORS_ROUND, - "args0": [ - { - "type": "input_value", - "name": "NUM" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_number"] - }); - } -}; - -Blockly.Blocks['operator_mathop'] = { - /** - * Block for "advanced" math ops on a number. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.OPERATORS_MATHOP, - "args0": [ - { - "type": "field_dropdown", - "name": "OPERATOR", - "options": [ - [Blockly.Msg.OPERATORS_MATHOP_ABS, 'abs'], - [Blockly.Msg.OPERATORS_MATHOP_FLOOR, 'floor'], - [Blockly.Msg.OPERATORS_MATHOP_CEILING, 'ceiling'], - [Blockly.Msg.OPERATORS_MATHOP_SQRT, 'sqrt'], - [Blockly.Msg.OPERATORS_MATHOP_SIN, 'sin'], - [Blockly.Msg.OPERATORS_MATHOP_COS, 'cos'], - [Blockly.Msg.OPERATORS_MATHOP_TAN, 'tan'], - [Blockly.Msg.OPERATORS_MATHOP_ASIN, 'asin'], - [Blockly.Msg.OPERATORS_MATHOP_ACOS, 'acos'], - [Blockly.Msg.OPERATORS_MATHOP_ATAN, 'atan'], - [Blockly.Msg.OPERATORS_MATHOP_LN, 'ln'], - [Blockly.Msg.OPERATORS_MATHOP_LOG, 'log'], - [Blockly.Msg.OPERATORS_MATHOP_EEXP, 'e ^'], - [Blockly.Msg.OPERATORS_MATHOP_10EXP, '10 ^'] - ] - }, - { - "type": "input_value", - "name": "NUM" - } - ], - "category": Blockly.Categories.operators, - "extensions": ["colours_operators", "output_number"] - }); - } -}; diff --git a/blocks_vertical/procedures.js b/blocks_vertical/procedures.js deleted file mode 100644 index 1d2d2153a6..0000000000 --- a/blocks_vertical/procedures.js +++ /dev/null @@ -1,973 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Procedure blocks for Scratch. - */ -'use strict'; - -goog.provide('Blockly.ScratchBlocks.ProcedureUtils'); - -goog.require('Blockly.Blocks'); -goog.require('Blockly.Colours'); -goog.require('Blockly.constants'); -goog.require('Blockly.ScratchBlocks.VerticalExtensions'); - -// Serialization and deserialization. - -/** - * Create XML to represent the (non-editable) name and arguments of a procedure - * call block. - * @return {!Element} XML storage element. - * @this Blockly.Block - */ -Blockly.ScratchBlocks.ProcedureUtils.callerMutationToDom = function() { - var container = document.createElement('mutation'); - container.setAttribute('proccode', this.procCode_); - container.setAttribute('argumentids', JSON.stringify(this.argumentIds_)); - container.setAttribute('warp', JSON.stringify(this.warp_)); - return container; -}; - -/** - * Parse XML to restore the (non-editable) name and arguments of a procedure - * call block. - * @param {!Element} xmlElement XML storage element. - * @this Blockly.Block - */ -Blockly.ScratchBlocks.ProcedureUtils.callerDomToMutation = function(xmlElement) { - this.procCode_ = xmlElement.getAttribute('proccode'); - this.generateShadows_ = - JSON.parse(xmlElement.getAttribute('generateshadows')); - this.argumentIds_ = JSON.parse(xmlElement.getAttribute('argumentids')); - this.warp_ = JSON.parse(xmlElement.getAttribute('warp')); - this.updateDisplay_(); -}; - -/** - * Create XML to represent the (non-editable) name and arguments of a - * procedures_prototype block or a procedures_declaration block. - * @param {boolean=} opt_generateShadows Whether to include the generateshadows - * flag in the generated XML. False if not provided. - * @return {!Element} XML storage element. - * @this Blockly.Block - */ -Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom = function( - opt_generateShadows) { - var container = document.createElement('mutation'); - - if (opt_generateShadows) { - container.setAttribute('generateshadows', true); - } - container.setAttribute('proccode', this.procCode_); - container.setAttribute('argumentids', JSON.stringify(this.argumentIds_)); - container.setAttribute('argumentnames', JSON.stringify(this.displayNames_)); - container.setAttribute('argumentdefaults', - JSON.stringify(this.argumentDefaults_)); - container.setAttribute('warp', JSON.stringify(this.warp_)); - return container; -}; - -/** - * Parse XML to restore the (non-editable) name and arguments of a - * procedures_prototype block or a procedures_declaration block. - * @param {!Element} xmlElement XML storage element. - * @this Blockly.Block - */ -Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation = function(xmlElement) { - this.procCode_ = xmlElement.getAttribute('proccode'); - this.warp_ = JSON.parse(xmlElement.getAttribute('warp')); - - var prevArgIds = this.argumentIds_; - var prevDisplayNames = this.displayNames_; - - this.argumentIds_ = JSON.parse(xmlElement.getAttribute('argumentids')); - this.displayNames_ = JSON.parse(xmlElement.getAttribute('argumentnames')); - this.argumentDefaults_ = JSON.parse( - xmlElement.getAttribute('argumentdefaults')); - this.updateDisplay_(); - if (this.updateArgumentReporterNames_) { - this.updateArgumentReporterNames_(prevArgIds, prevDisplayNames); - } -}; - -// End of serialization and deserialization. - -// Shared by all three procedure blocks (procedures_declaration, -// procedures_call, and procedures_prototype). -/** - * Returns the name of the procedure this block calls, or the empty string if - * it has not yet been set. - * @return {string} Procedure name. - * @this Blockly.Block - */ -Blockly.ScratchBlocks.ProcedureUtils.getProcCode = function() { - return this.procCode_; -}; - -/** - * Update the block's structure and appearance to match the internally stored - * mutation. - * @private - * @this Blockly.Block - */ -Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_ = function() { - var wasRendered = this.rendered; - this.rendered = false; - - var connectionMap = this.disconnectOldBlocks_(); - this.removeAllInputs_(); - - this.createAllInputs_(connectionMap); - this.deleteShadows_(connectionMap); - - this.rendered = wasRendered; - if (wasRendered && !this.isInsertionMarker()) { - this.initSvg(); - this.render(); - } -}; - -/** - * Disconnect old blocks from all value inputs on this block, but hold onto them - * in case they can be reattached later. Also save the shadow DOM if it exists. - * The result is a map from argument ID to information that was associated with - * that argument at the beginning of the mutation. - * @return {!Object.} An object - * mapping argument IDs to blocks and shadow DOMs. - * @private - * @this Blockly.Block - */ -Blockly.ScratchBlocks.ProcedureUtils.disconnectOldBlocks_ = function() { - // Remove old stuff - var connectionMap = {}; - for (var i = 0, input; input = this.inputList[i]; i++) { - if (input.connection) { - var target = input.connection.targetBlock(); - var saveInfo = { - shadow: input.connection.getShadowDom(), - block: target - }; - connectionMap[input.name] = saveInfo; - - // Remove the shadow DOM, then disconnect the block. Otherwise a shadow - // block will respawn instantly, and we'd have to remove it when we remove - // the input. - input.connection.setShadowDom(null); - if (target) { - input.connection.disconnect(); - } - } - } - return connectionMap; -}; - -/** - * Remove all inputs on the block, including dummy inputs. - * Assumes no input has shadow DOM set. - * @private - * @this Blockly.Block - */ -Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_ = function() { - // Delete inputs directly instead of with block.removeInput to avoid splicing - // out of the input list at every index. - for (var i = 0, input; input = this.inputList[i]; i++) { - input.dispose(); - } - this.inputList = []; -}; - -/** - * Create all inputs specified by the new procCode, and populate them with - * shadow blocks or reconnected old blocks as appropriate. - * @param {!Object.} - * connectionMap An object mapping argument IDs to blocks and shadow DOMs. - * @private - * @this Blockly.Block - */ -Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_ = function(connectionMap) { - // Split the proc into components, by %n, %b, and %s (ignoring escaped). - var procComponents = this.procCode_.split(/(?=[^\\]%[nbs])/); - procComponents = procComponents.map(function(c) { - return c.trim(); // Strip whitespace. - }); - // Create arguments and labels as appropriate. - var argumentCount = 0; - for (var i = 0, component; component = procComponents[i]; i++) { - var labelText; - if (component.substring(0, 1) == '%') { - var argumentType = component.substring(1, 2); - if (!(argumentType == 'n' || argumentType == 'b' || argumentType == 's')) { - throw new Error( - 'Found an custom procedure with an invalid type: ' + argumentType); - } - labelText = component.substring(2).trim(); - - var id = this.argumentIds_[argumentCount]; - - var input = this.appendValueInput(id); - if (argumentType == 'b') { - input.setCheck('Boolean'); - } - this.populateArgument_(argumentType, argumentCount, connectionMap, id, - input); - argumentCount++; - } else { - labelText = component.trim(); - } - this.addProcedureLabel_(labelText.replace(/\\%/, '%')); - } -}; - -/** - * Delete all shadow blocks in the given map. - * @param {!Object.} connectionMap An object mapping - * argument IDs to the blocks that were connected to those IDs at the - * beginning of the mutation. - * @private - * @this Blockly.Block - */ -Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_ = function(connectionMap) { - // Get rid of all of the old shadow blocks if they aren't connected. - if (connectionMap) { - for (var id in connectionMap) { - var saveInfo = connectionMap[id]; - if (saveInfo) { - var block = saveInfo['block']; - if (block && block.isShadow()) { - block.dispose(); - connectionMap[id] = null; - // At this point we know which shadow DOMs are about to be orphaned in - // the VM. What do we do with that information? - } - } - } - } -}; -// End of shared code. - -/** - * Add a label field with the given text to a procedures_call or - * procedures_prototype block. - * @param {string} text The label text. - * @private - */ -Blockly.ScratchBlocks.ProcedureUtils.addLabelField_ = function(text) { - this.appendDummyInput().appendField(text); -}; - -/** - * Add a label editor with the given text to a procedures_declaration - * block. Editing the text in the label editor updates the text of the - * corresponding label fields on function calls. - * @param {string} text The label text. - * @private - */ -Blockly.ScratchBlocks.ProcedureUtils.addLabelEditor_ = function(text) { - if (text) { - this.appendDummyInput(Blockly.utils.genUid()). - appendField(new Blockly.FieldTextInputRemovable(text)); - } -}; - -/** - * Build a DOM node representing a shadow block of the given type. - * @param {string} type One of 's' (string) or 'n' (number). - * @return {!Element} The DOM node representing the new shadow block. - * @private - * @this Blockly.Block - */ -Blockly.ScratchBlocks.ProcedureUtils.buildShadowDom_ = function(type) { - var shadowDom = goog.dom.createDom('shadow'); - if (type == 'n') { - var shadowType = 'math_number'; - var fieldName = 'NUM'; - var fieldValue = '1'; - } else { - var shadowType = 'text'; - var fieldName = 'TEXT'; - var fieldValue = ''; - } - shadowDom.setAttribute('type', shadowType); - var fieldDom = goog.dom.createDom('field', null, fieldValue); - fieldDom.setAttribute('name', fieldName); - shadowDom.appendChild(fieldDom); - return shadowDom; -}; - -/** - * Create a new shadow block and attach it to the given input. - * @param {!Blockly.Input} input The value input to attach a block to. - * @param {string} argumentType One of 'b' (boolean), 's' (string) or - * 'n' (number). - * @private - * @this Blockly.Block - */ -Blockly.ScratchBlocks.ProcedureUtils.attachShadow_ = function(input, - argumentType) { - if (argumentType == 'n' || argumentType == 's') { - var blockType = argumentType == 'n' ? 'math_number' : 'text'; - Blockly.Events.disable(); - try { - var newBlock = this.workspace.newBlock(blockType); - if (argumentType == 'n') { - newBlock.setFieldValue('1', 'NUM'); - } else { - newBlock.setFieldValue('', 'TEXT'); - } - newBlock.setShadow(true); - if (!this.isInsertionMarker()) { - newBlock.initSvg(); - newBlock.render(false); - } - } finally { - Blockly.Events.enable(); - } - if (Blockly.Events.isEnabled()) { - Blockly.Events.fire(new Blockly.Events.BlockCreate(newBlock)); - } - newBlock.outputConnection.connect(input.connection); - } -}; - -/** - * Create a new argument reporter block. - * @param {string} argumentType One of 'b' (boolean), 's' (string) or - * 'n' (number). - * @param {string} displayName The name of the argument as provided by the - * user, which becomes the text of the label on the argument reporter block. - * @return {!Blockly.BlockSvg} The newly created argument reporter block. - * @private - * @this Blockly.Block - */ -Blockly.ScratchBlocks.ProcedureUtils.createArgumentReporter_ = function( - argumentType, displayName) { - if (argumentType == 'n' || argumentType == 's') { - var blockType = 'argument_reporter_string_number'; - } else { - var blockType = 'argument_reporter_boolean'; - } - Blockly.Events.disable(); - try { - var newBlock = this.workspace.newBlock(blockType); - newBlock.setShadow(true); - newBlock.setFieldValue(displayName, 'VALUE'); - if (!this.isInsertionMarker()) { - newBlock.initSvg(); - newBlock.render(false); - } - } finally { - Blockly.Events.enable(); - } - if (Blockly.Events.isEnabled()) { - Blockly.Events.fire(new Blockly.Events.BlockCreate(newBlock)); - } - return newBlock; -}; - -/** - * Populate the argument by attaching the correct child block or shadow to the - * given input. - * @param {string} type One of 'b' (boolean), 's' (string) or 'n' (number). - * @param {number} index The index of this argument into the argument id array. - * @param {!Object.} - * connectionMap An object mapping argument IDs to blocks and shadow DOMs. - * @param {string} id The ID of the input to populate. - * @param {!Blockly.Input} input The newly created input to populate. - * @private - * @this Blockly.Block - */ -Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnCaller_ = function(type, - index, connectionMap, id, input) { - var oldBlock = null; - var oldShadow = null; - if (connectionMap && (id in connectionMap)) { - var saveInfo = connectionMap[id]; - oldBlock = saveInfo['block']; - oldShadow = saveInfo['shadow']; - } - - if (connectionMap && oldBlock) { - // Reattach the old block and shadow DOM. - connectionMap[input.name] = null; - oldBlock.outputConnection.connect(input.connection); - if (type != 'b' && this.generateShadows_) { - var shadowDom = oldShadow || this.buildShadowDom_(type); - console.log("setting shadow dom: " + shadowDom); - input.connection.setShadowDom(shadowDom); - } - } else if (this.generateShadows_) { - this.attachShadow_(input, type); - } -}; - -/** - * Populate the argument by attaching the correct argument reporter to the given - * input. - * @param {string} type One of 'b' (boolean), 's' (string) or 'n' (number). - * @param {number} index The index of this argument into the argument ID and - * argument display name arrays. - * @param {!Object.} - * connectionMap An object mapping argument IDs to blocks and shadow DOMs. - * @param {string} id The ID of the input to populate. - * @param {!Blockly.Input} input The newly created input to populate. - * @private - * @this Blockly.Block - */ -Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnPrototype_ = function( - type, index, connectionMap, id, input) { - var oldBlock = null; - if (connectionMap && (id in connectionMap)) { - var saveInfo = connectionMap[id]; - oldBlock = saveInfo['block']; - } - - var oldTypeMatches = - Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_(oldBlock, type); - var displayName = this.displayNames_[index]; - - // Decide which block to attach. - if (connectionMap && oldBlock && oldTypeMatches) { - // Update the text if needed. The old argument reporter is the same type, - // and on the same input, but the argument's display name may have changed. - var argumentReporter = oldBlock; - argumentReporter.setFieldValue(displayName, 'VALUE'); - connectionMap[input.name] = null; - } else { - var argumentReporter = this.createArgumentReporter_(type, displayName); - } - - // Attach the block. - input.connection.connect(argumentReporter.outputConnection); -}; - -/** - * Populate the argument by attaching the correct argument editor to the given - * input. - * @param {string} type One of 'b' (boolean), 's' (string) or 'n' (number). - * @param {number} index The index of this argument into the argument id and - * argument display name arrays. - * @param {!Object.} - * connectionMap An object mapping argument IDs to blocks and shadow DOMs. - * @param {string} id The ID of the input to populate. - * @param {!Blockly.Input} input The newly created input to populate. - * @private - * @this Blockly.Block - */ -Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnDeclaration_ = function( - type, index, connectionMap, id, input) { - - var oldBlock = null; - if (connectionMap && (id in connectionMap)) { - var saveInfo = connectionMap[id]; - oldBlock = saveInfo['block']; - } - - // TODO: This always returns false, because it checks for argument reporter - // blocks instead of argument editor blocks. Create a new version for argument - // editors. - var oldTypeMatches = - Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_(oldBlock, type); - var displayName = this.displayNames_[index]; - - // Decide which block to attach. - if (oldBlock && oldTypeMatches) { - var argumentEditor = oldBlock; - oldBlock.setFieldValue(displayName, 'TEXT'); - connectionMap[input.name] = null; - } else { - var argumentEditor = this.createArgumentEditor_(type, displayName); - } - - // Attach the block. - input.connection.connect(argumentEditor.outputConnection); -}; - -/** - * Check whether the type of the old block corresponds to the given argument - * type. - * @param {Blockly.BlockSvg} oldBlock The old block to check. - * @param {string} type The argument type. One of 'n', 'n', or 's'. - * @return {boolean} True if the type matches, false otherwise. - */ -Blockly.ScratchBlocks.ProcedureUtils.checkOldTypeMatches_ = function(oldBlock, - type) { - if (!oldBlock) { - return false; - } - if ((type == 'n' || type == 's') && - oldBlock.type == 'argument_reporter_string_number') { - return true; - } - if (type == 'b' && oldBlock.type == 'argument_reporter_boolean') { - return true; - } - return false; -}; - -/** - * Create an argument editor. - * An argument editor is a shadow block with a single text field, which is used - * to set the display name of the argument. - * @param {string} argumentType One of 'b' (boolean), 's' (string) or - * 'n' (number). - * @param {string} displayName The display name of this argument, which is the - * text of the field on the shadow block. - * @return {!Blockly.BlockSvg} The newly created argument editor block. - * @private - * @this Blockly.Block - */ -Blockly.ScratchBlocks.ProcedureUtils.createArgumentEditor_ = function( - argumentType, displayName) { - Blockly.Events.disable(); - try { - if (argumentType == 'n' || argumentType == 's') { - var newBlock = this.workspace.newBlock('argument_editor_string_number'); - } else { - var newBlock = this.workspace.newBlock('argument_editor_boolean'); - } - newBlock.setFieldValue(displayName, 'TEXT'); - newBlock.setShadow(true); - if (!this.isInsertionMarker()) { - newBlock.initSvg(); - newBlock.render(false); - } - } finally { - Blockly.Events.enable(); - } - if (Blockly.Events.isEnabled()) { - Blockly.Events.fire(new Blockly.Events.BlockCreate(newBlock)); - } - return newBlock; -}; - -/** - * Update the serializable information on the block based on the existing inputs - * and their text. - */ -Blockly.ScratchBlocks.ProcedureUtils.updateDeclarationProcCode_ = function() { - this.procCode_ = ''; - this.displayNames_ = []; - this.argumentIds_ = []; - for (var i = 0; i < this.inputList.length; i++) { - if (i != 0) { - this.procCode_ += ' '; - } - var input = this.inputList[i]; - if (input.type == Blockly.DUMMY_INPUT) { - this.procCode_ += input.fieldRow[0].getValue(); - } else if (input.type == Blockly.INPUT_VALUE) { - // Inspect the argument editor. - var target = input.connection.targetBlock(); - this.displayNames_.push(target.getFieldValue('TEXT')); - this.argumentIds_.push(input.name); - if (target.type == 'argument_editor_boolean') { - this.procCode_ += '%b'; - } else { - this.procCode_ += '%s'; - } - } else { - throw new Error( - 'Unexpected input type on a procedure mutator root: ' + input.type); - } - } -}; - -/** - * Focus on the last argument editor or label editor on the block. - * @private - */ -Blockly.ScratchBlocks.ProcedureUtils.focusLastEditor_ = function() { - if (this.inputList.length > 0) { - var newInput = this.inputList[this.inputList.length - 1]; - if (newInput.type == Blockly.DUMMY_INPUT) { - newInput.fieldRow[0].showEditor_(); - } else if (newInput.type == Blockly.INPUT_VALUE) { - // Inspect the argument editor. - var target = newInput.connection.targetBlock(); - target.getField('TEXT').showEditor_(); - } - } -}; - -/** - * Externally-visible function to add a label to the procedure declaration. - * @public - */ -Blockly.ScratchBlocks.ProcedureUtils.addLabelExternal = function() { - Blockly.WidgetDiv.hide(true); - this.procCode_ = this.procCode_ + ' label text'; - this.updateDisplay_(); - this.focusLastEditor_(); -}; - -/** - * Externally-visible function to add a boolean argument to the procedure - * declaration. - * @public - */ -Blockly.ScratchBlocks.ProcedureUtils.addBooleanExternal = function() { - Blockly.WidgetDiv.hide(true); - this.procCode_ = this.procCode_ + ' %b'; - this.displayNames_.push('boolean'); - this.argumentIds_.push(Blockly.utils.genUid()); - this.argumentDefaults_.push('false'); - this.updateDisplay_(); - this.focusLastEditor_(); -}; - -/** - * Externally-visible function to add a string/number argument to the procedure - * declaration. - * @public - */ -Blockly.ScratchBlocks.ProcedureUtils.addStringNumberExternal = function() { - Blockly.WidgetDiv.hide(true); - this.procCode_ = this.procCode_ + ' %s'; - this.displayNames_.push('number or text'); - this.argumentIds_.push(Blockly.utils.genUid()); - this.argumentDefaults_.push(''); - this.updateDisplay_(); - this.focusLastEditor_(); -}; - -/** - * Externally-visible function to get the warp on procedure declaration. - * @return {boolean} The value of the warp_ property. - * @public - */ -Blockly.ScratchBlocks.ProcedureUtils.getWarp = function() { - return this.warp_; -}; - -/** - * Externally-visible function to set the warp on procedure declaration. - * @param {boolean} warp The value of the warp_ property. - * @public - */ -Blockly.ScratchBlocks.ProcedureUtils.setWarp = function(warp) { - this.warp_ = warp; -}; - -/** - * Callback to remove a field, only for the declaration block. - * @param {Blockly.Field} field The field being removed. - * @public - */ -Blockly.ScratchBlocks.ProcedureUtils.removeFieldCallback = function(field) { - // Do not delete if there is only one input - if (this.inputList.length === 1) { - return; - } - var inputNameToRemove = null; - for (var n = 0; n < this.inputList.length; n++) { - var input = this.inputList[n]; - if (input.connection) { - var target = input.connection.targetBlock(); - if (target.getField(field.name) == field) { - inputNameToRemove = input.name; - } - } else { - for (var j = 0; j < input.fieldRow.length; j++) { - if (input.fieldRow[j] == field) { - inputNameToRemove = input.name; - } - } - } - } - if (inputNameToRemove) { - Blockly.WidgetDiv.hide(true); - this.removeInput(inputNameToRemove); - this.onChangeFn(); - this.updateDisplay_(); - } -}; - -/** - * Callback to pass removeField up to the declaration block from arguments. - * @param {Blockly.Field} field The field being removed. - * @public - */ -Blockly.ScratchBlocks.ProcedureUtils.removeArgumentCallback_ = function( - field) { - if (this.parentBlock_ && this.parentBlock_.removeFieldCallback) { - this.parentBlock_.removeFieldCallback(field); - } -}; - -/** - * Update argument reporter field values after an edit to the prototype mutation - * using previous argument ids and names. - * Because the argument reporters only store names and not which argument ids they - * are linked to, it would not be safe to update all argument reporters on the workspace - * since they may be argument reporters with the same name from a different procedure. - * Until there is a more explicit way of identifying argument reporter blocks using ids, - * be conservative and only update argument reporters that are used in the - * stack below the prototype, ie the definition. - * @param {!Array} prevArgIds The previous ordering of argument ids. - * @param {!Array} prevDisplayNames The previous argument names. - * @this Blockly.Block - */ -Blockly.ScratchBlocks.ProcedureUtils.updateArgumentReporterNames_ = function(prevArgIds, prevDisplayNames) { - var nameChanges = []; - var argReporters = []; - var definitionBlock = this.getParent(); - if (!definitionBlock) return; - - // Create a list of argument reporters that are descendants of the definition stack (see above comment) - var allBlocks = definitionBlock.getDescendants(false); - for (var i = 0; i < allBlocks.length; i++) { - var block = allBlocks[i]; - if ((block.type === 'argument_reporter_string_number' || - block.type === 'argument_reporter_boolean') && - !block.isShadow()) { // Exclude arg reporters in the prototype block, which are shadows. - argReporters.push(block); - } - } - - // Create a list of "name changes", including the new name and blocks matching the old name - // Only search over the current set of argument ids, ignore args that have been removed - for (var i = 0, id; id = this.argumentIds_[i]; i++) { - // Find the previous index of this argument id. Could be -1 if it is newly added. - var prevIndex = prevArgIds.indexOf(id); - if (prevIndex == -1) continue; // Newly added argument, no corresponding previous argument to update. - var prevName = prevDisplayNames[prevIndex]; - if (prevName != this.displayNames_[i]) { - nameChanges.push({ - newName: this.displayNames_[i], - blocks: argReporters.filter(function(block) { - return block.getFieldValue('VALUE') == prevName; - }) - }); - } - } - - // Finally update the blocks for each name change. - // Do this after creating the lists to avoid cycles of renaming. - for (var j = 0, nameChange; nameChange = nameChanges[j]; j++) { - for (var k = 0, block; block = nameChange.blocks[k]; k++) { - block.setFieldValue(nameChange.newName, 'VALUE'); - } - } -}; - -Blockly.Blocks['procedures_definition'] = { - /** - * Block for defining a procedure with no return value. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.PROCEDURES_DEFINITION, - "args0": [ - { - "type": "input_statement", - "name": "custom_block" - } - ], - "extensions": ["colours_more", "shape_hat", "procedure_def_contextmenu"] - }); - } -}; - -Blockly.Blocks['procedures_call'] = { - /** - * Block for calling a procedure with no return value. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "extensions": ["colours_more", "shape_statement", "procedure_call_contextmenu"] - }); - this.procCode_ = ''; - this.argumentIds_ = []; - this.warp_ = false; - }, - // Shared. - getProcCode: Blockly.ScratchBlocks.ProcedureUtils.getProcCode, - removeAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_, - disconnectOldBlocks_: Blockly.ScratchBlocks.ProcedureUtils.disconnectOldBlocks_, - deleteShadows_: Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_, - createAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_, - updateDisplay_: Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_, - - // Exist on all three blocks, but have different implementations. - mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.callerMutationToDom, - domToMutation: Blockly.ScratchBlocks.ProcedureUtils.callerDomToMutation, - populateArgument_: Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnCaller_, - addProcedureLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelField_, - - // Only exists on the external caller. - attachShadow_: Blockly.ScratchBlocks.ProcedureUtils.attachShadow_, - buildShadowDom_: Blockly.ScratchBlocks.ProcedureUtils.buildShadowDom_ -}; - -Blockly.Blocks['procedures_prototype'] = { - /** - * Block for calling a procedure with no return value, for rendering inside - * define block. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "extensions": ["colours_more", "shape_statement"] - }); - - /* Data known about the procedure. */ - this.procCode_ = ''; - this.displayNames_ = []; - this.argumentIds_ = []; - this.argumentDefaults_ = []; - this.warp_ = false; - }, - // Shared. - getProcCode: Blockly.ScratchBlocks.ProcedureUtils.getProcCode, - removeAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_, - disconnectOldBlocks_: Blockly.ScratchBlocks.ProcedureUtils.disconnectOldBlocks_, - deleteShadows_: Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_, - createAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_, - updateDisplay_: Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_, - - // Exist on all three blocks, but have different implementations. - mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom, - domToMutation: Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation, - populateArgument_: Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnPrototype_, - addProcedureLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelField_, - - // Only exists on procedures_prototype. - createArgumentReporter_: Blockly.ScratchBlocks.ProcedureUtils.createArgumentReporter_, - updateArgumentReporterNames_: Blockly.ScratchBlocks.ProcedureUtils.updateArgumentReporterNames_ -}; - -Blockly.Blocks['procedures_declaration'] = { - /** - * The root block in the procedure declaration editor. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "extensions": ["colours_more", "shape_statement"] - }); - /* Data known about the procedure. */ - this.procCode_ = ''; - this.displayNames_ = []; - this.argumentIds_ = []; - this.argumentDefaults_ = []; - this.warp_ = false; - }, - // Shared. - getProcCode: Blockly.ScratchBlocks.ProcedureUtils.getProcCode, - removeAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.removeAllInputs_, - disconnectOldBlocks_: Blockly.ScratchBlocks.ProcedureUtils.disconnectOldBlocks_, - deleteShadows_: Blockly.ScratchBlocks.ProcedureUtils.deleteShadows_, - createAllInputs_: Blockly.ScratchBlocks.ProcedureUtils.createAllInputs_, - updateDisplay_: Blockly.ScratchBlocks.ProcedureUtils.updateDisplay_, - - // Exist on all three blocks, but have different implementations. - mutationToDom: Blockly.ScratchBlocks.ProcedureUtils.definitionMutationToDom, - domToMutation: Blockly.ScratchBlocks.ProcedureUtils.definitionDomToMutation, - populateArgument_: Blockly.ScratchBlocks.ProcedureUtils.populateArgumentOnDeclaration_, - addProcedureLabel_: Blockly.ScratchBlocks.ProcedureUtils.addLabelEditor_, - - // Exist on declaration and arguments editors, with different implementations. - removeFieldCallback: Blockly.ScratchBlocks.ProcedureUtils.removeFieldCallback, - - // Only exist on procedures_declaration. - createArgumentEditor_: Blockly.ScratchBlocks.ProcedureUtils.createArgumentEditor_, - focusLastEditor_: Blockly.ScratchBlocks.ProcedureUtils.focusLastEditor_, - getWarp: Blockly.ScratchBlocks.ProcedureUtils.getWarp, - setWarp: Blockly.ScratchBlocks.ProcedureUtils.setWarp, - addLabelExternal: Blockly.ScratchBlocks.ProcedureUtils.addLabelExternal, - addBooleanExternal: Blockly.ScratchBlocks.ProcedureUtils.addBooleanExternal, - addStringNumberExternal: Blockly.ScratchBlocks.ProcedureUtils.addStringNumberExternal, - onChangeFn: Blockly.ScratchBlocks.ProcedureUtils.updateDeclarationProcCode_ -}; - -Blockly.Blocks['argument_reporter_boolean'] = { - init: function() { - this.jsonInit({ "message0": " %1", - "args0": [ - { - "type": "field_label_serializable", - "name": "VALUE", - "text": "" - } - ], - "extensions": ["colours_more", "output_boolean"] - }); - } -}; - -Blockly.Blocks['argument_reporter_string_number'] = { - init: function() { - this.jsonInit({ "message0": " %1", - "args0": [ - { - "type": "field_label_serializable", - "name": "VALUE", - "text": "" - } - ], - "extensions": ["colours_more", "output_number", "output_string"] - }); - } -}; - -Blockly.Blocks['argument_editor_boolean'] = { - init: function() { - this.jsonInit({ "message0": " %1", - "args0": [ - { - "type": "field_input_removable", - "name": "TEXT", - "text": "foo" - } - ], - "colour": Blockly.Colours.textField, - "colourSecondary": Blockly.Colours.textField, - "colourTertiary": Blockly.Colours.textField, - "colourQuaternary": Blockly.Colours.textField, - "extensions": ["output_boolean"] - }); - }, - // Exist on declaration and arguments editors, with different implementations. - removeFieldCallback: Blockly.ScratchBlocks.ProcedureUtils.removeArgumentCallback_ -}; - -Blockly.Blocks['argument_editor_string_number'] = { - init: function() { - this.jsonInit({ "message0": " %1", - "args0": [ - { - "type": "field_input_removable", - "name": "TEXT", - "text": "foo" - } - ], - "colour": Blockly.Colours.textField, - "colourSecondary": Blockly.Colours.textField, - "colourTertiary": Blockly.Colours.textField, - "colourQuaternary": Blockly.Colours.textField, - "extensions": ["output_number", "output_string"] - }); - }, - // Exist on declaration and arguments editors, with different implementations. - removeFieldCallback: Blockly.ScratchBlocks.ProcedureUtils.removeArgumentCallback_ -}; diff --git a/blocks_vertical/sensing.js b/blocks_vertical/sensing.js deleted file mode 100644 index cd75de8d12..0000000000 --- a/blocks_vertical/sensing.js +++ /dev/null @@ -1,539 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Massachusetts Institute of Technology - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -goog.provide('Blockly.Blocks.sensing'); - -goog.require('Blockly.Blocks'); -goog.require('Blockly.Colours'); -goog.require('Blockly.constants'); -goog.require('Blockly.ScratchBlocks.VerticalExtensions'); - - -Blockly.Blocks['sensing_touchingobject'] = { - /** - * Block to Report if its touching a Object. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SENSING_TOUCHINGOBJECT, - "args0": [ - { - "type": "input_value", - "name": "TOUCHINGOBJECTMENU" - } - ], - "category": Blockly.Categories.sensing, - "extensions": ["colours_sensing", "output_boolean"] - }); - } -}; - -Blockly.Blocks['sensing_touchingobjectmenu'] = { - /** - * "Touching [Object]" Block Menu. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "TOUCHINGOBJECTMENU", - "options": [ - [Blockly.Msg.SENSING_TOUCHINGOBJECT_POINTER, '_mouse_'], - [Blockly.Msg.SENSING_TOUCHINGOBJECT_EDGE, '_edge_'] - ] - } - ], - "extensions": ["colours_sensing", "output_string"] - }); - } -}; - -Blockly.Blocks['sensing_touchingcolor'] = { - /** - * Block to Report if its touching a certain Color. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SENSING_TOUCHINGCOLOR, - "args0": [ - { - "type": "input_value", - "name": "COLOR" - } - ], - "category": Blockly.Categories.sensing, - "extensions": ["colours_sensing", "output_boolean"] - }); - } -}; - -Blockly.Blocks['sensing_coloristouchingcolor'] = { - /** - * Block to Report if a color is touching a certain Color. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SENSING_COLORISTOUCHINGCOLOR, - "args0": [ - { - "type": "input_value", - "name": "COLOR" - }, - { - "type": "input_value", - "name": "COLOR2" - } - ], - "category": Blockly.Categories.sensing, - "extensions": ["colours_sensing", "output_boolean"] - }); - } -}; - -Blockly.Blocks['sensing_distanceto'] = { - /** - * Block to Report distance to another Object. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SENSING_DISTANCETO, - "args0": [ - { - "type": "input_value", - "name": "DISTANCETOMENU" - } - ], - "category": Blockly.Categories.sensing, - "extensions": ["colours_sensing", "output_number"] - }); - } -}; - -Blockly.Blocks['sensing_distancetomenu'] = { - /** - * "Distance to [Object]" Block Menu. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "DISTANCETOMENU", - "options": [ - [Blockly.Msg.SENSING_DISTANCETO_POINTER, '_mouse_'] - ] - } - ], - "extensions": ["colours_sensing", "output_string"] - }); - } -}; - -Blockly.Blocks['sensing_askandwait'] = { - /** - * Block to ask a question and wait - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SENSING_ASKANDWAIT, - "args0": [ - { - "type": "input_value", - "name": "QUESTION" - } - ], - "category": Blockly.Categories.sensing, - "extensions": ["colours_sensing", "shape_statement"] - }); - } -}; - -Blockly.Blocks['sensing_answer'] = { - /** - * Block to report answer - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SENSING_ANSWER, - "category": Blockly.Categories.sensing, - "checkboxInFlyout": true, - "extensions": ["colours_sensing", "output_number"] - }); - } -}; - -Blockly.Blocks['sensing_keypressed'] = { - /** - * Block to Report if a key is pressed. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SENSING_KEYPRESSED, - "args0": [ - { - "type": "input_value", - "name": "KEY_OPTION" - } - ], - "category": Blockly.Categories.sensing, - "extensions": ["colours_sensing", "output_boolean"] - }); - } -}; - -Blockly.Blocks['sensing_keyoptions'] = { - /** - * Options for Keys - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "KEY_OPTION", - "options": [ - [Blockly.Msg.EVENT_WHENKEYPRESSED_SPACE, 'space'], - [Blockly.Msg.EVENT_WHENKEYPRESSED_UP, 'up arrow'], - [Blockly.Msg.EVENT_WHENKEYPRESSED_DOWN, 'down arrow'], - [Blockly.Msg.EVENT_WHENKEYPRESSED_RIGHT, 'right arrow'], - [Blockly.Msg.EVENT_WHENKEYPRESSED_LEFT, 'left arrow'], - [Blockly.Msg.EVENT_WHENKEYPRESSED_ANY, 'any'], - ['a', 'a'], - ['b', 'b'], - ['c', 'c'], - ['d', 'd'], - ['e', 'e'], - ['f', 'f'], - ['g', 'g'], - ['h', 'h'], - ['i', 'i'], - ['j', 'j'], - ['k', 'k'], - ['l', 'l'], - ['m', 'm'], - ['n', 'n'], - ['o', 'o'], - ['p', 'p'], - ['q', 'q'], - ['r', 'r'], - ['s', 's'], - ['t', 't'], - ['u', 'u'], - ['v', 'v'], - ['w', 'w'], - ['x', 'x'], - ['y', 'y'], - ['z', 'z'], - ['0', '0'], - ['1', '1'], - ['2', '2'], - ['3', '3'], - ['4', '4'], - ['5', '5'], - ['6', '6'], - ['7', '7'], - ['8', '8'], - ['9', '9'] - ] - } - ], - "extensions": ["colours_sensing", "output_string"] - }); - } -}; - -Blockly.Blocks['sensing_mousedown'] = { - /** - * Block to Report if the mouse is down. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SENSING_MOUSEDOWN, - "category": Blockly.Categories.sensing, - "extensions": ["colours_sensing", "output_boolean"] - }); - } -}; - -Blockly.Blocks['sensing_mousex'] = { - /** - * Block to report mouse's x position - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SENSING_MOUSEX, - "category": Blockly.Categories.sensing, - "extensions": ["colours_sensing", "output_number"] - }); - } -}; - -Blockly.Blocks['sensing_mousey'] = { - /** - * Block to report mouse's y position - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SENSING_MOUSEY, - "category": Blockly.Categories.sensing, - "extensions": ["colours_sensing", "output_number"] - }); - } -}; - -Blockly.Blocks['sensing_setdragmode'] = { - /** - * Block to set drag mode. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SENSING_SETDRAGMODE, - "args0": [ - { - "type": "field_dropdown", - "name": "DRAG_MODE", - "options": [ - [Blockly.Msg.SENSING_SETDRAGMODE_DRAGGABLE, 'draggable'], - [Blockly.Msg.SENSING_SETDRAGMODE_NOTDRAGGABLE, 'not draggable'] - ] - } - ], - "category": Blockly.Categories.sensing, - "extensions": ["colours_sensing", "shape_statement"] - }); - } -}; - -Blockly.Blocks['sensing_loudness'] = { - /** - * Block to report loudness - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SENSING_LOUDNESS, - "category": Blockly.Categories.sensing, - "checkboxInFlyout": true, - "extensions": ["colours_sensing", "output_number"] - }); - } -}; - -Blockly.Blocks['sensing_loud'] = { - /** - * Block to report if the loudness is "loud" (greater than 10). This is an - * obsolete block that is implemented for compatibility with Scratch 2.0 and - * 1.4 projects. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SENSING_LOUD, - "category": Blockly.Categories.sensing, - "extensions": ["colours_sensing", "output_boolean"] - }); - } -}; - -Blockly.Blocks['sensing_timer'] = { - /** - * Block to report timer - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SENSING_TIMER, - "category": Blockly.Categories.sensing, - "checkboxInFlyout": true, - "extensions": ["colours_sensing", "output_number"] - }); - } -}; - -Blockly.Blocks['sensing_resettimer'] = { - /** - * Block to reset timer - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SENSING_RESETTIMER, - "category": Blockly.Categories.sensing, - "extensions": ["colours_sensing", "shape_statement"] - }); - } -}; - -Blockly.Blocks['sensing_of_object_menu'] = { - /** - * "* of _" object menu. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "OBJECT", - "options": [ - ['Sprite1', 'Sprite1'], - ['Stage', '_stage_'] - ] - } - ], - "category": Blockly.Categories.sensing, - "extensions": ["colours_sensing", "output_string"] - }); - } -}; - - -Blockly.Blocks['sensing_of'] = { - /** - * Block to report properties of sprites. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SENSING_OF, - "args0": [ - { - "type": "field_dropdown", - "name": "PROPERTY", - "options": [ - [Blockly.Msg.SENSING_OF_XPOSITION, 'x position'], - [Blockly.Msg.SENSING_OF_YPOSITION, 'y position'], - [Blockly.Msg.SENSING_OF_DIRECTION, 'direction'], - [Blockly.Msg.SENSING_OF_COSTUMENUMBER, 'costume #'], - [Blockly.Msg.SENSING_OF_COSTUMENAME, 'costume name'], - [Blockly.Msg.SENSING_OF_SIZE, 'size'], - [Blockly.Msg.SENSING_OF_VOLUME, 'volume'], - [Blockly.Msg.SENSING_OF_BACKDROPNUMBER, 'backdrop #'], - [Blockly.Msg.SENSING_OF_BACKDROPNAME, 'backdrop name'] - ] - }, - { - "type": "input_value", - "name": "OBJECT" - } - ], - "output": true, - "category": Blockly.Categories.sensing, - "outputShape": Blockly.OUTPUT_SHAPE_ROUND, - "extensions": ["colours_sensing"] - }); - } -}; - -Blockly.Blocks['sensing_current'] = { - /** - * Block to Report the current option. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SENSING_CURRENT, - "args0": [ - { - "type": "field_dropdown", - "name": "CURRENTMENU", - "options": [ - [Blockly.Msg.SENSING_CURRENT_YEAR, 'YEAR'], - [Blockly.Msg.SENSING_CURRENT_MONTH, 'MONTH'], - [Blockly.Msg.SENSING_CURRENT_DATE, 'DATE'], - [Blockly.Msg.SENSING_CURRENT_DAYOFWEEK, 'DAYOFWEEK'], - [Blockly.Msg.SENSING_CURRENT_HOUR, 'HOUR'], - [Blockly.Msg.SENSING_CURRENT_MINUTE, 'MINUTE'], - [Blockly.Msg.SENSING_CURRENT_SECOND, 'SECOND'] - ] - } - ], - "category": Blockly.Categories.sensing, - "checkboxInFlyout": true, - "extensions": ["colours_sensing", "output_number"] - }); - } -}; - -Blockly.Blocks['sensing_dayssince2000'] = { - /** - * Block to report days since 2000 - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SENSING_DAYSSINCE2000, - "category": Blockly.Categories.sensing, - "extensions": ["colours_sensing", "output_number"] - }); - } -}; - -Blockly.Blocks['sensing_username'] = { - /** - * Block to report user's username - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SENSING_USERNAME, - "category": Blockly.Categories.sensing, - "checkboxInFlyout": true, - "extensions": ["colours_sensing", "output_number"] - }); - } -}; - -Blockly.Blocks['sensing_userid'] = { - /** - * Block to report user's ID. Does not actually do anything. This is an - * obsolete block that is implemented for compatibility with Scratch 2.0 - * projects. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SENSING_USERID, - "category": Blockly.Categories.sensing, - "extensions": ["colours_sensing", "output_number"] - }); - } -}; diff --git a/blocks_vertical/sound.js b/blocks_vertical/sound.js deleted file mode 100644 index 03c33630ba..0000000000 --- a/blocks_vertical/sound.js +++ /dev/null @@ -1,246 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Massachusetts Institute of Technology - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -goog.provide('Blockly.Blocks.sound'); - -goog.require('Blockly.Blocks'); -goog.require('Blockly.Colours'); -goog.require('Blockly.constants'); -goog.require('Blockly.ScratchBlocks.VerticalExtensions'); - -Blockly.Blocks['sound_sounds_menu'] = { - /** - * Sound effects drop-down menu. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": "%1", - "args0": [ - { - "type": "field_dropdown", - "name": "SOUND_MENU", - "options": [ - ['1', '0'], - ['2', '1'], - ['3', '2'], - ['4', '3'], - ['5', '4'], - ['6', '5'], - ['7', '6'], - ['8', '7'], - ['9', '8'], - ['10', '9'], - ['call a function', function() { - window.alert('function called!');} - ] - ] - } - ], - "colour": Blockly.Colours.sounds.secondary, - "colourSecondary": Blockly.Colours.sounds.secondary, - "colourTertiary": Blockly.Colours.sounds.tertiary, - "colourQuaternary": Blockly.Colours.sounds.quaternary, - "extensions": ["output_string"] - }); - } -}; - -Blockly.Blocks['sound_play'] = { - /** - * Block to play sound. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SOUND_PLAY, - "args0": [ - { - "type": "input_value", - "name": "SOUND_MENU" - } - ], - "category": Blockly.Categories.sound, - "extensions": ["colours_sounds", "shape_statement"] - }); - } -}; - -Blockly.Blocks['sound_playuntildone'] = { - /** - * Block to play sound until done. - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SOUND_PLAYUNTILDONE, - "args0": [ - { - "type": "input_value", - "name": "SOUND_MENU" - } - ], - "category": Blockly.Categories.sound, - "extensions": ["colours_sounds", "shape_statement"] - }); - } -}; - -Blockly.Blocks['sound_stopallsounds'] = { - /** - * Block to stop all sounds - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SOUND_STOPALLSOUNDS, - "category": Blockly.Categories.sound, - "extensions": ["colours_sounds", "shape_statement"] - }); - } -}; - -Blockly.Blocks['sound_seteffectto'] = { - /** - * Block to set the audio effect - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SOUND_SETEFFECTO, - "args0": [ - { - "type": "field_dropdown", - "name": "EFFECT", - "options": [ - [Blockly.Msg.SOUND_EFFECTS_PITCH, 'PITCH'], - [Blockly.Msg.SOUND_EFFECTS_PAN, 'PAN'] - ] - }, - { - "type": "input_value", - "name": "VALUE" - } - ], - "category": Blockly.Categories.sound, - "extensions": ["colours_sounds", "shape_statement"] - }); - } -}; - - -Blockly.Blocks['sound_changeeffectby'] = { - /** - * Block to change the audio effect - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SOUND_CHANGEEFFECTBY, - "args0": [ - { - "type": "field_dropdown", - "name": "EFFECT", - "options": [ - [Blockly.Msg.SOUND_EFFECTS_PITCH, 'PITCH'], - [Blockly.Msg.SOUND_EFFECTS_PAN, 'PAN'] - ] - }, - { - "type": "input_value", - "name": "VALUE" - } - ], - "category": Blockly.Categories.sound, - "extensions": ["colours_sounds", "shape_statement"] - }); - } -}; - -Blockly.Blocks['sound_cleareffects'] = { - /** - * Block to clear audio effects - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SOUND_CLEAREFFECTS, - "category": Blockly.Categories.sound, - "extensions": ["colours_sounds", "shape_statement"] - }); - } -}; - -Blockly.Blocks['sound_changevolumeby'] = { - /** - * Block to change the sprite's volume by a certain value - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SOUND_CHANGEVOLUMEBY, - "args0": [ - { - "type": "input_value", - "name": "VOLUME" - } - ], - "category": Blockly.Categories.sound, - "extensions": ["colours_sounds", "shape_statement"] - }); - } -}; - -Blockly.Blocks['sound_setvolumeto'] = { - /** - * Block to set the sprite's volume to a certain percent - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SOUND_SETVOLUMETO, - "args0": [ - { - "type": "input_value", - "name": "VOLUME" - } - ], - "category": Blockly.Categories.sound, - "extensions": ["colours_sounds", "shape_statement"] - }); - } -}; - -Blockly.Blocks['sound_volume'] = { - /** - * Block to report volume - * @this Blockly.Block - */ - init: function() { - this.jsonInit({ - "message0": Blockly.Msg.SOUND_VOLUME, - "category": Blockly.Categories.sound, - "checkboxInFlyout": true, - "extensions": ["colours_sounds", "output_number"] - }); - } -}; diff --git a/blocks_vertical/vertical_extensions.js b/blocks_vertical/vertical_extensions.js deleted file mode 100644 index 16059da1ec..0000000000 --- a/blocks_vertical/vertical_extensions.js +++ /dev/null @@ -1,266 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Extensions for vertical blocks in scratch-blocks. - * The following extensions can be used to describe a block in Scratch terms. - * For instance, a block in the operators colour scheme with a number output - * would have the "colours_operators" and "output_number" extensions. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.ScratchBlocks.VerticalExtensions'); - -goog.require('Blockly.Colours'); -goog.require('Blockly.constants'); - - -/** - * Helper function that generates an extension based on a category name. - * The generated function will set primary, secondary, tertiary, and quaternary - * colours based on the category name. - * @param {String} category The name of the category to set colours for. - * @return {function} An extension function that sets colours based on the given - * category. - */ -Blockly.ScratchBlocks.VerticalExtensions.colourHelper = function(category) { - var colours = Blockly.Colours[category]; - if (!(colours && colours.primary && colours.secondary && colours.tertiary && - colours.quaternary)) { - throw new Error('Could not find colours for category "' + category + '"'); - } - /** - * Set the primary, secondary, tertiary, and quaternary colours on this block for - * the given category. - * @this {Blockly.Block} - */ - return function() { - this.setColourFromRawValues_(colours.primary, colours.secondary, - colours.tertiary, colours.quaternary); - }; -}; - -/** - * Extension to set the colours of a text field, which are all the same. - */ -Blockly.ScratchBlocks.VerticalExtensions.COLOUR_TEXTFIELD = function() { - this.setColourFromRawValues_(Blockly.Colours.textField, - Blockly.Colours.textField, Blockly.Colours.textField, - Blockly.Colours.textField); -}; - -/** - * Extension to make a block fit into a stack of statements, regardless of its - * inputs. That means the block should have a previous connection and a next - * connection and have inline inputs. - * @this {Blockly.Block} - * @readonly - */ -Blockly.ScratchBlocks.VerticalExtensions.SHAPE_STATEMENT = function() { - this.setInputsInline(true); - this.setPreviousStatement(true, null); - this.setNextStatement(true, null); -}; - -/** - * Extension to make a block be shaped as a hat block, regardless of its - * inputs. That means the block should have a next connection and have inline - * inputs, but have no previous connection. - * @this {Blockly.Block} - * @readonly - */ -Blockly.ScratchBlocks.VerticalExtensions.SHAPE_HAT = function() { - this.setInputsInline(true); - this.setNextStatement(true, null); -}; - -/** - * Extension to make a block be shaped as an end block, regardless of its - * inputs. That means the block should have a previous connection and have - * inline inputs, but have no next connection. - * @this {Blockly.Block} - * @readonly - */ -Blockly.ScratchBlocks.VerticalExtensions.SHAPE_END = function() { - this.setInputsInline(true); - this.setPreviousStatement(true, null); -}; - -/** - * Extension to make represent a number reporter in Scratch-Blocks. - * That means the block has inline inputs, a round output shape, and a 'Number' - * output type. - * @this {Blockly.Block} - * @readonly - */ -Blockly.ScratchBlocks.VerticalExtensions.OUTPUT_NUMBER = function() { - this.setInputsInline(true); - this.setOutputShape(Blockly.OUTPUT_SHAPE_ROUND); - this.setOutput(true, 'Number'); -}; - -/** - * Extension to make represent a string reporter in Scratch-Blocks. - * That means the block has inline inputs, a round output shape, and a 'String' - * output type. - * @this {Blockly.Block} - * @readonly - */ -Blockly.ScratchBlocks.VerticalExtensions.OUTPUT_STRING = function() { - this.setInputsInline(true); - this.setOutputShape(Blockly.OUTPUT_SHAPE_ROUND); - this.setOutput(true, 'String'); -}; - -/** - * Extension to make represent a boolean reporter in Scratch-Blocks. - * That means the block has inline inputs, a round output shape, and a 'Boolean' - * output type. - * @this {Blockly.Block} - * @readonly - */ -Blockly.ScratchBlocks.VerticalExtensions.OUTPUT_BOOLEAN = function() { - this.setInputsInline(true); - this.setOutputShape(Blockly.OUTPUT_SHAPE_HEXAGONAL); - this.setOutput(true, 'Boolean'); -}; - -/** - * Mixin to add a context menu for a procedure definition block. - * It adds the "edit" option and removes the "duplicate" option. - * @mixin - * @augments Blockly.Block - * @package - * @readonly - */ -Blockly.ScratchBlocks.VerticalExtensions.PROCEDURE_DEF_CONTEXTMENU = { - /** - * Add the "edit" option and removes the "duplicate" option from the context - * menu. - * @param {!Array.} menuOptions List of menu options to edit. - * @this Blockly.Block - */ - customContextMenu: function(menuOptions) { - // Add the edit option at the end. - menuOptions.push(Blockly.Procedures.makeEditOption(this)); - - // Find the delete option and update its callback to be specific to - // functions. - for (var i = 0, option; option = menuOptions[i]; i++) { - if (option.text == Blockly.Msg.DELETE_BLOCK) { - var input = this.getInput('custom_block'); - // this is the root block, not the shadow block. - if (input && input.connection && input.connection.targetBlock()) { - var procCode = input.connection.targetBlock().getProcCode(); - } else { - return; - } - var rootBlock = this; - option.callback = function() { - var didDelete = Blockly.Procedures.deleteProcedureDefCallback( - procCode, rootBlock); - if (!didDelete) { - alert(Blockly.Msg.PROCEDURE_USED); - } - }; - } - } - // Find and remove the duplicate option - for (var i = 0, option; option = menuOptions[i]; i++) { - if (option.text == Blockly.Msg.DUPLICATE) { - menuOptions.splice(i, 1); - break; - } - } - } -}; - -/** - * Mixin to add a context menu for a procedure call block. - * It adds the "edit" option and the "define" option. - * @mixin - * @augments Blockly.Block - * @package - * @readonly - */ -Blockly.ScratchBlocks.VerticalExtensions.PROCEDURE_CALL_CONTEXTMENU = { - /** - * Add the "edit" option to the context menu. - * @todo Add "go to definition" option once implemented. - * @param {!Array.} menuOptions List of menu options to edit. - * @this Blockly.Block - */ - customContextMenu: function(menuOptions) { - menuOptions.push(Blockly.Procedures.makeEditOption(this)); - } -}; - - -Blockly.ScratchBlocks.VerticalExtensions.SCRATCH_EXTENSION = function() { - this.isScratchExtension = true; -}; -/** - * Register all extensions for scratch-blocks. - * @package - */ -Blockly.ScratchBlocks.VerticalExtensions.registerAll = function() { - var categoryNames = - ['control', 'data', 'data_lists', 'sounds', 'motion', 'looks', 'event', - 'sensing', 'pen', 'operators', 'more']; - // Register functions for all category colours. - for (var i = 0; i < categoryNames.length; i++) { - var name = categoryNames[i]; - Blockly.Extensions.register('colours_' + name, - Blockly.ScratchBlocks.VerticalExtensions.colourHelper(name)); - } - - // Text fields transcend categories. - Blockly.Extensions.register('colours_textfield', - Blockly.ScratchBlocks.VerticalExtensions.COLOUR_TEXTFIELD); - - // Register extensions for common block shapes. - Blockly.Extensions.register('shape_statement', - Blockly.ScratchBlocks.VerticalExtensions.SHAPE_STATEMENT); - Blockly.Extensions.register('shape_hat', - Blockly.ScratchBlocks.VerticalExtensions.SHAPE_HAT); - Blockly.Extensions.register('shape_end', - Blockly.ScratchBlocks.VerticalExtensions.SHAPE_END); - - // Output shapes and types are related. - Blockly.Extensions.register('output_number', - Blockly.ScratchBlocks.VerticalExtensions.OUTPUT_NUMBER); - Blockly.Extensions.register('output_string', - Blockly.ScratchBlocks.VerticalExtensions.OUTPUT_STRING); - Blockly.Extensions.register('output_boolean', - Blockly.ScratchBlocks.VerticalExtensions.OUTPUT_BOOLEAN); - - // Custom procedures have interesting context menus. - Blockly.Extensions.registerMixin('procedure_def_contextmenu', - Blockly.ScratchBlocks.VerticalExtensions.PROCEDURE_DEF_CONTEXTMENU); - Blockly.Extensions.registerMixin('procedure_call_contextmenu', - Blockly.ScratchBlocks.VerticalExtensions.PROCEDURE_CALL_CONTEXTMENU); - - // Extension blocks have slightly different block rendering. - Blockly.Extensions.register('scratch_extension', - Blockly.ScratchBlocks.VerticalExtensions.SCRATCH_EXTENSION); -}; - -Blockly.ScratchBlocks.VerticalExtensions.registerAll(); diff --git a/build.py b/build.py deleted file mode 100755 index 3fc56e5fd3..0000000000 --- a/build.py +++ /dev/null @@ -1,636 +0,0 @@ -#!/usr/bin/python2.7 -# Compresses the core Blockly files into a single JavaScript file. -# -# Copyright 2012 Google Inc. -# https://developers.google.com/blockly/ -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This script generates two versions of Blockly's core files: -# blockly_compressed.js -# blockly_uncompressed.js -# The compressed file is a concatenation of all of Blockly's core files which -# have been run through Google's Closure Compiler. This is done using the -# online API (which takes a few seconds and requires an Internet connection). -# The uncompressed file is a script that loads in each of Blockly's core files -# one by one. This takes much longer for a browser to load, but is useful -# when debugging code since line numbers are meaningful and variables haven't -# been renamed. The uncompressed file also allows for a faster developement -# cycle since there is no need to rebuild or recompile, just reload. -# -# This script also generates: -# blocks_compressed.js: The compressed common blocks. -# blocks_horizontal_compressed.js: The compressed Scratch horizontal blocks. -# blocks_vertical_compressed.js: The compressed Scratch vertical blocks. -# msg/js/.js for every language defined in msg/js/.json. - -import sys - -import errno, glob, json, os, re, subprocess, threading, codecs, functools - -if sys.version_info[0] == 2: - import httplib - from urllib import urlencode -else: - import http.client as httplib - from urllib.parse import urlencode - from importlib import reload - -REMOTE_COMPILER = "remote" - -CLOSURE_DIR = os.path.pardir -CLOSURE_ROOT = os.path.pardir -CLOSURE_LIBRARY = "closure-library" -CLOSURE_COMPILER = REMOTE_COMPILER - -CLOSURE_DIR_NPM = "node_modules" -CLOSURE_ROOT_NPM = os.path.join("node_modules") -CLOSURE_LIBRARY_NPM = "google-closure-library" -CLOSURE_COMPILER_NPM = ("google-closure-compiler.cmd" if os.name == "nt" else "google-closure-compiler") - -def import_path(fullpath): - """Import a file with full path specification. - Allows one to import from any directory, something __import__ does not do. - - Args: - fullpath: Path and filename of import. - - Returns: - An imported module. - """ - path, filename = os.path.split(fullpath) - filename, ext = os.path.splitext(filename) - sys.path.append(path) - module = __import__(filename) - reload(module) # Might be out of date. - del sys.path[-1] - return module - -def read(filename): - f = open(filename) - content = "".join(f.readlines()) - f.close() - return content - -HEADER = ("// Do not edit this file; automatically generated by build.py.\n" - "'use strict';\n") - - -class Gen_uncompressed(threading.Thread): - """Generate a JavaScript file that loads Blockly's raw files. - Runs in a separate thread. - """ - def __init__(self, search_paths, vertical, closure_env): - threading.Thread.__init__(self) - self.search_paths = search_paths - self.vertical = vertical - self.closure_env = closure_env - - def run(self): - if self.vertical: - target_filename = 'blockly_uncompressed_vertical.js' - else: - target_filename = 'blockly_uncompressed_horizontal.js' - f = open(target_filename, 'w') - f.write(HEADER) - f.write(self.format_js(""" -var isNodeJS = !!(typeof module !== 'undefined' && module.exports && - typeof window === 'undefined'); - -if (isNodeJS) { - var window = {}; - require('{closure_library}'); -} - -window.BLOCKLY_DIR = (function() { - if (!isNodeJS) { - // Find name of current directory. - var scripts = document.getElementsByTagName('script'); - var re = new RegExp('(.+)[\/]blockly_uncompressed(_vertical|_horizontal|)\.js$'); - for (var i = 0, script; script = scripts[i]; i++) { - var match = re.exec(script.src); - if (match) { - return match[1]; - } - } - alert('Could not detect Blockly\\'s directory name.'); - } - return ''; -})(); - -window.BLOCKLY_BOOT = function() { - var dir = ''; - if (isNodeJS) { - require('{closure_library}'); - dir = 'blockly'; - } else { - // Execute after Closure has loaded. - if (!window.goog) { - alert('Error: Closure not found. Read this:\\n' + - 'developers.google.com/blockly/guides/modify/web/closure'); - } - if (window.BLOCKLY_DIR.search(/node_modules/)) { - dir = '..'; - } else { - dir = window.BLOCKLY_DIR.match(/[^\\/]+$/)[0]; - } - } -""")) - add_dependency = [] - base_path = calcdeps.FindClosureBasePath(self.search_paths) - for dep in calcdeps.BuildDependenciesFromFiles(self.search_paths): - add_dependency.append(calcdeps.GetDepsLine(dep, base_path)) - add_dependency.sort() # Deterministic build. - add_dependency = '\n'.join(add_dependency) - # Find the Blockly directory name and replace it with a JS variable. - # This allows blockly_uncompressed.js to be compiled on one computer and be - # used on another, even if the directory name differs. - m = re.search('[\\/]([^\\/]+)[\\/]core[\\/]blockly.js', add_dependency) - add_dependency = re.sub('([\\/])' + re.escape(m.group(1)) + - '([\\/]core[\\/])', '\\1" + dir + "\\2', add_dependency) - f.write(add_dependency + '\n') - - provides = [] - for dep in calcdeps.BuildDependenciesFromFiles(self.search_paths): - # starts with '../' or 'node_modules/' - if not dep.filename.startswith(self.closure_env["closure_root"] + os.sep): - provides.extend(dep.provides) - provides.sort() # Deterministic build. - f.write('\n') - f.write('// Load Blockly.\n') - for provide in provides: - f.write("goog.require('%s');\n" % provide) - - f.write(self.format_js(""" -delete this.BLOCKLY_DIR; -delete this.BLOCKLY_BOOT; -}; - -if (isNodeJS) { - window.BLOCKLY_BOOT(); - module.exports = Blockly; -} else { - // Delete any existing Closure (e.g. Soy's nogoog_shim). - document.write(''); - // Load fresh Closure Library. - document.write(''); - document.write(''); -} -""")) - f.close() - print("SUCCESS: " + target_filename) - - def format_js(self, code): - """Format JS in a way that python's format method can work with to not - consider brace-wrapped sections to be format replacements while still - replacing known keys. - """ - - key_whitelist = self.closure_env.keys() - - keys_pipe_separated = functools.reduce(lambda accum, key: accum + "|" + key, key_whitelist) - begin_brace = re.compile(r"\{(?!%s)" % (keys_pipe_separated,)) - - end_brace = re.compile(r"\}") - def end_replacement(match): - try: - maybe_key = match.string[match.string[:match.start()].rindex("{") + 1:match.start()] - except ValueError: - return "}}" - - if maybe_key and maybe_key in key_whitelist: - return "}" - else: - return "}}" - - return begin_brace.sub("{{", end_brace.sub(end_replacement, code)).format(**self.closure_env) - -class Gen_compressed(threading.Thread): - """Generate a JavaScript file that contains all of Blockly's core and all - required parts of Closure, compiled together. - Uses the Closure Compiler's online API. - Runs in a separate thread. - """ - def __init__(self, search_paths_vertical, search_paths_horizontal, closure_env): - threading.Thread.__init__(self) - self.search_paths_vertical = search_paths_vertical - self.search_paths_horizontal = search_paths_horizontal - self.closure_env = closure_env - - def run(self): - self.gen_core(True) - self.gen_core(False) - self.gen_blocks("horizontal") - self.gen_blocks("vertical") - self.gen_blocks("common") - - def gen_core(self, vertical): - if vertical: - target_filename = 'blockly_compressed_vertical.js' - search_paths = self.search_paths_vertical - else: - target_filename = 'blockly_compressed_horizontal.js' - search_paths = self.search_paths_horizontal - # Define the parameters for the POST request. - params = [ - ("compilation_level", "SIMPLE"), - - # remote will filter this out - ("language_in", "ECMASCRIPT_2017"), - ("language_out", "ECMASCRIPT5"), - ("rewrite_polyfills", "false"), - ("define", "goog.DEBUG=false"), - - # local will filter this out - ("use_closure_library", "true"), - ] - - # Read in all the source files. - filenames = calcdeps.CalculateDependencies(search_paths, - [os.path.join("core", "blockly.js")]) - filenames.sort() # Deterministic build. - for filename in filenames: - # Append filenames as false arguments the step before compiling will - # either transform them into arguments for local or remote compilation - params.append(("js_file", filename)) - - self.do_compile(params, target_filename, filenames, "") - - def gen_blocks(self, block_type): - if block_type == "horizontal": - target_filename = "blocks_compressed_horizontal.js" - filenames = glob.glob(os.path.join("blocks_horizontal", "*.js")) - elif block_type == "vertical": - target_filename = "blocks_compressed_vertical.js" - filenames = glob.glob(os.path.join("blocks_vertical", "*.js")) - elif block_type == "common": - target_filename = "blocks_compressed.js" - filenames = glob.glob(os.path.join("blocks_common", "*.js")) - - # glob.glob ordering is platform-dependent and not necessary deterministic - filenames.sort() # Deterministic build. - - # Define the parameters for the POST request. - params = [ - ("compilation_level", "SIMPLE"), - ] - - # Read in all the source files. - # Add Blockly.Blocks to be compatible with the compiler. - params.append(("js_file", os.path.join("build", "gen_blocks.js"))) - # Add Blockly.Colours for use of centralized colour bank - filenames.append(os.path.join("core", "colours.js")) - filenames.append(os.path.join("core", "constants.js")) - - for filename in filenames: - # Append filenames as false arguments the step before compiling will - # either transform them into arguments for local or remote compilation - params.append(("js_file", filename)) - - # Remove Blockly.Blocks to be compatible with Blockly. - remove = "var Blockly={Blocks:{}};" - self.do_compile(params, target_filename, filenames, remove) - - def do_compile(self, params, target_filename, filenames, remove): - if self.closure_env["closure_compiler"] == REMOTE_COMPILER: - do_compile = self.do_compile_remote - else: - do_compile = self.do_compile_local - json_data = do_compile(params, target_filename) - - if self.report_errors(target_filename, filenames, json_data): - self.write_output(target_filename, remove, json_data) - self.report_stats(target_filename, json_data) - - def do_compile_local(self, params, target_filename): - filter_keys = ["use_closure_library"] - - # Drop arg if arg is js_file else add dashes - dash_params = [] - for (arg, value) in params: - dash_params.append((value,) if arg == "js_file" else ("--" + arg, value)) - - # Flatten dash_params into dash_args if their keys are not in filter_keys - dash_args = [] - for pair in dash_params: - if pair[0][2:] not in filter_keys: - dash_args.extend(pair) - - # Build the final args array by prepending CLOSURE_COMPILER_NPM to - # dash_args and dropping any falsy members - args = [] - for group in [[CLOSURE_COMPILER_NPM], dash_args]: - args.extend(filter(lambda item: item, group)) - - proc = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) - (stdout, stderr) = proc.communicate() - - # Build the JSON response. - filesizes = [os.path.getsize(value) for (arg, value) in params if arg == "js_file"] - return dict( - compiledCode=stdout, - statistics=dict( - originalSize=functools.reduce(lambda v, size: v + size, filesizes, 0), - compressedSize=len(stdout), - ) - ) - - def do_compile_remote(self, params, target_filename): - filter_keys = [ - "language_in", - "language_out", - "rewrite_polyfills", - "define", - ] - - params.extend([ - ("output_format", "json"), - ("output_info", "compiled_code"), - ("output_info", "warnings"), - ("output_info", "errors"), - ("output_info", "statistics"), - ]) - - # Send the request to Google. - remoteParams = [] - for (arg, value) in params: - if not arg in filter_keys: - if arg == "js_file": - if not value.startswith(self.closure_env["closure_root"] + os.sep): - remoteParams.append(("js_code", read(value))) - # Change the normal compilation_level value SIMPLE to the remove - # service's SIMPLE_OPTIMIZATIONS - elif arg == "compilation_level" and value == "SIMPLE": - remoteParams.append((arg, "SIMPLE_OPTIMIZATIONS")) - else: - remoteParams.append((arg, value)) - - headers = {"Content-type": "application/x-www-form-urlencoded"} - conn = httplib.HTTPSConnection("closure-compiler.appspot.com") - conn.request("POST", "/compile", urlencode(remoteParams), headers) - response = conn.getresponse() - # Decode is necessary for Python 3.4 compatibility - json_str = response.read().decode("utf-8") - conn.close() - - # Parse the JSON response. - return json.loads(json_str) - - def report_errors(self, target_filename, filenames, json_data): - def file_lookup(name): - if not name.startswith("Input_"): - return "???" - n = int(name[6:]) - 1 - return filenames[n] - - if "serverErrors" in json_data: - errors = json_data["serverErrors"] - for error in errors: - print("SERVER ERROR: %s" % target_filename) - print(error["error"]) - elif "errors" in json_data: - errors = json_data["errors"] - for error in errors: - print("FATAL ERROR") - print(error["error"]) - if error["file"]: - print("%s at line %d:" % ( - file_lookup(error["file"]), error["lineno"])) - print(error["line"]) - print((" " * error["charno"]) + "^") - sys.exit(1) - else: - if "warnings" in json_data: - warnings = json_data["warnings"] - for warning in warnings: - print("WARNING") - print(warning["warning"]) - if warning["file"]: - print("%s at line %d:" % ( - file_lookup(warning["file"]), warning["lineno"])) - print(warning["line"]) - print((" " * warning["charno"]) + "^") - print() - - return True - - return False - - def write_output(self, target_filename, remove, json_data): - if "compiledCode" not in json_data: - print("FATAL ERROR: Compiler did not return compiledCode.") - sys.exit(1) - - code = HEADER + "\n" + json_data["compiledCode"].decode("utf-8") - code = code.replace(remove, "") - - # Trim down Google's (and only Google's) Apache licences. - # The Closure Compiler preserves these. - LICENSE = re.compile("""/\\* - - [\w ]+ - - Copyright \\d+ Google Inc. - https://developers.google.com/blockly/ - - Licensed under the Apache License, Version 2.0 \(the "License"\); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -\\*/""") - code = re.sub(LICENSE, "", code) - - stats = json_data["statistics"] - original_b = stats["originalSize"] - compressed_b = stats["compressedSize"] - if original_b > 0 and compressed_b > 0: - f = open(target_filename, "w") - f.write(code) - f.close() - - def report_stats(self, target_filename, json_data): - stats = json_data["statistics"] - original_b = stats["originalSize"] - compressed_b = stats["compressedSize"] - if original_b > 0 and compressed_b > 0: - original_kb = int(original_b / 1024 + 0.5) - compressed_kb = int(compressed_b / 1024 + 0.5) - ratio = int(float(compressed_b) / float(original_b) * 100 + 0.5) - print("SUCCESS: " + target_filename) - print("Size changed from %d KB to %d KB (%d%%)." % ( - original_kb, compressed_kb, ratio)) - else: - print("UNKNOWN ERROR") - - -class Gen_langfiles(threading.Thread): - """Generate JavaScript file for each natural language supported. - - Runs in a separate thread. - """ - - def __init__(self): - threading.Thread.__init__(self) - - def _rebuild(self, srcs, dests): - # Determine whether any of the files in srcs is newer than any in dests. - try: - return (max(os.path.getmtime(src) for src in srcs) > - min(os.path.getmtime(dest) for dest in dests)) - except OSError as e: - # Was a file not found? - if e.errno == errno.ENOENT: - # If it was a source file, we can't proceed. - if e.filename in srcs: - print("Source file missing: " + e.filename) - sys.exit(1) - else: - # If a destination file was missing, rebuild. - return True - else: - print("Error checking file creation times: " + str(e)) - - def run(self): - # The files msg/json/{en,qqq,synonyms}.json depend on msg/messages.js. - if self._rebuild([os.path.join("msg", "messages.js")], - [os.path.join("msg", "json", f) for f in - ["en.json", "qqq.json", "synonyms.json"]]): - try: - subprocess.check_call([ - "python", - os.path.join("i18n", "js_to_json.py"), - "--input_file", "msg/messages.js", - "--output_dir", "msg/json/", - "--quiet"]) - except (subprocess.CalledProcessError, OSError) as e: - # Documentation for subprocess.check_call says that CalledProcessError - # will be raised on failure, but I found that OSError is also possible. - print("Error running i18n/js_to_json.py: ", e) - sys.exit(1) - - # Checking whether it is necessary to rebuild the js files would be a lot of - # work since we would have to compare each .json file with each - # .js file. Rebuilding is easy and cheap, so just go ahead and do it. - try: - # Use create_messages.py to create .js files from .json files. - cmd = [ - "python", - os.path.join("i18n", "create_messages.py"), - "--source_lang_file", os.path.join("msg", "json", "en.json"), - "--source_synonym_file", os.path.join("msg", "json", "synonyms.json"), - "--source_constants_file", os.path.join("msg", "json", "constants.json"), - "--key_file", os.path.join("msg", "json", "keys.json"), - "--output_dir", os.path.join("msg", "js"), - "--quiet"] - json_files = glob.glob(os.path.join("msg", "json", "*.json")) - json_files = [file for file in json_files if not - (file.endswith(("keys.json", "synonyms.json", "qqq.json", "constants.json")))] - cmd.extend(json_files) - subprocess.check_call(cmd) - except (subprocess.CalledProcessError, OSError) as e: - print("Error running i18n/create_messages.py: ", e) - sys.exit(1) - - # Output list of .js files created. - for f in json_files: - # This assumes the path to the current directory does not contain "json". - f = f.replace("json", "js") - if os.path.isfile(f): - print("SUCCESS: " + f) - else: - print("FAILED to create " + f) - -def exclude_vertical(item): - return not item.endswith("block_render_svg_vertical.js") - -def exclude_horizontal(item): - return not item.endswith("block_render_svg_horizontal.js") - -if __name__ == "__main__": - try: - closure_dir = CLOSURE_DIR_NPM - closure_root = CLOSURE_ROOT_NPM - closure_library = CLOSURE_LIBRARY_NPM - closure_compiler = CLOSURE_COMPILER_NPM - - # Load calcdeps from the local library - calcdeps = import_path(os.path.join( - closure_root, closure_library, "closure", "bin", "calcdeps.py")) - - # Sanity check the local compiler - test_args = [closure_compiler, os.path.join("build", "test_input.js")] - test_proc = subprocess.Popen(test_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE) - (stdout, _) = test_proc.communicate() - assert stdout.decode("utf-8") == read(os.path.join("build", "test_expect.js")) - - print("Using local compiler: %s ...\n" % CLOSURE_COMPILER_NPM) - except (ImportError, AssertionError): - print("Using remote compiler: closure-compiler.appspot.com ...\n") - - try: - closure_dir = CLOSURE_DIR - closure_root = CLOSURE_ROOT - closure_library = CLOSURE_LIBRARY - closure_compiler = CLOSURE_COMPILER - - calcdeps = import_path(os.path.join( - closure_root, closure_library, "closure", "bin", "calcdeps.py")) - except ImportError: - if os.path.isdir(os.path.join(os.path.pardir, "closure-library-read-only")): - # Dir got renamed when Closure moved from Google Code to GitHub in 2014. - print("Error: Closure directory needs to be renamed from" - "'closure-library-read-only' to 'closure-library'.\n" - "Please rename this directory.") - elif os.path.isdir(os.path.join(os.path.pardir, "google-closure-library")): - print("Error: Closure directory needs to be renamed from" - "'google-closure-library' to 'closure-library'.\n" - "Please rename this directory.") - else: - print("""Error: Closure not found. Read this: - developers.google.com/blockly/guides/modify/web/closure""") - sys.exit(1) - - search_paths = list(calcdeps.ExpandDirectories( - ["core", os.path.join(closure_root, closure_library)])) - - search_paths_horizontal = list(filter(exclude_vertical, search_paths)) - search_paths_vertical = list(filter(exclude_horizontal, search_paths)) - - closure_env = { - "closure_dir": closure_dir, - "closure_root": closure_root, - "closure_library": closure_library, - "closure_compiler": closure_compiler, - } - - # Run all tasks in parallel threads. - # Uncompressed is limited by processor speed. - # Compressed is limited by network and server speed. - # Vertical: - Gen_uncompressed(search_paths_vertical, True, closure_env).start() - # Horizontal: - Gen_uncompressed(search_paths_horizontal, False, closure_env).start() - - # Compressed forms of vertical and horizontal. - Gen_compressed(search_paths_vertical, search_paths_horizontal, closure_env).start() - - # This is run locally in a separate thread. - # Gen_langfiles().start() diff --git a/build/gen_blocks.js b/build/gen_blocks.js deleted file mode 100644 index 2ab6e688b5..0000000000 --- a/build/gen_blocks.js +++ /dev/null @@ -1 +0,0 @@ -goog.provide('Blockly.Blocks'); diff --git a/build/test_expect.js b/build/test_expect.js deleted file mode 100644 index 555c3f7c54..0000000000 --- a/build/test_expect.js +++ /dev/null @@ -1 +0,0 @@ -var Blockly={Blocks:{}}; diff --git a/build/test_input.js b/build/test_input.js deleted file mode 100644 index 2ab6e688b5..0000000000 --- a/build/test_input.js +++ /dev/null @@ -1 +0,0 @@ -goog.provide('Blockly.Blocks'); diff --git a/cleanup.sh b/cleanup.sh deleted file mode 100755 index 8af8cf6a70..0000000000 --- a/cleanup.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/bin/bash - -# Script for cleaning up blockly-specific files when merging blockly into scratch-blocks -# Removes files and directories that scratch-blocks doesn't want. -# Rachel Fenichel (fenichel@google.com) - -# Note: 'ours' is scratch-blocks, 'theirs' is blockly. - -# Formatting helpers. -indent() { sed 's/^/ /'; } -indent_more() { sed 's/^/\t/'; } -empty_lines() { printf '\n\n'; } - - -empty_lines -echo Cleaning up a merge from Blockly to Scratch-Blocks... - -# Get rid of Blockly's internationalization/messages. This is not usually worth -# scrolling up to look at. -empty_lines -echo Cleaning up Blockly message files... -# Turn on more powerful globbing -shopt -s extglob - -# Having trouble with directories. Let's just go there. -cd msg/json -git rm -f !(en.json|synonyms.json) | indent_more -cd ../.. - -# Having trouble with directories. Let's just go there. -cd msg/js -git rm -f !(en.js) | indent_more -cd ../.. - -# Turn powerful globbing off again -shopt -u extglob - -# Whole directories that we want to get rid of. -empty_lines -echo Removing blockly-specific directories... -dirslist="accessible demos tests/generators appengine blocks local_build" -for directory in $dirslist -do - echo 'Cleaning up' $directory | indent - git rm -rf $directory | indent_more -done - -# Scratch-blocks does not use generators -empty_lines -echo Removing generators... -generated_langs="dart javascript lua php python" -for lang in $generated_langs -do - echo 'Cleaning up' $lang | indent - # Directories containing block generators. - git rm -rf generators/${lang} | indent_more -done - -# Built stuff that we should get rid of. -empty_lines -echo Removing built files... -built_files="blockly_compressed.js \ -blockly_uncompressed.js \ -blockly_accessible_compressed.js \ -blockly_accessible_uncompressed.js \ -blocks_compressed.js \ -dart_compressed.js \ -php_compressed.js \ -python_compressed.js \ -javascript_compressed.js \ -lua_compressed.js" - -for filename in $built_files -do - git rm $filename | indent_more -done - -empty_lines -echo Miscellaneous cleanup... -# Use ours. -keep_ours=".github/ISSUE_TEMPLATE.md \ -.github/PULL_REQUEST_TEMPLATE.md \ -.gitignore \ -.circleci/config.yml \ -core/block_animations.js \ -msg/messages.js \ -msg/js/en.js \ -msg/json/en.json" - - -for filename in $keep_ours -do - git checkout --ours $filename && git add $filename | indent_more -done - -# Scratch-blocks has separate vertical and horizontal playgrounds and block -# rendering. -git rm -f tests/playground.html core/block_render_svg.js | indent_more - -empty_lines -echo Done with cleanup. diff --git a/core/block.js b/core/block.js deleted file mode 100644 index 176cbcc586..0000000000 --- a/core/block.js +++ /dev/null @@ -1,1837 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2011 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview The class representing one block. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.Block'); - -goog.require('Blockly.Blocks'); -goog.require('Blockly.Colours'); -goog.require('Blockly.Comment'); -goog.require('Blockly.ScratchBlockComment'); -goog.require('Blockly.Connection'); -goog.require('Blockly.Events.BlockChange'); -goog.require('Blockly.Events.BlockCreate'); -goog.require('Blockly.Events.BlockDelete'); -goog.require('Blockly.Events.BlockMove'); -goog.require('Blockly.Extensions'); -goog.require('Blockly.FieldLabelSerializable'); -goog.require('Blockly.FieldVariableGetter'); -goog.require('Blockly.Input'); -goog.require('Blockly.Mutator'); -goog.require('Blockly.Warning'); -goog.require('Blockly.Workspace'); -goog.require('Blockly.Xml'); -goog.require('goog.array'); -goog.require('goog.asserts'); -goog.require('goog.math.Coordinate'); -goog.require('goog.string'); - - -/** - * Class for one block. - * Not normally called directly, workspace.newBlock() is preferred. - * @param {!Blockly.Workspace} workspace The block's workspace. - * @param {?string} prototypeName Name of the language object containing - * type-specific functions for this block. - * @param {string=} opt_id Optional ID. Use this ID if provided, otherwise - * create a new ID. If the ID conflicts with an in-use ID, a new one will - * be generated. - * @constructor - */ -Blockly.Block = function(workspace, prototypeName, opt_id) { - var flyoutWorkspace = workspace && workspace.getFlyout && workspace.getFlyout() ? - workspace.getFlyout().getWorkspace() : null; - /** @type {string} */ - this.id = (opt_id && !workspace.getBlockById(opt_id) && - (!flyoutWorkspace || !flyoutWorkspace.getBlockById(opt_id))) ? - opt_id : Blockly.utils.genUid(); - workspace.blockDB_[this.id] = this; - /** @type {Blockly.Connection} */ - this.outputConnection = null; - /** @type {Blockly.Connection} */ - this.nextConnection = null; - /** @type {Blockly.Connection} */ - this.previousConnection = null; - /** @type {!Array.} */ - this.inputList = []; - /** @type {boolean|undefined} */ - this.inputsInline = true; - /** @type {boolean} */ - this.disabled = false; - /** @type {string|!Function} */ - this.tooltip = ''; - /** @type {boolean} */ - this.contextMenu = true; - - /** - * @type {Blockly.Block} - * @protected - */ - this.parentBlock_ = null; - - /** - * @type {!Array.} - * @protected - */ - this.childBlocks_ = []; - - /** - * @type {boolean} - * @private - */ - this.deletable_ = true; - - /** - * @type {boolean} - * @private - */ - this.movable_ = true; - - /** - * @type {boolean} - * @private - */ - this.editable_ = true; - - /** - * @type {boolean} - * @private - */ - this.isShadow_ = false; - - /** - * @type {boolean} - * @protected - */ - this.collapsed_ = false; - - /** - * @type {boolean} - * @private - */ - this.checkboxInFlyout_ = false; - - /** @type {string|Blockly.Comment} */ - this.comment = null; - - /** - * @type {?number} - * @private - */ - this.outputShape_ = null; - - /** - * @type {?string} - * @private - */ - this.category_ = null; - - /** - * The block's position in workspace units. (0, 0) is at the workspace's - * origin; scale does not change this value. - * @type {!goog.math.Coordinate} - * @private - */ - this.xy_ = new goog.math.Coordinate(0, 0); - - /** @type {!Blockly.Workspace} */ - this.workspace = workspace; - /** @type {boolean} */ - this.isInFlyout = workspace.isFlyout; - /** @type {boolean} */ - this.isInMutator = workspace.isMutator; - - /** @type {boolean} */ - this.RTL = workspace.RTL; - - /** @type {boolean} */ - this.isInsertionMarker_ = false; - - // Copy the type-specific functions and data from the prototype. - if (prototypeName) { - /** @type {string} */ - this.type = prototypeName; - var prototype = Blockly.Blocks[prototypeName]; - goog.asserts.assertObject(prototype, - 'Error: Unknown block type "%s".', prototypeName); - goog.mixin(this, prototype); - } - - workspace.addTopBlock(this); - - // Call an initialization function, if it exists. - if (goog.isFunction(this.init)) { - this.init(); - } - // Record initial inline state. - /** @type {boolean|undefined} */ - this.inputsInlineDefault = this.inputsInline; - - // Fire a create event. - if (Blockly.Events.isEnabled()) { - var existingGroup = Blockly.Events.getGroup(); - if (!existingGroup) { - Blockly.Events.setGroup(true); - } - try { - Blockly.Events.fire(new Blockly.Events.BlockCreate(this)); - } finally { - if (!existingGroup) { - Blockly.Events.setGroup(false); - } - } - - } - // Bind an onchange function, if it exists. - if (goog.isFunction(this.onchange)) { - this.setOnChange(this.onchange); - } -}; - -/** - * Optional text data that round-trips beween blocks and XML. - * Has no effect. May be used by 3rd parties for meta information. - * @type {?string} - */ -Blockly.Block.prototype.data = null; - -/** - * Colour of the block in '#RRGGBB' format. - * @type {string} - * @private - */ -Blockly.Block.prototype.colour_ = '#FF0000'; - -/** - * Secondary colour of the block in '#RRGGBB' format. - * @type {string} - * @private - */ -Blockly.Block.prototype.colourSecondary_ = '#FF0000'; - -/** - * Tertiary colour of the block in '#RRGGBB' format. - * @type {string} - * @private - */ -Blockly.Block.prototype.colourTertiary_ = '#FF0000'; - -/** - * Quaternary colour of the block in '#RRGGBB' format. - * @type {string} - * @private - */ -Blockly.Block.prototype.colourQuaternary = '#FF0000'; - -/** - * Fill colour used to override default shadow colour behaviour. - * @type {string} - * @private - */ -Blockly.Block.prototype.shadowColour_ = null; - -/** - * Dispose of this block. - * @param {boolean} healStack If true, then try to heal any gap by connecting - * the next statement with the previous statement. Otherwise, dispose of - * all children of this block. - */ -Blockly.Block.prototype.dispose = function(healStack) { - if (!this.workspace) { - // Already deleted. - return; - } - // Terminate onchange event calls. - if (this.onchangeWrapper_) { - this.workspace.removeChangeListener(this.onchangeWrapper_); - } - this.unplug(healStack); - if (Blockly.Events.isEnabled()) { - Blockly.Events.fire(new Blockly.Events.BlockDelete(this)); - } - Blockly.Events.disable(); - - try { - // This block is now at the top of the workspace. - // Remove this block from the workspace's list of top-most blocks. - if (this.workspace) { - this.workspace.removeTopBlock(this); - // Remove from block database. - delete this.workspace.blockDB_[this.id]; - this.workspace = null; - } - - // Just deleting this block from the DOM would result in a memory leak as - // well as corruption of the connection database. Therefore we must - // methodically step through the blocks and carefully disassemble them. - - if (Blockly.selected == this) { - Blockly.selected = null; - } - - // First, dispose of all my children. - for (var i = this.childBlocks_.length - 1; i >= 0; i--) { - this.childBlocks_[i].dispose(false); - } - // Then dispose of myself. - // Dispose of all inputs and their fields. - for (var i = 0, input; input = this.inputList[i]; i++) { - input.dispose(); - } - this.inputList.length = 0; - // Dispose of any remaining connections (next/previous/output). - var connections = this.getConnections_(true); - for (var i = 0; i < connections.length; i++) { - var connection = connections[i]; - if (connection.isConnected()) { - connection.disconnect(); - } - connections[i].dispose(); - } - } finally { - Blockly.Events.enable(); - } -}; - -/** - * Call initModel on all fields on the block. - * May be called more than once. - * Either initModel or initSvg must be called after creating a block and before - * the first interaction with it. Interactions include UI actions - * (e.g. clicking and dragging) and firing events (e.g. create, delete, and - * change). - * @public - */ -Blockly.Block.prototype.initModel = function() { - for (var i = 0, input; input = this.inputList[i]; i++) { - for (var j = 0, field; field = input.fieldRow[j]; j++) { - if (field.initModel) { - field.initModel(); - } - } - } -}; - -/** - * Unplug this block from its superior block. If this block is a statement, - * optionally reconnect the block underneath with the block on top. - * @param {boolean=} opt_healStack Disconnect child statement and reconnect - * stack. Defaults to false. - */ -Blockly.Block.prototype.unplug = function(opt_healStack) { - if (this.outputConnection) { - if (this.outputConnection.isConnected()) { - // Disconnect from any superior block. - this.outputConnection.disconnect(); - } - } else { - if (this.previousConnection) { - var previousTarget = null; - if (this.previousConnection.isConnected()) { - // Remember the connection that any next statements need to connect to. - previousTarget = this.previousConnection.targetConnection; - // Detach this block from the parent's tree. - this.previousConnection.disconnect(); - } - } - var nextBlock = this.getNextBlock(); - if (opt_healStack && nextBlock) { - // Disconnect the next statement. - var nextTarget = this.nextConnection.targetConnection; - nextTarget.disconnect(); - if (previousTarget && previousTarget.checkType_(nextTarget)) { - // Attach the next statement to the previous statement. - previousTarget.connect(nextTarget); - } - } - } -}; - -/** - * Returns all connections originating from this block. - * @return {!Array.} Array of connections. - * @private - */ -Blockly.Block.prototype.getConnections_ = function() { - var myConnections = []; - if (this.outputConnection) { - myConnections.push(this.outputConnection); - } - if (this.previousConnection) { - myConnections.push(this.previousConnection); - } - if (this.nextConnection) { - myConnections.push(this.nextConnection); - } - for (var i = 0, input; input = this.inputList[i]; i++) { - if (input.connection) { - myConnections.push(input.connection); - } - } - return myConnections; -}; - -/** - * Walks down a stack of blocks and finds the last next connection on the stack. - * @return {Blockly.Connection} The last next connection on the stack, or null. - * @package - */ -Blockly.Block.prototype.lastConnectionInStack = function() { - var nextConnection = this.nextConnection; - while (nextConnection) { - var nextBlock = nextConnection.targetBlock(); - if (!nextBlock) { - // Found a next connection with nothing on the other side. - return nextConnection; - } - nextConnection = nextBlock.nextConnection; - } - // Ran out of next connections. - return null; -}; - -/** - * Bump unconnected blocks out of alignment. Two blocks which aren't actually - * connected should not coincidentally line up on screen. - * @protected - */ -Blockly.Block.prototype.bumpNeighbours_ = function() { - console.warn('Not expected to reach this bumpNeighbours_ function. The ' + - 'BlockSvg function for bumpNeighbours_ was expected to be called instead.'); -}; - -/** - * Return the parent block or null if this block is at the top level. - * @return {Blockly.Block} The block that holds the current block. - */ -Blockly.Block.prototype.getParent = function() { - // Look at the DOM to see if we are nested in another block. - return this.parentBlock_; -}; - -/** - * Return the input that connects to the specified block. - * @param {!Blockly.Block} block A block connected to an input on this block. - * @return {Blockly.Input} The input that connects to the specified block. - */ -Blockly.Block.prototype.getInputWithBlock = function(block) { - for (var i = 0, input; input = this.inputList[i]; i++) { - if (input.connection && input.connection.targetBlock() == block) { - return input; - } - } - return null; -}; - -/** - * Return the input that contains the specified connection - * @param {!Blockly.Connection} conn A connection on this block. - * @return {Blockly.Input} The input that contains the specified connection. - */ -Blockly.Block.prototype.getInputWithConnection = function(conn) { - for (var i = 0, input; input = this.inputList[i]; i++) { - if (input.connection == conn) { - return input; - } - } - return null; -}; - -/** - * Return the parent block that surrounds the current block, or null if this - * block has no surrounding block. A parent block might just be the previous - * statement, whereas the surrounding block is an if statement, while loop, etc. - * @return {Blockly.Block} The block that surrounds the current block. - */ -Blockly.Block.prototype.getSurroundParent = function() { - var block = this; - do { - var prevBlock = block; - block = block.getParent(); - if (!block) { - // Ran off the top. - return null; - } - } while (block.getNextBlock() == prevBlock); - // This block is an enclosing parent, not just a statement in a stack. - return block; -}; - -/** - * Return the next statement block directly connected to this block. - * @return {Blockly.Block} The next statement block or null. - */ -Blockly.Block.prototype.getNextBlock = function() { - return this.nextConnection && this.nextConnection.targetBlock(); -}; - -/** - * Return the previous statement block directly connected to this block. - * @return {Blockly.Block} The previous statement block or null. - */ -Blockly.Block.prototype.getPreviousBlock = function() { - return this.previousConnection && this.previousConnection.targetBlock(); -}; - -/** - * Return the connection on the first statement input on this block, or null if - * there are none. - * @return {Blockly.Connection} The first statement connection or null. - */ -Blockly.Block.prototype.getFirstStatementConnection = function() { - for (var i = 0, input; input = this.inputList[i]; i++) { - if (input.connection && input.connection.type == Blockly.NEXT_STATEMENT) { - return input.connection; - } - } - return null; -}; - -/** - * Return the top-most block in this block's tree. - * This will return itself if this block is at the top level. - * @return {!Blockly.Block} The root block. - */ -Blockly.Block.prototype.getRootBlock = function() { - var rootBlock; - var block = this; - do { - rootBlock = block; - block = rootBlock.parentBlock_; - } while (block); - return rootBlock; -}; - -/** - * Find all the blocks that are directly nested inside this one. - * Includes value and statement inputs, as well as any following statement. - * Excludes any connection on an output tab or any preceding statement. - * Blocks are optionally sorted by position; top to bottom. - * @param {boolean} ordered Sort the list if true. - * @return {!Array.} Array of blocks. - */ -Blockly.Block.prototype.getChildren = function(ordered) { - if (!ordered) { - return this.childBlocks_; - } - var blocks = []; - for (var i = 0, input; input = this.inputList[i]; i++) { - if (input.connection) { - var child = input.connection.targetBlock(); - if (child) { - blocks.push(child); - } - } - } - var next = this.getNextBlock(); - if (next) { - blocks.push(next); - } - return blocks; -}; - -/** - * Set parent of this block to be a new block or null. - * @param {Blockly.Block} newParent New parent block. - */ -Blockly.Block.prototype.setParent = function(newParent) { - if (newParent == this.parentBlock_) { - return; - } - if (this.parentBlock_) { - // Remove this block from the old parent's child list. - goog.array.remove(this.parentBlock_.childBlocks_, this); - - // Disconnect from superior blocks. - if (this.previousConnection && this.previousConnection.isConnected()) { - throw 'Still connected to previous block.'; - } - if (this.outputConnection && this.outputConnection.isConnected()) { - throw 'Still connected to parent block.'; - } - this.parentBlock_ = null; - // This block hasn't actually moved on-screen, so there's no need to update - // its connection locations. - } else { - // Remove this block from the workspace's list of top-most blocks. - this.workspace.removeTopBlock(this); - } - - this.parentBlock_ = newParent; - if (newParent) { - // Add this block to the new parent's child list. - newParent.childBlocks_.push(this); - } else { - this.workspace.addTopBlock(this); - } -}; - -/** - * Find all the blocks that are directly or indirectly nested inside this one. - * Includes this block in the list. - * Includes value and statement inputs, as well as any following statements. - * Excludes any connection on an output tab or any preceding statements. - * Blocks are optionally sorted by position, top to bottom. - * @param {boolean} ordered Sort the list if true. - * @param {boolean=} opt_ignoreShadows If set, don't include shadow blocks. - * @return {!Array.} Flattened array of blocks. - */ -Blockly.Block.prototype.getDescendants = function(ordered, opt_ignoreShadows) { - var blocks = [this]; - var childBlocks = this.getChildren(ordered); - for (var child, i = 0; child = childBlocks[i]; i++) { - if (!opt_ignoreShadows || !child.isShadow_) { - blocks.push.apply( - blocks, child.getDescendants(ordered, opt_ignoreShadows)); - } - } - return blocks; -}; - -/** - * Get whether this block is deletable or not. - * @return {boolean} True if deletable. - */ -Blockly.Block.prototype.isDeletable = function() { - return this.deletable_ && !this.isShadow_ && - !(this.workspace && this.workspace.options.readOnly); -}; - -/** - * Set whether this block is deletable or not. - * @param {boolean} deletable True if deletable. - */ -Blockly.Block.prototype.setDeletable = function(deletable) { - this.deletable_ = deletable; -}; - -/** - * Get whether this block is movable or not. - * @return {boolean} True if movable. - */ -Blockly.Block.prototype.isMovable = function() { - return this.movable_ && !this.isShadow_ && - !(this.workspace && this.workspace.options.readOnly); -}; - -/** - * Set whether this block is movable or not. - * @param {boolean} movable True if movable. - */ -Blockly.Block.prototype.setMovable = function(movable) { - this.movable_ = movable; -}; - -/** - * Get whether this block is a shadow block or not. - * @return {boolean} True if a shadow. - */ -Blockly.Block.prototype.isShadow = function() { - return this.isShadow_; -}; - -/** - * Set whether this block is a shadow block or not. - * @param {boolean} shadow True if a shadow. - */ -Blockly.Block.prototype.setShadow = function(shadow) { - this.isShadow_ = shadow; -}; - -/** - * Get whether this block is an insertion marker block or not. - * @return {boolean} True if an insertion marker. - */ -Blockly.Block.prototype.isInsertionMarker = function() { - return this.isInsertionMarker_; -}; - -/** - * Set whether this block is an insertion marker block or not. - * @param {boolean} insertionMarker True if an insertion marker. - */ -Blockly.Block.prototype.setInsertionMarker = function(insertionMarker) { - if (this.isInsertionMarker_ == insertionMarker) { - return; // No change. - } - this.isInsertionMarker_ = insertionMarker; - // TODO: handle removing insertion marker status. - if (this.isInsertionMarker_) { - this.setColour(Blockly.Colours.insertionMarker); - this.setOpacity(Blockly.Colours.insertionMarkerOpacity); - Blockly.utils.addClass(/** @type {!Element} */ (this.svgGroup_), - 'blocklyInsertionMarker'); - } -}; - -/** - * Get whether this block is editable or not. - * @return {boolean} True if editable. - */ -Blockly.Block.prototype.isEditable = function() { - return this.editable_ && !(this.workspace && this.workspace.options.readOnly); -}; - -/** - * Set whether this block is editable or not. - * @param {boolean} editable True if editable. - */ -Blockly.Block.prototype.setEditable = function(editable) { - this.editable_ = editable; - for (var i = 0, input; input = this.inputList[i]; i++) { - for (var j = 0, field; field = input.fieldRow[j]; j++) { - field.updateEditable(); - } - } -}; - -/** - * Set whether the connections are hidden (not tracked in a database) or not. - * Recursively walk down all child blocks (except collapsed blocks). - * @param {boolean} hidden True if connections are hidden. - */ -Blockly.Block.prototype.setConnectionsHidden = function(hidden) { - if (!hidden && this.isCollapsed()) { - if (this.outputConnection) { - this.outputConnection.setHidden(hidden); - } - if (this.previousConnection) { - this.previousConnection.setHidden(hidden); - } - if (this.nextConnection) { - this.nextConnection.setHidden(hidden); - var child = this.nextConnection.targetBlock(); - if (child) { - child.setConnectionsHidden(hidden); - } - } - } else { - var myConnections = this.getConnections_(true); - for (var i = 0, connection; connection = myConnections[i]; i++) { - connection.setHidden(hidden); - if (connection.isSuperior()) { - var child = connection.targetBlock(); - if (child) { - child.setConnectionsHidden(hidden); - } - } - } - } -}; - -/** - * Find the connection on this block that corresponds to the given connection - * on the other block. - * Used to match connections between a block and its insertion marker. - * @param {!Blockly.Block} otherBlock The other block to match against. - * @param {!Blockly.Connection} conn The other connection to match. - * @return {Blockly.Connection} the matching connection on this block, or null. - */ -Blockly.Block.prototype.getMatchingConnection = function(otherBlock, conn) { - var connections = this.getConnections_(true); - var otherConnections = otherBlock.getConnections_(true); - if (connections.length != otherConnections.length) { - throw "Connection lists did not match in length."; - } - for (var i = 0; i < otherConnections.length; i++) { - if (otherConnections[i] == conn) { - return connections[i]; - } - } - return null; -}; - -/** - * Set the URL of this block's help page. - * @param {string|Function} url URL string for block help, or function that - * returns a URL. Null for no help. - */ -Blockly.Block.prototype.setHelpUrl = function(url) { - this.helpUrl = url; -}; - -/** - * Change the tooltip text for a block. - * @param {string|!Function} newTip Text for tooltip or a parent element to - * link to for its tooltip. May be a function that returns a string. - */ -Blockly.Block.prototype.setTooltip = function(newTip) { - this.tooltip = newTip; -}; - -/** - * Get the colour of a block. - * @return {string} #RRGGBB string. - */ -Blockly.Block.prototype.getColour = function() { - return this.colour_; -}; - -/** - * Get the secondary colour of a block. - * @return {string} #RRGGBB string. - */ -Blockly.Block.prototype.getColourSecondary = function() { - return this.colourSecondary_; -}; - -/** - * Get the tertiary colour of a block. - * @return {string} #RRGGBB string. - */ -Blockly.Block.prototype.getColourTertiary = function() { - return this.colourTertiary_; -}; - -/** - * Get the quaternary colour of a block. - * @return {string} #RRGGBB string. - */ -Blockly.Block.prototype.getColourQuaternary = function() { - return this.colourQuaternary_; -}; - -/** - * Get the shadow colour of a block. - * @return {string} #RRGGBB string. - */ -Blockly.Block.prototype.getShadowColour = function() { - return this.shadowColour_; -}; - -/** - * Set the shadow colour of a block. - * @param {number|string} colour HSV hue value, or #RRGGBB string. - */ -Blockly.Block.prototype.setShadowColour = function(colour) { - this.shadowColour_ = this.makeColour_(colour); - if (this.rendered) { - this.updateColour(); - } -}; - -/** - * Clear the shadow colour of a block. - */ -Blockly.Block.prototype.clearShadowColour = function() { - this.shadowColour_ = null; - if (this.rendered) { - this.updateColour(); - } -}; - -/** -* Create an #RRGGBB string colour from a colour HSV hue value or #RRGGBB string. -* @param {number|string} colour HSV hue value, or #RRGGBB string. -* @return {string} #RRGGBB string. -* @private -*/ -Blockly.Block.prototype.makeColour_ = function(colour) { - var hue = Number(colour); - if (!isNaN(hue)) { - return Blockly.hueToRgb(hue); - } else if (goog.isString(colour) && colour.match(/^#[0-9a-fA-F]{6}$/)) { - return colour; - } else { - throw 'Invalid colour: ' + colour; - } -}; - -/** - * Change the colour of a block, and optional secondary/teriarty colours. - * @param {number|string} colour HSV hue value, or #RRGGBB string. - * @param {number|string} colourSecondary HSV hue value, or #RRGGBB string. - * @param {number|string} colourTertiary HSV hue value, or #RRGGBB string. - * @param {number|string} colourQuaternary HSV hue value, or #RRGGBB string. - */ -Blockly.Block.prototype.setColour = function(colour, colourSecondary, colourTertiary, - colourQuaternary) { - this.colour_ = this.makeColour_(colour); - if (colourSecondary !== undefined) { - this.colourSecondary_ = this.makeColour_(colourSecondary); - } else { - this.colourSecondary_ = goog.color.rgbArrayToHex( - goog.color.darken(goog.color.hexToRgb(this.colour_), 0.1)); - } - if (colourTertiary !== undefined) { - this.colourTertiary_ = this.makeColour_(colourTertiary); - } else { - this.colourTertiary_ = goog.color.rgbArrayToHex( - goog.color.darken(goog.color.hexToRgb(this.colour_), 0.2)); - } - if (colourQuaternary !== undefined) { - this.colourQuaternary_ = this.makeColour_(colourQuaternary); - } else { - this.colourQuaternary_ = this.colourTertiary_; - } - if (this.rendered) { - this.updateColour(); - } -}; - -/** - * Sets a callback function to use whenever the block's parent workspace - * changes, replacing any prior onchange handler. This is usually only called - * from the constructor, the block type initializer function, or an extension - * initializer function. - * @param {function(Blockly.Events.Abstract)} onchangeFn The callback to call - * when the block's workspace changes. - * @throws {Error} if onchangeFn is not falsey or a function. - */ -Blockly.Block.prototype.setOnChange = function(onchangeFn) { - if (onchangeFn && !goog.isFunction(onchangeFn)) { - throw new Error("onchange must be a function."); - } - if (this.onchangeWrapper_) { - this.workspace.removeChangeListener(this.onchangeWrapper_); - } - this.onchange = onchangeFn; - if (this.onchange) { - this.onchangeWrapper_ = onchangeFn.bind(this); - this.workspace.addChangeListener(this.onchangeWrapper_); - } -}; - -/** - * Returns the named field from a block. - * @param {string} name The name of the field. - * @return {Blockly.Field} Named field, or null if field does not exist. - */ -Blockly.Block.prototype.getField = function(name) { - for (var i = 0, input; input = this.inputList[i]; i++) { - for (var j = 0, field; field = input.fieldRow[j]; j++) { - if (field.name === name) { - return field; - } - } - } - return null; -}; - -/** - * Return all variables referenced by this block. - * @return {!Array.} List of variable names. - * @package - */ -Blockly.Block.prototype.getVars = function() { - var vars = []; - for (var i = 0, input; input = this.inputList[i]; i++) { - for (var j = 0, field; field = input.fieldRow[j]; j++) { - if (field.referencesVariables()) { - vars.push(field.getValue()); - } - } - } - return vars; -}; - -/** - * Return all variables referenced by this block. - * @return {!Array.} List of variable models. - * @package - */ -Blockly.Block.prototype.getVarModels = function() { - var vars = []; - for (var i = 0, input; input = this.inputList[i]; i++) { - for (var j = 0, field; field = input.fieldRow[j]; j++) { - if (field.referencesVariables()) { - var model = this.workspace.getVariableById(field.getValue()); - // Check if the variable actually exists (and isn't just a potential - // variable). - if (model) { - vars.push(model); - } - } - } - } - return vars; -}; - -/** - * 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. - * @package - */ -Blockly.Block.prototype.updateVarName = function(variable) { - for (var i = 0, input; input = this.inputList[i]; i++) { - for (var j = 0, field; field = input.fieldRow[j]; j++) { - if (field.referencesVariables() && - variable.getId() == field.getValue()) { - field.setText(variable.name); - } - } - } -}; - -/** - * Notification that a variable is renaming. - * If the ID matches one of this block's variables, rename it. - * @param {string} oldId ID of variable to rename. - * @param {string} newId ID of new variable. May be the same as oldId, but with - * an updated name. - */ -Blockly.Block.prototype.renameVarById = function(oldId, newId) { - for (var i = 0, input; input = this.inputList[i]; i++) { - for (var j = 0, field; field = input.fieldRow[j]; j++) { - if (field.referencesVariables() && - oldId == field.getValue()) { - field.setValue(newId); - } - } - } -}; - -/** - * Returns the language-neutral value from the field of a block. - * @param {string} name The name of the field. - * @return {?string} Value from the field or null if field does not exist. - */ -Blockly.Block.prototype.getFieldValue = function(name) { - var field = this.getField(name); - if (field) { - return field.getValue(); - } - return null; -}; - -/** - * Change the field value for a block (e.g. 'CHOOSE' or 'REMOVE'). - * @param {string} newValue Value to be the new field. - * @param {string} name The name of the field. - */ -Blockly.Block.prototype.setFieldValue = function(newValue, name) { - var field = this.getField(name); - goog.asserts.assertObject(field, 'Field "%s" not found.', name); - field.setValue(newValue); -}; - -/** - * Set whether this block can chain onto the bottom of another block. - * @param {boolean} newBoolean True if there can be a previous statement. - * @param {(string|Array.|null)=} opt_check Statement type or - * list of statement types. Null/undefined if any type could be connected. - */ -Blockly.Block.prototype.setPreviousStatement = function(newBoolean, opt_check) { - if (newBoolean) { - if (opt_check === undefined) { - opt_check = null; - } - if (!this.previousConnection) { - goog.asserts.assert(!this.outputConnection, - 'Remove output connection prior to adding previous connection.'); - this.previousConnection = - this.makeConnection_(Blockly.PREVIOUS_STATEMENT); - } - this.previousConnection.setCheck(opt_check); - } else { - if (this.previousConnection) { - goog.asserts.assert(!this.previousConnection.isConnected(), - 'Must disconnect previous statement before removing connection.'); - this.previousConnection.dispose(); - this.previousConnection = null; - } - } -}; - -/** - * Set whether another block can chain onto the bottom of this block. - * @param {boolean} newBoolean True if there can be a next statement. - * @param {(string|Array.|null)=} opt_check Statement type or - * list of statement types. Null/undefined if any type could be connected. - */ -Blockly.Block.prototype.setNextStatement = function(newBoolean, opt_check) { - if (newBoolean) { - if (opt_check === undefined) { - opt_check = null; - } - if (!this.nextConnection) { - this.nextConnection = this.makeConnection_(Blockly.NEXT_STATEMENT); - } - this.nextConnection.setCheck(opt_check); - } else { - if (this.nextConnection) { - goog.asserts.assert(!this.nextConnection.isConnected(), - 'Must disconnect next statement before removing connection.'); - this.nextConnection.dispose(); - this.nextConnection = null; - } - } -}; - -/** - * Set whether this block returns a value. - * @param {boolean} newBoolean True if there is an output. - * @param {(string|Array.|null)=} opt_check Returned type or list - * of returned types. Null or undefined if any type could be returned - * (e.g. variable get). - */ -Blockly.Block.prototype.setOutput = function(newBoolean, opt_check) { - if (newBoolean) { - if (opt_check === undefined) { - opt_check = null; - } - if (!this.outputConnection) { - goog.asserts.assert(!this.previousConnection, - 'Remove previous connection prior to adding output connection.'); - this.outputConnection = this.makeConnection_(Blockly.OUTPUT_VALUE); - } - this.outputConnection.setCheck(opt_check); - } else { - if (this.outputConnection) { - goog.asserts.assert(!this.outputConnection.isConnected(), - 'Must disconnect output value before removing connection.'); - this.outputConnection.dispose(); - this.outputConnection = null; - } - } -}; - -/** - * Set whether value inputs are arranged horizontally or vertically. - * @param {boolean} newBoolean True if inputs are horizontal. - */ -Blockly.Block.prototype.setInputsInline = function(newBoolean) { - if (this.inputsInline != newBoolean) { - Blockly.Events.fire(new Blockly.Events.BlockChange( - this, 'inline', null, this.inputsInline, newBoolean)); - this.inputsInline = newBoolean; - } -}; - -/** - * Get whether value inputs are arranged horizontally or vertically. - * @return {boolean} True if inputs are horizontal. - */ -Blockly.Block.prototype.getInputsInline = function() { - if (this.inputsInline != undefined) { - // Set explicitly. - return this.inputsInline; - } - // Not defined explicitly. Figure out what would look best. - for (var i = 1; i < this.inputList.length; i++) { - if (this.inputList[i - 1].type == Blockly.DUMMY_INPUT && - this.inputList[i].type == Blockly.DUMMY_INPUT) { - // Two dummy inputs in a row. Don't inline them. - return false; - } - } - for (var i = 1; i < this.inputList.length; i++) { - if (this.inputList[i - 1].type == Blockly.INPUT_VALUE && - this.inputList[i].type == Blockly.DUMMY_INPUT) { - // Dummy input after a value input. Inline them. - return true; - } - } - return false; -}; - -/** - * Set whether the block is disabled or not. - * @param {boolean} disabled True if disabled. - */ -Blockly.Block.prototype.setDisabled = function(disabled) { - if (this.disabled != disabled) { - Blockly.Events.fire(new Blockly.Events.BlockChange( - this, 'disabled', null, this.disabled, disabled)); - this.disabled = disabled; - } -}; - -/** - * Get whether the block is disabled or not due to parents. - * The block's own disabled property is not considered. - * @return {boolean} True if disabled. - */ -Blockly.Block.prototype.getInheritedDisabled = function() { - var ancestor = this.getSurroundParent(); - while (ancestor) { - if (ancestor.disabled) { - return true; - } - ancestor = ancestor.getSurroundParent(); - } - // Ran off the top. - return false; -}; - -/** - * Get whether the block is collapsed or not. - * @return {boolean} True if collapsed. - */ -Blockly.Block.prototype.isCollapsed = function() { - return this.collapsed_; -}; - -/** - * Set whether the block is collapsed or not. - * @param {boolean} collapsed True if collapsed. - */ -Blockly.Block.prototype.setCollapsed = function(collapsed) { - if (this.collapsed_ != collapsed) { - Blockly.Events.fire(new Blockly.Events.BlockChange( - this, 'collapsed', null, this.collapsed_, collapsed)); - this.collapsed_ = collapsed; - } -}; - -/** - * Create a human-readable text representation of this block and any children. - * @param {number=} opt_maxLength Truncate the string to this length. - * @param {string=} opt_emptyToken The placeholder string used to denote an - * empty field. If not specified, '?' is used. - * @return {string} Text of block. - */ -Blockly.Block.prototype.toString = function(opt_maxLength, opt_emptyToken) { - var text = []; - var emptyFieldPlaceholder = opt_emptyToken || '?'; - if (this.collapsed_) { - text.push(this.getInput('_TEMP_COLLAPSED_INPUT').fieldRow[0].text_); - } else { - for (var i = 0, input; input = this.inputList[i]; i++) { - for (var j = 0, field; field = input.fieldRow[j]; j++) { - if (field instanceof Blockly.FieldDropdown && !field.getValue()) { - text.push(emptyFieldPlaceholder); - } else { - text.push(field.getText()); - } - } - if (input.connection) { - var child = input.connection.targetBlock(); - if (child) { - text.push(child.toString(undefined, opt_emptyToken)); - } else { - text.push(emptyFieldPlaceholder); - } - } - } - } - text = goog.string.trim(text.join(' ')) || '???'; - if (opt_maxLength) { - // TODO: Improve truncation so that text from this block is given priority. - // E.g. "1+2+3+4+5+6+7+8+9=0" should be "...6+7+8+9=0", not "1+2+3+4+5...". - // E.g. "1+2+3+4+5=6+7+8+9+0" should be "...4+5=6+7...". - text = goog.string.truncate(text, opt_maxLength); - } - return text; -}; - -/** - * Shortcut for appending a value input row. - * @param {string} name Language-neutral identifier which may used to find this - * input again. Should be unique to this block. - * @return {!Blockly.Input} The input object created. - */ -Blockly.Block.prototype.appendValueInput = function(name) { - return this.appendInput_(Blockly.INPUT_VALUE, name); -}; - -/** - * Shortcut for appending a statement input row. - * @param {string} name Language-neutral identifier which may used to find this - * input again. Should be unique to this block. - * @return {!Blockly.Input} The input object created. - */ -Blockly.Block.prototype.appendStatementInput = function(name) { - return this.appendInput_(Blockly.NEXT_STATEMENT, name); -}; - -/** - * Shortcut for appending a dummy input row. - * @param {string=} opt_name Language-neutral identifier which may used to find - * this input again. Should be unique to this block. - * @return {!Blockly.Input} The input object created. - */ -Blockly.Block.prototype.appendDummyInput = function(opt_name) { - return this.appendInput_(Blockly.DUMMY_INPUT, opt_name || ''); -}; - -/** - * Initialize this block using a cross-platform, internationalization-friendly - * JSON description. - * @param {!Object} json Structured data describing the block. - */ -Blockly.Block.prototype.jsonInit = function(json) { - var warningPrefix = json['type'] ? 'Block "' + json['type'] + '": ' : ''; - - // Validate inputs. - goog.asserts.assert( - json['output'] == undefined || json['previousStatement'] == undefined, - warningPrefix + 'Must not have both an output and a previousStatement.'); - - // Set basic properties of block. - if (json['colour'] !== undefined) { - this.setColourFromJson_(json); - } - - // Interpolate the message blocks. - var i = 0; - while (json['message' + i] !== undefined) { - this.interpolate_(json['message' + i], json['args' + i] || [], - json['lastDummyAlign' + i]); - i++; - } - - if (json['inputsInline'] !== undefined) { - this.setInputsInline(json['inputsInline']); - } - // Set output and previous/next connections. - if (json['output'] !== undefined) { - this.setOutput(true, json['output']); - } - if (json['previousStatement'] !== undefined) { - this.setPreviousStatement(true, json['previousStatement']); - } - if (json['nextStatement'] !== undefined) { - this.setNextStatement(true, json['nextStatement']); - } - if (json['tooltip'] !== undefined) { - var rawValue = json['tooltip']; - var localizedText = Blockly.utils.replaceMessageReferences(rawValue); - this.setTooltip(localizedText); - } - if (json['enableContextMenu'] !== undefined) { - var rawValue = json['enableContextMenu']; - this.contextMenu = !!rawValue; - } - if (json['helpUrl'] !== undefined) { - var rawValue = json['helpUrl']; - var localizedValue = Blockly.utils.replaceMessageReferences(rawValue); - this.setHelpUrl(localizedValue); - } - if (goog.isString(json['extensions'])) { - console.warn('JSON attribute \'extensions\' should be an array of ' + - 'strings. Found raw string in JSON for \'' + json['type'] + '\' block.'); - json['extensions'] = [json['extensions']]; // Correct and continue. - } - - // Add the mutator to the block - if (json['mutator'] !== undefined) { - Blockly.Extensions.apply(json['mutator'], this, true); - } - - if (Array.isArray(json['extensions'])) { - var extensionNames = json['extensions']; - for (var i = 0; i < extensionNames.length; ++i) { - var extensionName = extensionNames[i]; - Blockly.Extensions.apply(extensionName, this, false); - } - } - if (json['outputShape'] !== undefined) { - this.setOutputShape(json['outputShape']); - } - if (json['checkboxInFlyout'] !== undefined) { - this.setCheckboxInFlyout(json['checkboxInFlyout']); - } - if (json['category'] !== undefined) { - this.setCategory(json['category']); - } -}; - -/** - * Add key/values from mixinObj to this block object. By default, this method - * will check that the keys in mixinObj will not overwrite existing values in - * the block, including prototype values. This provides some insurance against - * mixin / extension incompatibilities with future block features. This check - * can be disabled by passing true as the second argument. - * @param {!Object} mixinObj The key/values pairs to add to this block object. - * @param {boolean=} opt_disableCheck Option flag to disable overwrite checks. - */ -Blockly.Block.prototype.mixin = function(mixinObj, opt_disableCheck) { - if (goog.isDef(opt_disableCheck) && !goog.isBoolean(opt_disableCheck)) { - throw new Error("opt_disableCheck must be a boolean if provided"); - } - if (!opt_disableCheck) { - var overwrites = []; - for (var key in mixinObj) { - if (this[key] !== undefined) { - overwrites.push(key); - } - } - if (overwrites.length) { - throw new Error('Mixin will overwrite block members: ' + - JSON.stringify(overwrites)); - } - } - goog.mixin(this, mixinObj); -}; - -/** - * Set the colour of the block from strings or string table references. - * @param {string|?} primary Primary colour, which may be a string that contains - * string table references. - * @param {string|?} secondary Secondary colour, which may be a string that - * contains string table references. - * @param {string|?} tertiary Tertiary colour, which may be a string that - * contains string table references. - * @param {string|?} quaternary Quaternary colour, which may be a string that - * contains string table references. - * @private - */ -Blockly.Block.prototype.setColourFromRawValues_ = function(primary, secondary, - tertiary, quaternary) { - primary = goog.isString(primary) ? - Blockly.utils.replaceMessageReferences(primary) : primary; - secondary = goog.isString(secondary) ? - Blockly.utils.replaceMessageReferences(secondary) : secondary; - tertiary = goog.isString(tertiary) ? - Blockly.utils.replaceMessageReferences(tertiary) : tertiary; - quaternary = goog.isString(quaternary) ? - Blockly.utils.replaceMessageReferences(quaternary) : quaternary; - - this.setColour(primary, secondary, tertiary, quaternary); -}; - -/** - * Set the colour of the block from JSON, replacing message references as - * needed. - * @param {!Object} json Structured data describing the block. - * @private - */ -Blockly.Block.prototype.setColourFromJson_ = function(json) { - this.setColourFromRawValues_(json['colour'], json['colourSecondary'], - json['colourTertiary'], json['colourQuaternary']); -}; - -/** - * Interpolate a message description onto the block. - * @param {string} message Text contains interpolation tokens (%1, %2, ...) - * that match with fields or inputs defined in the args array. - * @param {!Array} args Array of arguments to be interpolated. - * @param {string=} lastDummyAlign If a dummy input is added at the end, - * how should it be aligned? - * @private - */ -Blockly.Block.prototype.interpolate_ = function(message, args, lastDummyAlign) { - var tokens = Blockly.utils.tokenizeInterpolation(message); - // Interpolate the arguments. Build a list of elements. - var indexDup = []; - var indexCount = 0; - var elements = []; - for (var i = 0; i < tokens.length; i++) { - var token = tokens[i]; - if (typeof token == 'number') { - if (token <= 0 || token > args.length) { - throw new Error('Block "' + this.type + '": ' + - 'Message index %' + token + ' out of range.'); - } - if (indexDup[token]) { - throw new Error('Block "' + this.type + '": ' + - 'Message index %' + token + ' duplicated.'); - } - indexDup[token] = true; - indexCount++; - elements.push(args[token - 1]); - } else { - token = token.trim(); - if (token) { - elements.push(token); - } - } - } - if (indexCount != args.length) { - throw new Error('Block "' + this.type + '": ' + - 'Message does not reference all ' + args.length + ' arg(s).'); - } - // Add last dummy input if needed. - if (elements.length && (typeof elements[elements.length - 1] == 'string' || - goog.string.startsWith( - elements[elements.length - 1]['type'], 'field_'))) { - var dummyInput = {type: 'input_dummy'}; - if (lastDummyAlign) { - dummyInput['align'] = lastDummyAlign; - } - elements.push(dummyInput); - } - // Lookup of alignment constants. - var alignmentLookup = { - 'LEFT': Blockly.ALIGN_LEFT, - 'RIGHT': Blockly.ALIGN_RIGHT, - 'CENTRE': Blockly.ALIGN_CENTRE - }; - // Populate block with inputs and fields. - var fieldStack = []; - for (var i = 0; i < elements.length; i++) { - var element = elements[i]; - if (typeof element == 'string') { - fieldStack.push([element, undefined]); - } else { - var field = null; - var input = null; - do { - var altRepeat = false; - if (typeof element == 'string') { - field = new Blockly.FieldLabel(element); - } else { - switch (element['type']) { - case 'input_value': - input = this.appendValueInput(element['name']); - break; - case 'input_statement': - input = this.appendStatementInput(element['name']); - break; - case 'input_dummy': - input = this.appendDummyInput(element['name']); - break; - default: - field = Blockly.Field.fromJson(element); - - // Unknown field. - if (!field) { - if (element['alt']) { - element = element['alt']; - altRepeat = true; - } else { - console.warn('Blockly could not create a field of type ' + - element['type'] + - '. You may need to register your custom field. See ' + - 'github.com/google/blockly/issues/1584'); - } - } - } - } - } while (altRepeat); - if (field) { - fieldStack.push([field, element['name']]); - } else if (input) { - if (element['check']) { - input.setCheck(element['check']); - } - if (element['align']) { - input.setAlign(alignmentLookup[element['align']]); - } - for (var j = 0; j < fieldStack.length; j++) { - input.appendField(fieldStack[j][0], fieldStack[j][1]); - } - fieldStack.length = 0; - } - } - } -}; - -/** - * Add a value input, statement input or local variable to this block. - * @param {number} type Either Blockly.INPUT_VALUE or Blockly.NEXT_STATEMENT or - * Blockly.DUMMY_INPUT. - * @param {string} name Language-neutral identifier which may used to find this - * input again. Should be unique to this block. - * @return {!Blockly.Input} The input object created. - * @protected - */ -Blockly.Block.prototype.appendInput_ = function(type, name) { - var connection = null; - if (type == Blockly.INPUT_VALUE || type == Blockly.NEXT_STATEMENT) { - connection = this.makeConnection_(type); - } - var input = new Blockly.Input(type, name, this, connection); - // Append input to list. - this.inputList.push(input); - return input; -}; - -/** - * Move a named input to a different location on this block. - * @param {string} name The name of the input to move. - * @param {?string} refName Name of input that should be after the moved input, - * or null to be the input at the end. - */ -Blockly.Block.prototype.moveInputBefore = function(name, refName) { - if (name == refName) { - return; - } - // Find both inputs. - var inputIndex = -1; - var refIndex = refName ? -1 : this.inputList.length; - for (var i = 0, input; input = this.inputList[i]; i++) { - if (input.name == name) { - inputIndex = i; - if (refIndex != -1) { - break; - } - } else if (refName && input.name == refName) { - refIndex = i; - if (inputIndex != -1) { - break; - } - } - } - goog.asserts.assert(inputIndex != -1, 'Named input "%s" not found.', name); - goog.asserts.assert( - refIndex != -1, 'Reference input "%s" not found.', refName); - this.moveNumberedInputBefore(inputIndex, refIndex); -}; - -/** - * Move a numbered input to a different location on this block. - * @param {number} inputIndex Index of the input to move. - * @param {number} refIndex Index of input that should be after the moved input. - */ -Blockly.Block.prototype.moveNumberedInputBefore = function( - inputIndex, refIndex) { - // Validate arguments. - goog.asserts.assert(inputIndex != refIndex, 'Can\'t move input to itself.'); - goog.asserts.assert(inputIndex < this.inputList.length, - 'Input index ' + inputIndex + ' out of bounds.'); - goog.asserts.assert(refIndex <= this.inputList.length, - 'Reference input ' + refIndex + ' out of bounds.'); - // Remove input. - var input = this.inputList[inputIndex]; - this.inputList.splice(inputIndex, 1); - if (inputIndex < refIndex) { - refIndex--; - } - // Reinsert input. - this.inputList.splice(refIndex, 0, input); -}; - -/** - * Remove an input from this block. - * @param {string} name The name of the input. - * @param {boolean=} opt_quiet True to prevent error if input is not present. - * @throws {goog.asserts.AssertionError} if the input is not present and - * opt_quiet is not true. - */ -Blockly.Block.prototype.removeInput = function(name, opt_quiet) { - for (var i = 0, input; input = this.inputList[i]; i++) { - if (input.name == name) { - if (input.connection && input.connection.isConnected()) { - input.connection.setShadowDom(null); - var block = input.connection.targetBlock(); - if (block.isShadow()) { - // Destroy any attached shadow block. - block.dispose(); - } else { - // Disconnect any attached normal block. - block.unplug(); - } - } - input.dispose(); - this.inputList.splice(i, 1); - return; - } - } - if (!opt_quiet) { - goog.asserts.fail('Input "%s" not found.', name); - } -}; - -/** - * Fetches the named input object. - * @param {string} name The name of the input. - * @return {Blockly.Input} The input object, or null if input does not exist. - */ -Blockly.Block.prototype.getInput = function(name) { - for (var i = 0, input; input = this.inputList[i]; i++) { - if (input.name == name) { - return input; - } - } - // This input does not exist. - return null; -}; - -/** - * Fetches the block attached to the named input. - * @param {string} name The name of the input. - * @return {Blockly.Block} The attached value block, or null if the input is - * either disconnected or if the input does not exist. - */ -Blockly.Block.prototype.getInputTargetBlock = function(name) { - var input = this.getInput(name); - return input && input.connection && input.connection.targetBlock(); -}; - -/** - * Returns the comment on this block (or '' if none). - * @return {string} Block's comment. - */ -Blockly.Block.prototype.getCommentText = function() { - return this.comment || ''; -}; - -/** - * Set this block's comment text. - * @param {?string} text The text, or null to delete. - */ -Blockly.Block.prototype.setCommentText = function(text) { - if (this.comment != text) { - Blockly.Events.fire(new Blockly.Events.BlockChange( - this, 'comment', null, this.comment, text || '')); - this.comment = text; - } -}; - -/** - * Set this block's output shape. - * e.g., null, OUTPUT_SHAPE_HEXAGONAL, OUTPUT_SHAPE_ROUND, OUTPUT_SHAPE_SQUARE. - * @param {?number} outputShape Value representing output shape - * (see constants.js). - */ -Blockly.Block.prototype.setOutputShape = function(outputShape) { - this.outputShape_ = outputShape; -}; - -/** - * Get this block's output shape. - * @return {?number} Value representing output shape (see constants.js). - */ -Blockly.Block.prototype.getOutputShape = function() { - return this.outputShape_; -}; - -/** - * Set this block's category (for styling purposes) - * @param {?string} category The block's category (see constants.js). - */ -Blockly.Block.prototype.setCategory = function(category) { - this.category_ = category; -}; - -/** - * Get this block's category (for styling purposes) - * @return {?string} category The block's category (see constants.js). - */ -Blockly.Block.prototype.getCategory = function() { - return this.category_; -}; - -/** - * Set whether this block has a checkbox next to it in the flyout. - * @param {boolean} hasCheckbox True if this block should have a checkbox. - */ -Blockly.Block.prototype.setCheckboxInFlyout = function(hasCheckbox) { - this.checkboxInFlyout_ = hasCheckbox; -}; - -/** - * Get whether this block has a checkbox next to it in the flyout. - * @return {boolean} True if this block should have a checkbox. - */ -Blockly.Block.prototype.hasCheckboxInFlyout = function() { - return this.checkboxInFlyout_; -}; - -/** - * Set this block's warning text. - * @param {?string} text The text, or null to delete. - * @abstract - */ -Blockly.Block.prototype.setWarningText = function(/* text */) { - // NOP. -}; - -/** - * Give this block a mutator dialog. - * @param {Blockly.Mutator} mutator A mutator dialog instance or null to remove. - * @abstract - */ -Blockly.Block.prototype.setMutator = function(/* mutator */) { - // NOP. -}; - -/** - * Return the coordinates of the top-left corner of this block relative to the - * drawing surface's origin (0,0), in workspace units. - * @return {!goog.math.Coordinate} Object with .x and .y properties. - */ -Blockly.Block.prototype.getRelativeToSurfaceXY = function() { - return this.xy_; -}; - -/** - * Move a block by a relative offset. - * @param {number} dx Horizontal offset, in workspace units. - * @param {number} dy Vertical offset, in workspace units. - */ -Blockly.Block.prototype.moveBy = function(dx, dy) { - goog.asserts.assert(!this.parentBlock_, 'Block has parent.'); - var event = new Blockly.Events.BlockMove(this); - this.xy_.translate(dx, dy); - event.recordNew(); - Blockly.Events.fire(event); -}; - -/** - * Create a connection of the specified type. - * @param {number} type The type of the connection to create. - * @return {!Blockly.Connection} A new connection of the specified type. - * @private - */ -Blockly.Block.prototype.makeConnection_ = function(type) { - return new Blockly.Connection(this, type); -}; - -/** - * Recursively checks whether all statement and value inputs are filled with - * blocks. Also checks all following statement blocks in this stack. - * @param {boolean=} opt_shadowBlocksAreFilled An optional argument controlling - * whether shadow blocks are counted as filled. Defaults to true. - * @return {boolean} True if all inputs are filled, false otherwise. - */ -Blockly.Block.prototype.allInputsFilled = function(opt_shadowBlocksAreFilled) { - // Account for the shadow block filledness toggle. - if (opt_shadowBlocksAreFilled === undefined) { - opt_shadowBlocksAreFilled = true; - } - if (!opt_shadowBlocksAreFilled && this.isShadow()) { - return false; - } - - // Recursively check each input block of the current block. - for (var i = 0, input; input = this.inputList[i]; i++) { - if (!input.connection) { - continue; - } - var target = input.connection.targetBlock(); - if (!target || !target.allInputsFilled(opt_shadowBlocksAreFilled)) { - return false; - } - } - - // Recursively check the next block after the current block. - var next = this.getNextBlock(); - if (next) { - return next.allInputsFilled(opt_shadowBlocksAreFilled); - } - - return true; -}; - -/** - * This method returns a string describing this Block in developer terms (type - * name and ID; English only). - * - * Intended to on be used in console logs and errors. If you need a string that - * uses the user's native language (including block text, field values, and - * child blocks), use [toString()]{@link Blockly.Block#toString}. - * @return {string} The description. - */ -Blockly.Block.prototype.toDevString = function() { - var msg = this.type ? '"' + this.type + '" block' : 'Block'; - if (this.id) { - msg += ' (id="' + this.id + '")'; - } - return msg; -}; diff --git a/core/block_animations.js b/core/block_animations.js deleted file mode 100644 index 1e708c08f5..0000000000 --- a/core/block_animations.js +++ /dev/null @@ -1,107 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2018 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Methods animating a block on connection and disconnection. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.BlockAnimations'); - - -/** - * Play some UI effects (sound, animation) when disposing of a block. - * @param {!Blockly.BlockSvg} block The block being disposed of. - * @package - */ -Blockly.BlockAnimations.disposeUiEffect = function(block) { - var workspace = block.workspace; - var svgGroup = block.getSvgRoot(); - workspace.getAudioManager().play('delete'); - - var xy = workspace.getSvgXY(svgGroup); - // Deeply clone the current block. - var clone = svgGroup.cloneNode(true); - clone.translateX_ = xy.x; - clone.translateY_ = xy.y; - clone.setAttribute('transform', 'translate(' + xy.x + ',' + xy.y + ')'); - workspace.getParentSvg().appendChild(clone); - clone.bBox_ = clone.getBBox(); - // Start the animation. - Blockly.BlockAnimations.disposeUiStep_(clone, workspace.RTL, new Date, - workspace.scale); -}; - -/** - * Animate a cloned block and eventually dispose of it. - * This is a class method, not an instance method since the original block has - * been destroyed and is no longer accessible. - * @param {!Element} clone SVG element to animate and dispose of. - * @param {boolean} rtl True if RTL, false if LTR. - * @param {!Date} start Date of animation's start. - * @param {number} workspaceScale Scale of workspace. - * @private - */ -Blockly.BlockAnimations.disposeUiStep_ = function(clone, rtl, start, - workspaceScale) { - var ms = new Date - start; - var percent = ms / 150; - if (percent > 1) { - goog.dom.removeNode(clone); - } else { - var x = clone.translateX_ + - (rtl ? -1 : 1) * clone.bBox_.width * workspaceScale / 2 * percent; - var y = clone.translateY_ + clone.bBox_.height * workspaceScale * percent; - var scale = (1 - percent) * workspaceScale; - clone.setAttribute('transform', 'translate(' + x + ',' + y + ')' + - ' scale(' + scale + ')'); - setTimeout(Blockly.BlockAnimations.disposeUiStep_, 10, clone, rtl, start, - workspaceScale); - } -}; - -/** - * Play some UI effects (sound, ripple) after a connection has been established. - * @param {!Blockly.BlockSvg} block The block being connected. - * @package - */ -Blockly.BlockAnimations.connectionUiEffect = function(block) { - block.workspace.getAudioManager().play('click'); -}; - -/** - * Play some UI effects (sound, animation) when disconnecting a block. - * No-op in scratch-blocks, which has no disconnect animation. - * @param {!Blockly.BlockSvg} _block The block being disconnected. - * @package - */ -Blockly.BlockAnimations.disconnectUiEffect = function( - /* eslint-disable no-unused-vars */ _block - /* eslint-enable no-unused-vars */) { -}; - -/** - * Stop the disconnect UI animation immediately. - * No-op in scratch-blocks, which has no disconnect animation. - * @package - */ -Blockly.BlockAnimations.disconnectUiStop = function() { -}; diff --git a/core/block_drag_surface.js b/core/block_drag_surface.js deleted file mode 100644 index d5ad047140..0000000000 --- a/core/block_drag_surface.js +++ /dev/null @@ -1,299 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview A class that manages a surface for dragging blocks. When a - * block drag is started, we move the block (and children) to a separate DOM - * element that we move around using translate3d. At the end of the drag, the - * blocks are put back in into the SVG they came from. This helps performance by - * avoiding repainting the entire SVG on every mouse move while dragging blocks. - * @author picklesrus - */ - -'use strict'; - -goog.provide('Blockly.BlockDragSurfaceSvg'); -goog.require('Blockly.utils'); -goog.require('goog.asserts'); -goog.require('goog.math.Coordinate'); - - -/** - * Class for a drag surface for the currently dragged block. This is a separate - * SVG that contains only the currently moving block, or nothing. - * @param {!Element} container Containing element. - * @constructor - */ -Blockly.BlockDragSurfaceSvg = function(container) { - /** - * @type {!Element} - * @private - */ - this.container_ = container; - this.createDom(); -}; - -/** - * The SVG drag surface. Set once by Blockly.BlockDragSurfaceSvg.createDom. - * @type {Element} - * @private - */ -Blockly.BlockDragSurfaceSvg.prototype.SVG_ = null; - -/** - * This is where blocks live while they are being dragged if the drag surface - * is enabled. - * @type {Element} - * @private - */ -Blockly.BlockDragSurfaceSvg.prototype.dragGroup_ = null; - -/** - * Containing HTML element; parent of the workspace and the drag surface. - * @type {Element} - * @private - */ -Blockly.BlockDragSurfaceSvg.prototype.container_ = null; - -/** - * Cached value for the scale of the drag surface. - * Used to set/get the correct translation during and after a drag. - * @type {number} - * @private - */ -Blockly.BlockDragSurfaceSvg.prototype.scale_ = 1; - -/** - * Cached value for the translation of the drag surface. - * This translation is in pixel units, because the scale is applied to the - * drag group rather than the top-level SVG. - * @type {goog.math.Coordinate} - * @private - */ -Blockly.BlockDragSurfaceSvg.prototype.surfaceXY_ = null; - -/** - * ID for the drag shadow filter, set in createDom. - * Belongs in Scratch Blocks but not Blockly. - * @type {string} - * @private - */ -Blockly.BlockDragSurfaceSvg.prototype.dragShadowFilterId_ = ''; - -/** - * Standard deviation for gaussian blur on drag shadow, in px. - * Belongs in Scratch Blocks but not Blockly. - * @type {number} - * @const - */ -Blockly.BlockDragSurfaceSvg.SHADOW_STD_DEVIATION = 6; - -/** - * Create the drag surface and inject it into the container. - */ -Blockly.BlockDragSurfaceSvg.prototype.createDom = function() { - if (this.SVG_) { - return; // Already created. - } - this.SVG_ = Blockly.utils.createSvgElement('svg', - { - 'xmlns': Blockly.SVG_NS, - 'xmlns:html': Blockly.HTML_NS, - 'xmlns:xlink': 'http://www.w3.org/1999/xlink', - 'version': '1.1', - 'class': 'blocklyBlockDragSurface' - }, this.container_); - this.dragGroup_ = Blockly.utils.createSvgElement('g', {}, this.SVG_); - // Belongs in Scratch Blocks, but not Blockly. - var defs = Blockly.utils.createSvgElement('defs', {}, this.SVG_); - this.dragShadowFilterId_ = this.createDropShadowDom_(defs); - this.dragGroup_.setAttribute( - 'filter', 'url(#' + this.dragShadowFilterId_ + ')'); -}; - -/** - * Scratch-specific: Create the SVG def for the drop shadow. - * @param {Element} defs Defs element to insert the shadow filter definition - * @return {string} ID for the filter element - * @private - */ -Blockly.BlockDragSurfaceSvg.prototype.createDropShadowDom_ = function(defs) { - var rnd = String(Math.random()).substring(2); - // Adjust these width/height, x/y properties to stop the shadow from clipping - var dragShadowFilter = Blockly.utils.createSvgElement('filter', - { - 'id': 'blocklyDragShadowFilter' + rnd, - 'height': '140%', - 'width': '140%', - 'y': '-20%', - 'x': '-20%' - }, - defs); - Blockly.utils.createSvgElement('feGaussianBlur', - { - 'in': 'SourceAlpha', - 'stdDeviation': Blockly.BlockDragSurfaceSvg.SHADOW_STD_DEVIATION - }, - dragShadowFilter); - var componentTransfer = Blockly.utils.createSvgElement( - 'feComponentTransfer', {'result': 'offsetBlur'}, dragShadowFilter); - // Shadow opacity is specified in the adjustable colour library, - // since the darkness of the shadow largely depends on the workspace colour. - Blockly.utils.createSvgElement('feFuncA', - { - 'type': 'linear', - 'slope': Blockly.Colours.dragShadowOpacity - }, - componentTransfer); - Blockly.utils.createSvgElement('feComposite', - { - 'in': 'SourceGraphic', - 'in2': 'offsetBlur', - 'operator': 'over' - }, - dragShadowFilter); - return dragShadowFilter.id; -}; - -/** - * Set the SVG blocks on the drag surface's group and show the surface. - * Only one block group should be on the drag surface at a time. - * @param {!Element} blocks Block or group of blocks to place on the drag - * surface. - */ -Blockly.BlockDragSurfaceSvg.prototype.setBlocksAndShow = function(blocks) { - goog.asserts.assert( - this.dragGroup_.childNodes.length == 0, 'Already dragging a block.'); - // appendChild removes the blocks from the previous parent - this.dragGroup_.appendChild(blocks); - this.SVG_.style.display = 'block'; - this.surfaceXY_ = new goog.math.Coordinate(0, 0); - // This allows blocks to be dragged outside of the blockly svg space. - // This should be reset to hidden at the end of the block drag. - // Note that this behavior is different from blockly where block disappear - // "under" the blockly area. - var injectionDiv = document.getElementsByClassName('injectionDiv')[0]; - injectionDiv.style.overflow = 'visible'; -}; - -/** - * Translate and scale the entire drag surface group to the given position, to - * keep in sync with the workspace. - * @param {number} x X translation in workspace coordinates. - * @param {number} y Y translation in workspace coordinates. - * @param {number} scale Scale of the group. - */ -Blockly.BlockDragSurfaceSvg.prototype.translateAndScaleGroup = function(x, y, scale) { - this.scale_ = scale; - // This is a work-around to prevent a the blocks from rendering - // fuzzy while they are being dragged on the drag surface. - var fixedX = x.toFixed(0); - var fixedY = y.toFixed(0); - this.dragGroup_.setAttribute('transform', - 'translate(' + fixedX + ',' + fixedY + ') scale(' + scale + ')'); -}; - -/** - * Translate the drag surface's SVG based on its internal state. - * @private - */ -Blockly.BlockDragSurfaceSvg.prototype.translateSurfaceInternal_ = function() { - var x = this.surfaceXY_.x; - var y = this.surfaceXY_.y; - // This is a work-around to prevent a the blocks from rendering - // fuzzy while they are being dragged on the drag surface. - x = x.toFixed(0); - y = y.toFixed(0); - this.SVG_.style.display = 'block'; - - Blockly.utils.setCssTransform(this.SVG_, - 'translate3d(' + x + 'px, ' + y + 'px, 0px)'); -}; - -/** - * Translate the entire drag surface during a drag. - * We translate the drag surface instead of the blocks inside the surface - * so that the browser avoids repainting the SVG. - * Because of this, the drag coordinates must be adjusted by scale. - * @param {number} x X translation for the entire surface. - * @param {number} y Y translation for the entire surface. - */ -Blockly.BlockDragSurfaceSvg.prototype.translateSurface = function(x, y) { - this.surfaceXY_ = new goog.math.Coordinate(x * this.scale_, y * this.scale_); - this.translateSurfaceInternal_(); -}; - -/** - * Reports the surface translation in scaled workspace coordinates. - * Use this when finishing a drag to return blocks to the correct position. - * @return {!goog.math.Coordinate} Current translation of the surface. - */ -Blockly.BlockDragSurfaceSvg.prototype.getSurfaceTranslation = function() { - var xy = Blockly.utils.getRelativeXY(this.SVG_); - return new goog.math.Coordinate(xy.x / this.scale_, xy.y / this.scale_); -}; - -/** - * Provide a reference to the drag group (primarily for - * BlockSvg.getRelativeToSurfaceXY). - * @return {Element} Drag surface group element. - */ -Blockly.BlockDragSurfaceSvg.prototype.getGroup = function() { - return this.dragGroup_; -}; - -/** - * Get the current blocks on the drag surface, if any (primarily - * for BlockSvg.getRelativeToSurfaceXY). - * @return {!Element|undefined} Drag surface block DOM element, or undefined - * if no blocks exist. - */ -Blockly.BlockDragSurfaceSvg.prototype.getCurrentBlock = function() { - return this.dragGroup_.firstChild; -}; - -/** - * Clear the group and hide the surface; move the blocks off onto the provided - * element. - * If the block is being deleted it doesn't need to go back to the original - * surface, since it would be removed immediately during dispose. - * @param {Element=} opt_newSurface Surface the dragging blocks should be moved - * to, or null if the blocks should be removed from this surface without - * being moved to a different surface. - */ -Blockly.BlockDragSurfaceSvg.prototype.clearAndHide = function(opt_newSurface) { - if (opt_newSurface) { - // appendChild removes the node from this.dragGroup_ - opt_newSurface.appendChild(this.getCurrentBlock()); - } else { - this.dragGroup_.removeChild(this.getCurrentBlock()); - } - this.SVG_.style.display = 'none'; - goog.asserts.assert( - this.dragGroup_.childNodes.length == 0, 'Drag group was not cleared.'); - this.surfaceXY_ = null; - - // Reset the overflow property back to hidden so that nothing appears outside - // of the blockly area. - // Note that this behavior is different from blockly. See note in - // setBlocksAndShow. - var injectionDiv = document.getElementsByClassName('injectionDiv')[0]; - injectionDiv.style.overflow = 'hidden'; -}; diff --git a/core/block_dragger.js b/core/block_dragger.js deleted file mode 100644 index 80591d1a72..0000000000 --- a/core/block_dragger.js +++ /dev/null @@ -1,421 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Methods for dragging a block visually. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.BlockDragger'); - -goog.require('Blockly.BlockAnimations'); -goog.require('Blockly.Events.BlockMove'); -goog.require('Blockly.Events.DragBlockOutside'); -goog.require('Blockly.Events.EndBlockDrag'); -goog.require('Blockly.InsertionMarkerManager'); - -goog.require('goog.math.Coordinate'); -goog.require('goog.asserts'); - - -/** - * Class for a block dragger. It moves blocks around the workspace when they - * are being dragged by a mouse or touch. - * @param {!Blockly.BlockSvg} block The block to drag. - * @param {!Blockly.WorkspaceSvg} workspace The workspace to drag on. - * @constructor - */ -Blockly.BlockDragger = function(block, workspace) { - /** - * The top block in the stack that is being dragged. - * @type {!Blockly.BlockSvg} - * @private - */ - this.draggingBlock_ = block; - - /** - * The workspace on which the block is being dragged. - * @type {!Blockly.WorkspaceSvg} - * @private - */ - this.workspace_ = workspace; - - /** - * Object that keeps track of connections on dragged blocks. - * @type {!Blockly.InsertionMarkerManager} - * @private - */ - this.draggedConnectionManager_ = new Blockly.InsertionMarkerManager( - this.draggingBlock_); - - /** - * Which delete area the mouse pointer is over, if any. - * One of {@link Blockly.DELETE_AREA_TRASH}, - * {@link Blockly.DELETE_AREA_TOOLBOX}, or {@link Blockly.DELETE_AREA_NONE}. - * @type {?number} - * @private - */ - this.deleteArea_ = null; - - /** - * Whether the block would be deleted if dropped immediately. - * @type {boolean} - * @private - */ - this.wouldDeleteBlock_ = false; - - /** - * Whether the currently dragged block is outside of the workspace. Keep - * track so that we can fire events only when this changes. - * @type {boolean} - * @private - */ - this.wasOutside_ = false; - - /** - * The location of the top left corner of the dragging block at the beginning - * of the drag in workspace coordinates. - * @type {!goog.math.Coordinate} - * @private - */ - this.startXY_ = this.draggingBlock_.getRelativeToSurfaceXY(); - - /** - * A list of all of the icons (comment, warning, and mutator) that are - * on this block and its descendants. Moving an icon moves the bubble that - * extends from it if that bubble is open. - * @type {Array.} - * @private - */ - this.dragIconData_ = Blockly.BlockDragger.initIconData_(block); -}; - -/** - * Sever all links from this object. - * @package - */ -Blockly.BlockDragger.prototype.dispose = function() { - this.draggingBlock_ = null; - this.workspace_ = null; - this.startWorkspace_ = null; - this.dragIconData_.length = 0; - - if (this.draggedConnectionManager_) { - this.draggedConnectionManager_.dispose(); - this.draggedConnectionManager_ = null; - } -}; - -/** - * Make a list of all of the icons (comment, warning, and mutator) that are - * on this block and its descendants. Moving an icon moves the bubble that - * extends from it if that bubble is open. - * @param {!Blockly.BlockSvg} block The root block that is being dragged. - * @return {!Array.} The list of all icons and their locations. - * @private - */ -Blockly.BlockDragger.initIconData_ = function(block) { - // Build a list of icons that need to be moved and where they started. - var dragIconData = []; - var descendants = block.getDescendants(false); - for (var i = 0, descendant; descendant = descendants[i]; i++) { - var icons = descendant.getIcons(); - for (var j = 0; j < icons.length; j++) { - var data = { - // goog.math.Coordinate with x and y properties (workspace coordinates). - location: icons[j].getIconLocation(), - // Blockly.Icon - icon: icons[j] - }; - dragIconData.push(data); - } - } - return dragIconData; -}; - -/** - * Start dragging a block. This includes moving it to the drag surface. - * @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has - * moved from the position at mouse down, in pixel units. - * @package - */ -Blockly.BlockDragger.prototype.startBlockDrag = function(currentDragDeltaXY) { - if (!Blockly.Events.getGroup()) { - Blockly.Events.setGroup(true); - } - - this.workspace_.setResizesEnabled(false); - Blockly.BlockAnimations.disconnectUiStop(); - - if (this.draggingBlock_.getParent()) { - this.draggingBlock_.unplug(); - var delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY); - var newLoc = goog.math.Coordinate.sum(this.startXY_, delta); - - this.draggingBlock_.translate(newLoc.x, newLoc.y); - Blockly.BlockAnimations.disconnectUiEffect(this.draggingBlock_); - } - this.draggingBlock_.setDragging(true); - // For future consideration: we may be able to put moveToDragSurface inside - // the block dragger, which would also let the block not track the block drag - // surface. - this.draggingBlock_.moveToDragSurface_(); - - var toolbox = this.workspace_.getToolbox(); - if (toolbox) { - var style = this.draggingBlock_.isDeletable() ? 'blocklyToolboxDelete' : - 'blocklyToolboxGrab'; - toolbox.addStyle(style); - } -}; - -/** - * Execute a step of block dragging, based on the given event. Update the - * display accordingly. - * @param {!Event} e The most recent move event. - * @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has - * moved from the position at the start of the drag, in pixel units. - * @package - * @return {boolean} True if the event should be propagated, false if not. - */ -Blockly.BlockDragger.prototype.dragBlock = function(e, currentDragDeltaXY) { - var delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY); - var newLoc = goog.math.Coordinate.sum(this.startXY_, delta); - - this.draggingBlock_.moveDuringDrag(newLoc); - this.dragIcons_(delta); - - this.deleteArea_ = this.workspace_.isDeleteArea(e); - var isOutside = !this.workspace_.isInsideBlocksArea(e); - this.draggedConnectionManager_.update(delta, this.deleteArea_, isOutside); - if (isOutside !== this.wasOutside_) { - this.fireDragOutsideEvent_(isOutside); - this.wasOutside_ = isOutside; - } - - this.updateCursorDuringBlockDrag_(isOutside); - return isOutside; -}; - -/** - * Finish a block drag and put the block back on the workspace. - * @param {!Event} e The mouseup/touchend event. - * @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has - * moved from the position at the start of the drag, in pixel units. - * @package - */ -Blockly.BlockDragger.prototype.endBlockDrag = function(e, currentDragDeltaXY) { - // Make sure internal state is fresh. - this.dragBlock(e, currentDragDeltaXY); - this.dragIconData_ = []; - var isOutside = this.wasOutside_; - this.fireEndDragEvent_(isOutside); - this.draggingBlock_.setMouseThroughStyle(false); - - Blockly.BlockAnimations.disconnectUiStop(); - - var delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY); - var newLoc = goog.math.Coordinate.sum(this.startXY_, delta); - this.draggingBlock_.moveOffDragSurface_(newLoc); - - // Scratch-specific: note possible illegal definition deletion for rollback below. - var isDeletingProcDef = this.wouldDeleteBlock_ && - (this.draggingBlock_.type == Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE); - - var deleted = this.maybeDeleteBlock_(); - if (!deleted) { - // These are expensive and don't need to be done if we're deleting. - this.draggingBlock_.moveConnections_(delta.x, delta.y); - this.draggingBlock_.setDragging(false); - this.fireMoveEvent_(); - if (this.draggedConnectionManager_.wouldConnectBlock()) { - // Applying connections also rerenders the relevant blocks. - this.draggedConnectionManager_.applyConnections(); - } else { - this.draggingBlock_.render(); - } - this.draggingBlock_.scheduleSnapAndBump(); - } - this.workspace_.setResizesEnabled(true); - - var toolbox = this.workspace_.getToolbox(); - if (toolbox) { - var style = this.draggingBlock_.isDeletable() ? 'blocklyToolboxDelete' : - 'blocklyToolboxGrab'; - toolbox.removeStyle(style); - } - Blockly.Events.setGroup(false); - - if (isOutside) { - var ws = this.workspace_; - // Reset a drag to outside of scratch-blocks - setTimeout(function() { - ws.undo(); - }); - } - - // Scratch-specific: roll back deletes that create call blocks with defines. - // Have to wait for connections to be re-established, so put in setTimeout. - // Only do this if we deleted a proc def. - if (isDeletingProcDef) { - var ws = this.workspace_; - setTimeout(function() { - var allBlocks = ws.getAllBlocks(); - for (var i = 0; i < allBlocks.length; i++) { - var block = allBlocks[i]; - if (block.type == Blockly.PROCEDURES_CALL_BLOCK_TYPE) { - var procCode = block.getProcCode(); - // Check for call blocks with no associated define block. - if (!Blockly.Procedures.getDefineBlock(procCode, ws)) { - alert(Blockly.Msg.PROCEDURE_USED); - ws.undo(); - return; // There can only be one define deletion at a time. - } - } - } - // The proc deletion was valid, update the toolbox. - ws.refreshToolboxSelection_(); - }); - } -}; - -/** - * Fire an event when the dragged blocks move outside or back into the blocks workspace - * @param {?boolean} isOutside True if the drag is going outside the visible area. - * @private - */ -Blockly.BlockDragger.prototype.fireDragOutsideEvent_ = function(isOutside) { - var event = new Blockly.Events.DragBlockOutside(this.draggingBlock_); - event.isOutside = isOutside; - Blockly.Events.fire(event); -}; - -/** - * Fire an end drag event at the end of a block drag. - * @param {?boolean} isOutside True if the drag is going outside the visible area. - * @private - */ -Blockly.BlockDragger.prototype.fireEndDragEvent_ = function(isOutside) { - var event = new Blockly.Events.EndBlockDrag(this.draggingBlock_, isOutside); - Blockly.Events.fire(event); -}; - -/** - * Fire a move event at the end of a block drag. - * @private - */ -Blockly.BlockDragger.prototype.fireMoveEvent_ = function() { - var event = new Blockly.Events.BlockMove(this.draggingBlock_); - event.oldCoordinate = this.startXY_; - event.recordNew(); - Blockly.Events.fire(event); -}; - -/** - * Shut the trash can and, if necessary, delete the dragging block. - * Should be called at the end of a block drag. - * @return {boolean} whether the block was deleted. - * @private - */ -Blockly.BlockDragger.prototype.maybeDeleteBlock_ = function() { - var trashcan = this.workspace_.trashcan; - - if (this.wouldDeleteBlock_) { - if (trashcan) { - goog.Timer.callOnce(trashcan.close, 100, trashcan); - } - // Fire a move event, so we know where to go back to for an undo. - this.fireMoveEvent_(); - this.draggingBlock_.dispose(false, true); - } else if (trashcan) { - // Make sure the trash can is closed. - trashcan.close(); - } - return this.wouldDeleteBlock_; -}; - -/** - * Update the cursor (and possibly the trash can lid) to reflect whether the - * dragging block would be deleted if released immediately. - * @param {boolean} isOutside True if the cursor is outside of the blocks workspace - * @private - */ -Blockly.BlockDragger.prototype.updateCursorDuringBlockDrag_ = function(isOutside) { - this.wouldDeleteBlock_ = this.draggedConnectionManager_.wouldDeleteBlock(); - var trashcan = this.workspace_.trashcan; - if (this.wouldDeleteBlock_) { - this.draggingBlock_.setDeleteStyle(true); - if (this.deleteArea_ == Blockly.DELETE_AREA_TRASH && trashcan) { - trashcan.setOpen_(true); - } - } else { - this.draggingBlock_.setDeleteStyle(false); - if (trashcan) { - trashcan.setOpen_(false); - } - } - - if (isOutside) { - // Let mouse events through to GUI - this.draggingBlock_.setMouseThroughStyle(true); - } else { - this.draggingBlock_.setMouseThroughStyle(false); - } -}; - -/** - * Convert a coordinate object from pixels to workspace units, including a - * correction for mutator workspaces. - * This function does not consider differing origins. It simply scales the - * input's x and y values. - * @param {!goog.math.Coordinate} pixelCoord A coordinate with x and y values - * in css pixel units. - * @return {!goog.math.Coordinate} The input coordinate divided by the workspace - * scale. - * @private - */ -Blockly.BlockDragger.prototype.pixelsToWorkspaceUnits_ = function(pixelCoord) { - var result = new goog.math.Coordinate(pixelCoord.x / this.workspace_.scale, - pixelCoord.y / this.workspace_.scale); - if (this.workspace_.isMutator) { - // If we're in a mutator, its scale is always 1, purely because of some - // oddities in our rendering optimizations. The actual scale is the same as - // the scale on the parent workspace. - // Fix that for dragging. - var mainScale = this.workspace_.options.parentWorkspace.scale; - result = result.scale(1 / mainScale); - } - return result; -}; - -/** - * Move all of the icons connected to this drag. - * @param {!goog.math.Coordinate} dxy How far to move the icons from their - * original positions, in workspace units. - * @private - */ -Blockly.BlockDragger.prototype.dragIcons_ = function(dxy) { - // Moving icons moves their associated bubbles. - for (var i = 0; i < this.dragIconData_.length; i++) { - var data = this.dragIconData_[i]; - data.icon.setIconLocation(goog.math.Coordinate.sum(data.location, dxy)); - } -}; diff --git a/core/block_events.js b/core/block_events.js deleted file mode 100644 index 681b8903e4..0000000000 --- a/core/block_events.js +++ /dev/null @@ -1,531 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2018 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Classes for all types of block events. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.Events.BlockBase'); -goog.provide('Blockly.Events.BlockChange'); -goog.provide('Blockly.Events.BlockCreate'); -goog.provide('Blockly.Events.BlockDelete'); -goog.provide('Blockly.Events.BlockMove'); -goog.provide('Blockly.Events.Change'); // Deprecated. -goog.provide('Blockly.Events.Create'); // Deprecated. -goog.provide('Blockly.Events.Delete'); // Deprecated. -goog.provide('Blockly.Events.Move'); // Deprecated. - -goog.require('Blockly.Events'); -goog.require('Blockly.Events.Abstract'); - -goog.require('goog.array'); -goog.require('goog.math.Coordinate'); - - -/** - * Abstract class for a block event. - * @param {Blockly.Block} block The block this event corresponds to. - * @extends {Blockly.Events.Abstract} - * @constructor - */ -Blockly.Events.BlockBase = function(block) { - Blockly.Events.BlockBase.superClass_.constructor.call(this); - - /** - * The block id for the block this event pertains to - * @type {string} - */ - this.blockId = block.id; - this.workspaceId = block.workspace.id; -}; -goog.inherits(Blockly.Events.BlockBase, Blockly.Events.Abstract); - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.BlockBase.prototype.toJson = function() { - var json = Blockly.Events.BlockBase.superClass_.toJson.call(this); - json['blockId'] = this.blockId; - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.BlockBase.prototype.fromJson = function(json) { - Blockly.Events.BlockBase.superClass_.toJson.call(this); - this.blockId = json['blockId']; -}; - -/** - * Class for a block change event. - * @param {Blockly.Block} block The changed block. Null for a blank event. - * @param {string} element One of 'field', 'comment', 'disabled', etc. - * @param {?string} name Name of input or field affected, or null. - * @param {*} oldValue Previous value of element. - * @param {*} newValue New value of element. - * @extends {Blockly.Events.BlockBase} - * @constructor - */ -Blockly.Events.Change = function(block, element, name, oldValue, newValue) { - if (!block) { - return; // Blank event to be populated by fromJson. - } - Blockly.Events.Change.superClass_.constructor.call(this, block); - this.element = element; - this.name = name; - this.oldValue = oldValue; - this.newValue = newValue; -}; -goog.inherits(Blockly.Events.Change, Blockly.Events.BlockBase); - -/** - * Class for a block change event. - * @param {Blockly.Block} block The changed block. Null for a blank event. - * @param {string} element One of 'field', 'comment', 'disabled', etc. - * @param {?string} name Name of input or field affected, or null. - * @param {*} oldValue Previous value of element. - * @param {*} newValue New value of element. - * @extends {Blockly.Events.BlockBase} - * @constructor - */ -Blockly.Events.BlockChange = Blockly.Events.Change; - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.Change.prototype.type = Blockly.Events.CHANGE; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.Change.prototype.toJson = function() { - var json = Blockly.Events.Change.superClass_.toJson.call(this); - json['element'] = this.element; - if (this.name) { - json['name'] = this.name; - } - json['newValue'] = this.newValue; - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.Change.prototype.fromJson = function(json) { - Blockly.Events.Change.superClass_.fromJson.call(this, json); - this.element = json['element']; - this.name = json['name']; - this.newValue = json['newValue']; -}; - -/** - * Does this event record any change of state? - * @return {boolean} False if something changed. - */ -Blockly.Events.Change.prototype.isNull = function() { - return this.oldValue == this.newValue; -}; - -/** - * Run a change event. - * @param {boolean} forward True if run forward, false if run backward (undo). - */ -Blockly.Events.Change.prototype.run = function(forward) { - var workspace = this.getEventWorkspace_(); - var block = workspace.getBlockById(this.blockId); - if (!block) { - console.warn("Can't change non-existent block: " + this.blockId); - return; - } - if (block.mutator) { - // Close the mutator (if open) since we don't want to update it. - block.mutator.setVisible(false); - } - var value = forward ? this.newValue : this.oldValue; - switch (this.element) { - case 'field': - var field = block.getField(this.name); - if (field) { - // Run the validator for any side-effects it may have. - // The validator's opinion on validity is ignored. - field.callValidator(value); - field.setValue(value); - } else { - console.warn("Can't set non-existent field: " + this.name); - } - break; - case 'comment': - block.setCommentText(value || null); - break; - case 'collapsed': - block.setCollapsed(value); - break; - case 'disabled': - block.setDisabled(value); - break; - case 'inline': - block.setInputsInline(value); - break; - case 'mutation': - var oldMutation = ''; - if (block.mutationToDom) { - var oldMutationDom = block.mutationToDom(); - oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom); - } - if (block.domToMutation) { - value = value || ''; - var dom = Blockly.Xml.textToDom('' + value + ''); - block.domToMutation(dom.firstChild); - } - Blockly.Events.fire(new Blockly.Events.Change( - block, 'mutation', null, oldMutation, value)); - break; - default: - console.warn('Unknown change type: ' + this.element); - } -}; - -/** - * Class for a block creation event. - * @param {Blockly.Block} block The created block. Null for a blank event. - * @extends {Blockly.Events.BlockBase} - * @constructor - */ -Blockly.Events.Create = function(block) { - if (!block) { - return; // Blank event to be populated by fromJson. - } - Blockly.Events.Create.superClass_.constructor.call(this, block); - - if (block.workspace.rendered) { - this.xml = Blockly.Xml.blockToDomWithXY(block); - } else { - this.xml = Blockly.Xml.blockToDom(block); - } - this.ids = Blockly.Events.getDescendantIds_(block); -}; -goog.inherits(Blockly.Events.Create, Blockly.Events.BlockBase); - -/** - * Class for a block creation event. - * @param {Blockly.Block} block The created block. Null for a blank event. - * @extends {Blockly.Events.BlockBase} - * @constructor - */ -Blockly.Events.BlockCreate = Blockly.Events.Create; - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.Create.prototype.type = Blockly.Events.CREATE; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.Create.prototype.toJson = function() { - var json = Blockly.Events.Create.superClass_.toJson.call(this); - json['xml'] = Blockly.Xml.domToText(this.xml); - json['ids'] = this.ids; - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.Create.prototype.fromJson = function(json) { - Blockly.Events.Create.superClass_.fromJson.call(this, json); - this.xml = Blockly.Xml.textToDom('' + json['xml'] + '').firstChild; - this.ids = json['ids']; -}; - -/** - * Run a creation event. - * @param {boolean} forward True if run forward, false if run backward (undo). - */ -Blockly.Events.Create.prototype.run = function(forward) { - var workspace = this.getEventWorkspace_(); - if (forward) { - var xml = goog.dom.createDom('xml'); - xml.appendChild(this.xml); - Blockly.Xml.domToWorkspace(xml, workspace); - } else { - for (var i = 0, id; id = this.ids[i]; i++) { - var block = workspace.getBlockById(id); - if (block) { - block.dispose(false, false); - } else if (id == this.blockId) { - // Only complain about root-level block. - console.warn("Can't uncreate non-existent block: " + id); - } - } - } -}; - -/** - * Class for a block deletion event. - * @param {Blockly.Block} block The deleted block. Null for a blank event. - * @extends {Blockly.Events.BlockBase} - * @constructor - */ -Blockly.Events.Delete = function(block) { - if (!block) { - return; // Blank event to be populated by fromJson. - } - if (block.getParent()) { - throw 'Connected blocks cannot be deleted.'; - } - Blockly.Events.Delete.superClass_.constructor.call(this, block); - - if (block.workspace.rendered) { - this.oldXml = Blockly.Xml.blockToDomWithXY(block); - } else { - this.oldXml = Blockly.Xml.blockToDom(block); - } - this.ids = Blockly.Events.getDescendantIds_(block); -}; -goog.inherits(Blockly.Events.Delete, Blockly.Events.BlockBase); - -/** - * Class for a block deletion event. - * @param {Blockly.Block} block The deleted block. Null for a blank event. - * @extends {Blockly.Events.BlockBase} - * @constructor - */ -Blockly.Events.BlockDelete = Blockly.Events.Delete; - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.Delete.prototype.type = Blockly.Events.DELETE; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.Delete.prototype.toJson = function() { - var json = Blockly.Events.Delete.superClass_.toJson.call(this); - json['ids'] = this.ids; - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.Delete.prototype.fromJson = function(json) { - Blockly.Events.Delete.superClass_.fromJson.call(this, json); - this.ids = json['ids']; -}; - -/** - * Run a deletion event. - * @param {boolean} forward True if run forward, false if run backward (undo). - */ -Blockly.Events.Delete.prototype.run = function(forward) { - var workspace = this.getEventWorkspace_(); - if (forward) { - for (var i = 0, id; id = this.ids[i]; i++) { - var block = workspace.getBlockById(id); - if (block) { - block.dispose(false, false); - } else if (id == this.blockId) { - // Only complain about root-level block. - console.warn("Can't delete non-existent block: " + id); - } - } - } else { - var xml = goog.dom.createDom('xml'); - xml.appendChild(this.oldXml); - Blockly.Xml.domToWorkspace(xml, workspace); - } -}; - -/** - * Class for a block move event. Created before the move. - * @param {Blockly.Block} block The moved block. Null for a blank event. - * @extends {Blockly.Events.BlockBase} - * @constructor - */ -Blockly.Events.Move = function(block) { - if (!block) { - return; // Blank event to be populated by fromJson. - } - Blockly.Events.Move.superClass_.constructor.call(this, block); - var location = this.currentLocation_(); - this.oldParentId = location.parentId; - this.oldInputName = location.inputName; - this.oldCoordinate = location.coordinate; -}; -goog.inherits(Blockly.Events.Move, Blockly.Events.BlockBase); - -/** - * Class for a block move event. Created before the move. - * @param {Blockly.Block} block The moved block. Null for a blank event. - * @extends {Blockly.Events.BlockBase} - * @constructor - */ -Blockly.Events.BlockMove = Blockly.Events.Move; - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.Move.prototype.type = Blockly.Events.MOVE; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.Move.prototype.toJson = function() { - var json = Blockly.Events.Move.superClass_.toJson.call(this); - if (this.newParentId) { - json['newParentId'] = this.newParentId; - } - if (this.newInputName) { - json['newInputName'] = this.newInputName; - } - if (this.newCoordinate) { - json['newCoordinate'] = Math.round(this.newCoordinate.x) + ',' + - Math.round(this.newCoordinate.y); - } - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.Move.prototype.fromJson = function(json) { - Blockly.Events.Move.superClass_.fromJson.call(this, json); - this.newParentId = json['newParentId']; - this.newInputName = json['newInputName']; - if (json['newCoordinate']) { - var xy = json['newCoordinate'].split(','); - this.newCoordinate = - new goog.math.Coordinate(parseFloat(xy[0]), parseFloat(xy[1])); - } -}; - -/** - * Record the block's new location. Called after the move. - */ -Blockly.Events.Move.prototype.recordNew = function() { - var location = this.currentLocation_(); - this.newParentId = location.parentId; - this.newInputName = location.inputName; - this.newCoordinate = location.coordinate; -}; - -/** - * Returns the parentId and input if the block is connected, - * or the XY location if disconnected. - * @return {!Object} Collection of location info. - * @private - */ -Blockly.Events.Move.prototype.currentLocation_ = function() { - var workspace = Blockly.Workspace.getById(this.workspaceId); - var block = workspace.getBlockById(this.blockId); - var location = {}; - var parent = block.getParent(); - if (parent) { - location.parentId = parent.id; - var input = parent.getInputWithBlock(block); - if (input) { - location.inputName = input.name; - } - } else { - var blockXY = block.getRelativeToSurfaceXY(); - // The X position in the block move event should be the language agnostic - // position of the block. I.e. it should not be different in LTR vs. RTL. - var rtlAwareX = workspace.RTL ? workspace.getWidth() - blockXY.x : blockXY.x; - location.coordinate = new goog.math.Coordinate(rtlAwareX, blockXY.y); - } - return location; -}; - -/** - * Does this event record any change of state? - * @return {boolean} False if something changed. - */ -Blockly.Events.Move.prototype.isNull = function() { - return this.oldParentId == this.newParentId && - this.oldInputName == this.newInputName && - goog.math.Coordinate.equals(this.oldCoordinate, this.newCoordinate); -}; - -/** - * Run a move event. - * @param {boolean} forward True if run forward, false if run backward (undo). - */ -Blockly.Events.Move.prototype.run = function(forward) { - var workspace = this.getEventWorkspace_(); - var block = workspace.getBlockById(this.blockId); - if (!block) { - console.warn("Can't move non-existent block: " + this.blockId); - return; - } - var parentId = forward ? this.newParentId : this.oldParentId; - var inputName = forward ? this.newInputName : this.oldInputName; - var coordinate = forward ? this.newCoordinate : this.oldCoordinate; - var parentBlock = null; - if (parentId) { - parentBlock = workspace.getBlockById(parentId); - if (!parentBlock) { - console.warn("Can't connect to non-existent block: " + parentId); - return; - } - } - if (block.getParent()) { - block.unplug(); - } - if (coordinate) { - var xy = block.getRelativeToSurfaceXY(); - var rtlAwareX = workspace.RTL ? workspace.getWidth() - coordinate.x : coordinate.x; - block.moveBy(rtlAwareX - xy.x, coordinate.y - xy.y); - } else { - var blockConnection = block.outputConnection || block.previousConnection; - var parentConnection; - if (inputName) { - var input = parentBlock.getInput(inputName); - if (input) { - parentConnection = input.connection; - } - } else if (blockConnection.type == Blockly.PREVIOUS_STATEMENT) { - parentConnection = parentBlock.nextConnection; - } - if (parentConnection) { - blockConnection.connect(parentConnection); - } else { - console.warn("Can't connect to non-existent input: " + inputName); - } - } -}; diff --git a/core/block_render_svg_horizontal.js b/core/block_render_svg_horizontal.js deleted file mode 100644 index c1c4ecce3f..0000000000 --- a/core/block_render_svg_horizontal.js +++ /dev/null @@ -1,890 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Methods for graphically rendering a block as SVG. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.BlockSvg.render'); - -goog.require('Blockly.BlockSvg'); - - -// UI constants for rendering blocks. -/** -* Grid unit to pixels conversion -* @const -*/ -Blockly.BlockSvg.GRID_UNIT = 4; - -/** - * Horizontal space between elements. - * @const - */ -Blockly.BlockSvg.SEP_SPACE_X = 3 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Vertical space between elements. - * @const - */ -Blockly.BlockSvg.SEP_SPACE_Y = 3 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Vertical space above blocks with statements. - * @const - */ -Blockly.BlockSvg.STATEMENT_BLOCK_SPACE = 3 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Height of user inputs - * @const - */ -Blockly.BlockSvg.FIELD_HEIGHT = 8 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Width of user inputs - * @const - */ -Blockly.BlockSvg.FIELD_WIDTH = 12 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Editable field padding (left/right of the text). - * @const - */ -Blockly.BlockSvg.EDITABLE_FIELD_PADDING = 0; - -/** - * Minimum width of user inputs during editing - * @const - */ -Blockly.BlockSvg.FIELD_WIDTH_MIN_EDIT = 13 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Maximum width of user inputs during editing - * @const - */ -Blockly.BlockSvg.FIELD_WIDTH_MAX_EDIT = 24 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Maximum height of user inputs during editing - * @const - */ -Blockly.BlockSvg.FIELD_HEIGHT_MAX_EDIT = 10 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Top padding of user inputs - * @const - */ -Blockly.BlockSvg.FIELD_TOP_PADDING = 0.25 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Corner radius of number inputs - * @const - */ -Blockly.BlockSvg.NUMBER_FIELD_CORNER_RADIUS = 4 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Corner radius of text inputs - * @const - */ -Blockly.BlockSvg.TEXT_FIELD_CORNER_RADIUS = 1 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Default radius for a field, in px. - * @const - */ -Blockly.BlockSvg.FIELD_DEFAULT_CORNER_RADIUS = 4 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Minimum width of a block. - * @const - */ -Blockly.BlockSvg.MIN_BLOCK_X = 1 / 2 * 16 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Minimum height of a block. - * @const - */ -Blockly.BlockSvg.MIN_BLOCK_Y = 16 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Width of horizontal puzzle tab. - * @const - */ -Blockly.BlockSvg.TAB_WIDTH = 2 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Rounded corner radius. - * @const - */ -Blockly.BlockSvg.CORNER_RADIUS = 1 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Rounded corner radius. - * @const - */ -Blockly.BlockSvg.HAT_CORNER_RADIUS = 8 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Full height of connector notch including rounded corner. - * @const - */ -Blockly.BlockSvg.NOTCH_HEIGHT = 8 * Blockly.BlockSvg.GRID_UNIT + 2; - -/** - * Width of connector notch - * @const - */ -Blockly.BlockSvg.NOTCH_WIDTH = 2 * Blockly.BlockSvg.GRID_UNIT; - -/** - * SVG path for drawing next/previous notch from top to bottom. - * Drawn in pixel units since Bezier control points are off the grid. - * @const - */ -Blockly.BlockSvg.NOTCH_PATH_DOWN = - 'c 0,2 1,3 2,4 ' + - 'l 4,4 ' + - 'c 1,1 2,2 2,4 ' + - 'v 12 ' + - 'c 0,2 -1,3 -2,4 ' + - 'l -4,4 ' + - 'c -1,1 -2,2 -2,4'; - -/** - * SVG path for drawing next/previous notch from bottom to top. - * Drawn in pixel units since Bezier control points are off the grid. - * @const - */ -Blockly.BlockSvg.NOTCH_PATH_UP = - 'c 0,-2 1,-3 2,-4 ' + - 'l 4,-4 ' + - 'c 1,-1 2,-2 2,-4 ' + - 'v -12 ' + - 'c 0,-2 -1,-3 -2,-4 ' + - 'l -4,-4 ' + - 'c -1,-1 -2,-2 -2,-4'; - -/** -* Width of rendered image field in px -* @const -*/ -Blockly.BlockSvg.IMAGE_FIELD_WIDTH = 10 * Blockly.BlockSvg.GRID_UNIT; - -/** -* Height of rendered image field in px -* @const -*/ -Blockly.BlockSvg.IMAGE_FIELD_HEIGHT = 10 * Blockly.BlockSvg.GRID_UNIT; - -/** -* y-offset of the top of the field shadow block from the bottom of the block. -* @const -*/ -Blockly.BlockSvg.FIELD_Y_OFFSET = -2 * Blockly.BlockSvg.GRID_UNIT; - -/** - * SVG start point for drawing the top-left corner. - * @const - */ -Blockly.BlockSvg.TOP_LEFT_CORNER_START = - 'm ' + Blockly.BlockSvg.CORNER_RADIUS + ',0'; - -/** - * SVG path for drawing the rounded top-left corner. - * @const - */ -Blockly.BlockSvg.TOP_LEFT_CORNER = - 'A ' + Blockly.BlockSvg.CORNER_RADIUS + ',' + - Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 ' + - '0,' + Blockly.BlockSvg.CORNER_RADIUS; - -/** - * SVG start point for drawing the top-left corner. - * @const - */ -Blockly.BlockSvg.HAT_TOP_LEFT_CORNER_START = - 'm ' + Blockly.BlockSvg.HAT_CORNER_RADIUS + ',0'; -/** - * SVG path for drawing the rounded top-left corner. - * @const - */ -Blockly.BlockSvg.HAT_TOP_LEFT_CORNER = - 'A ' + Blockly.BlockSvg.HAT_CORNER_RADIUS + ',' + - Blockly.BlockSvg.HAT_CORNER_RADIUS + ' 0 0,0 ' + - '0,' + Blockly.BlockSvg.HAT_CORNER_RADIUS; - -/** - * @type {Object} An object containing computed measurements of this block. - * @private - */ -Blockly.BlockSvg.renderingMetrics_ = null; - -/** - * Max text display length for a field (per-horizontal/vertical) - * @const - */ -Blockly.BlockSvg.MAX_DISPLAY_LENGTH = 4; - -/** - * Point size of text field before animation. Must match size in CSS. - * See implementation in field_textinput. - */ -Blockly.BlockSvg.FIELD_TEXTINPUT_FONTSIZE_INITIAL = 12; - -/** - * Point size of text field after animation. - * See implementation in field_textinput. - */ -Blockly.BlockSvg.FIELD_TEXTINPUT_FONTSIZE_FINAL = 14; - -/** - * Whether text fields are allowed to expand past their truncated block size. - * @const{boolean} - */ -Blockly.BlockSvg.FIELD_TEXTINPUT_EXPAND_PAST_TRUNCATION = true; - -/** - * Whether text fields should animate their positioning. - * @const{boolean} - */ -Blockly.BlockSvg.FIELD_TEXTINPUT_ANIMATE_POSITIONING = true; - -/** - * @param {!Object} first An object containing computed measurements of a - * block. - * @param {!Object} second Another object containing computed measurements of a - * block. - * @return {boolean} Whether the two sets of metrics are equivalent. - * @private - */ -Blockly.BlockSvg.metricsAreEquivalent_ = function(first, second) { - if (first.statement != second.statement) { - return false; - } - if (first.imageField != second.imageField) { - return false; - } - - if ((first.height != second.height) || - (first.width != second.width) || - (first.bayHeight != second.bayHeight) || - (first.bayWidth != second.bayWidth) || - (first.fieldRadius != second.fieldRadius) || - (first.startHat != second.startHat)) { - return false; - } - return true; -}; - -/** - * Play some UI effects (sound) after a connection has been established. - */ -Blockly.BlockSvg.prototype.connectionUiEffect = function() { - this.workspace.getAudioManager().play('click'); -}; - -/** - * Change the colour of a block. - */ -Blockly.BlockSvg.prototype.updateColour = function() { - var fillColour = (this.isGlowing_) ? this.getColourSecondary() : this.getColour(); - var strokeColour = this.getColourTertiary(); - - // Render block stroke - this.svgPath_.setAttribute('stroke', strokeColour); - - // Render block fill - var fillColour = (this.isGlowingBlock_) ? this.getColourSecondary() : this.getColour(); - this.svgPath_.setAttribute('fill', fillColour); - - // Render opacity - this.svgPath_.setAttribute('fill-opacity', this.getOpacity()); - - // Bump every dropdown to change its colour. - for (var x = 0, input; input = this.inputList[x]; x++) { - for (var y = 0, field; field = input.fieldRow[y]; y++) { - field.setText(null); - } - } -}; - -/** - * Visual effect to show that if the dragging block is dropped, this block will - * be replaced. If a shadow block it will disappear. Otherwise it will bump. - * @param {boolean} add True if highlighting should be added. - */ -Blockly.BlockSvg.prototype.highlightForReplacement = function(add) { - if (add) { - var replacementGlowFilterId = this.workspace.options.replacementGlowFilterId - || 'blocklyReplacementGlowFilter'; - this.svgPath_.setAttribute('filter', 'url(#' + replacementGlowFilterId + ')'); - Blockly.utils.addClass(/** @type {!Element} */ (this.svgGroup_), - 'blocklyReplaceable'); - } else { - this.svgPath_.removeAttribute('filter'); - Blockly.utils.removeClass(/** @type {!Element} */ (this.svgGroup_), - 'blocklyReplaceable'); - } -}; - -/** - * Returns a bounding box describing the dimensions of this block - * and any blocks stacked below it. - * @param {boolean=} opt_ignoreFields True if we should ignore fields in the - * size calculation, and just give the size of the base block(s). - * @return {!{height: number, width: number}} Object with height and width properties. - */ -Blockly.BlockSvg.prototype.getHeightWidth = function(opt_ignoreFields) { - var height = this.height; - var width = this.width; - // Add the size of the field shadow block. - if (!opt_ignoreFields && this.getFieldShadowBlock_()) { - height += Blockly.BlockSvg.FIELD_Y_OFFSET; - height += Blockly.BlockSvg.FIELD_HEIGHT; - } - // Recursively add size of subsequent blocks. - var nextBlock = this.getNextBlock(); - if (nextBlock) { - var nextHeightWidth = nextBlock.getHeightWidth(opt_ignoreFields); - width += nextHeightWidth.width; - width -= Blockly.BlockSvg.NOTCH_WIDTH; // Exclude width of connected notch. - height = Math.max(height, nextHeightWidth.height); - } - return {height: height, width: width}; -}; - -/** - * Render the block. - * Lays out and reflows a block based on its contents and settings. - * @param {boolean=} opt_bubble If false, just render this block. - * If true, also render block's parent, grandparent, etc. Defaults to true. - */ -Blockly.BlockSvg.prototype.render = function(opt_bubble) { - Blockly.Field.startCache(); - this.rendered = true; - - var oldMetrics = this.renderingMetrics_; - var metrics = this.renderCompute_(); - - // Don't redraw if we don't need to. - if (oldMetrics && - Blockly.BlockSvg.metricsAreEquivalent_(oldMetrics, metrics)) { - // Skipping the redraw is fine, but we may still have to tighten up our - // connections with child blocks. - if (metrics.statement && metrics.statement.connection && - metrics.statement.targetConnection) { - metrics.statement.connection.tighten_(); - } - if (this.nextConnection && this.nextConnection.targetConnection) { - this.nextConnection.tighten_(); - } - } else { - this.height = metrics.height; - this.width = metrics.width; - this.renderDraw_(metrics); - this.renderClassify_(metrics); - this.renderingMetrics_ = metrics; - } - - if (opt_bubble !== false) { - // Render all blocks above this one (propagate a reflow). - var parentBlock = this.getParent(); - if (parentBlock) { - parentBlock.render(true); - } else { - // Top-most block. Fire an event to allow scrollbars to resize. - Blockly.resizeSvgContents(this.workspace); - } - } - Blockly.Field.stopCache(); -}; - -/** - * Computes the height and widths for each row and field. - * @return {!Array.>} 2D array of objects, each containing - * position information. - * @private - */ -Blockly.BlockSvg.prototype.renderCompute_ = function() { - var metrics = { - statement: null, - imageField: null, - iconMenu: null, - width: 0, - height: 0, - bayHeight: 0, - bayWidth: 0, - bayNotchAtRight: true, - fieldRadius: 0, - startHat: false, - endCap: false - }; - - // Does block have a statement? - for (var i = 0, input; input = this.inputList[i]; i++) { - if (input.type == Blockly.NEXT_STATEMENT) { - metrics.statement = input; - // Compute minimum input size. - metrics.bayHeight = Blockly.BlockSvg.MIN_BLOCK_Y; - metrics.bayWidth = Blockly.BlockSvg.MIN_BLOCK_X; - // Expand input size if there is a connection. - if (input.connection && input.connection.targetConnection) { - var linkedBlock = input.connection.targetBlock(); - var bBox = linkedBlock.getHeightWidth(true); - metrics.bayHeight = Math.max(metrics.bayHeight, bBox.height); - metrics.bayWidth = Math.max(metrics.bayWidth, bBox.width); - } - var linkedBlock = input.connection.targetBlock(); - if (linkedBlock && !linkedBlock.lastConnectionInStack()) { - metrics.bayNotchAtRight = false; - } else { - metrics.bayWidth -= Blockly.BlockSvg.NOTCH_WIDTH; - } - } - - // Find image field, input fields - for (var j = 0, field; field = input.fieldRow[j]; j++) { - if (field instanceof Blockly.FieldImage) { - metrics.imageField = field; - } - if (field instanceof Blockly.FieldIconMenu) { - metrics.iconMenu = field; - } - if (field instanceof Blockly.FieldTextInput) { - metrics.fieldRadius = field.getBorderRadius(); - } else { - metrics.fieldRadius = Blockly.BlockSvg.FIELD_DEFAULT_CORNER_RADIUS; - } - } - } - - // Determine whether a block is a start hat or end cap by checking connections. - if (this.nextConnection && !this.previousConnection) { - metrics.startHat = true; - } - - // End caps have no bay, a previous, no output, and no next. - if (!this.nextConnection && this.previousConnection && - !this.outputConnection && !metrics.statement) { - metrics.endCap = true; - } - - // If this block is an icon menu shadow, attempt to set the parent's - // ImageField src to the one that represents the current value of the field. - if (metrics.iconMenu) { - var currentSrc = metrics.iconMenu.getSrcForValue(metrics.iconMenu.getValue()); - if (currentSrc) { - metrics.iconMenu.setParentFieldImage(currentSrc); - } - } - - // Always render image field at 40x40 px - // Normal block sizing - metrics.width = Blockly.BlockSvg.SEP_SPACE_X * 2 + Blockly.BlockSvg.IMAGE_FIELD_WIDTH; - metrics.height = Blockly.BlockSvg.SEP_SPACE_Y * 2 + Blockly.BlockSvg.IMAGE_FIELD_HEIGHT; - - if (this.outputConnection) { - // Field shadow block - metrics.height = Blockly.BlockSvg.FIELD_HEIGHT; - metrics.width = Blockly.BlockSvg.FIELD_WIDTH; - } - if (metrics.statement) { - // Block with statement (e.g., repeat, forever) - metrics.width += metrics.bayWidth + 4 * Blockly.BlockSvg.CORNER_RADIUS + 2 * Blockly.BlockSvg.GRID_UNIT; - metrics.height = metrics.bayHeight + Blockly.BlockSvg.STATEMENT_BLOCK_SPACE; - } - if (metrics.startHat) { - // Start hats are 1 unit wider to account for optical effect of curve. - metrics.width += 1 * Blockly.BlockSvg.GRID_UNIT; - } - if (metrics.endCap) { - // End caps are 1 unit wider to account for optical effect of no notch. - metrics.width += 1 * Blockly.BlockSvg.GRID_UNIT; - } - return metrics; -}; - - -/** - * Draw the path of the block. - * Move the fields to the correct locations. - * @param {!Object} metrics An object containing computed measurements of the - * block. - * @private - */ -Blockly.BlockSvg.prototype.renderDraw_ = function(metrics) { - // Fetch the block's coordinates on the surface for use in anchoring - // the connections. - var connectionsXY = this.getRelativeToSurfaceXY(); - // Assemble the block's path. - var steps = []; - - this.renderDrawLeft_(steps, connectionsXY, metrics); - this.renderDrawBottom_(steps, connectionsXY, metrics); - this.renderDrawRight_(steps, connectionsXY, metrics); - this.renderDrawTop_(steps, connectionsXY, metrics); - - var pathString = steps.join(' '); - this.svgPath_.setAttribute('d', pathString); - - if (this.RTL) { - // Mirror the block's path. - // This is awesome. - this.svgPath_.setAttribute('transform', 'scale(-1 1)'); - } - - // Horizontal blocks have a single Image Field that is specially positioned - if (metrics.imageField) { - var imageField = metrics.imageField.getSvgRoot(); - var imageFieldSize = metrics.imageField.getSize(); - // Image field's position is calculated relative to the "end" edge of the - // block. - var imageFieldX = metrics.width - imageFieldSize.width - - Blockly.BlockSvg.SEP_SPACE_X / 1.5; - var imageFieldY = metrics.height - imageFieldSize.height - - Blockly.BlockSvg.SEP_SPACE_Y; - if (metrics.endCap) { - // End-cap image is offset by a grid unit to account for optical effect of no notch. - imageFieldX -= Blockly.BlockSvg.GRID_UNIT; - } - var imageFieldScale = "scale(1 1)"; - if (this.RTL) { - // Do we want to mirror the Image Field left-to-right? - if (metrics.imageField.getFlipRTL()) { - imageFieldScale = "scale(-1 1)"; - imageFieldX = -metrics.width + imageFieldSize.width + - Blockly.BlockSvg.SEP_SPACE_X / 1.5; - } else { - // If not, don't offset by imageFieldSize.width - imageFieldX = -metrics.width + Blockly.BlockSvg.SEP_SPACE_X / 1.5; - } - } - if (imageField) { - // Fields are invisible on insertion marker. - if (this.isInsertionMarker()) { - imageField.setAttribute('display', 'none'); - } - imageField.setAttribute('transform', - 'translate(' + imageFieldX + ',' + imageFieldY + ') ' + - imageFieldScale); - } - } - - // Position value input - if (this.getFieldShadowBlock_()) { - var input = this.getFieldShadowBlock_().getSvgRoot(); - var valueX = (Blockly.BlockSvg.NOTCH_WIDTH + - (metrics.bayWidth ? 2 * Blockly.BlockSvg.GRID_UNIT + - Blockly.BlockSvg.NOTCH_WIDTH * 2 : 0) + metrics.bayWidth); - if (metrics.startHat) { - // Start hats add some left margin to field for visual balance - valueX += Blockly.BlockSvg.GRID_UNIT * 2; - } - if (this.RTL) { - valueX = -valueX; - } - var valueY = (metrics.height + Blockly.BlockSvg.FIELD_Y_OFFSET); - var transformation = 'translate(' + valueX + ',' + valueY + ')'; - input.setAttribute('transform', transformation); - } -}; - -/** - * Give the block an attribute 'data-shapes' that lists its shape[s], and an - * attribute 'data-category' with its category. - * @param {!Object} metrics An object containing computed measurements of the - * block. - * @private - */ -Blockly.BlockSvg.prototype.renderClassify_ = function(metrics) { - var shapes = []; - - if (this.isShadow_) { - shapes.push('argument'); - } else { - if(metrics.statement) { - shapes.push('c-block'); - } - if (metrics.startHat) { - shapes.push('hat'); // c-block+hats are possible (e.x. reprter procedures) - } else if (!metrics.statement) { - shapes.push('stack'); //only call it "stack" if it's not a c-block - } - if (!this.nextConnection) { - shapes.push('end'); - } - } - - this.svgGroup_.setAttribute('data-shapes', shapes.join(' ')); - - if (this.getCategory()) { - this.svgGroup_.setAttribute('data-category', this.getCategory()); - } -}; - -/** - * Render the left edge of the block. - * @param {!Array.} steps Path of block outline. - * @param {!Object} connectionsXY Location of block. - * @param {!Object} metrics An object containing computed measurements of the - * block. - * @private - */ -Blockly.BlockSvg.prototype.renderDrawLeft_ = function(steps, connectionsXY, metrics) { - // Top edge. - if (metrics.startHat) { - // Hat block - // Position the cursor at the top-left starting point. - steps.push(Blockly.BlockSvg.HAT_TOP_LEFT_CORNER_START); - // Top-left rounded corner. - steps.push(Blockly.BlockSvg.HAT_TOP_LEFT_CORNER); - } else if (this.previousConnection) { - // Regular block - // Position the cursor at the top-left starting point. - steps.push(Blockly.BlockSvg.TOP_LEFT_CORNER_START); - // Top-left rounded corner. - steps.push(Blockly.BlockSvg.TOP_LEFT_CORNER); - var cursorY = metrics.height - Blockly.BlockSvg.CORNER_RADIUS - - Blockly.BlockSvg.SEP_SPACE_Y - Blockly.BlockSvg.NOTCH_HEIGHT; - steps.push('V', cursorY); - steps.push(Blockly.BlockSvg.NOTCH_PATH_DOWN); - // Create previous block connection. - var connectionX = connectionsXY.x; - var connectionY = connectionsXY.y + metrics.height - - Blockly.BlockSvg.CORNER_RADIUS * 2; - this.previousConnection.moveTo(connectionX, connectionY); - // This connection will be tightened when the parent renders. - steps.push('V', metrics.height - Blockly.BlockSvg.CORNER_RADIUS); - } else { - // Input - // Position the cursor at the top-left starting point. - steps.push('m', metrics.fieldRadius + ',0'); - // Top-left rounded corner. - steps.push( - 'A', metrics.fieldRadius + ',' + metrics.fieldRadius, - '0', '0,0', '0,' + metrics.fieldRadius); - steps.push( - 'V', metrics.height - metrics.fieldRadius); - } -}; - -/** - * Render the bottom edge of the block. - * @param {!Array.} steps Path of block outline. - * @param {!Object} connectionsXY Location of block. - * @param {!Object} metrics An object containing computed measurements of the - * block. - * @private - */ -Blockly.BlockSvg.prototype.renderDrawBottom_ = function(steps, - connectionsXY, metrics) { - - if (metrics.startHat) { - steps.push('a', Blockly.BlockSvg.HAT_CORNER_RADIUS + ',' + - Blockly.BlockSvg.HAT_CORNER_RADIUS + ' 0 0,0 ' + - Blockly.BlockSvg.HAT_CORNER_RADIUS + ',' + - Blockly.BlockSvg.HAT_CORNER_RADIUS); - } else if (this.previousConnection) { - steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' + - Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 ' + - Blockly.BlockSvg.CORNER_RADIUS + ',' + - Blockly.BlockSvg.CORNER_RADIUS); - } else { - // Input - steps.push( - 'a', metrics.fieldRadius + ',' + metrics.fieldRadius, - '0', '0,0', metrics.fieldRadius + ',' + metrics.fieldRadius); - } - - // Has statement - if (metrics.statement) { - steps.push('h', 4 * Blockly.BlockSvg.GRID_UNIT); - steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' + - Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 ' + - Blockly.BlockSvg.CORNER_RADIUS + ',-' + - Blockly.BlockSvg.CORNER_RADIUS); - steps.push('v', -2.5 * Blockly.BlockSvg.GRID_UNIT); - steps.push(Blockly.BlockSvg.NOTCH_PATH_UP); - // @todo Why 3? - steps.push('v', -metrics.bayHeight + (Blockly.BlockSvg.CORNER_RADIUS * 3) + - Blockly.BlockSvg.NOTCH_HEIGHT + 2 * Blockly.BlockSvg.GRID_UNIT); - steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' + - Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,1 ' + - Blockly.BlockSvg.CORNER_RADIUS + ',-' + - Blockly.BlockSvg.CORNER_RADIUS); - steps.push('h', metrics.bayWidth - (Blockly.BlockSvg.CORNER_RADIUS * 2)); - steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' + - Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,1 ' + - Blockly.BlockSvg.CORNER_RADIUS + ',' + - Blockly.BlockSvg.CORNER_RADIUS); - if (metrics.bayNotchAtRight) { - steps.push('v', metrics.bayHeight - (Blockly.BlockSvg.CORNER_RADIUS * 3) - - Blockly.BlockSvg.NOTCH_HEIGHT - 2 * Blockly.BlockSvg.GRID_UNIT); - steps.push(Blockly.BlockSvg.NOTCH_PATH_DOWN); - } - steps.push('V', metrics.bayHeight + 2 * Blockly.BlockSvg.GRID_UNIT); - steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' + - Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 ' + - Blockly.BlockSvg.CORNER_RADIUS + ',' + - Blockly.BlockSvg.CORNER_RADIUS); - - // Create statement connection. - var connectionX = connectionsXY.x + Blockly.BlockSvg.CORNER_RADIUS * 2 + - 4 * Blockly.BlockSvg.GRID_UNIT; - if (this.RTL) { - connectionX = connectionsXY.x - Blockly.BlockSvg.CORNER_RADIUS * 2 - - 4 * Blockly.BlockSvg.GRID_UNIT; - } - var connectionY = connectionsXY.y + metrics.height - - Blockly.BlockSvg.CORNER_RADIUS * 2; - metrics.statement.connection.moveTo(connectionX, connectionY); - if (metrics.statement.connection.targetConnection) { - metrics.statement.connection.tighten_(); - } - } - - if (!this.isShadow()) { - steps.push('H', metrics.width - Blockly.BlockSvg.CORNER_RADIUS); - } else { - // input - steps.push('H', metrics.width - metrics.fieldRadius); - } -}; - -/** - * Render the right edge of the block. - * @param {!Array.} steps Path of block outline. - * @param {!Object} connectionsXY Location of block. - * @param {!Object} metrics An object containing computed measurements of the - * block. - * @private - */ -Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, connectionsXY, metrics) { - if (!this.isShadow()) { - steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' + - Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 ' + - Blockly.BlockSvg.CORNER_RADIUS + ',-' + - Blockly.BlockSvg.CORNER_RADIUS); - steps.push('v', -2.5 * Blockly.BlockSvg.GRID_UNIT); - } else { - // Input - steps.push( - 'a', metrics.fieldRadius + ',' + metrics.fieldRadius, - '0', '0,0', metrics.fieldRadius + ',' + -1 * metrics.fieldRadius); - steps.push('v', -1 * (metrics.height - metrics.fieldRadius * 2)); - } - - if (this.nextConnection) { - steps.push(Blockly.BlockSvg.NOTCH_PATH_UP); - - // Include width of notch in block width. - this.width += Blockly.BlockSvg.NOTCH_WIDTH; - - // Create next block connection. - var connectionX; - if (this.RTL) { - connectionX = connectionsXY.x - metrics.width; - } else { - connectionX = connectionsXY.x + metrics.width; - } - var connectionY = connectionsXY.y + metrics.height - - Blockly.BlockSvg.CORNER_RADIUS * 2; - this.nextConnection.moveTo(connectionX, connectionY); - if (this.nextConnection.targetConnection) { - this.nextConnection.tighten_(); - } - steps.push('V', Blockly.BlockSvg.CORNER_RADIUS); - } else if (!this.isShadow()) { - steps.push('V', Blockly.BlockSvg.CORNER_RADIUS); - } -}; - -/** - * Render the top edge of the block. - * @param {!Array.} steps Path of block outline. - * @param {!Object} connectionsXY Location of block. - * @param {!Object} metrics An object containing computed measurements of the - * block. - * @private - */ -Blockly.BlockSvg.prototype.renderDrawTop_ = function(steps, connectionsXY, metrics) { - if (!this.isShadow()) { - steps.push('a', Blockly.BlockSvg.CORNER_RADIUS + ',' + - Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 -' + - Blockly.BlockSvg.CORNER_RADIUS + ',-' + - Blockly.BlockSvg.CORNER_RADIUS); - } else { - steps.push( - 'a', metrics.fieldRadius + ',' + metrics.fieldRadius, - '0', '0,0', '-' + metrics.fieldRadius + ',-' + metrics.fieldRadius); - } - steps.push('z'); -}; - -/** - * Get the field shadow block, if this block has one. - *

This is horizontal Scratch-specific, as "fields" are implemented as inputs - * with shadow blocks, and there is only one per block. - * @return {Blockly.BlockSvg} The field shadow block, or null if not found. - * @private - */ -Blockly.BlockSvg.prototype.getFieldShadowBlock_ = function() { - for (var i = 0, child; child = this.childBlocks_[i]; i++) { - if (child.isShadow()) { - return child; - } - } - - return null; -}; - -/** - * Position an new block correctly, so that it doesn't move the existing block - * when connected to it. - * @param {!Blockly.Block} newBlock The block to position - either the first - * block in a dragged stack or an insertion marker. - * @param {!Blockly.Connection} newConnection The connection on the new block's - * stack - either a connection on newBlock, or the last NEXT_STATEMENT - * connection on the stack if the stack's being dropped before another - * block. - * @param {!Blockly.Connection} existingConnection The connection on the - * existing block, which newBlock should line up with. - */ -Blockly.BlockSvg.prototype.positionNewBlock = function(newBlock, newConnection, existingConnection) { - // We only need to position the new block if it's before the existing one, - // otherwise its position is set by the previous block. - if (newConnection.type == Blockly.NEXT_STATEMENT) { - var dx = existingConnection.x_ - newConnection.x_; - var dy = existingConnection.y_ - newConnection.y_; - - // When putting a c-block around another c-block, the outer block must - // positioned above the inner block, as its connection point will stretch - // downwards when connected. - if (newConnection == newBlock.getFirstStatementConnection()) { - dy -= existingConnection.sourceBlock_.getHeightWidth(true).height - - Blockly.BlockSvg.MIN_BLOCK_Y; - } - - newBlock.moveBy(dx, dy); - } -}; diff --git a/core/block_render_svg_vertical.js b/core/block_render_svg_vertical.js deleted file mode 100644 index ca5ea470f3..0000000000 --- a/core/block_render_svg_vertical.js +++ /dev/null @@ -1,1732 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Methods for graphically rendering a block as SVG. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.BlockSvg.render'); - -goog.require('Blockly.BlockSvg'); -goog.require('Blockly.scratchBlocksUtils'); -goog.require('Blockly.utils'); - - -// UI constants for rendering blocks. -/** -* Grid unit to pixels conversion -* @const -*/ -Blockly.BlockSvg.GRID_UNIT = 4; - -/** - * Horizontal space between elements. - * @const - */ -Blockly.BlockSvg.SEP_SPACE_X = 2 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Vertical space between elements. - * @const - */ -Blockly.BlockSvg.SEP_SPACE_Y = 2 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Minimum width of a block. - * @const - */ -Blockly.BlockSvg.MIN_BLOCK_X = 16 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Minimum width of a block with output (reporters). - * @const - */ -Blockly.BlockSvg.MIN_BLOCK_X_OUTPUT = 12 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Minimum width of a shadow block with output (single fields). - * @const - */ -Blockly.BlockSvg.MIN_BLOCK_X_SHADOW_OUTPUT = 10 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Minimum height of a block. - * @const - */ -Blockly.BlockSvg.MIN_BLOCK_Y = 12 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Height of extra row after a statement input. - * @const - */ -Blockly.BlockSvg.EXTRA_STATEMENT_ROW_Y = 8 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Minimum width of a C- or E-shaped block. - * @const - */ -Blockly.BlockSvg.MIN_BLOCK_X_WITH_STATEMENT = 40 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Minimum height of a shadow block with output and a single field. - * This is used for shadow blocks that only contain a field - which are smaller than even reporters. - * @const - */ -Blockly.BlockSvg.MIN_BLOCK_Y_SINGLE_FIELD_OUTPUT = 8 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Minimum height of a non-shadow block with output, i.e. a reporter. - * @const - */ -Blockly.BlockSvg.MIN_BLOCK_Y_REPORTER = 10 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Minimum space for a statement input height. - * @const - */ -Blockly.BlockSvg.MIN_STATEMENT_INPUT_HEIGHT = 6 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Width of vertical notch. - * @const - */ -Blockly.BlockSvg.NOTCH_WIDTH = 8 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Height of vertical notch. - * @const - */ -Blockly.BlockSvg.NOTCH_HEIGHT = 2 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Rounded corner radius. - * @const - */ -Blockly.BlockSvg.CORNER_RADIUS = 1 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Minimum width of statement input edge on the left, in px. - * @const - */ -Blockly.BlockSvg.STATEMENT_INPUT_EDGE_WIDTH = 4 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Inner space between edge of statement input and notch. - * @const - */ -Blockly.BlockSvg.STATEMENT_INPUT_INNER_SPACE = 2 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Height of the top hat. - * @const - */ -Blockly.BlockSvg.START_HAT_HEIGHT = 16; - -/** - * Height of the vertical separator line for icons that appear at the left edge - * of a block, such as extension icons. - * @const - */ -Blockly.BlockSvg.ICON_SEPARATOR_HEIGHT = 10 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Path of the top hat's curve. - * @const - */ -Blockly.BlockSvg.START_HAT_PATH = 'c 25,-22 71,-22 96,0'; - -/** - * SVG path for drawing next/previous notch from left to right. - * @const - */ -Blockly.BlockSvg.NOTCH_PATH_LEFT = ( - 'c 2,0 3,1 4,2 ' + - 'l 4,4 ' + - 'c 1,1 2,2 4,2 ' + - 'h 12 ' + - 'c 2,0 3,-1 4,-2 ' + - 'l 4,-4 ' + - 'c 1,-1 2,-2 4,-2' -); - -/** - * SVG path for drawing next/previous notch from right to left. - * @const - */ -Blockly.BlockSvg.NOTCH_PATH_RIGHT = ( - 'c -2,0 -3,1 -4,2 ' + - 'l -4,4 ' + - 'c -1,1 -2,2 -4,2 ' + - 'h -12 ' + - 'c -2,0 -3,-1 -4,-2 ' + - 'l -4,-4 ' + - 'c -1,-1 -2,-2 -4,-2' -); - -/** - * Amount of padding before the notch. - * @const - */ -Blockly.BlockSvg.NOTCH_START_PADDING = 3 * Blockly.BlockSvg.GRID_UNIT; - -/** - * SVG start point for drawing the top-left corner. - * @const - */ -Blockly.BlockSvg.TOP_LEFT_CORNER_START = - 'm 0,' + Blockly.BlockSvg.CORNER_RADIUS; - -/** - * SVG path for drawing the rounded top-left corner. - * @const - */ -Blockly.BlockSvg.TOP_LEFT_CORNER = - 'A ' + Blockly.BlockSvg.CORNER_RADIUS + ',' + - Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,1 ' + - Blockly.BlockSvg.CORNER_RADIUS + ',0'; - -/** - * SVG path for drawing the rounded top-right corner. - * @const - */ -Blockly.BlockSvg.TOP_RIGHT_CORNER = - 'a ' + Blockly.BlockSvg.CORNER_RADIUS + ',' + - Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,1 ' + - Blockly.BlockSvg.CORNER_RADIUS + ',' + - Blockly.BlockSvg.CORNER_RADIUS; - -/** - * SVG path for drawing the rounded bottom-right corner. - * @const - */ -Blockly.BlockSvg.BOTTOM_RIGHT_CORNER = - ' a ' + Blockly.BlockSvg.CORNER_RADIUS + ',' + - Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,1 -' + - Blockly.BlockSvg.CORNER_RADIUS + ',' + - Blockly.BlockSvg.CORNER_RADIUS; - -/** - * SVG path for drawing the rounded bottom-left corner. - * @const - */ -Blockly.BlockSvg.BOTTOM_LEFT_CORNER = - 'a ' + Blockly.BlockSvg.CORNER_RADIUS + ',' + - Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,1 -' + - Blockly.BlockSvg.CORNER_RADIUS + ',-' + - Blockly.BlockSvg.CORNER_RADIUS; - -/** - * SVG path for drawing the top-left corner of a statement input. - * @const - */ -Blockly.BlockSvg.INNER_TOP_LEFT_CORNER = - ' a ' + Blockly.BlockSvg.CORNER_RADIUS + ',' + - Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 -' + - Blockly.BlockSvg.CORNER_RADIUS + ',' + - Blockly.BlockSvg.CORNER_RADIUS; - -/** - * SVG path for drawing the bottom-left corner of a statement input. - * Includes the rounded inside corner. - * @const - */ -Blockly.BlockSvg.INNER_BOTTOM_LEFT_CORNER = - 'a ' + Blockly.BlockSvg.CORNER_RADIUS + ',' + - Blockly.BlockSvg.CORNER_RADIUS + ' 0 0,0 ' + - Blockly.BlockSvg.CORNER_RADIUS + ',' + - Blockly.BlockSvg.CORNER_RADIUS; - -/** - * SVG path for an empty hexagonal input shape. - * @const - */ -Blockly.BlockSvg.INPUT_SHAPE_HEXAGONAL = - 'M ' + 4 * Blockly.BlockSvg.GRID_UNIT + ',0 ' + - ' h ' + 4 * Blockly.BlockSvg.GRID_UNIT + - ' l ' + 4 * Blockly.BlockSvg.GRID_UNIT + ',' + 4 * Blockly.BlockSvg.GRID_UNIT + - ' l ' + -4 * Blockly.BlockSvg.GRID_UNIT + ',' + 4 * Blockly.BlockSvg.GRID_UNIT + - ' h ' + -4 * Blockly.BlockSvg.GRID_UNIT + - ' l ' + -4 * Blockly.BlockSvg.GRID_UNIT + ',' + -4 * Blockly.BlockSvg.GRID_UNIT + - ' l ' + 4 * Blockly.BlockSvg.GRID_UNIT + ',' + -4 * Blockly.BlockSvg.GRID_UNIT + - ' z'; - -/** - * Width of empty boolean input shape. - * @const - */ -Blockly.BlockSvg.INPUT_SHAPE_HEXAGONAL_WIDTH = 12 * Blockly.BlockSvg.GRID_UNIT; - -/** - * SVG path for an empty square input shape. - * @const - */ -Blockly.BlockSvg.INPUT_SHAPE_SQUARE = - Blockly.BlockSvg.TOP_LEFT_CORNER_START + - Blockly.BlockSvg.TOP_LEFT_CORNER + - ' h ' + (12 * Blockly.BlockSvg.GRID_UNIT - 2 * Blockly.BlockSvg.CORNER_RADIUS) + - Blockly.BlockSvg.TOP_RIGHT_CORNER + - ' v ' + (8 * Blockly.BlockSvg.GRID_UNIT - 2 * Blockly.BlockSvg.CORNER_RADIUS) + - Blockly.BlockSvg.BOTTOM_RIGHT_CORNER + - ' h ' + (-12 * Blockly.BlockSvg.GRID_UNIT + 2 * Blockly.BlockSvg.CORNER_RADIUS) + - Blockly.BlockSvg.BOTTOM_LEFT_CORNER + - ' z'; - -/** - * Width of empty square input shape. - * @const - */ -Blockly.BlockSvg.INPUT_SHAPE_SQUARE_WIDTH = 10 * Blockly.BlockSvg.GRID_UNIT; - -/** - * SVG path for an empty round input shape. - * @const - */ - -Blockly.BlockSvg.INPUT_SHAPE_ROUND = - 'M ' + (4 * Blockly.BlockSvg.GRID_UNIT) + ',0' + - ' h ' + (4 * Blockly.BlockSvg.GRID_UNIT) + - ' a ' + (4 * Blockly.BlockSvg.GRID_UNIT) + ' ' + - (4 * Blockly.BlockSvg.GRID_UNIT) + ' 0 0 1 0 ' + (8 * Blockly.BlockSvg.GRID_UNIT) + - ' h ' + (-4 * Blockly.BlockSvg.GRID_UNIT) + - ' a ' + (4 * Blockly.BlockSvg.GRID_UNIT) + ' ' + - (4 * Blockly.BlockSvg.GRID_UNIT) + ' 0 0 1 0 -' + (8 * Blockly.BlockSvg.GRID_UNIT) + - ' z'; - -/** - * Width of empty round input shape. - * @const - */ -Blockly.BlockSvg.INPUT_SHAPE_ROUND_WIDTH = 12 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Height of empty input shape. - * @const - */ -Blockly.BlockSvg.INPUT_SHAPE_HEIGHT = 8 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Height of user inputs - * @const - */ -Blockly.BlockSvg.FIELD_HEIGHT = 8 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Width of user inputs - * @const - */ -Blockly.BlockSvg.FIELD_WIDTH = 6 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Editable field padding (left/right of the text). - * @const - */ -Blockly.BlockSvg.EDITABLE_FIELD_PADDING = 6; - -/** - * Square box field padding (left/right of the text). - * @const - */ -Blockly.BlockSvg.BOX_FIELD_PADDING = 2 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Drop-down arrow padding. - * @const - */ -Blockly.BlockSvg.DROPDOWN_ARROW_PADDING = 2 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Minimum width of user inputs during editing - * @const - */ -Blockly.BlockSvg.FIELD_WIDTH_MIN_EDIT = 8 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Maximum width of user inputs during editing - * @const - */ -Blockly.BlockSvg.FIELD_WIDTH_MAX_EDIT = Infinity; - -/** - * Maximum height of user inputs during editing - * @const - */ -Blockly.BlockSvg.FIELD_HEIGHT_MAX_EDIT = Blockly.BlockSvg.FIELD_HEIGHT; - -/** - * Top padding of user inputs - * @const - */ -Blockly.BlockSvg.FIELD_TOP_PADDING = 0.5 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Corner radius of number inputs - * @const - */ -Blockly.BlockSvg.NUMBER_FIELD_CORNER_RADIUS = 4 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Corner radius of text inputs - * @const - */ -Blockly.BlockSvg.TEXT_FIELD_CORNER_RADIUS = 1 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Default radius for a field, in px. - * @const - */ -Blockly.BlockSvg.FIELD_DEFAULT_CORNER_RADIUS = 4 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Max text display length for a field (per-horizontal/vertical) - * @const - */ -Blockly.BlockSvg.MAX_DISPLAY_LENGTH = Infinity; - -/** - * Minimum X of inputs and fields for blocks with a previous connection. - * Ensures that inputs will not overlap with the top notch of blocks. - * @const - */ -Blockly.BlockSvg.INPUT_AND_FIELD_MIN_X = 12 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Vertical padding around inline elements. - * @const - */ -Blockly.BlockSvg.INLINE_PADDING_Y = 1 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Point size of text field before animation. Must match size in CSS. - * See implementation in field_textinput. - */ -Blockly.BlockSvg.FIELD_TEXTINPUT_FONTSIZE_INITIAL = 12; - -/** - * Point size of text field after animation. - * See implementation in field_textinput. - */ -Blockly.BlockSvg.FIELD_TEXTINPUT_FONTSIZE_FINAL = 12; - -/** - * Whether text fields are allowed to expand past their truncated block size. - * @const{boolean} - */ -Blockly.BlockSvg.FIELD_TEXTINPUT_EXPAND_PAST_TRUNCATION = false; - -/** - * Whether text fields should animate their positioning. - * @const{boolean} - */ -Blockly.BlockSvg.FIELD_TEXTINPUT_ANIMATE_POSITIONING = false; - -/** - * Map of output/input shapes and the amount they should cause a block to be padded. - * Outer key is the outer shape, inner key is the inner shape. - * When a block with the outer shape contains an input block with the inner shape - * on its left or right edge, that side is extended by the padding specified. - * See also: `Blockly.BlockSvg.computeOutputPadding_`. - */ -Blockly.BlockSvg.SHAPE_IN_SHAPE_PADDING = { - 1: { // Outer shape: hexagon. - 0: 5 * Blockly.BlockSvg.GRID_UNIT, // Field in hexagon. - 1: 2 * Blockly.BlockSvg.GRID_UNIT, // Hexagon in hexagon. - 2: 5 * Blockly.BlockSvg.GRID_UNIT, // Round in hexagon. - 3: 5 * Blockly.BlockSvg.GRID_UNIT // Square in hexagon. - }, - 2: { // Outer shape: round. - 0: 3 * Blockly.BlockSvg.GRID_UNIT, // Field in round. - 1: 3 * Blockly.BlockSvg.GRID_UNIT, // Hexagon in round. - 2: 1 * Blockly.BlockSvg.GRID_UNIT, // Round in round. - 3: 2 * Blockly.BlockSvg.GRID_UNIT // Square in round. - }, - 3: { // Outer shape: square. - 0: 2 * Blockly.BlockSvg.GRID_UNIT, // Field in square. - 1: 2 * Blockly.BlockSvg.GRID_UNIT, // Hexagon in square. - 2: 2 * Blockly.BlockSvg.GRID_UNIT, // Round in square. - 3: 2 * Blockly.BlockSvg.GRID_UNIT // Square in square. - } -}; - -/** - * Corner radius of the hat on the define block. - * @const - */ -Blockly.BlockSvg.DEFINE_HAT_CORNER_RADIUS = 5 * Blockly.BlockSvg.GRID_UNIT; - -/** - * SVG path for drawing the rounded top-left corner. - * @const - */ -Blockly.BlockSvg.TOP_LEFT_CORNER_DEFINE_HAT = - 'a ' + Blockly.BlockSvg.DEFINE_HAT_CORNER_RADIUS + ',' + - Blockly.BlockSvg.DEFINE_HAT_CORNER_RADIUS + ' 0 0,1 ' + - Blockly.BlockSvg.DEFINE_HAT_CORNER_RADIUS + ',-' + - Blockly.BlockSvg.DEFINE_HAT_CORNER_RADIUS; - -/** - * SVG path for drawing the rounded top-left corner. - * @const - */ -Blockly.BlockSvg.TOP_RIGHT_CORNER_DEFINE_HAT = - 'a ' + Blockly.BlockSvg.DEFINE_HAT_CORNER_RADIUS + ',' + - Blockly.BlockSvg.DEFINE_HAT_CORNER_RADIUS + ' 0 0,1 ' + - Blockly.BlockSvg.DEFINE_HAT_CORNER_RADIUS + ',' + - Blockly.BlockSvg.DEFINE_HAT_CORNER_RADIUS; - -/** - * Padding on the right side of the internal block on the define block. - * @const - */ -Blockly.BlockSvg.DEFINE_BLOCK_PADDING_RIGHT = 2 * Blockly.BlockSvg.GRID_UNIT; - -/** - * Change the colour of a block. - */ -Blockly.BlockSvg.prototype.updateColour = function() { - var strokeColour = this.getColourTertiary(); - var renderShadowed = this.isShadow() && - !Blockly.scratchBlocksUtils.isShadowArgumentReporter(this); - - if (renderShadowed && this.parentBlock_) { - // Pull shadow block stroke colour from parent block's tertiary if possible. - strokeColour = this.parentBlock_.getColourTertiary(); - // Special case: if we contain a colour field, set to a special stroke colour. - if (this.inputList[0] && - this.inputList[0].fieldRow[0] && - (this.inputList[0].fieldRow[0] instanceof Blockly.FieldColour || - this.inputList[0].fieldRow[0] instanceof Blockly.FieldColourSlider)) { - strokeColour = Blockly.Colours.colourPickerStroke; - } - } - - // Render block stroke - this.svgPath_.setAttribute('stroke', strokeColour); - - // Render block fill - if (this.isGlowingBlock_ || renderShadowed) { - // Use the block's shadow colour if possible. - if (this.getShadowColour()) { - var fillColour = this.getShadowColour(); - } else { - var fillColour = this.getColourSecondary(); - } - } else { - var fillColour = this.getColour(); - } - this.svgPath_.setAttribute('fill', fillColour); - - // Render opacity - this.svgPath_.setAttribute('fill-opacity', this.getOpacity()); - - // Update colours of input shapes. - for (var i = 0, input; input = this.inputList[i]; i++) { - if (input.outlinePath) { - input.outlinePath.setAttribute('fill', this.getColourTertiary()); - } - } - - // Render icon(s) if applicable - var icons = this.getIcons(); - for (var i = 0; i < icons.length; i++) { - icons[i].updateColour(); - } - - // Bump every dropdown to change its colour. - for (var x = 0, input; input = this.inputList[x]; x++) { - for (var y = 0, field; field = input.fieldRow[y]; y++) { - field.setText(null); - } - } -}; - -/** - * Visual effect to show that if the dragging block is dropped, this block will - * be replaced. If a shadow block it will disappear. Otherwise it will bump. - * @param {boolean} add True if highlighting should be added. - */ -Blockly.BlockSvg.prototype.highlightForReplacement = function(add) { - if (add) { - var replacementGlowFilterId = this.workspace.options.replacementGlowFilterId - || 'blocklyReplacementGlowFilter'; - this.svgPath_.setAttribute('filter', 'url(#' + replacementGlowFilterId + ')'); - Blockly.utils.addClass(/** @type {!Element} */ (this.svgGroup_), - 'blocklyReplaceable'); - } else { - this.svgPath_.removeAttribute('filter'); - Blockly.utils.removeClass(/** @type {!Element} */ (this.svgGroup_), - 'blocklyReplaceable'); - } -}; - -/** - * Visual effect to show that if the dragging block is dropped it will connect - * to this input. - * @param {Blockly.Connection} conn The connection on the input to highlight. - * @param {boolean} add True if highlighting should be added. - */ -Blockly.BlockSvg.prototype.highlightShapeForInput = function(conn, add) { - var input = this.getInputWithConnection(conn); - if (!input) { - throw 'No input found for the connection'; - } - if (!input.outlinePath) { - return; - } - if (add) { - var replacementGlowFilterId = this.workspace.options.replacementGlowFilterId - || 'blocklyReplacementGlowFilter'; - input.outlinePath.setAttribute('filter', - 'url(#' + replacementGlowFilterId + ')'); - Blockly.utils.addClass(/** @type {!Element} */ (this.svgGroup_), - 'blocklyReplaceable'); - } else { - input.outlinePath.removeAttribute('filter'); - Blockly.utils.removeClass(/** @type {!Element} */ (this.svgGroup_), - 'blocklyReplaceable'); - } -}; - -/** - * Returns a bounding box describing the dimensions of this block - * and any blocks stacked below it. - * @return {!{height: number, width: number}} Object with height and width properties. - */ -Blockly.BlockSvg.prototype.getHeightWidth = function() { - var height = this.height; - var width = this.width; - // Recursively add size of subsequent blocks. - var nextBlock = this.getNextBlock(); - if (nextBlock) { - var nextHeightWidth = nextBlock.getHeightWidth(); - height += nextHeightWidth.height; - height -= Blockly.BlockSvg.NOTCH_HEIGHT; // Exclude height of connected notch. - width = Math.max(width, nextHeightWidth.width); - } - return {height: height, width: width}; -}; - -/** - * Render the block. - * Lays out and reflows a block based on its contents and settings. - * @param {boolean=} opt_bubble If false, just render this block. - * If true, also render block's parent, grandparent, etc. Defaults to true. - */ -Blockly.BlockSvg.prototype.render = function(opt_bubble) { - Blockly.Field.startCache(); - this.rendered = true; - - var cursorX = Blockly.BlockSvg.SEP_SPACE_X; - if (this.RTL) { - cursorX = -cursorX; - } - // Move the icons into position. - var icons = this.getIcons(); - var scratchCommentIcon = null; - for (var i = 0; i < icons.length; i++) { - if (icons[i] instanceof Blockly.ScratchBlockComment) { - // Don't render scratch block comment icon until - // after the inputs - scratchCommentIcon = icons[i]; - } else { - cursorX = icons[i].renderIcon(cursorX); - } - } - cursorX += this.RTL ? - Blockly.BlockSvg.SEP_SPACE_X : -Blockly.BlockSvg.SEP_SPACE_X; - // If there are no icons, cursorX will be 0, otherwise it will be the - // width that the first label needs to move over by. - - // If this is an extension reporter block, add a horizontal offset. - if (this.isScratchExtension && this.outputConnection) { - cursorX += this.RTL ? - -Blockly.BlockSvg.GRID_UNIT : Blockly.BlockSvg.GRID_UNIT; - } - - var inputRows = this.renderCompute_(cursorX); - this.renderDraw_(cursorX, inputRows); - this.renderMoveConnections_(); - - this.renderClassify_(); - - // Position the Scratch Block Comment Icon at the end of the block - if (scratchCommentIcon) { - var iconX = this.RTL ? -inputRows.rightEdge : inputRows.rightEdge; - var inputMarginY = inputRows[0].height / 2; - scratchCommentIcon.renderIcon(iconX, inputMarginY); - } - - if (opt_bubble !== false) { - // Render all blocks above this one (propagate a reflow). - var parentBlock = this.getParent(); - if (parentBlock) { - parentBlock.render(true); - } else { - // Top-most block. Fire an event to allow scrollbars to resize. - Blockly.resizeSvgContents(this.workspace); - } - } - Blockly.Field.stopCache(); -}; - -/** - * Render a list of fields starting at the specified location. - * @param {!Array.} fieldList List of fields. - * @param {number} cursorX X-coordinate to start the fields. - * @param {number} cursorY Y-coordinate around which fields are centered. - * @return {number} X-coordinate of the end of the field row (plus a gap). - * @private - */ -Blockly.BlockSvg.prototype.renderFields_ = function(fieldList, cursorX, - cursorY) { - if (this.RTL) { - cursorX = -cursorX; - } - for (var t = 0, field; field = fieldList[t]; t++) { - var root = field.getSvgRoot(); - if (!root) { - continue; - } - // In blocks with a notch, fields should be bumped to a min X, - // to avoid overlapping with the notch. Label and image fields are - // excluded. - if (this.previousConnection && !(field instanceof Blockly.FieldLabel) && - !(field instanceof Blockly.FieldImage)) { - cursorX = this.RTL ? - Math.min(cursorX, -Blockly.BlockSvg.INPUT_AND_FIELD_MIN_X) : - Math.max(cursorX, Blockly.BlockSvg.INPUT_AND_FIELD_MIN_X); - } - // Offset the field upward by half its height. - // This vertically centers the fields around cursorY. - var yOffset = -field.getSize().height / 2; - - // If this is an extension block, and this field is the first field, and - // it is an image field, and this block has a previous connection, bump - // the image down by one grid unit to align it vertically. - if (this.isScratchExtension && (field === this.inputList[0].fieldRow[0]) - && (field instanceof Blockly.FieldImage) && this.previousConnection) { - yOffset += Blockly.BlockSvg.GRID_UNIT; - } - - // If this is an extension hat block, adjust the height of the vertical - // separator without adjusting the field height. The effect is to move - // the bottom end of the line up one grid unit. - if (this.isScratchExtension && - !this.previousConnection && this.nextConnection && - field instanceof Blockly.FieldVerticalSeparator) { - field.setLineHeight(Blockly.BlockSvg.ICON_SEPARATOR_HEIGHT - - Blockly.BlockSvg.GRID_UNIT); - } - - var translateX, translateY; - var scale = ''; - if (this.RTL) { - cursorX -= field.renderSep + field.renderWidth; - translateX = cursorX; - translateY = cursorY + yOffset; - if (field.renderWidth) { - cursorX -= Blockly.BlockSvg.SEP_SPACE_X; - } - } else { - translateX = cursorX + field.renderSep; - translateY = cursorY + yOffset; - if (field.renderWidth) { - cursorX += field.renderSep + field.renderWidth + - Blockly.BlockSvg.SEP_SPACE_X; - } - } - if (this.RTL && - field instanceof Blockly.FieldImage && - field.getFlipRTL()) { - scale = 'scale(-1 1)'; - translateX += field.renderWidth; - } - root.setAttribute('transform', - 'translate(' + translateX + ', ' + translateY + ') ' + scale); - - // Fields are invisible on insertion marker. - if (this.isInsertionMarker()) { - root.setAttribute('display', 'none'); - } - } - return this.RTL ? -cursorX : cursorX; -}; - -/** - * Computes the height and widths for each row and field. - * @param {number} iconWidth Offset of first row due to icons. - * @return {!Array.>} 2D array of objects, each containing - * position information. - * @private - */ -Blockly.BlockSvg.prototype.renderCompute_ = function(iconWidth) { - var inputList = this.inputList; - var inputRows = []; - // Block will be drawn from 0 (left edge) to rightEdge, in px. - inputRows.rightEdge = 0; - // Drawn from 0 to bottomEdge vertically. - inputRows.bottomEdge = 0; - var fieldValueWidth = 0; // Width of longest external value field. - var fieldStatementWidth = 0; // Width of longest statement field. - var hasValue = false; - var hasStatement = false; - var hasDummy = false; - var lastType = undefined; - - // Previously created row, for special-casing row heights on C- and E- shaped blocks. - var previousRow; - for (var i = 0, input; input = inputList[i]; i++) { - if (!input.isVisible()) { - continue; - } - var isSecondInputOnProcedure = this.type == 'procedures_definition' && - lastType && lastType == Blockly.NEXT_STATEMENT; - var row; - // Don't create a new row for the second dummy input on a procedure block. - // See github.com/LLK/scratch-blocks/issues/1658 - // In all other cases, statement and value inputs catch all preceding dummy - // inputs, and cause a line break before following inputs. - if (!isSecondInputOnProcedure && - (!lastType || lastType == Blockly.NEXT_STATEMENT || - input.type == Blockly.NEXT_STATEMENT)) { - lastType = input.type; - row = this.createRowForInput_(input); - inputRows.push(row); - } else { - row = inputRows[inputRows.length - 1]; - } - row.push(input); - - // Compute minimum dimensions for this input. - input.renderHeight = this.computeInputHeight_(input, row, previousRow); - input.renderWidth = this.computeInputWidth_(input); - - // If the input is a statement input, determine if a notch - // should be drawn at the inner bottom of the C. - row.statementNotchAtBottom = true; - if (input.connection && input.connection.type === Blockly.NEXT_STATEMENT) { - var linkedBlock = input.connection.targetBlock(); - if (linkedBlock && !linkedBlock.lastConnectionInStack()) { - row.statementNotchAtBottom = false; - } - } - - // Expand input size. - if (input.connection) { - var linkedBlock = input.connection.targetBlock(); - var paddedHeight = 0; - var paddedWidth = 0; - if (linkedBlock) { - // A block is connected to the input - use its size. - var bBox = linkedBlock.getHeightWidth(); - paddedHeight = bBox.height; - paddedWidth = bBox.width; - } else { - // No block connected - use the size of the rendered empty input shape. - paddedHeight = Blockly.BlockSvg.INPUT_SHAPE_HEIGHT; - } - if (input.connection.type === Blockly.INPUT_VALUE) { - paddedHeight += 2 * Blockly.BlockSvg.INLINE_PADDING_Y; - } - if (input.connection.type === Blockly.NEXT_STATEMENT) { - // Subtract height of notch, only if the last block in the stack has a next connection. - if (row.statementNotchAtBottom) { - paddedHeight -= Blockly.BlockSvg.NOTCH_HEIGHT; - } - } - input.renderHeight = Math.max(input.renderHeight, paddedHeight); - input.renderWidth = Math.max(input.renderWidth, paddedWidth); - } - row.height = Math.max(row.height, input.renderHeight); - input.fieldWidth = 0; - if (inputRows.length == 1) { - // The first row gets shifted to accommodate any icons. - input.fieldWidth += this.RTL ? -iconWidth : iconWidth; - } - var previousFieldEditable = false; - for (var j = 0, field; field = input.fieldRow[j]; j++) { - if (j != 0) { - input.fieldWidth += Blockly.BlockSvg.SEP_SPACE_X; - } - // Get the dimensions of the field. - var fieldSize = field.getSize(); - field.renderWidth = fieldSize.width; - field.renderSep = (previousFieldEditable && field.EDITABLE) ? - Blockly.BlockSvg.SEP_SPACE_X : 0; - // See github.com/LLK/scratch-blocks/issues/1658 - if (!isSecondInputOnProcedure) { - input.fieldWidth += field.renderWidth + field.renderSep; - } - row.height = Math.max(row.height, fieldSize.height); - previousFieldEditable = field.EDITABLE; - } - - if (row.type != Blockly.BlockSvg.INLINE) { - if (row.type == Blockly.NEXT_STATEMENT) { - hasStatement = true; - fieldStatementWidth = Math.max(fieldStatementWidth, input.fieldWidth); - } else { - if (row.type == Blockly.INPUT_VALUE) { - hasValue = true; - } else if (row.type == Blockly.DUMMY_INPUT) { - hasDummy = true; - } - fieldValueWidth = Math.max(fieldValueWidth, input.fieldWidth); - } - } - previousRow = row; - } - // Compute padding for output blocks. - // Data is attached to the row. - this.computeOutputPadding_(inputRows); - // Compute the statement edge. - // This is the width of a block where statements are nested. - inputRows.statementEdge = Blockly.BlockSvg.STATEMENT_INPUT_EDGE_WIDTH + - fieldStatementWidth; - - // Compute the preferred right edge. - inputRows.rightEdge = this.computeRightEdge_(inputRows.rightEdge, - hasStatement); - - // Bottom edge is sum of row heights - for (var i = 0; i < inputRows.length; i++) { - inputRows.bottomEdge += inputRows[i].height; - } - - inputRows.hasValue = hasValue; - inputRows.hasStatement = hasStatement; - inputRows.hasDummy = hasDummy; - return inputRows; -}; - -/** - * Compute the minimum width of this input based on the connection type and - * outputs. - * @param {!Blockly.Input} input The input to measure. - * @return {number} the computed width of this input. - * @private - */ -Blockly.BlockSvg.prototype.computeInputWidth_ = function(input) { - // Empty input shape widths. - if (input.type == Blockly.INPUT_VALUE && - (!input.connection || !input.connection.isConnected())) { - switch (input.connection.getOutputShape()) { - case Blockly.OUTPUT_SHAPE_SQUARE: - return Blockly.BlockSvg.INPUT_SHAPE_SQUARE_WIDTH; - case Blockly.OUTPUT_SHAPE_ROUND: - return Blockly.BlockSvg.INPUT_SHAPE_ROUND_WIDTH; - case Blockly.OUTPUT_SHAPE_HEXAGONAL: - return Blockly.BlockSvg.INPUT_SHAPE_HEXAGONAL_WIDTH; - default: - return 0; - } - } else { - return 0; - } -}; - -/** - * Compute the minimum height of this input. - * @param {!Blockly.Input} input The input to measure. - * @param {!Object} row The row of the block that is currently being measured. - * @param {!Object} previousRow The previous row of the block, which was just - * measured. - * @return {number} the computed height of this input. - * @private - */ -Blockly.BlockSvg.prototype.computeInputHeight_ = function(input, row, - previousRow) { - if (this.inputList.length === 1 && this.outputConnection && - (this.isShadow() && - !Blockly.scratchBlocksUtils.isShadowArgumentReporter(this))) { - // "Lone" field blocks are smaller. - return Blockly.BlockSvg.MIN_BLOCK_Y_SINGLE_FIELD_OUTPUT; - } else if (this.outputConnection) { - // If this is an extension reporter block, make it taller. - if (this.isScratchExtension) { - return Blockly.BlockSvg.MIN_BLOCK_Y_REPORTER + 2 * Blockly.BlockSvg.GRID_UNIT; - } - // All other reporters. - return Blockly.BlockSvg.MIN_BLOCK_Y_REPORTER; - } else if (row.type == Blockly.NEXT_STATEMENT) { - // Statement input. - return Blockly.BlockSvg.MIN_STATEMENT_INPUT_HEIGHT; - } else if (previousRow && previousRow.type == Blockly.NEXT_STATEMENT) { - // Extra row for below statement input. - return Blockly.BlockSvg.EXTRA_STATEMENT_ROW_Y; - } else { - // If this is an extension block, and it has a previous connection, - // make it taller. - if (this.isScratchExtension && this.previousConnection) { - return Blockly.BlockSvg.MIN_BLOCK_Y + 2 * Blockly.BlockSvg.GRID_UNIT; - } - // All other blocks. - return Blockly.BlockSvg.MIN_BLOCK_Y; - } -}; - -/** - * Create a row for an input and associated fields. - * @param {!Blockly.Input} input The input that the row is based on. - * @return {!Object} The new row, with the correct type and default sizing info. - */ -Blockly.BlockSvg.prototype.createRowForInput_ = function(input) { - // Create new row. - var row = []; - if (input.type != Blockly.NEXT_STATEMENT) { - row.type = Blockly.BlockSvg.INLINE; - } else { - row.type = input.type; - } - row.height = 0; - // Default padding for a block: same as separators between fields/inputs. - row.paddingStart = Blockly.BlockSvg.SEP_SPACE_X; - row.paddingEnd = Blockly.BlockSvg.SEP_SPACE_X; - return row; -}; - -/** - * Compute the preferred right edge of the block. - * @param {number} curEdge The previously calculated right edge. - * @param {boolean} hasStatement Whether this block has a statement input. - * @return {number} The preferred right edge of the block. - */ -Blockly.BlockSvg.prototype.computeRightEdge_ = function(curEdge, hasStatement) { - var edge = curEdge; - if (this.previousConnection || this.nextConnection) { - // Blocks with notches - edge = Math.max(edge, Blockly.BlockSvg.MIN_BLOCK_X); - } else if (this.outputConnection) { - if (this.isShadow() && - !Blockly.scratchBlocksUtils.isShadowArgumentReporter(this)) { - // Single-fields - edge = Math.max(edge, Blockly.BlockSvg.MIN_BLOCK_X_SHADOW_OUTPUT); - } else { - // Reporters - edge = Math.max(edge, Blockly.BlockSvg.MIN_BLOCK_X_OUTPUT); - } - } - if (hasStatement) { - // Statement blocks (C- or E- shaped) have a longer minimum width. - edge = Math.max(edge, Blockly.BlockSvg.MIN_BLOCK_X_WITH_STATEMENT); - } - - // Ensure insertion markers are at least insertionMarkerMinWidth_ wide. - if (this.insertionMarkerMinWidth_ > 0) { - edge = Math.max(edge, this.insertionMarkerMinWidth_); - } - return edge; -}; - -/** - * For a block with output, - * determine start and end padding, based on connected inputs. - * Padding will depend on the shape of the output, the shape of the input, - * and possibly the size of the input. - * @param {!Array.>} inputRows Partially calculated rows. - */ -Blockly.BlockSvg.prototype.computeOutputPadding_ = function(inputRows) { - // Only apply to blocks with outputs and not single fields (shadows). - if (!this.getOutputShape() || !this.outputConnection || - (this.isShadow() && - !Blockly.scratchBlocksUtils.isShadowArgumentReporter(this))) { - return; - } - // Blocks with outputs must have single row to be padded. - if (inputRows.length > 1) { - return; - } - var row = inputRows[0]; - var shape = this.getOutputShape(); - // Reset any padding: it's about to be set. - row.paddingStart = 0; - row.paddingEnd = 0; - // Start row padding: based on first input or first field. - var firstInput = row[0]; - var firstField = firstInput.fieldRow[0]; - var otherShape; - // In checking the left/start side, a field takes precedence over any input. - // That's because a field will be rendered before any value input. - if (firstField) { - otherShape = 0; // Field comes first in the row. - } else { - // Value input comes first in the row. - var inputConnection = firstInput.connection; - if (!inputConnection.targetConnection) { - // Not connected: use the drawn shape. - otherShape = inputConnection.getOutputShape(); - } else { - // Connected: use the connected block's output shape. - otherShape = inputConnection.targetConnection.getSourceBlock().getOutputShape(); - } - // Special case for hexagonal output: if the connection is larger height - // than a standard reporter, add some start padding. - // https://github.com/LLK/scratch-blocks/issues/376 - if (shape == Blockly.OUTPUT_SHAPE_HEXAGONAL && - otherShape != Blockly.OUTPUT_SHAPE_HEXAGONAL) { - var deltaHeight = firstInput.renderHeight - Blockly.BlockSvg.MIN_BLOCK_Y_REPORTER; - // One grid unit per level of nesting. - row.paddingStart += deltaHeight / 2; - } - } - row.paddingStart += Blockly.BlockSvg.SHAPE_IN_SHAPE_PADDING[shape][otherShape]; - // End row padding: based on last input or last field. - var lastInput = row[row.length - 1]; - // In checking the right/end side, any value input takes precedence over any field. - // That's because fields are rendered before inputs...the last item - // in the row will be an input, if one exists. - if (lastInput.connection) { - // Value input last in the row. - var inputConnection = lastInput.connection; - if (!inputConnection.targetConnection) { - // Not connected: use the drawn shape. - otherShape = inputConnection.getOutputShape(); - } else { - // Connected: use the connected block's output shape. - otherShape = inputConnection.targetConnection.getSourceBlock().getOutputShape(); - } - // Special case for hexagonal output: if the connection is larger height - // than a standard reporter, add some end padding. - // https://github.com/LLK/scratch-blocks/issues/376 - if (shape == Blockly.OUTPUT_SHAPE_HEXAGONAL && - otherShape != Blockly.OUTPUT_SHAPE_HEXAGONAL) { - var deltaHeight = lastInput.renderHeight - Blockly.BlockSvg.MIN_BLOCK_Y_REPORTER; - // One grid unit per level of nesting. - row.paddingEnd += deltaHeight / 2; - } - } else { - // No input in this row - mark as field. - otherShape = 0; - } - row.paddingEnd += Blockly.BlockSvg.SHAPE_IN_SHAPE_PADDING[shape][otherShape]; -}; - -/** - * Draw the path of the block. - * Move the fields to the correct locations. - * @param {number} iconWidth Offset of first row due to icons. - * @param {!Array.>} inputRows 2D array of objects, each - * containing position information. - * @private - */ -Blockly.BlockSvg.prototype.renderDraw_ = function(iconWidth, inputRows) { - this.startHat_ = false; - // Should the top left corners be rounded or square? - // Currently, it is squared only if it's a hat. - this.squareTopLeftCorner_ = false; - if (!this.outputConnection && !this.previousConnection) { - // No output or previous connection. - this.squareTopLeftCorner_ = true; - this.startHat_ = true; - inputRows.rightEdge = Math.max(inputRows.rightEdge, 100); - } - - // Amount of space to skip drawing the top and bottom, - // to make room for the left and right to draw shapes (curves or angles). - this.edgeShapeWidth_ = 0; - this.edgeShape_ = null; - if (this.outputConnection) { - // Width of the curve/pointy-curve - var shape = this.getOutputShape(); - if (shape === Blockly.OUTPUT_SHAPE_HEXAGONAL || shape === Blockly.OUTPUT_SHAPE_ROUND) { - this.edgeShapeWidth_ = inputRows.bottomEdge / 2; - this.edgeShape_ = shape; - this.squareTopLeftCorner_ = true; - } - } - - // Assemble the block's path. - var steps = []; - - this.renderDrawTop_(steps, inputRows.rightEdge); - var cursorY = this.renderDrawRight_(steps, inputRows, iconWidth); - this.renderDrawBottom_(steps, cursorY); - this.renderDrawLeft_(steps); - - var pathString = steps.join(' '); - this.svgPath_.setAttribute('d', pathString); - - if (this.RTL) { - // Mirror the block's path. - // This is awesome. - this.svgPath_.setAttribute('transform', 'scale(-1 1)'); - } -}; - -/** - * Give the block an attribute 'data-shapes' that lists its shape[s], and an - * attribute 'data-category' with its category. - * @private - */ -Blockly.BlockSvg.prototype.renderClassify_ = function() { - var shapes = []; - - if (this.outputConnection) { - if (this.isShadow_) { - shapes.push('argument'); - } else { - shapes.push('reporter'); - } - if (this.edgeShape_ === Blockly.OUTPUT_SHAPE_HEXAGONAL) { - shapes.push('boolean'); - } else if (this.edgeShape_ === Blockly.OUTPUT_SHAPE_ROUND) { - shapes.push('round'); - } - } else { - // count the number of statement inputs - var inputList = this.inputList; - var statementCount = 0; - for (var i = 0, input; input = inputList[i]; i++) { - if (input.connection && input.connection.type === Blockly.NEXT_STATEMENT) { - statementCount++; - } - } - - if (statementCount) { - shapes.push('c-block'); - shapes.push('c-' + statementCount); - } - if (this.startHat_) { - shapes.push('hat'); // c-block+hats are possible (e.x. reprter procedures) - } else if (!statementCount) { - shapes.push('stack'); //only call it "stack" if it's not a c-block - } - if (!this.nextConnection) { - shapes.push('end'); - } - } - - this.svgGroup_.setAttribute('data-shapes', shapes.join(' ')); - - if (this.getCategory()) { - this.svgGroup_.setAttribute('data-category', this.getCategory()); - } -}; - -/** - * Render the top edge of the block. - * @param {!Array.} steps Path of block outline. - * @param {number} rightEdge Minimum width of block. - * @private - */ -Blockly.BlockSvg.prototype.renderDrawTop_ = function(steps, rightEdge) { - if (this.type == Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE) { - steps.push('m 0, 0'); - steps.push(Blockly.BlockSvg.TOP_LEFT_CORNER_DEFINE_HAT); - } else { - // Position the cursor at the top-left starting point. - if (this.squareTopLeftCorner_) { - steps.push('m 0,0'); - if (this.startHat_) { - steps.push(Blockly.BlockSvg.START_HAT_PATH); - } - // Skip space for the output shape - if (this.edgeShapeWidth_) { - steps.push('m ' + this.edgeShapeWidth_ + ',0'); - } - } else { - steps.push(Blockly.BlockSvg.TOP_LEFT_CORNER_START); - // Top-left rounded corner. - steps.push(Blockly.BlockSvg.TOP_LEFT_CORNER); - } - - // Top edge. - if (this.previousConnection) { - // Space before the notch - steps.push('H', Blockly.BlockSvg.NOTCH_START_PADDING); - steps.push(Blockly.BlockSvg.NOTCH_PATH_LEFT); - // Create previous block connection. - var connectionX = (this.RTL ? - -Blockly.BlockSvg.NOTCH_WIDTH : Blockly.BlockSvg.NOTCH_WIDTH); - this.previousConnection.setOffsetInBlock(connectionX, 0); - } - } - this.width = rightEdge; -}; - -/** - * Render the right edge of the block. - * @param {!Array.} steps Path of block outline. - * @param {!Array.>} inputRows 2D array of objects, each - * containing position information. - * @param {number} iconWidth Offset of first row due to icons. - * @return {number} Height of block. - * @private - */ -Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, - inputRows, iconWidth) { - var cursorX = 0; - var cursorY = 0; - var connectionX, connectionY; - for (var y = 0, row; row = inputRows[y]; y++) { - cursorX = row.paddingStart; - if (y == 0) { - cursorX += this.RTL ? -iconWidth : iconWidth; - } - - if (row.type == Blockly.BlockSvg.INLINE) { - // Inline inputs. - for (var x = 0, input; input = row[x]; x++) { - // Align fields vertically within the row. - // Moves the field to half of the row's height. - // In renderFields_, the field is further centered - // by its own rendered height. - var fieldY = cursorY + row.height / 2; - - var fieldX = Blockly.BlockSvg.getAlignedCursor_(cursorX, input, - inputRows.rightEdge); - - cursorX = this.renderFields_(input.fieldRow, fieldX, fieldY); - if (input.type == Blockly.INPUT_VALUE) { - // Create inline input connection. - // In blocks with a notch, inputs should be bumped to a min X, - // to avoid overlapping with the notch. - if (this.previousConnection) { - cursorX = Math.max(cursorX, Blockly.BlockSvg.INPUT_AND_FIELD_MIN_X); - } - connectionX = this.RTL ? -cursorX : cursorX; - // Attempt to center the connection vertically. - var connectionYOffset = row.height / 2; - connectionY = cursorY + connectionYOffset; - input.connection.setOffsetInBlock(connectionX, connectionY); - this.renderInputShape_(input, cursorX, cursorY + connectionYOffset); - cursorX += input.renderWidth + Blockly.BlockSvg.SEP_SPACE_X; - } - } - // Remove final separator and replace it with right-padding. - cursorX -= Blockly.BlockSvg.SEP_SPACE_X; - cursorX += row.paddingEnd; - // Update right edge for all inputs, such that all rows - // stretch to be at least the size of all previous rows. - inputRows.rightEdge = Math.max(cursorX, inputRows.rightEdge); - // Move to the right edge - cursorX = Math.max(cursorX, inputRows.rightEdge); - this.width = Math.max(this.width, cursorX); - if (!this.edgeShape_) { - // Include corner radius in drawing the horizontal line. - steps.push('H', cursorX - Blockly.BlockSvg.CORNER_RADIUS - this.edgeShapeWidth_); - steps.push(Blockly.BlockSvg.TOP_RIGHT_CORNER); - } else { - // Don't include corner radius - no corner (edge shape drawn). - steps.push('H', cursorX - this.edgeShapeWidth_); - } - // Subtract CORNER_RADIUS * 2 to account for the top right corner - // and also the bottom right corner. Only move vertically the non-corner length. - if (!this.edgeShape_) { - steps.push('v', row.height - Blockly.BlockSvg.CORNER_RADIUS * 2); - } - } else if (row.type == Blockly.NEXT_STATEMENT) { - // Nested statement. - var input = row[0]; - var fieldX = cursorX; - // Align fields vertically within the row. - // In renderFields_, the field is further centered by its own height. - var fieldY = cursorY; - fieldY += Blockly.BlockSvg.MIN_STATEMENT_INPUT_HEIGHT; - this.renderFields_(input.fieldRow, fieldX, fieldY); - // Move to the start of the notch. - cursorX = inputRows.statementEdge + Blockly.BlockSvg.NOTCH_WIDTH; - - if (this.type == Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE) { - this.renderDefineBlock_(steps, inputRows, input, row, cursorY); - } else { - Blockly.BlockSvg.drawStatementInputFromTopRight_(steps, cursorX, - inputRows.rightEdge, row); - } - - // Create statement connection. - connectionX = this.RTL ? -cursorX : cursorX; - input.connection.setOffsetInBlock(connectionX, cursorY); - if (input.connection.isConnected()) { - this.width = Math.max(this.width, inputRows.statementEdge + - input.connection.targetBlock().getHeightWidth().width); - } - if (this.type != Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE && - (y == inputRows.length - 1 || - inputRows[y + 1].type == Blockly.NEXT_STATEMENT)) { - // If the final input is a statement stack, add a small row underneath. - // Consecutive statement stacks are also separated by a small divider. - steps.push(Blockly.BlockSvg.TOP_RIGHT_CORNER); - steps.push('v', Blockly.BlockSvg.EXTRA_STATEMENT_ROW_Y - 2 * Blockly.BlockSvg.CORNER_RADIUS); - cursorY += Blockly.BlockSvg.EXTRA_STATEMENT_ROW_Y; - } - } - cursorY += row.height; - } - this.drawEdgeShapeRight_(steps); - if (!inputRows.length) { - cursorY = Blockly.BlockSvg.MIN_BLOCK_Y; - steps.push('V', cursorY); - } - return cursorY; -}; - -/** - * Render the input shapes. - * If there's a connected block, hide the input shape. - * Otherwise, draw and set the position of the input shape. - * @param {!Blockly.Input} input Input to be rendered. - * @param {Number} x X offset of input. - * @param {Number} y Y offset of input. - */ -Blockly.BlockSvg.prototype.renderInputShape_ = function(input, x, y) { - var inputShape = input.outlinePath; - if (!inputShape) { - // No input shape for this input - e.g., the block is an insertion marker. - return; - } - // Input shapes are only visibly rendered on non-connected slots. - if (input.connection.targetConnection) { - inputShape.setAttribute('style', 'visibility: hidden'); - } else { - var inputShapeX = 0, inputShapeY = 0; - var inputShapeInfo = - Blockly.BlockSvg.getInputShapeInfo_(input.connection.getOutputShape()); - if (this.RTL) { - inputShapeX = -x - inputShapeInfo.width; - } else { - inputShapeX = x; - } - inputShapeY = y - (Blockly.BlockSvg.INPUT_SHAPE_HEIGHT / 2); - inputShape.setAttribute('d', inputShapeInfo.path); - inputShape.setAttribute('transform', - 'translate(' + inputShapeX + ',' + inputShapeY + ')'); - inputShape.setAttribute('data-argument-type', inputShapeInfo.argType); - inputShape.setAttribute('style', 'visibility: visible'); - } -}; - -/** - * Render the bottom edge of the block. - * @param {!Array.} steps Path of block outline. - * @param {number} cursorY Height of block. - * @private - */ -Blockly.BlockSvg.prototype.renderDrawBottom_ = function(steps, cursorY) { - this.height = cursorY; - if (!this.edgeShape_) { - steps.push(Blockly.BlockSvg.BOTTOM_RIGHT_CORNER); - } - if (this.nextConnection) { - // Move to the right-side of the notch. - var notchStart = ( - Blockly.BlockSvg.NOTCH_WIDTH + - Blockly.BlockSvg.NOTCH_START_PADDING + - Blockly.BlockSvg.CORNER_RADIUS - ); - steps.push('H', notchStart, ' '); - steps.push(Blockly.BlockSvg.NOTCH_PATH_RIGHT); - // Create next block connection. - var connectionX = this.RTL ? -Blockly.BlockSvg.NOTCH_WIDTH : - Blockly.BlockSvg.NOTCH_WIDTH; - this.nextConnection.setOffsetInBlock(connectionX, cursorY); - // Include height of notch in block height. - this.height += Blockly.BlockSvg.NOTCH_HEIGHT; - } - // Bottom horizontal line - if (!this.edgeShape_) { - steps.push('H', Blockly.BlockSvg.CORNER_RADIUS); - // Bottom left corner - steps.push(Blockly.BlockSvg.BOTTOM_LEFT_CORNER); - } else { - steps.push('H', this.edgeShapeWidth_); - } -}; - -/** - * Render the left edge of the block. - * @param {!Array.} steps Path of block outline. - * @param {number} cursorY Height of block. - * @private - */ -Blockly.BlockSvg.prototype.renderDrawLeft_ = function(steps) { - if (this.outputConnection) { - // Scratch-style reporters have output connection y at half block height. - this.outputConnection.setOffsetInBlock(0, this.height / 2); - } - if (this.edgeShape_) { - // Draw the left-side edge shape. - if (this.edgeShape_ === Blockly.OUTPUT_SHAPE_ROUND) { - // Draw a rounded arc. - steps.push('a ' + this.edgeShapeWidth_ + ' ' + this.edgeShapeWidth_ + ' 0 0 1 0 -' + this.edgeShapeWidth_ * 2); - } else if (this.edgeShape_ === Blockly.OUTPUT_SHAPE_HEXAGONAL) { - // Draw a half-hexagon. - steps.push('l ' + -this.edgeShapeWidth_ + ' ' + -this.edgeShapeWidth_ + - ' l ' + this.edgeShapeWidth_ + ' ' + -this.edgeShapeWidth_); - } - } - steps.push('z'); -}; - -/** - * Draw the edge shape (rounded or hexagonal) on the right side of a block with - * an output. - * @param {!Array.} steps Path of block outline. - * @private - */ -Blockly.BlockSvg.prototype.drawEdgeShapeRight_ = function(steps) { - if (this.edgeShape_) { - // Draw the right-side edge shape. - if (this.edgeShape_ === Blockly.OUTPUT_SHAPE_ROUND) { - // Draw a rounded arc. - steps.push('a ' + this.edgeShapeWidth_ + ' ' + this.edgeShapeWidth_ + - ' 0 0 1 0 ' + this.edgeShapeWidth_ * 2); - } else if (this.edgeShape_ === Blockly.OUTPUT_SHAPE_HEXAGONAL) { - // Draw an half-hexagon. - steps.push('l ' + this.edgeShapeWidth_ + ' ' + this.edgeShapeWidth_ + - ' l ' + -this.edgeShapeWidth_ + ' ' + this.edgeShapeWidth_); - } - } -}; - -/** - * Position an new block correctly, so that it doesn't move the existing block - * when connected to it. - * @param {!Blockly.Block} newBlock The block to position - either the first - * block in a dragged stack or an insertion marker. - * @param {!Blockly.Connection} newConnection The connection on the new block's - * stack - either a connection on newBlock, or the last NEXT_STATEMENT - * connection on the stack if the stack's being dropped before another - * block. - * @param {!Blockly.Connection} existingConnection The connection on the - * existing block, which newBlock should line up with. - */ -Blockly.BlockSvg.prototype.positionNewBlock = function(newBlock, newConnection, - existingConnection) { - // We only need to position the new block if it's before the existing one, - // otherwise its position is set by the previous block. - if (newConnection.type == Blockly.NEXT_STATEMENT) { - var dx = existingConnection.x_ - newConnection.x_; - var dy = existingConnection.y_ - newConnection.y_; - - newBlock.moveBy(dx, dy); - } -}; - -/** - * Draw the outline of a statement input, starting at the top right corner. - * @param {!Array.} steps Path of block outline. - * @param {number} cursorX The x position of the start of the notch at the top - * of the input. - * @param {number} rightEdge The far right edge of the block, which determines - * how wide the statement input is. - * @param {!Array.} row An object containing information about the - * current row, including its height and whether it should have a notch at - * the bottom. - * @private - */ -Blockly.BlockSvg.drawStatementInputFromTopRight_ = function(steps, cursorX, - rightEdge, row) { - Blockly.BlockSvg.drawStatementInputTop_(steps, cursorX); - steps.push('v', row.height - 2 * Blockly.BlockSvg.CORNER_RADIUS); - Blockly.BlockSvg.drawStatementInputBottom_(steps, rightEdge, row); -}; - -/** - * Draw the top of the outline of a statement input, starting at the top right - * corner. - * @param {!Array.} steps Path of block outline. - * @param {number} cursorX The x position of the start of the notch at the top - * of the input. - * @private - */ -Blockly.BlockSvg.drawStatementInputTop_ = function(steps, cursorX) { - steps.push(Blockly.BlockSvg.BOTTOM_RIGHT_CORNER); - steps.push('H', cursorX + Blockly.BlockSvg.STATEMENT_INPUT_INNER_SPACE + - 2 * Blockly.BlockSvg.CORNER_RADIUS); - steps.push(Blockly.BlockSvg.NOTCH_PATH_RIGHT); - steps.push('h', '-' + Blockly.BlockSvg.STATEMENT_INPUT_INNER_SPACE); - steps.push(Blockly.BlockSvg.INNER_TOP_LEFT_CORNER); -}; - -/** - * Draw the bottom of the outline of a statement input, starting at the inner - * left corner. - * @param {!Array.} steps Path of block outline. - * @param {number} rightEdge The far right edge of the block, which determines - * how wide the statement input is. - * @param {!Array.} row An object containing information about the - * current row, including its height and whether it should have a notch at - * the bottom. - * @private - */ -Blockly.BlockSvg.drawStatementInputBottom_ = function(steps, rightEdge, row) { - steps.push(Blockly.BlockSvg.INNER_BOTTOM_LEFT_CORNER); - if (row.statementNotchAtBottom) { - steps.push('h ', Blockly.BlockSvg.STATEMENT_INPUT_INNER_SPACE); - steps.push(Blockly.BlockSvg.NOTCH_PATH_LEFT); - } - steps.push('H', rightEdge - Blockly.BlockSvg.CORNER_RADIUS); -}; - -/** - * Render part of the hat and the right side of the define block to fully wrap - * the connected statement block. - * Scratch-specific. - * @param {!Array.} steps Path of block outline. - * @param {!Array.>} inputRows 2D array of objects, each - * containing position information. - * @param {!Blockly.Input} input The input that is currently being rendered. - * @param {!Array.} row An object containing information about the - * current row, including its height and whether it should have a notch at - * the bottom. - * @param {number} cursorY The y position of the start of this row. Used to - * position the following dummy input's fields. - * @private - */ -Blockly.BlockSvg.prototype.renderDefineBlock_ = function(steps, inputRows, - input, row, cursorY) { - // Following text shows up as a dummy input after the statement input, which - // we are forcing to stay inline with the statement input instead of letting - // it drop to a new line. - var hasFollowingText = row.length == 2; - - // Figure out where the right side of the block is. - var rightSide = inputRows.rightEdge; - if (input.connection && input.connection.targetBlock()) { - rightSide = inputRows.statementEdge + - input.connection.targetBlock().getHeightWidth().width + - Blockly.BlockSvg.DEFINE_BLOCK_PADDING_RIGHT; - } else { - // Handles the case where block is being rendered as an insertion marker - rightSide = Math.max(Blockly.BlockSvg.MIN_BLOCK_X_WITH_STATEMENT, rightSide) - + Blockly.BlockSvg.DEFINE_BLOCK_PADDING_RIGHT; - } - rightSide -= Blockly.BlockSvg.DEFINE_HAT_CORNER_RADIUS; - - if (hasFollowingText) { - var followingTextInput = row[1]; - var fieldStart = rightSide + 3 * Blockly.BlockSvg.SEP_SPACE_X; - rightSide += followingTextInput.fieldRow[0].getSize().width; - rightSide += 2 * Blockly.BlockSvg.SEP_SPACE_X; - - // Align fields vertically within the row. - // In renderFields_, the field is further centered by its own height. - // The dummy input's fields did not get laid out normally because we're - // forcing them to stay inline with a statement input. - var fieldY = cursorY; - fieldY += Blockly.BlockSvg.MIN_STATEMENT_INPUT_HEIGHT; - this.renderFields_(followingTextInput.fieldRow, fieldStart, fieldY); - } - // Draw the top and the right corner of the hat. - steps.push('H', rightSide); - steps.push(Blockly.BlockSvg.TOP_RIGHT_CORNER_DEFINE_HAT); - row.height += 3 * Blockly.BlockSvg.GRID_UNIT; - // Draw the right side of the block around the statement input. - steps.push('v', row.height); - // row.height will be used to update the cursor in the calling function. - row.height += Blockly.BlockSvg.GRID_UNIT; - -}; - -/** - * Get some information about the input shape to draw, based on the type of the - * connection. - * @param {number} shape An enum representing the shape of the connection we're - * drawing around. - * @return {!Object} An object containing an SVG path, a string representation - * of the argument type, and a width. - * @private - */ -Blockly.BlockSvg.getInputShapeInfo_ = function(shape) { - var inputShapePath = null; - var inputShapeArgType = null; - var inputShapeWidth = 0; - - switch (shape) { - case Blockly.OUTPUT_SHAPE_HEXAGONAL: - inputShapePath = Blockly.BlockSvg.INPUT_SHAPE_HEXAGONAL; - inputShapeWidth = Blockly.BlockSvg.INPUT_SHAPE_HEXAGONAL_WIDTH; - inputShapeArgType = 'boolean'; - break; - case Blockly.OUTPUT_SHAPE_ROUND: - inputShapePath = Blockly.BlockSvg.INPUT_SHAPE_ROUND; - inputShapeWidth = Blockly.BlockSvg.INPUT_SHAPE_ROUND_WIDTH; - inputShapeArgType = 'round'; - break; - case Blockly.OUTPUT_SHAPE_SQUARE: - default: // If the input connection is not connected, draw a hole shape. - inputShapePath = Blockly.BlockSvg.INPUT_SHAPE_SQUARE; - inputShapeWidth = Blockly.BlockSvg.INPUT_SHAPE_SQUARE_WIDTH; - inputShapeArgType = 'square'; - break; - } - return { - path: inputShapePath, - argType: inputShapeArgType, - width: inputShapeWidth - }; -}; - -/** - * Get the correct cursor position for the given input, based on alignment, - * the total size of the block, and the size of the input. - * @param {number} cursorX The minimum x value of the cursor. - * @param {!Blockly.Input} input The input to align the fields for. - * @param {number} rightEdge The maximum width of the block. Right-aligned - * fields are positioned based on this number. - * @return {number} The new cursor position. - * @private - */ -Blockly.BlockSvg.getAlignedCursor_ = function(cursorX, input, rightEdge) { - // Align inline field rows (left/right/centre). - if (input.align === Blockly.ALIGN_RIGHT) { - cursorX += rightEdge - input.fieldWidth - - (2 * Blockly.BlockSvg.SEP_SPACE_X); - } else if (input.align === Blockly.ALIGN_CENTRE) { - cursorX = Math.max(cursorX, rightEdge / 2 - input.fieldWidth / 2); - } - return cursorX; -}; - -/** - * Update all of the connections on this block with the new locaitons calculated - * in renderCompute, and move all of the connected blocks based on the new - * connection locations. - * @private - */ -Blockly.BlockSvg.prototype.renderMoveConnections_ = function() { - var blockTL = this.getRelativeToSurfaceXY(); - // Don't tighten previous or output connections because they are inferior. - if (this.previousConnection) { - this.previousConnection.moveToOffset(blockTL); - } - if (this.outputConnection) { - this.outputConnection.moveToOffset(blockTL); - } - - for (var i = 0; i < this.inputList.length; i++) { - var conn = this.inputList[i].connection; - if (conn) { - conn.moveToOffset(blockTL); - if (conn.isConnected()) { - conn.tighten_(); - } - } - } - - if (this.nextConnection) { - this.nextConnection.moveToOffset(blockTL); - if (this.nextConnection.isConnected()) { - this.nextConnection.tighten_(); - } - } -}; diff --git a/core/block_svg.js b/core/block_svg.js deleted file mode 100644 index 6e24dbe86f..0000000000 --- a/core/block_svg.js +++ /dev/null @@ -1,1316 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Methods for graphically rendering a block as SVG. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.BlockSvg'); - -goog.require('Blockly.Block'); -goog.require('Blockly.BlockAnimations'); -goog.require('Blockly.ContextMenu'); -goog.require('Blockly.Events.Ui'); -goog.require('Blockly.Events.BlockMove'); -goog.require('Blockly.Grid'); -goog.require('Blockly.RenderedConnection'); -goog.require('Blockly.scratchBlocksUtils'); -goog.require('Blockly.Tooltip'); -goog.require('Blockly.Touch'); -goog.require('Blockly.utils'); - -goog.require('goog.Timer'); -goog.require('goog.asserts'); -goog.require('goog.dom'); -goog.require('goog.math.Coordinate'); - - -/** - * Class for a block's SVG representation. - * Not normally called directly, workspace.newBlock() is preferred. - * @param {!Blockly.Workspace} workspace The block's workspace. - * @param {?string} prototypeName Name of the language object containing - * type-specific functions for this block. - * @param {string=} opt_id Optional ID. Use this ID if provided, otherwise - * create a new ID. If the ID conflicts with an in-use ID, a new one will - * be generated. - * @extends {Blockly.Block} - * @constructor - */ -Blockly.BlockSvg = function(workspace, prototypeName, opt_id) { - // Create core elements for the block. - /** - * @type {SVGElement} - * @private - */ - this.svgGroup_ = Blockly.utils.createSvgElement('g', {}, null); - /** @type {SVGElement} */ - this.svgPath_ = Blockly.utils.createSvgElement('path', - {'class': 'blocklyPath blocklyBlockBackground'}, - this.svgGroup_); - this.svgPath_.tooltip = this; - - /** @type {boolean} */ - this.rendered = false; - - /** - * Whether to move the block to the drag surface when it is dragged. - * True if it should move, false if it should be translated directly. - * @type {boolean} - * @private - */ - this.useDragSurface_ = Blockly.utils.is3dSupported() && !!workspace.blockDragSurface_; - - Blockly.Tooltip.bindMouseEvents(this.svgPath_); - Blockly.BlockSvg.superClass_.constructor.call(this, - workspace, prototypeName, opt_id); - - // Expose this block's ID on its top-level SVG group. - if (this.svgGroup_.dataset) { - this.svgGroup_.dataset.id = this.id; - } -}; -goog.inherits(Blockly.BlockSvg, Blockly.Block); - -/** - * Height of this block, not including any statement blocks above or below. - * Height is in workspace units. - */ -Blockly.BlockSvg.prototype.height = 0; - -/** - * Width of this block, including any connected value blocks. - * Width is in workspace units. - */ -Blockly.BlockSvg.prototype.width = 0; - -/** - * Minimum width of block if insertion marker; comes from inserting block. - * @type {number} - */ -Blockly.BlockSvg.prototype.insertionMarkerMinWidth_ = 0; - -/** - * Opacity of this block between 0 and 1. - * @type {number} - * @private - */ -Blockly.BlockSvg.prototype.opacity_ = 1; - -/** - * Original location of block being dragged. - * @type {goog.math.Coordinate} - * @private - */ -Blockly.BlockSvg.prototype.dragStartXY_ = null; - -/** - * Whether the block glows as if running. - * @type {boolean} - * @private - */ -Blockly.BlockSvg.prototype.isGlowingBlock_ = false; - -/** - * Whether the block's whole stack glows as if running. - * @type {boolean} - * @private - */ -Blockly.BlockSvg.prototype.isGlowingStack_ = false; - -/** - * Constant for identifying rows that are to be rendered inline. - * Don't collide with Blockly.INPUT_VALUE and friends. - * @const - */ -Blockly.BlockSvg.INLINE = -1; - -/** - * Create and initialize the SVG representation of the block. - * May be called more than once. - */ -Blockly.BlockSvg.prototype.initSvg = function() { - goog.asserts.assert(this.workspace.rendered, 'Workspace is headless.'); - if (!this.isInsertionMarker()) { // Insertion markers not allowed to have inputs or icons - // Input shapes are empty holes drawn when a value input is not connected. - for (var i = 0, input; input = this.inputList[i]; i++) { - input.init(); - input.initOutlinePath(this.svgGroup_); - } - var icons = this.getIcons(); - for (i = 0; i < icons.length; i++) { - icons[i].createIcon(); - } - } - this.updateColour(); - this.updateMovable(); - if (!this.workspace.options.readOnly && !this.eventsInit_) { - Blockly.bindEventWithChecks_( - this.getSvgRoot(), 'mousedown', this, this.onMouseDown_); - } - this.eventsInit_ = true; - - if (!this.getSvgRoot().parentNode) { - this.workspace.getCanvas().appendChild(this.getSvgRoot()); - } -}; - -/** - * Select this block. Highlight it visually. - */ -Blockly.BlockSvg.prototype.select = function() { - if (this.isShadow() && this.getParent()) { - // Shadow blocks should not be selected. - this.getParent().select(); - return; - } - if (Blockly.selected == this) { - return; - } - var oldId = null; - if (Blockly.selected) { - oldId = Blockly.selected.id; - // Unselect any previously selected block. - Blockly.Events.disable(); - try { - Blockly.selected.unselect(); - } finally { - Blockly.Events.enable(); - } - } - var event = new Blockly.Events.Ui(null, 'selected', oldId, this.id); - event.workspaceId = this.workspace.id; - Blockly.Events.fire(event); - Blockly.selected = this; - this.addSelect(); -}; - -/** - * Unselect this block. Remove its highlighting. - */ -Blockly.BlockSvg.prototype.unselect = function() { - if (Blockly.selected != this) { - return; - } - var event = new Blockly.Events.Ui(null, 'selected', this.id, null); - event.workspaceId = this.workspace.id; - Blockly.Events.fire(event); - Blockly.selected = null; - this.removeSelect(); -}; - -/** - * Glow only this particular block, to highlight it visually as if it's running. - * @param {boolean} isGlowingBlock Whether the block should glow. - */ -Blockly.BlockSvg.prototype.setGlowBlock = function(isGlowingBlock) { - this.isGlowingBlock_ = isGlowingBlock; - this.updateColour(); -}; - -/** - * Glow the stack starting with this block, to highlight it visually as if it's running. - * @param {boolean} isGlowingStack Whether the stack starting with this block should glow. - */ -Blockly.BlockSvg.prototype.setGlowStack = function(isGlowingStack) { - this.isGlowingStack_ = isGlowingStack; - // Update the applied SVG filter if the property has changed - var svg = this.getSvgRoot(); - if (this.isGlowingStack_ && !svg.hasAttribute('filter')) { - var stackGlowFilterId = this.workspace.options.stackGlowFilterId || 'blocklyStackGlowFilter'; - svg.setAttribute('filter', 'url(#' + stackGlowFilterId + ')'); - } else if (!this.isGlowingStack_ && svg.hasAttribute('filter')) { - svg.removeAttribute('filter'); - } -}; - -/** - * Block's mutator icon (if any). - * @type {Blockly.Mutator} - */ -Blockly.BlockSvg.prototype.mutator = null; - -/** - * Block's comment icon (if any). - * @type {Blockly.Comment} - */ -Blockly.BlockSvg.prototype.comment = null; - -/** - * Block's warning icon (if any). - * @type {Blockly.Warning} - */ -Blockly.BlockSvg.prototype.warning = null; - -/** - * Returns a list of mutator, comment, and warning icons. - * @return {!Array} List of icons. - */ -Blockly.BlockSvg.prototype.getIcons = function() { - var icons = []; - if (this.mutator) { - icons.push(this.mutator); - } - if (this.comment) { - icons.push(this.comment); - } - if (this.warning) { - icons.push(this.warning); - } - return icons; -}; - -/** - * Set parent of this block to be a new block or null. - * @param {Blockly.BlockSvg} newParent New parent block. - */ -Blockly.BlockSvg.prototype.setParent = function(newParent) { - var oldParent = this.parentBlock_; - if (newParent == oldParent) { - return; - } - Blockly.Field.startCache(); - Blockly.BlockSvg.superClass_.setParent.call(this, newParent); - Blockly.Field.stopCache(); - - var svgRoot = this.getSvgRoot(); - - // Bail early if workspace is clearing, or we aren't rendered. - // We won't need to reattach ourselves anywhere. - if (this.workspace.isClearing || !svgRoot) { - return; - } - - var oldXY = this.getRelativeToSurfaceXY(); - if (newParent) { - newParent.getSvgRoot().appendChild(svgRoot); - var newXY = this.getRelativeToSurfaceXY(); - // Move the connections to match the child's new position. - this.moveConnections_(newXY.x - oldXY.x, newXY.y - oldXY.y); - // If we are a shadow block, inherit tertiary colour. - if (this.isShadow()) { - this.setColour(this.getColour(), this.getColourSecondary(), - newParent.getColourTertiary(), this.getColourQuaternary()); - } - } - // If we are losing a parent, we want to move our DOM element to the - // root of the workspace. - else if (oldParent) { - this.workspace.getCanvas().appendChild(svgRoot); - this.translate(oldXY.x, oldXY.y); - } - -}; - -/** - * Return the coordinates of the top-left corner of this block relative to the - * drawing surface's origin (0,0), in workspace units. - * If the block is on the workspace, (0, 0) is the origin of the workspace - * coordinate system. - * This does not change with workspace scale. - * @return {!goog.math.Coordinate} Object with .x and .y properties in - * workspace coordinates. - */ -Blockly.BlockSvg.prototype.getRelativeToSurfaceXY = function() { - // The drawing surface is relative to either the workspace canvas - // or to the drag surface group. - var x = 0; - var y = 0; - - var dragSurfaceGroup = this.useDragSurface_ ? - this.workspace.blockDragSurface_.getGroup() : null; - - var element = this.getSvgRoot(); - if (element) { - do { - // Loop through this block and every parent. - var xy = Blockly.utils.getRelativeXY(element); - x += xy.x; - y += xy.y; - // If this element is the current element on the drag surface, include - // the translation of the drag surface itself. - if (this.useDragSurface_ && - this.workspace.blockDragSurface_.getCurrentBlock() == element) { - var surfaceTranslation = this.workspace.blockDragSurface_.getSurfaceTranslation(); - x += surfaceTranslation.x; - y += surfaceTranslation.y; - } - element = element.parentNode; - } while (element && element != this.workspace.getCanvas() && - element != dragSurfaceGroup); - } - return new goog.math.Coordinate(x, y); -}; - -/** - * Move a block by a relative offset. - * @param {number} dx Horizontal offset in workspace units. - * @param {number} dy Vertical offset in workspace units. - */ -Blockly.BlockSvg.prototype.moveBy = function(dx, dy) { - goog.asserts.assert(!this.parentBlock_, 'Block has parent.'); - var eventsEnabled = Blockly.Events.isEnabled(); - if (eventsEnabled) { - var event = new Blockly.Events.BlockMove(this); - } - var xy = this.getRelativeToSurfaceXY(); - this.translate(xy.x + dx, xy.y + dy); - this.moveConnections_(dx, dy); - if (eventsEnabled) { - event.recordNew(); - Blockly.Events.fire(event); - } - this.workspace.resizeContents(); -}; - -/** - * Transforms a block by setting the translation on the transform attribute - * of the block's SVG. - * @param {number} x The x coordinate of the translation in workspace units. - * @param {number} y The y coordinate of the translation in workspace units. - */ -Blockly.BlockSvg.prototype.translate = function(x, y) { - this.getSvgRoot().setAttribute('transform', - 'translate(' + x + ',' + y + ')'); -}; - -/** - * Move this block to its workspace's drag surface, accounting for positioning. - * Generally should be called at the same time as setDragging_(true). - * Does nothing if useDragSurface_ is false. - * @private - */ -Blockly.BlockSvg.prototype.moveToDragSurface_ = function() { - if (!this.useDragSurface_) { - return; - } - // The translation for drag surface blocks, - // is equal to the current relative-to-surface position, - // to keep the position in sync as it move on/off the surface. - // This is in workspace coordinates. - var xy = this.getRelativeToSurfaceXY(); - this.clearTransformAttributes_(); - this.workspace.blockDragSurface_.translateSurface(xy.x, xy.y); - // Execute the move on the top-level SVG component - this.workspace.blockDragSurface_.setBlocksAndShow(this.getSvgRoot()); -}; - -/** - * Move this block back to the workspace block canvas. - * Generally should be called at the same time as setDragging_(false). - * Does nothing if useDragSurface_ is false. - * @param {!goog.math.Coordinate} newXY The position the block should take on - * on the workspace canvas, in workspace coordinates. - * @private - */ -Blockly.BlockSvg.prototype.moveOffDragSurface_ = function(newXY) { - if (!this.useDragSurface_) { - return; - } - // Translate to current position, turning off 3d. - this.translate(newXY.x, newXY.y); - this.workspace.blockDragSurface_.clearAndHide(this.workspace.getCanvas()); -}; - -/** - * Move this block during a drag, taking into account whether we are using a - * drag surface to translate blocks. - * This block must be a top-level block. - * @param {!goog.math.Coordinate} newLoc The location to translate to, in - * workspace coordinates. - * @package - */ -Blockly.BlockSvg.prototype.moveDuringDrag = function(newLoc) { - if (this.useDragSurface_) { - this.workspace.blockDragSurface_.translateSurface(newLoc.x, newLoc.y); - } else { - this.svgGroup_.translate_ = 'translate(' + newLoc.x + ',' + newLoc.y + ')'; - this.svgGroup_.setAttribute('transform', - this.svgGroup_.translate_ + this.svgGroup_.skew_); - } -}; - -/** - * Clear the block of transform="..." attributes. - * Used when the block is switching from 3d to 2d transform or vice versa. - * @private - */ -Blockly.BlockSvg.prototype.clearTransformAttributes_ = function() { - Blockly.utils.removeAttribute(this.getSvgRoot(), 'transform'); -}; - -/** - * Snap this block to the nearest grid point. - */ -Blockly.BlockSvg.prototype.snapToGrid = function() { - if (!this.workspace) { - return; // Deleted block. - } - if (this.workspace.isDragging()) { - return; // Don't bump blocks during a drag. - } - if (this.getParent()) { - return; // Only snap top-level blocks. - } - if (this.isInFlyout) { - return; // Don't move blocks around in a flyout. - } - var grid = this.workspace.getGrid(); - if (!grid || !grid.shouldSnap()) { - return; // Config says no snapping. - } - var spacing = grid.getSpacing(); - var half = spacing / 2; - var xy = this.getRelativeToSurfaceXY(); - var dx = Math.round((xy.x - half) / spacing) * spacing + half - xy.x; - var dy = Math.round((xy.y - half) / spacing) * spacing + half - xy.y; - dx = Math.round(dx); - dy = Math.round(dy); - if (dx != 0 || dy != 0) { - this.moveBy(dx, dy); - } -}; - -/** - * Returns the coordinates of a bounding box describing the dimensions of this - * block and any blocks stacked below it. - * Coordinate system: workspace coordinates. - * @return {!{topLeft: goog.math.Coordinate, bottomRight: goog.math.Coordinate}} - * Object with top left and bottom right coordinates of the bounding box. - */ -Blockly.BlockSvg.prototype.getBoundingRectangle = function() { - var blockXY = this.getRelativeToSurfaceXY(this); - var blockBounds = this.getHeightWidth(); - var topLeft; - var bottomRight; - if (this.RTL) { - topLeft = new goog.math.Coordinate(blockXY.x - blockBounds.width, - blockXY.y); - bottomRight = new goog.math.Coordinate(blockXY.x, - blockXY.y + blockBounds.height); - } else { - topLeft = new goog.math.Coordinate(blockXY.x, blockXY.y); - bottomRight = new goog.math.Coordinate(blockXY.x + blockBounds.width, - blockXY.y + blockBounds.height); - } - - return {topLeft: topLeft, bottomRight: bottomRight}; -}; - -/** - * Set block opacity for SVG rendering. - * @param {number} opacity Intended opacity, betweeen 0 and 1 - */ -Blockly.BlockSvg.prototype.setOpacity = function(opacity) { - this.opacity_ = opacity; - if (this.rendered) { - this.updateColour(); - } -}; - -/** - * Get block opacity for SVG rendering. - * @return {number} Intended opacity, betweeen 0 and 1 - */ -Blockly.BlockSvg.prototype.getOpacity = function() { - return this.opacity_; -}; - -/** - * Set whether the block is collapsed or not. - * @param {boolean} collapsed True if collapsed. - */ -Blockly.BlockSvg.prototype.setCollapsed = function(collapsed) { - if (this.collapsed_ == collapsed) { - return; - } - var renderList = []; - // Show/hide the inputs. - for (var i = 0, input; input = this.inputList[i]; i++) { - renderList.push.apply(renderList, input.setVisible(!collapsed)); - } - - var COLLAPSED_INPUT_NAME = '_TEMP_COLLAPSED_INPUT'; - if (collapsed) { - var icons = this.getIcons(); - for (var i = 0; i < icons.length; i++) { - icons[i].setVisible(false); - } - var text = this.toString(Blockly.COLLAPSE_CHARS); - this.appendDummyInput(COLLAPSED_INPUT_NAME).appendField(text).init(); - } else { - this.removeInput(COLLAPSED_INPUT_NAME); - // Clear any warnings inherited from enclosed blocks. - this.setWarningText(null); - } - Blockly.BlockSvg.superClass_.setCollapsed.call(this, collapsed); - - if (!renderList.length) { - // No child blocks, just render this block. - renderList[0] = this; - } - if (this.rendered) { - for (var i = 0, block; block = renderList[i]; i++) { - block.render(); - } - // Don't bump neighbours. - // Although bumping neighbours would make sense, users often collapse - // all their functions and store them next to each other. Expanding and - // bumping causes all their definitions to go out of alignment. - } -}; - -/** - * Open the next (or previous) FieldTextInput. - * @param {Blockly.Field|Blockly.Block} start Current location. - * @param {boolean} forward If true go forward, otherwise backward. - */ -Blockly.BlockSvg.prototype.tab = function(start, forward) { - var list = this.createTabList_(); - var i = list.indexOf(start); - if (i == -1) { - // No start location, start at the beginning or end. - i = forward ? -1 : list.length; - } - var target = list[forward ? i + 1 : i - 1]; - if (!target) { - // Ran off of list. - // If there is an output, tab up to that block. - var outputBlock = this.outputConnection && this.outputConnection.targetBlock(); - if (outputBlock) { - outputBlock.tab(this, forward); - } else { // Otherwise, go to next / previous block, depending on value of `forward` - var block = forward ? this.getNextBlock() : this.getPreviousBlock(); - if (block) { - block.tab(this, forward); - } - } - } else if (target instanceof Blockly.Field) { - target.showEditor_(); - } else { - target.tab(null, forward); - } -}; - -/** - * Create an ordered list of all text fields and connected inputs. - * @return {!Array.} The ordered list. - * @private - */ -Blockly.BlockSvg.prototype.createTabList_ = function() { - // This function need not be efficient since it runs once on a keypress. - var list = []; - for (var i = 0, input; input = this.inputList[i]; i++) { - for (var j = 0, field; field = input.fieldRow[j]; j++) { - if (field instanceof Blockly.FieldTextInput) { - // TODO(# 1276): Also support dropdown fields. - list.push(field); - } - } - if (input.connection) { - var block = input.connection.targetBlock(); - if (block) { - list.push(block); - } - } - } - return list; -}; - -/** - * Handle a mouse-down on an SVG block. - * @param {!Event} e Mouse down event or touch start event. - * @private - */ -Blockly.BlockSvg.prototype.onMouseDown_ = function(e) { - var gesture = this.workspace && this.workspace.getGesture(e); - if (gesture) { - gesture.handleBlockStart(e, this); - } -}; - -/** - * Load the block's help page in a new window. - * @private - */ -Blockly.BlockSvg.prototype.showHelp_ = function() { - var url = goog.isFunction(this.helpUrl) ? this.helpUrl() : this.helpUrl; - if (url) { - // @todo rewrite - alert(url); - } -}; - - -/** - * Show the context menu for this block. - * @param {!Event} e Mouse event. - * @private - */ -Blockly.BlockSvg.prototype.showContextMenu_ = function(e) { - if (this.workspace.options.readOnly || !this.contextMenu) { - return; - } - // Save the current block in a variable for use in closures. - var block = this; - var menuOptions = []; - if (this.isDeletable() && this.isMovable() && !block.isInFlyout) { - menuOptions.push( - Blockly.ContextMenu.blockDuplicateOption(block, e)); - if (this.isEditable() && this.workspace.options.comments) { - menuOptions.push(Blockly.ContextMenu.blockCommentOption(block)); - } - menuOptions.push(Blockly.ContextMenu.blockDeleteOption(block)); - } else if (this.parentBlock_ && this.isShadow_) { - this.parentBlock_.showContextMenu_(e); - return; - } - - // Allow the block to add or modify menuOptions. - if (this.customContextMenu) { - this.customContextMenu(menuOptions); - } - Blockly.ContextMenu.show(e, menuOptions, this.RTL); - Blockly.ContextMenu.currentBlock = this; -}; - -/** - * Move the connections for this block and all blocks attached under it. - * Also update any attached bubbles. - * @param {number} dx Horizontal offset from current location, in workspace - * units. - * @param {number} dy Vertical offset from current location, in workspace - * units. - * @private - */ -Blockly.BlockSvg.prototype.moveConnections_ = function(dx, dy) { - if (!this.rendered) { - // Rendering is required to lay out the blocks. - // This is probably an invisible block attached to a collapsed block. - return; - } - var myConnections = this.getConnections_(false); - for (var i = 0; i < myConnections.length; i++) { - myConnections[i].moveBy(dx, dy); - } - var icons = this.getIcons(); - for (i = 0; i < icons.length; i++) { - icons[i].computeIconLocation(); - } - - // Recurse through all blocks attached under this one. - for (i = 0; i < this.childBlocks_.length; i++) { - this.childBlocks_[i].moveConnections_(dx, dy); - } -}; - -/** - * Recursively adds or removes the dragging class to this node and its children. - * @param {boolean} adding True if adding, false if removing. - * @package - */ -Blockly.BlockSvg.prototype.setDragging = function(adding) { - if (adding) { - var group = this.getSvgRoot(); - group.translate_ = ''; - group.skew_ = ''; - Blockly.draggingConnections_ = - Blockly.draggingConnections_.concat(this.getConnections_(true)); - Blockly.utils.addClass( - /** @type {!Element} */ (this.svgGroup_), 'blocklyDragging'); - } else { - Blockly.draggingConnections_ = []; - Blockly.utils.removeClass( - /** @type {!Element} */ (this.svgGroup_), 'blocklyDragging'); - } - // Recurse through all blocks attached under this one. - for (var i = 0; i < this.childBlocks_.length; i++) { - this.childBlocks_[i].setDragging(adding); - } -}; - -/** - * Add or remove the UI indicating if this block is movable or not. - */ -Blockly.BlockSvg.prototype.updateMovable = function() { - if (this.isMovable()) { - Blockly.utils.addClass( - /** @type {!Element} */ (this.svgGroup_), 'blocklyDraggable'); - } else { - Blockly.utils.removeClass( - /** @type {!Element} */ (this.svgGroup_), 'blocklyDraggable'); - } -}; - -/** - * Set whether this block is movable or not. - * @param {boolean} movable True if movable. - */ -Blockly.BlockSvg.prototype.setMovable = function(movable) { - Blockly.BlockSvg.superClass_.setMovable.call(this, movable); - this.updateMovable(); -}; - -/** - * Set whether this block is editable or not. - * @param {boolean} editable True if editable. - */ -Blockly.BlockSvg.prototype.setEditable = function(editable) { - Blockly.BlockSvg.superClass_.setEditable.call(this, editable); - var icons = this.getIcons(); - for (var i = 0; i < icons.length; i++) { - icons[i].updateEditable(); - } -}; - -/** - * Set whether this block is a shadow block or not. - * @param {boolean} shadow True if a shadow. - */ -Blockly.BlockSvg.prototype.setShadow = function(shadow) { - Blockly.BlockSvg.superClass_.setShadow.call(this, shadow); - this.updateColour(); -}; - -/** - * Set whether this block is an insertion marker block or not. - * @param {boolean} insertionMarker True if an insertion marker. - * @param {Number=} opt_minWidth Optional minimum width of the marker. - */ -Blockly.BlockSvg.prototype.setInsertionMarker = function(insertionMarker, opt_minWidth) { - Blockly.BlockSvg.superClass_.setInsertionMarker.call(this, insertionMarker); - this.insertionMarkerMinWidth_ = opt_minWidth; - this.updateColour(); -}; - -/** - * Return the root node of the SVG or null if none exists. - * @return {Element} The root SVG node (probably a group). - */ -Blockly.BlockSvg.prototype.getSvgRoot = function() { - return this.svgGroup_; -}; - -/** - * Dispose of this block. - * @param {boolean} healStack If true, then try to heal any gap by connecting - * the next statement with the previous statement. Otherwise, dispose of - * all children of this block. - * @param {boolean} animate If true, show a disposal animation and sound. - */ -Blockly.BlockSvg.prototype.dispose = function(healStack, animate) { - if (!this.workspace) { - // The block has already been deleted. - return; - } - Blockly.Tooltip.hide(); - Blockly.Field.startCache(); - // Save the block's workspace temporarily so we can resize the - // contents once the block is disposed. - var blockWorkspace = this.workspace; - // If this block is being dragged, unlink the mouse events. - if (Blockly.selected == this) { - this.unselect(); - this.workspace.cancelCurrentGesture(); - } - // If this block has a context menu open, close it. - if (Blockly.ContextMenu.currentBlock == this) { - Blockly.ContextMenu.hide(); - } - - if (animate && this.rendered) { - this.unplug(healStack); - Blockly.BlockAnimations.disposeUiEffect(this); - } - // Stop rerendering. - this.rendered = false; - - Blockly.Events.disable(); - try { - var icons = this.getIcons(); - for (var i = 0; i < icons.length; i++) { - icons[i].dispose(); - } - } finally { - Blockly.Events.enable(); - } - Blockly.BlockSvg.superClass_.dispose.call(this, healStack); - - goog.dom.removeNode(this.svgGroup_); - blockWorkspace.resizeContents(); - // Sever JavaScript to DOM connections. - this.svgGroup_ = null; - this.svgPath_ = null; - Blockly.Field.stopCache(); -}; - -/** - * Enable or disable a block. - */ -Blockly.BlockSvg.prototype.updateDisabled = function() { - // not supported -}; - -/** - * Returns the comment on this block (or '' if none). - * @return {string} Block's comment. - */ -Blockly.BlockSvg.prototype.getCommentText = function() { - if (this.comment) { - var comment = this.comment.getText(); - // Trim off trailing whitespace. - return comment.replace(/\s+$/, '').replace(/ +\n/g, '\n'); - } - return ''; -}; - -/** - * Set this block's comment text. - * @param {?string} text The text, or null to delete. - * @param {string=} commentId Id of the comment, or a new one will be generated if not provided. - * @param {number=} commentX Optional x position for scratch comment in workspace coordinates - * @param {number=} commentY Optional y position for scratch comment in workspace coordinates - * @param {boolean=} minimized Optional minimized state for scratch comment, defaults to false - */ -Blockly.BlockSvg.prototype.setCommentText = function(text, commentId, - commentX, commentY, minimized) { - var changedState = false; - if (goog.isString(text)) { - if (!this.comment) { - this.comment = new Blockly.ScratchBlockComment(this, text, commentId, - commentX, commentY, minimized); - changedState = true; - } else { - this.comment.setText(/** @type {string} */ (text)); - } - } else { - if (this.comment) { - this.comment.dispose(); - changedState = true; - } - } - if (changedState && this.rendered) { - this.render(); - if (goog.isString(text)) { - this.comment.setVisible(true); - } - // Adding or removing a comment icon will cause the block to change shape. - this.bumpNeighbours_(); - } -}; - -/** - * Set this block's warning text. - * @param {?string} text The text, or null to delete. - * @param {string=} opt_id An optional ID for the warning text to be able to - * maintain multiple warnings. - */ -Blockly.BlockSvg.prototype.setWarningText = function(text, opt_id) { - if (!this.setWarningText.pid_) { - // Create a database of warning PIDs. - // Only runs once per block (and only those with warnings). - this.setWarningText.pid_ = Object.create(null); - } - var id = opt_id || ''; - if (!id) { - // Kill all previous pending processes, this edit supersedes them all. - for (var n in this.setWarningText.pid_) { - clearTimeout(this.setWarningText.pid_[n]); - delete this.setWarningText.pid_[n]; - } - } else if (this.setWarningText.pid_[id]) { - // Only queue up the latest change. Kill any earlier pending process. - clearTimeout(this.setWarningText.pid_[id]); - delete this.setWarningText.pid_[id]; - } - if (this.workspace.isDragging()) { - // Don't change the warning text during a drag. - // Wait until the drag finishes. - var thisBlock = this; - this.setWarningText.pid_[id] = setTimeout(function() { - if (thisBlock.workspace) { // Check block wasn't deleted. - delete thisBlock.setWarningText.pid_[id]; - thisBlock.setWarningText(text, id); - } - }, 100); - return; - } - if (this.isInFlyout) { - text = null; - } - - var changedState = false; - if (goog.isString(text)) { - if (!this.warning) { - this.warning = new Blockly.Warning(this); - changedState = true; - } - this.warning.setText(/** @type {string} */ (text), id); - } else { - // Dispose all warnings if no ID is given. - if (this.warning && !id) { - this.warning.dispose(); - changedState = true; - } else if (this.warning) { - var oldText = this.warning.getText(); - this.warning.setText('', id); - var newText = this.warning.getText(); - if (!newText) { - this.warning.dispose(); - } - changedState = oldText != newText; - } - } - if (changedState && this.rendered) { - this.render(); - // Adding or removing a warning icon will cause the block to change shape. - this.bumpNeighbours_(); - } -}; - -/** - * Give this block a mutator dialog. - * @param {Blockly.Mutator} mutator A mutator dialog instance or null to remove. - */ -Blockly.BlockSvg.prototype.setMutator = function(mutator) { - if (this.mutator && this.mutator !== mutator) { - this.mutator.dispose(); - } - if (mutator) { - mutator.block_ = this; - this.mutator = mutator; - mutator.createIcon(); - } -}; - -/** - * Select this block. Highlight it visually. - */ -Blockly.BlockSvg.prototype.addSelect = function() { - Blockly.utils.addClass( - /** @type {!Element} */ (this.svgGroup_), 'blocklySelected'); -}; - -/** - * Unselect this block. Remove its highlighting. - */ -Blockly.BlockSvg.prototype.removeSelect = function() { - Blockly.utils.removeClass( - /** @type {!Element} */ (this.svgGroup_), 'blocklySelected'); -}; - -/** - * Update the cursor over this block by adding or removing a class. - * @param {boolean} letMouseThrough True if the blocks should ignore pointer - * events, false otherwise. - * @package - */ -Blockly.BlockSvg.prototype.setMouseThroughStyle = function(letMouseThrough) { - if (letMouseThrough) { - Blockly.utils.addClass(/** @type {!Element} */ (this.svgGroup_), - 'blocklyDraggingMouseThrough'); - } else { - Blockly.utils.removeClass(/** @type {!Element} */ (this.svgGroup_), - 'blocklyDraggingMouseThrough'); - } -}; - -/** - * Update the cursor over this block by adding or removing a class. - * @param {boolean} enable True if the delete cursor should be shown, false - * otherwise. - * @package - */ -Blockly.BlockSvg.prototype.setDeleteStyle = function(enable) { - if (enable) { - Blockly.utils.addClass(/** @type {!Element} */ (this.svgGroup_), - 'blocklyDraggingDelete'); - } else { - Blockly.utils.removeClass(/** @type {!Element} */ (this.svgGroup_), - 'blocklyDraggingDelete'); - } -}; - -// Overrides of functions on Blockly.Block that take into account whether the -// block has been rendered. - -/** - * Change the colour of a block. - * @param {number|string} colour HSV hue value, or #RRGGBB string. - * @param {number|string} colourSecondary Secondary HSV hue value, or #RRGGBB - * string. - * @param {number|string} colourTertiary Tertiary HSV hue value, or #RRGGBB - * string. - * @param {number|string} colourQuaternary Quaternary HSV hue value, or #RRGGBB - * string. - */ -Blockly.BlockSvg.prototype.setColour = function(colour, colourSecondary, - colourTertiary, colourQuaternary) { - Blockly.BlockSvg.superClass_.setColour.call(this, colour, colourSecondary, - colourTertiary, colourQuaternary); - - if (this.rendered) { - this.updateColour(); - } -}; - -/** - * Move this block to the front of the visible workspace. - * tags do not respect z-index so SVG renders them in the - * order that they are in the DOM. By placing this block first within the - * block group's , it will render on top of any other blocks. - * @package - */ -Blockly.BlockSvg.prototype.bringToFront = function() { - var block = this; - do { - var root = block.getSvgRoot(); - root.parentNode.appendChild(root); - block = block.getParent(); - } while (block); -}; - -/** - * Set whether this block can chain onto the bottom of another block. - * @param {boolean} newBoolean True if there can be a previous statement. - * @param {(string|Array.|null)=} opt_check Statement type or - * list of statement types. Null/undefined if any type could be connected. - */ -Blockly.BlockSvg.prototype.setPreviousStatement = function(newBoolean, - opt_check) { - Blockly.BlockSvg.superClass_.setPreviousStatement.call(this, newBoolean, - opt_check); - - if (this.rendered) { - this.render(); - this.bumpNeighbours_(); - } -}; - -/** - * Set whether another block can chain onto the bottom of this block. - * @param {boolean} newBoolean True if there can be a next statement. - * @param {(string|Array.|null)=} opt_check Statement type or - * list of statement types. Null/undefined if any type could be connected. - */ -Blockly.BlockSvg.prototype.setNextStatement = function(newBoolean, opt_check) { - Blockly.BlockSvg.superClass_.setNextStatement.call(this, newBoolean, - opt_check); - - if (this.rendered) { - this.render(); - this.bumpNeighbours_(); - } -}; - -/** - * Set whether this block returns a value. - * @param {boolean} newBoolean True if there is an output. - * @param {(string|Array.|null)=} opt_check Returned type or list - * of returned types. Null or undefined if any type could be returned - * (e.g. variable get). - */ -Blockly.BlockSvg.prototype.setOutput = function(newBoolean, opt_check) { - Blockly.BlockSvg.superClass_.setOutput.call(this, newBoolean, opt_check); - - if (this.rendered) { - this.render(); - this.bumpNeighbours_(); - } -}; - -/** - * Set whether value inputs are arranged horizontally or vertically. - * @param {boolean} newBoolean True if inputs are horizontal. - */ -Blockly.BlockSvg.prototype.setInputsInline = function(newBoolean) { - Blockly.BlockSvg.superClass_.setInputsInline.call(this, newBoolean); - - if (this.rendered) { - this.render(); - this.bumpNeighbours_(); - } -}; - -/** - * Remove an input from this block. - * @param {string} name The name of the input. - * @param {boolean=} opt_quiet True to prevent error if input is not present. - * @throws {goog.asserts.AssertionError} if the input is not present and - * opt_quiet is not true. - */ -Blockly.BlockSvg.prototype.removeInput = function(name, opt_quiet) { - Blockly.BlockSvg.superClass_.removeInput.call(this, name, opt_quiet); - - if (this.rendered) { - this.render(); - // Removing an input will cause the block to change shape. - this.bumpNeighbours_(); - } -}; - -/** - * Move a numbered input to a different location on this block. - * @param {number} inputIndex Index of the input to move. - * @param {number} refIndex Index of input that should be after the moved input. - */ -Blockly.BlockSvg.prototype.moveNumberedInputBefore = function( - inputIndex, refIndex) { - Blockly.BlockSvg.superClass_.moveNumberedInputBefore.call(this, inputIndex, - refIndex); - - if (this.rendered) { - this.render(); - // Moving an input will cause the block to change shape. - this.bumpNeighbours_(); - } -}; - -/** - * Add a value input, statement input or local variable to this block. - * @param {number} type Either Blockly.INPUT_VALUE or Blockly.NEXT_STATEMENT or - * Blockly.DUMMY_INPUT. - * @param {string} name Language-neutral identifier which may used to find this - * input again. Should be unique to this block. - * @return {!Blockly.Input} The input object created. - * @private - */ -Blockly.BlockSvg.prototype.appendInput_ = function(type, name) { - var input = Blockly.BlockSvg.superClass_.appendInput_.call(this, type, name); - - if (this.rendered) { - this.render(); - // Adding an input will cause the block to change shape. - this.bumpNeighbours_(); - } - return input; -}; - -/** - * Returns connections originating from this block. - * @param {boolean} all If true, return all connections even hidden ones. - * Otherwise, for a non-rendered block return an empty list, and for a - * collapsed block don't return inputs connections. - * @return {!Array.} Array of connections. - * @package - */ -Blockly.BlockSvg.prototype.getConnections_ = function(all) { - var myConnections = []; - if (all || this.rendered) { - if (this.outputConnection) { - myConnections.push(this.outputConnection); - } - if (this.previousConnection) { - myConnections.push(this.previousConnection); - } - if (this.nextConnection) { - myConnections.push(this.nextConnection); - } - if (all || !this.collapsed_) { - for (var i = 0, input; input = this.inputList[i]; i++) { - if (input.connection) { - myConnections.push(input.connection); - } - } - } - } - return myConnections; -}; - -/** - * Create a connection of the specified type. - * @param {number} type The type of the connection to create. - * @return {!Blockly.RenderedConnection} A new connection of the specified type. - * @private - */ -Blockly.BlockSvg.prototype.makeConnection_ = function(type) { - return new Blockly.RenderedConnection(this, type); -}; - -/** - * Bump unconnected blocks out of alignment. Two blocks which aren't actually - * connected should not coincidentally line up on screen. - * @private - */ -Blockly.BlockSvg.prototype.bumpNeighbours_ = function() { - if (!this.workspace) { - return; // Deleted block. - } - if (this.workspace.isDragging()) { - return; // Don't bump blocks during a drag. - } - var rootBlock = this.getRootBlock(); - if (rootBlock.isInFlyout) { - return; // Don't move blocks around in a flyout. - } - // Loop through every connection on this block. - var myConnections = this.getConnections_(false); - for (var i = 0, connection; connection = myConnections[i]; i++) { - - // Spider down from this block bumping all sub-blocks. - if (connection.isConnected() && connection.isSuperior()) { - connection.targetBlock().bumpNeighbours_(); - } - - var neighbours = connection.neighbours_(Blockly.SNAP_RADIUS); - for (var j = 0, otherConnection; otherConnection = neighbours[j]; j++) { - - // If both connections are connected, that's probably fine. But if - // either one of them is unconnected, then there could be confusion. - if (!connection.isConnected() || !otherConnection.isConnected()) { - // Only bump blocks if they are from different tree structures. - if (otherConnection.getSourceBlock().getRootBlock() != rootBlock) { - - // Always bump the inferior block. - if (connection.isSuperior()) { - otherConnection.bumpAwayFrom_(connection); - } else { - connection.bumpAwayFrom_(otherConnection); - } - } - } - } - } -}; - -/** - * Schedule snapping to grid and bumping neighbours to occur after a brief - * delay. - * @package - */ -Blockly.BlockSvg.prototype.scheduleSnapAndBump = function() { - var block = this; - // Ensure that any snap and bump are part of this move's event group. - var group = Blockly.Events.getGroup(); - - setTimeout(function() { - Blockly.Events.setGroup(group); - block.snapToGrid(); - Blockly.Events.setGroup(false); - }, Blockly.BUMP_DELAY / 2); - - setTimeout(function() { - Blockly.Events.setGroup(group); - block.bumpNeighbours_(); - Blockly.Events.setGroup(false); - }, Blockly.BUMP_DELAY); -}; diff --git a/core/blockly.js b/core/blockly.js deleted file mode 100644 index fa0b484896..0000000000 --- a/core/blockly.js +++ /dev/null @@ -1,622 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2011 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Core JavaScript library for Blockly. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -/** - * The top level namespace used to access the Blockly library. - * @namespace Blockly - **/ -goog.provide('Blockly'); - -goog.require('Blockly.BlockSvg.render'); -goog.require('Blockly.DropDownDiv'); -goog.require('Blockly.Events'); -goog.require('Blockly.FieldAngle'); -goog.require('Blockly.FieldCheckbox'); -goog.require('Blockly.FieldColour'); -goog.require('Blockly.FieldColourSlider'); -// Date picker commented out since it increases footprint by 60%. -// Add it only if you need it. -//goog.require('Blockly.FieldDate'); -goog.require('Blockly.FieldDropdown'); -goog.require('Blockly.FieldIconMenu'); -goog.require('Blockly.FieldImage'); -goog.require('Blockly.FieldNote'); -goog.require('Blockly.FieldTextInput'); -goog.require('Blockly.FieldTextInputRemovable'); -goog.require('Blockly.FieldTextDropdown'); -goog.require('Blockly.FieldNumber'); -goog.require('Blockly.FieldNumberDropdown'); -goog.require('Blockly.FieldMatrix'); -goog.require('Blockly.FieldVariable'); -goog.require('Blockly.FieldVerticalSeparator'); -goog.require('Blockly.Generator'); -goog.require('Blockly.Msg'); -goog.require('Blockly.Procedures'); -goog.require('Blockly.ScratchMsgs'); -goog.require('Blockly.Toolbox'); -goog.require('Blockly.Touch'); -goog.require('Blockly.WidgetDiv'); -goog.require('Blockly.WorkspaceSvg'); -goog.require('Blockly.constants'); -goog.require('Blockly.inject'); -goog.require('Blockly.utils'); -goog.require('goog.color'); - - -// Turn off debugging when compiled. -/* eslint-disable no-unused-vars */ -var CLOSURE_DEFINES = {'goog.DEBUG': false}; -/* eslint-enable no-unused-vars */ - -/** - * The main workspace most recently used. - * Set by Blockly.WorkspaceSvg.prototype.markFocused - * @type {Blockly.Workspace} - */ -Blockly.mainWorkspace = null; - -/** - * Currently selected block. - * @type {Blockly.Block} - */ -Blockly.selected = null; - -/** - * All of the connections on blocks that are currently being dragged. - * @type {!Array.} - * @private - */ -Blockly.draggingConnections_ = []; - -/** - * Contents of the local clipboard. - * @type {Element} - * @private - */ -Blockly.clipboardXml_ = null; - -/** - * Source of the local clipboard. - * @type {Blockly.WorkspaceSvg} - * @private - */ -Blockly.clipboardSource_ = null; - -/** - * Cached value for whether 3D is supported. - * @type {!boolean} - * @private - */ -Blockly.cache3dSupported_ = null; - -/** - * 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'. - */ -Blockly.hueToRgb = function(hue) { - return goog.color.hsvToHex(hue, Blockly.HSV_SATURATION, - Blockly.HSV_VALUE * 255); -}; - -/** - * Returns the dimensions of the specified SVG image. - * @param {!Element} svg SVG image. - * @return {!Object} Contains width and height properties. - */ -Blockly.svgSize = function(svg) { - return { - width: svg.cachedWidth_, - height: svg.cachedHeight_ - }; -}; - -/** - * Size the workspace when the contents change. This also updates - * scrollbars accordingly. - * @param {!Blockly.WorkspaceSvg} workspace The workspace to resize. - */ -Blockly.resizeSvgContents = function(workspace) { - workspace.resizeContents(); -}; - -/** - * Size the SVG image to completely fill its container. Call this when the view - * actually changes sizes (e.g. on a window resize/device orientation change). - * See Blockly.resizeSvgContents to resize the workspace when the contents - * change (e.g. when a block is added or removed). - * Record the height/width of the SVG image. - * @param {!Blockly.WorkspaceSvg} workspace Any workspace in the SVG. - */ -Blockly.svgResize = function(workspace) { - var mainWorkspace = workspace; - while (mainWorkspace.options.parentWorkspace) { - mainWorkspace = mainWorkspace.options.parentWorkspace; - } - var svg = mainWorkspace.getParentSvg(); - var div = svg.parentNode; - if (!div) { - // Workspace deleted, or something. - return; - } - var width = div.offsetWidth; - var height = div.offsetHeight; - if (svg.cachedWidth_ != width) { - svg.setAttribute('width', width + 'px'); - svg.cachedWidth_ = width; - } - if (svg.cachedHeight_ != height) { - svg.setAttribute('height', height + 'px'); - svg.cachedHeight_ = height; - } - mainWorkspace.resize(); -}; - -/** - * Handle a key-down on SVG drawing surface. Does nothing if the main workspace is not visible. - * @param {!Event} e Key down event. - * @private - */ -// TODO (https://github.com/google/blockly/issues/1998) handle cases where there are multiple workspaces -// and non-main workspaces are able to accept input. -Blockly.onKeyDown_ = function(e) { - if (Blockly.mainWorkspace.options.readOnly || Blockly.utils.isTargetInput(e) - || (Blockly.mainWorkspace.rendered && !Blockly.mainWorkspace.isVisible())) { - // No key actions on readonly workspaces. - // When focused on an HTML text input widget, don't trap any keys. - // Ignore keypresses on rendered workspaces that have been explicitly - // hidden. - return; - } - var deleteBlock = false; - if (e.keyCode == 27) { - // Pressing esc closes the context menu and any drop-down - Blockly.hideChaff(); - Blockly.DropDownDiv.hide(); - } else if (e.keyCode == 8 || e.keyCode == 46) { - // Delete or backspace. - // Stop the browser from going back to the previous page. - // Do this first to prevent an error in the delete code from resulting in - // data loss. - e.preventDefault(); - // Don't delete while dragging. Jeez. - if (Blockly.mainWorkspace.isDragging()) { - return; - } - if (Blockly.selected && Blockly.selected.isDeletable()) { - deleteBlock = true; - } - } else if (e.altKey || e.ctrlKey || e.metaKey) { - // Don't use meta keys during drags. - if (Blockly.mainWorkspace.isDragging()) { - return; - } - if (Blockly.selected && - Blockly.selected.isDeletable() && Blockly.selected.isMovable()) { - // Don't allow copying immovable or undeletable blocks. The next step - // would be to paste, which would create additional undeletable/immovable - // blocks on the workspace. - if (e.keyCode == 67) { - // 'c' for copy. - Blockly.hideChaff(); - Blockly.copy_(Blockly.selected); - } else if (e.keyCode == 88 && !Blockly.selected.workspace.isFlyout) { - // 'x' for cut, but not in a flyout. - // Don't even copy the selected item in the flyout. - Blockly.copy_(Blockly.selected); - deleteBlock = true; - } - } - if (e.keyCode == 86) { - // 'v' for paste. - if (Blockly.clipboardXml_) { - Blockly.Events.setGroup(true); - // Pasting always pastes to the main workspace, even if the copy started - // in a flyout workspace. - var workspace = Blockly.clipboardSource_; - if (workspace.isFlyout) { - workspace = workspace.targetWorkspace; - } - workspace.paste(Blockly.clipboardXml_); - Blockly.Events.setGroup(false); - } - } else if (e.keyCode == 90) { - // 'z' for undo 'Z' is for redo. - Blockly.hideChaff(); - Blockly.mainWorkspace.undo(e.shiftKey); - } - } - // Common code for delete and cut. - // Don't delete in the flyout. - if (deleteBlock && !Blockly.selected.workspace.isFlyout) { - Blockly.Events.setGroup(true); - Blockly.hideChaff(); - Blockly.selected.dispose(/* heal */ true, true); - Blockly.Events.setGroup(false); - } -}; - -/** - * Copy a block or workspace comment onto the local clipboard. - * @param {!Blockly.Block | !Blockly.WorkspaceComment} toCopy Block or Workspace Comment - * to be copied. - * @private - */ -Blockly.copy_ = function(toCopy) { - if (toCopy.isComment) { - var xml = toCopy.toXmlWithXY(); - } else { - var xml = Blockly.Xml.blockToDom(toCopy); - // Encode start position in XML. - var xy = toCopy.getRelativeToSurfaceXY(); - xml.setAttribute('x', toCopy.RTL ? -xy.x : xy.x); - xml.setAttribute('y', xy.y); - } - Blockly.clipboardXml_ = xml; - Blockly.clipboardSource_ = toCopy.workspace; -}; - -/** - * Duplicate this block and its children, or a workspace comment. - * @param {!Blockly.Block | !Blockly.WorkspaceComment} toDuplicate Block or - * Workspace Comment to be copied. - * @private - */ -Blockly.duplicate_ = function(toDuplicate) { - // Save the clipboard. - var clipboardXml = Blockly.clipboardXml_; - var clipboardSource = Blockly.clipboardSource_; - - // Create a duplicate via a copy/paste operation. - Blockly.copy_(toDuplicate); - toDuplicate.workspace.paste(Blockly.clipboardXml_); - - // Restore the clipboard. - Blockly.clipboardXml_ = clipboardXml; - Blockly.clipboardSource_ = clipboardSource; -}; - -/** - * Cancel the native context menu, unless the focus is on an HTML input widget. - * @param {!Event} e Mouse down event. - * @private - */ -Blockly.onContextMenu_ = function(e) { - if (!Blockly.utils.isTargetInput(e)) { - // When focused on an HTML text input widget, don't cancel the context menu. - e.preventDefault(); - } -}; - -/** - * Close tooltips, context menus, dropdown selections, etc. - * @param {boolean=} opt_allowToolbox If true, don't close the toolbox. - */ -Blockly.hideChaff = function(opt_allowToolbox) { - Blockly.hideChaffInternal_(opt_allowToolbox); - Blockly.WidgetDiv.hide(true); -}; - -/** - * Close tooltips, context menus, dropdown selections, etc. - * For some elements (e.g. field text inputs), rather than hiding, it will - * move them. - * @param {boolean=} opt_allowToolbox If true, don't close the toolbox. - */ -Blockly.hideChaffOnResize = function(opt_allowToolbox) { - Blockly.hideChaffInternal_(opt_allowToolbox); - Blockly.WidgetDiv.repositionForWindowResize(); -}; - -/** - * Does a majority of the work for hideChaff including tooltips, dropdowns, - * toolbox, etc. It does not deal with the WidgetDiv. - * @param {boolean=} opt_allowToolbox If true, don't close the toolbox. - * @private - */ -Blockly.hideChaffInternal_ = function(opt_allowToolbox) { - Blockly.Tooltip.hide(); - Blockly.DropDownDiv.hideWithoutAnimation(); - if (!opt_allowToolbox) { - var workspace = Blockly.getMainWorkspace(); - if (workspace.toolbox_ && - workspace.toolbox_.flyout_ && - workspace.toolbox_.flyout_.autoClose) { - workspace.toolbox_.clearSelection(); - } - } -}; - -/** - * Returns the main workspace. Returns the last used main workspace (based on - * focus). Try not to use this function, particularly if there are multiple - * Blockly instances on a page. - * @return {!Blockly.Workspace} The main workspace. - */ -Blockly.getMainWorkspace = function() { - return Blockly.mainWorkspace; -}; - -/** - * Wrapper to window.alert() that app developers may override 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. - */ -Blockly.alert = function(message, opt_callback) { - window.alert(message); - if (opt_callback) { - opt_callback(); - } -}; - -/** - * Wrapper to window.confirm() that app developers may override 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. - */ -Blockly.confirm = function(message, callback) { - callback(window.confirm(message)); -}; - -/** - * Wrapper to window.prompt() that app developers may override to provide - * alternatives to the modal browser window. Built-in browser prompts are - * often used for better text input experience on mobile device. We strongly - * recommend testing mobile when overriding this. - * @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. - * @param {?string} _opt_title An optional title for the prompt. - * @param {?string} _opt_varType An optional variable type for variable specific - * prompt behavior. - */ -Blockly.prompt = function(message, defaultValue, callback, _opt_title, - _opt_varType) { - // opt_title and opt_varType are unused because we only need them to pass - // information to the scratch-gui, which overwrites this function - callback(window.prompt(message, defaultValue)); -}; - -/** - * A callback for status buttons. The window.alert is here for testing and - * should be overridden. - * @param {string} id An identifier. - */ -Blockly.statusButtonCallback = function(id) { - window.alert('status button was pressed for ' + id); -}; - -/** - * Refresh the visual state of a status button in all extension category headers. - * @param {Blockly.Workspace} workspace A workspace. - */ -Blockly.refreshStatusButtons = function(workspace) { - var buttons = workspace.getFlyout().buttons_; - for (var i = 0; i < buttons.length; i++) { - if (buttons[i] instanceof Blockly.FlyoutExtensionCategoryHeader) { - buttons[i].refreshStatus(); - } - } -}; - -/** - * Helper function for defining a block from JSON. The resulting function has - * the correct value of jsonDef at the point in code where jsonInit is called. - * @param {!Object} jsonDef The JSON definition of a block. - * @return {function()} A function that calls jsonInit with the correct value - * of jsonDef. - * @private - */ -Blockly.jsonInitFactory_ = function(jsonDef) { - return function() { - this.jsonInit(jsonDef); - }; -}; - -/** - * Define blocks from an array of JSON block definitions, as might be generated - * by the Blockly Developer Tools. - * @param {!Array.} jsonArray An array of JSON block definitions. - */ -Blockly.defineBlocksWithJsonArray = function(jsonArray) { - for (var i = 0; i < jsonArray.length; i++) { - var elem = jsonArray[i]; - if (!elem) { - console.warn( - 'Block definition #' + i + ' in JSON array is ' + elem + '. ' + - 'Skipping.'); - } else { - var typename = elem.type; - if (typename == null || typename === '') { - console.warn( - 'Block definition #' + i + - ' in JSON array is missing a type attribute. Skipping.'); - } else { - if (Blockly.Blocks[typename]) { - console.warn( - 'Block definition #' + i + ' in JSON array' + - ' overwrites prior definition of "' + typename + '".'); - } - Blockly.Blocks[typename] = { - init: Blockly.jsonInitFactory_(elem) - }; - } - } - } -}; - -/** - * Bind an event to a function call. When calling the function, verifies that - * it belongs to the touch stream that is currently being processed, and splits - * multitouch events into multiple events as needed. - * @param {!EventTarget} node Node upon which to listen. - * @param {string} name Event name to listen to (e.g. 'mousedown'). - * @param {Object} thisObject The value of 'this' in the function. - * @param {!Function} func Function to call when event is triggered. - * @param {boolean=} opt_noCaptureIdentifier True if triggering on this event - * should not block execution of other event handlers on this touch or other - * simultaneous touches. - * @param {boolean=} opt_noPreventDefault True if triggering on this event - * should prevent the default handler. False by default. If - * opt_noPreventDefault is provided, opt_noCaptureIdentifier must also be - * provided. - * @return {!Array.} Opaque data that can be passed to unbindEvent_. - */ -Blockly.bindEventWithChecks_ = function(node, name, thisObject, func, - opt_noCaptureIdentifier, opt_noPreventDefault) { - var handled = false; - var wrapFunc = function(e) { - var captureIdentifier = !opt_noCaptureIdentifier; - // Handle each touch point separately. If the event was a mouse event, this - // will hand back an array with one element, which we're fine handling. - var events = Blockly.Touch.splitEventByTouches(e); - for (var i = 0, event; event = events[i]; i++) { - if (captureIdentifier && !Blockly.Touch.shouldHandleEvent(event)) { - continue; - } - Blockly.Touch.setClientFromTouch(event); - if (thisObject) { - func.call(thisObject, event); - } else { - func(event); - } - handled = true; - } - }; - - node.addEventListener(name, wrapFunc, false); - var bindData = [[node, name, wrapFunc]]; - - // Add equivalent touch event. - if (name in Blockly.Touch.TOUCH_MAP) { - var touchWrapFunc = function(e) { - wrapFunc(e); - // Calling preventDefault stops the browser from scrolling/zooming the - // page. - var preventDef = !opt_noPreventDefault; - if (handled && preventDef) { - e.preventDefault(); - } - }; - for (var i = 0, type; type = Blockly.Touch.TOUCH_MAP[name][i]; i++) { - node.addEventListener(type, touchWrapFunc, false); - bindData.push([node, type, touchWrapFunc]); - } - } - return bindData; -}; - - -/** - * Bind an event to a function call. Handles multitouch events by using the - * coordinates of the first changed touch, and doesn't do any safety checks for - * simultaneous event processing. - * @deprecated in favor of bindEventWithChecks_, but preserved for external - * users. - * @param {!EventTarget} node Node upon which to listen. - * @param {string} name Event name to listen to (e.g. 'mousedown'). - * @param {Object} thisObject The value of 'this' in the function. - * @param {!Function} func Function to call when event is triggered. - * @return {!Array.} Opaque data that can be passed to unbindEvent_. - * @private - */ -Blockly.bindEvent_ = function(node, name, thisObject, func) { - var wrapFunc = function(e) { - if (thisObject) { - func.call(thisObject, e); - } else { - func(e); - } - }; - - node.addEventListener(name, wrapFunc, false); - var bindData = [[node, name, wrapFunc]]; - - // Add equivalent touch event. - if (name in Blockly.Touch.TOUCH_MAP) { - var touchWrapFunc = function(e) { - // Punt on multitouch events. - if (e.changedTouches.length == 1) { - // Map the touch event's properties to the event. - var touchPoint = e.changedTouches[0]; - e.clientX = touchPoint.clientX; - e.clientY = touchPoint.clientY; - } - wrapFunc(e); - - // Stop the browser from scrolling/zooming the page. - e.preventDefault(); - }; - for (var i = 0, type; type = Blockly.Touch.TOUCH_MAP[name][i]; i++) { - node.addEventListener(type, touchWrapFunc, false); - bindData.push([node, type, touchWrapFunc]); - } - } - return bindData; -}; - -/** - * Unbind one or more events event from a function call. - * @param {!Array.} bindData Opaque data from bindEvent_. - * This list is emptied during the course of calling this function. - * @return {!Function} The function call. - * @private - */ -Blockly.unbindEvent_ = function(bindData) { - while (bindData.length) { - var bindDatum = bindData.pop(); - var node = bindDatum[0]; - var name = bindDatum[1]; - var func = bindDatum[2]; - node.removeEventListener(name, func, false); - } - return func; -}; - -/** - * Is the given string a number (includes negative and decimals). - * @param {string} str Input string. - * @return {boolean} True if number, false otherwise. - */ -Blockly.isNumber = function(str) { - return !!str.match(/^\s*-?\d+(\.\d+)?\s*$/); -}; - -// IE9 does not have a console. Create a stub to stop errors. -if (!goog.global['console']) { - goog.global['console'] = { - 'log': function() {}, - 'warn': function() {} - }; -} - -// Export symbols that would otherwise be renamed by Closure compiler. -if (!goog.global['Blockly']) { - goog.global['Blockly'] = {}; -} -goog.global['Blockly']['getMainWorkspace'] = Blockly.getMainWorkspace; diff --git a/core/bubble.js b/core/bubble.js deleted file mode 100644 index 9121554241..0000000000 --- a/core/bubble.js +++ /dev/null @@ -1,664 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Object representing a UI bubble. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.Bubble'); - -goog.require('Blockly.Touch'); -goog.require('Blockly.Workspace'); -goog.require('goog.dom'); -goog.require('goog.math.Coordinate'); -goog.require('goog.userAgent'); - - -/** - * Class for UI bubble. - * @param {!Blockly.WorkspaceSvg} workspace The workspace on which to draw the - * bubble. - * @param {!Element} content SVG content for the bubble. - * @param {Element} shape SVG element to avoid eclipsing. - * @param {!goog.math.Coordinate} anchorXY Absolute position of bubble's anchor - * point. - * @param {?number} bubbleWidth Width of bubble, or null if not resizable. - * @param {?number} bubbleHeight Height of bubble, or null if not resizable. - * @constructor - */ -Blockly.Bubble = function(workspace, content, shape, anchorXY, - bubbleWidth, bubbleHeight) { - this.workspace_ = workspace; - this.content_ = content; - this.shape_ = shape; - - var angle = Blockly.Bubble.ARROW_ANGLE; - if (this.workspace_.RTL) { - angle = -angle; - } - this.arrow_radians_ = Blockly.utils.toRadians(angle); - - var canvas = workspace.getBubbleCanvas(); - canvas.appendChild(this.createDom_(content, !!(bubbleWidth && bubbleHeight))); - - this.setAnchorLocation(anchorXY); - if (!bubbleWidth || !bubbleHeight) { - var bBox = /** @type {SVGLocatable} */ (this.content_).getBBox(); - bubbleWidth = bBox.width + 2 * Blockly.Bubble.BORDER_WIDTH; - bubbleHeight = bBox.height + 2 * Blockly.Bubble.BORDER_WIDTH; - } - this.setBubbleSize(bubbleWidth, bubbleHeight); - - // Render the bubble. - this.positionBubble_(); - this.renderArrow_(); - this.rendered_ = true; - - if (!workspace.options.readOnly) { - Blockly.bindEventWithChecks_( - this.bubbleBack_, 'mousedown', this, this.bubbleMouseDown_); - if (this.resizeGroup_) { - Blockly.bindEventWithChecks_( - this.resizeGroup_, 'mousedown', this, this.resizeMouseDown_); - } - } -}; - -/** - * Width of the border around the bubble. - */ -Blockly.Bubble.BORDER_WIDTH = 6; - -/** - * Determines the thickness of the base of the arrow in relation to the size - * of the bubble. Higher numbers result in thinner arrows. - */ -Blockly.Bubble.ARROW_THICKNESS = 5; - -/** - * The number of degrees that the arrow bends counter-clockwise. - */ -Blockly.Bubble.ARROW_ANGLE = 20; - -/** - * The sharpness of the arrow's bend. Higher numbers result in smoother arrows. - */ -Blockly.Bubble.ARROW_BEND = 4; - -/** - * Distance between arrow point and anchor point. - */ -Blockly.Bubble.ANCHOR_RADIUS = 8; - -/** - * Wrapper function called when a mouseUp occurs during a drag operation. - * @type {Array.} - * @private - */ -Blockly.Bubble.onMouseUpWrapper_ = null; - -/** - * Wrapper function called when a mouseMove occurs during a drag operation. - * @type {Array.} - * @private - */ -Blockly.Bubble.onMouseMoveWrapper_ = null; - -/** - * Function to call on resize of bubble. - * @type {Function} - */ -Blockly.Bubble.prototype.resizeCallback_ = null; - -/** - * Stop binding to the global mouseup and mousemove events. - * @private - */ -Blockly.Bubble.unbindDragEvents_ = function() { - if (Blockly.Bubble.onMouseUpWrapper_) { - Blockly.unbindEvent_(Blockly.Bubble.onMouseUpWrapper_); - Blockly.Bubble.onMouseUpWrapper_ = null; - } - if (Blockly.Bubble.onMouseMoveWrapper_) { - Blockly.unbindEvent_(Blockly.Bubble.onMouseMoveWrapper_); - Blockly.Bubble.onMouseMoveWrapper_ = null; - } -}; - -/* - * Handle a mouse-up event while dragging a bubble's border or resize handle. - * @param {!Event} e Mouse up event. - * @private - */ -Blockly.Bubble.bubbleMouseUp_ = function(/*e*/) { - Blockly.Touch.clearTouchIdentifier(); - Blockly.Bubble.unbindDragEvents_(); -}; - -/** - * Flag to stop incremental rendering during construction. - * @private - */ -Blockly.Bubble.prototype.rendered_ = false; - -/** - * Absolute coordinate of anchor point, in workspace coordinates. - * @type {goog.math.Coordinate} - * @private - */ -Blockly.Bubble.prototype.anchorXY_ = null; - -/** - * Relative X coordinate of bubble with respect to the anchor's centre, - * in workspace units. - * In RTL mode the initial value is negated. - * @private - */ -Blockly.Bubble.prototype.relativeLeft_ = 0; - -/** - * Relative Y coordinate of bubble with respect to the anchor's centre. - * @private - */ -Blockly.Bubble.prototype.relativeTop_ = 0; - -/** - * Width of bubble. - * @private - */ -Blockly.Bubble.prototype.width_ = 0; - -/** - * Height of bubble. - * @private - */ -Blockly.Bubble.prototype.height_ = 0; - -/** - * Automatically position and reposition the bubble. - * @private - */ -Blockly.Bubble.prototype.autoLayout_ = true; - -/** - * Create the bubble's DOM. - * @param {!Element} content SVG content for the bubble. - * @param {boolean} hasResize Add diagonal resize gripper if true. - * @return {!Element} The bubble's SVG group. - * @private - */ -Blockly.Bubble.prototype.createDom_ = function(content, hasResize) { - /* Create the bubble. Here's the markup that will be generated: - - - - - - - - - - - [...content goes here...] - - */ - this.bubbleGroup_ = Blockly.utils.createSvgElement('g', {}, null); - var filter = - {'filter': 'url(#' + this.workspace_.options.embossFilterId + ')'}; - if (goog.userAgent.getUserAgentString().indexOf('JavaFX') != -1) { - // Multiple reports that JavaFX can't handle filters. UserAgent: - // Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.44 - // (KHTML, like Gecko) JavaFX/8.0 Safari/537.44 - // https://github.com/google/blockly/issues/99 - filter = {}; - } - var bubbleEmboss = Blockly.utils.createSvgElement('g', - filter, this.bubbleGroup_); - this.bubbleArrow_ = Blockly.utils.createSvgElement('path', {}, bubbleEmboss); - this.bubbleBack_ = Blockly.utils.createSvgElement('rect', - { - 'class': 'blocklyDraggable', - 'x': 0, - 'y': 0, - 'rx': Blockly.Bubble.BORDER_WIDTH, - 'ry': Blockly.Bubble.BORDER_WIDTH - }, - bubbleEmboss); - if (hasResize) { - this.resizeGroup_ = Blockly.utils.createSvgElement('g', - {'class': this.workspace_.RTL ? - 'blocklyResizeSW' : 'blocklyResizeSE'}, - this.bubbleGroup_); - var resizeSize = 2 * Blockly.Bubble.BORDER_WIDTH; - Blockly.utils.createSvgElement('polygon', - {'points': '0,x x,x x,0'.replace(/x/g, resizeSize.toString())}, - this.resizeGroup_); - Blockly.utils.createSvgElement('line', - { - 'class': 'blocklyResizeLine', - 'x1': resizeSize / 3, 'y1': resizeSize - 1, - 'x2': resizeSize - 1, 'y2': resizeSize / 3 - }, this.resizeGroup_); - Blockly.utils.createSvgElement('line', - { - 'class': 'blocklyResizeLine', - 'x1': resizeSize * 2 / 3, - 'y1': resizeSize - 1, - 'x2': resizeSize - 1, - 'y2': resizeSize * 2 / 3 - }, this.resizeGroup_); - } else { - this.resizeGroup_ = null; - } - this.bubbleGroup_.appendChild(content); - return this.bubbleGroup_; -}; - -/** - * Return the root node of the bubble's SVG group. - * @return {Element} The root SVG node of the bubble's group. - */ -Blockly.Bubble.prototype.getSvgRoot = function() { - return this.bubbleGroup_; -}; - -/** - * Expose the block's ID on the bubble's top-level SVG group. - * @param {string} id ID of block. - */ -Blockly.Bubble.prototype.setSvgId = function(id) { - if (this.bubbleGroup_.dataset) { - this.bubbleGroup_.dataset.blockId = id; - } -}; - -/** - * Handle a mouse-down on bubble's border. - * @param {!Event} e Mouse down event. - * @private - */ -Blockly.Bubble.prototype.bubbleMouseDown_ = function(e) { - var gesture = this.workspace_.getGesture(e); - if (gesture) { - gesture.handleBubbleStart(e, this); - } -}; - -/** - * Show the context menu for this bubble. - * @param {!Event} _e Mouse event. - * @private - */ -Blockly.Bubble.prototype.showContextMenu_ = function(_e) { - // NOP on bubbles, but used by the bubble dragger to pass events to - // workspace comments. -}; - -/** - * Get whether this bubble is deletable or not. - * @return {boolean} True if deletable. - * @package - */ -Blockly.Bubble.prototype.isDeletable = function() { - return false; -}; - -/** - * Handle a mouse-down on bubble's resize corner. - * @param {!Event} e Mouse down event. - * @private - */ -Blockly.Bubble.prototype.resizeMouseDown_ = function(e) { - this.promote_(); - Blockly.Bubble.unbindDragEvents_(); - if (Blockly.utils.isRightButton(e)) { - // No right-click. - e.stopPropagation(); - return; - } - // Left-click (or middle click) - this.workspace_.startDrag(e, new goog.math.Coordinate( - this.workspace_.RTL ? -this.width_ : this.width_, this.height_)); - - Blockly.Bubble.onMouseUpWrapper_ = Blockly.bindEventWithChecks_(document, - 'mouseup', this, Blockly.Bubble.bubbleMouseUp_); - Blockly.Bubble.onMouseMoveWrapper_ = Blockly.bindEventWithChecks_(document, - 'mousemove', this, this.resizeMouseMove_); - Blockly.hideChaff(); - // This event has been handled. No need to bubble up to the document. - e.stopPropagation(); -}; - -/** - * Resize this bubble to follow the mouse. - * @param {!Event} e Mouse move event. - * @private - */ -Blockly.Bubble.prototype.resizeMouseMove_ = function(e) { - this.autoLayout_ = false; - var newXY = this.workspace_.moveDrag(e); - this.setBubbleSize(this.workspace_.RTL ? -newXY.x : newXY.x, newXY.y); - if (this.workspace_.RTL) { - // RTL requires the bubble to move its left edge. - this.positionBubble_(); - } -}; - -/** - * Register a function as a callback event for when the bubble is resized. - * @param {!Function} callback The function to call on resize. - */ -Blockly.Bubble.prototype.registerResizeEvent = function(callback) { - this.resizeCallback_ = callback; -}; - -/** - * Move this bubble to the top of the stack. - * @return {!boolean} Whether or not the bubble has been moved. - * @private - */ -Blockly.Bubble.prototype.promote_ = function() { - var svgGroup = this.bubbleGroup_.parentNode; - if (svgGroup.lastChild !== this.bubbleGroup_) { - svgGroup.appendChild(this.bubbleGroup_); - return true; - } - return false; -}; - -/** - * Notification that the anchor has moved. - * Update the arrow and bubble accordingly. - * @param {!goog.math.Coordinate} xy Absolute location. - */ -Blockly.Bubble.prototype.setAnchorLocation = function(xy) { - this.anchorXY_ = xy; - if (this.rendered_) { - this.positionBubble_(); - } -}; - -/** - * Position the bubble so that it does not fall off-screen. - * @private - */ -Blockly.Bubble.prototype.layoutBubble_ = function() { - // Compute the preferred bubble location. - var relativeLeft = -this.width_ / 4; - var relativeTop = -this.height_ - Blockly.BlockSvg.MIN_BLOCK_Y; - // Prevent the bubble from being off-screen. - var metrics = this.workspace_.getMetrics(); - metrics.viewWidth /= this.workspace_.scale; - metrics.viewLeft /= this.workspace_.scale; - var anchorX = this.anchorXY_.x; - if (this.workspace_.RTL) { - if (anchorX - metrics.viewLeft - relativeLeft - this.width_ < - Blockly.Scrollbar.scrollbarThickness) { - // Slide the bubble right until it is onscreen. - relativeLeft = anchorX - metrics.viewLeft - this.width_ - - Blockly.Scrollbar.scrollbarThickness; - } else if (anchorX - metrics.viewLeft - relativeLeft > - metrics.viewWidth) { - // Slide the bubble left until it is onscreen. - relativeLeft = anchorX - metrics.viewLeft - metrics.viewWidth; - } - } else { - if (anchorX + relativeLeft < metrics.viewLeft) { - // Slide the bubble right until it is onscreen. - relativeLeft = metrics.viewLeft - anchorX; - } else if (metrics.viewLeft + metrics.viewWidth < - anchorX + relativeLeft + this.width_ + - Blockly.BlockSvg.SEP_SPACE_X + - Blockly.Scrollbar.scrollbarThickness) { - // Slide the bubble left until it is onscreen. - relativeLeft = metrics.viewLeft + metrics.viewWidth - anchorX - - this.width_ - Blockly.Scrollbar.scrollbarThickness; - } - } - if (this.anchorXY_.y + relativeTop < metrics.viewTop) { - // Slide the bubble below the block. - var bBox = /** @type {SVGLocatable} */ (this.shape_).getBBox(); - relativeTop = bBox.height; - } - this.relativeLeft_ = relativeLeft; - this.relativeTop_ = relativeTop; -}; - -/** - * Move the bubble to a location relative to the anchor's centre. - * @private - */ -Blockly.Bubble.prototype.positionBubble_ = function() { - var left = this.anchorXY_.x; - if (this.workspace_.RTL) { - left -= this.relativeLeft_ ; - } else { - left += this.relativeLeft_; - } - var top = this.relativeTop_ + this.anchorXY_.y; - this.moveTo(left, top); -}; - -/** - * Move the bubble group to the specified location in workspace coordinates. - * @param {number} x The x position to move to. - * @param {number} y The y position to move to. - * @package - */ -Blockly.Bubble.prototype.moveTo = function(x, y) { - this.bubbleGroup_.setAttribute('transform', 'translate(' + x + ',' + y + ')'); -}; - -/** - * Get the dimensions of this bubble. - * @return {!Object} Object with width and height properties. - */ -Blockly.Bubble.prototype.getBubbleSize = function() { - return {width: this.width_, height: this.height_}; -}; - -/** - * Size this bubble. - * @param {number} width Width of the bubble. - * @param {number} height Height of the bubble. - */ -Blockly.Bubble.prototype.setBubbleSize = function(width, height) { - var doubleBorderWidth = 2 * Blockly.Bubble.BORDER_WIDTH; - // Minimum size of a bubble. - width = Math.max(width, doubleBorderWidth + 45); - height = Math.max(height, doubleBorderWidth + 20); - this.width_ = width; - this.height_ = height; - this.bubbleBack_.setAttribute('width', width); - this.bubbleBack_.setAttribute('height', height); - if (this.resizeGroup_) { - if (this.workspace_.RTL) { - // Mirror the resize group. - var resizeSize = 2 * Blockly.Bubble.BORDER_WIDTH; - this.resizeGroup_.setAttribute('transform', 'translate(' + - resizeSize + ',' + (height - doubleBorderWidth) + ') scale(-1 1)'); - } else { - this.resizeGroup_.setAttribute('transform', 'translate(' + - (width - doubleBorderWidth) + ',' + - (height - doubleBorderWidth) + ')'); - } - } - if (this.rendered_) { - if (this.autoLayout_) { - this.layoutBubble_(); - } - this.positionBubble_(); - this.renderArrow_(); - } - // Allow the contents to resize. - if (this.resizeCallback_) { - this.resizeCallback_(); - } -}; - -/** - * Draw the arrow between the bubble and the origin. - * @private - */ -Blockly.Bubble.prototype.renderArrow_ = function() { - var steps = []; - // Find the relative coordinates of the center of the bubble. - var relBubbleX = this.width_ / 2; - var relBubbleY = this.height_ / 2; - // Find the relative coordinates of the center of the anchor. - var relAnchorX = -this.relativeLeft_; - var relAnchorY = -this.relativeTop_; - if (relBubbleX == relAnchorX && relBubbleY == relAnchorY) { - // Null case. Bubble is directly on top of the anchor. - // Short circuit this rather than wade through divide by zeros. - steps.push('M ' + relBubbleX + ',' + relBubbleY); - } else { - // Compute the angle of the arrow's line. - var rise = relAnchorY - relBubbleY; - var run = relAnchorX - relBubbleX; - if (this.workspace_.RTL) { - run *= -1; - } - var hypotenuse = Math.sqrt(rise * rise + run * run); - var angle = Math.acos(run / hypotenuse); - if (rise < 0) { - angle = 2 * Math.PI - angle; - } - // Compute a line perpendicular to the arrow. - var rightAngle = angle + Math.PI / 2; - if (rightAngle > Math.PI * 2) { - rightAngle -= Math.PI * 2; - } - var rightRise = Math.sin(rightAngle); - var rightRun = Math.cos(rightAngle); - - // Calculate the thickness of the base of the arrow. - var bubbleSize = this.getBubbleSize(); - var thickness = (bubbleSize.width + bubbleSize.height) / - Blockly.Bubble.ARROW_THICKNESS; - thickness = Math.min(thickness, bubbleSize.width, bubbleSize.height) / 4; - - // Back the tip of the arrow off of the anchor. - var backoffRatio = 1 - Blockly.Bubble.ANCHOR_RADIUS / hypotenuse; - relAnchorX = relBubbleX + backoffRatio * run; - relAnchorY = relBubbleY + backoffRatio * rise; - - // Coordinates for the base of the arrow. - var baseX1 = relBubbleX + thickness * rightRun; - var baseY1 = relBubbleY + thickness * rightRise; - var baseX2 = relBubbleX - thickness * rightRun; - var baseY2 = relBubbleY - thickness * rightRise; - - // Distortion to curve the arrow. - var swirlAngle = angle + this.arrow_radians_; - if (swirlAngle > Math.PI * 2) { - swirlAngle -= Math.PI * 2; - } - var swirlRise = Math.sin(swirlAngle) * - hypotenuse / Blockly.Bubble.ARROW_BEND; - var swirlRun = Math.cos(swirlAngle) * - hypotenuse / Blockly.Bubble.ARROW_BEND; - - steps.push('M' + baseX1 + ',' + baseY1); - steps.push('C' + (baseX1 + swirlRun) + ',' + (baseY1 + swirlRise) + - ' ' + relAnchorX + ',' + relAnchorY + - ' ' + relAnchorX + ',' + relAnchorY); - steps.push('C' + relAnchorX + ',' + relAnchorY + - ' ' + (baseX2 + swirlRun) + ',' + (baseY2 + swirlRise) + - ' ' + baseX2 + ',' + baseY2); - } - steps.push('z'); - this.bubbleArrow_.setAttribute('d', steps.join(' ')); -}; - -/** - * Change the colour of a bubble. - * @param {string} hexColour Hex code of colour. - */ -Blockly.Bubble.prototype.setColour = function(hexColour) { - this.bubbleBack_.setAttribute('fill', hexColour); - this.bubbleArrow_.setAttribute('fill', hexColour); -}; - -/** - * Dispose of this bubble. - */ -Blockly.Bubble.prototype.dispose = function() { - Blockly.Bubble.unbindDragEvents_(); - // Dispose of and unlink the bubble. - goog.dom.removeNode(this.bubbleGroup_); - this.bubbleGroup_ = null; - this.bubbleArrow_ = null; - this.bubbleBack_ = null; - this.resizeGroup_ = null; - this.workspace_ = null; - this.content_ = null; - this.shape_ = null; -}; - -/** - * Move this bubble during a drag, taking into account whether or not there is - * a drag surface. - * @param {?Blockly.BlockDragSurfaceSvg} dragSurface The surface that carries - * rendered items during a drag, or null if no drag surface is in use. - * @param {!goog.math.Coordinate} newLoc The location to translate to, in - * workspace coordinates. - * @package - */ -Blockly.Bubble.prototype.moveDuringDrag = function(dragSurface, newLoc) { - if (dragSurface) { - dragSurface.translateSurface(newLoc.x, newLoc.y); - } else { - this.moveTo(newLoc.x, newLoc.y); - } - if (this.workspace_.RTL) { - this.relativeLeft_ = this.anchorXY_.x - newLoc.x - this.width_; - } else { - this.relativeLeft_ = newLoc.x - this.anchorXY_.x; - } - this.relativeTop_ = newLoc.y - this.anchorXY_.y; - this.renderArrow_(); -}; - -/** - * Return the coordinates of the top corner of this bubble's starting edge (e.g. - * top left corner in LTR and top right corner in RTL) relative - * to the drawing surface's origin (0,0), in workspace units. - * @return {!goog.math.Coordinate} Object with .x and .y properties. - */ -Blockly.Bubble.prototype.getRelativeToSurfaceXY = function() { - return new goog.math.Coordinate( - this.workspace_.RTL ? this.anchorXY_.x - this.relativeLeft_ : this.anchorXY_.x + this.relativeLeft_, - this.anchorXY_.y + this.relativeTop_); -}; - -/** - * Set whether auto-layout of this bubble is enabled. The first time a bubble - * is shown it positions itself to not cover any blocks. Once a user has - * dragged it to reposition, it renders where the user put it. - * @param {boolean} enable True if auto-layout should be enabled, false - * otherwise. - * @package - */ -Blockly.Bubble.prototype.setAutoLayout = function(enable) { - this.autoLayout_ = enable; -}; diff --git a/core/bubble_dragger.js b/core/bubble_dragger.js deleted file mode 100644 index 2366581e8c..0000000000 --- a/core/bubble_dragger.js +++ /dev/null @@ -1,285 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2018 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Methods for dragging a bubble visually. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.BubbleDragger'); - -goog.require('Blockly.Bubble'); -goog.require('Blockly.Events.CommentMove'); -goog.require('Blockly.WorkspaceCommentSvg'); - -goog.require('goog.math.Coordinate'); -goog.require('goog.asserts'); - - -/** - * Class for a bubble dragger. It moves things on the bubble canvas around the - * workspace when they are being dragged by a mouse or touch. These can be - * block comments, mutators, warnings, or workspace comments. - * @param {!Blockly.Bubble|!Blockly.WorkspaceCommentSvg} bubble The item on the - * bubble canvas to drag. - * @param {!Blockly.WorkspaceSvg} workspace The workspace to drag on. - * @constructor - */ -Blockly.BubbleDragger = function(bubble, workspace) { - /** - * The item on the bubble canvas that is being dragged. - * @type {!Blockly.Bubble|!Blockly.WorkspaceCommentSvg} - * @private - */ - this.draggingBubble_ = bubble; - - /** - * The workspace on which the bubble is being dragged. - * @type {!Blockly.WorkspaceSvg} - * @private - */ - this.workspace_ = workspace; - - /** - * Which delete area the mouse pointer is over, if any. - * One of {@link Blockly.DELETE_AREA_TRASH}, - * {@link Blockly.DELETE_AREA_TOOLBOX}, or {@link Blockly.DELETE_AREA_NONE}. - * @type {?number} - * @private - */ - this.deleteArea_ = null; - - /** - * Whether the bubble would be deleted if dropped immediately. - * @type {boolean} - * @private - */ - this.wouldDeleteBubble_ = false; - - /** - * The location of the top left corner of the dragging bubble's body at the - * beginning of the drag, in workspace coordinates. - * @type {!goog.math.Coordinate} - * @private - */ - this.startXY_ = this.draggingBubble_.getRelativeToSurfaceXY(); - - /** - * The drag surface to move bubbles to during a drag, or null if none should - * be used. Block dragging and bubble dragging use the same surface. - * @type {?Blockly.BlockDragSurfaceSvg} - * @private - */ - this.dragSurface_ = - Blockly.utils.is3dSupported() && !!workspace.getBlockDragSurface() ? - workspace.getBlockDragSurface() : null; -}; - -/** - * Sever all links from this object. - * @package - */ -Blockly.BubbleDragger.prototype.dispose = function() { - this.draggingBubble_ = null; - this.workspace_ = null; - this.dragSurface_ = null; -}; - -/** - * Start dragging a bubble. This includes moving it to the drag surface. - * @package - */ -Blockly.BubbleDragger.prototype.startBubbleDrag = function() { - if (!Blockly.Events.getGroup()) { - Blockly.Events.setGroup(true); - } - - this.workspace_.setResizesEnabled(false); - this.draggingBubble_.setAutoLayout(false); - if (this.dragSurface_) { - this.moveToDragSurface_(); - } - - this.draggingBubble_.setDragging && this.draggingBubble_.setDragging(true); - - var toolbox = this.workspace_.getToolbox(); - if (toolbox) { - var style = this.draggingBubble_.isDeletable() ? 'blocklyToolboxDelete' : - 'blocklyToolboxGrab'; - toolbox.addStyle(style); - } -}; - -/** - * Execute a step of bubble dragging, based on the given event. Update the - * display accordingly. - * @param {!Event} e The most recent move event. - * @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has - * moved from the position at the start of the drag, in pixel units. - * @package - */ -Blockly.BubbleDragger.prototype.dragBubble = function(e, currentDragDeltaXY) { - var delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY); - var newLoc = goog.math.Coordinate.sum(this.startXY_, delta); - - this.draggingBubble_.moveDuringDrag(this.dragSurface_, newLoc); - - if (this.draggingBubble_.isDeletable()) { - this.deleteArea_ = this.workspace_.isDeleteArea(e); - this.updateCursorDuringBubbleDrag_(); - } -}; - -/** - * Shut the trash can and, if necessary, delete the dragging bubble. - * Should be called at the end of a bubble drag. - * @return {boolean} whether the bubble was deleted. - * @private - */ -Blockly.BubbleDragger.prototype.maybeDeleteBubble_ = function() { - var trashcan = this.workspace_.trashcan; - - if (this.wouldDeleteBubble_) { - if (trashcan) { - setTimeout(trashcan.close.bind(trashcan), 100); - } - // Fire a move event, so we know where to go back to for an undo. - this.fireMoveEvent_(); - this.draggingBubble_.dispose(false, true); - } else if (trashcan) { - // Make sure the trash can is closed. - trashcan.close(); - } - return this.wouldDeleteBubble_; -}; - -/** - * Update the cursor (and possibly the trash can lid) to reflect whether the - * dragging bubble would be deleted if released immediately. - * @private - */ -Blockly.BubbleDragger.prototype.updateCursorDuringBubbleDrag_ = function() { - this.wouldDeleteBubble_ = this.deleteArea_ != Blockly.DELETE_AREA_NONE; - var trashcan = this.workspace_.trashcan; - if (this.wouldDeleteBubble_) { - this.draggingBubble_.setDeleteStyle(true); - if (this.deleteArea_ == Blockly.DELETE_AREA_TRASH && trashcan) { - trashcan.setOpen_(true); - } - } else { - this.draggingBubble_.setDeleteStyle(false); - if (trashcan) { - trashcan.setOpen_(false); - } - } -}; - -/** - * Finish a bubble drag and put the bubble back on the workspace. - * @param {!Event} e The mouseup/touchend event. - * @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has - * moved from the position at the start of the drag, in pixel units. - * @package - */ -Blockly.BubbleDragger.prototype.endBubbleDrag = function( - e, currentDragDeltaXY) { - // Make sure internal state is fresh. - this.dragBubble(e, currentDragDeltaXY); - - var delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY); - var newLoc = goog.math.Coordinate.sum(this.startXY_, delta); - - // Move the bubble to its final location. - this.draggingBubble_.moveTo(newLoc.x, newLoc.y); - var deleted = this.maybeDeleteBubble_(); - - if (!deleted) { - // Put everything back onto the bubble canvas. - if (this.dragSurface_) { - this.dragSurface_.clearAndHide(this.workspace_.getBubbleCanvas()); - } - - this.draggingBubble_.setDragging && this.draggingBubble_.setDragging(false); - this.fireMoveEvent_(); - } - this.workspace_.setResizesEnabled(true); - - if (this.workspace_.toolbox_) { - var style = this.draggingBubble_.isDeletable() ? 'blocklyToolboxDelete' : - 'blocklyToolboxGrab'; - this.workspace_.toolbox_.removeStyle(style); - } - Blockly.Events.setGroup(false); -}; - -/** - * Fire a move event at the end of a bubble drag. - * @private - */ -Blockly.BubbleDragger.prototype.fireMoveEvent_ = function() { - var event = null; - if (this.draggingBubble_.isComment) { - event = new Blockly.Events.CommentMove(this.draggingBubble_); - } else if (this.draggingBubble_ instanceof Blockly.ScratchBubble) { - event = new Blockly.Events.CommentMove(this.draggingBubble_.comment); - } else { - return; - } - event.setOldCoordinate(this.startXY_); - event.recordNew(); - Blockly.Events.fire(event); -}; - -/** - * Convert a coordinate object from pixels to workspace units, including a - * correction for mutator workspaces. - * This function does not consider differing origins. It simply scales the - * input's x and y values. - * @param {!goog.math.Coordinate} pixelCoord A coordinate with x and y values - * in css pixel units. - * @return {!goog.math.Coordinate} The input coordinate divided by the workspace - * scale. - * @private - */ -Blockly.BubbleDragger.prototype.pixelsToWorkspaceUnits_ = function(pixelCoord) { - var result = new goog.math.Coordinate(pixelCoord.x / this.workspace_.scale, - pixelCoord.y / this.workspace_.scale); - if (this.workspace_.isMutator) { - // If we're in a mutator, its scale is always 1, purely because of some - // oddities in our rendering optimizations. The actual scale is the same as - // the scale on the parent workspace. - // Fix that for dragging. - var mainScale = this.workspace_.options.parentWorkspace.scale; - result = result.scale(1 / mainScale); - } - return result; -}; -/** - * Move the bubble onto the drag surface at the beginning of a drag. Move the - * drag surface to preserve the apparent location of the bubble. - * @private - */ -Blockly.BubbleDragger.prototype.moveToDragSurface_ = function() { - this.draggingBubble_.moveTo(0, 0); - this.dragSurface_.translateSurface(this.startXY_.x, this.startXY_.y); - // Execute the move on the top-level SVG component. - this.dragSurface_.setBlocksAndShow(this.draggingBubble_.getSvgRoot()); -}; diff --git a/core/colours.js b/core/colours.js deleted file mode 100644 index 78b259c878..0000000000 --- a/core/colours.js +++ /dev/null @@ -1,160 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Massachusetts Institute of Technology - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -goog.provide('Blockly.Colours'); - -Blockly.Colours = { - // SVG colours: these must be specificed in #RRGGBB style - // To add an opacity, this must be specified as a separate property (for SVG fill-opacity) - "motion": { - "primary": "#4C97FF", - "secondary": "#4280D7", - "tertiary": "#3373CC", - "quaternary": "#3373CC" - }, - "looks": { - "primary": "#9966FF", - "secondary": "#855CD6", - "tertiary": "#774DCB", - "quaternary": "#774DCB" - }, - "sounds": { - "primary": "#CF63CF", - "secondary": "#C94FC9", - "tertiary": "#BD42BD", - "quaternary": "#BD42BD" - }, - "control": { - "primary": "#FFAB19", - "secondary": "#EC9C13", - "tertiary": "#CF8B17", - "quaternary": "#CF8B17" - }, - "event": { - "primary": "#FFBF00", - "secondary": "#E6AC00", - "tertiary": "#CC9900", - "quaternary": "#CC9900" - }, - "sensing": { - "primary": "#5CB1D6", - "secondary": "#47A8D1", - "tertiary": "#2E8EB8", - "quaternary": "#2E8EB8" - }, - "pen": { - "primary": "#0fBD8C", - "secondary": "#0DA57A", - "tertiary": "#0B8E69", - "quaternary": "#0B8E69" - }, - "operators": { - "primary": "#59C059", - "secondary": "#46B946", - "tertiary": "#389438", - "quaternary": "#389438" - }, - "data": { - "primary": "#FF8C1A", - "secondary": "#FF8000", - "tertiary": "#DB6E00", - "quaternary": "#DB6E00" - }, - // This is not a new category, but rather for differentiation - // between lists and scalar variables. - "data_lists": { - "primary": "#FF661A", - "secondary": "#FF5500", - "tertiary": "#E64D00", - "quaternary": "#E64D00" - }, - "more": { - "primary": "#FF6680", - "secondary": "#FF4D6A", - "tertiary": "#FF3355", - "quaternary": "#FF3355" - }, - "text": "#FFFFFF", - "workspace": "#F9F9F9", - "toolboxHover": "#4C97FF", - "toolboxSelected": "#e9eef2", - "toolboxText": "#575E75", - "toolbox": "#FFFFFF", - "flyout": "#F9F9F9", - "scrollbar": "#CECDCE", - "scrollbarHover": '#CECDCE', - "textField": "#FFFFFF", - "textFieldText": "#575E75", - "insertionMarker": "#000000", - "insertionMarkerOpacity": 0.2, - "dragShadowOpacity": 0.3, - "stackGlow": "#FFF200", - "stackGlowSize": 4, - "stackGlowOpacity": 1, - "replacementGlow": "#FFFFFF", - "replacementGlowSize": 2, - "replacementGlowOpacity": 1, - "colourPickerStroke": "#FFFFFF", - // CSS colours: support RGBA - "fieldShadow": "rgba(0,0,0,0.1)", - "dropDownShadow": "rgba(0, 0, 0, .3)", - "numPadBackground": "#547AB2", - "numPadBorder": "#435F91", - "numPadActiveBackground": "#435F91", - "numPadText": "white", // Do not use hex here, it cannot be inlined with data-uri SVG - "valueReportBackground": "#FFFFFF", - "valueReportBorder": "#AAAAAA", - "menuHover": "rgba(0, 0, 0, 0.2)" -}; - -/** - * Override the colours in Blockly.Colours with new values basded on the - * given dictionary. - * @param {!Object} colours Dictionary of colour properties and new values. - * @package - */ -Blockly.Colours.overrideColours = function(colours) { - // Colour overrides provided by the injection - if (colours) { - for (var colourProperty in colours) { - if (colours.hasOwnProperty(colourProperty) && - Blockly.Colours.hasOwnProperty(colourProperty)) { - // If a property is in both colours option and Blockly.Colours, - // set the Blockly.Colours value to the override. - // Override Blockly category color object properties with those - // provided. - var colourPropertyValue = colours[colourProperty]; - if (goog.isObject(colourPropertyValue)) { - for (var colourSequence in colourPropertyValue) { - if (colourPropertyValue.hasOwnProperty(colourSequence) && - Blockly.Colours[colourProperty].hasOwnProperty(colourSequence)) { - Blockly.Colours[colourProperty][colourSequence] = - colourPropertyValue[colourSequence]; - } - } - } else { - Blockly.Colours[colourProperty] = colourPropertyValue; - } - } - } - } -}; diff --git a/core/comment.js b/core/comment.js deleted file mode 100644 index 65e1bceca9..0000000000 --- a/core/comment.js +++ /dev/null @@ -1,293 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2011 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Object representing a code comment. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.Comment'); - -goog.require('Blockly.Bubble'); -goog.require('Blockly.Events.BlockChange'); -goog.require('Blockly.Events.Ui'); -goog.require('Blockly.Icon'); -goog.require('goog.userAgent'); - - -/** - * Class for a comment. - * @param {!Blockly.Block} block The block associated with this comment. - * @extends {Blockly.Icon} - * @constructor - */ -Blockly.Comment = function(block) { - Blockly.Comment.superClass_.constructor.call(this, block); - this.createIcon(); -}; -goog.inherits(Blockly.Comment, Blockly.Icon); - -/** - * Comment text (if bubble is not visible). - * @private - */ -Blockly.Comment.prototype.text_ = ''; - -/** - * Width of bubble. - * @private - */ -Blockly.Comment.prototype.width_ = 160; - -/** - * Height of bubble. - * @private - */ -Blockly.Comment.prototype.height_ = 80; - -/** - * Draw the comment icon. - * @param {!Element} group The icon group. - * @private - */ -Blockly.Comment.prototype.drawIcon_ = function(group) { - // Circle. - Blockly.utils.createSvgElement('circle', - {'class': 'blocklyIconShape', 'r': '8', 'cx': '8', 'cy': '8'}, - group); - // Can't use a real '?' text character since different browsers and operating - // systems render it differently. - // Body of question mark. - Blockly.utils.createSvgElement('path', - { - 'class': 'blocklyIconSymbol', - 'd': 'm6.8,10h2c0.003,-0.617 0.271,-0.962 0.633,-1.266 2.875,-2.405' + - '0.607,-5.534 -3.765,-3.874v1.7c3.12,-1.657 3.698,0.118 2.336,1.25' + - '-1.201,0.998 -1.201,1.528 -1.204,2.19z' - }, - group); - // Dot of question mark. - Blockly.utils.createSvgElement('rect', - { - 'class': 'blocklyIconSymbol', - 'x': '6.8', - 'y': '10.78', - 'height': '2', - 'width': '2' - }, - group); -}; - -/** - * Create the editor for the comment's bubble. - * @return {!Element} The top-level node of the editor. - * @private - */ -Blockly.Comment.prototype.createEditor_ = function() { - /* Create the editor. Here's the markup that will be generated: - - - - - - */ - this.foreignObject_ = Blockly.utils.createSvgElement('foreignObject', - {'x': Blockly.Bubble.BORDER_WIDTH, 'y': Blockly.Bubble.BORDER_WIDTH}, - null); - var body = document.createElementNS(Blockly.HTML_NS, 'body'); - body.setAttribute('xmlns', Blockly.HTML_NS); - body.className = 'blocklyMinimalBody'; - var textarea = document.createElementNS(Blockly.HTML_NS, 'textarea'); - textarea.className = 'blocklyCommentTextarea'; - textarea.setAttribute('dir', this.block_.RTL ? 'RTL' : 'LTR'); - body.appendChild(textarea); - this.textarea_ = textarea; - this.foreignObject_.appendChild(body); - Blockly.bindEventWithChecks_(textarea, 'mouseup', this, this.textareaFocus_); - // Don't zoom with mousewheel. - Blockly.bindEventWithChecks_(textarea, 'wheel', this, function(e) { - e.stopPropagation(); - }); - Blockly.bindEventWithChecks_(textarea, 'change', this, function(_e) { - if (this.text_ != textarea.value) { - Blockly.Events.fire(new Blockly.Events.BlockChange( - this.block_, 'comment', null, this.text_, textarea.value)); - this.text_ = textarea.value; - } - }); - setTimeout(function() { - textarea.focus(); - }, 0); - return this.foreignObject_; -}; - -/** - * Add or remove editability of the comment. - * @override - */ -Blockly.Comment.prototype.updateEditable = function() { - if (this.isVisible()) { - // Toggling visibility will force a rerendering. - this.setVisible(false); - this.setVisible(true); - } - // Allow the icon to update. - Blockly.Icon.prototype.updateEditable.call(this); -}; - -/** - * Callback function triggered when the bubble has resized. - * Resize the text area accordingly. - * @private - */ -Blockly.Comment.prototype.resizeBubble_ = function() { - if (this.isVisible()) { - var size = this.bubble_.getBubbleSize(); - var doubleBorderWidth = 2 * Blockly.Bubble.BORDER_WIDTH; - this.foreignObject_.setAttribute('width', size.width - doubleBorderWidth); - this.foreignObject_.setAttribute('height', size.height - doubleBorderWidth); - this.textarea_.style.width = (size.width - doubleBorderWidth - 4) + 'px'; - this.textarea_.style.height = (size.height - doubleBorderWidth - 4) + 'px'; - } -}; - -/** - * Show or hide the comment bubble. - * @param {boolean} visible True if the bubble should be visible. - */ -Blockly.Comment.prototype.setVisible = function(visible) { - if (visible == this.isVisible()) { - // No change. - return; - } - Blockly.Events.fire( - new Blockly.Events.Ui(this.block_, 'commentOpen', !visible, visible)); - if ((!this.block_.isEditable() && !this.textarea_) || goog.userAgent.IE) { - // Steal the code from warnings to make an uneditable text bubble. - // MSIE does not support foreignobject; textareas are impossible. - // http://msdn.microsoft.com/en-us/library/hh834675%28v=vs.85%29.aspx - // Always treat comments in IE as uneditable. - Blockly.Warning.prototype.setVisible.call(this, visible); - return; - } - // Save the bubble stats before the visibility switch. - var text = this.getText(); - var size = this.getBubbleSize(); - if (visible) { - // Create the bubble. - this.bubble_ = new Blockly.Bubble( - /** @type {!Blockly.WorkspaceSvg} */ (this.block_.workspace), - this.createEditor_(), this.block_.svgPath_, - this.iconXY_, this.width_, this.height_); - // Expose this comment's block's ID on its top-level SVG group. - this.bubble_.setSvgId(this.block_.id); - this.bubble_.registerResizeEvent(this.resizeBubble_.bind(this)); - this.updateColour(); - } else { - // Dispose of the bubble. - this.bubble_.dispose(); - this.bubble_ = null; - this.textarea_ = null; - this.foreignObject_ = null; - } - // Restore the bubble stats after the visibility switch. - this.setText(text); - this.setBubbleSize(size.width, size.height); -}; - -/** - * Bring the comment to the top of the stack when clicked on. - * @param {!Event} _e Mouse up event. - * @private - */ -Blockly.Comment.prototype.textareaFocus_ = function(_e) { - // Ideally this would be hooked to the focus event for the comment. - // This is tied to mousedown, however doing so in Firefox swallows the cursor - // for unknown reasons. - // See https://github.com/LLK/scratch-blocks/issues/1631 for more history. - if (this.bubble_.promote_()) { - // Since the act of moving this node within the DOM causes a loss of focus, - // we need to reapply the focus. - this.textarea_.focus(); - } -}; - -/** - * Get the dimensions of this comment's bubble. - * @return {!Object} Object with width and height properties. - */ -Blockly.Comment.prototype.getBubbleSize = function() { - if (this.isVisible()) { - return this.bubble_.getBubbleSize(); - } else { - return {width: this.width_, height: this.height_}; - } -}; - -/** - * Size this comment's bubble. - * @param {number} width Width of the bubble. - * @param {number} height Height of the bubble. - */ -Blockly.Comment.prototype.setBubbleSize = function(width, height) { - if (this.textarea_) { - this.bubble_.setBubbleSize(width, height); - } else { - this.width_ = width; - this.height_ = height; - } -}; - -/** - * Returns this comment's text. - * @return {string} Comment text. - */ -Blockly.Comment.prototype.getText = function() { - return this.textarea_ ? this.textarea_.value : this.text_; -}; - -/** - * Set this comment's text. - * @param {string} text Comment text. - */ -Blockly.Comment.prototype.setText = function(text) { - if (this.text_ != text) { - Blockly.Events.fire(new Blockly.Events.BlockChange( - this.block_, 'comment', null, this.text_, text)); - this.text_ = text; - } - if (this.textarea_) { - this.textarea_.value = text; - } -}; - -/** - * Dispose of this comment. - */ -Blockly.Comment.prototype.dispose = function() { - if (Blockly.Events.isEnabled()) { - this.setText(''); // Fire event to delete comment. - } - this.block_.comment = null; - Blockly.Icon.prototype.dispose.call(this); -}; diff --git a/core/comment_events.js b/core/comment_events.js deleted file mode 100644 index d9a13c7f2d..0000000000 --- a/core/comment_events.js +++ /dev/null @@ -1,539 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2018 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Classes for all comment events. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.Events.CommentBase'); -goog.provide('Blockly.Events.CommentChange'); -goog.provide('Blockly.Events.CommentCreate'); -goog.provide('Blockly.Events.CommentDelete'); -goog.provide('Blockly.Events.CommentMove'); - -goog.require('Blockly.Events'); -goog.require('Blockly.Events.Abstract'); - -goog.require('goog.math.Coordinate'); - - -/** - * Abstract class for a comment event. - * @param {Blockly.WorkspaceComment | Blockly.ScratchBlockComment} comment - * The comment this event corresponds to. - * @extends {Blockly.Events.Abstract} - * @constructor - */ -Blockly.Events.CommentBase = function(comment) { - /** - * The ID of the comment this event pertains to. - * @type {string} - */ - this.commentId = comment.id; - - /** - * The workspace identifier for this event. - * @type {string} - */ - this.workspaceId = comment.workspace.id; - - /** - * The ID of the block this comment belongs to or null if it is not a block - * comment. - * @type {string} - */ - this.blockId = comment.blockId || null; - - /** - * 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} - */ - this.group = Blockly.Events.group_; - - /** - * Sets whether the event should be added to the undo stack. - * @type {boolean} - */ - this.recordUndo = Blockly.Events.recordUndo; -}; -goog.inherits(Blockly.Events.CommentBase, Blockly.Events.Abstract); - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.CommentBase.prototype.toJson = function() { - var json = { - 'type': this.type - }; - if (this.group) { - json['group'] = this.group; - } - if (this.commentId) { - json['commentId'] = this.commentId; - } - if (this.blockId) { - json['blockId'] = this.blockId; - } - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.CommentBase.prototype.fromJson = function(json) { - this.commentId = json['commentId']; - this.group = json['group']; - this.blockId = json['blockId']; -}; - -/** - * Helper function for finding the comment this event pertains to. - * @return {?(Blockly.WorkspaceComment | Blockly.ScratchBlockComment)} - * The comment this event pertains to, or null if it no longer exists. - * @private - */ -Blockly.Events.CommentBase.prototype.getComment_ = function() { - var workspace = this.getEventWorkspace_(); - return workspace.getCommentById(this.commentId); -}; - -/** - * Class for a comment change event. - * @param {Blockly.WorkspaceComment | Blockly.ScratchBlockComment} comment - * The comment that is being changed. Null for a blank event. - * @param {!object} oldContents Object containing previous state of a comment's - * properties. The possible properties can be: 'minimized', 'text', or - * 'width' and 'height' together. Must contain the same property (or in the - * case of 'width' and 'height' properties) as the 'newContents' param. - * @param {!object} newContents Object containing the new state of a comment's - * properties. The possible properties can be: 'minimized', 'text', or - * 'width' and 'height' together. Must contain the same property (or in the - * case of 'width' and 'height' properties) as the 'oldContents' param. - * @extends {Blockly.Events.CommentBase} - * @constructor - */ -Blockly.Events.CommentChange = function(comment, oldContents, newContents) { - if (!comment) { - return; // Blank event to be populated by fromJson. - } - Blockly.Events.CommentChange.superClass_.constructor.call(this, comment); - this.oldContents_ = oldContents; - this.newContents_ = newContents; -}; -goog.inherits(Blockly.Events.CommentChange, Blockly.Events.CommentBase); - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.CommentChange.prototype.type = Blockly.Events.COMMENT_CHANGE; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.CommentChange.prototype.toJson = function() { - var json = Blockly.Events.CommentChange.superClass_.toJson.call(this); - json['newContents'] = this.newContents_; - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.CommentChange.prototype.fromJson = function(json) { - Blockly.Events.CommentChange.superClass_.fromJson.call(this, json); - this.newContents_ = json['newValue']; -}; - -/** - * Does this event record any change of state? - * @return {boolean} False if something changed. - */ -Blockly.Events.CommentChange.prototype.isNull = function() { - return this.oldContents_ == this.newContents_; -}; - -/** - * Run a change event. - * @param {boolean} forward True if run forward, false if run backward (undo). - */ -Blockly.Events.CommentChange.prototype.run = function(forward) { - var comment = this.getComment_(); - if (!comment) { - console.warn('Can\'t change non-existent comment: ' + this.commentId); - return; - } - var contents = forward ? this.newContents_ : this.oldContents_; - - if (contents.hasOwnProperty('minimized')) { - comment.setMinimized(contents.minimized); - } - if (contents.hasOwnProperty('width') && contents.hasOwnProperty('height')) { - comment.setSize(contents.width, contents.height); - } - if (contents.hasOwnProperty('text')) { - comment.setText(contents.text); - } -}; - -/** - * Class for a comment creation event. - * @param {Blockly.WorkspaceComment | Blockly.ScratchBlockComment} comment - * The created comment. Null for a blank event. - * @param {string=} opt_blockId Optional id for the block this comment belongs - * to, if it is a block comment. - * @extends {Blockly.Events.CommentBase} - * @constructor - */ -Blockly.Events.CommentCreate = function(comment) { - if (!comment) { - return; // Blank event to be populated by fromJson. - } - Blockly.Events.CommentCreate.superClass_.constructor.call(this, comment); - - /** - * The text content of this comment. - * @type {string} - */ - this.text = comment.getText(); - - /** - * The XY position of this comment on the workspace. - * @type {goog.math.Coordinate} - */ - this.xy = comment.getXY(); - - var hw = comment.getHeightWidth(); - - /** - * The width of this comment when it is full size. - * @type {number} - */ - this.width = hw.width; - - /** - * The height of this comment when it is full size. - * @type {number} - */ - this.height = hw.height; - - /** - * Whether or not this comment is minimized. - * @type {boolean} - */ - this.minimized = comment.isMinimized() || false; - - this.xml = comment.toXmlWithXY(); -}; -goog.inherits(Blockly.Events.CommentCreate, Blockly.Events.CommentBase); - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.CommentCreate.prototype.type = Blockly.Events.COMMENT_CREATE; - -/** - * Encode the event as JSON. - * TODO (github.com/google/blockly/issues/1266): "Full" and "minimal" - * serialization. - * @return {!Object} JSON representation. - */ -Blockly.Events.CommentCreate.prototype.toJson = function() { - var json = Blockly.Events.CommentCreate.superClass_.toJson.call(this); - json['xml'] = Blockly.Xml.domToText(this.xml); - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.CommentCreate.prototype.fromJson = function(json) { - Blockly.Events.CommentCreate.superClass_.fromJson.call(this, json); - this.xml = Blockly.Xml.textToDom('' + json['xml'] + '').firstChild; -}; - -/** - * Run a creation event. - * @param {boolean} forward True if run forward, false if run backward (undo). - */ -Blockly.Events.CommentCreate.prototype.run = function(forward) { - if (forward) { - var workspace = this.getEventWorkspace_(); - if (this.blockId) { - var block = workspace.getBlockById(this.blockId); - if (block) { - block.setCommentText('', this.commentId, this.xy.x, this.xy.y, this.minimized); - } - } else { - var xml = goog.dom.createDom('xml'); - xml.appendChild(this.xml); - Blockly.Xml.domToWorkspace(xml, workspace); - } - } else { - var comment = this.getComment_(); - if (comment) { - comment.dispose(false, false); - } else { - // Only complain about root-level block. - console.warn("Can't uncreate non-existent comment: " + this.commentId); - } - } -}; - -/** - * Class for a comment deletion event. - * @param {Blockly.WorkspaceComment | Blockly.ScratchBlockComment} comment - * The deleted comment. Null for a blank event. - * @extends {Blockly.Events.CommentBase} - * @constructor - */ -Blockly.Events.CommentDelete = function(comment) { - if (!comment) { - return; // Blank event to be populated by fromJson. - } - Blockly.Events.CommentDelete.superClass_.constructor.call(this, comment); - this.xy = comment.getXY(); - this.minimized = comment.isMinimized() || false; - this.text = comment.getText(); - var hw = comment.getHeightWidth(); - this.height = hw.height; - this.width = hw.width; - - this.xml = comment.toXmlWithXY(); -}; -goog.inherits(Blockly.Events.CommentDelete, Blockly.Events.CommentBase); - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.CommentDelete.prototype.type = Blockly.Events.COMMENT_DELETE; - -/** - * Encode the event as JSON. - * TODO (github.com/google/blockly/issues/1266): "Full" and "minimal" - * serialization. - * @return {!Object} JSON representation. - */ -Blockly.Events.CommentDelete.prototype.toJson = function() { - var json = Blockly.Events.CommentDelete.superClass_.toJson.call(this); - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.CommentDelete.prototype.fromJson = function(json) { - Blockly.Events.CommentDelete.superClass_.fromJson.call(this, json); -}; - -/** - * Run a creation event. - * @param {boolean} forward True if run forward, false if run backward (undo). - */ -Blockly.Events.CommentDelete.prototype.run = function(forward) { - if (forward) { - var comment = this.getComment_(); - if (comment) { - comment.dispose(false, false); - } else { - // Only complain about root-level block. - console.warn("Can't delete non-existent comment: " + this.commentId); - } - } else { - var workspace = this.getEventWorkspace_(); - if (this.blockId) { - var block = workspace.getBlockById(this.blockId); - block.setCommentText(this.text, this.commentId, this.xy.x, this.xy.y, this.minimized); - block.comment.setSize(this.width, this.height); - } else { - var xml = goog.dom.createDom('xml'); - xml.appendChild(this.xml); - Blockly.Xml.domToWorkspace(xml, workspace); - } - } -}; - -/** - * Class for a comment move event. Created before the move. - * @param {Blockly.WorkspaceComment | Blockly.ScratchBlockComment} comment - * The comment that is being moved. Null for a blank event. - * @extends {Blockly.Events.CommentBase} - * @constructor - */ -Blockly.Events.CommentMove = function(comment) { - if (!comment) { - return; // Blank event to be populated by fromJson. - } - Blockly.Events.CommentMove.superClass_.constructor.call(this, comment); - - /** - * The comment that is being moved. Will be cleared after recording the new - * location. - * @type {?Blockly.WorkspaceComment | Blockly.ScratchBlockComment} - */ - this.comment_ = comment; - - this.workspaceWidth_ = comment.workspace.getWidth(); - /** - * The location before the move, in workspace coordinates. - * @type {!goog.math.Coordinate} - */ - this.oldCoordinate_ = this.currentLocation_(); - - /** - * The location after the move, in workspace coordinates. - * @type {!goog.math.Coordinate} - */ - this.newCoordinate_ = null; -}; -goog.inherits(Blockly.Events.CommentMove, Blockly.Events.CommentBase); - -/** - * Calculate the current, language agnostic location of the comment. - * This value should not report different numbers in LTR vs. RTL. - * @return {goog.math.Coordinate} The location of the comment. - * @private - */ -Blockly.Events.CommentMove.prototype.currentLocation_ = function() { - var xy = this.comment_.getXY(); - if (!this.comment_.workspace.RTL) { - return xy; - } - - var rtlAwareX; - if (this.comment_ instanceof Blockly.ScratchBlockComment) { - var commentWidth = this.comment_.getBubbleSize().width; - rtlAwareX = this.workspaceWidth_ - xy.x - commentWidth; - } else { - rtlAwareX = this.workspaceWidth_ - xy.x; - } - return new goog.math.Coordinate(rtlAwareX, xy.y); -}; - -/** - * Record the comment's new location. Called after the move. Can only be - * called once. - */ -Blockly.Events.CommentMove.prototype.recordNew = function() { - if (!this.comment_) { - throw new Error('Tried to record the new position of a comment on the ' + - 'same event twice.'); - } - this.newCoordinate_ = this.currentLocation_(); - this.comment_ = null; -}; - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.CommentMove.prototype.type = Blockly.Events.COMMENT_MOVE; - -/** - * 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 {!goog.math.Coordinate} xy The location before the move, in workspace - * coordinates. - */ -Blockly.Events.CommentMove.prototype.setOldCoordinate = function(xy) { - this.oldCoordinate_ = new goog.math.Coordinate(this.comment_.workspace.RTL ? - this.workspaceWidth_ - xy.x : xy.x, xy.y); -}; - -/** - * Encode the event as JSON. - * TODO (github.com/google/blockly/issues/1266): "Full" and "minimal" - * serialization. - * @return {!Object} JSON representation. - */ -Blockly.Events.CommentMove.prototype.toJson = function() { - var json = Blockly.Events.CommentMove.superClass_.toJson.call(this); - if (this.newCoordinate_) { - json['newCoordinate'] = Math.round(this.newCoordinate_.x) + ',' + - Math.round(this.newCoordinate_.y); - } - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.CommentMove.prototype.fromJson = function(json) { - Blockly.Events.CommentMove.superClass_.fromJson.call(this, json); - - if (json['newCoordinate']) { - var xy = json['newCoordinate'].split(','); - this.newCoordinate_ = - new goog.math.Coordinate(parseFloat(xy[0]), parseFloat(xy[1])); - } -}; - -/** - * Does this event record any change of state? - * @return {boolean} False if something changed. - */ -Blockly.Events.CommentMove.prototype.isNull = function() { - return goog.math.Coordinate.equals(this.oldCoordinate_, this.newCoordinate_); -}; - -/** - * Run a move event. - * @param {boolean} forward True if run forward, false if run backward (undo). - */ -Blockly.Events.CommentMove.prototype.run = function(forward) { - var comment = this.getComment_(); - if (!comment) { - console.warn('Can\'t move non-existent comment: ' + this.commentId); - return; - } - - var target = forward ? this.newCoordinate_ : this.oldCoordinate_; - - if (comment instanceof Blockly.ScratchBlockComment) { - if (comment.workspace.RTL) { - comment.moveTo(this.workspaceWidth_ - target.x, target.y); - } else { - comment.moveTo(target.x, target.y); - } - } else { - // TODO: Check if the comment is being dragged, and give up if so. - var current = comment.getXY(); - if (comment.workspace.RTL) { - var deltaX = target.x - (this.workspaceWidth_ - current.x); - comment.moveBy(-deltaX, target.y - current.y); - } else { - comment.moveBy(target.x - current.x, target.y - current.y); - } - - } -}; diff --git a/core/connection.js b/core/connection.js deleted file mode 100644 index cfc0f779c3..0000000000 --- a/core/connection.js +++ /dev/null @@ -1,766 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2011 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Components for creating connections between blocks. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.Connection'); - -goog.require('Blockly.Events.BlockMove'); - -goog.require('goog.asserts'); -goog.require('goog.dom'); - - -/** - * Class for a connection between blocks. - * @param {!Blockly.Block} source The block establishing this connection. - * @param {number} type The type of the connection. - * @constructor - */ -Blockly.Connection = function(source, type) { - /** - * @type {!Blockly.Block} - * @protected - */ - this.sourceBlock_ = source; - /** @type {number} */ - this.type = type; - // Shortcut for the databases for this connection's workspace. - if (source.workspace.connectionDBList) { - this.db_ = source.workspace.connectionDBList[type]; - this.dbOpposite_ = - source.workspace.connectionDBList[Blockly.OPPOSITE_TYPE[type]]; - this.hidden_ = !this.db_; - } -}; - -/** - * Constants for checking whether two connections are compatible. - */ -Blockly.Connection.CAN_CONNECT = 0; -Blockly.Connection.REASON_SELF_CONNECTION = 1; -Blockly.Connection.REASON_WRONG_TYPE = 2; -Blockly.Connection.REASON_TARGET_NULL = 3; -Blockly.Connection.REASON_CHECKS_FAILED = 4; -Blockly.Connection.REASON_DIFFERENT_WORKSPACES = 5; -Blockly.Connection.REASON_SHADOW_PARENT = 6; -// Fixes #1127, but may be the wrong solution. -Blockly.Connection.REASON_CUSTOM_PROCEDURE = 7; - -/** - * Connection this connection connects to. Null if not connected. - * @type {Blockly.Connection} - */ -Blockly.Connection.prototype.targetConnection = null; - -/** - * List of compatible value types. Null if all types are compatible. - * @type {Array} - * @private - */ -Blockly.Connection.prototype.check_ = null; - -/** - * DOM representation of a shadow block, or null if none. - * @type {Element} - * @private - */ -Blockly.Connection.prototype.shadowDom_ = null; - -/** - * Horizontal location of this connection. - * @type {number} - * @protected - */ -Blockly.Connection.prototype.x_ = 0; - -/** - * Vertical location of this connection. - * @type {number} - * @protected - */ -Blockly.Connection.prototype.y_ = 0; - -/** - * Has this connection been added to the connection database? - * @type {boolean} - * @protected - */ -Blockly.Connection.prototype.inDB_ = false; - -/** - * Connection database for connections of this type on the current workspace. - * @type {Blockly.ConnectionDB} - * @protected - */ -Blockly.Connection.prototype.db_ = null; - -/** - * Connection database for connections compatible with this type on the - * current workspace. - * @type {Blockly.ConnectionDB} - * @protected - */ -Blockly.Connection.prototype.dbOpposite_ = null; - -/** - * Whether this connections is hidden (not tracked in a database) or not. - * @type {boolean} - * @protected - */ -Blockly.Connection.prototype.hidden_ = null; - -/** - * Connect two connections together. This is the connection on the superior - * block. - * @param {!Blockly.Connection} childConnection Connection on inferior block. - * @protected - */ -Blockly.Connection.prototype.connect_ = function(childConnection) { - var parentConnection = this; - var parentBlock = parentConnection.getSourceBlock(); - var childBlock = childConnection.getSourceBlock(); - var isSurroundingC = false; - if (parentConnection == parentBlock.getFirstStatementConnection()) { - isSurroundingC = true; - } - // Disconnect any existing parent on the child connection. - if (childConnection.isConnected()) { - // Scratch-specific behaviour: - // If we're using a c-shaped block to surround a stack, remember where the - // stack used to be connected. - if (isSurroundingC) { - var previousParentConnection = childConnection.targetConnection; - } - childConnection.disconnect(); - } - if (parentConnection.isConnected()) { - // Other connection is already connected to something. - // Disconnect it and reattach it or bump it as needed. - var orphanBlock = parentConnection.targetBlock(); - var shadowDom = parentConnection.getShadowDom(); - // Temporarily set the shadow DOM to null so it does not respawn. - parentConnection.setShadowDom(null); - // Displaced shadow blocks dissolve rather than reattaching or bumping. - if (orphanBlock.isShadow()) { - // Save the shadow block so that field values are preserved. - shadowDom = Blockly.Xml.blockToDom(orphanBlock); - orphanBlock.dispose(); - orphanBlock = null; - } else if (parentConnection.type == Blockly.NEXT_STATEMENT) { - // Statement connections. - // Statement blocks may be inserted into the middle of a stack. - // Split the stack. - if (!orphanBlock.previousConnection) { - throw 'Orphan block does not have a previous connection.'; - } - // Attempt to reattach the orphan at the bottom of the newly inserted - // block. Since this block may be a stack, walk down to the end. - var newBlock = childBlock; - while (newBlock.nextConnection) { - var nextBlock = newBlock.getNextBlock(); - if (nextBlock && !nextBlock.isShadow()) { - newBlock = nextBlock; - } else { - if (orphanBlock.previousConnection.checkType_( - newBlock.nextConnection)) { - newBlock.nextConnection.connect(orphanBlock.previousConnection); - orphanBlock = null; - } - break; - } - } - } - if (orphanBlock) { - // Unable to reattach orphan. - parentConnection.disconnect(); - if (Blockly.Events.recordUndo) { - // Bump it off to the side after a moment. - var group = Blockly.Events.getGroup(); - setTimeout(function() { - // Verify orphan hasn't been deleted or reconnected (user on meth). - if (orphanBlock.workspace && !orphanBlock.getParent()) { - Blockly.Events.setGroup(group); - if (orphanBlock.outputConnection) { - orphanBlock.outputConnection.bumpAwayFrom_(parentConnection); - } else if (orphanBlock.previousConnection) { - orphanBlock.previousConnection.bumpAwayFrom_(parentConnection); - } - Blockly.Events.setGroup(false); - } - }, Blockly.BUMP_DELAY); - } - } - // Restore the shadow DOM. - parentConnection.setShadowDom(shadowDom); - } - - if (isSurroundingC && previousParentConnection) { - previousParentConnection.connect(parentBlock.previousConnection); - } - - var event; - if (Blockly.Events.isEnabled()) { - event = new Blockly.Events.BlockMove(childBlock); - } - // Establish the connections. - Blockly.Connection.connectReciprocally_(parentConnection, childConnection); - // Demote the inferior block so that one is a child of the superior one. - childBlock.setParent(parentBlock); - if (event) { - event.recordNew(); - Blockly.Events.fire(event); - } -}; - -/** - * Sever all links to this connection (not including from the source object). - */ -Blockly.Connection.prototype.dispose = function() { - if (this.isConnected()) { - throw 'Disconnect connection before disposing of it.'; - } - if (this.inDB_) { - this.db_.removeConnection_(this); - } - this.db_ = null; - this.dbOpposite_ = null; -}; - -/** - * @return {boolean} true if the connection is not connected or is connected to - * an insertion marker, false otherwise. - */ -Blockly.Connection.prototype.isConnectedToNonInsertionMarker = function() { - return this.targetConnection && !this.targetBlock().isInsertionMarker(); -}; - -/** - * Get the source block for this connection. - * @return {Blockly.Block} The source block, or null if there is none. - */ -Blockly.Connection.prototype.getSourceBlock = function() { - return this.sourceBlock_; -}; - -/** - * Does the connection belong to a superior block (higher in the source stack)? - * @return {boolean} True if connection faces down or right. - */ -Blockly.Connection.prototype.isSuperior = function() { - return this.type == Blockly.INPUT_VALUE || - this.type == Blockly.NEXT_STATEMENT; -}; - -/** - * Is the connection connected? - * @return {boolean} True if connection is connected to another connection. - */ -Blockly.Connection.prototype.isConnected = function() { - return !!this.targetConnection; -}; - -/** - * Checks whether the current connection can connect with the target - * connection. - * @param {Blockly.Connection} target Connection to check compatibility with. - * @return {number} Blockly.Connection.CAN_CONNECT if the connection is legal, - * an error code otherwise. - * @private - */ -Blockly.Connection.prototype.canConnectWithReason_ = function(target) { - if (!target) { - return Blockly.Connection.REASON_TARGET_NULL; - } - if (this.isSuperior()) { - var blockA = this.sourceBlock_; - var blockB = target.getSourceBlock(); - var superiorConn = this; - } else { - var blockB = this.sourceBlock_; - var blockA = target.getSourceBlock(); - var superiorConn = target; - } - if (blockA && blockA == blockB) { - return Blockly.Connection.REASON_SELF_CONNECTION; - } else if (target.type != Blockly.OPPOSITE_TYPE[this.type]) { - return Blockly.Connection.REASON_WRONG_TYPE; - } else if (blockA && blockB && blockA.workspace !== blockB.workspace) { - return Blockly.Connection.REASON_DIFFERENT_WORKSPACES; - } else if (!this.checkType_(target)) { - return Blockly.Connection.REASON_CHECKS_FAILED; - } else if (blockA.isShadow() && !blockB.isShadow()) { - return Blockly.Connection.REASON_SHADOW_PARENT; - } else if ((blockA.type == Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE && - blockB.type != Blockly.PROCEDURES_PROTOTYPE_BLOCK_TYPE && - superiorConn == blockA.getInput('custom_block').connection) || - (blockB.type == Blockly.PROCEDURES_PROTOTYPE_BLOCK_TYPE && - blockA.type != Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE)) { - // Hack to fix #1127: Fail attempts to connect to the custom_block input - // on a defnoreturn block, unless the connecting block is a specific type. - // And hack to fix #1534: Fail attempts to connect anything but a - // defnoreturn block to a prototype block. - return Blockly.Connection.REASON_CUSTOM_PROCEDURE; - } - return Blockly.Connection.CAN_CONNECT; -}; - -/** - * Checks whether the current connection and target connection are compatible - * and throws an exception if they are not. - * @param {Blockly.Connection} target The connection to check compatibility - * with. - * @private - */ -Blockly.Connection.prototype.checkConnection_ = function(target) { - switch (this.canConnectWithReason_(target)) { - case Blockly.Connection.CAN_CONNECT: - break; - case Blockly.Connection.REASON_SELF_CONNECTION: - throw 'Attempted to connect a block to itself.'; - case Blockly.Connection.REASON_DIFFERENT_WORKSPACES: - // Usually this means one block has been deleted. - throw 'Blocks not on same workspace.'; - case Blockly.Connection.REASON_WRONG_TYPE: - throw 'Attempt to connect incompatible types.'; - case Blockly.Connection.REASON_TARGET_NULL: - throw 'Target connection is null.'; - case Blockly.Connection.REASON_CHECKS_FAILED: - var msg = 'Connection checks failed. '; - msg += this + ' expected ' + this.check_ + ', found ' + target.check_; - throw msg; - case Blockly.Connection.REASON_SHADOW_PARENT: - throw 'Connecting non-shadow to shadow block.'; - case Blockly.Connection.REASON_CUSTOM_PROCEDURE: - throw 'Trying to replace a shadow on a custom procedure definition.'; - default: - throw 'Unknown connection failure: this should never happen!'; - } -}; - -/** - * Check if the two connections can be dragged to connect to each other. - * This is used by the connection database when searching for the closest - * connection. - * @param {!Blockly.Connection} candidate A nearby connection to check, which - * must be a previous connection. - * @return {boolean} True if the connection is allowed, false otherwise. - */ -Blockly.Connection.prototype.canConnectToPrevious_ = function(candidate) { - if (this.targetConnection) { - // This connection is already occupied. - // A next connection will never disconnect itself mid-drag. - return false; - } - - // Don't let blocks try to connect to themselves or ones they nest. - if (Blockly.draggingConnections_.indexOf(candidate) != -1) { - return false; - } - - var firstStatementConnection = - this.sourceBlock_.getFirstStatementConnection(); - // Is it a C-shaped (e.g. repeat) or E-shaped (e.g. if-else) block? - var isComplexStatement = firstStatementConnection != null; - var isFirstStatementConnection = this == firstStatementConnection; - var isNextConnection = this == this.sourceBlock_.nextConnection; - - // Scratch-specific behaviour: can connect to the first statement input of a - // C-shaped or E-shaped block, or to the next connection of any statement - // block, but not to the second statement input of an E-shaped block. - if (isComplexStatement && !isFirstStatementConnection && !isNextConnection) { - return false; - } - - // Complex blocks with no previous connection will not be allowed to connect - // mid-stack. - var sourceHasPreviousConn = this.sourceBlock_.previousConnection != null; - - if (isFirstStatementConnection && sourceHasPreviousConn) { - return true; - } - - if (isNextConnection || - (isFirstStatementConnection && !sourceHasPreviousConn)) { - // If the candidate is the first connection in a stack, we can connect. - if (!candidate.targetConnection) { - return true; - } - - var targetBlock = candidate.targetBlock(); - // If it is connected a real block, game over. - if (!targetBlock.isInsertionMarker()) { - return false; - } - // If it's connected to an insertion marker but that insertion marker - // is the first block in a stack, it's still fine. If that insertion - // marker is in the middle of a stack, it won't work. - return !targetBlock.getPreviousBlock(); - } -}; - -/** - * Check if the two connections can be dragged to connect to each other. - * This is used by the connection database when searching for the closest - * connection. - * @param {!Blockly.Connection} candidate A nearby connection to check. - * @return {boolean} True if the connection is allowed, false otherwise. - */ -Blockly.Connection.prototype.isConnectionAllowed = function(candidate) { - - // Don't consider insertion markers. - if (candidate.sourceBlock_.isInsertionMarker()) { - return false; - } - - // Type checking. - var canConnect = this.canConnectWithReason_(candidate); - if (canConnect != Blockly.Connection.CAN_CONNECT) { - return false; - } - - var firstStatementConnection = - this.sourceBlock_.getFirstStatementConnection(); - switch (candidate.type) { - case Blockly.PREVIOUS_STATEMENT: - return this.canConnectToPrevious_(candidate); - case Blockly.OUTPUT_VALUE: { - // Can't drag an input to an output--you have to move the inferior block. - return false; - } - case Blockly.INPUT_VALUE: { - // Offering to connect the left (male) of a value block to an already - // connected value pair is ok, we'll splice it in. - // However, don't offer to splice into an unmovable block. - if (candidate.targetConnection && - !candidate.targetBlock().isMovable() && - !candidate.targetBlock().isShadow()) { - return false; - } - break; - } - case Blockly.NEXT_STATEMENT: { - // Scratch-specific behaviour: - // If this is a c-block, we can't connect this block's - // previous connection unless we're connecting to the end of the last - // block on a stack or there's already a block connected inside the c. - if (firstStatementConnection && - this == this.sourceBlock_.previousConnection && - candidate.isConnectedToNonInsertionMarker() && - !firstStatementConnection.targetConnection) { - return false; - } - // Don't let a block with no next connection bump other blocks out of the - // stack. But covering up a shadow block or stack of shadow blocks is - // fine. Similarly, replacing a terminal statement with another terminal - // statement is allowed. - if (candidate.isConnectedToNonInsertionMarker() && - !this.sourceBlock_.nextConnection && - !candidate.targetBlock().isShadow() && - candidate.targetBlock().nextConnection) { - return false; - } - break; - } - default: - throw 'Unknown connection type in isConnectionAllowed'; - } - - // Don't let blocks try to connect to themselves or ones they nest. - if (Blockly.draggingConnections_.indexOf(candidate) != -1) { - return false; - } - - return true; -}; - -/** - * Connect this connection to another connection. - * @param {!Blockly.Connection} otherConnection Connection to connect to. - */ -Blockly.Connection.prototype.connect = function(otherConnection) { - if (this.targetConnection == otherConnection) { - // Already connected together. NOP. - return; - } - this.checkConnection_(otherConnection); - // Determine which block is superior (higher in the source stack). - if (this.isSuperior()) { - // Superior block. - this.connect_(otherConnection); - } else { - // Inferior block. - otherConnection.connect_(this); - } -}; - -/** - * Update two connections to target each other. - * @param {Blockly.Connection} first The first connection to update. - * @param {Blockly.Connection} second The second connection to update. - * @private - */ -Blockly.Connection.connectReciprocally_ = function(first, second) { - goog.asserts.assert(first && second, 'Cannot connect null connections.'); - first.targetConnection = second; - second.targetConnection = first; -}; - -/** - * Does the given block have one and only one connection point that will accept - * an orphaned block? - * @param {!Blockly.Block} block The superior block. - * @param {!Blockly.Block} orphanBlock The inferior block. - * @return {Blockly.Connection} The suitable connection point on 'block', - * or null. - * @private - */ -Blockly.Connection.singleConnection_ = function(block, orphanBlock) { - var connection = false; - for (var i = 0; i < block.inputList.length; i++) { - var thisConnection = block.inputList[i].connection; - if (thisConnection && thisConnection.type == Blockly.INPUT_VALUE && - orphanBlock.outputConnection.checkType_(thisConnection)) { - if (connection) { - return null; // More than one connection. - } - connection = thisConnection; - } - } - return connection; -}; - -/** - * Disconnect this connection. - */ -Blockly.Connection.prototype.disconnect = function() { - var otherConnection = this.targetConnection; - goog.asserts.assert(otherConnection, 'Source connection not connected.'); - goog.asserts.assert(otherConnection.targetConnection == this, - 'Target connection not connected to source connection.'); - - var parentBlock, childBlock, parentConnection; - if (this.isSuperior()) { - // Superior block. - parentBlock = this.sourceBlock_; - childBlock = otherConnection.getSourceBlock(); - parentConnection = this; - } else { - // Inferior block. - parentBlock = otherConnection.getSourceBlock(); - childBlock = this.sourceBlock_; - parentConnection = otherConnection; - } - this.disconnectInternal_(parentBlock, childBlock); - parentConnection.respawnShadow_(); -}; - -/** - * Disconnect two blocks that are connected by this connection. - * @param {!Blockly.Block} parentBlock The superior block. - * @param {!Blockly.Block} childBlock The inferior block. - * @protected - */ -Blockly.Connection.prototype.disconnectInternal_ = function(parentBlock, - childBlock) { - var event; - if (Blockly.Events.isEnabled()) { - event = new Blockly.Events.BlockMove(childBlock); - } - var otherConnection = this.targetConnection; - otherConnection.targetConnection = null; - this.targetConnection = null; - childBlock.setParent(null); - if (event) { - event.recordNew(); - Blockly.Events.fire(event); - } -}; - -/** - * Respawn the shadow block if there was one connected to the this connection. - * @protected - */ -Blockly.Connection.prototype.respawnShadow_ = function() { - var parentBlock = this.getSourceBlock(); - var shadow = this.getShadowDom(); - if (parentBlock.workspace && shadow && Blockly.Events.recordUndo) { - var blockShadow = - Blockly.Xml.domToBlock(shadow, parentBlock.workspace); - if (blockShadow.outputConnection) { - this.connect(blockShadow.outputConnection); - } else if (blockShadow.previousConnection) { - this.connect(blockShadow.previousConnection); - } else { - throw 'Child block does not have output or previous statement.'; - } - } -}; - -/** - * Returns the block that this connection connects to. - * @return {Blockly.Block} The connected block or null if none is connected. - */ -Blockly.Connection.prototype.targetBlock = function() { - if (this.isConnected()) { - return this.targetConnection.getSourceBlock(); - } - return null; -}; - -/** - * Is this connection compatible with another connection with respect to the - * value type system. E.g. square_root("Hello") is not compatible. - * @param {!Blockly.Connection} otherConnection Connection to compare against. - * @return {boolean} True if the connections share a type. - * @protected - */ -Blockly.Connection.prototype.checkType_ = function(otherConnection) { - if (!this.check_ || !otherConnection.check_) { - // One or both sides are promiscuous enough that anything will fit. - return true; - } - // Find any intersection in the check lists. - for (var i = 0; i < this.check_.length; i++) { - if (otherConnection.check_.indexOf(this.check_[i]) != -1) { - return true; - } - } - // No intersection. - return false; -}; - -/** - * Function to be called when this connection's compatible types have changed. - * @private - */ -Blockly.Connection.prototype.onCheckChanged_ = function() { - // The new value type may not be compatible with the existing connection. - if (this.isConnected() && !this.checkType_(this.targetConnection)) { - var child = this.isSuperior() ? this.targetBlock() : this.sourceBlock_; - child.unplug(); - } -}; - -/** - * Change a connection's compatibility. - * @param {*} check Compatible value type or list of value types. - * Null if all types are compatible. - * @return {!Blockly.Connection} The connection being modified - * (to allow chaining). - */ -Blockly.Connection.prototype.setCheck = function(check) { - if (check) { - // Ensure that check is in an array. - if (!goog.isArray(check)) { - check = [check]; - } - this.check_ = check; - this.onCheckChanged_(); - } else { - this.check_ = null; - } - return this; -}; - -/** - * Returns a shape enum for this connection. - * Used in scratch-blocks to draw unoccupied inputs. - * @return {number} Enum representing shape. - */ -Blockly.Connection.prototype.getOutputShape = function() { - if (!this.check_) return Blockly.OUTPUT_SHAPE_ROUND; - if (this.check_.indexOf('Boolean') !== -1) { - return Blockly.OUTPUT_SHAPE_HEXAGONAL; - } - if (this.check_.indexOf('Number') !== -1) { - return Blockly.OUTPUT_SHAPE_ROUND; - } - if (this.check_.indexOf('String') !== -1) { - return Blockly.OUTPUT_SHAPE_SQUARE; - } - return Blockly.OUTPUT_SHAPE_ROUND; -}; - -/** - * Change a connection's shadow block. - * @param {Element} shadow DOM representation of a block or null. - */ -Blockly.Connection.prototype.setShadowDom = function(shadow) { - this.shadowDom_ = shadow; -}; - -/** - * Return a connection's shadow block. - * @return {Element} shadow DOM representation of a block or null. - */ -Blockly.Connection.prototype.getShadowDom = function() { - return this.shadowDom_; -}; - -/** - * Find all nearby compatible connections to this connection. - * Type checking does not apply, since this function is used for bumping. - * - * Headless configurations (the default) do not have neighboring connection, - * and always return an empty list (the default). - * {@link Blockly.RenderedConnection} overrides this behavior with a list - * computed from the rendered positioning. - * @param {number} maxLimit The maximum radius to another connection. - * @return {!Array.} List of connections. - * @private - */ -Blockly.Connection.prototype.neighbours_ = function(/* maxLimit */) { - return []; -}; - -/** - * This method returns a string describing this Connection in developer terms - * (English only). Intended to on be used in console logs and errors. - * @return {string} The description. - */ -Blockly.Connection.prototype.toString = function() { - var msg; - var block = this.sourceBlock_; - if (!block) { - return 'Orphan Connection'; - } else if (block.outputConnection == this) { - msg = 'Output Connection of '; - } else if (block.previousConnection == this) { - msg = 'Previous Connection of '; - } else if (block.nextConnection == this) { - msg = 'Next Connection of '; - } else { - var parentInput = goog.array.find(block.inputList, function(input) { - return input.connection == this; - }, this); - if (parentInput) { - msg = 'Input "' + parentInput.name + '" connection on '; - } else { - console.warn('Connection not actually connected to sourceBlock_'); - return 'Orphan Connection'; - } - } - return msg + block.toDevString(); -}; diff --git a/core/connection_db.js b/core/connection_db.js deleted file mode 100644 index 476b824bbc..0000000000 --- a/core/connection_db.js +++ /dev/null @@ -1,300 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2011 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Components for managing connections between blocks. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.ConnectionDB'); - -goog.require('Blockly.Connection'); - - -/** - * 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. - * @constructor - */ -Blockly.ConnectionDB = function() { - /** - * Array of connections sorted by y coordinate. - * @type {!Array.} - * @private - */ - this.connections_ = []; -}; - -/** - * Add a connection to the database. Must not already exist in DB. - * @param {!Blockly.Connection} connection The connection to be added. - */ -Blockly.ConnectionDB.prototype.addConnection = function(connection) { - if (connection.inDB_) { - throw Error('Connection already in database.'); - } - if (connection.getSourceBlock().isInFlyout) { - // Don't bother maintaining a database of connections in a flyout. - return; - } - var position = this.findPositionForConnection_(connection); - this.connections_.splice(position, 0, connection); - connection.inDB_ = true; -}; - -/** - * Find the given connection. - * Starts by doing a binary search to find the approximate location, then - * linearly searches nearby for the exact connection. - * @param {!Blockly.Connection} conn The connection to find. - * @return {number} The index of the connection, or -1 if the connection was - * not found. - */ -Blockly.ConnectionDB.prototype.findConnection = function(conn) { - if (!this.connections_.length) { - return -1; - } - - var bestGuess = this.findPositionForConnection_(conn); - if (bestGuess >= this.connections_.length) { - // Not in list - return -1; - } - - var yPos = conn.y_; - // Walk forward and back on the y axis looking for the connection. - var pointerMin = bestGuess; - var pointerMax = bestGuess; - while (pointerMin >= 0 && this.connections_[pointerMin].y_ == yPos) { - if (this.connections_[pointerMin] == conn) { - return pointerMin; - } - pointerMin--; - } - - while (pointerMax < this.connections_.length && - this.connections_[pointerMax].y_ == yPos) { - if (this.connections_[pointerMax] == conn) { - return pointerMax; - } - pointerMax++; - } - return -1; -}; - -/** - * Finds a candidate position for inserting this connection into the list. - * This will be in the correct y order but makes no guarantees about ordering in - * the x axis. - * @param {!Blockly.Connection} connection The connection to insert. - * @return {number} The candidate index. - * @private - */ -Blockly.ConnectionDB.prototype.findPositionForConnection_ = function( - connection) { - if (!this.connections_.length) { - return 0; - } - var pointerMin = 0; - var pointerMax = this.connections_.length; - while (pointerMin < pointerMax) { - var pointerMid = Math.floor((pointerMin + pointerMax) / 2); - if (this.connections_[pointerMid].y_ < connection.y_) { - pointerMin = pointerMid + 1; - } else if (this.connections_[pointerMid].y_ > connection.y_) { - pointerMax = pointerMid; - } else { - pointerMin = pointerMid; - break; - } - } - return pointerMin; -}; - -/** - * Remove a connection from the database. Must already exist in DB. - * @param {!Blockly.Connection} connection The connection to be removed. - * @private - */ -Blockly.ConnectionDB.prototype.removeConnection_ = function(connection) { - if (!connection.inDB_) { - throw Error('Connection not in database.'); - } - var removalIndex = this.findConnection(connection); - if (removalIndex == -1) { - throw Error('Unable to find connection in connectionDB.'); - } - connection.inDB_ = false; - this.connections_.splice(removalIndex, 1); -}; - -/** - * Find all nearby connections to the given connection. - * Type checking does not apply, since this function is used for bumping. - * @param {!Blockly.Connection} connection The connection whose neighbours - * should be returned. - * @param {number} maxRadius The maximum radius to another connection. - * @return {!Array.} List of connections. - */ -Blockly.ConnectionDB.prototype.getNeighbours = function(connection, maxRadius) { - var db = this.connections_; - var currentX = connection.x_; - var currentY = connection.y_; - - // Binary search to find the closest y location. - var pointerMin = 0; - var pointerMax = db.length - 2; - var pointerMid = pointerMax; - while (pointerMin < pointerMid) { - if (db[pointerMid].y_ < currentY) { - pointerMin = pointerMid; - } else { - pointerMax = pointerMid; - } - pointerMid = Math.floor((pointerMin + pointerMax) / 2); - } - - var neighbours = []; - /** - * Computes if the current connection is within the allowed radius of another - * connection. - * This function is a closure and has access to outside variables. - * @param {number} yIndex The other connection's index in the database. - * @return {boolean} True if the current connection's vertical distance from - * the other connection is less than the allowed radius. - */ - function checkConnection_(yIndex) { - var dx = currentX - db[yIndex].x_; - var dy = currentY - db[yIndex].y_; - var r = Math.sqrt(dx * dx + dy * dy); - if (r <= maxRadius) { - neighbours.push(db[yIndex]); - } - return dy < maxRadius; - } - - // Walk forward and back on the y axis looking for the closest x,y point. - pointerMin = pointerMid; - pointerMax = pointerMid; - if (db.length) { - while (pointerMin >= 0 && checkConnection_(pointerMin)) { - pointerMin--; - } - do { - pointerMax++; - } while (pointerMax < db.length && checkConnection_(pointerMax)); - } - - return neighbours; -}; - - -/** - * 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 - */ -Blockly.ConnectionDB.prototype.isInYRange_ = function(index, baseY, maxRadius) { - return (Math.abs(this.connections_[index].y_ - baseY) <= maxRadius); -}; - -/** - * Find the closest compatible connection to this connection. - * @param {!Blockly.Connection} conn The connection searching for a compatible - * mate. - * @param {number} maxRadius The maximum radius to another connection. - * @param {!goog.math.Coordinate} dxy Offset between this connection's location - * in the database and the current location (as a result of dragging). - * @return {!{connection: ?Blockly.Connection, radius: number}} Contains two - * properties:' connection' which is either another connection or null, - * and 'radius' which is the distance. - */ -Blockly.ConnectionDB.prototype.searchForClosest = function(conn, maxRadius, - dxy) { - // Don't bother. - if (!this.connections_.length) { - return {connection: null, radius: maxRadius}; - } - - // Stash the values of x and y from before the drag. - var baseY = conn.y_; - var baseX = conn.x_; - - conn.x_ = baseX + dxy.x; - conn.y_ = baseY + dxy.y; - - // findPositionForConnection finds an index for insertion, which is always - // after any block with the same y index. We want to search both forward - // and back, so search on both sides of the index. - var closestIndex = this.findPositionForConnection_(conn); - - var bestConnection = null; - var bestRadius = maxRadius; - var temp; - - // Walk forward and back on the y axis looking for the closest x,y point. - var pointerMin = closestIndex - 1; - while (pointerMin >= 0 && this.isInYRange_(pointerMin, conn.y_, maxRadius)) { - temp = this.connections_[pointerMin]; - if (conn.isConnectionAllowed(temp, bestRadius)) { - bestConnection = temp; - bestRadius = temp.distanceFrom(conn); - } - pointerMin--; - } - - var pointerMax = closestIndex; - while (pointerMax < this.connections_.length && - this.isInYRange_(pointerMax, conn.y_, maxRadius)) { - temp = this.connections_[pointerMax]; - if (conn.isConnectionAllowed(temp, bestRadius)) { - bestConnection = temp; - bestRadius = temp.distanceFrom(conn); - } - pointerMax++; - } - - // Reset the values of x and y. - conn.x_ = baseX; - conn.y_ = baseY; - - // If there were no valid connections, bestConnection will be null. - return {connection: bestConnection, radius: bestRadius}; -}; - -/** - * Initialize a set of connection DBs for a specified workspace. - * @param {!Blockly.Workspace} workspace The workspace this DB is for. - */ -Blockly.ConnectionDB.init = function(workspace) { - // Create four databases, one for each connection type. - var dbList = []; - dbList[Blockly.INPUT_VALUE] = new Blockly.ConnectionDB(); - dbList[Blockly.OUTPUT_VALUE] = new Blockly.ConnectionDB(); - dbList[Blockly.NEXT_STATEMENT] = new Blockly.ConnectionDB(); - dbList[Blockly.PREVIOUS_STATEMENT] = new Blockly.ConnectionDB(); - workspace.connectionDBList = dbList; -}; diff --git a/core/constants.js b/core/constants.js deleted file mode 100644 index 2b6d653586..0000000000 --- a/core/constants.js +++ /dev/null @@ -1,387 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Blockly constants. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.constants'); - - -/** - * Number of pixels the mouse must move before a drag starts. - */ -Blockly.DRAG_RADIUS = 3; - -/** - * 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 Blockly.DRAG_RADIUS so that the drag-direction is clearer. - */ -Blockly.FLYOUT_DRAG_RADIUS = 10; - -/** - * Maximum misalignment between connections for them to snap together. - */ -Blockly.SNAP_RADIUS = 48; - -/** - * Maximum misalignment between connections for them to snap together, - * when a connection is already highlighted. - */ -Blockly.CONNECTING_SNAP_RADIUS = 68; - -/** - * 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. - */ -Blockly.CURRENT_CONNECTION_PREFERENCE = 20; - -/** - * Delay in ms between trigger and bumping unconnected block out of alignment. - */ -Blockly.BUMP_DELAY = 0; - -/** - * Number of characters to truncate a collapsed block to. - */ -Blockly.COLLAPSE_CHARS = 30; - -/** - * Length in ms for a touch to become a long press. - */ -Blockly.LONGPRESS = 750; - -/** - * Distance to scroll when a mouse wheel event is received and its delta mode - * is line (0x1) instead of pixel (0x0). In these cases, a single "scroll" has - * a delta of 1, which makes the workspace scroll very slowly (just one pixel). - * To compensate, that delta is multiplied by this value. - * @const - * @package - */ -Blockly.LINE_SCROLL_MULTIPLIER = 15; - -/** - * Prevent a sound from playing if another sound preceded it within this many - * milliseconds. - */ -Blockly.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). - */ -Blockly.DRAG_STACK = true; - -/** - * The richness of block colours, regardless of the hue. - * Must be in the range of 0 (inclusive) to 1 (exclusive). - */ -Blockly.HSV_SATURATION = 0.45; - -/** - * The intensity of block colours, regardless of the hue. - * Must be in the range of 0 (inclusive) to 1 (exclusive). - */ -Blockly.HSV_VALUE = 0.65; - -/** - * Sprited icons and images. - */ -Blockly.SPRITE = { - width: 96, - height: 124, - url: 'sprites.png' -}; - -// Constants below this point are not intended to be changed. - -/** - * Required name space for SVG elements. - * @const - */ -Blockly.SVG_NS = 'http://www.w3.org/2000/svg'; - -/** - * Required name space for HTML elements. - * @const - */ -Blockly.HTML_NS = 'http://www.w3.org/1999/xhtml'; - -/** - * ENUM for a right-facing value input. E.g. 'set item to' or 'return'. - * @const - */ -Blockly.INPUT_VALUE = 1; - -/** - * ENUM for a left-facing value output. E.g. 'random fraction'. - * @const - */ -Blockly.OUTPUT_VALUE = 2; - -/** - * ENUM for a down-facing block stack. E.g. 'if-do' or 'else'. - * @const - */ -Blockly.NEXT_STATEMENT = 3; - -/** - * ENUM for an up-facing block stack. E.g. 'break out of loop'. - * @const - */ -Blockly.PREVIOUS_STATEMENT = 4; - -/** - * ENUM for an dummy input. Used to add field(s) with no input. - * @const - */ -Blockly.DUMMY_INPUT = 5; - -/** - * ENUM for left alignment. - * @const - */ -Blockly.ALIGN_LEFT = -1; - -/** - * ENUM for centre alignment. - * @const - */ -Blockly.ALIGN_CENTRE = 0; - -/** - * ENUM for right alignment. - * @const - */ -Blockly.ALIGN_RIGHT = 1; - -/** - * ENUM for no drag operation. - * @const - */ -Blockly.DRAG_NONE = 0; - -/** - * ENUM for inside the sticky DRAG_RADIUS. - * @const - */ -Blockly.DRAG_STICKY = 1; - -/** - * ENUM for inside the non-sticky DRAG_RADIUS, for differentiating between - * clicks and drags. - * @const - */ -Blockly.DRAG_BEGIN = 1; - -/** - * ENUM for freely draggable (outside the DRAG_RADIUS, if one applies). - * @const - */ -Blockly.DRAG_FREE = 2; - -/** - * Lookup table for determining the opposite type of a connection. - * @const - */ -Blockly.OPPOSITE_TYPE = []; -Blockly.OPPOSITE_TYPE[Blockly.INPUT_VALUE] = Blockly.OUTPUT_VALUE; -Blockly.OPPOSITE_TYPE[Blockly.OUTPUT_VALUE] = Blockly.INPUT_VALUE; -Blockly.OPPOSITE_TYPE[Blockly.NEXT_STATEMENT] = Blockly.PREVIOUS_STATEMENT; -Blockly.OPPOSITE_TYPE[Blockly.PREVIOUS_STATEMENT] = Blockly.NEXT_STATEMENT; - -/** - * ENUM for toolbox and flyout at top of screen. - * @const - */ -Blockly.TOOLBOX_AT_TOP = 0; - -/** - * ENUM for toolbox and flyout at bottom of screen. - * @const - */ -Blockly.TOOLBOX_AT_BOTTOM = 1; - -/** - * ENUM for toolbox and flyout at left of screen. - * @const - */ -Blockly.TOOLBOX_AT_LEFT = 2; - -/** - * ENUM for toolbox and flyout at right of screen. - * @const - */ -Blockly.TOOLBOX_AT_RIGHT = 3; - -/** - * ENUM for output shape: hexagonal (booleans/predicates). - * @const - */ -Blockly.OUTPUT_SHAPE_HEXAGONAL = 1; - -/** - * ENUM for output shape: rounded (numbers). - * @const - */ -Blockly.OUTPUT_SHAPE_ROUND = 2; - -/** - * ENUM for output shape: squared (any/all values; strings). - * @const - */ -Blockly.OUTPUT_SHAPE_SQUARE = 3; - -/** - * ENUM for categories. - * @const - */ -Blockly.Categories = { - "motion": "motion", - "looks": "looks", - "sound": "sounds", - "pen": "pen", - "data": "data", - "dataLists": "data-lists", - "event": "events", - "control": "control", - "sensing": "sensing", - "operators": "operators", - "more": "more" -}; - -/** - * ENUM representing that an event is not in any delete areas. - * Null for backwards compatibility reasons. - * @const - */ -Blockly.DELETE_AREA_NONE = null; - -/** - * ENUM representing that an event is in the delete area of the trash can. - * @const - */ -Blockly.DELETE_AREA_TRASH = 1; - -/** - * ENUM representing that an event is in the delete area of the toolbox or - * flyout. - * @const - */ -Blockly.DELETE_AREA_TOOLBOX = 2; - -/** - * 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} - */ -Blockly.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 - * procedure blocks. - * @const {string} - */ -Blockly.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} - */ -Blockly.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} - */ -Blockly.DELETE_VARIABLE_ID = 'DELETE_VARIABLE_ID'; - -/** - * String for use in the dropdown created in field_variable, - * specifically for broadcast messages. - * This string indicates that this option in the dropdown is 'New message...' - * and if selected, should trigger the prompt to create a new message. - * @const {string} - */ -Blockly.NEW_BROADCAST_MESSAGE_ID = 'NEW_BROADCAST_MESSAGE_ID'; - -/** - * String representing the variable type of broadcast message blocks. - * This string, for use in differentiating between types of variables, - * indicates that the current variable is a broadcast message. - * @const {string} - */ -Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE = 'broadcast_msg'; - -/** - * String representing the variable type of list blocks. - * This string, for use in differentiating between types of variables, - * indicates that the current variable is a list. - * @const {string} - */ -Blockly.LIST_VARIABLE_TYPE = 'list'; - -// TODO (#1251) Replace '' below with 'scalar', and start using this constant -// everywhere. -/** - * String representing the variable type of scalar variables. - * This string, for use in differentiating between types of variables, - * indicates that the current variable is a scalar variable. - * @const {string} - */ -Blockly.SCALAR_VARIABLE_TYPE = ''; - -/** - * The type of all procedure definition blocks. - * @const {string} - */ -Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE = 'procedures_definition'; - -/** - * The type of all procedure prototype blocks. - * @const {string} - */ -Blockly.PROCEDURES_PROTOTYPE_BLOCK_TYPE = 'procedures_prototype'; - -/** - * The type of all procedure call blocks. - * @const {string} - */ -Blockly.PROCEDURES_CALL_BLOCK_TYPE = 'procedures_call'; - -/** - * ENUM for flyout status button states. - * @const - */ -Blockly.StatusButtonState = { - "READY": "ready", - "NOT_READY": "not ready", -}; diff --git a/core/contextmenu.js b/core/contextmenu.js deleted file mode 100644 index 65401747e6..0000000000 --- a/core/contextmenu.js +++ /dev/null @@ -1,519 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2011 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Functionality for the right-click context menus. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -/** - * @name Blockly.ContextMenu - * @namespace - */ -goog.provide('Blockly.ContextMenu'); - -goog.require('Blockly.Events.BlockCreate'); -goog.require('Blockly.scratchBlocksUtils'); -goog.require('Blockly.utils'); -goog.require('Blockly.utils.uiMenu'); - -goog.require('goog.dom'); -goog.require('goog.events'); -goog.require('goog.style'); -goog.require('goog.ui.Menu'); -goog.require('goog.ui.MenuItem'); -goog.require('goog.userAgent'); - - -/** - * Which block is the context menu attached to? - * @type {Blockly.Block} - */ -Blockly.ContextMenu.currentBlock = null; - -/** - * Opaque data that can be passed to unbindEvent_. - * @type {Array.} - * @private - */ -Blockly.ContextMenu.eventWrapper_ = null; - -/** - * Construct the menu based on the list of options and show the menu. - * @param {!Event} e Mouse event. - * @param {!Array.} options Array of menu options. - * @param {boolean} rtl True if RTL, false if LTR. - */ -Blockly.ContextMenu.show = function(e, options, rtl) { - Blockly.WidgetDiv.show(Blockly.ContextMenu, rtl, null); - if (!options.length) { - Blockly.ContextMenu.hide(); - return; - } - var menu = Blockly.ContextMenu.populate_(options, rtl); - - goog.events.listen( - menu, goog.ui.Component.EventType.ACTION, Blockly.ContextMenu.hide); - - Blockly.ContextMenu.position_(menu, e, rtl); - // 1ms delay is required for focusing on context menus because some other - // mouse event is still waiting in the queue and clears focus. - setTimeout(function() {menu.getElement().focus();}, 1); - Blockly.ContextMenu.currentBlock = null; // May be set by Blockly.Block. -}; - -/** - * Create the context menu object and populate it with the given options. - * @param {!Array.} options Array of menu options. - * @param {boolean} rtl True if RTL, false if LTR. - * @return {!goog.ui.Menu} The menu that will be shown on right click. - * @private - */ -Blockly.ContextMenu.populate_ = function(options, rtl) { - /* Here's what one option object looks like: - {text: 'Make It So', - enabled: true, - callback: Blockly.MakeItSo} - */ - var menu = new goog.ui.Menu(); - menu.setRightToLeft(rtl); - for (var i = 0, option; option = options[i]; i++) { - var menuItem = new goog.ui.MenuItem(option.text); - menuItem.setRightToLeft(rtl); - menu.addChild(menuItem, true); - menuItem.setEnabled(option.enabled); - if (option.enabled) { - goog.events.listen( - menuItem, goog.ui.Component.EventType.ACTION, option.callback); - menuItem.handleContextMenu = function(/* e */) { - // Right-clicking on menu option should count as a click. - goog.events.dispatchEvent(this, goog.ui.Component.EventType.ACTION); - }; - } - } - return menu; -}; - -/** - * Add the menu to the page and position it correctly. - * @param {!goog.ui.Menu} menu The menu to add and position. - * @param {!Event} e Mouse event for the right click that is making the context - * menu appear. - * @param {boolean} rtl True if RTL, false if LTR. - * @private - */ -Blockly.ContextMenu.position_ = function(menu, e, rtl) { - // Record windowSize and scrollOffset before adding menu. - var viewportBBox = Blockly.utils.getViewportBBox(); - // This one is just a point, but we'll pretend that it's a rect so we can use - // some helper functions. - var anchorBBox = { - top: e.clientY + viewportBBox.top, - bottom: e.clientY + viewportBBox.top, - left: e.clientX + viewportBBox.left, - right: e.clientX + viewportBBox.left - }; - - Blockly.ContextMenu.createWidget_(menu); - var menuSize = Blockly.utils.uiMenu.getSize(menu); - - if (rtl) { - Blockly.utils.uiMenu.adjustBBoxesForRTL(viewportBBox, anchorBBox, menuSize); - } - - Blockly.WidgetDiv.positionWithAnchor(viewportBBox, anchorBBox, menuSize, rtl); - // Calling menuDom.focus() has to wait until after the menu has been placed - // correctly. Otherwise it will cause a page scroll to get the misplaced menu - // in view. See issue #1329. - menu.getElement().focus(); -}; - -/** - * Create and render the menu widget inside Blockly's widget div. - * @param {!goog.ui.Menu} menu The menu to add to the widget div. - * @private - */ -Blockly.ContextMenu.createWidget_ = function(menu) { - var div = Blockly.WidgetDiv.DIV; - menu.render(div); - var menuDom = menu.getElement(); - Blockly.utils.addClass(menuDom, 'blocklyContextMenu'); - // Prevent system context menu when right-clicking a Blockly context menu. - Blockly.bindEventWithChecks_( - menuDom, 'contextmenu', null, Blockly.utils.noEvent); - // Enable autofocus after the initial render to avoid issue #1329. - menu.setAllowAutoFocus(true); -}; - -/** - * Hide the context menu. - */ -Blockly.ContextMenu.hide = function() { - Blockly.WidgetDiv.hideIfOwner(Blockly.ContextMenu); - Blockly.ContextMenu.currentBlock = null; - if (Blockly.ContextMenu.eventWrapper_) { - Blockly.unbindEvent_(Blockly.ContextMenu.eventWrapper_); - } -}; - -/** - * Create a callback function that creates and configures a block, - * then places the new block next to the original. - * @param {!Blockly.Block} block Original block. - * @param {!Element} xml XML representation of new block. - * @return {!Function} Function that creates a block. - */ -Blockly.ContextMenu.callbackFactory = function(block, xml) { - return function() { - Blockly.Events.disable(); - try { - var newBlock = Blockly.Xml.domToBlock(xml, block.workspace); - // Move the new block next to the old block. - var xy = block.getRelativeToSurfaceXY(); - if (block.RTL) { - xy.x -= Blockly.SNAP_RADIUS; - } else { - xy.x += Blockly.SNAP_RADIUS; - } - xy.y += Blockly.SNAP_RADIUS * 2; - newBlock.moveBy(xy.x, xy.y); - } finally { - Blockly.Events.enable(); - } - if (Blockly.Events.isEnabled() && !newBlock.isShadow()) { - Blockly.Events.fire(new Blockly.Events.BlockCreate(newBlock)); - } - newBlock.select(); - }; -}; - -// Helper functions for creating context menu options. - -/** - * Make a context menu option for deleting the current block. - * @param {!Blockly.BlockSvg} block The block where the right-click originated. - * @return {!Object} A menu option, containing text, enabled, and a callback. - * @package - */ -Blockly.ContextMenu.blockDeleteOption = function(block) { - // Option to delete this block but not blocks lower in the stack. - // Count the number of blocks that are nested in this block, - // ignoring shadows and without ordering. - var descendantCount = block.getDescendants(false, true).length; - var nextBlock = block.getNextBlock(); - if (nextBlock) { - // Blocks in the current stack would survive this block's deletion. - descendantCount -= nextBlock.getDescendants(false, true).length; - } - var deleteOption = { - text: descendantCount == 1 ? Blockly.Msg.DELETE_BLOCK : - Blockly.Msg.DELETE_X_BLOCKS.replace('%1', String(descendantCount)), - enabled: true, - callback: function() { - Blockly.Events.setGroup(true); - block.dispose(true, true); - Blockly.Events.setGroup(false); - } - }; - return deleteOption; -}; - -/** - * Make a context menu option for showing help for the current block. - * @param {!Blockly.BlockSvg} block The block where the right-click originated. - * @return {!Object} A menu option, containing text, enabled, and a callback. - * @package - */ -Blockly.ContextMenu.blockHelpOption = function(block) { - var url = goog.isFunction(block.helpUrl) ? block.helpUrl() : block.helpUrl; - var helpOption = { - enabled: !!url, - text: Blockly.Msg.HELP, - callback: function() { - block.showHelp_(); - } - }; - return helpOption; -}; - -/** - * Make a context menu option for duplicating the current block. - * @param {!Blockly.BlockSvg} block The block where the right-click originated. - * @param {!Event} event Event that caused the context menu to open. - * @return {!Object} A menu option, containing text, enabled, and a callback. - * @package - */ -Blockly.ContextMenu.blockDuplicateOption = function(block, event) { - var duplicateOption = { - text: Blockly.Msg.DUPLICATE, - enabled: true, - callback: - Blockly.scratchBlocksUtils.duplicateAndDragCallback(block, event) - }; - return duplicateOption; -}; - -/** - * Make a context menu option for adding or removing comments on the current - * block. - * @param {!Blockly.BlockSvg} block The block where the right-click originated. - * @return {!Object} A menu option, containing text, enabled, and a callback. - * @package - */ -Blockly.ContextMenu.blockCommentOption = function(block) { - var commentOption = { - enabled: !goog.userAgent.IE - }; - // If there's already a comment, add an option to delete it. - if (block.comment) { - commentOption.text = Blockly.Msg.REMOVE_COMMENT; - commentOption.callback = function() { - block.setCommentText(null); - }; - } else { - // If there's no comment, add an option to create a comment. - commentOption.text = Blockly.Msg.ADD_COMMENT; - commentOption.callback = function() { - block.setCommentText(''); - block.comment.focus(); - }; - } - return commentOption; -}; - -/** - * Make a context menu option for undoing the most recent action on the - * workspace. - * @param {!Blockly.WorkspaceSvg} ws The workspace where the right-click - * originated. - * @return {!Object} A menu option, containing text, enabled, and a callback. - * @package - */ -Blockly.ContextMenu.wsUndoOption = function(ws) { - return { - text: Blockly.Msg.UNDO, - enabled: ws.hasUndoStack(), - callback: ws.undo.bind(ws, false) - }; -}; - -/** - * Make a context menu option for redoing the most recent action on the - * workspace. - * @param {!Blockly.WorkspaceSvg} ws The workspace where the right-click - * originated. - * @return {!Object} A menu option, containing text, enabled, and a callback. - * @package - */ -Blockly.ContextMenu.wsRedoOption = function(ws) { - return { - text: Blockly.Msg.REDO, - enabled: ws.hasRedoStack(), - callback: ws.undo.bind(ws, true) - }; -}; - -/** - * Make a context menu option for cleaning up blocks on the workspace, by - * aligning them vertically. - * @param {!Blockly.WorkspaceSvg} ws The workspace where the right-click - * originated. - * @param {number} numTopBlocks The number of top blocks on the workspace. - * @return {!Object} A menu option, containing text, enabled, and a callback. - * @package - */ -Blockly.ContextMenu.wsCleanupOption = function(ws, numTopBlocks) { - return { - text: Blockly.Msg.CLEAN_UP, - enabled: numTopBlocks > 1, - callback: ws.cleanUp.bind(ws, true) - }; -}; - -/** - * Helper function for toggling delete state on blocks on the workspace, to be - * called from a right-click menu. - * @param {!Array.} topBlocks The list of top blocks on the - * the workspace. - * @param {boolean} shouldCollapse True if the blocks should be collapsed, false - * if they should be expanded. - * @private - */ -Blockly.ContextMenu.toggleCollapseFn_ = function(topBlocks, shouldCollapse) { - // Add a little animation to collapsing and expanding. - var DELAY = 10; - var ms = 0; - for (var i = 0; i < topBlocks.length; i++) { - var block = topBlocks[i]; - while (block) { - setTimeout(block.setCollapsed.bind(block, shouldCollapse), ms); - block = block.getNextBlock(); - ms += DELAY; - } - } -}; - -/** - * Make a context menu option for collapsing all block stacks on the workspace. - * @param {boolean} hasExpandedBlocks Whether there are any non-collapsed blocks - * on the workspace. - * @param {!Array.} topBlocks The list of top blocks on the - * the workspace. - * @return {!Object} A menu option, containing text, enabled, and a callback. - * @package - */ -Blockly.ContextMenu.wsCollapseOption = function(hasExpandedBlocks, topBlocks) { - return { - enabled: hasExpandedBlocks, - text: Blockly.Msg.COLLAPSE_ALL, - callback: function() { - Blockly.ContextMenu.toggleCollapseFn_(topBlocks, true); - } - }; -}; - -/** - * Make a context menu option for expanding all block stacks on the workspace. - * @param {boolean} hasCollapsedBlocks Whether there are any collapsed blocks - * on the workspace. - * @param {!Array.} topBlocks The list of top blocks on the - * the workspace. - * @return {!Object} A menu option, containing text, enabled, and a callback. - * @package - */ -Blockly.ContextMenu.wsExpandOption = function(hasCollapsedBlocks, topBlocks) { - return { - enabled: hasCollapsedBlocks, - text: Blockly.Msg.EXPAND_ALL, - callback: function() { - Blockly.ContextMenu.toggleCollapseFn_(topBlocks, false); - } - }; -}; - -/** - * Make a context menu option for deleting the current workspace comment. - * @param {!Blockly.WorkspaceCommentSvg} comment The workspace comment where the - * right-click originated. - * @return {!Object} A menu option, containing text, enabled, and a callback. - * @package - */ -Blockly.ContextMenu.commentDeleteOption = function(comment) { - var deleteOption = { - text: Blockly.Msg.DELETE, - enabled: true, - callback: function() { - Blockly.Events.setGroup(true); - comment.dispose(true, true); - Blockly.Events.setGroup(false); - } - }; - return deleteOption; -}; - -/** - * Make a context menu option for duplicating the current workspace comment. - * @param {!Blockly.WorkspaceCommentSvg} comment The workspace comment where the - * right-click originated. - * @return {!Object} A menu option, containing text, enabled, and a callback. - * @package - */ -Blockly.ContextMenu.commentDuplicateOption = function(comment) { - var duplicateOption = { - text: Blockly.Msg.DUPLICATE, - enabled: true, - callback: function() { - Blockly.duplicate_(comment); - } - }; - return duplicateOption; -}; - -/** - * Make a context menu option for adding a comment on the workspace. - * @param {!Blockly.WorkspaceSvg} ws The workspace where the right-click - * originated. - * @param {!Event} e The right-click mouse event. - * @return {!Object} A menu option, containing text, enabled, and a callback. - * @package - */ -Blockly.ContextMenu.workspaceCommentOption = function(ws, e) { - // Helper function to create and position a comment correctly based on the - // location of the mouse event. - var addWsComment = function() { - // Disable events while this comment is getting created - // so that we can fire a single create event for this comment - // at the end (instead of CommentCreate followed by CommentMove, - // which results in unexpected undo behavior). - var disabled = false; - if (Blockly.Events.isEnabled()) { - Blockly.Events.disable(); - disabled = true; - } - var comment = new Blockly.WorkspaceCommentSvg( - ws, '', Blockly.WorkspaceCommentSvg.DEFAULT_SIZE, - Blockly.WorkspaceCommentSvg.DEFAULT_SIZE, false); - - var injectionDiv = ws.getInjectionDiv(); - // Bounding rect coordinates are in client coordinates, meaning that they - // are in pixels relative to the upper left corner of the visible browser - // window. These coordinates change when you scroll the browser window. - var boundingRect = injectionDiv.getBoundingClientRect(); - - // The client coordinates offset by the injection div's upper left corner. - var clientOffsetPixels = new goog.math.Coordinate( - e.clientX - boundingRect.left, e.clientY - boundingRect.top); - - // The offset in pixels between the main workspace's origin and the upper - // left corner of the injection div. - var mainOffsetPixels = ws.getOriginOffsetInPixels(); - - // The position of the new comment in pixels relative to the origin of the - // main workspace. - var finalOffsetPixels = goog.math.Coordinate.difference(clientOffsetPixels, - mainOffsetPixels); - - // The position of the new comment in main workspace coordinates. - var finalOffsetMainWs = finalOffsetPixels.scale(1 / ws.scale); - - var commentX = finalOffsetMainWs.x; - var commentY = finalOffsetMainWs.y; - comment.moveBy(commentX, commentY); - if (ws.rendered) { - comment.initSvg(); - comment.render(false); - comment.select(); - } - if (disabled) { - Blockly.Events.enable(); - } - Blockly.WorkspaceComment.fireCreateEvent(comment); - }; - - var wsCommentOption = {enabled: true}; - wsCommentOption.text = Blockly.Msg.ADD_COMMENT; - wsCommentOption.callback = function() { - addWsComment(); - }; - return wsCommentOption; -}; - -// End helper functions for creating context menu options. diff --git a/core/css.js b/core/css.js deleted file mode 100644 index 03c6bab0f1..0000000000 --- a/core/css.js +++ /dev/null @@ -1,1351 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2013 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Inject Blockly's CSS synchronously. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -/** - * @name Blockly.Css - * @namespace - */ -goog.provide('Blockly.Css'); - -goog.require('Blockly.Colours'); - -goog.require('goog.userAgent'); - -/** - * List of cursors. - * @enum {string} - */ -Blockly.Css.Cursor = { - OPEN: 'handopen', - CLOSED: 'handclosed', - DELETE: 'handdelete' -}; - -/** - * Current cursor (cached value). - * @type {string} - * @private - */ -Blockly.Css.currentCursor_ = ''; - -/** - * Large stylesheet added by Blockly.Css.inject. - * @type {Element} - * @private - */ -Blockly.Css.styleSheet_ = null; - -/** - * Path to media directory, with any trailing slash removed. - * @type {string} - * @private - */ -Blockly.Css.mediaPath_ = ''; - -/** - * 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. - */ -Blockly.Css.inject = function(hasCss, pathToMedia) { - // Clear the CSS if it has already been injected. - if (Blockly.Css.styleSheet_) { - document.head.removeChild(Blockly.Css.styleSheet_.ownerNode); - } - // Placeholder for cursor rule. Must be first rule (index 0). - var text = '.blocklyDraggable {}\n'; - if (hasCss) { - text += Blockly.Css.CONTENT.join('\n'); - if (Blockly.FieldDate) { - text += Blockly.FieldDate.CSS.join('\n'); - } - } - // Strip off any trailing slash (either Unix or Windows). - Blockly.Css.mediaPath_ = pathToMedia.replace(/[\\\/]$/, ''); - text = text.replace(/<<>>/g, Blockly.Css.mediaPath_); - // Dynamically replace colours in the CSS text, in case they have - // been set at run-time injection. - // Process longer colour properties first to handle common prefixes. - var compareByLength = function(a, b) { return b.length - a.length; }; - var colourProperties = Object.keys(Blockly.Colours).sort(compareByLength); - for (var i = 0, colourProperty; colourProperty = colourProperties[i]; i++) { - // Replace all - text = text.replace( - new RegExp('\\$colour\\_' + colourProperty, 'g'), - Blockly.Colours[colourProperty] - ); - } - - // Inject CSS tag at start of head. - var cssNode = document.createElement('style'); - document.head.insertBefore(cssNode, document.head.firstChild); - - var cssTextNode = document.createTextNode(text); - cssNode.appendChild(cssTextNode); - Blockly.Css.styleSheet_ = cssNode.sheet; -}; - -/** - * Set the cursor to be displayed when over something draggable. - * See See https://github.com/google/blockly/issues/981 for context. - * @param {Blockly.Css.Cursor} cursor Enum. - * @deprecated April 2017. - */ -Blockly.Css.setCursor = function(cursor) { - console.warn('Deprecated call to Blockly.Css.setCursor.' + - 'See https://github.com/google/blockly/issues/981 for context'); -}; - -/** - * Array making up the CSS content for Blockly. - */ -Blockly.Css.CONTENT = [ - '.blocklySvg {', - 'background-color: $colour_workspace;', - 'outline: none;', - 'overflow: hidden;', /* IE overflows by default. */ - 'position: absolute;', - 'display: block;', - '}', - - /* Necessary to position the drag surface */ - '.blocklyRelativeWrapper {', - 'position: relative;', - 'width: 100%;', - 'height: 100%;', - '}', - - '.blocklyWidgetDiv {', - 'display: none;', - 'position: absolute;', - 'z-index: 99999;', /* big value for bootstrap3 compatibility */ - '}', - - '.injectionDiv {', - 'height: 100%;', - 'position: relative;', - 'overflow: hidden;', /* So blocks in drag surface disappear at edges */ - 'touch-action: none', - '}', - - '.blocklyNonSelectable {', - 'user-select: none;', - '-moz-user-select: none;', - '-webkit-user-select: none;', - '-ms-user-select: none;', - '}', - - '.blocklyWidgetDiv.fieldTextInput {', - 'overflow: hidden;', - 'border: 1px solid;', - 'box-sizing: border-box;', - 'transform-origin: 0 0;', - '-ms-transform-origin: 0 0;', - '-moz-transform-origin: 0 0;', - '-webkit-transform-origin: 0 0;', - '}', - - '.blocklyWidgetDiv.fieldTextInput.removableTextInput {', - 'overflow: visible;', - '}', - - '.blocklyTextDropDownArrow {', - 'position: absolute;', - '}', - - '.blocklyTextRemoveIcon {', - 'position: absolute;', - 'width: 24px;', - 'height: 24px;', - 'top: -40px;', - 'left: 50%;', - 'margin-left: -12px;', - 'cursor: pointer;', - '}', - - '.blocklyNonSelectable {', - 'user-select: none;', - '-moz-user-select: none;', - '-webkit-user-select: none;', - '-ms-user-select: none;', - '}', - - '.blocklyWsDragSurface {', - 'display: none;', - 'position: absolute;', - 'top: 0;', - 'left: 0;', - '}', - /* Added as a separate rule with multiple classes to make it more specific - than a bootstrap rule that selects svg:root. See issue #1275 for context. - */ - '.blocklyWsDragSurface.blocklyOverflowVisible {', - 'overflow: visible;', - '}', - - '.blocklyBlockDragSurface {', - 'display: none;', - 'position: absolute;', - 'top: 0;', - 'left: 0;', - 'right: 0;', - 'bottom: 0;', - 'overflow: visible !important;', - 'z-index: 50;', /* Display above the toolbox */ - '}', - - '.blocklyTooltipDiv {', - 'background-color: #ffffc7;', - 'border: 1px solid #ddc;', - 'box-shadow: 4px 4px 20px 1px rgba(0,0,0,.15);', - 'color: #000;', - 'display: none;', - 'font-family: "Helvetica Neue", Helvetica, sans-serif;', - 'font-size: 9pt;', - 'opacity: 0.9;', - 'padding: 2px;', - 'position: absolute;', - 'z-index: 100000;', /* big value for bootstrap3 compatibility */ - '}', - - '.blocklyDropDownDiv {', - 'position: fixed;', - 'left: 0;', - 'top: 0;', - 'z-index: 1000;', - 'display: none;', - 'border: 1px solid;', - 'border-radius: 4px;', - 'box-shadow: 0px 0px 8px 1px ' + Blockly.Colours.dropDownShadow + ';', - 'padding: 4px;', - '-webkit-user-select: none;', - 'min-height: 15px', - '}', - - '.blocklyDropDownContent {', - 'max-height: 300px;', // @todo: spec for maximum height. - 'overflow: auto;', - '}', - - '.blocklyDropDownArrow {', - 'position: absolute;', - 'left: 0;', - 'top: 0;', - 'width: 16px;', - 'height: 16px;', - 'z-index: -1;', - 'background-color: inherit;', - 'border-color: inherit;', - '}', - - '.blocklyDropDownButton {', - 'display: inline-block;', - 'float: left;', - 'padding: 0;', - 'margin: 4px;', - 'border-radius: 4px;', - 'outline: none;', - 'border: 1px solid;', - 'transition: box-shadow .1s;', - 'cursor: pointer;', - '}', - - '.blocklyDropDownButtonHover {', - 'box-shadow: 0px 0px 0px 4px ' + Blockly.Colours.fieldShadow + ';', - '}', - - '.blocklyDropDownButton:active {', - 'box-shadow: 0px 0px 0px 6px ' + Blockly.Colours.fieldShadow + ';', - '}', - - '.blocklyDropDownButton > img {', - 'width: 80%;', - 'height: 80%;', - 'margin-top: 5%', - '}', - - '.blocklyDropDownPlaceholder {', - 'display: inline-block;', - 'float: left;', - 'padding: 0;', - 'margin: 4px;', - '}', - - '.blocklyNumPadButton {', - 'display: inline-block;', - 'float: left;', - 'padding: 0;', - 'width: 48px;', - 'height: 48px;', - 'margin: 4px;', - 'border-radius: 4px;', - 'background: $colour_numPadBackground;', - 'color: $colour_numPadText;', - 'outline: none;', - 'border: 1px solid $colour_numPadBorder;', - 'cursor: pointer;', - 'font-weight: 600;', - 'font-family: "Helvetica Neue", Helvetica, sans-serif;', - 'font-size: 12pt;', - '-webkit-tap-highlight-color: rgba(0,0,0,0);', - '}', - - '.blocklyNumPadButton > img {', - 'margin-top: 10%;', - 'width: 80%;', - 'height: 80%;', - '}', - - '.blocklyNumPadButton:active {', - 'background: $colour_numPadActiveBackground;', - '-webkit-tap-highlight-color: rgba(0,0,0,0);', - '}', - - '.arrowTop {', - 'border-top: 1px solid;', - 'border-left: 1px solid;', - 'border-top-left-radius: 4px;', - 'border-color: inherit;', - '}', - - '.arrowBottom {', - 'border-bottom: 1px solid;', - 'border-right: 1px solid;', - 'border-bottom-right-radius: 4px;', - 'border-color: inherit;', - '}', - - '.valueReportBox {', - 'min-width: 50px;', - 'max-width: 300px;', - 'max-height: 200px;', - 'overflow: auto;', - 'word-wrap: break-word;', - 'text-align: center;', - 'font-family: "Helvetica Neue", Helvetica, sans-serif;', - 'font-size: .8em;', - '}', - - '.blocklyResizeSE {', - 'cursor: se-resize;', - 'fill: #aaa;', - '}', - - '.blocklyResizeSW {', - 'cursor: sw-resize;', - 'fill: #aaa;', - '}', - - '.blocklyResizeLine {', - 'stroke: #888;', - 'stroke-width: 1;', - '}', - - '.blocklyHighlightedConnectionPath {', - 'fill: none;', - 'stroke: #fc3;', - 'stroke-width: 4px;', - '}', - - '.blocklyPath {', - 'stroke-width: 1px;', - '}', - - '.blocklySelected>.blocklyPath {', - // 'stroke: #fc3;', - // 'stroke-width: 3px;', - '}', - - '.blocklySelected>.blocklyPathLight {', - 'display: none;', - '}', - - '.blocklyDraggable {', - /* backup for browsers (e.g. IE11) that don't support grab */ - 'cursor: url("<<>>/handopen.cur"), auto;', - 'cursor: grab;', - 'cursor: -webkit-grab;', - 'cursor: -moz-grab;', - '}', - - '.blocklyDragging {', - /* backup for browsers (e.g. IE11) that don't support grabbing */ - 'cursor: url("<<>>/handclosed.cur"), auto;', - 'cursor: grabbing;', - 'cursor: -webkit-grabbing;', - 'cursor: -moz-grabbing;', - '}', - /* Changes cursor on mouse down. Not effective in Firefox because of - https://bugzilla.mozilla.org/show_bug.cgi?id=771241 */ - '.blocklyDraggable:active {', - /* backup for browsers (e.g. IE11) that don't support grabbing */ - 'cursor: url("<<>>/handclosed.cur"), auto;', - 'cursor: grabbing;', - 'cursor: -webkit-grabbing;', - 'cursor: -moz-grabbing;', - '}', - /* Change the cursor on the whole drag surface in case the mouse gets - ahead of block during a drag. This way the cursor is still a closed hand. - */ - '.blocklyBlockDragSurface .blocklyDraggable {', - /* backup for browsers (e.g. IE11) that don't support grabbing */ - 'cursor: url("<<>>/handclosed.cur"), auto;', - 'cursor: grabbing;', - 'cursor: -webkit-grabbing;', - 'cursor: -moz-grabbing;', - '}', - - '.blocklyDragging.blocklyDraggingDelete {', - 'cursor: url("<<>>/handdelete.cur"), auto;', - '}', - - '.blocklyDragging.blocklyDraggingMouseThrough {', - 'pointer-events: none;', - '}', - - '.blocklyToolboxDelete {', - 'cursor: url("<<>>/handdelete.cur"), auto;', - '}', - - '.blocklyToolboxGrab {', - 'cursor: url("<<>>/handclosed.cur"), auto;', - 'cursor: grabbing;', - 'cursor: -webkit-grabbing;', - '}', - - '.blocklyDragging>.blocklyPath,', - '.blocklyDragging>.blocklyPathLight {', - 'fill-opacity: 1.0;', - 'stroke-opacity: 1.0;', - '}', - - '.blocklyDragging>.blocklyPath {', - '}', - - '.blocklyDisabled>.blocklyPath {', - 'fill-opacity: .5;', - 'stroke-opacity: .5;', - '}', - - '.blocklyInsertionMarker>.blocklyPath {', - 'stroke: none;', - '}', - - '.blocklyText {', - 'fill: $colour_text;', - 'font-family: "Helvetica Neue", Helvetica, sans-serif;', - 'font-size: 12pt;', - 'font-weight: 500;', - '}', - - '.blocklyTextTruncated {', - 'font-size: 11pt;', - '}', - - '.blocklyNonEditableText>text {', - 'pointer-events: none;', - '}', - '.blocklyNonEditableText>text,', - '.blocklyEditableText>text {', - 'fill: $colour_textFieldText;', - '}', - - '.blocklyEditableText>.blocklyEditableLabel {', - 'fill: #fff;', - '}', - - '.blocklyDropdownText {', - 'fill: $colour_text !important;', - '}', - - '.blocklyBubbleText {', - 'fill: $colour_textFieldText;', - '}', - '.blocklyFlyout {', - 'position: absolute;', - 'z-index: 20;', - '}', - '.blocklyFlyoutButton {', - 'fill: none;', - 'pointer-events: all;', - '}', - - '.blocklyFlyoutButtonBackground {', - 'stroke: #c6c6c6;', - '}', - - '.blocklyFlyoutButton .blocklyText {', - 'fill: $colour_textFieldText;', - '}', - - '.blocklyFlyoutButtonShadow {', - 'fill: transparent;', - '}', - - '.blocklyFlyoutButton:hover {', - 'fill: white;', - 'cursor: pointer;', - '}', - - '.blocklyFlyoutLabel {', - 'cursor: default;', - '}', - - '.blocklyFlyoutLabelBackground {', - 'opacity: 0;', - '}', - - '.blocklyTouchTargetBackground {', - 'fill: transparent;', - 'cursor: pointer;', - '}', - - '.blocklyFlyoutLabelText {', - 'font-family: "Helvetica Neue", Helvetica, sans-serif;', - 'font-size: 14pt;', - 'fill: #575E75;', - 'font-weight: bold;', - '}', - - /* - Don't allow users to select text. It gets annoying when trying to - drag a block and selected text moves instead. - */ - '.blocklySvg text, .blocklyBlockDragSurface text, .blocklyFlyout text, .blocklyToolboxDiv text {', - 'user-select: none;', - '-moz-user-select: none;', - '-webkit-user-select: none;', - 'cursor: inherit;', - '}', - - '.blocklyHidden {', - 'display: none;', - '}', - - '.blocklyFieldDropdown:not(.blocklyHidden) {', - 'display: block;', - '}', - - '.blocklyIconGroup {', - 'cursor: default;', - '}', - - '.blocklyIconGroup:not(:hover),', - '.blocklyIconGroupReadonly {', - 'opacity: .6;', - '}', - - '.blocklyIconShape {', - 'fill: #00f;', - 'stroke: #fff;', - 'stroke-width: 1px;', - '}', - - '.blocklyIconSymbol {', - 'fill: #fff;', - '}', - - '.blocklyMinimalBody {', - 'margin: 0;', - 'padding: 0;', - '}', - - '.blocklyCommentForeignObject {', - 'position: relative;', - 'z-index: 0;', - '}', - - '.blocklyCommentRect {', - 'fill: #E7DE8E;', - 'stroke: #bcA903;', - 'stroke-width: 1px', - '}', - - '.blocklyCommentTarget {', - 'fill: transparent;', - 'stroke: #bcA903;', - '}', - - '.blocklyCommentTargetFocused {', - 'fill: none;', - '}', - - '.blocklyCommentHandleTarget {', - 'fill: none;', - '}', - - '.blocklyCommentHandleTargetFocused {', - 'fill: transparent;', - '}', - - '.blocklyFocused>.blocklyCommentRect {', - 'fill: #B9B272;', - 'stroke: #B9B272;', - '}', - - '.blocklySelected>.blocklyCommentTarget {', - 'stroke: #fc3;', - 'stroke-width: 3px;', - '}', - - - '.blocklyCommentTextarea {', - 'background-color: #fef49c;', - 'border: 0;', - 'outline: 0;', - 'margin: 0;', - 'padding: 3px;', - 'resize: none;', - 'display: block;', - 'overflow: hidden;', - '}', - - '.blocklyCommentDeleteIcon {', - 'cursor: pointer;', - 'fill: #000;', - 'display: none', - '}', - - '.blocklySelected > .blocklyCommentDeleteIcon {', - 'display: block', - '}', - - '.blocklyDeleteIconShape {', - 'fill: #000;', - 'stroke: #000;', - 'stroke-width: 1px;', - '}', - - '.blocklyDeleteIconShape.blocklyDeleteIconHighlighted {', - 'stroke: #fc3;', - '}', - - // Scratch Comments - - '.scratchCommentForeignObject {', - 'position: relative;', - '}', - - '.scratchCommentBody {', - 'background-color: #fef49c;', - 'border-radius: 4px;', - '}', - - '.scratchCommentRect {', - 'fill: #fef49c;', - '}', - - '.scratchCommentTarget {', - 'fill: transparent;', - '}', - - '.scratchWorkspaceCommentBorder {', - 'stroke: #bcA903;', - 'stroke-width: 1px;', - '}', - - '.scratchCommentTargetFocused {', - 'fill: none;', - '}', - - '.scratchCommentTopBar {', - 'fill: #000000;', - 'fill-opacity: 0.1', - '}', - - '.scratchCommentText {', - 'font-family: "Helvetica Neue", Helvetica, sans-serif;', - 'font-size: 12pt;', - 'font-weight: 400;', - '}', - - '.scratchCommentTextarea {', - 'background-color: #fef49c;', - 'border: 0;', - 'outline: 0;', - 'padding: 0;', - 'resize: none;', - 'overflow: hidden;', - '}', - - '.scratchCommentTextarea::placeholder {', - 'color: rgba(0,0,0,0.5);', - 'font-style: italic;', - '}', - - '.scratchCommentResizeSE {', - 'cursor: se-resize;', - 'fill: transparent;', - '}', - - '.scratchCommentResizeSW {', - 'cursor: sw-resize;', - 'fill: transparent;', - '}', - - '.blocklyHtmlInput {', - 'border: none;', - 'font-family: "Helvetica Neue", Helvetica, sans-serif;', - 'font-size: 12pt;', - 'height: 100%;', - 'margin: 0;', - 'outline: none;', - 'box-sizing: border-box;', - 'width: 100%;', - 'text-align: center;', - 'color: $colour_textFieldText;', - 'font-weight: 500;', - '}', - - '.blocklyMainBackground {', - 'stroke-width: 1;', - 'stroke: #c6c6c6;', /* Equates to #ddd due to border being off-pixel. */ - '}', - - '.blocklyMutatorBackground {', - 'fill: #fff;', - 'stroke: #ddd;', - 'stroke-width: 1;', - '}', - - '.blocklyFlyoutBackground {', - 'fill: $colour_flyout;', - 'fill-opacity: .8;', - '}', - - '.blocklyMainWorkspaceScrollbar {', - 'z-index: 20;', - '}', - - '.blocklyFlyoutScrollbar {', - 'z-index: 30;', - '}', - - '.blocklyScrollbarHorizontal, .blocklyScrollbarVertical {', - 'position: absolute;', - 'outline: none;', - '}', - - '.blocklyScrollbarBackground {', - 'opacity: 0;', - '}', - - '.blocklyScrollbarHandle {', - 'fill: $colour_scrollbar;', - '}', - - '.blocklyScrollbarBackground:hover+.blocklyScrollbarHandle,', - '.blocklyScrollbarHandle:hover {', - 'fill: $colour_scrollbarHover;', - '}', - - '.blocklyZoom>image {', - 'opacity: 1;', - '}', - - /* Darken flyout scrollbars due to being on a grey background. */ - /* By contrast, workspace scrollbars are on a white background. */ - '.blocklyFlyout .blocklyScrollbarHandle {', - 'fill: #bbb;', - '}', - - '.blocklyFlyout .blocklyScrollbarBackground:hover+.blocklyScrollbarHandle,', - '.blocklyFlyout .blocklyScrollbarHandle:hover {', - 'fill: #aaa;', - '}', - - '.blocklyInvalidInput {', - 'background: #faa;', - '}', - - '.blocklyAngleCircle {', - 'stroke: ' + Blockly.Colours.motion.tertiary + ';', - 'stroke-width: 1;', - 'fill: ' + Blockly.Colours.motion.secondary + ';', - '}', - - '.blocklyAngleCenterPoint {', - 'stroke: #fff;', - 'stroke-width: 1;', - 'fill: #fff;', - '}', - - '.blocklyAngleDragHandle {', - 'stroke: #fff;', - 'stroke-width: 5;', - 'stroke-opacity: 0.25;', - 'fill: #fff;', - 'cursor: pointer;', - '}', - - '.blocklyAngleDragArrow {', - 'pointer-events: none', - '}', - - '.blocklyAngleMarks {', - 'stroke: #fff;', - 'stroke-width: 1;', - 'stroke-opacity: 0.5;', - '}', - - '.blocklyAngleGauge {', - 'fill: #fff;', - 'fill-opacity: 0.20;', - '}', - - '.blocklyAngleLine {', - 'stroke: #fff;', - 'stroke-width: 1;', - 'stroke-linecap: round;', - 'pointer-events: none;', - '}', - - '.blocklyContextMenu {', - 'border-radius: 4px;', - 'max-height: 100%;', - '}', - - '.blocklyDropdownMenu {', - 'padding: 0 !important;', - '}', - - '.blocklyDropDownNumPad {', - 'background-color: $colour_numPadBackground;', - '}', - - /* Override the default Closure URL. */ - '.blocklyWidgetDiv .goog-option-selected .goog-menuitem-checkbox,', - '.blocklyWidgetDiv .goog-option-selected .goog-menuitem-icon {', - 'background: url(<<>>/sprites.png) no-repeat -48px -16px !important;', - '}', - - /* Category tree in Toolbox. */ - '.blocklyToolboxDiv {', - 'background-color: $colour_toolbox;', - 'color: $colour_toolboxText;', - 'overflow-x: visible;', - 'overflow-y: auto;', - 'position: absolute;', - 'font-family: "Helvetica Neue", Helvetica, sans-serif;', - 'z-index: 40;', /* so blocks go over toolbox when dragging */ - '-webkit-tap-highlight-color: transparent;', /* issue #1345 */ - '}', - - '.blocklyTreeRoot {', - 'padding: 4px 0;', - '}', - - '.blocklyTreeRoot:focus {', - 'outline: none;', - '}', - - '.blocklyTreeRow {', - 'height: 22px;', - 'line-height: 22px;', - 'margin-bottom: 3px;', - 'padding-right: 8px;', - 'white-space: nowrap;', - '}', - - '.blocklyHorizontalTree {', - 'float: left;', - 'margin: 1px 5px 8px 0;', - '}', - - '.blocklyHorizontalTreeRtl {', - 'float: right;', - 'margin: 1px 0 8px 5px;', - '}', - - '.blocklyToolboxDiv[dir="RTL"] .blocklyTreeRow {', - 'margin-left: 8px;', - '}', - - '.blocklyTreeRow:not(.blocklyTreeSelected):hover {', - 'background-color: #e4e4e4;', - '}', - - '.blocklyTreeSeparator {', - 'border-bottom: solid #e5e5e5 1px;', - 'height: 0;', - 'margin: 5px 0;', - '}', - - '.blocklyTreeSeparatorHorizontal {', - 'border-right: solid #e5e5e5 1px;', - 'width: 0;', - 'padding: 5px 0;', - 'margin: 0 5px;', - '}', - - '.blocklyTreeIcon {', - 'background-image: url(<<>>/sprites.png);', - 'height: 16px;', - 'vertical-align: middle;', - 'width: 16px;', - '}', - - '.blocklyTreeIconClosedLtr {', - 'background-position: -32px -1px;', - '}', - - '.blocklyTreeIconClosedRtl {', - 'background-position: 0px -1px;', - '}', - - '.blocklyTreeIconOpen {', - 'background-position: -16px -1px;', - '}', - - '.blocklyTreeSelected>.blocklyTreeIconClosedLtr {', - 'background-position: -32px -17px;', - '}', - - '.blocklyTreeSelected>.blocklyTreeIconClosedRtl {', - 'background-position: 0px -17px;', - '}', - - '.blocklyTreeSelected>.blocklyTreeIconOpen {', - 'background-position: -16px -17px;', - '}', - - '.blocklyTreeIconNone,', - '.blocklyTreeSelected>.blocklyTreeIconNone {', - 'background-position: -48px -1px;', - '}', - - '.blocklyTreeLabel {', - 'cursor: default;', - 'font-family: "Helvetica Neue", Helvetica, sans-serif;', - 'font-size: 16px;', - 'padding: 0 3px;', - 'vertical-align: middle;', - '}', - - '.blocklyToolboxDelete .blocklyTreeLabel {', - 'cursor: url("<<>>/handdelete.cur"), auto;', - '}', - - '.blocklyTreeSelected .blocklyTreeLabel {', - 'color: #fff;', - '}', - - '.blocklyDropDownDiv .goog-slider-horizontal {', - 'margin: 8px;', - 'height: 22px;', - 'width: 150px;', - 'position: relative;', - 'outline: none;', - 'border-radius: 11px;', - 'margin-bottom: 20px;', - '}', - - '.blocklyDropDownDiv .goog-slider-horizontal .goog-slider-thumb {', - 'width: 26px;', - 'height: 26px;', - 'top: -1px;', - 'position: absolute;', - 'background-color: white;', - 'border-radius: 100%;', - '-webkit-box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.15);', - '-moz-box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.15);', - 'box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.15);', - '}', - - '.scratchEyedropper {', - 'background: none;', - 'outline: none;', - 'border: none;', - 'width: 100%;', - 'text-align: center;', - 'border-top: 1px solid #ddd;', - 'padding-top: 5px;', - 'cursor: pointer;', - '}', - - '.scratchColourPickerLabel {', - 'font-family: "Helvetica Neue", Helvetica, sans-serif;', - 'font-size: 0.65rem;', - 'color: $colour_toolboxText;', - 'margin: 8px;', - '}', - - '.scratchColourPickerLabelText {', - 'font-weight: bold;', - '}', - - '.scratchColourPickerReadout {', - 'margin-left: 10px;', - '}', - - '.scratchMatrixButtonDiv {', - 'width: 50%;', - 'text-align: center;', - 'float: left;', - '}', - - '.scratchNotePickerKeyLabel {', - 'font-family: "Helvetica Neue", Helvetica, sans-serif;', - 'font-size: 0.75rem;', - 'fill: $colour_textFieldText;', - 'pointer-events: none;', - '}', - - /* Copied from: goog/css/menu.css */ - /* - * Copyright 2009 The Closure Library Authors. All Rights Reserved. - * - * Use of this source code is governed by the Apache License, Version 2.0. - * See the COPYING file for details. - */ - - /** - * Standard styling for menus created by goog.ui.MenuRenderer. - * - * @author attila@google.com (Attila Bodis) - */ - - '.blocklyWidgetDiv .goog-menu {', - 'background: #fff;', - 'border-color: #ccc #666 #666 #ccc;', - 'border-style: solid;', - 'border-width: 1px;', - 'cursor: default;', - 'font: normal 13px "Helvetica Neue", Helvetica, sans-serif;', - 'margin: 0;', - 'outline: none;', - 'padding: 4px 0;', - 'position: absolute;', - 'overflow-y: auto;', - 'overflow-x: hidden;', - 'z-index: 20000;', /* Arbitrary, but some apps depend on it... */ - '}', - - '.blocklyDropDownDiv .goog-menu {', - 'cursor: default;', - 'font: normal 13px "Helvetica Neue", Helvetica, sans-serif;', - 'outline: none;', - 'z-index: 20000;', /* Arbitrary, but some apps depend on it... */ - '}', - - /* Copied from: goog/css/menuitem.css */ - /* - * Copyright 2009 The Closure Library Authors. All Rights Reserved. - * - * Use of this source code is governed by the Apache License, Version 2.0. - * See the COPYING file for details. - */ - - /** - * Standard styling for menus created by goog.ui.MenuItemRenderer. - * - * @author attila@google.com (Attila Bodis) - */ - - /** - * State: resting. - * - * NOTE(mleibman,chrishenry): - * The RTL support in Closure is provided via two mechanisms -- "rtl" CSS - * classes and BiDi flipping done by the CSS compiler. Closure supports RTL - * with or without the use of the CSS compiler. In order for them not - * to conflict with each other, the "rtl" CSS classes need to have the #noflip - * annotation. The non-rtl counterparts should ideally have them as well, but, - * since .goog-menuitem existed without .goog-menuitem-rtl for so long before - * being added, there is a risk of people having templates where they are not - * rendering the .goog-menuitem-rtl class when in RTL and instead rely solely - * on the BiDi flipping by the CSS compiler. That's why we're not adding the - * #noflip to .goog-menuitem. - */ - '.blocklyWidgetDiv .goog-menuitem {', - 'color: #000;', - 'font: normal 13px "Helvetica Neue", Helvetica, sans-serif;', - 'list-style: none;', - 'margin: 0;', - /* 28px on the left for icon or checkbox; 7em on the right for shortcut. */ - 'padding: 4px 7em 4px 28px;', - 'white-space: nowrap;', - '}', - - '.blocklyDropDownDiv .goog-menuitem {', - 'color: $colour_text;', - 'font: normal 13px "Helvetica Neue", Helvetica, sans-serif;', - 'font-weight: bold;', - 'list-style: none;', - 'margin: 0;', - 'min-height: 24px;', - /* 28px on the left for icon or checkbox; 7em on the right for shortcut. */ - 'padding: 4px 7em 4px 28px;', - 'white-space: nowrap;', - '}', - - /* BiDi override for the resting state. */ - /* #noflip */ - '.blocklyWidgetDiv .goog-menuitem.goog-menuitem-rtl, ', - '.blocklyDropDownDiv .goog-menuitem.goog-menuitem-rtl {', - /* Flip left/right padding for BiDi. */ - 'padding-left: 7em;', - 'padding-right: 28px;', - '}', - - /* If a menu doesn't have checkable items or items with icons, remove padding. */ - '.blocklyWidgetDiv .goog-menu-nocheckbox .goog-menuitem,', - '.blocklyWidgetDiv .goog-menu-noicon .goog-menuitem, ', - '.blocklyDropDownDiv .goog-menu-nocheckbox .goog-menuitem,', - '.blocklyDropDownDiv .goog-menu-noicon .goog-menuitem { ', - 'padding-left: 12px;', - '}', - - /* - * If a menu doesn't have items with shortcuts, leave just enough room for - * submenu arrows, if they are rendered. - */ - '.blocklyWidgetDiv .goog-menu-noaccel .goog-menuitem, ', - '.blocklyDropDownDiv .goog-menu-noaccel .goog-menuitem {', - 'padding-right: 20px;', - '}', - - '.blocklyWidgetDiv .goog-menuitem-content ', - '.blocklyDropDownDiv .goog-menuitem-content {', - 'color: #000;', - 'font: normal 13px "Helvetica Neue", Helvetica, sans-serif;', - '}', - - /* State: disabled. */ - '.blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-accel,', - '.blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-content, ', - '.blocklyDropDownDiv .goog-menuitem-disabled .goog-menuitem-accel,', - '.blocklyDropDownDiv .goog-menuitem-disabled .goog-menuitem-content {', - 'color: #ccc !important;', - '}', - - '.blocklyWidgetDiv .goog-menuitem-disabled .goog-menuitem-icon, ', - '.blocklyDropDownDiv .goog-menuitem-disabled .goog-menuitem-icon {', - 'opacity: 0.3;', - '-moz-opacity: 0.3;', - 'filter: alpha(opacity=30);', - '}', - - /* State: hover. */ - '.blocklyWidgetDiv .goog-menuitem-highlight,', - '.blocklyWidgetDiv .goog-menuitem-hover {', - 'background-color: #d6e9f8;', - /* Use an explicit top and bottom border so that the selection is visible', - * in high contrast mode. */ - 'border-color: #d6e9f8;', - 'border-style: dotted;', - 'border-width: 1px 0;', - 'padding-bottom: 3px;', - 'padding-top: 3px;', - '}', - - '.blocklyDropDownDiv .goog-menuitem-highlight,', - '.blocklyDropDownDiv .goog-menuitem-hover {', - 'background-color: $colour_menuHover;', - '}', - - /* State: selected/checked. */ - '.blocklyWidgetDiv .goog-menuitem-checkbox,', - '.blocklyWidgetDiv .goog-menuitem-icon, ', - '.blocklyDropDownDiv .goog-menuitem-checkbox,', - '.blocklyDropDownDiv .goog-menuitem-icon {', - 'background-repeat: no-repeat;', - 'height: 16px;', - 'left: 6px;', - 'position: absolute;', - 'right: auto;', - 'vertical-align: middle;', - 'width: 16px;', - '}', - - '.blocklyWidgetDiv .goog-option-selected .goog-menuitem-checkbox,', - '.blocklyWidgetDiv .goog-option-selected .goog-menuitem-icon,', - '.blocklyDropDownDiv .goog-option-selected .goog-menuitem-checkbox,', - '.blocklyDropDownDiv .goog-option-selected .goog-menuitem-icon {', - /* Client apps may override the URL at which they serve the sprite. */ - 'background: url(<<>>/sprites.png) no-repeat -48px -16px !important;', - 'position: static;', /* Scroll with the menu. */ - 'float: left;', - 'margin-left: -24px;', - '}', - - /* BiDi override for the selected/checked state. */ - /* #noflip */ - '.blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-checkbox,', - '.blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-icon,', - '.blocklyDropDownDiv .goog-menuitem-rtl .goog-menuitem-checkbox,', - '.blocklyDropDownDiv .goog-menuitem-rtl .goog-menuitem-icon {', - /* Flip left/right positioning. */ - 'float: right;', - 'margin-right: -24px;', - '}', - - /* Keyboard shortcut ("accelerator") style. */ - '.blocklyWidgetDiv .goog-menuitem-accel, ', - '.blocklyDropDownDiv .goog-menuitem-accel {', - 'color: #999;', - /* Keyboard shortcuts are untranslated; always left-to-right. */ - /* #noflip */ - 'direction: ltr;', - 'left: auto;', - 'padding: 0 6px;', - 'position: absolute;', - 'right: 0;', - 'text-align: right;', - '}', - - /* BiDi override for shortcut style. */ - /* #noflip */ - '.blocklyWidgetDiv .goog-menuitem-rtl .goog-menuitem-accel, ', - '.blocklyDropDownDiv .goog-menuitem-rtl .goog-menuitem-accel {', - /* Flip left/right positioning and text alignment. */ - 'left: 0;', - 'right: auto;', - 'text-align: left;', - '}', - - /* Mnemonic styles. */ - '.blocklyWidgetDiv .goog-menuitem-mnemonic-hint, ', - '.blocklyDropDownDiv .goog-menuitem-mnemonic-hint {', - 'text-decoration: underline;', - '}', - - '.blocklyWidgetDiv .goog-menuitem-mnemonic-separator, ', - '.blocklyDropDownDiv .goog-menuitem-mnemonic-separator {', - 'color: #999;', - 'font-size: 12px;', - 'padding-left: 4px;', - '}', - - /* Copied from: goog/css/menuseparator.css */ - /* - * Copyright 2009 The Closure Library Authors. All Rights Reserved. - * - * Use of this source code is governed by the Apache License, Version 2.0. - * See the COPYING file for details. - */ - - /** - * Standard styling for menus created by goog.ui.MenuSeparatorRenderer. - * - * @author attila@google.com (Attila Bodis) - */ - - '.blocklyWidgetDiv .goog-menuseparator, ', - '.blocklyDropDownDiv .goog-menuseparator {', - 'border-top: 1px solid #ccc;', - 'margin: 4px 0;', - 'padding: 0;', - '}', - - '.blocklyFlyoutCheckbox {', - 'fill: white;', - 'stroke: #c8c8c8;', - '}', - - '.checked > .blocklyFlyoutCheckbox {', - 'fill: ' + Blockly.Colours.motion.primary + ';', - 'stroke: ' + Blockly.Colours.motion.tertiary + ';', - '}', - - '.blocklyFlyoutCheckboxPath {', - 'fill: transparent;', - 'stroke: white;', - 'stroke-width: 3;', - 'stroke-linecap: round;', - 'stroke-linejoin: round;', - '}', - - '.scratchCategoryMenu {', - 'width: 60px;', - 'background: $colour_toolbox;', - 'color: $colour_toolboxText;', - 'font-size: .7rem;', - 'user-select: none;', - '-webkit-user-select: none;', - '-moz-user-select: none;', - '-ms-user-select: none;', - '}', - - '.scratchCategoryMenuHorizontal {', - 'width: 100%;', - 'height: 50px;', - 'background: $colour_toolbox;', - 'color: $colour_toolboxText;', - 'font-size: .7em;', - 'user-select: none;', - '-webkit-user-select: none;', - '-moz-user-select: none;', - '-ms-user-select: none;', - '}', - - '.scratchCategoryMenuHorizontal .scratchCategoryMenuRow {', - 'float: left;', - 'margin: 3px;', - '}', - - '.scratchCategoryMenuRow {', - '}', - - '.scratchCategoryMenuItem {', - 'padding: 0.375rem 0px;', - 'cursor: pointer;', - 'text-align: center;', - '}', - - '.scratchCategoryMenuHorizontal .scratchCategoryMenuItem {', - 'padding: 6px 5px;', - '}', - - '.scratchCategoryMenuItem.categorySelected {', - 'background: $colour_toolboxSelected;', - '}', - - '.scratchCategoryItemBubble {', - 'width: 1.25rem;', - 'height: 1.25rem;', - 'border: 1px solid;', - 'border-radius: 100%;', - 'margin: 0 auto 0.125rem;', - '}', - - '.scratchCategoryItemIcon {', - 'width: 1.25rem;', - 'height: 1.25rem;', - 'margin: 0 auto 0.125rem;', - 'background-size: 100%;', - '}', - - '.scratchCategoryMenuItem:hover {', - 'color: $colour_toolboxHover !important;', - '}', - '' -]; diff --git a/core/data_category.js b/core/data_category.js deleted file mode 100644 index aa1a0ebd19..0000000000 --- a/core/data_category.js +++ /dev/null @@ -1,490 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Data Flyout components including variable and list blocks. - * @author marisaleung@google.com (Marisa Leung) - */ -'use strict'; - -/** - * @name Blockly.DataCategory - * @namespace - **/ -goog.provide('Blockly.DataCategory'); - -goog.require('Blockly.Blocks'); -goog.require('Blockly.VariableModel'); -goog.require('Blockly.Variables'); -goog.require('Blockly.Workspace'); - -/** - * Construct the blocks required by the flyout for the variable category. - * @param {!Blockly.Workspace} workspace The workspace containing variables. - * @return {!Array.} Array of XML block elements. - */ -Blockly.DataCategory = function(workspace) { - var variableModelList = workspace.getVariablesOfType(''); - variableModelList.sort(Blockly.VariableModel.compareByName); - var xmlList = []; - - Blockly.DataCategory.addCreateButton(xmlList, workspace, 'VARIABLE'); - - for (var i = 0; i < variableModelList.length; i++) { - Blockly.DataCategory.addDataVariable(xmlList, variableModelList[i]); - } - - if (variableModelList.length > 0) { - xmlList[xmlList.length - 1].setAttribute('gap', 24); - var firstVariable = variableModelList[0]; - - Blockly.DataCategory.addSetVariableTo(xmlList, firstVariable); - Blockly.DataCategory.addChangeVariableBy(xmlList, firstVariable); - Blockly.DataCategory.addShowVariable(xmlList, firstVariable); - Blockly.DataCategory.addHideVariable(xmlList, firstVariable); - } - - // Now add list variables to the flyout - Blockly.DataCategory.addCreateButton(xmlList, workspace, 'LIST'); - variableModelList = workspace.getVariablesOfType(Blockly.LIST_VARIABLE_TYPE); - variableModelList.sort(Blockly.VariableModel.compareByName); - for (var i = 0; i < variableModelList.length; i++) { - Blockly.DataCategory.addDataList(xmlList, variableModelList[i]); - } - - if (variableModelList.length > 0) { - xmlList[xmlList.length - 1].setAttribute('gap', 24); - var firstVariable = variableModelList[0]; - - Blockly.DataCategory.addAddToList(xmlList, firstVariable); - Blockly.DataCategory.addSep(xmlList); - Blockly.DataCategory.addDeleteOfList(xmlList, firstVariable); - Blockly.DataCategory.addDeleteAllOfList(xmlList, firstVariable); - Blockly.DataCategory.addInsertAtList(xmlList, firstVariable); - Blockly.DataCategory.addReplaceItemOfList(xmlList, firstVariable); - Blockly.DataCategory.addSep(xmlList); - Blockly.DataCategory.addItemOfList(xmlList, firstVariable); - Blockly.DataCategory.addItemNumberOfList(xmlList, firstVariable); - Blockly.DataCategory.addLengthOfList(xmlList, firstVariable); - Blockly.DataCategory.addListContainsItem(xmlList, firstVariable); - Blockly.DataCategory.addSep(xmlList); - Blockly.DataCategory.addShowList(xmlList, firstVariable); - Blockly.DataCategory.addHideList(xmlList, firstVariable); - } - - return xmlList; -}; - -/** - * Construct and add a data_variable block to xmlList. - * @param {!Array.} xmlList Array of XML block elements. - * @param {?Blockly.VariableModel} variable Variable to select in the field. - */ -Blockly.DataCategory.addDataVariable = function(xmlList, variable) { - // - // variablename - // - Blockly.DataCategory.addBlock(xmlList, variable, 'data_variable', 'VARIABLE'); - // In the flyout, this ID must match variable ID for monitor syncing reasons - xmlList[xmlList.length - 1].setAttribute('id', variable.getId()); -}; - -/** - * Construct and add a data_setvariableto block to xmlList. - * @param {!Array.} xmlList Array of XML block elements. - * @param {?Blockly.VariableModel} variable Variable to select in the field. - */ -Blockly.DataCategory.addSetVariableTo = function(xmlList, variable) { - // - // - // - // - // - // - // 0 - // - // - // - Blockly.DataCategory.addBlock(xmlList, variable, 'data_setvariableto', - 'VARIABLE', ['VALUE', 'text', 0]); -}; - -/** - * Construct and add a data_changevariableby block to xmlList. - * @param {!Array.} xmlList Array of XML block elements. - * @param {?Blockly.VariableModel} variable Variable to select in the field. - */ -Blockly.DataCategory.addChangeVariableBy = function(xmlList, variable) { - // - // - // - // - // - // - // 1 - // - // - // - Blockly.DataCategory.addBlock(xmlList, variable, 'data_changevariableby', - 'VARIABLE', ['VALUE', 'math_number', 1]); -}; - -/** - * Construct and add a data_showVariable block to xmlList. - * @param {!Array.} xmlList Array of XML block elements. - * @param {?Blockly.VariableModel} variable Variable to select in the field. - */ -Blockly.DataCategory.addShowVariable = function(xmlList, variable) { - // - // - // - // - // - Blockly.DataCategory.addBlock(xmlList, variable, 'data_showvariable', - 'VARIABLE'); -}; - -/** - * Construct and add a data_hideVariable block to xmlList. - * @param {!Array.} xmlList Array of XML block elements. - * @param {?Blockly.VariableModel} variable Variable to select in the field. - */ -Blockly.DataCategory.addHideVariable = function(xmlList, variable) { - // - // - // - // - // - Blockly.DataCategory.addBlock(xmlList, variable, 'data_hidevariable', - 'VARIABLE'); -}; - -/** - * Construct and add a data_listcontents block to xmlList. - * @param {!Array.} xmlList Array of XML block elements. - * @param {?Blockly.VariableModel} variable Variable to select in the field. - */ -Blockly.DataCategory.addDataList = function(xmlList, variable) { - // - // variablename - // - Blockly.DataCategory.addBlock(xmlList, variable, 'data_listcontents', 'LIST'); - // In the flyout, this ID must match variable ID for monitor syncing reasons - xmlList[xmlList.length - 1].setAttribute('id', variable.getId()); -}; - -/** - * Construct and add a data_addtolist block to xmlList. - * @param {!Array.} xmlList Array of XML block elements. - * @param {?Blockly.VariableModel} variable Variable to select in the field. - */ -Blockly.DataCategory.addAddToList = function(xmlList, variable) { - // - // variablename - // - // - // thing - // - // - // - Blockly.DataCategory.addBlock(xmlList, variable, 'data_addtolist', 'LIST', - ['ITEM', 'text', Blockly.Msg.DEFAULT_LIST_ITEM]); -}; - -/** - * Construct and add a data_deleteoflist block to xmlList. - * @param {!Array.} xmlList Array of XML block elements. - * @param {?Blockly.VariableModel} variable Variable to select in the field. - */ -Blockly.DataCategory.addDeleteOfList = function(xmlList, variable) { - // - // variablename - // - // - // 1 - // - // - // - Blockly.DataCategory.addBlock(xmlList, variable, 'data_deleteoflist', 'LIST', - ['INDEX', 'math_integer', 1]); -}; - -/** - * Construct and add a data_deleteoflist block to xmlList. - * @param {!Array.} xmlList Array of XML block elements. - * @param {?Blockly.VariableModel} variable Variable to select in the field. - */ -Blockly.DataCategory.addDeleteAllOfList = function(xmlList, variable) { - // - // variablename - // - Blockly.DataCategory.addBlock(xmlList, variable, 'data_deletealloflist', - 'LIST'); -}; - -/** - * Construct and add a data_insertatlist block to xmlList. - * @param {!Array.} xmlList Array of XML block elements. - * @param {?Blockly.VariableModel} variable Variable to select in the field. - */ -Blockly.DataCategory.addInsertAtList = function(xmlList, variable) { - // - // variablename - // - // - // 1 - // - // - // - // - // thing - // - // - // - Blockly.DataCategory.addBlock(xmlList, variable, 'data_insertatlist', 'LIST', - ['INDEX', 'math_integer', 1], ['ITEM', 'text', Blockly.Msg.DEFAULT_LIST_ITEM]); -}; - -/** - * Construct and add a data_replaceitemoflist block to xmlList. - * @param {!Array.} xmlList Array of XML block elements. - * @param {?Blockly.VariableModel} variable Variable to select in the field. - */ -Blockly.DataCategory.addReplaceItemOfList = function(xmlList, variable) { - // - // variablename - // - // - // 1 - // - // - // - // - // thing - // - // - // - Blockly.DataCategory.addBlock(xmlList, variable, 'data_replaceitemoflist', - 'LIST', ['INDEX', 'math_integer', 1], ['ITEM', 'text', Blockly.Msg.DEFAULT_LIST_ITEM]); -}; - -/** - * Construct and add a data_itemoflist block to xmlList. - * @param {!Array.} xmlList Array of XML block elements. - * @param {?Blockly.VariableModel} variable Variable to select in the field. - */ -Blockly.DataCategory.addItemOfList = function(xmlList, variable) { - // - // variablename - // - // - // 1 - // - // - // - Blockly.DataCategory.addBlock(xmlList, variable, 'data_itemoflist', 'LIST', - ['INDEX', 'math_integer', 1]); -}; - -/** Construct and add a data_itemnumoflist block to xmlList. - * @param {!Array.} xmlList Array of XML block elements. - * @param {?Blockly.VariableModel} variable Variable to select in the field. - */ -Blockly.DataCategory.addItemNumberOfList = function(xmlList, variable) { - // - // - // - // thing - // - // - // variablename - // - Blockly.DataCategory.addBlock(xmlList, variable, 'data_itemnumoflist', - 'LIST', ['ITEM', 'text', Blockly.Msg.DEFAULT_LIST_ITEM]); -}; - -/** - * Construct and add a data_lengthoflist block to xmlList. - * @param {!Array.} xmlList Array of XML block elements. - * @param {?Blockly.VariableModel} variable Variable to select in the field. - */ -Blockly.DataCategory.addLengthOfList = function(xmlList, variable) { - // - // variablename - // - Blockly.DataCategory.addBlock(xmlList, variable, 'data_lengthoflist', 'LIST'); -}; - -/** - * Construct and add a data_listcontainsitem block to xmlList. - * @param {!Array.} xmlList Array of XML block elements. - * @param {?Blockly.VariableModel} variable Variable to select in the field. - */ -Blockly.DataCategory.addListContainsItem = function(xmlList, variable) { - // - // variablename - // - // - // thing - // - // - // - Blockly.DataCategory.addBlock(xmlList, variable, 'data_listcontainsitem', - 'LIST', ['ITEM', 'text', Blockly.Msg.DEFAULT_LIST_ITEM]); -}; - -/** - * Construct and add a data_showlist block to xmlList. - * @param {!Array.} xmlList Array of XML block elements. - * @param {?Blockly.VariableModel} variable Variable to select in the field. - */ -Blockly.DataCategory.addShowList = function(xmlList, variable) { - // - // variablename - // - Blockly.DataCategory.addBlock(xmlList, variable, 'data_showlist', 'LIST'); -}; - -/** - * Construct and add a data_hidelist block to xmlList. - * @param {!Array.} xmlList Array of XML block elements. - * @param {?Blockly.VariableModel} variable Variable to select in the field. - */ -Blockly.DataCategory.addHideList = function(xmlList, variable) { - // - // variablename - // - Blockly.DataCategory.addBlock(xmlList, variable, 'data_hidelist', 'LIST'); -}; - -/** - * Construct a create variable button and push it to the xmlList. - * @param {!Array.} xmlList Array of XML block elements. - * @param {Blockly.Workspace} workspace Workspace to register callback to. - * @param {string} type Type of variable this is for. For example, 'LIST' or - * 'VARIABLE'. - */ -Blockly.DataCategory.addCreateButton = function(xmlList, workspace, type) { - var button = goog.dom.createDom('button'); - // Set default msg, callbackKey, and callback values for type 'VARIABLE' - var msg = Blockly.Msg.NEW_VARIABLE; - var callbackKey = 'CREATE_VARIABLE'; - var callback = function(button) { - Blockly.Variables.createVariable(button.getTargetWorkspace(), null, '');}; - - if (type === 'LIST') { - msg = Blockly.Msg.NEW_LIST; - callbackKey = 'CREATE_LIST'; - callback = function(button) { - Blockly.Variables.createVariable(button.getTargetWorkspace(), null, - Blockly.LIST_VARIABLE_TYPE);}; - } - button.setAttribute('text', msg); - button.setAttribute('callbackKey', callbackKey); - workspace.registerButtonCallback(callbackKey, callback); - xmlList.push(button); -}; - -/** - * Construct a variable block with the given variable, blockType, and optional - * value tags. Add the variable block to the given xmlList. - * @param {!Array.} xmlList Array of XML block elements. - * @param {?Blockly.VariableModel} variable Variable to select in the field. - * @param {string} blockType Type of block. For example, 'data_hidelist' or - * data_showlist'. - * @param {string} fieldName Name of field in block. For example: 'VARIABLE' or - * 'LIST'. - * @param {?Array.} opt_value Optional array containing the value name - * and shadow type of value tags. - * @param {?Array.} opt_secondValue Optional array containing the value - * name and shadow type of a second pair of value tags. - */ -Blockly.DataCategory.addBlock = function(xmlList, variable, blockType, - fieldName, opt_value, opt_secondValue) { - if (Blockly.Blocks[blockType]) { - var firstValueField; - var secondValueField; - if (opt_value) { - firstValueField = Blockly.DataCategory.createValue(opt_value[0], - opt_value[1], opt_value[2]); - } - if (opt_secondValue) { - secondValueField = Blockly.DataCategory.createValue(opt_secondValue[0], - opt_secondValue[1], opt_secondValue[2]); - } - - var gap = 8; - var blockText = '' + - '' + - Blockly.Variables.generateVariableFieldXml_(variable, fieldName) + - firstValueField + secondValueField + - '' + - ''; - var block = Blockly.Xml.textToDom(blockText).firstChild; - xmlList.push(block); - } -}; - -/** - * Create the text representation of a value dom element with a shadow of the - * indicated type inside. - * @param {string} valueName Name of the value tags. - * @param {string} type The type of the shadow tags. - * @param {string|number} value The default shadow value. - * @return {string} The generated dom element in text. - */ -Blockly.DataCategory.createValue = function(valueName, type, value) { - var fieldName; - switch (valueName) { - case 'ITEM': - fieldName = 'TEXT'; - break; - case 'INDEX': - fieldName = 'NUM'; - break; - case 'VALUE': - if (type === 'math_number') { - fieldName = 'NUM'; - } else { - fieldName = 'TEXT'; - } - break; - } - var valueField = - '' + - '' + - '' + value + '' + - '' + - ''; - return valueField; -}; - -/** - * Construct a block separator. Add the separator to the given xmlList. - * @param {!Array.} xmlList Array of XML block elements. - */ -Blockly.DataCategory.addSep = function(xmlList) { - var gap = 36; - var sepText = '' + - '' + - ''; - var sep = Blockly.Xml.textToDom(sepText).firstChild; - xmlList.push(sep); -}; diff --git a/core/dragged_connection_manager.js b/core/dragged_connection_manager.js deleted file mode 100644 index 9727c0844f..0000000000 --- a/core/dragged_connection_manager.js +++ /dev/null @@ -1,260 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Class that controls updates to connections during drags. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.DraggedConnectionManager'); - -goog.require('Blockly.BlockAnimations'); -goog.require('Blockly.RenderedConnection'); - -goog.require('goog.math.Coordinate'); - - -/** - * Class that controls updates to connections during drags. It is primarily - * responsible for finding the closest eligible connection and highlighting or - * unhiglighting it as needed during a drag. - * @param {!Blockly.BlockSvg} block The top block in the stack being dragged. - * @constructor - */ -Blockly.DraggedConnectionManager = function(block) { - Blockly.selected = block; - - /** - * The top block in the stack being dragged. - * Does not change during a drag. - * @type {!Blockly.Block} - * @private - */ - this.topBlock_ = block; - - /** - * The workspace on which these connections are being dragged. - * Does not change during a drag. - * @type {!Blockly.WorkspaceSvg} - * @private - */ - this.workspace_ = block.workspace; - - /** - * The connections on the dragging blocks that are available to connect to - * other blocks. This includes all open connections on the top block, as well - * as the last connection on the block stack. - * Does not change during a drag. - * @type {!Array.} - * @private - */ - this.availableConnections_ = this.initAvailableConnections_(); - - /** - * The connection that this block would connect to if released immediately. - * Updated on every mouse move. - * @type {Blockly.RenderedConnection} - * @private - */ - this.closestConnection_ = null; - - /** - * The connection that would connect to this.closestConnection_ if this block - * were released immediately. - * Updated on every mouse move. - * @type {Blockly.RenderedConnection} - * @private - */ - this.localConnection_ = null; - - /** - * The distance between this.closestConnection_ and this.localConnection_, - * in workspace units. - * Updated on every mouse move. - * @type {number} - * @private - */ - this.radiusConnection_ = 0; - - /** - * Whether the block would be deleted if it were dropped immediately. - * Updated on every mouse move. - * @type {boolean} - * @private - */ - this.wouldDeleteBlock_ = false; -}; - -/** - * Sever all links from this object. - * @package - */ -Blockly.DraggedConnectionManager.prototype.dispose = function() { - this.topBlock_ = null; - this.workspace_ = null; - this.availableConnections_.length = 0; - this.closestConnection_ = null; - this.localConnection_ = null; -}; - -/** - * Return whether the block would be deleted if dropped immediately, based on - * information from the most recent move event. - * @return {boolean} true if the block would be deleted if dropped immediately. - * @package - */ -Blockly.DraggedConnectionManager.prototype.wouldDeleteBlock = function() { - return this.wouldDeleteBlock_; -}; - -/** - * Return whether the block would be connected if dropped immediately, based on - * information from the most recent move event. - * @return {boolean} true if the block would be connected if dropped immediately. - * @package - */ -Blockly.DraggedConnectionManager.prototype.wouldConnectBlock = function() { - return !!this.closestConnection_; -}; - -/** - * Connect to the closest connection and render the results. - * This should be called at the end of a drag. - * @package - */ -Blockly.DraggedConnectionManager.prototype.applyConnections = function() { - if (this.closestConnection_) { - // Connect two blocks together. - this.localConnection_.connect(this.closestConnection_); - if (this.topBlock_.rendered) { - // Trigger a connection animation. - // Determine which connection is inferior (lower in the source stack). - var inferiorConnection = this.localConnection_.isSuperior() ? - this.closestConnection_ : this.localConnection_; - Blockly.BlockAnimations.connectionUiEffect( - inferiorConnection.getSourceBlock()); - // Bring the just-edited stack to the front. - var rootBlock = this.topBlock_.getRootBlock(); - rootBlock.bringToFront(); - } - this.removeHighlighting_(); - } -}; - -/** - * Update highlighted connections based on the most recent move location. - * @param {!goog.math.Coordinate} dxy Position relative to drag start, - * in workspace units. - * @param {?number} deleteArea One of {@link Blockly.DELETE_AREA_TRASH}, - * {@link Blockly.DELETE_AREA_TOOLBOX}, or {@link Blockly.DELETE_AREA_NONE}. - * @param {?boolean} isOutside True if the drag is going outside the blocks workspace - * @package - */ -Blockly.DraggedConnectionManager.prototype.update = function(dxy, deleteArea, isOutside) { - var oldClosestConnection; - var closestConnectionChanged; - // If dragged outside, don't connect, since the connections aren't visible. - if (!isOutside) { - oldClosestConnection = this.closestConnection_; - closestConnectionChanged = this.updateClosest_(dxy); - if (closestConnectionChanged && oldClosestConnection) { - oldClosestConnection.unhighlight(); - } - } else if (this.closestConnection_) { - this.closestConnection_.unhighlight(); - this.closestConnection_ = null; - } - - // Prefer connecting over dropping into the trash can, but prefer dragging to - // the toolbox over connecting to other blocks. - var wouldConnect = !!this.closestConnection_ && - deleteArea != Blockly.DELETE_AREA_TOOLBOX; - var wouldDelete = !!deleteArea && !this.topBlock_.getParent() && - this.topBlock_.isDeletable(); - this.wouldDeleteBlock_ = wouldDelete && !wouldConnect; - - if (!this.wouldDeleteBlock_ && closestConnectionChanged && - this.closestConnection_) { - this.addHighlighting_(); - } -}; - -/** - * Remove highlighting from the currently highlighted connection, if it exists. - * @private - */ -Blockly.DraggedConnectionManager.prototype.removeHighlighting_ = function() { - if (this.closestConnection_) { - this.closestConnection_.unhighlight(); - } -}; - -/** - * Add highlighting to the closest connection, if it exists. - * @private - */ -Blockly.DraggedConnectionManager.prototype.addHighlighting_ = function() { - if (this.closestConnection_) { - this.closestConnection_.highlight(); - } -}; - -/** - * Populate the list of available connections on this block stack. This should - * only be called once, at the beginning of a drag. - * @return {!Array.} a list of available - * connections. - * @private - */ -Blockly.DraggedConnectionManager.prototype.initAvailableConnections_ = function() { - var available = this.topBlock_.getConnections_(false); - // Also check the last connection on this stack - var lastOnStack = this.topBlock_.lastConnectionInStack(); - if (lastOnStack && lastOnStack != this.topBlock_.nextConnection) { - available.push(lastOnStack); - } - return available; -}; - -/** - * Find the new closest connection, and update internal state in response. - * @param {!goog.math.Coordinate} dxy Position relative to the drag start, - * in workspace units. - * @return {boolean} Whether the closest connection has changed. - * @private - */ -Blockly.DraggedConnectionManager.prototype.updateClosest_ = function(dxy) { - var oldClosestConnection = this.closestConnection_; - - this.closestConnection_ = null; - this.localConnection_ = null; - this.radiusConnection_ = Blockly.SNAP_RADIUS; - for (var i = 0; i < this.availableConnections_.length; i++) { - var myConnection = this.availableConnections_[i]; - var neighbour = myConnection.closest(this.radiusConnection_, dxy); - if (neighbour.connection) { - this.closestConnection_ = neighbour.connection; - this.localConnection_ = myConnection; - this.radiusConnection_ = neighbour.radius; - } - } - return oldClosestConnection != this.closestConnection_; -}; diff --git a/core/dropdowndiv.js b/core/dropdowndiv.js deleted file mode 100644 index ea64b77ce6..0000000000 --- a/core/dropdowndiv.js +++ /dev/null @@ -1,408 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Massachusetts Institute of Technology - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview A div that floats on top of the workspace, for drop-down menus. - * The drop-down can be kept inside the workspace, animate in/out, etc. - * @author tmickel@mit.edu (Tim Mickel) - */ - -'use strict'; - -goog.provide('Blockly.DropDownDiv'); - -goog.require('goog.dom'); -goog.require('goog.style'); - -/** - * Class for drop-down div. - * @constructor - */ -Blockly.DropDownDiv = function() { -}; - -/** - * The div element. Set once by Blockly.DropDownDiv.createDom. - * @type {Element} - * @private - */ -Blockly.DropDownDiv.DIV_ = null; - -/** - * Drop-downs will appear within the bounds of this element if possible. - * Set in Blockly.DropDownDiv.setBoundsElement. - * @type {Element} - * @private - */ -Blockly.DropDownDiv.boundsElement_ = null; - -/** - * The object currently using the drop-down. - * @type {Object} - * @private - */ -Blockly.DropDownDiv.owner_ = null; - -/** - * Arrow size in px. Should match the value in CSS (need to position pre-render). - * @type {number} - * @const - */ -Blockly.DropDownDiv.ARROW_SIZE = 16; - -/** - * Drop-down border size in px. Should match the value in CSS (need to position the arrow). - * @type {number} - * @const - */ -Blockly.DropDownDiv.BORDER_SIZE = 1; - -/** - * Amount the arrow must be kept away from the edges of the main drop-down div, in px. - * @type {number} - * @const - */ -Blockly.DropDownDiv.ARROW_HORIZONTAL_PADDING = 12; - -/** - * Amount drop-downs should be padded away from the source, in px. - * @type {number} - * @const - */ -Blockly.DropDownDiv.PADDING_Y = 20; - -/** - * Length of animations in seconds. - * @type {number} - * @const - */ -Blockly.DropDownDiv.ANIMATION_TIME = 0.25; - -/** - * Timer for animation out, to be cleared if we need to immediately hide - * without disrupting new shows. - * @type {number} - */ -Blockly.DropDownDiv.animateOutTimer_ = null; - -/** - * Callback for when the drop-down is hidden. - * @type {Function} - */ -Blockly.DropDownDiv.onHide_ = 0; - -/** - * Create and insert the DOM element for this div. - * @param {Element} container Element that the div should be contained in. - */ -Blockly.DropDownDiv.createDom = function() { - if (Blockly.DropDownDiv.DIV_) { - return; // Already created. - } - Blockly.DropDownDiv.DIV_ = goog.dom.createDom('div', 'blocklyDropDownDiv'); - document.body.appendChild(Blockly.DropDownDiv.DIV_); - Blockly.DropDownDiv.content_ = goog.dom.createDom('div', 'blocklyDropDownContent'); - Blockly.DropDownDiv.DIV_.appendChild(Blockly.DropDownDiv.content_); - Blockly.DropDownDiv.arrow_ = goog.dom.createDom('div', 'blocklyDropDownArrow'); - Blockly.DropDownDiv.DIV_.appendChild(Blockly.DropDownDiv.arrow_); - - // Transition animation for transform: translate() and opacity. - Blockly.DropDownDiv.DIV_.style.transition = 'transform ' + - Blockly.DropDownDiv.ANIMATION_TIME + 's, ' + - 'opacity ' + Blockly.DropDownDiv.ANIMATION_TIME + 's'; -}; - -/** - * Set an element to maintain bounds within. Drop-downs will appear - * within the box of this element if possible. - * @param {Element} boundsElement Element to bound drop-down to. - */ -Blockly.DropDownDiv.setBoundsElement = function(boundsElement) { - Blockly.DropDownDiv.boundsElement_ = boundsElement; -}; - -/** - * Provide the div for inserting content into the drop-down. - * @return {Element} Div to populate with content - */ -Blockly.DropDownDiv.getContentDiv = function() { - return Blockly.DropDownDiv.content_; -}; - -/** - * Clear the content of the drop-down. - */ -Blockly.DropDownDiv.clearContent = function() { - Blockly.DropDownDiv.content_.innerHTML = ''; - Blockly.DropDownDiv.content_.style.width = ''; -}; - -/** - * Set the colour for the drop-down. - * @param {string} backgroundColour Any CSS color for the background - * @param {string} borderColour Any CSS color for the border - */ -Blockly.DropDownDiv.setColour = function(backgroundColour, borderColour) { - Blockly.DropDownDiv.DIV_.style.backgroundColor = backgroundColour; - Blockly.DropDownDiv.DIV_.style.borderColor = borderColour; -}; - -/** - * Set the category for the drop-down. - * @param {string} category The new category for the drop-down. - */ -Blockly.DropDownDiv.setCategory = function(category) { - Blockly.DropDownDiv.DIV_.setAttribute('data-category', category); -}; - -/** - * Shortcut to show and place the drop-down with positioning determined - * by a particular block. The primary position will be below the block, - * and the secondary position above the block. Drop-down will be - * constrained to the block's workspace. - * @param {Object} owner The object showing the drop-down - * @param {!Blockly.Block} block Block to position the drop-down around. - * @param {Function=} opt_onHide Optional callback for when the drop-down is hidden. - * @param {Number} opt_secondaryYOffset Optional Y offset for above-block positioning. - * @return {boolean} True if the menu rendered below block; false if above. - */ -Blockly.DropDownDiv.showPositionedByBlock = function(owner, block, - opt_onHide, opt_secondaryYOffset) { - var scale = block.workspace.scale; - var bBox = {width: block.width, height: block.height}; - bBox.width *= scale; - bBox.height *= scale; - var position = block.getSvgRoot().getBoundingClientRect(); - // If we can fit it, render below the block. - var primaryX = position.left + bBox.width / 2; - var primaryY = position.top + bBox.height; - // If we can't fit it, render above the entire parent block. - var secondaryX = primaryX; - var secondaryY = position.top; - if (opt_secondaryYOffset) { - secondaryY += opt_secondaryYOffset; - } - // Set bounds to workspace; show the drop-down. - Blockly.DropDownDiv.setBoundsElement(block.workspace.getParentSvg().parentNode); - return Blockly.DropDownDiv.show(this, primaryX, primaryY, secondaryX, secondaryY, opt_onHide); -}; - -/** - * Show and place the drop-down. - * The drop-down is placed with an absolute "origin point" (x, y) - i.e., - * the arrow will point at this origin and box will positioned below or above it. - * If we can maintain the container bounds at the primary point, the arrow will - * point there, and the container will be positioned below it. - * If we can't maintain the container bounds at the primary point, fall-back to the - * secondary point and position above. - * @param {Object} owner The object showing the drop-down - * @param {number} primaryX Desired origin point x, in absolute px - * @param {number} primaryY Desired origin point y, in absolute px - * @param {number} secondaryX Secondary/alternative origin point x, in absolute px - * @param {number} secondaryY Secondary/alternative origin point y, in absolute px - * @param {Function=} opt_onHide Optional callback for when the drop-down is hidden - * @return {boolean} True if the menu rendered at the primary origin point. - */ -Blockly.DropDownDiv.show = function(owner, primaryX, primaryY, secondaryX, secondaryY, opt_onHide) { - Blockly.DropDownDiv.owner_ = owner; - Blockly.DropDownDiv.onHide_ = opt_onHide; - var div = Blockly.DropDownDiv.DIV_; - var metrics = Blockly.DropDownDiv.getPositionMetrics(primaryX, primaryY, secondaryX, secondaryY); - // Update arrow CSS - Blockly.DropDownDiv.arrow_.style.transform = 'translate(' + - metrics.arrowX + 'px,' + metrics.arrowY + 'px) rotate(45deg)'; - Blockly.DropDownDiv.arrow_.setAttribute('class', - metrics.arrowAtTop ? 'blocklyDropDownArrow arrowTop' : 'blocklyDropDownArrow arrowBottom'); - // Set direction based on owner's rtl - div.style.direction = owner.sourceBlock_ && owner.sourceBlock_.RTL ? 'rtl' : 'ltr'; - - // When we change `translate` multiple times in close succession, - // Chrome may choose to wait and apply them all at once. - // Since we want the translation to initial X, Y to be immediate, - // and the translation to final X, Y to be animated, - // we saw problems where both would be applied after animation was turned on, - // making the dropdown appear to fly in from (0, 0). - // Using both `left`, `top` for the initial translation and then `translate` - // for the animated transition to final X, Y is a workaround. - - // First apply initial translation. - div.style.left = metrics.initialX + 'px'; - div.style.top = metrics.initialY + 'px'; - // Show the div. - div.style.display = 'block'; - div.style.opacity = 1; - // Add final translate, animated through `transition`. - // Coordinates are relative to (initialX, initialY), - // where the drop-down is absolutely positioned. - var dx = (metrics.finalX - metrics.initialX); - var dy = (metrics.finalY - metrics.initialY); - div.style.transform = 'translate(' + dx + 'px,' + dy + 'px)'; - return metrics.arrowAtTop; -}; - -/** - * Helper to position the drop-down and the arrow, maintaining bounds. - * See explanation of origin points in Blockly.DropDownDiv.show. - * @param {number} primaryX Desired origin point x, in absolute px - * @param {number} primaryY Desired origin point y, in absolute px - * @param {number} secondaryX Secondary/alternative origin point x, in absolute px - * @param {number} secondaryY Secondary/alternative origin point y, in absolute px - * @returns {Object} Various final metrics, including rendered positions for drop-down and arrow. - */ -Blockly.DropDownDiv.getPositionMetrics = function(primaryX, primaryY, secondaryX, secondaryY) { - var div = Blockly.DropDownDiv.DIV_; - var boundPosition = Blockly.DropDownDiv.boundsElement_.getBoundingClientRect(); - - var boundSize = goog.style.getSize(Blockly.DropDownDiv.boundsElement_); - var divSize = goog.style.getSize(div); - - // First decide if we will render at primary or secondary position - // i.e., above or below - // renderX, renderY will eventually be the final rendered position of the box. - var renderX, renderY, renderedSecondary; - // Can the div fit inside the bounds if we render below the primary point? - if (primaryY + divSize.height > boundPosition.top + boundSize.height) { - // We can't fit below in terms of y. Can we fit above? - if (secondaryY - divSize.height < boundPosition.top) { - // We also can't fit above, so just render below anyway. - renderX = primaryX; - renderY = primaryY + Blockly.DropDownDiv.PADDING_Y; - renderedSecondary = false; - } else { - // We can fit above, render secondary - renderX = secondaryX; - renderY = secondaryY - divSize.height - Blockly.DropDownDiv.PADDING_Y; - renderedSecondary = true; - } - } else { - // We can fit below, render primary - renderX = primaryX; - renderY = primaryY + Blockly.DropDownDiv.PADDING_Y; - renderedSecondary = false; - } - // First calculate the absolute arrow X - // This needs to be done before positioning the div, since the arrow - // wants to be as close to the origin point as possible. - var arrowX = renderX - Blockly.DropDownDiv.ARROW_SIZE / 2; - // Keep in overall bounds - arrowX = Math.max(boundPosition.left, Math.min(arrowX, boundPosition.left + boundSize.width)); - - // Adjust the x-position of the drop-down so that the div is centered and within bounds. - var centerX = divSize.width / 2; - renderX -= centerX; - // Fit horizontally in the bounds. - renderX = Math.max( - boundPosition.left, - Math.min(renderX, boundPosition.left + boundSize.width - divSize.width) - ); - // After we've finished caclulating renderX, adjust the arrow to be relative to it. - arrowX -= renderX; - - // Pad the arrow by some pixels, primarily so that it doesn't render on top of a rounded border. - arrowX = Math.max( - Blockly.DropDownDiv.ARROW_HORIZONTAL_PADDING, - Math.min(arrowX, divSize.width - Blockly.DropDownDiv.ARROW_HORIZONTAL_PADDING - Blockly.DropDownDiv.ARROW_SIZE) - ); - - // Calculate arrow Y. If we rendered secondary, add on bottom. - // Extra pixels are added so that it covers the border of the div. - var arrowY = (renderedSecondary) ? divSize.height - Blockly.DropDownDiv.BORDER_SIZE : 0; - arrowY -= (Blockly.DropDownDiv.ARROW_SIZE / 2) + Blockly.DropDownDiv.BORDER_SIZE; - - // Initial position calculated without any padding to provide an animation point. - var initialX = renderX; // X position remains constant during animation. - var initialY; - if (renderedSecondary) { - initialY = secondaryY - divSize.height; // No padding on Y - } else { - initialY = primaryY; // No padding on Y - } - - return { - initialX: initialX, - initialY : initialY, - finalX: renderX, - finalY: renderY, - arrowX: arrowX, - arrowY: arrowY, - arrowAtTop: !renderedSecondary - }; -}; - -/** - * Is the container visible? - * @return {boolean} True if visible. - */ -Blockly.DropDownDiv.isVisible = function() { - return !!Blockly.DropDownDiv.owner_; -}; - -/** - * Hide the menu only if it is owned by the provided object. - * @param {Object} owner Object which must be owning the drop-down to hide - * @return {Boolean} True if hidden - */ -Blockly.DropDownDiv.hideIfOwner = function(owner) { - if (Blockly.DropDownDiv.owner_ === owner) { - Blockly.DropDownDiv.hide(); - return true; - } - return false; -}; - -/** - * Hide the menu, triggering animation. - */ -Blockly.DropDownDiv.hide = function() { - // Start the animation by setting the translation and fading out. - var div = Blockly.DropDownDiv.DIV_; - // Reset to (initialX, initialY) - i.e., no translation. - div.style.transform = 'translate(0px, 0px)'; - div.style.opacity = 0; - Blockly.DropDownDiv.animateOutTimer_ = setTimeout(function() { - // Finish animation - reset all values to default. - Blockly.DropDownDiv.hideWithoutAnimation(); - }, Blockly.DropDownDiv.ANIMATION_TIME * 1000); - if (Blockly.DropDownDiv.onHide_) { - Blockly.DropDownDiv.onHide_(); - Blockly.DropDownDiv.onHide_ = null; - } -}; - -/** - * Hide the menu, without animation. - */ -Blockly.DropDownDiv.hideWithoutAnimation = function() { - if (!Blockly.DropDownDiv.isVisible()) { - return; - } - var div = Blockly.DropDownDiv.DIV_; - Blockly.DropDownDiv.animateOutTimer_ && window.clearTimeout(Blockly.DropDownDiv.animateOutTimer_); - div.style.transform = ''; - div.style.top = ''; - div.style.left = ''; - div.style.display = 'none'; - Blockly.DropDownDiv.clearContent(); - Blockly.DropDownDiv.owner_ = null; - if (Blockly.DropDownDiv.onHide_) { - Blockly.DropDownDiv.onHide_(); - Blockly.DropDownDiv.onHide_ = null; - } -}; diff --git a/core/events.js b/core/events.js deleted file mode 100644 index 9dc046ba0e..0000000000 --- a/core/events.js +++ /dev/null @@ -1,429 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Events fired as a result of actions in Blockly's editor. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -/** - * Events fired as a result of actions in Blockly's editor. - * @namespace Blockly.Events - */ -goog.provide('Blockly.Events'); - -goog.require('goog.array'); -goog.require('goog.math.Coordinate'); - - -/** - * Group ID for new events. Grouped events are indivisible. - * @type {string} - * @private - */ -Blockly.Events.group_ = ''; - -/** - * Sets whether events should be added to the undo stack. - * @type {boolean} - */ -Blockly.Events.recordUndo = true; - -/** - * Allow change events to be created and fired. - * @type {number} - * @private - */ -Blockly.Events.disabled_ = 0; - -/** - * Name of event that creates a block. Will be deprecated for BLOCK_CREATE. - * @const - */ -Blockly.Events.CREATE = 'create'; - -/** - * Name of event that creates a block. - * @const - */ -Blockly.Events.BLOCK_CREATE = Blockly.Events.CREATE; - -/** - * Name of event that deletes a block. Will be deprecated for BLOCK_DELETE. - * @const - */ -Blockly.Events.DELETE = 'delete'; - -/** - * Name of event that deletes a block. - * @const - */ -Blockly.Events.BLOCK_DELETE = Blockly.Events.DELETE; - -/** - * Name of event that changes a block. Will be deprecated for BLOCK_CHANGE. - * @const - */ -Blockly.Events.CHANGE = 'change'; - -/** - * Name of event that changes a block. - * @const - */ -Blockly.Events.BLOCK_CHANGE = Blockly.Events.CHANGE; - -/** - * Name of event that moves a block. Will be deprecated for BLOCK_MOVE. - * @const - */ -Blockly.Events.MOVE = 'move'; - -/** - * Name of event that drags a block outside of or into the blocks workspace - * @const - */ -Blockly.Events.DRAG_OUTSIDE = 'dragOutside'; - -/** - * Name of event that ends a block drag - * @const - */ -Blockly.Events.END_DRAG = 'endDrag'; - -/** - * Name of event that moves a block. - * @const - */ -Blockly.Events.BLOCK_MOVE = Blockly.Events.MOVE; - -/** - * Name of event that creates a variable. - * @const - */ -Blockly.Events.VAR_CREATE = 'var_create'; - -/** - * Name of event that deletes a variable. - * @const - */ -Blockly.Events.VAR_DELETE = 'var_delete'; - -/** - * Name of event that renames a variable. - * @const - */ -Blockly.Events.VAR_RENAME = 'var_rename'; - -/** - * Name of event that creates a comment. - * @const - */ -Blockly.Events.COMMENT_CREATE = 'comment_create'; - -/** - * Name of event that moves a comment. - * @const - */ -Blockly.Events.COMMENT_MOVE = 'comment_move'; - -/** - * Name of event that changes a comment's property - * (text content, size, or minimized state). - * @const - */ -Blockly.Events.COMMENT_CHANGE = 'comment_change'; - -/** - * Name of event that deletes a comment. - * @const - */ -Blockly.Events.COMMENT_DELETE = 'comment_delete'; - -/** - * Name of event that records a UI change. - * @const - */ -Blockly.Events.UI = 'ui'; - -/** - * List of events queued for firing. - * @private - */ -Blockly.Events.FIRE_QUEUE_ = []; - -/** - * Create a custom event and fire it. - * @param {!Blockly.Events.Abstract} event Custom data for event. - */ -Blockly.Events.fire = function(event) { - if (!Blockly.Events.isEnabled()) { - return; - } - if (!Blockly.Events.FIRE_QUEUE_.length) { - // First event added; schedule a firing of the event queue. - setTimeout(Blockly.Events.fireNow_, 0); - } - Blockly.Events.FIRE_QUEUE_.push(event); -}; - -/** - * Fire all queued events. - * @private - */ -Blockly.Events.fireNow_ = function() { - var queue = Blockly.Events.filter(Blockly.Events.FIRE_QUEUE_, true); - Blockly.Events.FIRE_QUEUE_.length = 0; - for (var i = 0, event; event = queue[i]; i++) { - var workspace = Blockly.Workspace.getById(event.workspaceId); - if (workspace) { - workspace.fireChangeListener(event); - } - } -}; - -/** - * Filter the queued events and merge duplicates. - * @param {!Array.} queueIn Array of events. - * @param {boolean} forward True if forward (redo), false if backward (undo). - * @return {!Array.} Array of filtered events. - */ -Blockly.Events.filter = function(queueIn, forward) { - var queue = goog.array.clone(queueIn); - if (!forward) { - // Undo is merged in reverse order. - queue.reverse(); - } - var mergedQueue = []; - var hash = Object.create(null); - // Merge duplicates. - for (var i = 0, event; event = queue[i]; i++) { - if (!event.isNull()) { - var key = [event.type, event.blockId, event.workspaceId].join(' '); - - var lastEntry = hash[key]; - var lastEvent = lastEntry ? lastEntry.event : null; - if (!lastEntry) { - // Each item in the hash table has the event and the index of that event - // in the input array. This lets us make sure we only merge adjacent - // move events. - hash[key] = {event: event, index: i}; - mergedQueue.push(event); - } else if (event.type == Blockly.Events.MOVE && - lastEntry.index == i - 1) { - // Merge move events. - lastEvent.newParentId = event.newParentId; - lastEvent.newInputName = event.newInputName; - lastEvent.newCoordinate = event.newCoordinate; - lastEntry.index = i; - } else if (event.type == Blockly.Events.CHANGE && - event.element == lastEvent.element && - event.name == lastEvent.name) { - // Merge change events. - lastEvent.newValue = event.newValue; - } else if (event.type == Blockly.Events.UI && - event.element == 'click' && - (lastEvent.element == 'commentOpen' || - lastEvent.element == 'mutatorOpen' || - lastEvent.element == 'warningOpen')) { - // Merge click events. - lastEvent.newValue = event.newValue; - } else { - // Collision: newer events should merge into this event to maintain order - hash[key] = {event: event, index: 1}; - mergedQueue.push(event); - } - } - } - // Filter out any events that have become null due to merging. - queue = mergedQueue.filter(function(e) { return !e.isNull(); }); - if (!forward) { - // Restore undo order. - queue.reverse(); - } - // Move mutation events to the top of the queue. - // Intentionally skip first event. - for (var i = 1, event; event = queue[i]; i++) { - if (event.type == Blockly.Events.CHANGE && - event.element == 'mutation') { - queue.unshift(queue.splice(i, 1)[0]); - } - } - return queue; -}; - -/** - * Modify pending undo events so that when they are fired they don't land - * in the undo stack. Called by Blockly.Workspace.clearUndo. - */ -Blockly.Events.clearPendingUndo = function() { - for (var i = 0, event; event = Blockly.Events.FIRE_QUEUE_[i]; i++) { - event.recordUndo = false; - } -}; - -/** - * Stop sending events. Every call to this function MUST also call enable. - */ -Blockly.Events.disable = function() { - Blockly.Events.disabled_++; -}; - -/** - * Start sending events. Unless events were already disabled when the - * corresponding call to disable was made. - */ -Blockly.Events.enable = function() { - Blockly.Events.disabled_--; -}; - -/** - * Returns whether events may be fired or not. - * @return {boolean} True if enabled. - */ -Blockly.Events.isEnabled = function() { - return Blockly.Events.disabled_ == 0; -}; - -/** - * Current group. - * @return {string} ID string. - */ -Blockly.Events.getGroup = function() { - return Blockly.Events.group_; -}; - -/** - * Start or stop a group. - * @param {boolean|string} state True to start new group, false to end group. - * String to set group explicitly. - */ -Blockly.Events.setGroup = function(state) { - if (typeof state == 'boolean') { - Blockly.Events.group_ = state ? Blockly.utils.genUid() : ''; - } else { - Blockly.Events.group_ = state; - } -}; - -/** - * Compute a list of the IDs of the specified block and all its descendants. - * @param {!Blockly.Block} block The root block. - * @return {!Array.} List of block IDs. - * @private - */ -Blockly.Events.getDescendantIds_ = function(block) { - var ids = []; - var descendants = block.getDescendants(false); - for (var i = 0, descendant; descendant = descendants[i]; i++) { - ids[i] = descendant.id; - } - return ids; -}; - -/** - * Decode the JSON into an event. - * @param {!Object} json JSON representation. - * @param {!Blockly.Workspace} workspace Target workspace for event. - * @return {!Blockly.Events.Abstract} The event represented by the JSON. - */ -Blockly.Events.fromJson = function(json, workspace) { - var event; - switch (json.type) { - case Blockly.Events.CREATE: - event = new Blockly.Events.Create(null); - break; - case Blockly.Events.DELETE: - event = new Blockly.Events.Delete(null); - break; - case Blockly.Events.CHANGE: - event = new Blockly.Events.Change(null); - break; - case Blockly.Events.MOVE: - event = new Blockly.Events.Move(null); - break; - case Blockly.Events.VAR_CREATE: - event = new Blockly.Events.VarCreate(null); - break; - case Blockly.Events.VAR_DELETE: - event = new Blockly.Events.VarDelete(null); - break; - case Blockly.Events.VAR_RENAME: - event = new Blockly.Events.VarRename(null); - break; - case Blockly.Events.COMMENT_CREATE: - event = new Blockly.Events.CommentCreate(null); - break; - case Blockly.Events.COMMENT_CHANGE: - event = new Blockly.Events.CommentChange(null); - break; - case Blockly.Events.COMMENT_MOVE: - event = new Blockly.Events.CommentMove(null); - break; - case Blockly.Events.COMMENT_DELETE: - event = new Blockly.Events.CommentDelete(null); - break; - case Blockly.Events.UI: - event = new Blockly.Events.Ui(null); - break; - case Blockly.Events.DRAG_OUTSIDE: - event = new Blockly.Events.DragBlockOutside(null); - break; - case Blockly.Events.END_DRAG: - event = new Blockly.Events.EndBlockDrag(null, false); - break; - default: - throw 'Unknown event type.'; - } - event.fromJson(json); - event.workspaceId = workspace.id; - return event; -}; - -/** - * Enable/disable a block depending on whether it is properly connected. - * Use this on applications where all blocks should be connected to a top block. - * Recommend setting the 'disable' option to 'false' in the config so that - * users don't try to reenable disabled orphan blocks. - * @param {!Blockly.Events.Abstract} event Custom data for event. - */ -Blockly.Events.disableOrphans = function(event) { - if (event.type == Blockly.Events.MOVE || - event.type == Blockly.Events.CREATE) { - Blockly.Events.disable(); - var workspace = Blockly.Workspace.getById(event.workspaceId); - var block = workspace.getBlockById(event.blockId); - if (block) { - if (block.getParent() && !block.getParent().disabled) { - var children = block.getDescendants(false); - for (var i = 0, child; child = children[i]; i++) { - child.setDisabled(false); - } - } else if ((block.outputConnection || block.previousConnection) && - !workspace.isDragging()) { - do { - block.setDisabled(true); - block = block.getNextBlock(); - } while (block); - } - } - Blockly.Events.enable(); - } -}; diff --git a/core/events_abstract.js b/core/events_abstract.js deleted file mode 100644 index 2af78c3fc5..0000000000 --- a/core/events_abstract.js +++ /dev/null @@ -1,113 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2018 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Abstract class for events fired as a result of actions in - * Blockly's editor. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.Events.Abstract'); - -goog.require('Blockly.Events'); -goog.require('goog.array'); -goog.require('goog.math.Coordinate'); - -/** - * Abstract class for an event. - * @constructor - */ -Blockly.Events.Abstract = function() { - /** - * The workspace identifier for this event. - * @type {string|undefined} - */ - this.workspaceId = undefined; - - /** - * 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} - */ - this.group = Blockly.Events.group_; - - /** - * Sets whether the event should be added to the undo stack. - * @type {boolean} - */ - this.recordUndo = Blockly.Events.recordUndo; -}; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.Abstract.prototype.toJson = function() { - var json = { - 'type': this.type - }; - if (this.group) { - json['group'] = this.group; - } - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.Abstract.prototype.fromJson = function(json) { - this.group = json['group']; -}; - -/** - * Does this event record any change of state? - * By default we assume events are non-null. Subclasses may override to - * indicate that they do not change state. - * @return {boolean} False if something changed. - */ -Blockly.Events.Abstract.prototype.isNull = function() { - return false; -}; - -/** - * Run an event. - * @param {boolean} _forward True if run forward, false if run backward (undo). - */ -Blockly.Events.Abstract.prototype.run = function(_forward) { - // Defined by subclasses. -}; - -/** - * Get workspace the event belongs to. - * @return {Blockly.Workspace} The workspace the event belongs to. - * @throws {Error} if workspace is null. - * @protected - */ -Blockly.Events.Abstract.prototype.getEventWorkspace_ = function() { - var workspace = Blockly.Workspace.getById(this.workspaceId); - if (!workspace) { - throw Error('Workspace is null. Event must have been generated from real' + - ' Blockly events.'); - } - return workspace; -}; diff --git a/core/extensions.js b/core/extensions.js deleted file mode 100644 index 1aabed1be0..0000000000 --- a/core/extensions.js +++ /dev/null @@ -1,450 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Extensions are functions that help initialize blocks, usually - * adding dynamic behavior such as onchange handlers and mutators. These - * are applied using Block.applyExtension(), or the JSON "extensions" - * array attribute. - * @author Anm@anm.me (Andrew n marshall) - */ -'use strict'; - -/** - * @name Blockly.Extensions - * @namespace - **/ -goog.provide('Blockly.Extensions'); - -goog.require('Blockly.Mutator'); -goog.require('Blockly.utils'); - -goog.require('goog.string'); - - -/** - * The set of all registered extensions, keyed by extension name/id. - * @private - */ -Blockly.Extensions.ALL_ = {}; - -/** - * Registers a new extension function. Extensions are functions that help - * initialize blocks, usually adding dynamic behavior such as onchange - * handlers and mutators. These are applied using Block.applyExtension(), or - * the JSON "extensions" array attribute. - * @param {string} name The name of this extension. - * @param {Function} initFn The function to initialize an extended block. - * @throws {Error} if the extension name is empty, the extension is already - * registered, or extensionFn is not a function. - */ -Blockly.Extensions.register = function(name, initFn) { - if (!goog.isString(name) || goog.string.isEmptyOrWhitespace(name)) { - throw new Error('Error: Invalid extension name "' + name + '"'); - } - if (Blockly.Extensions.ALL_[name]) { - throw new Error('Error: Extension "' + name + '" is already registered.'); - } - if (!goog.isFunction(initFn)) { - throw new Error('Error: Extension "' + name + '" must be a function'); - } - Blockly.Extensions.ALL_[name] = initFn; -}; - -/** - * Registers a new extension function that adds all key/value of mixinObj. - * @param {string} name The name of this extension. - * @param {!Object} mixinObj The values to mix in. - * @throws {Error} if the extension name is empty or the extension is already - * registered. - */ -Blockly.Extensions.registerMixin = function(name, mixinObj) { - if (!goog.isObject(mixinObj)){ - throw new Error('Error: Mixin "' + name + '" must be a object'); - } - Blockly.Extensions.register(name, function() { - this.mixin(mixinObj); - }); -}; - -/** - * Registers a new extension function that adds a mutator to the block. - * At register time this performs some basic sanity checks on the mutator. - * The wrapper may also add a mutator dialog to the block, if both compose and - * decompose are defined on the mixin. - * @param {string} name The name of this mutator extension. - * @param {!Object} mixinObj The values to mix in. - * @param {(function())=} opt_helperFn An optional function to apply after - * mixing in the object. - * @param {Array.=} opt_blockList A list of blocks to appear in the - * flyout of the mutator dialog. - * @throws {Error} if the mutation is invalid or can't be applied to the block. - */ -Blockly.Extensions.registerMutator = function(name, mixinObj, opt_helperFn, - opt_blockList) { - var errorPrefix = 'Error when registering mutator "' + name + '": '; - - // Sanity check the mixin object before registering it. - Blockly.Extensions.checkHasFunction_( - errorPrefix, mixinObj.domToMutation, 'domToMutation'); - Blockly.Extensions.checkHasFunction_( - errorPrefix, mixinObj.mutationToDom, 'mutationToDom'); - - var hasMutatorDialog = - Blockly.Extensions.checkMutatorDialog_(mixinObj, errorPrefix); - - if (opt_helperFn && !goog.isFunction(opt_helperFn)) { - throw new Error('Extension "' + name + '" is not a function'); - } - - // Sanity checks passed. - Blockly.Extensions.register(name, function() { - if (hasMutatorDialog) { - this.setMutator(new Blockly.Mutator(opt_blockList)); - } - // Mixin the object. - this.mixin(mixinObj); - - if (opt_helperFn) { - opt_helperFn.apply(this); - } - }); -}; - -/** - * Applies an extension method to a block. This should only be called during - * block construction. - * @param {string} name The name of the extension. - * @param {!Blockly.Block} block The block to apply the named extension to. - * @param {boolean} isMutator True if this extension defines a mutator. - * @throws {Error} if the extension is not found. - */ -Blockly.Extensions.apply = function(name, block, isMutator) { - var extensionFn = Blockly.Extensions.ALL_[name]; - if (!goog.isFunction(extensionFn)) { - throw new Error('Error: Extension "' + name + '" not found.'); - } - if (isMutator) { - // Fail early if the block already has mutation properties. - Blockly.Extensions.checkNoMutatorProperties_(name, block); - } else { - // Record the old properties so we can make sure they don't change after - // applying the extension. - var mutatorProperties = Blockly.Extensions.getMutatorProperties_(block); - } - extensionFn.apply(block); - - if (isMutator) { - var errorPrefix = 'Error after applying mutator "' + name + '": '; - Blockly.Extensions.checkBlockHasMutatorProperties_(errorPrefix, block); - } else { - if (!Blockly.Extensions.mutatorPropertiesMatch_(mutatorProperties, block)) { - throw new Error('Error when applying extension "' + name + '": ' + - 'mutation properties changed when applying a non-mutator extension.'); - } - } -}; - -/** - * Check that the given value is a function. - * @param {string} errorPrefix The string to prepend to any error message. - * @param {*} func Function to check. - * @param {string} propertyName Which property to check. - * @throws {Error} if the property does not exist or is not a function. - * @private - */ -Blockly.Extensions.checkHasFunction_ = function(errorPrefix, func, - propertyName) { - if (!func) { - throw new Error(errorPrefix + - 'missing required property "' + propertyName + '"'); - } else if (typeof func != 'function') { - throw new Error(errorPrefix + - '" required property "' + propertyName + '" must be a function'); - } -}; - -/** - * Check that the given block does not have any of the four mutator properties - * defined on it. This function should be called before applying a mutator - * extension to a block, to make sure we are not overwriting properties. - * @param {string} mutationName The name of the mutation to reference in error - * messages. - * @param {!Blockly.Block} block The block to check. - * @throws {Error} if any of the properties already exist on the block. - * @private - */ -Blockly.Extensions.checkNoMutatorProperties_ = function(mutationName, block) { - var properties = Blockly.Extensions.getMutatorProperties_(block); - if (properties.length) { - throw new Error('Error: tried to apply mutation "' + mutationName + - '" to a block that already has mutator functions.' + - ' Block id: ' + block.id); - } -}; - -/** - * Check that the given object has both or neither of the functions required - * to have a mutator dialog. - * These functions are 'compose' and 'decompose'. If a block has one, it must - * have both. - * @param {!Object} object The object to check. - * @param {string} errorPrefix The string to prepend to any error message. - * @return {boolean} True if the object has both functions. False if it has - * neither function. - * @throws {Error} if the object has only one of the functions. - * @private - */ -Blockly.Extensions.checkMutatorDialog_ = function(object, errorPrefix) { - var hasCompose = object.compose !== undefined; - var hasDecompose = object.decompose !== undefined; - - if (hasCompose && hasDecompose) { - if (typeof object.compose != 'function') { - throw new Error(errorPrefix + 'compose must be a function.'); - } else if (typeof object.decompose != 'function') { - throw new Error(errorPrefix + 'decompose must be a function.'); - } - return true; - } else if (!hasCompose && !hasDecompose) { - return false; - } else { - throw new Error(errorPrefix + - 'Must have both or neither of "compose" and "decompose"'); - } -}; - -/** - * Check that a block has required mutator properties. This should be called - * after applying a mutation extension. - * @param {string} errorPrefix The string to prepend to any error message. - * @param {!Blockly.Block} block The block to inspect. - * @private - */ -Blockly.Extensions.checkBlockHasMutatorProperties_ = function(errorPrefix, - block) { - if (typeof block.domToMutation !== 'function') { - throw new Error(errorPrefix + 'Applying a mutator didn\'t add "domToMutation"'); - } - if (typeof block.mutationToDom != 'function') { - throw new Error(errorPrefix + - 'Applying a mutator didn\'t add "mutationToDom"'); - } - - // A block with a mutator isn't required to have a mutation dialog, but - // it should still have both or neither of compose and decompose. - Blockly.Extensions.checkMutatorDialog_(block, errorPrefix); -}; - -/** - * Get a list of values of mutator properties on the given block. - * @param {!Blockly.Block} block The block to inspect. - * @return {!Array.} a list with all of the defined properties, which - * should be functions, but may be anything other than undefined. - * @private - */ -Blockly.Extensions.getMutatorProperties_ = function(block) { - var result = []; - // List each function explicitly by reference to allow for renaming - // during compilation. - if (block.domToMutation !== undefined) { - result.push(block.domToMutation); - } - if (block.mutationToDom !== undefined) { - result.push(block.mutationToDom); - } - if (block.compose !== undefined) { - result.push(block.compose); - } - if (block.decompose !== undefined) { - result.push(block.decompose); - } - return result; -}; - -/** - * Check that the current mutator properties match a list of old mutator - * properties. This should be called after applying a non-mutator extension, - * to verify that the extension didn't change properties it shouldn't. - * @param {!Array.} oldProperties The old values to compare to. - * @param {!Blockly.Block} block The block to inspect for new values. - * @return {boolean} True if the property lists match. - * @private - */ -Blockly.Extensions.mutatorPropertiesMatch_ = function(oldProperties, block) { - var newProperties = Blockly.Extensions.getMutatorProperties_(block); - if (newProperties.length != oldProperties.length) { - return false; - } - for (var i = 0; i < newProperties.length; i++) { - if (oldProperties[i] != newProperties[i]) { - return false; - } - } - return true; -}; - -/** - * Builds an extension function that will map a dropdown value to a tooltip - * string. - * - * This method includes multiple checks to ensure tooltips, dropdown options, - * and message references are aligned. This aims to catch errors as early as - * possible, without requiring developers to manually test tooltips under each - * option. After the page is loaded, each tooltip text string will be checked - * for matching message keys in the internationalized string table. Deferring - * this until the page is loaded decouples loading dependencies. Later, upon - * loading the first block of any given type, the extension will validate every - * dropdown option has a matching tooltip in the lookupTable. Errors are - * reported as warnings in the console, and are never fatal. - * @param {string} dropdownName The name of the field whose value is the key - * to the lookup table. - * @param {!Object.} lookupTable The table of field values to - * tooltip text. - * @return {Function} The extension function. - */ -Blockly.Extensions.buildTooltipForDropdown = function(dropdownName, - lookupTable) { - // List of block types already validated, to minimize duplicate warnings. - var blockTypesChecked = []; - - // Check the tooltip string messages for invalid references. - // Wait for load, in case Blockly.Msg is not yet populated. - // runAfterPageLoad() does not run in a Node.js environment due to lack of - // document object, in which case skip the validation. - if (document) { // Relies on document.readyState - Blockly.utils.runAfterPageLoad(function() { - for (var key in lookupTable) { - // Will print warnings is reference is missing. - Blockly.utils.checkMessageReferences(lookupTable[key]); - } - }); - } - - /** - * The actual extension. - * @this {Blockly.Block} - */ - var extensionFn = function() { - if (this.type && blockTypesChecked.indexOf(this.type) === -1) { - Blockly.Extensions.checkDropdownOptionsInTable_( - this, dropdownName, lookupTable); - blockTypesChecked.push(this.type); - } - - this.setTooltip(function() { - var value = this.getFieldValue(dropdownName); - var tooltip = lookupTable[value]; - if (tooltip == null) { - if (blockTypesChecked.indexOf(this.type) === -1) { - // Warn for missing values on generated tooltips. - var warning = 'No tooltip mapping for value ' + value + - ' of field ' + dropdownName; - if (this.type != null) { - warning += (' of block type ' + this.type); - } - console.warn(warning + '.'); - } - } else { - tooltip = Blockly.utils.replaceMessageReferences(tooltip); - } - return tooltip; - }.bind(this)); - }; - return extensionFn; -}; - -/** - * Checks all options keys are present in the provided string lookup table. - * Emits console warnings when they are not. - * @param {!Blockly.Block} block The block containing the dropdown - * @param {string} dropdownName The name of the dropdown - * @param {!Object.} lookupTable The string lookup table - * @private - */ -Blockly.Extensions.checkDropdownOptionsInTable_ = function(block, dropdownName, - lookupTable) { - // Validate all dropdown options have values. - var dropdown = block.getField(dropdownName); - if (!dropdown.isOptionListDynamic()) { - var options = dropdown.getOptions(); - for (var i = 0; i < options.length; ++i) { - var optionKey = options[i][1]; // label, then value - if (lookupTable[optionKey] == null) { - console.warn('No tooltip mapping for value ' + optionKey + - ' of field ' + dropdownName + ' of block type ' + block.type); - } - } - } -}; - -/** - * Builds an extension function that will install a dynamic tooltip. The - * tooltip message should include the string '%1' and that string will be - * replaced with the value of the named field. - * @param {string} msgTemplate The template form to of the message text, with - * %1 placeholder. - * @param {string} fieldName The field with the replacement value. - * @returns {Function} The extension function. - */ -Blockly.Extensions.buildTooltipWithFieldValue = - function(msgTemplate, fieldName) { - // Check the tooltip string messages for invalid references. - // Wait for load, in case Blockly.Msg is not yet populated. - // runAfterPageLoad() does not run in a Node.js environment due to lack of - // document object, in which case skip the validation. - if (document) { // Relies on document.readyState - Blockly.utils.runAfterPageLoad(function() { - // Will print warnings is reference is missing. - Blockly.utils.checkMessageReferences(msgTemplate); - }); - } - - /** - * The actual extension. - * @this {Blockly.Block} - */ - var extensionFn = function() { - this.setTooltip(function() { - return Blockly.utils.replaceMessageReferences(msgTemplate) - .replace('%1', this.getFieldValue(fieldName)); - }.bind(this)); - }; - return extensionFn; - }; - -/** - * Configures the tooltip to mimic the parent block when connected. Otherwise, - * uses the tooltip text at the time this extension is initialized. This takes - * advantage of the fact that all other values from JSON are initialized before - * extensions. - * @this {Blockly.Block} - * @private - */ -Blockly.Extensions.extensionParentTooltip_ = function() { - this.tooltipWhenNotConnected_ = this.tooltip; - this.setTooltip(function() { - var parent = this.getParent(); - return (parent && parent.getInputsInline() && parent.tooltip) || - this.tooltipWhenNotConnected_; - }.bind(this)); -}; -Blockly.Extensions.register('parent_tooltip_when_inline', - Blockly.Extensions.extensionParentTooltip_); diff --git a/core/field.js b/core/field.js deleted file mode 100644 index 692caa96a2..0000000000 --- a/core/field.js +++ /dev/null @@ -1,807 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Field. Used for editable titles, variables, etc. - * This is an abstract class that defines the UI on the block. Actual - * instances would be Blockly.FieldTextInput, Blockly.FieldDropdown, etc. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.Field'); - -goog.require('Blockly.Events.BlockChange'); -goog.require('Blockly.Gesture'); - -goog.require('goog.asserts'); -goog.require('goog.dom'); -goog.require('goog.math.Size'); -goog.require('goog.style'); -goog.require('goog.userAgent'); - - -/** - * Abstract class for an editable field. - * @param {string} text The initial content of the field. - * @param {Function=} opt_validator An optional function that is called - * to validate any constraints on what the user entered. Takes the new - * text as an argument and returns either the accepted text, a replacement - * text, or null to abort the change. - * @constructor - */ -Blockly.Field = function(text, opt_validator) { - this.size_ = new goog.math.Size( - Blockly.BlockSvg.FIELD_WIDTH, - Blockly.BlockSvg.FIELD_HEIGHT); - this.setValue(text); - this.setValidator(opt_validator); - - /** - * Maximum characters of text to display before adding an ellipsis. - * Same for strings and numbers. - * @type {number} - */ - this.maxDisplayLength = Blockly.BlockSvg.MAX_DISPLAY_LENGTH; -}; - - -/** - * The set of all registered fields, keyed by field type as used in the JSON - * definition of a block. - * @type {!Object} - * @private - */ -Blockly.Field.TYPE_MAP_ = {}; - -/** - * Registers a field type. May also override an existing field type. - * Blockly.Field.fromJson uses this registry to find the appropriate field. - * @param {!string} type The field type name as used in the JSON definition. - * @param {!{fromJson: Function}} fieldClass The field class containing a - * fromJson function that can construct an instance of the field. - * @throws {Error} if the type name is empty, or the fieldClass is not an - * object containing a fromJson function. - */ -Blockly.Field.register = function(type, fieldClass) { - if (!goog.isString(type) || goog.string.isEmptyOrWhitespace(type)) { - throw new Error('Invalid field type "' + type + '"'); - } - if (!goog.isObject(fieldClass) || !goog.isFunction(fieldClass.fromJson)) { - throw new Error('Field "' + fieldClass + - '" must have a fromJson function'); - } - Blockly.Field.TYPE_MAP_[type] = fieldClass; -}; - -/** - * Construct a Field from a JSON arg object. - * Finds the appropriate registered field by the type name as registered using - * Blockly.Field.register. - * @param {!Object} options A JSON object with a type and options specific - * to the field type. - * @returns {?Blockly.Field} The new field instance or null if a field wasn't - * found with the given type name - * @package - */ -Blockly.Field.fromJson = function(options) { - var fieldClass = Blockly.Field.TYPE_MAP_[options['type']]; - if (fieldClass) { - return fieldClass.fromJson(options); - } - return null; -}; - -/** - * Temporary cache of text widths. - * @type {Object} - * @private - */ -Blockly.Field.cacheWidths_ = null; - -/** - * Number of current references to cache. - * @type {number} - * @private - */ -Blockly.Field.cacheReference_ = 0; - - -/** - * Name of field. Unique within each block. - * Static labels are usually unnamed. - * @type {string|undefined} - */ -Blockly.Field.prototype.name = undefined; - -/** - * CSS class name for the text element. - * @type {string} - * @package - */ -Blockly.Field.prototype.className_ = 'blocklyText'; - -/** - * Visible text to display. - * @type {string} - * @private - */ -Blockly.Field.prototype.text_ = ''; - -/** - * Block this field is attached to. Starts as null, then in set in init. - * @type {Blockly.Block} - * @private - */ -Blockly.Field.prototype.sourceBlock_ = null; - -/** - * Is the field visible, or hidden due to the block being collapsed? - * @type {boolean} - * @private - */ -Blockly.Field.prototype.visible_ = true; - -/** - * Null, or an array of the field's argTypes (for styling). - * @type {Array} - * @private - */ -Blockly.Field.prototype.argType_ = null; - -/** - * Validation function called when user edits an editable field. - * @type {Function} - * @private - */ -Blockly.Field.prototype.validator_ = null; - -/** - * Whether to assume user is using a touch device for interactions. - * Used to show different UI for touch interactions, e.g. - * @type {boolean} - * @private - */ -Blockly.Field.prototype.useTouchInteraction_ = false; - -/** - * Non-breaking space. - * @const - */ -Blockly.Field.NBSP = '\u00A0'; - -/** - * Text offset used for IE/Edge. - * @const - */ -Blockly.Field.IE_TEXT_OFFSET = '0.3em'; - -/** - * Editable fields usually show some sort of UI for the user to change them. - * @type {boolean} - * @public - */ -Blockly.Field.prototype.EDITABLE = true; - -/** - * Serializable fields are saved by the XML renderer, non-serializable fields - * are not. Editable fields should be serialized. - * @type {boolean} - * @public - */ -Blockly.Field.prototype.SERIALIZABLE = true; - -/** - * Attach this field to a block. - * @param {!Blockly.Block} block The block containing this field. - */ -Blockly.Field.prototype.setSourceBlock = function(block) { - goog.asserts.assert(!this.sourceBlock_, 'Field already bound to a block.'); - this.sourceBlock_ = block; -}; - -/** - * Install this field on a block. - */ -Blockly.Field.prototype.init = function() { - if (this.fieldGroup_) { - // Field has already been initialized once. - return; - } - // Build the DOM. - this.fieldGroup_ = Blockly.utils.createSvgElement('g', {}, null); - if (!this.visible_) { - this.fieldGroup_.style.display = 'none'; - } - // Add an attribute to cassify the type of field. - if (this.getArgTypes() !== null) { - if (this.sourceBlock_.isShadow()) { - this.sourceBlock_.svgGroup_.setAttribute('data-argument-type', - this.getArgTypes()); - } else { - // Fields without a shadow wrapper, like square dropdowns. - this.fieldGroup_.setAttribute('data-argument-type', this.getArgTypes()); - } - } - // Adjust X to be flipped for RTL. Position is relative to horizontal start of source block. - var size = this.getSize(); - var fieldX = (this.sourceBlock_.RTL) ? -size.width / 2 : size.width / 2; - /** @type {!Element} */ - this.textElement_ = Blockly.utils.createSvgElement('text', - { - 'class': this.className_, - 'x': fieldX, - 'y': size.height / 2 + Blockly.BlockSvg.FIELD_TOP_PADDING, - 'dominant-baseline': 'middle', - 'dy': goog.userAgent.EDGE_OR_IE ? Blockly.Field.IE_TEXT_OFFSET : '0', - 'text-anchor': 'middle' - }, this.fieldGroup_); - - this.updateEditable(); - this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_); - // Force a render. - this.render_(); - this.size_.width = 0; - this.mouseDownWrapper_ = Blockly.bindEventWithChecks_( - this.getClickTarget_(), 'mousedown', this, this.onMouseDown_); -}; - -/** - * Initializes the model of the field after it has been installed on a block. - * No-op by default. - */ -Blockly.Field.prototype.initModel = function() { -}; - -/** - * Dispose of all DOM objects belonging to this editable field. - */ -Blockly.Field.prototype.dispose = function() { - if (this.mouseDownWrapper_) { - Blockly.unbindEvent_(this.mouseDownWrapper_); - this.mouseDownWrapper_ = null; - } - this.sourceBlock_ = null; - goog.dom.removeNode(this.fieldGroup_); - this.fieldGroup_ = null; - this.textElement_ = null; - this.validator_ = null; -}; - -/** - * Add or remove the UI indicating if this field is editable or not. - */ -Blockly.Field.prototype.updateEditable = function() { - var group = this.fieldGroup_; - if (!this.EDITABLE || !group) { - return; - } - if (this.sourceBlock_.isEditable()) { - Blockly.utils.addClass(group, 'blocklyEditableText'); - Blockly.utils.removeClass(group, 'blocklyNonEditableText'); - this.fieldGroup_.style.cursor = this.CURSOR; - } else { - Blockly.utils.addClass(group, 'blocklyNonEditableText'); - Blockly.utils.removeClass(group, 'blocklyEditableText'); - this.fieldGroup_.style.cursor = ''; - } -}; - -/** - * Check whether this field is currently editable. Some fields are never - * editable (e.g. text labels). Those fields are not serialized to XML. Other - * fields may be editable, and therefore serialized, but may exist on - * non-editable blocks. - * @return {boolean} whether this field is editable and on an editable block - */ -Blockly.Field.prototype.isCurrentlyEditable = function() { - return this.EDITABLE && !!this.sourceBlock_ && this.sourceBlock_.isEditable(); -}; - -/** - * Gets whether this editable field is visible or not. - * @return {boolean} True if visible. - */ -Blockly.Field.prototype.isVisible = function() { - return this.visible_; -}; - -/** - * Sets whether this editable field is visible or not. - * @param {boolean} visible True if visible. - */ -Blockly.Field.prototype.setVisible = function(visible) { - if (this.visible_ == visible) { - return; - } - this.visible_ = visible; - var root = this.getSvgRoot(); - if (root) { - root.style.display = visible ? 'block' : 'none'; - this.render_(); - } -}; - -/** - * Adds a string to the field's array of argTypes (used for styling). - * @param {string} argType New argType. - */ -Blockly.Field.prototype.addArgType = function(argType) { - if (this.argType_ == null) { - this.argType_ = []; - } - this.argType_.push(argType); -}; - -/** - * Gets the field's argTypes joined as a string, or returns null (used for styling). - * @return {string} argType string, or null. - */ -Blockly.Field.prototype.getArgTypes = function() { - if (this.argType_ === null || this.argType_.length === 0) { - return null; - } else { - return this.argType_.join(' '); - } -}; - -/** - * Sets a new validation function for editable fields. - * @param {Function} handler New validation function, or null. - */ -Blockly.Field.prototype.setValidator = function(handler) { - this.validator_ = handler; -}; - -/** - * Gets the validation function for editable fields. - * @return {Function} Validation function, or null. - */ -Blockly.Field.prototype.getValidator = function() { - return this.validator_; -}; - -/** - * Validates a change. Does nothing. Subclasses may override this. - * @param {string} text The user's text. - * @return {string} No change needed. - */ -Blockly.Field.prototype.classValidator = function(text) { - return text; -}; - -/** - * Calls the validation function for this field, as well as all the validation - * function for the field's class and its parents. - * @param {string} text Proposed text. - * @return {?string} Revised text, or null if invalid. - */ -Blockly.Field.prototype.callValidator = function(text) { - var classResult = this.classValidator(text); - if (classResult === null) { - // Class validator rejects value. Game over. - return null; - } else if (classResult !== undefined) { - text = classResult; - } - var userValidator = this.getValidator(); - if (userValidator) { - var userResult = userValidator.call(this, text); - if (userResult === null) { - // User validator rejects value. Game over. - return null; - } else if (userResult !== undefined) { - text = userResult; - } - } - return text; -}; - -/** - * Gets the group element for this editable field. - * Used for measuring the size and for positioning. - * @return {!Element} The group element. - */ -Blockly.Field.prototype.getSvgRoot = function() { - return /** @type {!Element} */ (this.fieldGroup_); -}; - -/** - * Draws the border with the correct width. - * Saves the computed width in a property. - * @private - */ -Blockly.Field.prototype.render_ = function() { - if (this.visible_ && this.textElement_) { - // Replace the text. - this.textElement_.textContent = this.getDisplayText_(); - this.updateWidth(); - - // Update text centering, based on newly calculated width. - var centerTextX = (this.size_.width - this.arrowWidth_) / 2; - if (this.sourceBlock_.RTL) { - centerTextX += this.arrowWidth_; - } - - // In a text-editing shadow block's field, - // if half the text length is not at least center of - // visible field (FIELD_WIDTH), center it there instead, - // unless there is a drop-down arrow. - if (this.sourceBlock_.isShadow() && !this.positionArrow) { - var minOffset = Blockly.BlockSvg.FIELD_WIDTH / 2; - if (this.sourceBlock_.RTL) { - // X position starts at the left edge of the block, in both RTL and LTR. - // First offset by the width of the block to move to the right edge, - // and then subtract to move to the same position as LTR. - var minCenter = this.size_.width - minOffset; - centerTextX = Math.min(minCenter, centerTextX); - } else { - // (width / 2) should exceed Blockly.BlockSvg.FIELD_WIDTH / 2 - // if the text is longer. - centerTextX = Math.max(minOffset, centerTextX); - } - } - - // Apply new text element x position. - this.textElement_.setAttribute('x', centerTextX); - } - - // Update any drawn box to the correct width and height. - if (this.box_) { - this.box_.setAttribute('width', this.size_.width); - this.box_.setAttribute('height', this.size_.height); - } -}; - -/** - * Updates the width of the field. This calls getCachedWidth which won't cache - * the approximated width on IE/Edge when `getComputedTextLength` fails. Once - * it eventually does succeed, the result will be cached. - **/ -Blockly.Field.prototype.updateWidth = function() { - // Calculate width of field - var width = Blockly.Field.getCachedWidth(this.textElement_); - - // Add padding to left and right of text. - if (this.EDITABLE) { - width += Blockly.BlockSvg.EDITABLE_FIELD_PADDING; - } - - // Adjust width for drop-down arrows. - this.arrowWidth_ = 0; - if (this.positionArrow) { - this.arrowWidth_ = this.positionArrow(width); - width += this.arrowWidth_; - } - - // Add padding to any drawn box. - if (this.box_) { - width += 2 * Blockly.BlockSvg.BOX_FIELD_PADDING; - } - - // Set width of the field. - this.size_.width = width; -}; - -/** - * Gets the width of a text element, caching it in the process. - * @param {!Element} textElement An SVG 'text' element. - * @return {number} Width of element. - */ -Blockly.Field.getCachedWidth = function(textElement) { - var key = textElement.textContent + '\n' + textElement.className.baseVal; - var width; - - // Return the cached width if it exists. - if (Blockly.Field.cacheWidths_) { - width = Blockly.Field.cacheWidths_[key]; - if (width) { - return width; - } - } - - // Attempt to compute fetch the width of the SVG text element. - try { - if (goog.userAgent.IE || goog.userAgent.EDGE) { - width = textElement.getBBox().width; - } else { - width = textElement.getComputedTextLength(); - } - } catch (e) { - // In other cases where we fail to geth the computed text. Instead, use an - // approximation and do not cache the result. At some later point in time - // when the block is inserted into the visible DOM, this method will be - // called again and, at that point in time, will not throw an exception. - return textElement.textContent.length * 8; - } - - // Cache the computed width and return. - if (Blockly.Field.cacheWidths_) { - Blockly.Field.cacheWidths_[key] = width; - } - return width; -}; - -/** - * Start caching field widths. Every call to this function MUST also call - * stopCache. Caches must not survive between execution threads. - */ -Blockly.Field.startCache = function() { - Blockly.Field.cacheReference_++; - if (!Blockly.Field.cacheWidths_) { - Blockly.Field.cacheWidths_ = {}; - } -}; - -/** - * Stop caching field widths. Unless caching was already on when the - * corresponding call to startCache was made. - */ -Blockly.Field.stopCache = function() { - Blockly.Field.cacheReference_--; - if (!Blockly.Field.cacheReference_) { - Blockly.Field.cacheWidths_ = null; - } -}; - -/** - * Returns the height and width of the field. - * @return {!goog.math.Size} Height and width. - */ -Blockly.Field.prototype.getSize = function() { - if (!this.size_.width) { - this.render_(); - } - return this.size_; -}; - -/** - * Returns the bounding box of the rendered field, accounting for workspace - * scaling. - * @return {!Object} An object with top, bottom, left, and right in pixels - * relative to the top left corner of the page (window coordinates). - * @private - */ -Blockly.Field.prototype.getScaledBBox_ = function() { - var size = this.getSize(); - var scaledHeight = size.height * this.sourceBlock_.workspace.scale; - var scaledWidth = size.width * this.sourceBlock_.workspace.scale; - var xy = this.getAbsoluteXY_(); - return { - top: xy.y, - bottom: xy.y + scaledHeight, - left: xy.x, - right: xy.x + scaledWidth - }; -}; - -/** - * Get the text from this field as displayed on screen. May differ from getText - * due to ellipsis, and other formatting. - * @return {string} Currently displayed text. - * @private - */ -Blockly.Field.prototype.getDisplayText_ = function() { - var text = this.text_; - if (!text) { - // Prevent the field from disappearing if empty. - return Blockly.Field.NBSP; - } - if (text.length > this.maxDisplayLength) { - // Truncate displayed string and add an ellipsis ('...'). - text = text.substring(0, this.maxDisplayLength - 2) + '\u2026'; - } - // Replace whitespace with non-breaking spaces so the text doesn't collapse. - text = text.replace(/\s/g, Blockly.Field.NBSP); - if (this.sourceBlock_.RTL) { - // The SVG is LTR, force text to be RTL unless a number. - if (this.sourceBlock_.editable_ && this.sourceBlock_.type === 'math_number') { - text = '\u202A' + text + '\u202C'; - } else { - text = '\u202B' + text + '\u202C'; - } - } - return text; -}; - -/** - * Get the text from this field. - * @return {string} Current text. - */ -Blockly.Field.prototype.getText = function() { - return this.text_; -}; - -/** - * Set the text in this field. Trigger a rerender of the source block. - * @param {*} newText New text. - */ -Blockly.Field.prototype.setText = function(newText) { - if (newText === null) { - // No change if null. - return; - } - newText = String(newText); - if (newText === this.text_) { - // No change. - return; - } - this.text_ = newText; - this.forceRerender(); -}; - -/** - * Force a rerender of the block that this field is installed on, which will - * rerender this field and adjust for any sizing changes. - * Other fields on the same block will not rerender, because their sizes have - * already been recorded. - * @package - */ -Blockly.Field.prototype.forceRerender = function() { - // Set width to 0 to force a rerender of this field. - this.size_.width = 0; - - if (this.sourceBlock_ && this.sourceBlock_.rendered) { - this.sourceBlock_.render(); - this.sourceBlock_.bumpNeighbours_(); - } -}; - -/** - * Update the text node of this field to display the current text. - * @private - */ -Blockly.Field.prototype.updateTextNode_ = function() { - if (!this.textElement_) { - // Not rendered yet. - return; - } - var text = this.text_; - if (text.length > this.maxDisplayLength) { - // Truncate displayed string and add an ellipsis ('...'). - text = text.substring(0, this.maxDisplayLength - 2) + '\u2026'; - // Add special class for sizing font when truncated - this.textElement_.setAttribute('class', this.className_ + ' blocklyTextTruncated'); - } else { - this.textElement_.setAttribute('class', this.className_); - } - // Empty the text element. - goog.dom.removeChildren(/** @type {!Element} */ (this.textElement_)); - // Replace whitespace with non-breaking spaces so the text doesn't collapse. - text = text.replace(/\s/g, Blockly.Field.NBSP); - if (this.sourceBlock_.RTL && text) { - // The SVG is LTR, force text to be RTL. - if (this.sourceBlock_.editable_ && this.sourceBlock_.type === 'math_number') { - text = '\u202A' + text + '\u202C'; - } else { - text = '\u202B' + text + '\u202C'; - } - } - if (!text) { - // Prevent the field from disappearing if empty. - text = Blockly.Field.NBSP; - } - var textNode = document.createTextNode(text); - this.textElement_.appendChild(textNode); - - // Cached width is obsolete. Clear it. - this.size_.width = 0; -}; - -/** - * By default there is no difference between the human-readable text and - * the language-neutral values. Subclasses (such as dropdown) may define this. - * @return {string} Current value. - */ -Blockly.Field.prototype.getValue = function() { - return this.getText(); -}; - -/** - * By default there is no difference between the human-readable text and - * the language-neutral values. Subclasses (such as dropdown) may define this. - * @param {string} newValue New value. - */ -Blockly.Field.prototype.setValue = function(newValue) { - if (newValue === null) { - // No change if null. - return; - } - var oldValue = this.getValue(); - if (oldValue == newValue) { - return; - } - if (this.sourceBlock_ && Blockly.Events.isEnabled()) { - Blockly.Events.fire(new Blockly.Events.BlockChange( - this.sourceBlock_, 'field', this.name, oldValue, newValue)); - } - this.setText(newValue); -}; - -/** - * Handle a mouse down event on a field. - * @param {!Event} e Mouse down event. - * @private - */ -Blockly.Field.prototype.onMouseDown_ = function(e) { - if (!this.sourceBlock_ || !this.sourceBlock_.workspace) { - return; - } - var gesture = this.sourceBlock_.workspace.getGesture(e); - if (gesture) { - gesture.setStartField(this); - } - this.useTouchInteraction_ = Blockly.Touch.getTouchIdentifierFromEvent(event) !== 'mouse'; -}; - -/** - * Change the tooltip text for this field. - * @param {string|!Element} _newTip Text for tooltip or a parent element to - * link to for its tooltip. - * @abstract - */ -Blockly.Field.prototype.setTooltip = function(_newTip) { - // Non-abstract sub-classes may wish to implement this. See FieldLabel. -}; - -/** - * Select the element to bind the click handler to. When this element is - * clicked on an editable field, the editor will open. - * - * If the block has only one field and no output connection, we handle clicks - * over the whole block. Otherwise, handle clicks over the the group containing - * the field. - * - * @return {!Element} Element to bind click handler to. - * @private - */ -Blockly.Field.prototype.getClickTarget_ = function() { - var nFields = 0; - - for (var i = 0, input; input = this.sourceBlock_.inputList[i]; i++) { - nFields += input.fieldRow.length; - } - if (nFields <= 1 && this.sourceBlock_.outputConnection) { - return this.sourceBlock_.getSvgRoot(); - } else { - return this.getSvgRoot(); - } -}; - -/** - * Return the absolute coordinates of the top-left corner of this field. - * The origin (0,0) is the top-left corner of the page body. - * @return {!goog.math.Coordinate} Object with .x and .y properties. - * @private - */ -Blockly.Field.prototype.getAbsoluteXY_ = function() { - return goog.style.getPageOffset(this.getClickTarget_()); -}; - -/** - * Whether this field references any Blockly variables. If true it may need to - * be handled differently during serialization and deserialization. Subclasses - * may override this. - * @return {boolean} True if this field has any variable references. - * @package - */ -Blockly.Field.prototype.referencesVariables = function() { - return false; -}; diff --git a/core/field_angle.js b/core/field_angle.js deleted file mode 100644 index f178a2a748..0000000000 --- a/core/field_angle.js +++ /dev/null @@ -1,398 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2013 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Angle input field. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.FieldAngle'); - -goog.require('Blockly.DropDownDiv'); -goog.require('Blockly.FieldTextInput'); -goog.require('goog.math'); -goog.require('goog.userAgent'); - - -/** - * Class for an editable angle field. - * @param {(string|number)=} opt_value The initial content of the field. The - * value should cast to a number, and if it does not, '0' will be used. - * @param {Function=} opt_validator An optional function that is called - * to validate any constraints on what the user entered. Takes the new - * text as an argument and returns the accepted text or null to abort - * the change. - * @extends {Blockly.FieldTextInput} - * @constructor - */ -Blockly.FieldAngle = function(opt_value, opt_validator) { - // Add degree symbol: '360°' (LTR) or '°360' (RTL) - this.symbol_ = Blockly.utils.createSvgElement('tspan', {}, null); - this.symbol_.appendChild(document.createTextNode('\u00B0')); - - var numRestrictor = new RegExp("[\\d]|[\\.]|[-]|[eE]"); - - opt_value = (opt_value && !isNaN(opt_value)) ? String(opt_value) : '0'; - Blockly.FieldAngle.superClass_.constructor.call( - this, opt_value, opt_validator, numRestrictor); - this.addArgType('angle'); -}; -goog.inherits(Blockly.FieldAngle, Blockly.FieldTextInput); - -/** - * Construct a FieldAngle from a JSON arg object. - * @param {!Object} options A JSON object with options (angle). - * @returns {!Blockly.FieldAngle} The new field instance. - * @package - * @nocollapse - */ -Blockly.FieldAngle.fromJson = function(options) { - return new Blockly.FieldAngle(options['angle']); -}; - -/** - * Round angles to the nearest 15 degrees when using mouse. - * Set to 0 to disable rounding. - */ -Blockly.FieldAngle.ROUND = 15; - -/** - * Half the width of protractor image. - */ -Blockly.FieldAngle.HALF = 120 / 2; - -/* The following two settings work together to set the behaviour of the angle - * picker. While many combinations are possible, two modes are typical: - * Math mode. - * 0 deg is right, 90 is up. This is the style used by protractors. - * Blockly.FieldAngle.CLOCKWISE = false; - * Blockly.FieldAngle.OFFSET = 0; - * Compass mode. - * 0 deg is up, 90 is right. This is the style used by maps. - * Blockly.FieldAngle.CLOCKWISE = true; - * Blockly.FieldAngle.OFFSET = 90; - */ - -/** - * Angle increases clockwise (true) or counterclockwise (false). - */ -Blockly.FieldAngle.CLOCKWISE = true; - -/** - * Offset the location of 0 degrees (and all angles) by a constant. - * Usually either 0 (0 = right) or 90 (0 = up). - */ -Blockly.FieldAngle.OFFSET = 90; - -/** - * Maximum allowed angle before wrapping. - * Usually either 360 (for 0 to 359.9) or 180 (for -179.9 to 180). - */ -Blockly.FieldAngle.WRAP = 180; - -/** - * Radius of drag handle - */ -Blockly.FieldAngle.HANDLE_RADIUS = 10; - -/** - * Width of drag handle arrow - */ -Blockly.FieldAngle.ARROW_WIDTH = Blockly.FieldAngle.HANDLE_RADIUS; - -/** - * Half the stroke-width used for the "glow" around the drag handle, rounded up to nearest whole pixel - */ - -Blockly.FieldAngle.HANDLE_GLOW_WIDTH = 3; - -/** - * Radius of protractor circle. Slightly smaller than protractor size since - * otherwise SVG crops off half the border at the edges. - */ -Blockly.FieldAngle.RADIUS = Blockly.FieldAngle.HALF - - Blockly.FieldAngle.HANDLE_RADIUS - Blockly.FieldAngle.HANDLE_GLOW_WIDTH; - -/** - * Radius of central dot circle. - */ -Blockly.FieldAngle.CENTER_RADIUS = 2; - -/** - * Path to the arrow svg icon. - */ -Blockly.FieldAngle.ARROW_SVG_PATH = 'icons/arrow.svg'; - -/** - * Clean up this FieldAngle, as well as the inherited FieldTextInput. - * @return {!Function} Closure to call on destruction of the WidgetDiv. - * @private - */ -Blockly.FieldAngle.prototype.dispose_ = function() { - var thisField = this; - return function() { - Blockly.FieldAngle.superClass_.dispose_.call(thisField)(); - thisField.gauge_ = null; - if (thisField.mouseDownWrapper_) { - Blockly.unbindEvent_(thisField.mouseDownWrapper_); - } - if (thisField.mouseUpWrapper_) { - Blockly.unbindEvent_(thisField.mouseUpWrapper_); - } - if (thisField.mouseMoveWrapper_) { - Blockly.unbindEvent_(thisField.mouseMoveWrapper_); - } - }; -}; - -/** - * Show the inline free-text editor on top of the text. - * @private - */ -Blockly.FieldAngle.prototype.showEditor_ = function() { - // Mobile browsers have issues with in-line textareas (focus & keyboards). - Blockly.FieldAngle.superClass_.showEditor_.call(this, this.useTouchInteraction_); - // If there is an existing drop-down someone else owns, hide it immediately and clear it. - Blockly.DropDownDiv.hideWithoutAnimation(); - Blockly.DropDownDiv.clearContent(); - var div = Blockly.DropDownDiv.getContentDiv(); - // Build the SVG DOM. - var svg = Blockly.utils.createSvgElement('svg', { - 'xmlns': 'http://www.w3.org/2000/svg', - 'xmlns:html': 'http://www.w3.org/1999/xhtml', - 'xmlns:xlink': 'http://www.w3.org/1999/xlink', - 'version': '1.1', - 'height': (Blockly.FieldAngle.HALF * 2) + 'px', - 'width': (Blockly.FieldAngle.HALF * 2) + 'px' - }, div); - Blockly.utils.createSvgElement('circle', { - 'cx': Blockly.FieldAngle.HALF, 'cy': Blockly.FieldAngle.HALF, - 'r': Blockly.FieldAngle.RADIUS, - 'class': 'blocklyAngleCircle' - }, svg); - this.gauge_ = Blockly.utils.createSvgElement('path', - {'class': 'blocklyAngleGauge'}, svg); - // The moving line, x2 and y2 are set in updateGraph_ - this.line_ = Blockly.utils.createSvgElement('line',{ - 'x1': Blockly.FieldAngle.HALF, - 'y1': Blockly.FieldAngle.HALF, - 'class': 'blocklyAngleLine' - }, svg); - // The fixed vertical line at the offset - var offsetRadians = Math.PI * Blockly.FieldAngle.OFFSET / 180; - Blockly.utils.createSvgElement('line', { - 'x1': Blockly.FieldAngle.HALF, - 'y1': Blockly.FieldAngle.HALF, - 'x2': Blockly.FieldAngle.HALF + Blockly.FieldAngle.RADIUS * Math.cos(offsetRadians), - 'y2': Blockly.FieldAngle.HALF - Blockly.FieldAngle.RADIUS * Math.sin(offsetRadians), - 'class': 'blocklyAngleLine' - }, svg); - // Draw markers around the edge. - for (var angle = 0; angle < 360; angle += 15) { - Blockly.utils.createSvgElement('line', { - 'x1': Blockly.FieldAngle.HALF + Blockly.FieldAngle.RADIUS - 13, - 'y1': Blockly.FieldAngle.HALF, - 'x2': Blockly.FieldAngle.HALF + Blockly.FieldAngle.RADIUS - 7, - 'y2': Blockly.FieldAngle.HALF, - 'class': 'blocklyAngleMarks', - 'transform': 'rotate(' + angle + ',' + - Blockly.FieldAngle.HALF + ',' + Blockly.FieldAngle.HALF + ')' - }, svg); - } - // Center point - Blockly.utils.createSvgElement('circle', { - 'cx': Blockly.FieldAngle.HALF, 'cy': Blockly.FieldAngle.HALF, - 'r': Blockly.FieldAngle.CENTER_RADIUS, - 'class': 'blocklyAngleCenterPoint' - }, svg); - // Handle group: a circle and the arrow image - this.handle_ = Blockly.utils.createSvgElement('g', {}, svg); - Blockly.utils.createSvgElement('circle', { - 'cx': 0, - 'cy': 0, - 'r': Blockly.FieldAngle.HANDLE_RADIUS, - 'class': 'blocklyAngleDragHandle' - }, this.handle_); - this.arrowSvg_ = Blockly.utils.createSvgElement('image', - { - 'width': Blockly.FieldAngle.ARROW_WIDTH, - 'height': Blockly.FieldAngle.ARROW_WIDTH, - 'x': -Blockly.FieldAngle.ARROW_WIDTH / 2, - 'y': -Blockly.FieldAngle.ARROW_WIDTH / 2, - 'class': 'blocklyAngleDragArrow' - }, - this.handle_); - this.arrowSvg_.setAttributeNS( - 'http://www.w3.org/1999/xlink', - 'xlink:href', - Blockly.mainWorkspace.options.pathToMedia + Blockly.FieldAngle.ARROW_SVG_PATH - ); - - Blockly.DropDownDiv.setColour(this.sourceBlock_.parentBlock_.getColour(), - this.sourceBlock_.getColourTertiary()); - Blockly.DropDownDiv.setCategory(this.sourceBlock_.parentBlock_.getCategory()); - Blockly.DropDownDiv.showPositionedByBlock(this, this.sourceBlock_); - - this.mouseDownWrapper_ = - Blockly.bindEvent_(this.handle_, 'mousedown', this, this.onMouseDown); - - this.updateGraph_(); -}; -/** - * Set the angle to match the mouse's position. - * @param {!Event} e Mouse move event. - */ -Blockly.FieldAngle.prototype.onMouseDown = function() { - this.mouseMoveWrapper_ = Blockly.bindEvent_(document.body, 'mousemove', this, this.onMouseMove); - this.mouseUpWrapper_ = Blockly.bindEvent_(document.body, 'mouseup', this, this.onMouseUp); -}; - -/** - * Set the angle to match the mouse's position. - * @param {!Event} e Mouse move event. - */ -Blockly.FieldAngle.prototype.onMouseUp = function() { - Blockly.unbindEvent_(this.mouseMoveWrapper_); - Blockly.unbindEvent_(this.mouseUpWrapper_); -}; - -/** - * Set the angle to match the mouse's position. - * @param {!Event} e Mouse move event. - */ -Blockly.FieldAngle.prototype.onMouseMove = function(e) { - e.preventDefault(); - var bBox = this.gauge_.ownerSVGElement.getBoundingClientRect(); - var dx = e.clientX - bBox.left - Blockly.FieldAngle.HALF; - var dy = e.clientY - bBox.top - Blockly.FieldAngle.HALF; - var angle = Math.atan(-dy / dx); - if (isNaN(angle)) { - // This shouldn't happen, but let's not let this error propagate further. - return; - } - angle = goog.math.toDegrees(angle); - // 0: East, 90: North, 180: West, 270: South. - if (dx < 0) { - angle += 180; - } else if (dy > 0) { - angle += 360; - } - if (Blockly.FieldAngle.CLOCKWISE) { - angle = Blockly.FieldAngle.OFFSET + 360 - angle; - } else { - angle -= Blockly.FieldAngle.OFFSET; - } - if (Blockly.FieldAngle.ROUND) { - angle = Math.round(angle / Blockly.FieldAngle.ROUND) * - Blockly.FieldAngle.ROUND; - } - angle = this.callValidator(angle); - Blockly.FieldTextInput.htmlInput_.value = angle; - this.setValue(angle); - this.validate_(); - this.resizeEditor_(); -}; - -/** - * Insert a degree symbol. - * @param {?string} text New text. - */ -Blockly.FieldAngle.prototype.setText = function(text) { - Blockly.FieldAngle.superClass_.setText.call(this, text); - if (!this.textElement_) { - // Not rendered yet. - return; - } - this.updateGraph_(); - // Cached width is obsolete. Clear it. - this.size_.width = 0; -}; - -/** - * Redraw the graph with the current angle. - * @private - */ -Blockly.FieldAngle.prototype.updateGraph_ = function() { - if (!this.gauge_) { - return; - } - var angleDegrees = Number(this.getText()) % 360 + Blockly.FieldAngle.OFFSET; - var angleRadians = goog.math.toRadians(angleDegrees); - var path = ['M ', Blockly.FieldAngle.HALF, ',', Blockly.FieldAngle.HALF]; - var x2 = Blockly.FieldAngle.HALF; - var y2 = Blockly.FieldAngle.HALF; - if (!isNaN(angleRadians)) { - var angle1 = goog.math.toRadians(Blockly.FieldAngle.OFFSET); - var x1 = Math.cos(angle1) * Blockly.FieldAngle.RADIUS; - var y1 = Math.sin(angle1) * -Blockly.FieldAngle.RADIUS; - if (Blockly.FieldAngle.CLOCKWISE) { - angleRadians = 2 * angle1 - angleRadians; - } - x2 += Math.cos(angleRadians) * Blockly.FieldAngle.RADIUS; - y2 -= Math.sin(angleRadians) * Blockly.FieldAngle.RADIUS; - // Use large arc only if input value is greater than wrap - var largeFlag = Math.abs(angleDegrees - Blockly.FieldAngle.OFFSET) > 180 ? 1 : 0; - var sweepFlag = Number(Blockly.FieldAngle.CLOCKWISE); - if (angleDegrees < Blockly.FieldAngle.OFFSET) { - sweepFlag = 1 - sweepFlag; // Sweep opposite direction if less than the offset - } - path.push(' l ', x1, ',', y1, - ' A ', Blockly.FieldAngle.RADIUS, ',', Blockly.FieldAngle.RADIUS, - ' 0 ', largeFlag, ' ', sweepFlag, ' ', x2, ',', y2, ' z'); - - // Image rotation needs to be set in degrees - if (Blockly.FieldAngle.CLOCKWISE) { - var imageRotation = angleDegrees + 2 * Blockly.FieldAngle.OFFSET; - } else { - var imageRotation = -angleDegrees; - } - this.arrowSvg_.setAttribute('transform', 'rotate(' + (imageRotation) + ')'); - } - this.gauge_.setAttribute('d', path.join('')); - this.line_.setAttribute('x2', x2); - this.line_.setAttribute('y2', y2); - this.handle_.setAttribute('transform', 'translate(' + x2 + ',' + y2 + ')'); -}; - -/** - * Ensure that only an angle may be entered. - * @param {string} text The user's text. - * @return {?string} A string representing a valid angle, or null if invalid. - */ -Blockly.FieldAngle.prototype.classValidator = function(text) { - if (text === null) { - return null; - } - var n = parseFloat(text || 0); - if (isNaN(n)) { - return null; - } - n = n % 360; - if (n < 0) { - n += 360; - } - if (n > Blockly.FieldAngle.WRAP) { - n -= 360; - } - return String(n); -}; - -Blockly.Field.register('field_angle', Blockly.FieldAngle); diff --git a/core/field_checkbox.js b/core/field_checkbox.js deleted file mode 100644 index a20b4b16aa..0000000000 --- a/core/field_checkbox.js +++ /dev/null @@ -1,133 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Checkbox field. Checked or not checked. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.FieldCheckbox'); - -goog.require('Blockly.Field'); - - -/** - * Class for a checkbox field. - * @param {string} state The initial state of the field ('TRUE' or 'FALSE'). - * @param {Function=} opt_validator A function that is executed when a new - * option is selected. Its sole argument is the new checkbox state. If - * it returns a value, this becomes the new checkbox state, unless the - * value is null, in which case the change is aborted. - * @extends {Blockly.Field} - * @constructor - */ -Blockly.FieldCheckbox = function(state, opt_validator) { - Blockly.FieldCheckbox.superClass_.constructor.call(this, '', opt_validator); - // Set the initial state. - this.setValue(state); - this.addArgType('checkbox'); -}; -goog.inherits(Blockly.FieldCheckbox, Blockly.Field); - -/** - * Construct a FieldCheckbox from a JSON arg object. - * @param {!Object} options A JSON object with options (checked). - * @returns {!Blockly.FieldCheckbox} The new field instance. - * @package - * @nocollapse - */ -Blockly.FieldCheckbox.fromJson = function(options) { - return new Blockly.FieldCheckbox(options['checked'] ? 'TRUE' : 'FALSE'); -}; - -/** - * Character for the checkmark. - */ -Blockly.FieldCheckbox.CHECK_CHAR = '\u2713'; - -/** - * Mouse cursor style when over the hotspot that initiates editability. - */ -Blockly.FieldCheckbox.prototype.CURSOR = 'default'; - -/** - * Install this checkbox on a block. - */ -Blockly.FieldCheckbox.prototype.init = function() { - if (this.fieldGroup_) { - // Checkbox has already been initialized once. - return; - } - Blockly.FieldCheckbox.superClass_.init.call(this); - // The checkbox doesn't use the inherited text element. - // Instead it uses a custom checkmark element that is either visible or not. - this.checkElement_ = Blockly.utils.createSvgElement('text', - {'class': 'blocklyText blocklyCheckbox', 'x': -3, 'y': 14}, - this.fieldGroup_); - var textNode = document.createTextNode(Blockly.FieldCheckbox.CHECK_CHAR); - this.checkElement_.appendChild(textNode); - this.checkElement_.style.display = this.state_ ? 'block' : 'none'; -}; - -/** - * Return 'TRUE' if the checkbox is checked, 'FALSE' otherwise. - * @return {string} Current state. - */ -Blockly.FieldCheckbox.prototype.getValue = function() { - return String(this.state_).toUpperCase(); -}; - -/** - * Set the checkbox to be checked if newBool is 'TRUE' or true, - * unchecks otherwise. - * @param {string|boolean} newBool New state. - */ -Blockly.FieldCheckbox.prototype.setValue = function(newBool) { - var newState = (typeof newBool == 'string') ? - (newBool.toUpperCase() == 'TRUE') : !!newBool; - if (this.state_ !== newState) { - if (this.sourceBlock_ && Blockly.Events.isEnabled()) { - Blockly.Events.fire(new Blockly.Events.BlockChange( - this.sourceBlock_, 'field', this.name, this.state_, newState)); - } - this.state_ = newState; - if (this.checkElement_) { - this.checkElement_.style.display = newState ? 'block' : 'none'; - } - } -}; - -/** - * Toggle the state of the checkbox. - * @private - */ -Blockly.FieldCheckbox.prototype.showEditor_ = function() { - var newState = !this.state_; - if (this.sourceBlock_) { - // Call any validation function, and allow it to override. - newState = this.callValidator(newState); - } - if (newState !== null) { - this.setValue(String(newState).toUpperCase()); - } -}; - -Blockly.Field.register('field_checkbox', Blockly.FieldCheckbox); diff --git a/core/field_colour.js b/core/field_colour.js deleted file mode 100644 index 6dab2e42a3..0000000000 --- a/core/field_colour.js +++ /dev/null @@ -1,253 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Colour input field. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.FieldColour'); - -goog.require('Blockly.Field'); -goog.require('Blockly.utils'); - -goog.require('goog.dom'); -goog.require('goog.events'); -goog.require('goog.style'); -goog.require('goog.ui.ColorPicker'); - - -/** - * Class for a colour input field. - * @param {string} colour The initial colour in '#rrggbb' format. - * @param {Function=} opt_validator A function that is executed when a new - * colour is selected. Its sole argument is the new colour value. Its - * return value becomes the selected colour, unless it is undefined, in - * which case the new colour stands, or it is null, in which case the change - * is aborted. - * @extends {Blockly.Field} - * @constructor - */ -Blockly.FieldColour = function(colour, opt_validator) { - Blockly.FieldColour.superClass_.constructor.call(this, colour, opt_validator); - this.addArgType('colour'); -}; -goog.inherits(Blockly.FieldColour, Blockly.Field); - -/** - * Construct a FieldColour from a JSON arg object. - * @param {!Object} options A JSON object with options (colour). - * @returns {!Blockly.FieldColour} The new field instance. - * @package - * @nocollapse - */ -Blockly.FieldColour.fromJson = function(options) { - return new Blockly.FieldColour(options['colour']); -}; - -/** - * By default use the global constants for colours. - * @type {Array.} - * @private - */ -Blockly.FieldColour.prototype.colours_ = null; - -/** - * By default use the global constants for columns. - * @type {number} - * @private - */ -Blockly.FieldColour.prototype.columns_ = 0; - -/** - * Install this field on a block. - * @param {!Blockly.Block} block The block containing this field. - */ -Blockly.FieldColour.prototype.init = function(block) { - if (this.fieldGroup_) { - // Colour field has already been initialized once. - return; - } - Blockly.FieldColour.superClass_.init.call(this, block); - this.setValue(this.getValue()); -}; - -/** - * Mouse cursor style when over the hotspot that initiates the editor. - */ -Blockly.FieldColour.prototype.CURSOR = 'default'; - -/** - * Close the colour picker if this input is being deleted. - */ -Blockly.FieldColour.prototype.dispose = function() { - Blockly.WidgetDiv.hideIfOwner(this); - Blockly.FieldColour.superClass_.dispose.call(this); -}; - -/** - * Return the current colour. - * @return {string} Current colour in '#rrggbb' format. - */ -Blockly.FieldColour.prototype.getValue = function() { - return this.colour_; -}; - -/** - * Set the colour. - * @param {string} colour The new colour in '#rrggbb' format. - */ -Blockly.FieldColour.prototype.setValue = function(colour) { - if (this.sourceBlock_ && Blockly.Events.isEnabled() && - this.colour_ != colour) { - Blockly.Events.fire(new Blockly.Events.BlockChange( - this.sourceBlock_, 'field', this.name, this.colour_, colour)); - } - this.colour_ = colour; - if (this.sourceBlock_) { - // Set the primary, secondary, tertiary, and quaternary colour to this value. - // The renderer expects to be able to use the secondary color as the fill for a shadow. - this.sourceBlock_.setColour(colour, colour, colour, colour); - } -}; - -/** - * Get the text from this field. Used when the block is collapsed. - * @return {string} Current text. - */ -Blockly.FieldColour.prototype.getText = function() { - var colour = this.colour_; - // Try to use #rgb format if possible, rather than #rrggbb. - var m = colour.match(/^#(.)\1(.)\2(.)\3$/); - if (m) { - colour = '#' + m[1] + m[2] + m[3]; - } - return colour; -}; - -/** - * Returns the fixed height and width. - * @return {!goog.math.Size} Height and width. - */ -Blockly.FieldColour.prototype.getSize = function() { - return new goog.math.Size(Blockly.BlockSvg.FIELD_WIDTH, Blockly.BlockSvg.FIELD_HEIGHT); -}; - -/** - * An array of colour strings for the palette. - * See bottom of this page for the default: - * http://docs.closure-library.googlecode.com/git/closure_goog_ui_colorpicker.js.source.html - * @type {!Array.} - */ -Blockly.FieldColour.COLOURS = goog.ui.ColorPicker.SIMPLE_GRID_COLORS; - -/** - * Number of columns in the palette. - */ -Blockly.FieldColour.COLUMNS = 7; - -/** - * Set a custom colour grid for this field. - * @param {Array.} colours Array of colours for this block, - * or null to use default (Blockly.FieldColour.COLOURS). - * @return {!Blockly.FieldColour} Returns itself (for method chaining). - */ -Blockly.FieldColour.prototype.setColours = function(colours) { - this.colours_ = colours; - return this; -}; - -/** - * Set a custom grid size for this field. - * @param {number} columns Number of columns for this block, - * or 0 to use default (Blockly.FieldColour.COLUMNS). - * @return {!Blockly.FieldColour} Returns itself (for method chaining). - */ -Blockly.FieldColour.prototype.setColumns = function(columns) { - this.columns_ = columns; - return this; -}; - -/** - * Create a palette under the colour field. - * @private - */ -Blockly.FieldColour.prototype.showEditor_ = function() { - Blockly.WidgetDiv.show(this, this.sourceBlock_.RTL, - Blockly.FieldColour.widgetDispose_); - - // Record viewport dimensions before adding the widget. - var viewportBBox = Blockly.utils.getViewportBBox(); - var anchorBBox = this.getScaledBBox_(); - - // Create and add the colour picker, then record the size. - var picker = this.createWidget_(); - var paletteSize = goog.style.getSize(picker.getElement()); - - // Position the picker to line up with the field. - Blockly.WidgetDiv.positionWithAnchor(viewportBBox, anchorBBox, paletteSize, - this.sourceBlock_.RTL); - - // Configure event handler. - var thisField = this; - Blockly.FieldColour.changeEventKey_ = goog.events.listen(picker, - goog.ui.ColorPicker.EventType.CHANGE, - function(event) { - var colour = event.target.getSelectedColor() || '#000000'; - Blockly.WidgetDiv.hide(); - if (thisField.sourceBlock_) { - // Call any validation function, and allow it to override. - colour = thisField.callValidator(colour); - } - if (colour !== null) { - thisField.setValue(colour); - } - }); -}; - -/** - * Create a color picker widget and render it inside the widget div. - * @return {!goog.ui.ColorPicker} The newly created color picker. - * @private - */ -Blockly.FieldColour.prototype.createWidget_ = function() { - // Create the palette using Closure. - var picker = new goog.ui.ColorPicker(); - picker.setSize(this.columns_ || Blockly.FieldColour.COLUMNS); - picker.setColors(this.colours_ || Blockly.FieldColour.COLOURS); - var div = Blockly.WidgetDiv.DIV; - picker.render(div); - picker.setSelectedColor(this.getValue()); - return picker; -}; - -/** - * Hide the colour palette. - * @private - */ -Blockly.FieldColour.widgetDispose_ = function() { - if (Blockly.FieldColour.changeEventKey_) { - goog.events.unlistenByKey(Blockly.FieldColour.changeEventKey_); - } - Blockly.Events.setGroup(false); -}; - -Blockly.Field.register('field_colour', Blockly.FieldColour); diff --git a/core/field_colour_slider.js b/core/field_colour_slider.js deleted file mode 100644 index 331de6975a..0000000000 --- a/core/field_colour_slider.js +++ /dev/null @@ -1,387 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Colour input field. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.FieldColourSlider'); - -goog.require('Blockly.Field'); -goog.require('Blockly.DropDownDiv'); -goog.require('goog.dom'); -goog.require('goog.events'); -goog.require('goog.style'); -goog.require('goog.color'); -goog.require('goog.ui.Slider'); - -/** - * Class for a slider-based colour input field. - * @param {string} colour The initial colour in '#rrggbb' format. - * @param {Function=} opt_validator A function that is executed when a new - * colour is selected. Its sole argument is the new colour value. Its - * return value becomes the selected colour, unless it is undefined, in - * which case the new colour stands, or it is null, in which case the change - * is aborted. - * @extends {Blockly.Field} - * @constructor - */ -Blockly.FieldColourSlider = function(colour, opt_validator) { - Blockly.FieldColourSlider.superClass_.constructor.call(this, colour, opt_validator); - this.addArgType('colour'); - - // Flag to track whether or not the slider callbacks should execute - this.sliderCallbacksEnabled_ = false; -}; -goog.inherits(Blockly.FieldColourSlider, Blockly.Field); - -/** - * Construct a FieldColourSlider from a JSON arg object. - * @param {!Object} options A JSON object with options (colour). - * @returns {!Blockly.FieldColourSlider} The new field instance. - * @package - * @nocollapse - */ -Blockly.FieldColourSlider.fromJson = function(options) { - return new Blockly.FieldColourSlider(options['colour']); -}; - -/** - * Function to be called if eyedropper can be activated. - * If defined, an eyedropper button will be added to the color picker. - * The button calls this function with a callback to update the field value. - * BEWARE: This is not a stable API, so it is being marked as private. It may change. - * @private - */ -Blockly.FieldColourSlider.activateEyedropper_ = null; - -/** - * Path to the eyedropper svg icon. - */ -Blockly.FieldColourSlider.EYEDROPPER_PATH = 'eyedropper.svg'; - -/** - * Install this field on a block. - * @param {!Blockly.Block} block The block containing this field. - */ -Blockly.FieldColourSlider.prototype.init = function(block) { - if (this.fieldGroup_) { - // Colour slider has already been initialized once. - return; - } - Blockly.FieldColourSlider.superClass_.init.call(this, block); - this.setValue(this.getValue()); -}; - -/** - * Return the current colour. - * @return {string} Current colour in '#rrggbb' format. - */ -Blockly.FieldColourSlider.prototype.getValue = function() { - return this.colour_; -}; - -/** - * Set the colour. - * @param {string} colour The new colour in '#rrggbb' format. - */ -Blockly.FieldColourSlider.prototype.setValue = function(colour) { - if (this.sourceBlock_ && Blockly.Events.isEnabled() && - this.colour_ != colour) { - Blockly.Events.fire(new Blockly.Events.BlockChange( - this.sourceBlock_, 'field', this.name, this.colour_, colour)); - } - this.colour_ = colour; - if (this.sourceBlock_) { - // Set the colours to this value. - // The renderer expects to be able to use the secondary colour as the fill for a shadow. - this.sourceBlock_.setColour(colour, colour, this.sourceBlock_.getColourTertiary(), - this.sourceBlock_.getColourQuaternary()); - } - this.updateSliderHandles_(); - this.updateDom_(); -}; - -/** - * Create the hue, saturation or value CSS gradient for the slide backgrounds. - * @param {string} channel – Either "hue", "saturation" or "value". - * @return {string} Array colour hex colour stops for the given channel - * @private - */ -Blockly.FieldColourSlider.prototype.createColourStops_ = function(channel) { - var stops = []; - for(var n = 0; n <= 360; n += 20) { - switch (channel) { - case 'hue': - stops.push(goog.color.hsvToHex(n, this.saturation_, this.brightness_)); - break; - case 'saturation': - stops.push(goog.color.hsvToHex(this.hue_, n / 360, this.brightness_)); - break; - case 'brightness': - stops.push(goog.color.hsvToHex(this.hue_, this.saturation_, 255 * n / 360)); - break; - default: - throw new Error("Unknown channel for colour sliders: " + channel); - } - } - return stops; -}; - -/** - * Set the gradient CSS properties for the given node and channel - * @param {Node} node - The DOM node the gradient will be set on. - * @param {string} channel – Either "hue", "saturation" or "value". - * @private - */ -Blockly.FieldColourSlider.prototype.setGradient_ = function(node, channel) { - var gradient = this.createColourStops_(channel).join(','); - goog.style.setStyle(node, 'background', - '-moz-linear-gradient(left, ' + gradient + ')'); - goog.style.setStyle(node, 'background', - '-webkit-linear-gradient(left, ' + gradient + ')'); - goog.style.setStyle(node, 'background', - '-o-linear-gradient(left, ' + gradient + ')'); - goog.style.setStyle(node, 'background', - '-ms-linear-gradient(left, ' + gradient + ')'); - goog.style.setStyle(node, 'background', - 'linear-gradient(left, ' + gradient + ')'); -}; - -/** - * Update the readouts and slider backgrounds after value has changed. - * @private - */ -Blockly.FieldColourSlider.prototype.updateDom_ = function() { - if (this.hueSlider_) { - // Update the slider backgrounds - this.setGradient_(this.hueSlider_.getElement(), 'hue'); - this.setGradient_(this.saturationSlider_.getElement(), 'saturation'); - this.setGradient_(this.brightnessSlider_.getElement(), 'brightness'); - - // Update the readouts - this.hueReadout_.textContent = Math.floor(100 * this.hue_ / 360).toFixed(0); - this.saturationReadout_.textContent = Math.floor(100 * this.saturation_).toFixed(0); - this.brightnessReadout_.textContent = Math.floor(100 * this.brightness_ / 255).toFixed(0); - } -}; - -/** - * Update the slider handle positions from the current field value. - * @private - */ -Blockly.FieldColourSlider.prototype.updateSliderHandles_ = function() { - if (this.hueSlider_) { - // Don't let the following calls to setValue for each of the sliders - // trigger the slider callbacks (which then call setValue on this field again - // unnecessarily) - this.sliderCallbacksEnabled_ = false; - this.hueSlider_.setValue(this.hue_); - this.saturationSlider_.setValue(this.saturation_); - this.brightnessSlider_.setValue(this.brightness_); - this.sliderCallbacksEnabled_ = true; - } -}; - -/** - * Get the text from this field. Used when the block is collapsed. - * @return {string} Current text. - */ -Blockly.FieldColourSlider.prototype.getText = function() { - var colour = this.colour_; - // Try to use #rgb format if possible, rather than #rrggbb. - var m = colour.match(/^#(.)\1(.)\2(.)\3$/); - if (m) { - colour = '#' + m[1] + m[2] + m[3]; - } - return colour; -}; - -/** - * Create label and readout DOM elements, returning the readout - * @param {string} labelText - Text for the label - * @return {Array} The container node and the readout node. - * @private - */ -Blockly.FieldColourSlider.prototype.createLabelDom_ = function(labelText) { - var labelContainer = document.createElement('div'); - labelContainer.setAttribute('class', 'scratchColourPickerLabel'); - var readout = document.createElement('span'); - readout.setAttribute('class', 'scratchColourPickerReadout'); - var label = document.createElement('span'); - label.setAttribute('class', 'scratchColourPickerLabelText'); - label.textContent = labelText; - labelContainer.appendChild(label); - labelContainer.appendChild(readout); - return [labelContainer, readout]; -}; - -/** - * Factory for creating the different slider callbacks - * @param {string} channel - One of "hue", "saturation" or "brightness" - * @return {function} the callback for slider update - * @private - */ -Blockly.FieldColourSlider.prototype.sliderCallbackFactory_ = function(channel) { - var thisField = this; - return function(event) { - if (!thisField.sliderCallbacksEnabled_) return; - var channelValue = event.target.getValue(); - switch (channel) { - case 'hue': - thisField.hue_ = channelValue; - break; - case 'saturation': - thisField.saturation_ = channelValue; - break; - case 'brightness': - thisField.brightness_ = channelValue; - break; - } - var colour = goog.color.hsvToHex(thisField.hue_, thisField.saturation_, thisField.brightness_); - if (thisField.sourceBlock_) { - // Call any validation function, and allow it to override. - colour = thisField.callValidator(colour); - } - if (colour !== null) { - thisField.setValue(colour, true); - } - }; -}; - -/** - * Activate the eyedropper, passing in a callback for setting the field value. - * @private - */ -Blockly.FieldColourSlider.prototype.activateEyedropperInternal_ = function() { - var thisField = this; - Blockly.FieldColourSlider.activateEyedropper_(function(value) { - // Update the internal hue/saturation/brightness values so sliders update. - var hsv = goog.color.hexToHsv(value); - thisField.hue_ = hsv[0]; - thisField.saturation_ = hsv[1]; - thisField.brightness_ = hsv[2]; - thisField.setValue(value); - }); -}; - -/** - * Create hue, saturation and brightness sliders under the colour field. - * @private - */ -Blockly.FieldColourSlider.prototype.showEditor_ = function() { - Blockly.DropDownDiv.hideWithoutAnimation(); - Blockly.DropDownDiv.clearContent(); - var div = Blockly.DropDownDiv.getContentDiv(); - - // Init color component values that are used while the editor is open - // in order to keep the slider values stable. - var hsv = goog.color.hexToHsv(this.getValue()); - this.hue_ = hsv[0]; - this.saturation_ = hsv[1]; - this.brightness_ = hsv[2]; - - var hueElements = this.createLabelDom_(Blockly.Msg.COLOUR_HUE_LABEL); - div.appendChild(hueElements[0]); - this.hueReadout_ = hueElements[1]; - this.hueSlider_ = new goog.ui.Slider(); - this.hueSlider_.setUnitIncrement(5); - this.hueSlider_.setMinimum(0); - this.hueSlider_.setMaximum(360); - this.hueSlider_.setMoveToPointEnabled(true); - this.hueSlider_.render(div); - - var saturationElements = - this.createLabelDom_(Blockly.Msg.COLOUR_SATURATION_LABEL); - div.appendChild(saturationElements[0]); - this.saturationReadout_ = saturationElements[1]; - this.saturationSlider_ = new goog.ui.Slider(); - this.saturationSlider_.setMoveToPointEnabled(true); - this.saturationSlider_.setUnitIncrement(0.01); - this.saturationSlider_.setStep(0.001); - this.saturationSlider_.setMinimum(0); - this.saturationSlider_.setMaximum(1.0); - this.saturationSlider_.render(div); - - var brightnessElements = - this.createLabelDom_(Blockly.Msg.COLOUR_BRIGHTNESS_LABEL); - div.appendChild(brightnessElements[0]); - this.brightnessReadout_ = brightnessElements[1]; - this.brightnessSlider_ = new goog.ui.Slider(); - this.brightnessSlider_.setUnitIncrement(2); - this.brightnessSlider_.setMinimum(0); - this.brightnessSlider_.setMaximum(255); - this.brightnessSlider_.setMoveToPointEnabled(true); - this.brightnessSlider_.render(div); - - if (Blockly.FieldColourSlider.activateEyedropper_) { - var button = document.createElement('button'); - button.setAttribute('class', 'scratchEyedropper'); - var image = document.createElement('img'); - image.src = Blockly.mainWorkspace.options.pathToMedia + Blockly.FieldColourSlider.EYEDROPPER_PATH; - button.appendChild(image); - div.appendChild(button); - Blockly.FieldColourSlider.eyedropperEventData_ = - Blockly.bindEventWithChecks_(button, 'click', this, - this.activateEyedropperInternal_); - } - - Blockly.DropDownDiv.setColour('#ffffff', '#dddddd'); - Blockly.DropDownDiv.setCategory(this.sourceBlock_.parentBlock_.getCategory()); - Blockly.DropDownDiv.showPositionedByBlock(this, this.sourceBlock_); - - // Set value updates the slider positions - // Do this before attaching callbacks to avoid extra events from initial set - this.setValue(this.getValue()); - - // Enable callbacks for the sliders - this.sliderCallbacksEnabled_ = true; - - Blockly.FieldColourSlider.hueChangeEventKey_ = goog.events.listen(this.hueSlider_, - goog.ui.Component.EventType.CHANGE, - this.sliderCallbackFactory_('hue')); - Blockly.FieldColourSlider.saturationChangeEventKey_ = goog.events.listen(this.saturationSlider_, - goog.ui.Component.EventType.CHANGE, - this.sliderCallbackFactory_('saturation')); - Blockly.FieldColourSlider.brightnessChangeEventKey_ = goog.events.listen(this.brightnessSlider_, - goog.ui.Component.EventType.CHANGE, - this.sliderCallbackFactory_('brightness')); -}; - -Blockly.FieldColourSlider.prototype.dispose = function() { - if (Blockly.FieldColourSlider.hueChangeEventKey_) { - goog.events.unlistenByKey(Blockly.FieldColourSlider.hueChangeEventKey_); - } - if (Blockly.FieldColourSlider.saturationChangeEventKey_) { - goog.events.unlistenByKey(Blockly.FieldColourSlider.saturationChangeEventKey_); - } - if (Blockly.FieldColourSlider.brightnessChangeEventKey_) { - goog.events.unlistenByKey(Blockly.FieldColourSlider.brightnessChangeEventKey_); - } - if (Blockly.FieldColourSlider.eyedropperEventData_) { - Blockly.unbindEvent_(Blockly.FieldColourSlider.eyedropperEventData_); - } - Blockly.Events.setGroup(false); - Blockly.FieldColourSlider.superClass_.dispose.call(this); -}; - -Blockly.Field.register('field_colour_slider', Blockly.FieldColourSlider); diff --git a/core/field_date.js b/core/field_date.js deleted file mode 100644 index 253cbb4b83..0000000000 --- a/core/field_date.js +++ /dev/null @@ -1,353 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2015 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Date input field. - * @author pkendall64@gmail.com (Paul Kendall) - */ -'use strict'; - -goog.provide('Blockly.FieldDate'); - -goog.require('Blockly.Field'); -goog.require('Blockly.utils'); - -goog.require('goog.date'); -goog.require('goog.date.DateTime'); -goog.require('goog.dom'); -goog.require('goog.events'); -goog.require('goog.i18n.DateTimeSymbols'); -goog.require('goog.i18n.DateTimeSymbols_he'); -goog.require('goog.style'); -goog.require('goog.ui.DatePicker'); - - -/** - * Class for a date input field. - * @param {string} date The initial date. - * @param {Function=} opt_validator A function that is executed when a new - * date is selected. Its sole argument is the new date value. Its - * return value becomes the selected date, unless it is undefined, in - * which case the new date stands, or it is null, in which case the change - * is aborted. - * @extends {Blockly.Field} - * @constructor - */ -Blockly.FieldDate = function(date, opt_validator) { - if (!date) { - date = new goog.date.Date().toIsoString(true); - } - Blockly.FieldDate.superClass_.constructor.call(this, date, opt_validator); - this.setValue(date); - this.addArgType('date'); -}; -goog.inherits(Blockly.FieldDate, Blockly.Field); - -/** - * Construct a FieldDate from a JSON arg object. - * @param {!Object} options A JSON object with options (date). - * @returns {!Blockly.FieldDate} The new field instance. - * @package - * @nocollapse - */ -Blockly.FieldDate.fromJson = function(options) { - return new Blockly.FieldDate(options['date']); -}; - -/** - * Mouse cursor style when over the hotspot that initiates the editor. - */ -Blockly.FieldDate.prototype.CURSOR = 'text'; - -/** - * Close the colour picker if this input is being deleted. - */ -Blockly.FieldDate.prototype.dispose = function() { - Blockly.WidgetDiv.hideIfOwner(this); - Blockly.FieldDate.superClass_.dispose.call(this); -}; - -/** - * Return the current date. - * @return {string} Current date. - */ -Blockly.FieldDate.prototype.getValue = function() { - return this.date_; -}; - -/** - * Set the date. - * @param {string} date The new date. - */ -Blockly.FieldDate.prototype.setValue = function(date) { - if (this.sourceBlock_) { - var validated = this.callValidator(date); - // If the new date is invalid, validation returns null. - // In this case we still want to display the illegal result. - if (validated !== null) { - date = validated; - } - } - this.date_ = date; - Blockly.Field.prototype.setText.call(this, date); -}; - -/** - * Create a date picker under the date field. - * @private - */ -Blockly.FieldDate.prototype.showEditor_ = function() { - Blockly.WidgetDiv.show(this, this.sourceBlock_.RTL, - Blockly.FieldDate.widgetDispose_); - - // Record viewport dimensions before adding the picker. - var viewportBBox = Blockly.utils.getViewportBBox(); - var anchorBBox = this.getScaledBBox_(); - - // Create and add the date picker, then record the size. - var picker = this.createWidget_(); - var pickerSize = goog.style.getSize(picker.getElement()); - - // Position the picker to line up with the field. - Blockly.WidgetDiv.positionWithAnchor(viewportBBox, anchorBBox, pickerSize, - this.sourceBlock_.RTL); - - // Configure event handler. - var thisField = this; - Blockly.FieldDate.changeEventKey_ = goog.events.listen(picker, - goog.ui.DatePicker.Events.CHANGE, - function(event) { - var date = event.date ? event.date.toIsoString(true) : ''; - Blockly.WidgetDiv.hide(); - if (thisField.sourceBlock_) { - // Call any validation function, and allow it to override. - date = thisField.callValidator(date); - } - thisField.setValue(date); - }); -}; - -/** - * Create a date picker widget and render it inside the widget div. - * @return {!goog.ui.DatePicker} The newly created date picker. - * @private - */ -Blockly.FieldDate.prototype.createWidget_ = function() { - // Create the date picker using Closure. - Blockly.FieldDate.loadLanguage_(); - var picker = new goog.ui.DatePicker(); - picker.setAllowNone(false); - picker.setShowWeekNum(false); - var div = Blockly.WidgetDiv.DIV; - picker.render(div); - picker.setDate(goog.date.DateTime.fromIsoString(this.getValue())); - return picker; -}; - -/** - * Hide the date picker. - * @private - */ -Blockly.FieldDate.widgetDispose_ = function() { - if (Blockly.FieldDate.changeEventKey_) { - goog.events.unlistenByKey(Blockly.FieldDate.changeEventKey_); - } - Blockly.Events.setGroup(false); -}; - -/** - * Load the best language pack by scanning the Blockly.Msg object for a - * language that matches the available languages in Closure. - * @private - */ -Blockly.FieldDate.loadLanguage_ = function() { - var reg = /^DateTimeSymbols_(.+)$/; - for (var prop in goog.i18n) { - var m = prop.match(reg); - if (m) { - var lang = m[1].toLowerCase().replace('_', '.'); // E.g. 'pt.br' - if (goog.getObjectByName(lang, Blockly.Msg)) { - goog.i18n.DateTimeSymbols = goog.i18n[prop]; - } - } - } -}; - -/** - * CSS for date picker. See css.js for use. - */ -Blockly.FieldDate.CSS = [ - /* Copied from: goog/css/datepicker.css */ - /** - * Copyright 2009 The Closure Library Authors. All Rights Reserved. - * - * Use of this source code is governed by the Apache License, Version 2.0. - * See the COPYING file for details. - */ - - /** - * Standard styling for a goog.ui.DatePicker. - * - * @author arv@google.com (Erik Arvidsson) - */ - - '.blocklyWidgetDiv .goog-date-picker,', - '.blocklyWidgetDiv .goog-date-picker th,', - '.blocklyWidgetDiv .goog-date-picker td {', - ' font: 13px Arial, sans-serif;', - '}', - - '.blocklyWidgetDiv .goog-date-picker {', - ' -moz-user-focus: normal;', - ' -moz-user-select: none;', - ' position: relative;', - ' border: 1px solid #000;', - ' float: left;', - ' padding: 2px;', - ' color: #000;', - ' background: #c3d9ff;', - ' cursor: default;', - '}', - - '.blocklyWidgetDiv .goog-date-picker th {', - ' text-align: center;', - '}', - - '.blocklyWidgetDiv .goog-date-picker td {', - ' text-align: center;', - ' vertical-align: middle;', - ' padding: 1px 3px;', - '}', - - '.blocklyWidgetDiv .goog-date-picker-menu {', - ' position: absolute;', - ' background: threedface;', - ' border: 1px solid gray;', - ' -moz-user-focus: normal;', - ' z-index: 1;', - ' outline: none;', - '}', - - '.blocklyWidgetDiv .goog-date-picker-menu ul {', - ' list-style: none;', - ' margin: 0px;', - ' padding: 0px;', - '}', - - '.blocklyWidgetDiv .goog-date-picker-menu ul li {', - ' cursor: default;', - '}', - - '.blocklyWidgetDiv .goog-date-picker-menu-selected {', - ' background: #ccf;', - '}', - - '.blocklyWidgetDiv .goog-date-picker th {', - ' font-size: .9em;', - '}', - - '.blocklyWidgetDiv .goog-date-picker td div {', - ' float: left;', - '}', - - '.blocklyWidgetDiv .goog-date-picker button {', - ' padding: 0px;', - ' margin: 1px 0;', - ' border: 0;', - ' color: #20c;', - ' font-weight: bold;', - ' background: transparent;', - '}', - - '.blocklyWidgetDiv .goog-date-picker-date {', - ' background: #fff;', - '}', - - '.blocklyWidgetDiv .goog-date-picker-week,', - '.blocklyWidgetDiv .goog-date-picker-wday {', - ' padding: 1px 3px;', - ' border: 0;', - ' border-color: #a2bbdd;', - ' border-style: solid;', - '}', - - '.blocklyWidgetDiv .goog-date-picker-week {', - ' border-right-width: 1px;', - '}', - - '.blocklyWidgetDiv .goog-date-picker-wday {', - ' border-bottom-width: 1px;', - '}', - - '.blocklyWidgetDiv .goog-date-picker-head td {', - ' text-align: center;', - '}', - - /** Use td.className instead of !important */ - '.blocklyWidgetDiv td.goog-date-picker-today-cont {', - ' text-align: center;', - '}', - - /** Use td.className instead of !important */ - '.blocklyWidgetDiv td.goog-date-picker-none-cont {', - ' text-align: center;', - '}', - - '.blocklyWidgetDiv .goog-date-picker-month {', - ' min-width: 11ex;', - ' white-space: nowrap;', - '}', - - '.blocklyWidgetDiv .goog-date-picker-year {', - ' min-width: 6ex;', - ' white-space: nowrap;', - '}', - - '.blocklyWidgetDiv .goog-date-picker-monthyear {', - ' white-space: nowrap;', - '}', - - '.blocklyWidgetDiv .goog-date-picker table {', - ' border-collapse: collapse;', - '}', - - '.blocklyWidgetDiv .goog-date-picker-other-month {', - ' color: #888;', - '}', - - '.blocklyWidgetDiv .goog-date-picker-wkend-start,', - '.blocklyWidgetDiv .goog-date-picker-wkend-end {', - ' background: #eee;', - '}', - - /** Use td.className instead of !important */ - '.blocklyWidgetDiv td.goog-date-picker-selected {', - ' background: #c3d9ff;', - '}', - - '.blocklyWidgetDiv .goog-date-picker-today {', - ' background: #9ab;', - ' font-weight: bold !important;', - ' border-color: #246 #9bd #9bd #246;', - ' color: #fff;', - '}' -]; - -Blockly.Field.register('field_date', Blockly.FieldDate); diff --git a/core/field_dropdown.js b/core/field_dropdown.js deleted file mode 100644 index 627fbf89e4..0000000000 --- a/core/field_dropdown.js +++ /dev/null @@ -1,483 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Dropdown input field. Used for editable titles and variables. - * In the interests of a consistent UI, the toolbox shares some functions and - * properties with the context menu. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.FieldDropdown'); - -goog.require('Blockly.Field'); -goog.require('Blockly.DropDownDiv'); -goog.require('goog.dom'); -goog.require('goog.events'); -goog.require('goog.style'); -goog.require('goog.ui.Menu'); -goog.require('goog.ui.MenuItem'); -goog.require('goog.userAgent'); - - -/** - * Class for an editable dropdown field. - * @param {(!Array.|!Function)} menuGenerator An array of options - * for a dropdown list, or a function which generates these options. - * @param {Function=} opt_validator A function that is executed when a new - * option is selected, with the newly selected value as its sole argument. - * If it returns a value, that value (which must be one of the options) will - * become selected in place of the newly selected option, unless the return - * value is null, in which case the change is aborted. - * @extends {Blockly.Field} - * @constructor - */ -Blockly.FieldDropdown = function(menuGenerator, opt_validator) { - this.menuGenerator_ = menuGenerator; - this.trimOptions_(); - var firstTuple = this.getOptions()[0]; - - // Call parent's constructor. - Blockly.FieldDropdown.superClass_.constructor.call(this, firstTuple[1], - opt_validator); - this.addArgType('dropdown'); -}; -goog.inherits(Blockly.FieldDropdown, Blockly.Field); - -/** - * Construct a FieldDropdown from a JSON arg object. - * @param {!Object} element A JSON object with options. - * @returns {!Blockly.FieldDropdown} The new field instance. - * @package - * @nocollapse - */ -Blockly.FieldDropdown.fromJson = function(element) { - return new Blockly.FieldDropdown(element['options']); -}; - -/** - * Horizontal distance that a checkmark overhangs the dropdown. - */ -Blockly.FieldDropdown.CHECKMARK_OVERHANG = 25; - -/** - * Mouse cursor style when over the hotspot that initiates the editor. - */ -Blockly.FieldDropdown.prototype.CURSOR = 'default'; - -/** - * Closure menu item currently selected. - * @type {?goog.ui.MenuItem} - */ -Blockly.FieldDropdown.prototype.selectedItem = null; - -/** - * Language-neutral currently selected string or image object. - * @type {string|!Object} - * @private - */ -Blockly.FieldDropdown.prototype.value_ = ''; - -/** - * SVG image element if currently selected option is an image, or null. - * @type {SVGElement} - * @private - */ -Blockly.FieldDropdown.prototype.imageElement_ = null; - -/** - * Object with src, height, width, and alt attributes if currently selected - * option is an image, or null. - * @type {Object} - * @private - */ -Blockly.FieldDropdown.prototype.imageJson_ = null; - -/** - * Install this dropdown on a block. - */ -Blockly.FieldDropdown.prototype.init = function() { - if (this.fieldGroup_) { - // Dropdown has already been initialized once. - return; - } - // Add dropdown arrow: "option ▾" (LTR) or "▾ אופציה" (RTL) - // Positioned on render, after text size is calculated. - /** @type {Number} */ - this.arrowSize_ = 12; - /** @type {Number} */ - this.arrowX_ = 0; - /** @type {Number} */ - this.arrowY_ = 11; - this.arrow_ = Blockly.utils.createSvgElement('image', { - 'height': this.arrowSize_ + 'px', - 'width': this.arrowSize_ + 'px' - }); - this.arrow_.setAttributeNS('http://www.w3.org/1999/xlink', - 'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'dropdown-arrow.svg'); - this.className_ += ' blocklyDropdownText'; - - Blockly.FieldDropdown.superClass_.init.call(this); - // If not in a shadow block, draw a box. - if (!this.sourceBlock_.isShadow()) { - this.box_ = Blockly.utils.createSvgElement('rect', { - 'rx': Blockly.BlockSvg.CORNER_RADIUS, - 'ry': Blockly.BlockSvg.CORNER_RADIUS, - 'x': 0, - 'y': 0, - 'width': this.size_.width, - 'height': this.size_.height, - 'stroke': this.sourceBlock_.getColourTertiary(), - 'fill': this.sourceBlock_.getColour(), - 'class': 'blocklyBlockBackground', - 'fill-opacity': 1 - }, null); - this.fieldGroup_.insertBefore(this.box_, this.textElement_); - } - // Force a reset of the text to add the arrow. - var text = this.text_; - this.text_ = null; - this.setText(text); -}; - -/** - * Create a dropdown menu under the text. - * @private - */ -Blockly.FieldDropdown.prototype.showEditor_ = function() { - var options = this.getOptions(); - if (options.length == 0) return; - - this.dropDownOpen_ = true; - // If there is an existing drop-down someone else owns, hide it immediately and clear it. - Blockly.DropDownDiv.hideWithoutAnimation(); - Blockly.DropDownDiv.clearContent(); - - var contentDiv = Blockly.DropDownDiv.getContentDiv(); - - var thisField = this; - - function callback(e) { - var menu = this; - var menuItem = e.target; - if (menuItem) { - thisField.onItemSelected(menu, menuItem); - } - Blockly.DropDownDiv.hide(); - Blockly.Events.setGroup(false); - } - - var menu = new goog.ui.Menu(); - menu.setRightToLeft(this.sourceBlock_.RTL); - for (var i = 0; i < options.length; i++) { - var content = options[i][0]; // Human-readable text or image. - var value = options[i][1]; // Language-neutral value. - if (typeof content == 'object') { - // An image, not text. - var image = new Image(content['width'], content['height']); - image.src = content['src']; - image.alt = content['alt'] || ''; - content = image; - } - var menuItem = new goog.ui.MenuItem(content); - menuItem.setRightToLeft(this.sourceBlock_.RTL); - menuItem.setValue(value); - menuItem.setCheckable(true); - menu.addChild(menuItem, true); - var checked = (value == this.value_); - menuItem.setChecked(checked); - if (checked) { - this.selectedItem = menuItem; - } - } - // Listen for mouse/keyboard events. - goog.events.listen(menu, goog.ui.Component.EventType.ACTION, callback); - - // Record windowSize and scrollOffset before adding menu. - menu.render(contentDiv); - var menuDom = menu.getElement(); - Blockly.utils.addClass(menuDom, 'blocklyDropdownMenu'); - // Record menuSize after adding menu. - var menuSize = goog.style.getSize(menuDom); - // Recalculate height for the total content, not only box height. - menuSize.height = menuDom.scrollHeight; - - var primaryColour = (this.sourceBlock_.isShadow()) ? - this.sourceBlock_.parentBlock_.getColour() : this.sourceBlock_.getColour(); - - Blockly.DropDownDiv.setColour(primaryColour, this.sourceBlock_.getColourTertiary()); - - var category = (this.sourceBlock_.isShadow()) ? - this.sourceBlock_.parentBlock_.getCategory() : this.sourceBlock_.getCategory(); - Blockly.DropDownDiv.setCategory(category); - - // Calculate positioning based on the field position. - var scale = this.sourceBlock_.workspace.scale; - var bBox = {width: this.size_.width, height: this.size_.height}; - bBox.width *= scale; - bBox.height *= scale; - var position = this.fieldGroup_.getBoundingClientRect(); - var primaryX = position.left + bBox.width / 2; - var primaryY = position.top + bBox.height; - var secondaryX = primaryX; - var secondaryY = position.top; - // Set bounds to workspace; show the drop-down. - Blockly.DropDownDiv.setBoundsElement(this.sourceBlock_.workspace.getParentSvg().parentNode); - Blockly.DropDownDiv.show( - this, primaryX, primaryY, secondaryX, secondaryY, this.onHide.bind(this)); - - menu.setAllowAutoFocus(true); - menuDom.focus(); - - // Update colour to look selected. - if (!this.disableColourChange_) { - if (this.sourceBlock_.isShadow()) { - this.sourceBlock_.setShadowColour(this.sourceBlock_.getColourQuaternary()); - } else if (this.box_) { - this.box_.setAttribute('fill', this.sourceBlock_.getColourQuaternary()); - } - } -}; - -/** - * Callback for when the drop-down is hidden. - */ -Blockly.FieldDropdown.prototype.onHide = function() { - this.dropDownOpen_ = false; - // Update colour to look selected. - if (!this.disableColourChange_ && this.sourceBlock_) { - if (this.sourceBlock_.isShadow()) { - this.sourceBlock_.clearShadowColour(); - } else if (this.box_) { - this.box_.setAttribute('fill', this.sourceBlock_.getColour()); - } - } -}; - -/** - * Handle the selection of an item in the dropdown menu. - * @param {!goog.ui.Menu} menu The Menu component clicked. - * @param {!goog.ui.MenuItem} menuItem The MenuItem selected within menu. - */ -Blockly.FieldDropdown.prototype.onItemSelected = function(menu, menuItem) { - var value = menuItem.getValue(); - if (this.sourceBlock_) { - // Call any validation function, and allow it to override. - value = this.callValidator(value); - } - // If the value of the menu item is a function, call it and do not select it. - if (typeof value == 'function') { - value(); - return; - } - if (value !== null) { - this.setValue(value); - } -}; - -/** - * Factor out common words in statically defined options. - * Create prefix and/or suffix labels. - * @private - */ -Blockly.FieldDropdown.prototype.trimOptions_ = function() { - this.prefixField = null; - this.suffixField = null; - var options = this.menuGenerator_; - if (!goog.isArray(options)) { - return; - } - var hasImages = false; - - // Localize label text and image alt text. - for (var i = 0; i < options.length; i++) { - var label = options[i][0]; - if (typeof label == 'string') { - options[i][0] = Blockly.utils.replaceMessageReferences(label); - } else { - if (label.alt != null) { - options[i][0].alt = Blockly.utils.replaceMessageReferences(label.alt); - } - hasImages = true; - } - } - if (hasImages || options.length < 2) { - return; // Do nothing if too few items or at least one label is an image. - } - var strings = []; - for (var i = 0; i < options.length; i++) { - strings.push(options[i][0]); - } - var shortest = Blockly.utils.shortestStringLength(strings); - var prefixLength = Blockly.utils.commonWordPrefix(strings, shortest); - var suffixLength = Blockly.utils.commonWordSuffix(strings, shortest); - if (!prefixLength && !suffixLength) { - return; - } - if (shortest <= prefixLength + suffixLength) { - // One or more strings will entirely vanish if we proceed. Abort. - return; - } - if (prefixLength) { - this.prefixField = strings[0].substring(0, prefixLength - 1); - } - if (suffixLength) { - this.suffixField = strings[0].substr(1 - suffixLength); - } - // Remove the prefix and suffix from the options. - var newOptions = []; - for (var i = 0; i < options.length; i++) { - var text = options[i][0]; - var value = options[i][1]; - text = text.substring(prefixLength, text.length - suffixLength); - newOptions[i] = [text, value]; - } - this.menuGenerator_ = newOptions; -}; - -/** - * @return {boolean} True if the option list is generated by a function. - * Otherwise false. - */ -Blockly.FieldDropdown.prototype.isOptionListDynamic = function() { - return goog.isFunction(this.menuGenerator_); -}; - -/** - * Return a list of the options for this dropdown. - * @return {!Array.} Array of option tuples: - * (human-readable text or image, language-neutral name). - */ -Blockly.FieldDropdown.prototype.getOptions = function() { - if (goog.isFunction(this.menuGenerator_)) { - return this.menuGenerator_.call(this); - } - return /** @type {!Array.>} */ (this.menuGenerator_); -}; - -/** - * Get the language-neutral value from this dropdown menu. - * @return {string} Current text. - */ -Blockly.FieldDropdown.prototype.getValue = function() { - return this.value_; -}; - -/** - * Set the language-neutral value for this dropdown menu. - * @param {string} newValue New value to set. - */ -Blockly.FieldDropdown.prototype.setValue = function(newValue) { - if (newValue === null || newValue === this.value_) { - return; // No change if null. - } - if (this.sourceBlock_ && Blockly.Events.isEnabled()) { - Blockly.Events.fire(new Blockly.Events.BlockChange( - this.sourceBlock_, 'field', this.name, this.value_, newValue)); - } - // Clear menu item for old value. - if (this.selectedItem) { - this.selectedItem.setChecked(false); - this.selectedItem = null; - } - this.value_ = newValue; - // Look up and display the human-readable text. - var options = this.getOptions(); - for (var i = 0; i < options.length; i++) { - // Options are tuples of human-readable text and language-neutral values. - if (options[i][1] == newValue) { - var content = options[i][0]; - if (typeof content == 'object') { - this.imageJson_ = content; - this.text_ = content.alt; - } else { - this.imageJson_ = null; - this.text_ = content; - } - // Always rerender if either the value or the text has changed. - this.forceRerender(); - return; - } - } - // Value not found. Add it, maybe it will become valid once set - // (like variable names). - this.text_ = newValue; - this.forceRerender(); -}; - -/** - * Sets the text in this field. Trigger a rerender of the source block. - * @param {?string} text New text. - */ -Blockly.FieldDropdown.prototype.setText = function(text) { - if (text === null || text === this.text_) { - // No change if null. - return; - } - this.text_ = text; - this.updateTextNode_(); - - if (this.textElement_) { - this.textElement_.parentNode.appendChild(this.arrow_); - } - if (this.sourceBlock_ && this.sourceBlock_.rendered) { - this.sourceBlock_.render(); - this.sourceBlock_.bumpNeighbours_(); - } -}; - -/** - * Position a drop-down arrow at the appropriate location at render-time. - * @param {number} x X position the arrow is being rendered at, in px. - * @return {number} Amount of space the arrow is taking up, in px. - */ -Blockly.FieldDropdown.prototype.positionArrow = function(x) { - if (!this.arrow_) { - return 0; - } - - var addedWidth = 0; - if (this.sourceBlock_.RTL) { - this.arrowX_ = this.arrowSize_ - Blockly.BlockSvg.DROPDOWN_ARROW_PADDING; - addedWidth = this.arrowSize_ + Blockly.BlockSvg.DROPDOWN_ARROW_PADDING; - } else { - this.arrowX_ = x + Blockly.BlockSvg.DROPDOWN_ARROW_PADDING / 2; - addedWidth = this.arrowSize_ + Blockly.BlockSvg.DROPDOWN_ARROW_PADDING; - } - if (this.box_) { - // Bump positioning to the right for a box-type drop-down. - this.arrowX_ += Blockly.BlockSvg.BOX_FIELD_PADDING; - } - this.arrow_.setAttribute('transform', - 'translate(' + this.arrowX_ + ',' + this.arrowY_ + ')'); - return addedWidth; -}; - -/** - * Close the dropdown menu if this input is being deleted. - */ -Blockly.FieldDropdown.prototype.dispose = function() { - this.selectedItem = null; - Blockly.WidgetDiv.hideIfOwner(this); - Blockly.FieldDropdown.superClass_.dispose.call(this); -}; - -Blockly.Field.register('field_dropdown', Blockly.FieldDropdown); diff --git a/core/field_iconmenu.js b/core/field_iconmenu.js deleted file mode 100644 index f5d2a83b55..0000000000 --- a/core/field_iconmenu.js +++ /dev/null @@ -1,309 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Massachusetts Institute of Technology - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Icon picker input field. - * This is primarily for use in Scratch Horizontal blocks. - * Pops open a drop-down with icons; when an icon is selected, it replaces - * the icon (image field) in the original block. - * @author tmickel@mit.edu (Tim Mickel) - */ -'use strict'; - -goog.provide('Blockly.FieldIconMenu'); - -goog.require('Blockly.DropDownDiv'); - -/** - * Class for an icon menu field. - * @param {Object} icons List of icons. These take the same options as an Image Field. - * @extends {Blockly.Field} - * @constructor - */ -Blockly.FieldIconMenu = function(icons) { - /** @type {object} */ - this.icons_ = icons; - // Example: - // [{src: '...', width: 20, height: 20, alt: '...', value: 'machine_value'}, ...] - // First icon provides the default values. - var defaultValue = icons[0].value; - Blockly.FieldIconMenu.superClass_.constructor.call(this, defaultValue); - this.addArgType('iconmenu'); -}; -goog.inherits(Blockly.FieldIconMenu, Blockly.Field); - -/** - * Construct a FieldIconMenu from a JSON arg object. - * @param {!Object} element A JSON object with options. - * @returns {!Blockly.FieldIconMenu} The new field instance. - * @package - * @nocollapse - */ -Blockly.FieldIconMenu.fromJson = function(element) { - return new Blockly.FieldIconMenu(element['options']); -}; - -/** - * Fixed width of the drop-down, in px. Icon buttons will flow inside this width. - * @type {number} - * @const - */ -Blockly.FieldIconMenu.DROPDOWN_WIDTH = 168; - -/** - * Save the primary colour of the source block while the menu is open, for reset. - * @type {number|string} - * @private - */ -Blockly.FieldIconMenu.savedPrimary_ = null; - -/** - * Called when the field is placed on a block. - * @param {Block} block The owning block. - */ -Blockly.FieldIconMenu.prototype.init = function(block) { - if (this.fieldGroup_) { - // Icon menu has already been initialized once. - return; - } - // Render the arrow icon - // Fixed sizes in px. Saved for creating the flip transform of the menu renders above the button. - var arrowSize = 12; - /** @type {Number} */ - this.arrowX_ = 18; - /** @type {Number} */ - this.arrowY_ = 10; - if (block.RTL) { - // In RTL, the icon position is flipped and rendered from the right (offset by width) - this.arrowX_ = -this.arrowX_ - arrowSize; - } - /** @type {Element} */ - this.arrowIcon_ = Blockly.utils.createSvgElement('image', { - 'height': arrowSize + 'px', - 'width': arrowSize + 'px', - 'transform': 'translate(' + this.arrowX_ + ',' + this.arrowY_ + ')' - }); - this.arrowIcon_.setAttributeNS('http://www.w3.org/1999/xlink', - 'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'dropdown-arrow.svg'); - block.getSvgRoot().appendChild(this.arrowIcon_); - Blockly.FieldIconMenu.superClass_.init.call(this, block); -}; - -/** - * Mouse cursor style when over the hotspot that initiates the editor. - * @const - */ -Blockly.FieldIconMenu.prototype.CURSOR = 'default'; - -/** -* Set the language-neutral value for this icon drop-down menu. - * @param {?string} newValue New value. - * @override - */ -Blockly.FieldIconMenu.prototype.setValue = function(newValue) { - if (newValue === null || newValue === this.value_) { - return; // No change - } - if (this.sourceBlock_ && Blockly.Events.isEnabled()) { - Blockly.Events.fire(new Blockly.Events.Change( - this.sourceBlock_, 'field', this.name, this.value_, newValue)); - } - this.value_ = newValue; - // Find the relevant icon in this.icons_ to get the image src. - this.setParentFieldImage(this.getSrcForValue(this.value_)); -}; - -/** -* Find the parent block's FieldImage and set its src. - * @param {?string} src New src for the parent block FieldImage. - * @private - */ -Blockly.FieldIconMenu.prototype.setParentFieldImage = function(src) { - // Only attempt if we have a set sourceBlock_ and parentBlock_ - // It's possible that this function could be called before - // a parent block is set; in that case, fail silently. - if (this.sourceBlock_ && this.sourceBlock_.parentBlock_) { - var parentBlock = this.sourceBlock_.parentBlock_; - // Loop through all inputs' fields to find the first FieldImage - for (var i = 0, input; input = parentBlock.inputList[i]; i++) { - for (var j = 0, field; field = input.fieldRow[j]; j++) { - if (field instanceof Blockly.FieldImage) { - // Src for a FieldImage is stored in its value. - field.setValue(src); - return; - } - } - } - } -}; - -/** - * Get the language-neutral value from this drop-down menu. - * @return {string} Current language-neutral value. - */ -Blockly.FieldIconMenu.prototype.getValue = function() { - return this.value_; -}; - -/** - * For a language-neutral value, get the src for the image that represents it. - * @param {string} value Language-neutral value to look up. - * @return {string} Src to image representing value - */ -Blockly.FieldIconMenu.prototype.getSrcForValue = function(value) { - for (var i = 0, icon; icon = this.icons_[i]; i++) { - if (icon.value === value) { - return icon.src; - } - } -}; - -/** - * Show the drop-down menu for editing this field. - * @private - */ -Blockly.FieldIconMenu.prototype.showEditor_ = function() { - // If there is an existing drop-down we own, this is a request to hide the drop-down. - if (Blockly.DropDownDiv.hideIfOwner(this)) { - return; - } - // If there is an existing drop-down someone else owns, hide it immediately and clear it. - Blockly.DropDownDiv.hideWithoutAnimation(); - Blockly.DropDownDiv.clearContent(); - // Populate the drop-down with the icons for this field. - var contentDiv = Blockly.DropDownDiv.getContentDiv(); - // Accessibility properties - contentDiv.setAttribute('role', 'menu'); - contentDiv.setAttribute('aria-haspopup', 'true'); - for (var i = 0, icon; icon = this.icons_[i]; i++) { - // Icons with the type property placeholder take up space but don't have any functionality - // Use for special-case layouts - if (icon.type == 'placeholder') { - var placeholder = document.createElement('span'); - placeholder.setAttribute('class', 'blocklyDropDownPlaceholder'); - placeholder.style.width = icon.width + 'px'; - placeholder.style.height = icon.height + 'px'; - contentDiv.appendChild(placeholder); - continue; - } - var button = document.createElement('button'); - button.setAttribute('id', ':' + i); // For aria-activedescendant - button.setAttribute('role', 'menuitem'); - button.setAttribute('class', 'blocklyDropDownButton'); - button.title = icon.alt; - button.style.width = icon.width + 'px'; - button.style.height = icon.height + 'px'; - var backgroundColor = this.sourceBlock_.getColour(); - if (icon.value == this.getValue()) { - // This icon is selected, show it in a different colour - backgroundColor = this.sourceBlock_.getColourTertiary(); - button.setAttribute('aria-selected', 'true'); - } - button.style.backgroundColor = backgroundColor; - button.style.borderColor = this.sourceBlock_.getColourTertiary(); - Blockly.bindEvent_(button, 'click', this, this.buttonClick_); - Blockly.bindEvent_(button, 'mouseup', this, this.buttonClick_); - // These are applied manually instead of using the :hover pseudoclass - // because Android has a bad long press "helper" menu and green highlight - // that we must prevent with ontouchstart preventDefault - Blockly.bindEvent_(button, 'mousedown', button, function(e) { - this.setAttribute('class', 'blocklyDropDownButton blocklyDropDownButtonHover'); - e.preventDefault(); - }); - Blockly.bindEvent_(button, 'mouseover', button, function() { - this.setAttribute('class', 'blocklyDropDownButton blocklyDropDownButtonHover'); - contentDiv.setAttribute('aria-activedescendant', this.id); - }); - Blockly.bindEvent_(button, 'mouseout', button, function() { - this.setAttribute('class', 'blocklyDropDownButton'); - contentDiv.removeAttribute('aria-activedescendant'); - }); - var buttonImg = document.createElement('img'); - buttonImg.src = icon.src; - //buttonImg.alt = icon.alt; - // Upon click/touch, we will be able to get the clicked element as e.target - // Store a data attribute on all possible click targets so we can match it to the icon. - button.setAttribute('data-value', icon.value); - buttonImg.setAttribute('data-value', icon.value); - button.appendChild(buttonImg); - contentDiv.appendChild(button); - } - contentDiv.style.width = Blockly.FieldIconMenu.DROPDOWN_WIDTH + 'px'; - - Blockly.DropDownDiv.setColour(this.sourceBlock_.getColour(), this.sourceBlock_.getColourTertiary()); - Blockly.DropDownDiv.setCategory(this.sourceBlock_.parentBlock_.getCategory()); - - // Update source block colour to look selected - this.savedPrimary_ = this.sourceBlock_.getColour(); - this.sourceBlock_.setColour(this.sourceBlock_.getColourSecondary(), - this.sourceBlock_.getColourSecondary(), - this.sourceBlock_.getColourTertiary(), - this.sourceBlock_.getColourQuaternary()); - - var scale = this.sourceBlock_.workspace.scale; - // Offset for icon-type horizontal blocks. - var secondaryYOffset = ( - -(Blockly.BlockSvg.MIN_BLOCK_Y * scale) - (Blockly.BlockSvg.FIELD_Y_OFFSET * scale) - ); - var renderedPrimary = Blockly.DropDownDiv.showPositionedByBlock( - this, this.sourceBlock_, this.onHide_.bind(this), secondaryYOffset); - if (!renderedPrimary) { - // Adjust for rotation - var arrowX = this.arrowX_ + Blockly.DropDownDiv.ARROW_SIZE / 1.5 + 1; - var arrowY = this.arrowY_ + Blockly.DropDownDiv.ARROW_SIZE / 1.5; - // Flip the arrow on the button - this.arrowIcon_.setAttribute('transform', - 'translate(' + arrowX + ',' + arrowY + ') rotate(180)');} -}; - -/** - * Callback for when a button is clicked inside the drop-down. - * Should be bound to the FieldIconMenu. - * @param {Event} e DOM event for the click/touch - * @private - */ -Blockly.FieldIconMenu.prototype.buttonClick_ = function(e) { - var value = e.target.getAttribute('data-value'); - this.setValue(value); - Blockly.DropDownDiv.hide(); -}; - -/** - * Callback for when the drop-down is hidden. - */ -Blockly.FieldIconMenu.prototype.onHide_ = function() { - // Reset the button colour and clear accessibility properties - // Only attempt to do this reset if sourceBlock_ is not disposed. - // It could become disposed before an onHide_, for example, - // when a block is dragged from the flyout. - if (this.sourceBlock_) { - this.sourceBlock_.setColour(this.savedPrimary_, - this.sourceBlock_.getColourSecondary(), - this.sourceBlock_.getColourTertiary(), - this.sourceBlock_.getColourQuaternary()); - } - Blockly.DropDownDiv.content_.removeAttribute('role'); - Blockly.DropDownDiv.content_.removeAttribute('aria-haspopup'); - Blockly.DropDownDiv.content_.removeAttribute('aria-activedescendant'); - // Unflip the arrow if appropriate - this.arrowIcon_.setAttribute('transform', 'translate(' + this.arrowX_ + ',' + this.arrowY_ + ')'); -}; - -Blockly.Field.register('field_iconmenu', Blockly.FieldIconMenu); diff --git a/core/field_image.js b/core/field_image.js deleted file mode 100644 index 57fcf30eff..0000000000 --- a/core/field_image.js +++ /dev/null @@ -1,193 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Image field. Used for pictures, icons, etc. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.FieldImage'); - -goog.require('Blockly.Field'); -goog.require('goog.dom'); -goog.require('goog.math.Size'); -goog.require('goog.userAgent'); - - -/** - * Class for an image on a block. - * @param {string} src The URL of the image. - * @param {number} width Width of the image. - * @param {number} height Height of the image. - * @param {string=} opt_alt Optional alt text for when block is collapsed. - * @param {boolean} flip_rtl Whether to flip the icon in RTL - * @extends {Blockly.Field} - * @constructor - */ -Blockly.FieldImage = function(src, width, height, opt_alt, flip_rtl) { - this.sourceBlock_ = null; - - // Ensure height and width are numbers. Strings are bad at math. - this.height_ = Number(height); - this.width_ = Number(width); - this.size_ = new goog.math.Size(this.width_, this.height_); - this.text_ = opt_alt || ''; - this.flipRTL_ = flip_rtl; - this.setValue(src); -}; -goog.inherits(Blockly.FieldImage, Blockly.Field); - -/** - * Construct a FieldImage from a JSON arg object, - * dereferencing any string table references. - * @param {!Object} options A JSON object with options (src, width, height, alt, - * and flipRtl/flip_rtl). - * @returns {!Blockly.FieldImage} The new field instance. - * @package - * @nocollapse - */ -Blockly.FieldImage.fromJson = function(options) { - var src = Blockly.utils.replaceMessageReferences(options['src']); - var width = Number(Blockly.utils.replaceMessageReferences(options['width'])); - var height = - Number(Blockly.utils.replaceMessageReferences(options['height'])); - var alt = Blockly.utils.replaceMessageReferences(options['alt']); - var flip_rtl = !!options['flip_rtl'] || !!options['flipRtl']; - return new Blockly.FieldImage(src, width, height, alt, flip_rtl); -}; - -/** - * Editable fields are saved by the XML renderer, non-editable fields are not. - */ -Blockly.FieldImage.prototype.EDITABLE = false; - -/** - * Install this image on a block. - */ -Blockly.FieldImage.prototype.init = function() { - if (this.fieldGroup_) { - // Image has already been initialized once. - return; - } - // Build the DOM. - /** @type {SVGElement} */ - this.fieldGroup_ = Blockly.utils.createSvgElement('g', {}, null); - if (!this.visible_) { - this.fieldGroup_.style.display = 'none'; - } - /** @type {SVGElement} */ - this.imageElement_ = Blockly.utils.createSvgElement( - 'image', - { - 'height': this.height_ + 'px', - 'width': this.width_ + 'px' - }, - this.fieldGroup_); - this.setValue(this.src_); - this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_); - - // Configure the field to be transparent with respect to tooltips. - this.setTooltip(this.sourceBlock_); - Blockly.Tooltip.bindMouseEvents(this.imageElement_); -}; - -/** - * Dispose of all DOM objects belonging to this text. - */ -Blockly.FieldImage.prototype.dispose = function() { - goog.dom.removeNode(this.fieldGroup_); - this.fieldGroup_ = null; - this.imageElement_ = null; -}; - -/** - * Change the tooltip text for this field. - * @param {string|!Element} newTip Text for tooltip or a parent element to - * link to for its tooltip. - */ -Blockly.FieldImage.prototype.setTooltip = function(newTip) { - this.imageElement_.tooltip = newTip; -}; - -/** - * Get the source URL of this image. - * @return {string} Current text. - * @override - */ -Blockly.FieldImage.prototype.getValue = function() { - return this.src_; -}; - -/** - * Set the source URL of this image. - * @param {?string} src New source. - * @override - */ -Blockly.FieldImage.prototype.setValue = function(src) { - if (src === null) { - // No change if null. - return; - } - this.src_ = src; - if (this.imageElement_) { - this.imageElement_.setAttributeNS('http://www.w3.org/1999/xlink', - 'xlink:href', src || ''); - } -}; - -/** - * Get whether to flip this image in RTL - * @return {boolean} True if we should flip in RTL. - */ -Blockly.FieldImage.prototype.getFlipRTL = function() { - return this.flipRTL_; -}; - -/** - * Set the alt text of this image. - * @param {?string} alt New alt text. - * @override - */ -Blockly.FieldImage.prototype.setText = function(alt) { - if (alt === null) { - // No change if null. - return; - } - this.text_ = alt; -}; - -/** - * Images are fixed width, no need to render. - * @private - */ -Blockly.FieldImage.prototype.render_ = function() { - // NOP -}; - -/** - * Images are fixed width, no need to update. - * @private - */ -Blockly.FieldImage.prototype.updateWidth = function() { - // NOP -}; - -Blockly.Field.register('field_image', Blockly.FieldImage); diff --git a/core/field_label.js b/core/field_label.js deleted file mode 100644 index 1565b66368..0000000000 --- a/core/field_label.js +++ /dev/null @@ -1,136 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Non-editable text field. Used for titles, labels, etc. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.FieldLabel'); - -goog.require('Blockly.Field'); -goog.require('Blockly.Tooltip'); -goog.require('goog.dom'); -goog.require('goog.math.Size'); -goog.require('goog.userAgent'); - - -/** - * Class for a non-editable field. - * @param {string} text The initial content of the field. - * @param {string=} opt_class Optional CSS class for the field's text. - * @extends {Blockly.Field} - * @constructor - */ -Blockly.FieldLabel = function(text, opt_class) { - this.size_ = new goog.math.Size(0, 0); - this.class_ = opt_class; - this.setValue(text); -}; -goog.inherits(Blockly.FieldLabel, Blockly.Field); - -/** - * Construct a FieldLabel from a JSON arg object, - * dereferencing any string table references. - * @param {!Object} options A JSON object with options (text, and class). - * @returns {!Blockly.FieldLabel} The new field instance. - * @package - * @nocollapse - */ -Blockly.FieldLabel.fromJson = function(options) { - var text = Blockly.utils.replaceMessageReferences(options['text']); - return new Blockly.FieldLabel(text, options['class']); -}; - -/** - * Editable fields usually show some sort of UI for the user to change them. - * @type {boolean} - * @public - */ -Blockly.FieldLabel.prototype.EDITABLE = false; - -/** - * Serializable fields are saved by the XML renderer, non-serializable fields - * are not. Editable fields should be serialized. - * @type {boolean} - * @public - */ -Blockly.FieldLabel.prototype.SERIALIZABLE = false; - -/** - * Install this text on a block. - */ -Blockly.FieldLabel.prototype.init = function() { - if (this.textElement_) { - // Text has already been initialized once. - return; - } - // Build the DOM. - this.textElement_ = Blockly.utils.createSvgElement('text', - { - 'class': 'blocklyText', - 'y': Blockly.BlockSvg.FIELD_TOP_PADDING, - 'text-anchor': 'middle', - 'dominant-baseline': 'middle', - 'dy': goog.userAgent.EDGE_OR_IE ? Blockly.Field.IE_TEXT_OFFSET : '0' - }, null); - if (this.class_) { - Blockly.utils.addClass(this.textElement_, this.class_); - } - if (!this.visible_) { - this.textElement_.style.display = 'none'; - } - this.sourceBlock_.getSvgRoot().appendChild(this.textElement_); - - // Configure the field to be transparent with respect to tooltips. - this.textElement_.tooltip = this.sourceBlock_; - Blockly.Tooltip.bindMouseEvents(this.textElement_); - // Force a render. - this.render_(); -}; - -/** - * Dispose of all DOM objects belonging to this text. - */ -Blockly.FieldLabel.prototype.dispose = function() { - goog.dom.removeNode(this.textElement_); - this.textElement_ = null; -}; - -/** - * Gets the group element for this field. - * Used for measuring the size and for positioning. - * @return {!Element} The group element. - */ -Blockly.FieldLabel.prototype.getSvgRoot = function() { - return /** @type {!Element} */ (this.textElement_); -}; - -/** - * Change the tooltip text for this field. - * @param {string|!Element} newTip Text for tooltip or a parent element to - * link to for its tooltip. - */ -Blockly.FieldLabel.prototype.setTooltip = function(newTip) { - this.textElement_.tooltip = newTip; -}; - -Blockly.Field.register('field_label', Blockly.FieldLabel); diff --git a/core/field_label_serializable.js b/core/field_label_serializable.js deleted file mode 100644 index 3b468fd80e..0000000000 --- a/core/field_label_serializable.js +++ /dev/null @@ -1,125 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Serialized label field. Behaves like a normal label but is - * always serialized to XML. It may only be edited programmatically. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.FieldLabelSerializable'); - -goog.require('Blockly.FieldLabel'); - - -/** - * Class for a variable getter field. - * @param {string} text The initial content of the field. - * @param {string} opt_class Optional CSS class for the field's text. - * @extends {Blockly.FieldLabel} - * @constructor - * - */ -Blockly.FieldLabelSerializable = function(text, opt_class) { - Blockly.FieldLabelSerializable.superClass_.constructor.call(this, text, - opt_class); - // Used in base field rendering, but we don't need it. - this.arrowWidth_ = 0; -}; -goog.inherits(Blockly.FieldLabelSerializable, Blockly.FieldLabel); - -/** - * Construct a FieldLabelSerializable from a JSON arg object, - * dereferencing any string table references. - * @param {!Object} options A JSON object with options (text, and class). - * @returns {!Blockly.FieldLabelSerializable} The new field instance. - * @package - * @nocollapse - */ -Blockly.FieldLabelSerializable.fromJson = function(options) { - var text = Blockly.utils.replaceMessageReferences(options['text']); - return new Blockly.FieldLabelSerializable(text, options['class']); -}; - -/** - * Editable fields usually show some sort of UI for the user to change them. - * This field should be serialized, but only edited programmatically. - * @type {boolean} - * @public - */ -Blockly.FieldLabelSerializable.prototype.EDITABLE = false; - -/** - * Serializable fields are saved by the XML renderer, non-serializable fields - * are not. This field should be serialized, but only edited programmatically. - * @type {boolean} - * @public - */ -Blockly.FieldLabelSerializable.prototype.SERIALIZABLE = true; - -/** - * Updates the width of the field. This calls getCachedWidth which won't cache - * the approximated width on IE/Edge when `getComputedTextLength` fails. Once - * it eventually does succeed, the result will be cached. - **/ -Blockly.FieldLabelSerializable.prototype.updateWidth = function() { - // Set width of the field. - // Unlike the base Field class, this doesn't add space to editable fields. - this.size_.width = Blockly.Field.getCachedWidth(this.textElement_); -}; - -/** - * Draws the border with the correct width. - * Saves the computed width in a property. - * @private - */ -Blockly.FieldLabelSerializable.prototype.render_ = function() { - if (this.visible_ && this.textElement_) { - // Replace the text. - goog.dom.removeChildren(/** @type {!Element} */ (this.textElement_)); - var textNode = document.createTextNode(this.getDisplayText_()); - this.textElement_.appendChild(textNode); - this.updateWidth(); - - // Update text centering, based on newly calculated width. - var centerTextX = this.size_.width / 2; - - // If half the text length is not at least center of - // visible field (FIELD_WIDTH), center it there instead. - var minOffset = Blockly.BlockSvg.FIELD_WIDTH / 2; - if (this.sourceBlock_.RTL) { - // X position starts at the left edge of the block, in both RTL and LTR. - // First offset by the width of the block to move to the right edge, - // and then subtract to move to the same position as LTR. - var minCenter = this.size_.width - minOffset; - centerTextX = Math.min(minCenter, centerTextX); - } else { - // (width / 2) should exceed Blockly.BlockSvg.FIELD_WIDTH / 2 - // if the text is longer. - centerTextX = Math.max(minOffset, centerTextX); - } - // Apply new text element x position. - this.textElement_.setAttribute('x', centerTextX); - } -}; - -Blockly.Field.register( - 'field_label_serializable', Blockly.FieldLabelSerializable); diff --git a/core/field_matrix.js b/core/field_matrix.js deleted file mode 100644 index e8410e30b9..0000000000 --- a/core/field_matrix.js +++ /dev/null @@ -1,566 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Massachusetts Institute of Technology - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview 5x5 matrix input field. - * Displays an editable 5x5 matrix for controlling LED arrays. - * @author khanning@gmail.com (Kreg Hanning) - */ -'use strict'; - -goog.provide('Blockly.FieldMatrix'); - -goog.require('Blockly.DropDownDiv'); - -/** - * Class for a matrix field. - * @param {number} matrix The default matrix value represented by a 25-bit integer. - * @extends {Blockly.Field} - * @constructor - */ -Blockly.FieldMatrix = function(matrix) { - Blockly.FieldMatrix.superClass_.constructor.call(this, matrix); - this.addArgType('matrix'); - /** - * Array of SVGElement for matrix thumbnail image on block field. - * @type {!Array} - * @private - */ - this.ledThumbNodes_ = []; - /** - * Array of SVGElement for matrix editor in dropdown menu. - * @type {!Array} - * @private - */ - this.ledButtons_ = []; - /** - * String for storing current matrix value. - * @type {!String] - * @private - */ - this.matrix_ = ''; - /** - * SVGElement for LED matrix in editor. - * @type {?SVGElement} - * @private - */ - this.matrixStage_ = null; - /** - * SVG image for dropdown arrow. - * @type {?SVGElement} - * @private - */ - this.arrow_ = null; - /** - * String indicating matrix paint style. - * value can be [null, 'fill', 'clear']. - * @type {?String} - * @private - */ - this.paintStyle_ = null; - /** - * Touch event wrapper. - * Runs when the field is selected. - * @type {!Array} - * @private - */ - this.mouseDownWrapper_ = null; - /** - * Touch event wrapper. - * Runs when the clear button editor button is selected. - * @type {!Array} - * @private - */ - this.clearButtonWrapper_ = null; - /** - * Touch event wrapper. - * Runs when the fill button editor button is selected. - * @type {!Array} - * @private - */ - this.fillButtonWrapper_ = null; - /** - * Touch event wrapper. - * Runs when the matrix editor is touched. - * @type {!Array} - * @private - */ - this.matrixTouchWrapper_ = null; - /** - * Touch event wrapper. - * Runs when the matrix editor touch event moves. - * @type {!Array} - * @private - */ - this.matrixMoveWrapper_ = null; - /** - * Touch event wrapper. - * Runs when the matrix editor is released. - * @type {!Array} - * @private - */ - this.matrixReleaseWrapper_ = null; -}; -goog.inherits(Blockly.FieldMatrix, Blockly.Field); - -/** - * Construct a FieldMatrix from a JSON arg object. - * @param {!Object} options A JSON object with options (matrix). - * @returns {!Blockly.FieldMatrix} The new field instance. - * @package - * @nocollapse - */ -Blockly.FieldMatrix.fromJson = function(options) { - return new Blockly.FieldMatrix(options['matrix']); -}; - -/** - * Fixed size of the matrix thumbnail in the input field, in px. - * @type {number} - * @const - */ -Blockly.FieldMatrix.THUMBNAIL_SIZE = 26; - -/** - * Fixed size of each matrix thumbnail node, in px. - * @type {number} - * @const - */ -Blockly.FieldMatrix.THUMBNAIL_NODE_SIZE = 4; - -/** - * Fixed size of each matrix thumbnail node, in px. - * @type {number} - * @const - */ -Blockly.FieldMatrix.THUMBNAIL_NODE_PAD = 1; - -/** - * Fixed size of arrow icon in drop down menu, in px. - * @type {number} - * @const - */ -Blockly.FieldMatrix.ARROW_SIZE = 12; - -/** - * Fixed size of each button inside the 5x5 matrix, in px. - * @type {number} - * @const - */ -Blockly.FieldMatrix.MATRIX_NODE_SIZE = 18; - -/** - * Fixed corner radius for 5x5 matrix buttons, in px. - * @type {number} - * @const - */ -Blockly.FieldMatrix.MATRIX_NODE_RADIUS = 4; - -/** - * Fixed padding for 5x5 matrix buttons, in px. - * @type {number} - * @const - */ -Blockly.FieldMatrix.MATRIX_NODE_PAD = 5; - -/** - * String with 25 '0' chars. - * Used for clearing a matrix or filling an LED node array. - * @type {string} - * @const - */ -Blockly.FieldMatrix.ZEROS = '0000000000000000000000000'; - -/** - * String with 25 '1' chars. - * Used for filling a matrix. - * @type {string} - * @const - */ -Blockly.FieldMatrix.ONES = '1111111111111111111111111'; - -/** - * Called when the field is placed on a block. - * @param {Block} block The owning block. - */ -Blockly.FieldMatrix.prototype.init = function() { - if (this.fieldGroup_) { - // Matrix menu has already been initialized once. - return; - } - - // Build the DOM. - this.fieldGroup_ = Blockly.utils.createSvgElement('g', {}, null); - this.size_.width = Blockly.FieldMatrix.THUMBNAIL_SIZE + - Blockly.FieldMatrix.ARROW_SIZE + (Blockly.BlockSvg.DROPDOWN_ARROW_PADDING * 1.5); - - this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_); - - var thumbX = Blockly.BlockSvg.DROPDOWN_ARROW_PADDING / 2; - var thumbY = (this.size_.height - Blockly.FieldMatrix.THUMBNAIL_SIZE) / 2; - var thumbnail = Blockly.utils.createSvgElement('g', { - 'transform': 'translate(' + thumbX + ', ' + thumbY + ')', - 'pointer-events': 'bounding-box', 'cursor': 'pointer' - }, this.fieldGroup_); - this.ledThumbNodes_ = []; - var nodeSize = Blockly.FieldMatrix.THUMBNAIL_NODE_SIZE; - var nodePad = Blockly.FieldMatrix.THUMBNAIL_NODE_PAD; - for (var i = 0; i < 5; i++) { - for (var n = 0; n < 5; n++) { - var attr = { - 'x': ((nodeSize + nodePad) * n) + nodePad, - 'y': ((nodeSize + nodePad) * i) + nodePad, - 'width': nodeSize, 'height': nodeSize, - 'rx': nodePad, 'ry': nodePad - }; - this.ledThumbNodes_.push( - Blockly.utils.createSvgElement('rect', attr, thumbnail) - ); - } - thumbnail.style.cursor = 'default'; - this.updateMatrix_(); - } - - if (!this.arrow_) { - var arrowX = Blockly.FieldMatrix.THUMBNAIL_SIZE + - Blockly.BlockSvg.DROPDOWN_ARROW_PADDING * 1.5; - var arrowY = (this.size_.height - Blockly.FieldMatrix.ARROW_SIZE) / 2; - this.arrow_ = Blockly.utils.createSvgElement('image', { - 'height': Blockly.FieldMatrix.ARROW_SIZE + 'px', - 'width': Blockly.FieldMatrix.ARROW_SIZE + 'px', - 'transform': 'translate(' + arrowX + ', ' + arrowY + ')' - }, this.fieldGroup_); - this.arrow_.setAttributeNS('http://www.w3.org/1999/xlink', - 'xlink:href', Blockly.mainWorkspace.options.pathToMedia + - 'dropdown-arrow.svg'); - this.arrow_.style.cursor = 'default'; - } - - this.mouseDownWrapper_ = Blockly.bindEventWithChecks_( - this.getClickTarget_(), 'mousedown', this, this.onMouseDown_); -}; - -/** - * Set the value for this matrix menu. - * @param {string} matrix The new matrix value represented by a 25-bit integer. - * @override - */ -Blockly.FieldMatrix.prototype.setValue = function(matrix) { - if (!matrix || matrix === this.matrix_) { - return; // No change - } - if (this.sourceBlock_ && Blockly.Events.isEnabled()) { - Blockly.Events.fire(new Blockly.Events.Change( - this.sourceBlock_, 'field', this.name, this.matrix_, matrix)); - } - matrix = matrix + Blockly.FieldMatrix.ZEROS.substr(0, 25 - matrix.length); - this.matrix_ = matrix; - this.updateMatrix_(); -}; - -/** - * Get the value from this matrix menu. - * @return {string} Current matrix value. - */ -Blockly.FieldMatrix.prototype.getValue = function() { - return String(this.matrix_); -}; - -/** - * Show the drop-down menu for editing this field. - * @private - */ -Blockly.FieldMatrix.prototype.showEditor_ = function() { - // If there is an existing drop-down someone else owns, hide it immediately and clear it. - Blockly.DropDownDiv.hideWithoutAnimation(); - Blockly.DropDownDiv.clearContent(); - var div = Blockly.DropDownDiv.getContentDiv(); - // Build the SVG DOM. - var matrixSize = (Blockly.FieldMatrix.MATRIX_NODE_SIZE * 5) + - (Blockly.FieldMatrix.MATRIX_NODE_PAD * 6); - this.matrixStage_ = Blockly.utils.createSvgElement('svg', { - 'xmlns': 'http://www.w3.org/2000/svg', - 'xmlns:html': 'http://www.w3.org/1999/xhtml', - 'xmlns:xlink': 'http://www.w3.org/1999/xlink', - 'version': '1.1', - 'height': matrixSize + 'px', - 'width': matrixSize + 'px' - }, div); - // Create the 5x5 matrix - this.ledButtons_ = []; - for (var i = 0; i < 5; i++) { - for (var n = 0; n < 5; n++) { - var x = (Blockly.FieldMatrix.MATRIX_NODE_SIZE * n) + - (Blockly.FieldMatrix.MATRIX_NODE_PAD * (n + 1)); - var y = (Blockly.FieldMatrix.MATRIX_NODE_SIZE * i) + - (Blockly.FieldMatrix.MATRIX_NODE_PAD * (i + 1)); - var attr = { - 'x': x + 'px', 'y': y + 'px', - 'width': Blockly.FieldMatrix.MATRIX_NODE_SIZE, - 'height': Blockly.FieldMatrix.MATRIX_NODE_SIZE, - 'rx': Blockly.FieldMatrix.MATRIX_NODE_RADIUS, - 'ry': Blockly.FieldMatrix.MATRIX_NODE_RADIUS - }; - var led = Blockly.utils.createSvgElement('rect', attr, this.matrixStage_); - this.matrixStage_.appendChild(led); - this.ledButtons_.push(led); - } - } - // Div for lower button menu - var buttonDiv = document.createElement('div'); - // Button to clear matrix - var clearButtonDiv = document.createElement('div'); - clearButtonDiv.className = 'scratchMatrixButtonDiv'; - var clearButton = this.createButton_(this.sourceBlock_.colourSecondary_); - clearButtonDiv.appendChild(clearButton); - // Button to fill matrix - var fillButtonDiv = document.createElement('div'); - fillButtonDiv.className = 'scratchMatrixButtonDiv'; - var fillButton = this.createButton_('#FFFFFF'); - fillButtonDiv.appendChild(fillButton); - - buttonDiv.appendChild(clearButtonDiv); - buttonDiv.appendChild(fillButtonDiv); - div.appendChild(buttonDiv); - - Blockly.DropDownDiv.setColour(this.sourceBlock_.getColour(), - this.sourceBlock_.getColourTertiary()); - Blockly.DropDownDiv.setCategory(this.sourceBlock_.getCategory()); - Blockly.DropDownDiv.showPositionedByBlock(this, this.sourceBlock_); - - this.matrixTouchWrapper_ = - Blockly.bindEvent_(this.matrixStage_, 'mousedown', this, this.onMouseDown); - this.clearButtonWrapper_ = - Blockly.bindEvent_(clearButton, 'click', this, this.clearMatrix_); - this.fillButtonWrapper_ = - Blockly.bindEvent_(fillButton, 'click', this, this.fillMatrix_); - - // Update the matrix for the current value - this.updateMatrix_(); - -}; - -this.nodeCallback_ = function(e, num) { - console.log(num); -}; - -/** - * Make an svg object that resembles a 3x3 matrix to be used as a button. - * @param {string} fill The color to fill the matrix nodes. - * @return {SvgElement} The button svg element. - */ -Blockly.FieldMatrix.prototype.createButton_ = function(fill) { - var button = Blockly.utils.createSvgElement('svg', { - 'xmlns': 'http://www.w3.org/2000/svg', - 'xmlns:html': 'http://www.w3.org/1999/xhtml', - 'xmlns:xlink': 'http://www.w3.org/1999/xlink', - 'version': '1.1', - 'height': Blockly.FieldMatrix.MATRIX_NODE_SIZE + 'px', - 'width': Blockly.FieldMatrix.MATRIX_NODE_SIZE + 'px' - }); - var nodeSize = Blockly.FieldMatrix.MATRIX_NODE_SIZE / 4; - var nodePad = Blockly.FieldMatrix.MATRIX_NODE_SIZE / 16; - for (var i = 0; i < 3; i++) { - for (var n = 0; n < 3; n++) { - Blockly.utils.createSvgElement('rect', { - 'x': ((nodeSize + nodePad) * n) + nodePad, - 'y': ((nodeSize + nodePad) * i) + nodePad, - 'width': nodeSize, 'height': nodeSize, - 'rx': nodePad, 'ry': nodePad, - 'fill': fill - }, button); - } - } - return button; -}; - -/** - * Redraw the matrix with the current value. - * @private - */ -Blockly.FieldMatrix.prototype.updateMatrix_ = function() { - for (var i = 0; i < this.matrix_.length; i++) { - if (this.matrix_[i] === '0') { - this.fillMatrixNode_(this.ledButtons_, i, this.sourceBlock_.colourSecondary_); - this.fillMatrixNode_(this.ledThumbNodes_, i, this.sourceBlock_.colour_); - } else { - this.fillMatrixNode_(this.ledButtons_, i, '#FFFFFF'); - this.fillMatrixNode_(this.ledThumbNodes_, i, '#FFFFFF'); - } - } -}; - -/** - * Clear the matrix. - * @param {!Event} e Mouse event. - */ -Blockly.FieldMatrix.prototype.clearMatrix_ = function(e) { - if (e.button != 0) return; - this.setValue(Blockly.FieldMatrix.ZEROS); -}; - -/** - * Fill the matrix. - * @param {!Event} e Mouse event. - */ -Blockly.FieldMatrix.prototype.fillMatrix_ = function(e) { - if (e.button != 0) return; - this.setValue(Blockly.FieldMatrix.ONES); -}; - -/** - * Fill matrix node with specified colour. - * @param {!Array} node The array of matrix nodes. - * @param {!number} index The index of the matrix node. - * @param {!string} fill The fill colour in '#rrggbb' format. - */ -Blockly.FieldMatrix.prototype.fillMatrixNode_ = function(node, index, fill) { - if (!node || !node[index] || !fill) return; - node[index].setAttribute('fill', fill); -}; - -Blockly.FieldMatrix.prototype.setLEDNode_ = function(led, state) { - if (led < 0 || led > 24) return; - var matrix = this.matrix_.substr(0, led) + state + this.matrix_.substr(led + 1); - this.setValue(matrix); -}; - -Blockly.FieldMatrix.prototype.fillLEDNode_ = function(led) { - if (led < 0 || led > 24) return; - this.setLEDNode_(led, '1'); -}; - -Blockly.FieldMatrix.prototype.clearLEDNode_ = function(led) { - if (led < 0 || led > 24) return; - this.setLEDNode_(led, '0'); -}; - -Blockly.FieldMatrix.prototype.toggleLEDNode_ = function(led) { - if (led < 0 || led > 24) return; - if (this.matrix_.charAt(led) === '0') { - this.setLEDNode_(led, '1'); - } else { - this.setLEDNode_(led, '0'); - } -}; - -/** - * Toggle matrix nodes on and off. - * @param {!Event} e Mouse event. - */ -Blockly.FieldMatrix.prototype.onMouseDown = function(e) { - this.matrixMoveWrapper_ = - Blockly.bindEvent_(document.body, 'mousemove', this, this.onMouseMove); - this.matrixReleaseWrapper_ = - Blockly.bindEvent_(document.body, 'mouseup', this, this.onMouseUp); - var ledHit = this.checkForLED_(e); - if (ledHit > -1) { - if (this.matrix_.charAt(ledHit) === '0') { - this.paintStyle_ = 'fill'; - } else { - this.paintStyle_ = 'clear'; - } - this.toggleLEDNode_(ledHit); - this.updateMatrix_(); - } else { - this.paintStyle_ = null; - } -}; - -/** - * Unbind mouse move event and clear the paint style. - * @param {!Event} e Mouse move event. - */ -Blockly.FieldMatrix.prototype.onMouseUp = function() { - Blockly.unbindEvent_(this.matrixMoveWrapper_); - Blockly.unbindEvent_(this.matrixReleaseWrapper_); - this.paintStyle_ = null; -}; - -/** - * Toggle matrix nodes on and off by dragging mouse. - * @param {!Event} e Mouse move event. - */ -Blockly.FieldMatrix.prototype.onMouseMove = function(e) { - e.preventDefault(); - if (this.paintStyle_) { - var led = this.checkForLED_(e); - if (led < 0) return; - if (this.paintStyle_ === 'clear') { - this.clearLEDNode_(led); - } else if (this.paintStyle_ === 'fill') { - this.fillLEDNode_(led); - } - } -}; - -/** - * Check if mouse coordinates collide with a matrix node. - * @param {!Event} e Mouse move event. - * @return {number} The matching matrix node or -1 for none. - */ -Blockly.FieldMatrix.prototype.checkForLED_ = function(e) { - var bBox = this.matrixStage_.getBoundingClientRect(); - var nodeSize = Blockly.FieldMatrix.MATRIX_NODE_SIZE; - var nodePad = Blockly.FieldMatrix.MATRIX_NODE_PAD; - var dx = e.clientX - bBox.left; - var dy = e.clientY - bBox.top; - var min = nodePad / 2; - var max = bBox.width - (nodePad / 2); - if (dx < min || dx > max || dy < min || dy > max) { - return -1; - } - var xDiv = Math.trunc((dx - nodePad / 2) / (nodeSize + nodePad)); - var yDiv = Math.trunc((dy - nodePad / 2) / (nodeSize + nodePad)); - return xDiv + (yDiv * nodePad); -}; - -/** - * Clean up this FieldMatrix, as well as the inherited Field. - * @return {!Function} Closure to call on destruction of the WidgetDiv. - * @private - */ -Blockly.FieldMatrix.prototype.dispose_ = function() { - var thisField = this; - return function() { - Blockly.FieldMatrix.superClass_.dispose_.call(thisField)(); - thisField.matrixStage_ = null; - if (thisField.mouseDownWrapper_) { - Blockly.unbindEvent_(thisField.mouseDownWrapper_); - } - if (thisField.matrixTouchWrapper_) { - Blockly.unbindEvent_(thisField.matrixTouchWrapper_); - } - if (thisField.matrixReleaseWrapper_) { - Blockly.unbindEvent_(thisField.matrixReleaseWrapper_); - } - if (thisField.matrixMoveWrapper_) { - Blockly.unbindEvent_(thisField.matrixMoveWrapper_); - } - if (thisField.clearButtonWrapper_) { - Blockly.unbindEvent_(thisField.clearButtonWrapper_); - } - if (thisField.fillButtonWrapper_) { - Blockly.unbindEvent_(thisField.fillButtonWrapper_); - } - }; -}; - -Blockly.Field.register('field_matrix', Blockly.FieldMatrix); diff --git a/core/field_note.js b/core/field_note.js deleted file mode 100644 index c3928062d9..0000000000 --- a/core/field_note.js +++ /dev/null @@ -1,850 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2018 Massachusetts Institute of Technology - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Note input field, for selecting a musical note on a piano. - * @author ericr@media.mit.edu (Eric Rosenbaum) - */ -'use strict'; - -goog.provide('Blockly.FieldNote'); - -goog.require('Blockly.DropDownDiv'); -goog.require('Blockly.FieldTextInput'); -goog.require('goog.math'); -goog.require('goog.userAgent'); - -/** - * Class for a note input field, for selecting a musical note on a piano. - * @param {(string|number)=} opt_value The initial content of the field. The - * value should cast to a number, and if it does not, '0' will be used. - * @param {Function=} opt_validator An optional function that is called - * to validate any constraints on what the user entered. Takes the new - * text as an argument and returns the accepted text or null to abort - * the change. - * @extends {Blockly.FieldTextInput} - * @constructor - */ -Blockly.FieldNote = function(opt_value, opt_validator) { - opt_value = (opt_value && !isNaN(opt_value)) ? String(opt_value) : '0'; - Blockly.FieldNote.superClass_.constructor.call( - this, opt_value, opt_validator); - this.addArgType('note'); - - /** - * Width of the field. Computed when drawing it, and used for animation. - * @type {number} - * @private - */ - this.fieldEditorWidth_ = 0; - - /** - * Height of the field. Computed when drawing it. - * @type {number} - * @private - */ - this.fieldEditorHeight_ = 0; - - /** - * The piano SVG. - * @type {SVGElement} - * @private - */ - this.pianoSVG_ = null; - - /** - * Array of SVG elements representing the clickable piano keys. - * @type {!Array} - * @private - */ - this.keySVGs_ = []; - - /** - * Note name indicator at the top of the field. - * @type {SVGElement} - * @private - */ - this.noteNameText_ = null; - - /** - * Note name indicator on the low C key. - * @type {SVGElement} - * @private - */ - this.lowCText_ = null; - - /** - * Note name indicator on the low C key. - * @type {SVGElement} - * @private - */ - this.highCText_ = null; - - /** - * Octave number of the currently displayed range of keys. - * @type {number} - * @private - */ - this.displayedOctave_ = null; - - /** - * Current animation position of the piano SVG, as it shifts left or right to - * change octaves. - * @type {number} - * @private - */ - this.animationPos_ = 0; - - /** - * Target position for the animation as the piano SVG shifts left or right. - * @type {number} - * @private - */ - this.animationTarget_ = 0; - - /** - * A flag indicating that the mouse is currently down. Used in combination with - * mouse enter events to update the key selection while dragging. - * @type {boolean} - * @private - */ - this.mouseIsDown_ = false; - - /** - * An array of wrappers for mouse down events on piano keys. - * @type {!Array.} - * @private - */ - this.mouseDownWrappers_ = []; - - /** - * A wrapper for the mouse up event. - * @type {!Array.} - * @private - */ - this.mouseUpWrapper_ = null; - - /** - * An array of wrappers for mouse enter events on piano keys. - * @type {!Array.} - * @private - */ - this.mouseEnterWrappers_ = []; - - /** - * A wrapper for the mouse down event on the octave down button. - * @type {!Array.} - * @private - */ - this.octaveDownMouseDownWrapper_ = null; - - /** - * A wrapper for the mouse down event on the octave up button. - * @type {!Array.} - * @private - */ - this.octaveUpMouseDownWrapper_ = null; -}; -goog.inherits(Blockly.FieldNote, Blockly.FieldTextInput); - -/** - * Inset in pixels of content displayed in the field, caused by parent properties. - * The inset is actually determined by the CSS property blocklyDropDownDiv- it is - * the sum of the padding and border thickness. - */ -Blockly.FieldNote.INSET = 5; - -/** - * Height of the top area of the field, in px. - * @type {number} - * @const - */ -Blockly.FieldNote.TOP_MENU_HEIGHT = 32 - Blockly.FieldNote.INSET; - -/** - * Padding on the top and sides of the field, in px. - * @type {number} - * @const - */ -Blockly.FieldNote.EDGE_PADDING = 1; - -/** - * Height of the drop shadow on the piano, in px. - * @type {number} - * @const - */ -Blockly.FieldNote.SHADOW_HEIGHT = 4; - -/** - * Color for the shadow on the piano. - * @type {string} - * @const - */ -Blockly.FieldNote.SHADOW_COLOR = '#000'; - -/** - * Opacity for the shadow on the piano. - * @type {string} - * @const - */ -Blockly.FieldNote.SHADOW_OPACITY = .2; - -/** - * A color for the white piano keys. - * @type {string} - * @const - */ -Blockly.FieldNote.WHITE_KEY_COLOR = '#FFFFFF'; - -/** - * A color for the black piano keys. - * @type {string} - * @const - */ -Blockly.FieldNote.BLACK_KEY_COLOR = '#323133'; - -/** - * A color for stroke around black piano keys. - * @type {string} - * @const - */ -Blockly.FieldNote.BLACK_KEY_STROKE = '#555555'; - -/** - * A color for the selected state of a piano key. - * @type {string} - * @const - */ -Blockly.FieldNote.KEY_SELECTED_COLOR = '#b0d6ff'; - -/** - * The number of white keys in one octave on the piano. - * @type {number} - * @const - */ -Blockly.FieldNote.NUM_WHITE_KEYS = 8; - -/** - * Height of a white piano key, in px. - * @type {string} - * @const - */ -Blockly.FieldNote.WHITE_KEY_HEIGHT = 72; - -/** - * Width of a white piano key, in px. - * @type {string} - * @const - */ -Blockly.FieldNote.WHITE_KEY_WIDTH = 40; - -/** - * Height of a black piano key, in px. - * @type {string} - * @const - */ -Blockly.FieldNote.BLACK_KEY_HEIGHT = 40; - -/** - * Width of a black piano key, in px. - * @type {string} - * @const - */ -Blockly.FieldNote.BLACK_KEY_WIDTH = 32; - -/** - * Radius of the curved bottom corner of a piano key, in px. - * @type {string} - * @const - */ -Blockly.FieldNote.KEY_RADIUS = 6; - -/** - * Bottom padding for the labels on C keys. - * @type {string} - * @const - */ -Blockly.FieldNote.KEY_LABEL_PADDING = 8; - -/** - * An array of objects with data describing the keys on the piano. - * @type {Array.<{name: String, pitch: Number, isBlack: boolean}>} - * @const - */ -Blockly.FieldNote.KEY_INFO = [ - {name: 'C', pitch: 0}, - {name: 'C♯', pitch: 1, isBlack: true}, - {name: 'D', pitch: 2}, - {name: 'E♭', pitch: 3, isBlack: true}, - {name: 'E', pitch: 4}, - {name: 'F', pitch: 5}, - {name: 'F♯', pitch: 6, isBlack: true}, - {name: 'G', pitch: 7}, - {name: 'G♯', pitch: 8, isBlack: true}, - {name: 'A', pitch: 9}, - {name: 'B♭', pitch: 10, isBlack: true}, - {name: 'B', pitch: 11}, - {name: 'C', pitch: 12} -]; - -/** - * The MIDI note number of the highest note selectable on the piano. - * @type {number} - * @const - */ -Blockly.FieldNote.MAX_NOTE = 130; - -/** - * The fraction of the distance to the target location to move the piano at each - * step of the animation. - * @type {number} - * @const - */ -Blockly.FieldNote.ANIMATION_FRACTION = 0.2; - -/** - * Path to the arrow svg icon, used on the octave buttons. - * @type {string} - * @const - */ -Blockly.FieldNote.ARROW_SVG_PATH = 'icons/arrow_button.svg'; - -/** - * The size of the square octave buttons. - * @type {number} - * @const - */ -Blockly.FieldNote.OCTAVE_BUTTON_SIZE = 32; - -/** - * Construct a FieldNote from a JSON arg object. - * @param {!Object} options A JSON object with options. - * @returns {!Blockly.FieldNote} The new field instance. - * @package - * @nocollapse - */ -Blockly.FieldNote.fromJson = function(options) { - return new Blockly.FieldNote(options['note']); -}; - -/** - * Clean up this FieldNote, as well as the inherited FieldTextInput. - * @return {!Function} Closure to call on destruction of the WidgetDiv. - * @private - */ -Blockly.FieldNote.prototype.dispose_ = function() { - var thisField = this; - return function() { - Blockly.FieldNote.superClass_.dispose_.call(thisField)(); - thisField.mouseDownWrappers_.forEach(function(wrapper) { - Blockly.unbindEvent_(wrapper); - }); - thisField.mouseEnterWrappers_.forEach(function(wrapper) { - Blockly.unbindEvent_(wrapper); - }); - if (thisField.mouseUpWrapper_) { - Blockly.unbindEvent_(thisField.mouseUpWrapper_); - } - if (thisField.octaveDownMouseDownWrapper_) { - Blockly.unbindEvent_(thisField.octaveDownMouseDownWrapper_); - } - if (thisField.octaveUpMouseDownWrapper_) { - Blockly.unbindEvent_(thisField.octaveUpMouseDownWrapper_); - } - this.pianoSVG_ = null; - this.keySVGs_.length = 0; - this.noteNameText_ = null; - this.lowCText_ = null; - this.highCText_ = null; - }; -}; - -/** - * Show a field with piano keys. - * @private - */ -Blockly.FieldNote.prototype.showEditor_ = function() { - // Mobile browsers have issues with in-line textareas (focus & keyboards). - Blockly.FieldNote.superClass_.showEditor_.call(this, this.useTouchInteraction_); - - // If there is an existing drop-down someone else owns, hide it immediately and clear it. - Blockly.DropDownDiv.hideWithoutAnimation(); - Blockly.DropDownDiv.clearContent(); - - // Build the SVG DOM. - var div = Blockly.DropDownDiv.getContentDiv(); - - this.fieldEditorWidth_ = Blockly.FieldNote.NUM_WHITE_KEYS * Blockly.FieldNote.WHITE_KEY_WIDTH + - Blockly.FieldNote.EDGE_PADDING; - this.fieldEditorHeight_ = Blockly.FieldNote.TOP_MENU_HEIGHT + - Blockly.FieldNote.WHITE_KEY_HEIGHT + - Blockly.FieldNote.EDGE_PADDING; - - var svg = Blockly.utils.createSvgElement('svg', { - 'xmlns': 'http://www.w3.org/2000/svg', - 'xmlns:html': 'http://www.w3.org/1999/xhtml', - 'xmlns:xlink': 'http://www.w3.org/1999/xlink', - 'version': '1.1', - 'height': this.fieldEditorHeight_ + 'px', - 'width': this.fieldEditorWidth_ + 'px' - }, div); - - // Add the white and black keys - // Since we are adding the keys from left to right in order, they need - // to be in two groups in order to layer correctly. - this.pianoSVG_ = Blockly.utils.createSvgElement('g', {}, svg); - var whiteKeyGroup = Blockly.utils.createSvgElement('g', {}, this.pianoSVG_); - var blackKeyGroup = Blockly.utils.createSvgElement('g', {}, this.pianoSVG_); - - // Add three piano octaves, so we can animate moving up or down an octave. - // Only the middle octave gets bound to events. - this.keySVGs_ = []; - this.addPianoOctave_(-this.fieldEditorWidth_ + Blockly.FieldNote.EDGE_PADDING, - whiteKeyGroup, blackKeyGroup, null); - this.addPianoOctave_(0, whiteKeyGroup, blackKeyGroup, this.keySVGs_); - this.addPianoOctave_(this.fieldEditorWidth_ - Blockly.FieldNote.EDGE_PADDING, - whiteKeyGroup, blackKeyGroup, null); - - // Note name indicator at the top of the field - this.noteNameText_ = Blockly.utils.createSvgElement('text', - { - 'x': this.fieldEditorWidth_ / 2, - 'y': Blockly.FieldNote.TOP_MENU_HEIGHT / 2, - 'class': 'blocklyText', - 'text-anchor': 'middle', - 'dominant-baseline': 'middle', - }, svg); - - // Note names on the low and high C keys - var lowCX = Blockly.FieldNote.WHITE_KEY_WIDTH / 2; - this.lowCText_ = this.addCKeyLabel_(lowCX, svg); - var highCX = lowCX + (Blockly.FieldNote.WHITE_KEY_WIDTH * - (Blockly.FieldNote.NUM_WHITE_KEYS - 1)); - this.highCText_ = this.addCKeyLabel_(highCX, svg); - - // Horizontal line at the top of the keys - Blockly.utils.createSvgElement('line', - { - 'stroke': this.sourceBlock_.getColourTertiary(), - 'x1': 0, - 'y1': Blockly.FieldNote.TOP_MENU_HEIGHT, - 'x2': this.fieldEditorWidth_, - 'y2': Blockly.FieldNote.TOP_MENU_HEIGHT - }, svg); - - // Drop shadow at the top of the keys - Blockly.utils.createSvgElement('rect', - { - 'x': 0, - 'y': Blockly.FieldNote.TOP_MENU_HEIGHT, - 'width': this.fieldEditorWidth_, - 'height': Blockly.FieldNote.SHADOW_HEIGHT, - 'fill': Blockly.FieldNote.SHADOW_COLOR, - 'fill-opacity': Blockly.FieldNote.SHADOW_OPACITY - }, svg); - - // Octave buttons - this.octaveDownButton = this.addOctaveButton_(0, true, svg); - this.octaveUpButton = this.addOctaveButton_( - (this.fieldEditorWidth_ + Blockly.FieldNote.INSET * 2) - - Blockly.FieldNote.OCTAVE_BUTTON_SIZE, false, svg); - - this.octaveDownMouseDownWrapper_ = - Blockly.bindEvent_(this.octaveDownButton, 'mousedown', this, function() { - this.changeOctaveBy_(-1); - }); - this.octaveUpMouseDownWrapper_ = - Blockly.bindEvent_(this.octaveUpButton, 'mousedown', this,function() { - this.changeOctaveBy_(1); - }); - Blockly.DropDownDiv.setColour(this.sourceBlock_.parentBlock_.getColour(), - this.sourceBlock_.getColourTertiary()); - Blockly.DropDownDiv.setCategory(this.sourceBlock_.parentBlock_.getCategory()); - Blockly.DropDownDiv.showPositionedByBlock(this, this.sourceBlock_); - - this.updateSelection_(); -}; - -/** - * Add one octave of piano keys drawn using SVG. - * @param {number} x The x position of the left edge of this octave of keys. - * @param {SVGElement} whiteKeyGroup The group for all white piano keys. - * @param {SvgElement} blackKeyGroup The group for all black piano keys. - * @param {!Array.} keySVGarray An array containing all the key SVGs. - * @private - */ -Blockly.FieldNote.prototype.addPianoOctave_ = function(x, whiteKeyGroup, blackKeyGroup, keySVGarray) { - var xIncrement, width, height, fill, stroke, group; - x += Blockly.FieldNote.EDGE_PADDING / 2; - var y = Blockly.FieldNote.TOP_MENU_HEIGHT; - for (var i = 0; i < Blockly.FieldNote.KEY_INFO.length; i++) { - // Draw a black or white key - if (Blockly.FieldNote.KEY_INFO[i].isBlack) { - // Black keys are shifted back half a key - x -= Blockly.FieldNote.BLACK_KEY_WIDTH / 2; - xIncrement = Blockly.FieldNote.BLACK_KEY_WIDTH / 2; - width = Blockly.FieldNote.BLACK_KEY_WIDTH; - height = Blockly.FieldNote.BLACK_KEY_HEIGHT; - fill = Blockly.FieldNote.BLACK_KEY_COLOR; - stroke = Blockly.FieldNote.BLACK_KEY_STROKE; - group = blackKeyGroup; - } else { - xIncrement = Blockly.FieldNote.WHITE_KEY_WIDTH; - width = Blockly.FieldNote.WHITE_KEY_WIDTH; - height = Blockly.FieldNote.WHITE_KEY_HEIGHT; - fill = Blockly.FieldNote.WHITE_KEY_COLOR; - stroke = this.sourceBlock_.getColourTertiary(); - group = whiteKeyGroup; - } - var attr = { - 'd': this.getPianoKeyPath_(x, y, width, height), - 'fill': fill, - 'stroke': stroke - }; - x += xIncrement; - - var keySVG = Blockly.utils.createSvgElement('path', attr, group); - - if (keySVGarray) { - keySVGarray[i] = keySVG; - keySVG.setAttribute('data-pitch', Blockly.FieldNote.KEY_INFO[i].pitch); - keySVG.setAttribute('data-name', Blockly.FieldNote.KEY_INFO[i].name); - keySVG.setAttribute('data-isBlack', Blockly.FieldNote.KEY_INFO[i].isBlack); - - this.mouseDownWrappers_[i] = - Blockly.bindEvent_(keySVG, 'mousedown', this, this.onMouseDownOnKey_); - this.mouseEnterWrappers_[i] = - Blockly.bindEvent_(keySVG, 'mouseenter', this, this.onMouseEnter_); - } - } -}; - -/** - * Construct the SVG path string for a piano key shape: a rectangle with rounded - * corners at the bottom. - * @param {number} x the x position for the key. - * @param {number} y the y position for the key. - * @param {number} width the width of the key. - * @param {number} height the height of the key. - * @returns {string} the SVG path as a string. - * @private - */ -Blockly.FieldNote.prototype.getPianoKeyPath_ = function(x, y, width, height) { - return 'M' + x + ' ' + y + ' ' + - 'L' + x + ' ' + (y + height - Blockly.FieldNote.KEY_RADIUS) + ' ' + - 'Q' + x + ' ' + (y + height) + ' ' + - (x + Blockly.FieldNote.KEY_RADIUS) + ' ' + (y + height) + ' ' + - 'L' + (x + width - Blockly.FieldNote.KEY_RADIUS) + ' ' + (y + height) + ' ' + - 'Q' + (x + width) + ' ' + (y + height) + ' ' + - (x + width) + ' ' + (y + height - Blockly.FieldNote.KEY_RADIUS) + ' ' + - 'L' + (x + width) + ' ' + y + ' ' + - 'L' + x + ' ' + y; -}; - -/** - * Add a button for switching the displayed octave of the piano up or down. - * @param {number} x The x position of the button. - * @param {boolean} flipped If true, the icon should be flipped. - * @param {SvgElement} svg The svg element to add the buttons to. - * @returns {SvgElement} A group containing the button SVG elements. - * @private - */ -Blockly.FieldNote.prototype.addOctaveButton_ = function(x, flipped, svg) { - var group = Blockly.utils.createSvgElement('g', {}, svg); - var imageSize = Blockly.FieldNote.OCTAVE_BUTTON_SIZE; - var arrow = Blockly.utils.createSvgElement('image', - { - 'width': imageSize, - 'height': imageSize, - 'x': x - Blockly.FieldNote.INSET, - 'y': -1 * Blockly.FieldNote.INSET - }, group); - arrow.setAttributeNS( - 'http://www.w3.org/1999/xlink', - 'xlink:href', - Blockly.mainWorkspace.options.pathToMedia + Blockly.FieldNote.ARROW_SVG_PATH - ); - Blockly.utils.createSvgElement('line', - { - 'stroke': this.sourceBlock_.getColourTertiary(), - 'x1': x - Blockly.FieldNote.INSET, - 'y1': 0, - 'x2': x - Blockly.FieldNote.INSET, - 'y2': Blockly.FieldNote.TOP_MENU_HEIGHT - Blockly.FieldNote.INSET - }, group); - if (flipped) { - var translateX = -1 * Blockly.FieldNote.OCTAVE_BUTTON_SIZE + (Blockly.FieldNote.INSET * 2); - group.setAttribute('transform', 'scale(-1, 1) ' + - 'translate(' + translateX + ', 0)'); - } - return group; -}; - -/** - * Add an SVG text label for display on the C keys of the piano. - * @param {number} x The x position for the label. - * @param {SvgElement} svg The SVG element to add the label to. - * @returns {SvgElement} The SVG element containing the label. - * @private - */ -Blockly.FieldNote.prototype.addCKeyLabel_ = function(x, svg) { - return Blockly.utils.createSvgElement('text', - { - 'x': x, - 'y': Blockly.FieldNote.TOP_MENU_HEIGHT + Blockly.FieldNote.WHITE_KEY_HEIGHT - - Blockly.FieldNote.KEY_LABEL_PADDING, - 'class': 'scratchNotePickerKeyLabel', - 'text-anchor': 'middle' - }, svg); -}; - -/** - * Set the visibility of the C key labels. - * @param {boolean} visible If true, set labels to be visible. - * @private - */ -Blockly.FieldNote.prototype.setCKeyLabelsVisible_ = function(visible) { - if (visible) { - this.fadeSvgToOpacity_(this.lowCText_, 1); - this.fadeSvgToOpacity_(this.highCText_, 1); - } else { - this.fadeSvgToOpacity_(this.lowCText_, 0); - this.fadeSvgToOpacity_(this.highCText_, 0); - } -}; - -/** - * Animate an SVG to fade it in or out to a target opacity. - * @param {SvgElement} svg The SVG element to apply the fade to. - * @param {number} opacity The target opacity. - * @private - */ -Blockly.FieldNote.prototype.fadeSvgToOpacity_ = function(svg, opacity) { - svg.setAttribute('style', 'opacity: ' + opacity + '; transition: opacity 0.1s;'); -}; - -/** - * Handle the mouse down event on a piano key. - * @param {!Event} e Mouse down event. - * @private - */ -Blockly.FieldNote.prototype.onMouseDownOnKey_ = function(e) { - this.mouseIsDown_ = true; - this.mouseUpWrapper_ = Blockly.bindEvent_(document.body, 'mouseup', this, this.onMouseUp_); - this.selectNoteWithMouseEvent_(e); -}; - -/** - * Handle the mouse up event following a mouse down on a piano key. - * @private - */ -Blockly.FieldNote.prototype.onMouseUp_ = function() { - this.mouseIsDown_ = false; - Blockly.unbindEvent_(this.mouseUpWrapper_); -}; - -/** - * Handle the event when the mouse enters a piano key. - * @param {!Event} e Mouse enter event. - * @private - */ -Blockly.FieldNote.prototype.onMouseEnter_ = function(e) { - if (this.mouseIsDown_) { - this.selectNoteWithMouseEvent_(e); - } -}; - -/** - * Use the data in a mouse event to select a new note, and play it. - * @param {!Event} e Mouse event. - * @private - */ -Blockly.FieldNote.prototype.selectNoteWithMouseEvent_ = function(e) { - var newNoteNum = Number(e.target.getAttribute('data-pitch')) + this.displayedOctave_ * 12; - this.setNoteNum_(newNoteNum); - this.playNoteInternal_(); -}; - -/** - * Play a note, by calling the externally overriden play note function. - * @private - */ -Blockly.FieldNote.prototype.playNoteInternal_ = function() { - if (Blockly.FieldNote.playNote_) { - Blockly.FieldNote.playNote_( - this.getValue(), - this.sourceBlock_.parentBlock_.getCategory() - ); - } -}; - -/** - * Function to play a musical note corresponding to the key selected. - * Overridden externally. - * @param {number} noteNum the MIDI note number to play. - * @param {string} id An id to select a scratch extension to play the note. - * @private - */ -Blockly.FieldNote.playNote_ = function(/* noteNum, id*/) { - return; -}; - -/** - * Change the selected note by a number of octaves, and start the animation. - * @param {number} octaves The number of octaves to change by. - * @private - */ -Blockly.FieldNote.prototype.changeOctaveBy_ = function(octaves) { - this.displayedOctave_ += octaves; - if (this.displayedOctave_ < 0) { - this.displayedOctave_ = 0; - return; - } - var maxOctave = Math.floor(Blockly.FieldNote.MAX_NOTE / 12); - if (this.displayedOctave_ > maxOctave) { - this.displayedOctave_ = maxOctave; - return; - } - - var newNote = Number(this.getText()) + (octaves * 12); - this.setNoteNum_(newNote); - - this.animationTarget_ = this.fieldEditorWidth_ * octaves * -1; - this.animationPos_ = 0; - this.stepOctaveAnimation_(); - this.setCKeyLabelsVisible_(false); -}; - -/** - * Animate the piano up or down an octave by sliding it to the left or right. - * @private - */ -Blockly.FieldNote.prototype.stepOctaveAnimation_ = function() { - var absDiff = Math.abs(this.animationPos_ - this.animationTarget_); - if (absDiff < 1) { - this.pianoSVG_.setAttribute('transform', 'translate(0, 0)'); - this.setCKeyLabelsVisible_(true); - this.playNoteInternal_(); - return; - } - this.animationPos_ += (this.animationTarget_ - this.animationPos_) * - Blockly.FieldNote.ANIMATION_FRACTION; - this.pianoSVG_.setAttribute('transform', 'translate(' + this.animationPos_ + ',0)'); - requestAnimationFrame(this.stepOctaveAnimation_.bind(this)); -}; - -/** - * Set the selected note number, and update the piano display and the input field. - * @param {number} noteNum The MIDI note number to select. - * @private - */ -Blockly.FieldNote.prototype.setNoteNum_ = function(noteNum) { - noteNum = this.callValidator(noteNum); - this.setValue(noteNum); - Blockly.FieldTextInput.htmlInput_.value = noteNum; -}; - -/** - * Sets the text in this field. Triggers a rerender of the source block, and - * updates the selection on the field. - * @param {?string} text New text. - */ -Blockly.FieldNote.prototype.setText = function(text) { - Blockly.FieldNote.superClass_.setText.call(this, text); - if (!this.textElement_) { - // Not rendered yet. - return; - } - this.updateSelection_(); - // Cached width is obsolete. Clear it. - this.size_.width = 0; -}; - -/** - * For a MIDI note number, find the index of the corresponding piano key. - * @param {number} noteNum The note number. - * @returns {number} The index of the piano key. - * @private - */ -Blockly.FieldNote.prototype.noteNumToKeyIndex_ = function(noteNum) { - return Math.floor(noteNum) - (this.displayedOctave_ * 12); -}; - -/** - * Update the selected note and labels on the field. - * @private - */ -Blockly.FieldNote.prototype.updateSelection_ = function() { - var noteNum = Number(this.getText()); - - // If the note is outside the currently displayed octave, update it - if (this.displayedOctave_ == null || - noteNum > ((this.displayedOctave_ * 12) + 12) || - noteNum < (this.displayedOctave_ * 12)) { - this.displayedOctave_ = Math.floor(noteNum / 12); - } - - var index = this.noteNumToKeyIndex_(noteNum); - - // Clear the highlight on all keys - this.keySVGs_.forEach(function(svg) { - var isBlack = svg.getAttribute('data-isBlack'); - if (isBlack === 'true') { - svg.setAttribute('fill', Blockly.FieldNote.BLACK_KEY_COLOR); - } else { - svg.setAttribute('fill', Blockly.FieldNote.WHITE_KEY_COLOR); - } - }); - // Set the highlight on the selected key - if (this.keySVGs_[index]) { - this.keySVGs_[index].setAttribute('fill', Blockly.FieldNote.KEY_SELECTED_COLOR); - // Update the note name text - var noteName = Blockly.FieldNote.KEY_INFO[index].name; - this.noteNameText_.textContent = noteName + ' (' + Math.floor(noteNum) + ')'; - // Update the low and high C note names - var lowCNum = this.displayedOctave_ * 12; - this.lowCText_.textContent = 'C(' + lowCNum + ')'; - this.highCText_.textContent = 'C(' + (lowCNum + 12) + ')'; - } -}; - -/** - * Ensure that only a valid MIDI note number may be entered. - * @param {string} text The user's text. - * @return {?string} A string representing a valid note number, or null if invalid. - */ -Blockly.FieldNote.prototype.classValidator = function(text) { - if (text === null) { - return null; - } - var n = parseFloat(text || 0); - if (isNaN(n)) { - return null; - } - if (n < 0) { - n = 0; - } - if (n > Blockly.FieldNote.MAX_NOTE) { - n = Blockly.FieldNote.MAX_NOTE; - } - return String(n); -}; - -Blockly.Field.register('field_note', Blockly.FieldNote); diff --git a/core/field_number.js b/core/field_number.js deleted file mode 100644 index c3307c623a..0000000000 --- a/core/field_number.js +++ /dev/null @@ -1,366 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Massachusetts Institute of Technology - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Field for numbers. Includes validator and numpad on touch. - * @author tmickel@mit.edu (Tim Mickel) - */ -'use strict'; - -goog.provide('Blockly.FieldNumber'); - -goog.require('Blockly.FieldTextInput'); -goog.require('Blockly.Touch'); -goog.require('goog.math'); -goog.require('goog.userAgent'); - -/** - * Class for an editable number field. - * In scratch-blocks, the min/max/precision properties are only used - * to construct a restrictor on typable characters, and to inform the pop-up - * numpad on touch devices. - * These properties are included here (i.e. instead of just accepting a - * decimalAllowed, negativeAllowed) to maintain API compatibility with Blockly - * and Blockly for Android. - * @param {(string|number)=} opt_value The initial content of the field. The value - * should cast to a number, and if it does not, '0' will be used. - * @param {(string|number)=} opt_min Minimum value. - * @param {(string|number)=} opt_max Maximum value. - * @param {(string|number)=} opt_precision Precision for value. - * @param {Function=} opt_validator An optional function that is called - * to validate any constraints on what the user entered. Takes the new - * text as an argument and returns the accepted text or null to abort - * the change. - * @extends {Blockly.FieldTextInput} - * @constructor - */ -Blockly.FieldNumber = function(opt_value, opt_min, opt_max, opt_precision, - opt_validator) { - var numRestrictor = this.getNumRestrictor(opt_min, opt_max, opt_precision); - opt_value = (opt_value && !isNaN(opt_value)) ? String(opt_value) : '0'; - Blockly.FieldNumber.superClass_.constructor.call( - this, opt_value, opt_validator, numRestrictor); - this.addArgType('number'); -}; -goog.inherits(Blockly.FieldNumber, Blockly.FieldTextInput); - -/** - * Construct a FieldNumber from a JSON arg object. - * @param {!Object} options A JSON object with options (value, min, max, and - * precision). - * @returns {!Blockly.FieldNumber} The new field instance. - * @package - * @nocollapse - */ -Blockly.FieldNumber.fromJson = function(options) { - return new Blockly.FieldNumber(options['value'], - options['min'], options['max'], options['precision']); -}; - -/** - * Fixed width of the num-pad drop-down, in px. - * @type {number} - * @const - */ -Blockly.FieldNumber.DROPDOWN_WIDTH = 168; - -/** - * Buttons for the num-pad, in order from the top left. - * Values are strings of the number or symbol will be added to the field text - * when the button is pressed. - * @type {Array.} - * @const - */ -// Calculator order -Blockly.FieldNumber.NUMPAD_BUTTONS = - ['7', '8', '9', '4', '5', '6', '1', '2', '3', '.', '0', '-', ' ']; - -/** - * Src for the delete icon to be shown on the num-pad. - * @type {string} - * @const - */ -Blockly.FieldNumber.NUMPAD_DELETE_ICON = 'data:image/svg+xml;utf8,' + - '' + - ''; - -/** - * Currently active field during an edit. - * Used to give a reference to the num-pad button callbacks. - * @type {?FieldNumber} - * @private - */ -Blockly.FieldNumber.activeField_ = null; - -/** - * Return an appropriate restrictor, depending on whether this FieldNumber - * allows decimal or negative numbers. - * @param {number|string|undefined} opt_min Minimum value. - * @param {number|string|undefined} opt_max Maximum value. - * @param {number|string|undefined} opt_precision Precision for value. - * @return {!RegExp} Regular expression for this FieldNumber's restrictor. - */ -Blockly.FieldNumber.prototype.getNumRestrictor = function(opt_min, opt_max, - opt_precision) { - this.setConstraints_(opt_min, opt_max, opt_precision); - var pattern = "[\\d]"; // Always allow digits. - if (this.decimalAllowed_) { - pattern += "|[\\.]"; - } - if (this.negativeAllowed_) { - pattern += "|[-]"; - } - if (this.exponentialAllowed_) { - pattern += "|[eE]"; - } - return new RegExp(pattern); -}; - -/** - * Set the constraints for this field. - * @param {number=} opt_min Minimum number allowed. - * @param {number=} opt_max Maximum number allowed. - * @param {number=} opt_precision Step allowed between numbers - */ -Blockly.FieldNumber.prototype.setConstraints_ = function(opt_min, opt_max, - opt_precision) { - this.decimalAllowed_ = (typeof opt_precision == 'undefined') || - isNaN(opt_precision) || (opt_precision == 0) || - (Math.floor(opt_precision) != opt_precision); - this.negativeAllowed_ = (typeof opt_min == 'undefined') || isNaN(opt_min) || - opt_min < 0; - this.exponentialAllowed_ = this.decimalAllowed_; -}; - -/** - * Show the inline free-text editor on top of the text and the num-pad if - * appropriate. - * @private - */ -Blockly.FieldNumber.prototype.showEditor_ = function() { - Blockly.FieldNumber.activeField_ = this; - // Do not focus on mobile devices so we can show the num-pad - var showNumPad = this.useTouchInteraction_; - Blockly.FieldNumber.superClass_.showEditor_.call(this, false, showNumPad); - - // Show a numeric keypad in the drop-down on touch - if (showNumPad) { - this.showNumPad_(); - } -}; - -/** - * Show the number pad. - * @private - */ -Blockly.FieldNumber.prototype.showNumPad_ = function() { - // If there is an existing drop-down someone else owns, hide it immediately - // and clear it. - Blockly.DropDownDiv.hideWithoutAnimation(); - Blockly.DropDownDiv.clearContent(); - - var contentDiv = Blockly.DropDownDiv.getContentDiv(); - - // Accessibility properties - contentDiv.setAttribute('role', 'menu'); - contentDiv.setAttribute('aria-haspopup', 'true'); - - this.addButtons_(contentDiv); - - // Set colour and size of drop-down - Blockly.DropDownDiv.setColour(this.sourceBlock_.parentBlock_.getColour(), - this.sourceBlock_.getColourTertiary()); - contentDiv.style.width = Blockly.FieldNumber.DROPDOWN_WIDTH + 'px'; - - this.position_(); -}; - -/** - * Figure out where to place the drop-down, and move it there. - * @private - */ -Blockly.FieldNumber.prototype.position_ = function() { - // Calculate positioning for the drop-down - // sourceBlock_ is the rendered shadow field input box - var scale = this.sourceBlock_.workspace.scale; - var bBox = this.sourceBlock_.getHeightWidth(); - bBox.width *= scale; - bBox.height *= scale; - var position = this.getAbsoluteXY_(); - // If we can fit it, render below the shadow block - var primaryX = position.x + bBox.width / 2; - var primaryY = position.y + bBox.height; - // If we can't fit it, render above the entire parent block - var secondaryX = primaryX; - var secondaryY = position.y; - - Blockly.DropDownDiv.setBoundsElement( - this.sourceBlock_.workspace.getParentSvg().parentNode); - Blockly.DropDownDiv.show(this, primaryX, primaryY, secondaryX, secondaryY, - this.onHide_.bind(this)); -}; - -/** - * Add number, punctuation, and erase buttons to the numeric keypad's content - * div. - * @param {Element} contentDiv The div for the numeric keypad. - * @private - */ -Blockly.FieldNumber.prototype.addButtons_ = function(contentDiv) { - var buttonColour = this.sourceBlock_.parentBlock_.getColour(); - var buttonBorderColour = this.sourceBlock_.parentBlock_.getColourTertiary(); - - // Add numeric keypad buttons - var buttons = Blockly.FieldNumber.NUMPAD_BUTTONS; - for (var i = 0, buttonText; buttonText = buttons[i]; i++) { - var button = document.createElement('button'); - button.setAttribute('role', 'menuitem'); - button.setAttribute('class', 'blocklyNumPadButton'); - button.setAttribute('style', - 'background:' + buttonColour + ';' + - 'border: 1px solid ' + buttonBorderColour + ';'); - button.title = buttonText; - button.innerHTML = buttonText; - Blockly.bindEvent_(button, 'mousedown', button, - Blockly.FieldNumber.numPadButtonTouch); - if (buttonText == '.' && !this.decimalAllowed_) { - // Don't show the decimal point for inputs that must be round numbers - button.setAttribute('style', 'visibility: hidden'); - } else if (buttonText == '-' && !this.negativeAllowed_) { - continue; - } else if (buttonText == ' ' && !this.negativeAllowed_) { - continue; - } else if (buttonText == ' ' && this.negativeAllowed_) { - button.setAttribute('style', 'visibility: hidden'); - } - contentDiv.appendChild(button); - } - // Add erase button to the end - var eraseButton = document.createElement('button'); - eraseButton.setAttribute('role', 'menuitem'); - eraseButton.setAttribute('class', 'blocklyNumPadButton'); - eraseButton.setAttribute('style', - 'background:' + buttonColour + ';' + - 'border: 1px solid ' + buttonBorderColour + ';'); - eraseButton.title = 'Delete'; - - var eraseImage = document.createElement('img'); - eraseImage.src = Blockly.FieldNumber.NUMPAD_DELETE_ICON; - eraseButton.appendChild(eraseImage); - - Blockly.bindEvent_(eraseButton, 'mousedown', null, - Blockly.FieldNumber.numPadEraseButtonTouch); - contentDiv.appendChild(eraseButton); -}; - -/** - * Call for when a num-pad number or punctuation button is touched. - * Determine what the user is inputting and update the text field appropriately. - * @param {Event} e DOM event triggering the touch. - */ -Blockly.FieldNumber.numPadButtonTouch = function(e) { - // String of the button (e.g., '7') - var spliceValue = this.innerHTML; - // Old value of the text field - var oldValue = Blockly.FieldTextInput.htmlInput_.value; - // Determine the selected portion of the text field - var selectionStart = Blockly.FieldTextInput.htmlInput_.selectionStart; - var selectionEnd = Blockly.FieldTextInput.htmlInput_.selectionEnd; - - // Splice in the new value - var newValue = oldValue.slice(0, selectionStart) + spliceValue + - oldValue.slice(selectionEnd); - - // Set new value and advance the cursor - Blockly.FieldNumber.updateDisplay_(newValue, selectionStart + spliceValue.length); - - // This is just a click. - Blockly.Touch.clearTouchIdentifier(); - - // Prevent default to not lose input focus - e.preventDefault(); -}; - -/** - * Call for when the num-pad erase button is touched. - * Determine what the user is asking to erase, and erase it. - * @param {Event} e DOM event triggering the touch. - */ -Blockly.FieldNumber.numPadEraseButtonTouch = function(e) { - // Old value of the text field - var oldValue = Blockly.FieldTextInput.htmlInput_.value; - // Determine what is selected to erase (if anything) - var selectionStart = Blockly.FieldTextInput.htmlInput_.selectionStart; - var selectionEnd = Blockly.FieldTextInput.htmlInput_.selectionEnd; - - // If selection is zero-length, shift start to the left 1 character - if (selectionStart == selectionEnd) { - selectionStart = Math.max(0, selectionStart - 1); - } - - // Cut out selected range - var newValue = oldValue.slice(0, selectionStart) + - oldValue.slice(selectionEnd); - - Blockly.FieldNumber.updateDisplay_(newValue, selectionStart); - - // This is just a click. - Blockly.Touch.clearTouchIdentifier(); - - // Prevent default to not lose input focus which resets cursors in Chrome - e.preventDefault(); -}; - -/** - * Update the displayed value and resize/scroll the text field as needed. - * @param {string} newValue The new text to display. - * @param {string} newSelection The new index to put the cursor - * @private. - */ -Blockly.FieldNumber.updateDisplay_ = function(newValue, newSelection) { - var htmlInput = Blockly.FieldTextInput.htmlInput_; - // Updates the display. The actual setValue occurs when editing ends. - htmlInput.value = newValue; - // Resize and scroll the text field appropriately - Blockly.FieldNumber.superClass_.resizeEditor_.call( - Blockly.FieldNumber.activeField_); - htmlInput.setSelectionRange(newSelection, newSelection); - htmlInput.scrollLeft = htmlInput.scrollWidth; - Blockly.FieldNumber.activeField_.validate_(); -}; - -/** - * Callback for when the drop-down is hidden. - */ -Blockly.FieldNumber.prototype.onHide_ = function() { - // Clear accessibility properties - Blockly.DropDownDiv.content_.removeAttribute('role'); - Blockly.DropDownDiv.content_.removeAttribute('aria-haspopup'); -}; - -Blockly.Field.register('field_number', Blockly.FieldNumber); diff --git a/core/field_numberdropdown.js b/core/field_numberdropdown.js deleted file mode 100644 index 27f1c91c04..0000000000 --- a/core/field_numberdropdown.js +++ /dev/null @@ -1,77 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2013 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Combination number + drop-down field - * @author tmickel@mit.edu (Tim Mickel) - */ -'use strict'; - -goog.provide('Blockly.FieldNumberDropdown'); - -goog.require('Blockly.FieldTextDropdown'); -goog.require('goog.userAgent'); - - -/** - * Class for a combination number + drop-down field. - * @param {number|string} value The initial content of the field. - * @param {(!Array.>|!Function)} menuGenerator An array of - * options for a dropdown list, or a function which generates these options. - * @param {number|string|undefined} opt_min Minimum value. - * @param {number|string|undefined} opt_max Maximum value. - * @param {number|string|undefined} opt_precision Precision for value. - * @param {Function=} opt_validator An optional function that is called - * to validate any constraints on what the user entered. Takes the new - * text as an argument and returns the accepted text or null to abort - * the change. - * @extends {Blockly.FieldTextInput} - * @constructor - */ -Blockly.FieldNumberDropdown = function(value, menuGenerator, opt_min, opt_max, - opt_precision, opt_validator) { - this.setConstraints_ = Blockly.FieldNumber.prototype.setConstraints_; - - var numRestrictor = Blockly.FieldNumber.prototype.getNumRestrictor.call( - this, opt_min, opt_max, opt_precision - ); - Blockly.FieldNumberDropdown.superClass_.constructor.call( - this, value, menuGenerator, opt_validator, numRestrictor - ); - this.addArgType('numberdropdown'); -}; -goog.inherits(Blockly.FieldNumberDropdown, Blockly.FieldTextDropdown); - -/** - * Construct a FieldTextDropdown from a JSON arg object, - * dereferencing any string table references. - * @param {!Object} element A JSON object with options. - * @returns {!Blockly.FieldNumberDropdown} The new field instance. - * @package - * @nocollapse - */ -Blockly.FieldNumberDropdown.fromJson = function(element) { - return new Blockly.FieldNumberDropdown( - element['value'], element['options'], - element['min'], element['max'], element['precision'] - ); -}; - -Blockly.Field.register('field_numberdropdown', Blockly.FieldNumberDropdown); diff --git a/core/field_textdropdown.js b/core/field_textdropdown.js deleted file mode 100644 index 4e192a33f6..0000000000 --- a/core/field_textdropdown.js +++ /dev/null @@ -1,164 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2013 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Combination text + drop-down field - * @author tmickel@mit.edu (Tim Mickel) - */ -'use strict'; - -goog.provide('Blockly.FieldTextDropdown'); - -goog.require('Blockly.DropDownDiv'); -goog.require('Blockly.FieldDropdown'); -goog.require('Blockly.FieldTextInput'); -goog.require('goog.userAgent'); - - -/** - * Class for a combination text + drop-down field. - * @param {string} text The initial content of the text field. - * @param {(!Array.>|!Function)} menuGenerator An array of - * options for a dropdown list, or a function which generates these options. - * @param {Function=} opt_validator An optional function that is called - * to validate any constraints on what the user entered. Takes the new - * text as an argument and returns the accepted text or null to abort - * the change. - * @param {RegExp=} opt_restrictor An optional regular expression to restrict - * typed text to. Text that doesn't match the restrictor will never show - * in the text field. - * @extends {Blockly.FieldTextInput} - * @constructor - */ -Blockly.FieldTextDropdown = function(text, menuGenerator, opt_validator, opt_restrictor) { - this.menuGenerator_ = menuGenerator; - Blockly.FieldDropdown.prototype.trimOptions_.call(this); - Blockly.FieldTextDropdown.superClass_.constructor.call(this, text, opt_validator, opt_restrictor); - this.addArgType('textdropdown'); -}; -goog.inherits(Blockly.FieldTextDropdown, Blockly.FieldTextInput); - -/** - * Construct a FieldTextDropdown from a JSON arg object, - * dereferencing any string table references. - * @param {!Object} element A JSON object with options. - * @returns {!Blockly.FieldTextDropdown} The new field instance. - * @package - * @nocollapse - */ -Blockly.FieldTextDropdown.fromJson = function(element) { - var field = - new Blockly.FieldTextDropdown(element['text'], element['options']); - if (typeof element['spellcheck'] == 'boolean') { - field.setSpellcheck(element['spellcheck']); - } - return field; -}; - -/** - * Install this text drop-down field on a block. - */ -Blockly.FieldTextDropdown.prototype.init = function() { - if (this.fieldGroup_) { - // Text input + dropdown has already been initialized once. - return; - } - Blockly.FieldTextDropdown.superClass_.init.call(this); - // Add dropdown arrow: "option ▾" (LTR) or "▾ אופציה" (RTL) - // Positioned on render, after text size is calculated. - if (!this.arrow_) { - /** @type {Number} */ - this.arrowSize_ = 12; - /** @type {Number} */ - this.arrowX_ = 0; - /** @type {Number} */ - this.arrowY_ = 11; - this.arrow_ = Blockly.utils.createSvgElement('image', - { - 'height': this.arrowSize_ + 'px', - 'width': this.arrowSize_ + 'px' - }); - this.arrow_.setAttributeNS('http://www.w3.org/1999/xlink', - 'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'dropdown-arrow-dark.svg'); - this.arrow_.style.cursor = 'pointer'; - this.fieldGroup_.appendChild(this.arrow_); - this.mouseUpWrapper_ = - Blockly.bindEvent_(this.arrow_, 'mouseup', this, this.showDropdown_); - } - // Prevent the drop-down handler from changing the field colour on open. - this.disableColourChange_ = true; -}; - -/** - * Close the input widget if this input is being deleted. - */ -Blockly.FieldTextDropdown.prototype.dispose = function() { - if (this.mouseUpWrapper_) { - Blockly.unbindEvent_(this.mouseUpWrapper_); - this.mouseUpWrapper_ = null; - Blockly.Touch.clearTouchIdentifier(); - } - Blockly.FieldTextDropdown.superClass_.dispose.call(this); -}; - -/** - * If the drop-down isn't open, show the text editor. - */ -Blockly.FieldTextDropdown.prototype.showEditor_ = function() { - if (!this.dropDownOpen_) { - Blockly.FieldTextDropdown.superClass_.showEditor_.call(this, null, null, - true, function() { - // When the drop-down arrow is clicked, hide text editor and show drop-down. - Blockly.WidgetDiv.hide(); - this.showDropdown_(); - Blockly.Touch.clearTouchIdentifier(); - }); - } -}; - -/** - * Return a list of the options for this dropdown. - * See: Blockly.FieldDropDown.prototype.getOptions_. - * @return {!Array.>} Array of option tuples: - * (human-readable text, language-neutral name). - * @private - */ -Blockly.FieldTextDropdown.prototype.getOptions_ = Blockly.FieldDropdown.prototype.getOptions_; - -/** - * Position a drop-down arrow at the appropriate location at render-time. - * See: Blockly.FieldDropDown.prototype.positionArrow. - * @param {number} x X position the arrow is being rendered at, in px. - * @return {number} Amount of space the arrow is taking up, in px. - */ -Blockly.FieldTextDropdown.prototype.positionArrow = Blockly.FieldDropdown.prototype.positionArrow; - -/** - * Create the dropdown menu. - * @private - */ -Blockly.FieldTextDropdown.prototype.showDropdown_ = Blockly.FieldDropdown.prototype.showEditor_; - -/** - * Callback when the drop-down menu is hidden. - */ -Blockly.FieldTextDropdown.prototype.onHide = Blockly.FieldDropdown.prototype.onHide; - -Blockly.Field.register('field_textdropdown', Blockly.FieldTextDropdown); diff --git a/core/field_textinput.js b/core/field_textinput.js deleted file mode 100644 index 8ecf563024..0000000000 --- a/core/field_textinput.js +++ /dev/null @@ -1,675 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Text input field. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.FieldTextInput'); - -goog.require('Blockly.BlockSvg.render'); -goog.require('Blockly.Colours'); -goog.require('Blockly.Field'); -goog.require('Blockly.Msg'); -goog.require('Blockly.scratchBlocksUtils'); -goog.require('Blockly.utils'); - -goog.require('goog.asserts'); -goog.require('goog.dom'); -goog.require('goog.dom.TagName'); -goog.require('goog.userAgent'); - - -/** - * Class for an editable text field. - * @param {string} text The initial content of the field. - * @param {Function=} opt_validator An optional function that is called - * to validate any constraints on what the user entered. Takes the new - * text as an argument and returns either the accepted text, a replacement - * text, or null to abort the change. - * @param {RegExp=} opt_restrictor An optional regular expression to restrict - * typed text to. Text that doesn't match the restrictor will never show - * in the text field. - * @extends {Blockly.Field} - * @constructor - */ -Blockly.FieldTextInput = function(text, opt_validator, opt_restrictor) { - Blockly.FieldTextInput.superClass_.constructor.call(this, text, - opt_validator); - this.setRestrictor(opt_restrictor); - this.addArgType('text'); -}; -goog.inherits(Blockly.FieldTextInput, Blockly.Field); - -/** - * Construct a FieldTextInput from a JSON arg object, - * dereferencing any string table references. - * @param {!Object} options A JSON object with options (text, class, and - * spellcheck). - * @returns {!Blockly.FieldTextInput} The new field instance. - * @package - * @nocollapse - */ -Blockly.FieldTextInput.fromJson = function(options) { - var text = Blockly.utils.replaceMessageReferences(options['text']); - var field = new Blockly.FieldTextInput(text, options['class']); - if (typeof options['spellcheck'] === 'boolean') { - field.setSpellcheck(options['spellcheck']); - } - return field; -}; - -/** - * Length of animations in seconds. - */ -Blockly.FieldTextInput.ANIMATION_TIME = 0.25; - -/** - * Padding to use for text measurement for the field during editing, in px. - */ -Blockly.FieldTextInput.TEXT_MEASURE_PADDING_MAGIC = 45; - -/** - * The HTML input element for the user to type, or null if no FieldTextInput - * editor is currently open. - * @type {HTMLInputElement} - * @private - */ -Blockly.FieldTextInput.htmlInput_ = null; - -/** - * Mouse cursor style when over the hotspot that initiates the editor. - */ -Blockly.FieldTextInput.prototype.CURSOR = 'text'; - -/** - * Allow browser to spellcheck this field. - * @private - */ -Blockly.FieldTextInput.prototype.spellcheck_ = true; - -/** - * Install this text field on a block. - */ -Blockly.FieldTextInput.prototype.init = function() { - if (this.fieldGroup_) { - // Field has already been initialized once. - return; - } - - var notInShadow = !this.sourceBlock_.isShadow(); - - if (notInShadow) { - this.className_ += ' blocklyEditableLabel'; - } - - Blockly.FieldTextInput.superClass_.init.call(this); - - // If not in a shadow block, draw a box. - if (notInShadow) { - this.box_ = Blockly.utils.createSvgElement('rect', - { - 'x': 0, - 'y': 0, - 'width': this.size_.width, - 'height': this.size_.height, - 'fill': this.sourceBlock_.getColourTertiary() - } - ); - this.fieldGroup_.insertBefore(this.box_, this.textElement_); - } -}; - -/** - * Close the input widget if this input is being deleted. - */ -Blockly.FieldTextInput.prototype.dispose = function() { - Blockly.WidgetDiv.hideIfOwner(this); - Blockly.FieldTextInput.superClass_.dispose.call(this); -}; - -/** - * Set the value of this field. - * @param {?string} newValue New value. - * @override - */ -Blockly.FieldTextInput.prototype.setValue = function(newValue) { - if (newValue === null) { - return; // No change if null. - } - if (this.sourceBlock_) { - var validated = this.callValidator(newValue); - // If the new value is invalid, validation returns null. - // In this case we still want to display the illegal result. - if (validated !== null) { - newValue = validated; - } - } - Blockly.Field.prototype.setValue.call(this, newValue); -}; - -/** - * Set the text in this field and fire a change event. - * @param {*} newText New text. - */ -Blockly.FieldTextInput.prototype.setText = function(newText) { - if (newText === null) { - // No change if null. - return; - } - newText = String(newText); - if (newText === this.text_) { - // No change. - return; - } - if (this.sourceBlock_ && Blockly.Events.isEnabled()) { - Blockly.Events.fire(new Blockly.Events.BlockChange( - this.sourceBlock_, 'field', this.name, this.text_, newText)); - } - Blockly.Field.prototype.setText.call(this, newText); -}; - -/** - * Set whether this field is spellchecked by the browser. - * @param {boolean} check True if checked. - */ -Blockly.FieldTextInput.prototype.setSpellcheck = function(check) { - this.spellcheck_ = check; -}; - -/** - * Set the restrictor regex for this text input. - * Text that doesn't match the restrictor will never show in the text field. - * @param {?RegExp} restrictor Regular expression to restrict text. - */ -Blockly.FieldTextInput.prototype.setRestrictor = function(restrictor) { - this.restrictor_ = restrictor; -}; - -/** - * Show the inline free-text editor on top of the text. - * @param {boolean=} opt_quietInput True if editor should be created without - * focus. Defaults to false. - * @param {boolean=} opt_readOnly True if editor should be created with HTML - * input set to read-only, to prevent virtual keyboards. - * @param {boolean=} opt_withArrow True to show drop-down arrow in text editor. - * @param {Function=} opt_arrowCallback Callback for when drop-down arrow clicked. - * @private - */ -Blockly.FieldTextInput.prototype.showEditor_ = function( - opt_quietInput, opt_readOnly, opt_withArrow, opt_arrowCallback) { - this.workspace_ = this.sourceBlock_.workspace; - var quietInput = opt_quietInput || false; - var readOnly = opt_readOnly || false; - Blockly.WidgetDiv.show(this, this.sourceBlock_.RTL, - this.widgetDispose_(), this.widgetDisposeAnimationFinished_(), - Blockly.FieldTextInput.ANIMATION_TIME); - var div = Blockly.WidgetDiv.DIV; - // Apply text-input-specific fixed CSS - div.className += ' fieldTextInput'; - // Create the input. - var htmlInput = - goog.dom.createDom(goog.dom.TagName.INPUT, 'blocklyHtmlInput'); - htmlInput.setAttribute('spellcheck', this.spellcheck_); - if (readOnly) { - htmlInput.setAttribute('readonly', 'true'); - } - /** @type {!HTMLInputElement} */ - Blockly.FieldTextInput.htmlInput_ = htmlInput; - div.appendChild(htmlInput); - - if (opt_withArrow) { - // Move text in input to account for displayed drop-down arrow. - if (this.sourceBlock_.RTL) { - htmlInput.style.paddingLeft = (this.arrowSize_ + Blockly.BlockSvg.DROPDOWN_ARROW_PADDING) + 'px'; - } else { - htmlInput.style.paddingRight = (this.arrowSize_ + Blockly.BlockSvg.DROPDOWN_ARROW_PADDING) + 'px'; - } - // Create the arrow. - var dropDownArrow = - goog.dom.createDom(goog.dom.TagName.IMG, 'blocklyTextDropDownArrow'); - dropDownArrow.setAttribute('src', - Blockly.mainWorkspace.options.pathToMedia + 'dropdown-arrow-dark.svg'); - dropDownArrow.style.width = this.arrowSize_ + 'px'; - dropDownArrow.style.height = this.arrowSize_ + 'px'; - dropDownArrow.style.top = this.arrowY_ + 'px'; - dropDownArrow.style.cursor = 'pointer'; - // Magic number for positioning the drop-down arrow on top of the text editor. - var dropdownArrowMagic = '11px'; - if (this.sourceBlock_.RTL) { - dropDownArrow.style.left = dropdownArrowMagic; - } else { - dropDownArrow.style.right = dropdownArrowMagic; - } - if (opt_arrowCallback) { - htmlInput.dropDownArrowMouseWrapper_ = Blockly.bindEvent_(dropDownArrow, - 'mousedown', this, opt_arrowCallback); - } - div.appendChild(dropDownArrow); - } - - htmlInput.value = htmlInput.defaultValue = this.text_; - htmlInput.oldValue_ = null; - this.validate_(); - this.resizeEditor_(); - if (!quietInput) { - htmlInput.focus(); - htmlInput.select(); - // For iOS only - htmlInput.setSelectionRange(0, 99999); - } - - this.bindEvents_(htmlInput, quietInput || readOnly); - - // Add animation transition properties - var transitionProperties = 'box-shadow ' + Blockly.FieldTextInput.ANIMATION_TIME + 's'; - if (Blockly.BlockSvg.FIELD_TEXTINPUT_ANIMATE_POSITIONING) { - div.style.transition += ',padding ' + Blockly.FieldTextInput.ANIMATION_TIME + 's,' + - 'width ' + Blockly.FieldTextInput.ANIMATION_TIME + 's,' + - 'height ' + Blockly.FieldTextInput.ANIMATION_TIME + 's,' + - 'margin-left ' + Blockly.FieldTextInput.ANIMATION_TIME + 's'; - } - div.style.transition = transitionProperties; - htmlInput.style.transition = 'font-size ' + Blockly.FieldTextInput.ANIMATION_TIME + 's'; - // The animated properties themselves - htmlInput.style.fontSize = Blockly.BlockSvg.FIELD_TEXTINPUT_FONTSIZE_FINAL + 'pt'; - div.style.boxShadow = '0px 0px 0px 4px ' + Blockly.Colours.fieldShadow; -}; - -/** - * Bind handlers for user input on this field and size changes on the workspace. - * @param {!HTMLInputElement} htmlInput The htmlInput created in showEditor, to - * which event handlers will be bound. - * @param {boolean} bindGlobalKeypress Whether to bind a keypress listener to enable - * keyboard editing without focusing the field. - * @private - */ -Blockly.FieldTextInput.prototype.bindEvents_ = function( - htmlInput, bindGlobalKeypress) { - // Bind to keydown -- trap Enter without IME and Esc to hide. - htmlInput.onKeyDownWrapper_ = - Blockly.bindEventWithChecks_(htmlInput, 'keydown', this, - this.onHtmlInputKeyDown_); - // Bind to keyup -- trap Enter; resize after every keystroke. - htmlInput.onKeyUpWrapper_ = - Blockly.bindEventWithChecks_(htmlInput, 'keyup', this, - this.onHtmlInputChange_); - // Bind to keyPress -- repeatedly resize when holding down a key. - htmlInput.onKeyPressWrapper_ = - Blockly.bindEventWithChecks_(htmlInput, 'keypress', this, - this.onHtmlInputChange_); - // For modern browsers (IE 9+, Chrome, Firefox, etc.) that support the - // DOM input event, also trigger onHtmlInputChange_ then. The input event - // is triggered on keypress but after the value of the text input - // has updated, allowing us to resize the block at that time. - htmlInput.onInputWrapper_ = - Blockly.bindEvent_(htmlInput, 'input', this, this.onHtmlInputChange_); - htmlInput.onWorkspaceChangeWrapper_ = this.resizeEditor_.bind(this); - this.workspace_.addChangeListener(htmlInput.onWorkspaceChangeWrapper_); - - if (bindGlobalKeypress) { - htmlInput.onDocumentKeyDownWrapper_ = - Blockly.bindEventWithChecks_(document, 'keydown', this, - this.onDocumentKeyDown_); - } -}; - -/** - * Unbind handlers for user input and workspace size changes. - * @param {!HTMLInputElement} htmlInput The html for this text input. - * @private - */ -Blockly.FieldTextInput.prototype.unbindEvents_ = function(htmlInput) { - Blockly.unbindEvent_(htmlInput.onKeyDownWrapper_); - Blockly.unbindEvent_(htmlInput.onKeyUpWrapper_); - Blockly.unbindEvent_(htmlInput.onKeyPressWrapper_); - Blockly.unbindEvent_(htmlInput.onInputWrapper_); - this.workspace_.removeChangeListener( - htmlInput.onWorkspaceChangeWrapper_); - - // Remove document handler only if it was added (e.g. in quiet mode) - if (htmlInput.onDocumentKeyDownWrapper_) { - Blockly.unbindEvent_(htmlInput.onDocumentKeyDownWrapper_); - } -}; - -/** - * Handle key down to the editor. - * @param {!Event} e Keyboard event. - * @private - */ -Blockly.FieldTextInput.prototype.onHtmlInputKeyDown_ = function(e) { - var htmlInput = Blockly.FieldTextInput.htmlInput_; - var tabKey = 9, enterKey = 13, escKey = 27; - if (e.keyCode == enterKey) { - Blockly.WidgetDiv.hide(); - Blockly.DropDownDiv.hideWithoutAnimation(); - } else if (e.keyCode == escKey) { - htmlInput.value = htmlInput.defaultValue; - Blockly.WidgetDiv.hide(); - Blockly.DropDownDiv.hideWithoutAnimation(); - } else if (e.keyCode == tabKey) { - Blockly.WidgetDiv.hide(); - Blockly.DropDownDiv.hideWithoutAnimation(); - this.sourceBlock_.tab(this, !e.shiftKey); - e.preventDefault(); - } -}; - -Blockly.FieldTextInput.prototype.onDocumentKeyDown_ = function(e) { - var htmlInput = Blockly.FieldTextInput.htmlInput_; - var targetMatches = e.target === htmlInput; - var targetIsInput = e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA'; - if (targetMatches || !targetIsInput) { // Ignore keys into other inputs - htmlInput.removeAttribute('readonly'); - htmlInput.value = ''; // Reset the input, new value is picked up by input keypress - htmlInput.focus(); - Blockly.unbindEvent_(htmlInput.onDocumentKeyDownWrapper_); - htmlInput.onDocumentKeyDownWrapper_ = null; - } -}; - -/** - * Key codes that are whitelisted from the restrictor. - * These are only needed and used on Gecko (Firefox). - * See: https://github.com/LLK/scratch-blocks/issues/503. - */ -Blockly.FieldTextInput.GECKO_KEYCODE_WHITELIST = [ - 97, // Select all, META-A. - 99, // Copy, META-C. - 118, // Paste, META-V. - 120 // Cut, META-X. -]; - -/** - * Handle a change to the editor. - * @param {!Event} e Keyboard event. - * @private - */ -Blockly.FieldTextInput.prototype.onHtmlInputChange_ = function(e) { - // Check if the key matches the restrictor. - if (e.type === 'keypress' && this.restrictor_) { - var keyCode; - var isWhitelisted = false; - if (goog.userAgent.GECKO) { - // e.keyCode is not available in Gecko. - keyCode = e.charCode; - // Gecko reports control characters (e.g., left, right, copy, paste) - // in the key event - whitelist these from being restricted. - // < 32 and 127 (delete) are control characters. - // See: http://www.theasciicode.com.ar/ascii-control-characters/delete-ascii-code-127.html - if (keyCode < 32 || keyCode == 127) { - isWhitelisted = true; - } else if (e.metaKey || e.ctrlKey) { - // For combos (ctrl-v, ctrl-c, etc.), Gecko reports the ASCII letter - // and the metaKey/ctrlKey flags. - isWhitelisted = Blockly.FieldTextInput.GECKO_KEYCODE_WHITELIST.indexOf(keyCode) > -1; - } - } else { - keyCode = e.keyCode; - } - var char = String.fromCharCode(keyCode); - if (!isWhitelisted && !this.restrictor_.test(char) && e.preventDefault) { - // Failed to pass restrictor. - e.preventDefault(); - return; - } - } - var htmlInput = Blockly.FieldTextInput.htmlInput_; - // Update source block. - var text = htmlInput.value; - if (text !== htmlInput.oldValue_) { - htmlInput.oldValue_ = text; - this.setText(text); - this.validate_(); - } else if (goog.userAgent.WEBKIT) { - // Cursor key. Render the source block to show the caret moving. - // Chrome only (version 26, OS X). - this.sourceBlock_.render(); - } - this.resizeEditor_(); -}; - -/** - * Check to see if the contents of the editor validates. - * Style the editor accordingly. - * @private - */ -Blockly.FieldTextInput.prototype.validate_ = function() { - var valid = true; - goog.asserts.assertObject(Blockly.FieldTextInput.htmlInput_); - var htmlInput = Blockly.FieldTextInput.htmlInput_; - if (this.sourceBlock_) { - valid = this.callValidator(htmlInput.value); - } - if (valid === null) { - Blockly.utils.addClass(htmlInput, 'blocklyInvalidInput'); - } else { - Blockly.utils.removeClass(htmlInput, 'blocklyInvalidInput'); - } -}; - -/** - * Resize the editor and the underlying block to fit the text. - * @private - */ -Blockly.FieldTextInput.prototype.resizeEditor_ = function() { - var scale = this.sourceBlock_.workspace.scale; - var div = Blockly.WidgetDiv.DIV; - - var initialWidth; - if (this.sourceBlock_.isShadow()) { - initialWidth = this.sourceBlock_.getHeightWidth().width * scale; - } else { - initialWidth = this.size_.width * scale; - } - - var width; - if (Blockly.BlockSvg.FIELD_TEXTINPUT_EXPAND_PAST_TRUNCATION) { - // Resize the box based on the measured width of the text, pre-truncation - var textWidth = Blockly.scratchBlocksUtils.measureText( - Blockly.FieldTextInput.htmlInput_.style.fontSize, - Blockly.FieldTextInput.htmlInput_.style.fontFamily, - Blockly.FieldTextInput.htmlInput_.style.fontWeight, - Blockly.FieldTextInput.htmlInput_.value - ); - // Size drawn in the canvas needs padding and scaling - textWidth += Blockly.FieldTextInput.TEXT_MEASURE_PADDING_MAGIC; - textWidth *= scale; - width = textWidth; - } else { - // Set width to (truncated) block size. - width = initialWidth; - } - // The width must be at least FIELD_WIDTH and at most FIELD_WIDTH_MAX_EDIT - width = Math.max(width, Blockly.BlockSvg.FIELD_WIDTH_MIN_EDIT * scale); - width = Math.min(width, Blockly.BlockSvg.FIELD_WIDTH_MAX_EDIT * scale); - // Add 1px to width and height to account for border (pre-scale) - div.style.width = (width / scale + 1) + 'px'; - div.style.height = (Blockly.BlockSvg.FIELD_HEIGHT_MAX_EDIT + 1) + 'px'; - div.style.transform = 'scale(' + scale + ')'; - - // Use margin-left to animate repositioning of the box (value is unscaled). - // This is the difference between the default position and the positioning - // after growing the box. - div.style.marginLeft = -0.5 * (width - initialWidth) + 'px'; - - // Add 0.5px to account for slight difference between SVG and CSS border - var borderRadius = this.getBorderRadius() + 0.5; - div.style.borderRadius = borderRadius + 'px'; - Blockly.FieldTextInput.htmlInput_.style.borderRadius = borderRadius + 'px'; - // Pull stroke colour from the existing shadow block - var strokeColour = this.sourceBlock_.getColourTertiary(); - div.style.borderColor = strokeColour; - - var xy = this.getAbsoluteXY_(); - // Account for border width, post-scale - xy.x -= scale / 2; - xy.y -= scale / 2; - // In RTL mode block fields and LTR input fields the left edge moves, - // whereas the right edge is fixed. Reposition the editor. - if (this.sourceBlock_.RTL) { - xy.x += width; - xy.x -= div.offsetWidth * scale; - xy.x += 1 * scale; - } - // Shift by a few pixels to line up exactly. - xy.y += 1 * scale; - if (goog.userAgent.GECKO && Blockly.WidgetDiv.DIV.style.top) { - // Firefox mis-reports the location of the border by a pixel - // once the WidgetDiv is moved into position. - xy.x += 2 * scale; - xy.y += 1 * scale; - } - if (goog.userAgent.WEBKIT) { - xy.y -= 1 * scale; - } - // Finally, set the actual style - div.style.left = xy.x + 'px'; - div.style.top = xy.y + 'px'; -}; - -/** - * Border radius for drawing this field, called when rendering the owning shadow block. - * @return {Number} Border radius in px. -*/ -Blockly.FieldTextInput.prototype.getBorderRadius = function() { - if (this.sourceBlock_.getOutputShape() == Blockly.OUTPUT_SHAPE_ROUND) { - return Blockly.BlockSvg.NUMBER_FIELD_CORNER_RADIUS; - } - return Blockly.BlockSvg.TEXT_FIELD_CORNER_RADIUS; -}; - -/** - * Close the editor, save the results, and start animating the disposal of elements. - * @return {!Function} Closure to call on destruction of the WidgetDiv. - * @private - */ -Blockly.FieldTextInput.prototype.widgetDispose_ = function() { - var thisField = this; - return function() { - var div = Blockly.WidgetDiv.DIV; - var htmlInput = Blockly.FieldTextInput.htmlInput_; - // Save the edit (if it validates). - thisField.maybeSaveEdit_(); - - thisField.unbindEvents_(htmlInput); - if (htmlInput.dropDownArrowMouseWrapper_) { - Blockly.unbindEvent_(htmlInput.dropDownArrowMouseWrapper_); - } - Blockly.Events.setGroup(false); - - // Animation of disposal - htmlInput.style.fontSize = Blockly.BlockSvg.FIELD_TEXTINPUT_FONTSIZE_INITIAL + 'pt'; - div.style.boxShadow = ''; - // Resize to actual size of final source block. - if (thisField.sourceBlock_) { - if (thisField.sourceBlock_.isShadow()) { - var size = thisField.sourceBlock_.getHeightWidth(); - div.style.width = (size.width + 1) + 'px'; - div.style.height = (size.height + 1) + 'px'; - } else { - div.style.width = (thisField.size_.width + 1) + 'px'; - div.style.height = (Blockly.BlockSvg.FIELD_HEIGHT_MAX_EDIT + 1) + 'px'; - } - } - div.style.marginLeft = 0; - }; -}; - -/** - * Final disposal of the text field's elements and properties. - * @return {!Function} Closure to call on finish animation of the WidgetDiv. - * @private - */ -Blockly.FieldTextInput.prototype.widgetDisposeAnimationFinished_ = function() { - return function() { - // Delete style properties. - var style = Blockly.WidgetDiv.DIV.style; - style.width = 'auto'; - style.height = 'auto'; - style.fontSize = ''; - // Reset class - Blockly.WidgetDiv.DIV.className = 'blocklyWidgetDiv'; - // Remove all styles - Blockly.WidgetDiv.DIV.removeAttribute('style'); - Blockly.FieldTextInput.htmlInput_.style.transition = ''; - Blockly.FieldTextInput.htmlInput_ = null; - }; -}; - -Blockly.FieldTextInput.prototype.maybeSaveEdit_ = function() { - var htmlInput = Blockly.FieldTextInput.htmlInput_; - // Save the edit (if it validates). - var text = htmlInput.value; - if (this.sourceBlock_) { - var text1 = this.callValidator(text); - if (text1 === null) { - // Invalid edit. - text = htmlInput.defaultValue; - } else { - // Validation function has changed the text. - text = text1; - if (this.onFinishEditing_) { - this.onFinishEditing_(text); - } - } - } - this.setText(text); - this.sourceBlock_.rendered && this.sourceBlock_.render(); -}; - -/** - * Ensure that only a number may be entered. - * @param {string} text The user's text. - * @return {?string} A string representing a valid number, or null if invalid. - */ -Blockly.FieldTextInput.numberValidator = function(text) { - console.warn('Blockly.FieldTextInput.numberValidator is deprecated. ' + - 'Use Blockly.FieldNumber instead.'); - if (text === null) { - return null; - } - text = String(text); - // TODO: Handle cases like 'ten', '1.203,14', etc. - // 'O' is sometimes mistaken for '0' by inexperienced users. - text = text.replace(/O/ig, '0'); - // Strip out thousands separators. - text = text.replace(/,/g, ''); - var n = parseFloat(text || 0); - return isNaN(n) ? null : String(n); -}; - -/** - * Ensure that only a nonnegative integer may be entered. - * @param {string} text The user's text. - * @return {?string} A string representing a valid int, or null if invalid. - */ -Blockly.FieldTextInput.nonnegativeIntegerValidator = function(text) { - var n = Blockly.FieldTextInput.numberValidator(text); - if (n) { - n = String(Math.max(0, Math.floor(n))); - } - return n; -}; - -Blockly.Field.register('field_input', Blockly.FieldTextInput); diff --git a/core/field_textinput_removable.js b/core/field_textinput_removable.js deleted file mode 100644 index a37e071845..0000000000 --- a/core/field_textinput_removable.js +++ /dev/null @@ -1,105 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Massachusetts Institute of Technology - * All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Text input field with floating "remove" button. - * @author pkaplan@media.mit.edu (Paul Kaplan) - */ -'use strict'; - -goog.provide('Blockly.FieldTextInputRemovable'); - -goog.require('Blockly.BlockSvg.render'); -goog.require('Blockly.Colours'); -goog.require('Blockly.FieldTextInput'); -goog.require('Blockly.Msg'); -goog.require('Blockly.utils'); -goog.require('goog.dom'); -goog.require('goog.dom.TagName'); - -/** - * Class for an editable text field displaying a deletion icon when selected. - * @param {string} text The initial content of the field. - * @param {Function=} opt_validator An optional function that is called - * to validate any constraints on what the user entered. Takes the new - * text as an argument and returns either the accepted text, a replacement - * text, or null to abort the change. - * @param {RegExp=} opt_restrictor An optional regular expression to restrict - * typed text to. Text that doesn't match the restrictor will never show - * in the text field. - * @extends {Blockly.FieldTextInput} - * @constructor - */ -Blockly.FieldTextInputRemovable = function(text, opt_validator, opt_restrictor) { - Blockly.FieldTextInputRemovable.superClass_.constructor.call(this, text, - opt_validator, opt_restrictor); -}; -goog.inherits(Blockly.FieldTextInputRemovable, Blockly.FieldTextInput); - -/** - * Show the inline free-text editor on top of the text with the remove button. - * @private - */ -Blockly.FieldTextInputRemovable.prototype.showEditor_ = function() { - Blockly.FieldTextInputRemovable.superClass_.showEditor_.call(this); - - var div = Blockly.WidgetDiv.DIV; - div.className += ' removableTextInput'; - var removeButton = - goog.dom.createDom(goog.dom.TagName.IMG, 'blocklyTextRemoveIcon'); - removeButton.setAttribute('src', - Blockly.mainWorkspace.options.pathToMedia + 'icons/remove.svg'); - this.removeButtonMouseWrapper_ = Blockly.bindEvent_(removeButton, - 'mousedown', this, this.removeCallback_); - div.appendChild(removeButton); -}; - -/** - * Function to call when remove button is called. Checks for removeFieldCallback - * on sourceBlock and calls it if possible. - * @private - */ -Blockly.FieldTextInputRemovable.prototype.removeCallback_ = function() { - if (this.sourceBlock_ && this.sourceBlock_.removeFieldCallback) { - this.sourceBlock_.removeFieldCallback(this); - } else { - console.warn('Expected a source block with removeFieldCallback'); - } -}; - -/** - * Helper function to construct a FieldTextInputRemovable from a JSON arg object, - * dereferencing any string table references. - * @param {!Object} options A JSON object with options (text, class, and - * spellcheck). - * @returns {!Blockly.FieldTextInputRemovable} The new text input. - * @public - */ -Blockly.FieldTextInputRemovable.fromJson = function(options) { - var text = Blockly.utils.replaceMessageReferences(options['text']); - var field = new Blockly.FieldTextInputRemovable(text, options['class']); - if (typeof options['spellcheck'] == 'boolean') { - field.setSpellcheck(options['spellcheck']); - } - return field; -}; - -Blockly.Field.register( - 'field_input_removable', Blockly.FieldTextInputRemovable); diff --git a/core/field_variable.js b/core/field_variable.js deleted file mode 100644 index c188482bd6..0000000000 --- a/core/field_variable.js +++ /dev/null @@ -1,385 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Variable input field. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.FieldVariable'); - -goog.require('Blockly.FieldDropdown'); -goog.require('Blockly.Msg'); -goog.require('Blockly.VariableModel'); -goog.require('Blockly.Variables'); -goog.require('goog.asserts'); -goog.require('goog.string'); - - -/** - * Class for a variable's dropdown field. - * @param {?string} varname The default name for the variable. If null, - * a unique variable name will be generated. - * @param {Function=} opt_validator A function that is executed when a new - * option is selected. Its sole argument is the new option value. - * @param {Array.} opt_variableTypes A list of the types of variables to - * include in the dropdown. - * @extends {Blockly.FieldDropdown} - * @constructor - */ -Blockly.FieldVariable = function(varname, opt_validator, opt_variableTypes) { - // The FieldDropdown constructor would call setValue, which might create a - // spurious variable. Just do the relevant parts of the constructor. - this.menuGenerator_ = Blockly.FieldVariable.dropdownCreate; - this.size_ = new goog.math.Size(Blockly.BlockSvg.FIELD_WIDTH, - Blockly.BlockSvg.FIELD_HEIGHT); - this.setValidator(opt_validator); - // TODO (blockly #1499): Add opt_default_type to match default value. - // If not set, ''. - this.defaultVariableName = (varname || ''); - var hasSingleVarType = opt_variableTypes && (opt_variableTypes.length == 1); - this.defaultType_ = hasSingleVarType ? opt_variableTypes[0] : ''; - this.variableTypes = opt_variableTypes; - this.addArgType('variable'); - - this.value_ = null; -}; -goog.inherits(Blockly.FieldVariable, Blockly.FieldDropdown); - -/** - * Construct a FieldVariable from a JSON arg object, - * dereferencing any string table references. - * @param {!Object} options A JSON object with options (variable, - * variableTypes, and defaultType). - * @returns {!Blockly.FieldVariable} The new field instance. - * @package - * @nocollapse - */ -Blockly.FieldVariable.fromJson = function(options) { - var varname = Blockly.utils.replaceMessageReferences(options['variable']); - var variableTypes = options['variableTypes']; - return new Blockly.FieldVariable(varname, null, variableTypes); -}; - -/** - * Initialize everything needed to render this field. This includes making sure - * that the field's value is valid. - * @public - */ -Blockly.FieldVariable.prototype.init = function() { - if (this.fieldGroup_) { - // Dropdown has already been initialized once. - return; - } - Blockly.FieldVariable.superClass_.init.call(this); - - // TODO (blockly #1010): Change from init/initModel to initView/initModel - this.initModel(); -}; - -/** - * Initialize the model for this field if it has not already been initialized. - * If the value has not been set to a variable by the first render, we make up a - * variable rather than let the value be invalid. - * @package - */ -Blockly.FieldVariable.prototype.initModel = function() { - if (this.variable_) { - return; // Initialization already happened. - } - this.workspace_ = this.sourceBlock_.workspace; - // Initialize this field if it's in a broadcast block in the flyout - var variable = this.initFlyoutBroadcast_(this.workspace_); - if (!variable) { - var variable = Blockly.Variables.getOrCreateVariablePackage( - this.workspace_, null, this.defaultVariableName, this.defaultType_); - } - // Don't fire a change event for this setValue. It would have null as the - // old value, which is not valid. - Blockly.Events.disable(); - try { - this.setValue(variable.getId()); - } finally { - Blockly.Events.enable(); - } -}; - -/** - * Initialize broadcast blocks in the flyout. - * Implicit deletion of broadcast messages from the scratch vm may cause - * broadcast blocks in the flyout to change which variable they display as the - * selected option when the workspace is refreshed. - * Re-sort the broadcast messages by name, and set the field value to the id - * of the variable that comes first in sorted order. - * @param {!Blockly.Workspace} workspace The flyout workspace containing the - * broadcast block. - * @return {string} The variable of type 'broadcast_msg' that comes - * first in sorted order. - */ -Blockly.FieldVariable.prototype.initFlyoutBroadcast_ = function(workspace) { - // Using shorter name for this constant - var broadcastMsgType = Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE; - var broadcastVars = workspace.getVariablesOfType(broadcastMsgType); - if(workspace.isFlyout && this.defaultType_ == broadcastMsgType && - broadcastVars.length != 0) { - broadcastVars.sort(Blockly.VariableModel.compareByName); - return broadcastVars[0]; - } -}; - -/** - * Dispose of this field. - * @public - */ -Blockly.FieldVariable.dispose = function() { - Blockly.FieldVariable.superClass_.dispose.call(this); - this.workspace_ = null; - this.variableMap_ = null; -}; - -/** - * Attach this field to a block. - * @param {!Blockly.Block} block The block containing this field. - */ -Blockly.FieldVariable.prototype.setSourceBlock = function(block) { - goog.asserts.assert(!block.isShadow(), - 'Variable fields are not allowed to exist on shadow blocks.'); - Blockly.FieldVariable.superClass_.setSourceBlock.call(this, block); -}; - -/** - * Get the variable's ID. - * @return {string} Current variable's ID. - */ -Blockly.FieldVariable.prototype.getValue = function() { - return this.variable_ ? this.variable_.getId() : null; -}; - -/** - * Get the text from this field, which is the selected variable's name. - * @return {string} The selected variable's name, or the empty string if no - * variable is selected. - */ -Blockly.FieldVariable.prototype.getText = function() { - return this.variable_ ? this.variable_.name : ''; -}; - -/** - * Get the variable model for the selected variable. - * Not guaranteed to be in the variable map on the workspace (e.g. if accessed - * after the variable has been deleted). - * @return {?Blockly.VariableModel} the selected variable, or null if none was - * selected. - * @package - */ -Blockly.FieldVariable.prototype.getVariable = function() { - return this.variable_; -}; - -/** - * Set the variable ID. - * @param {string} id New variable ID, which must reference an existing - * variable. - */ -Blockly.FieldVariable.prototype.setValue = function(id) { - var workspace = this.sourceBlock_.workspace; - var variable = Blockly.Variables.getVariable(workspace, id); - - if (!variable) { - throw new Error('Variable id doesn\'t point to a real variable! ID was ' + - id); - } - // Type checks! - var type = variable.type; - if (!this.typeIsAllowed_(type)) { - throw new Error('Variable type doesn\'t match this field! Type was ' + - type); - } - if (this.sourceBlock_ && Blockly.Events.isEnabled()) { - var oldValue = this.variable_ ? this.variable_.getId() : null; - Blockly.Events.fire(new Blockly.Events.BlockChange( - this.sourceBlock_, 'field', this.name, oldValue, id)); - } - this.variable_ = variable; - this.value_ = id; - this.setText(variable.name); -}; - -/** - * Check whether the given variable type is allowed on this field. - * @param {string} type The type to check. - * @return {boolean} True if the type is in the list of allowed types. - * @private - */ -Blockly.FieldVariable.prototype.typeIsAllowed_ = function(type) { - var typeList = this.getVariableTypes_(); - if (!typeList) { - return true; // If it's null, all types are valid. - } - for (var i = 0; i < typeList.length; i++) { - if (type == typeList[i]) { - return true; - } - } - return false; -}; - -/** - * Return a list of variable types to include in the dropdown. - * @return {!Array.} Array of variable types. - * @throws {Error} if variableTypes is an empty array. - * @private - */ -Blockly.FieldVariable.prototype.getVariableTypes_ = function() { - // TODO (#1513): Try to avoid calling this every time the field is edited. - var variableTypes = this.variableTypes; - if (variableTypes === null) { - // If variableTypes is null, return all variable types. - if (this.sourceBlock_) { - var workspace = this.sourceBlock_.workspace; - return workspace.getVariableTypes(); - } - } - variableTypes = variableTypes || ['']; - if (variableTypes.length == 0) { - // Throw an error if variableTypes is an empty list. - var name = this.getText(); - throw new Error('\'variableTypes\' of field variable ' + - name + ' was an empty list'); - } - return variableTypes; -}; - -/** - * Return a sorted list of variable names for variable dropdown menus. - * Include a special option at the end for creating a new variable name. - * @return {!Array.} Array of variable names. - * @this {Blockly.FieldVariable} - */ -Blockly.FieldVariable.dropdownCreate = function() { - if (!this.variable_) { - throw new Error('Tried to call dropdownCreate on a variable field with no' + - ' variable selected.'); - } - var variableModelList = []; - var name = this.getText(); - var workspace = null; - if (this.sourceBlock_) { - workspace = this.sourceBlock_.workspace; - } - if (workspace) { - var variableTypes = this.getVariableTypes_(); - var variableModelList = []; - // Get a copy of the list, so that adding rename and new variable options - // doesn't modify the workspace's list. - for (var i = 0; i < variableTypes.length; i++) { - var variableType = variableTypes[i]; - var variables = workspace.getVariablesOfType(variableType); - variableModelList = variableModelList.concat(variables); - - var potentialVarMap = workspace.getPotentialVariableMap(); - if (potentialVarMap) { - var potentialVars = potentialVarMap.getVariablesOfType(variableType); - variableModelList = variableModelList.concat(potentialVars); - } - } - } - variableModelList.sort(Blockly.VariableModel.compareByName); - - var options = []; - for (var i = 0; i < variableModelList.length; i++) { - // Set the uuid as the internal representation of the variable. - options[i] = [variableModelList[i].name, variableModelList[i].getId()]; - } - if (this.defaultType_ == Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE) { - options.unshift( - [Blockly.Msg.NEW_BROADCAST_MESSAGE, Blockly.NEW_BROADCAST_MESSAGE_ID]); - } else { - // Scalar variables and lists have the same backing action, but the option - // text is different. - if (this.defaultType_ == Blockly.LIST_VARIABLE_TYPE) { - var renameText = Blockly.Msg.RENAME_LIST; - var deleteText = Blockly.Msg.DELETE_LIST; - } else { - var renameText = Blockly.Msg.RENAME_VARIABLE; - var deleteText = Blockly.Msg.DELETE_VARIABLE; - } - options.push([renameText, Blockly.RENAME_VARIABLE_ID]); - if (deleteText) { - options.push( - [ - deleteText.replace('%1', name), - Blockly.DELETE_VARIABLE_ID - ]); - } - } - - return options; -}; - -/** - * Handle the selection of an item in the variable dropdown menu. - * Special case the 'Rename variable...', 'Delete variable...', - * and 'New message...' options. - * In the rename case, prompt the user for a new name. - * @param {!goog.ui.Menu} menu The Menu component clicked. - * @param {!goog.ui.MenuItem} menuItem The MenuItem selected within menu. - */ -Blockly.FieldVariable.prototype.onItemSelected = function(menu, menuItem) { - var id = menuItem.getValue(); - if (this.sourceBlock_ && this.sourceBlock_.workspace) { - var workspace = this.sourceBlock_.workspace; - if (id == Blockly.RENAME_VARIABLE_ID) { - // Rename variable. - Blockly.Variables.renameVariable(workspace, this.variable_); - return; - } else if (id == Blockly.DELETE_VARIABLE_ID) { - // Delete variable. - workspace.deleteVariableById(this.variable_.getId()); - return; - } else if (id == Blockly.NEW_BROADCAST_MESSAGE_ID) { - var thisField = this; - var updateField = function(varId) { - if (varId) { - thisField.setValue(varId); - } - }; - Blockly.Variables.createVariable(workspace, updateField, - Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE); - return; - } - - // TODO (blockly #1529): Call any validation function, and allow it to override. - } - this.setValue(id); -}; - -/** - * Overrides referencesVariables(), indicating this field refers to a variable. - * @return {boolean} True. - * @package - * @override - */ -Blockly.FieldVariable.prototype.referencesVariables = function() { - return true; -}; - -Blockly.Field.register('field_variable', Blockly.FieldVariable); diff --git a/core/field_variable_getter.js b/core/field_variable_getter.js deleted file mode 100644 index 93721289f3..0000000000 --- a/core/field_variable_getter.js +++ /dev/null @@ -1,185 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Variable getter field. Appears as a label but has a variable - * picker in the right-click menu. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.FieldVariableGetter'); - -goog.require('Blockly.Field'); - - -/** - * Class for a variable getter field. - * @param {string} text The initial content of the field. - * @param {string} name Optional CSS class for the field's text. - * @param {string} opt_varType The type of variable this field is associated with. - * @extends {Blockly.FieldLabel} - * @constructor - * - */ -Blockly.FieldVariableGetter = function(text, name, opt_varType) { - this.size_ = new goog.math.Size(Blockly.BlockSvg.FIELD_WIDTH, - Blockly.BlockSvg.FIELD_HEIGHT); - this.text_ = text; - - /** - * Maximum characters of text to display before adding an ellipsis. - * Same for strings and numbers. - * @type {number} - */ - this.maxDisplayLength = Blockly.BlockSvg.MAX_DISPLAY_LENGTH; - - this.name_ = name; - this.variableType_ = opt_varType ? opt_varType : ''; -}; -goog.inherits(Blockly.FieldVariableGetter, Blockly.Field); - -/** - * Construct a FieldVariableGetter from a JSON arg object, - * dereferencing any string table references. - * @param {!Object} options A JSON object with options (variable, - * variableTypes, and defaultType). - * @returns {!Blockly.FieldVariableGetter} The new field instance. - * @package - * @nocollapse - */ -Blockly.FieldVariableGetter.fromJson = function(options) { - var varname = Blockly.utils.replaceMessageReferences(options['text']); - return new Blockly.FieldVariableGetter(varname, options['name'], - options['class'], options['variableType']); -}; - -/** - * Editable fields usually show some sort of UI for the user to change them. - * This field should be serialized, but only edited programmatically. - * @type {boolean} - * @public - */ -Blockly.FieldVariableGetter.prototype.EDITABLE = false; - -/** - * Serializable fields are saved by the XML renderer, non-serializable fields - * are not. This field should be serialized, but only edited programmatically. - * @type {boolean} - * @public - */ -Blockly.FieldVariableGetter.prototype.SERIALIZABLE = true; - -/** - * Install this field on a block. - */ -Blockly.FieldVariableGetter.prototype.init = function() { - if (this.fieldGroup_) { - // Field has already been initialized once. - return; - } - Blockly.FieldVariableGetter.superClass_.init.call(this); - if (this.variable_) { - return; // Initialization already happened. - } - this.workspace_ = this.sourceBlock_.workspace; - var variable = Blockly.Variables.getOrCreateVariablePackage( - this.workspace_, null, this.text_, this.variableType_); - this.setValue(variable.getId()); -}; - -/** - * Get the variable's ID. - * @return {string} Current variable's ID. - */ -Blockly.FieldVariableGetter.prototype.getValue = function() { - return this.variable_ ? this.variable_.getId() : ''; -}; - -/** - * Get the text from this field. - * @return {string} Current text. - */ -Blockly.FieldVariableGetter.prototype.getText = function() { - return this.variable_ ? this.variable_.name : ''; -}; - -/** - * Get the variable model for the variable associated with this field. - * Not guaranteed to be in the variable map on the workspace (e.g. if accessed - * after the variable has been deleted). - * @return {?Blockly.VariableModel} the selected variable, or null if none was - * selected. - * @package - */ -Blockly.FieldVariableGetter.prototype.getVariable = function() { - return this.variable_; -}; - -Blockly.FieldVariableGetter.prototype.setValue = function(id) { - // What do I do when id is null? That happens when undoing a change event - // for the first time the value was set. - var workspace = this.sourceBlock_.workspace; - var variable = Blockly.Variables.getVariable(workspace, id); - - if (!variable) { - throw new Error('Variable id doesn\'t point to a real variable! ID was ' + - id); - } - - if (this.sourceBlock_ && Blockly.Events.isEnabled()) { - var oldValue = this.variable_ ? this.variable_.getId() : null; - Blockly.Events.fire(new Blockly.Events.BlockChange( - this.sourceBlock_, 'field', this.name, oldValue, variable.getId())); - } - this.variable_ = variable; - this.value_ = id; - this.setText(variable.name); -}; - -/** - * This field is editable, but only through the right-click menu. - * @private - */ -Blockly.FieldVariableGetter.prototype.showEditor_ = function() { - // nop. -}; - -/** - * Add or remove the UI indicating if this field is editable or not. - * This field is editable, but only through the right-click menu. - * Suppress default editable behaviour. - */ -Blockly.FieldVariableGetter.prototype.updateEditable = function() { - // nop. -}; - -/** - * Whether this field references any Blockly variables. If true it may need to - * be handled differently during serialization and deserialization. Subclasses - * may override this. - * @return {boolean} True if this field has any variable references. - * @package - */ -Blockly.FieldVariableGetter.prototype.referencesVariables = function() { - return true; -}; - -Blockly.Field.register('field_variable_getter', Blockly.FieldVariableGetter); diff --git a/core/field_vertical_separator.js b/core/field_vertical_separator.js deleted file mode 100644 index cde897d61f..0000000000 --- a/core/field_vertical_separator.js +++ /dev/null @@ -1,161 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 Massachusetts Institute of Technology - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Vertical separator field. Draws a vertical line. - * @author ericr@media.mit.edu (Eric Rosenbaum) - */ -'use strict'; - -goog.provide('Blockly.FieldVerticalSeparator'); - -goog.require('Blockly.Field'); -goog.require('goog.dom'); -goog.require('goog.math.Size'); - - -/** - * Class for a vertical separator line. - * @extends {Blockly.Field} - * @constructor - */ -Blockly.FieldVerticalSeparator = function() { - this.sourceBlock_ = null; - this.width_ = 1; - this.height_ = Blockly.BlockSvg.ICON_SEPARATOR_HEIGHT; - this.size_ = new goog.math.Size(this.width_, this.height_); -}; -goog.inherits(Blockly.FieldVerticalSeparator, Blockly.Field); - -/** - * Construct a FieldVerticalSeparator from a JSON arg object. - * @param {!Object} _element A JSON object with options (unused, but passed in - * by Field.fromJson). - * @returns {!Blockly.FieldVerticalSeparator} The new field instance. - * @package - * @nocollapse - */ -Blockly.FieldVerticalSeparator.fromJson = function( - /* eslint-disable no-unused-vars */ _element - /* eslint-enable no-unused-vars */) { - return new Blockly.FieldVerticalSeparator(); -}; -/** - * Editable fields are saved by the XML renderer, non-editable fields are not. - */ -Blockly.FieldVerticalSeparator.prototype.EDITABLE = false; - -/** - * Install this field on a block. - */ -Blockly.FieldVerticalSeparator.prototype.init = function() { - if (this.fieldGroup_) { - // Image has already been initialized once. - return; - } - // Build the DOM. - /** @type {SVGElement} */ - this.fieldGroup_ = Blockly.utils.createSvgElement('g', {}, null); - if (!this.visible_) { - this.fieldGroup_.style.display = 'none'; - } - /** @type {SVGElement} */ - this.lineElement_ = Blockly.utils.createSvgElement('line', - { - 'stroke': this.sourceBlock_.getColourSecondary(), - 'stroke-linecap': 'round', - 'x1': 0, - 'y1': 0, - 'x2': 0, - 'y2': this.height_ - }, this.fieldGroup_); - - this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_); -}; - -/** - * Set the height of the line element, without adjusting the field's height. - * This allows the line's height to be changed without causing it to be - * centered with the new height (needed for correct rendering of hat blocks). - * @param {number} newHeight the new height for the line. - * @package - */ -Blockly.FieldVerticalSeparator.prototype.setLineHeight = function(newHeight) { - this.lineElement_.setAttribute('y2', newHeight); -}; - -/** - * Dispose of all DOM objects belonging to this text. - */ -Blockly.FieldVerticalSeparator.prototype.dispose = function() { - goog.dom.removeNode(this.fieldGroup_); - this.fieldGroup_ = null; - this.lineElement_ = null; -}; - -/** - * Get the value of this field. A no-op in this case. - * @return {string} null. - * @override - */ -Blockly.FieldVerticalSeparator.prototype.getValue = function() { - return null; -}; - -/** - * Set the value of this field. A no-op in this case. - * @param {?string} src New value. - * @override - */ -Blockly.FieldVerticalSeparator.prototype.setValue = function( - /* eslint-disable no-unused-vars */ src - /* eslint-enable no-unused-vars */) { - return; -}; - -/** - * Set the text of this field. A no-op in this case. - * @param {?string} alt New text. - * @override - */ -Blockly.FieldVerticalSeparator.prototype.setText = function( - /* eslint-disable no-unused-vars */ alt - /* eslint-enable no-unused-vars */) { - return; -}; - -/** - * Separator lines are fixed width, no need to render. - * @private - */ -Blockly.FieldVerticalSeparator.prototype.render_ = function() { - // NOP -}; - -/** - * Separator lines are fixed width, no need to update. - * @private - */ -Blockly.FieldVerticalSeparator.prototype.updateWidth = function() { - // NOP -}; - -Blockly.Field.register( - 'field_vertical_separator', Blockly.FieldVerticalSeparator); diff --git a/core/flyout_base.js b/core/flyout_base.js deleted file mode 100644 index fe5b3fcd4e..0000000000 --- a/core/flyout_base.js +++ /dev/null @@ -1,923 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2011 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Flyout tray containing blocks which may be created. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.Flyout'); - -goog.require('Blockly.Block'); -goog.require('Blockly.Comment'); -goog.require('Blockly.Events'); -goog.require('Blockly.Events.BlockCreate'); -goog.require('Blockly.Events.VarCreate'); -goog.require('Blockly.FlyoutButton'); -goog.require('Blockly.FlyoutExtensionCategoryHeader'); -goog.require('Blockly.Gesture'); -goog.require('Blockly.scratchBlocksUtils'); -goog.require('Blockly.Touch'); -goog.require('Blockly.WorkspaceSvg'); -goog.require('goog.dom'); -goog.require('goog.events'); -goog.require('goog.math.Rect'); -goog.require('goog.userAgent'); - - -/** - * Class for a flyout. - * @param {!Object} workspaceOptions Dictionary of options for the workspace. - * @constructor - */ -Blockly.Flyout = function(workspaceOptions) { - workspaceOptions.getMetrics = this.getMetrics_.bind(this); - workspaceOptions.setMetrics = this.setMetrics_.bind(this); - - /** - * @type {!Blockly.Workspace} - * @protected - */ - this.workspace_ = new Blockly.WorkspaceSvg(workspaceOptions); - this.workspace_.isFlyout = true; - - // When we create blocks for this workspace, instead of using the "optional" id - // make the default `id` the same as the `type` for easier re-use. - var newBlock = this.workspace_.newBlock; - this.workspace_.newBlock = function(type, id) { - // Use `type` if `id` isn't passed. `this` will be workspace. - return newBlock.call(this, type, id || type); - }; - - /** - * Is RTL vs LTR. - * @type {boolean} - */ - this.RTL = !!workspaceOptions.RTL; - - /** - * Flyout should be laid out horizontally vs vertically. - * @type {boolean} - * @private - */ - this.horizontalLayout_ = workspaceOptions.horizontalLayout; - - /** - * Position of the toolbox and flyout relative to the workspace. - * @type {number} - * @protected - */ - this.toolboxPosition_ = workspaceOptions.toolboxPosition; - - /** - * Opaque data that can be passed to Blockly.unbindEvent_. - * @type {!Array.} - * @private - */ - this.eventWrappers_ = []; - - /** - * List of background buttons that lurk behind each block to catch clicks - * landing in the blocks' lakes and bays. - * @type {!Array.} - * @private - */ - this.backgroundButtons_ = []; - - /** - * List of visible buttons. - * @type {!Array.} - * @protected - */ - this.buttons_ = []; - - /** - * List of event listeners. - * @type {!Array.} - * @private - */ - this.listeners_ = []; - - /** - * List of blocks that should always be disabled. - * @type {!Array.} - * @private - */ - this.permanentlyDisabled_ = []; - - /** - * The toolbox that this flyout belongs to, or none if tihs is a simple - * workspace. - * @type {Blockly.Toolbox} - * @private - */ - this.parentToolbox_ = null; - - /** - * The target position for the flyout scroll animation in pixels. - * Is a number while animating, null otherwise. - * @type {?number} - * @package - */ - this.scrollTarget = null; - - /** - * A recycle bin for blocks. - * @type {!Array.} - * @private - */ - this.recycleBlocks_ = []; - -}; - -/** - * Does the flyout automatically close when a block is created? - * @type {boolean} - */ -Blockly.Flyout.prototype.autoClose = false; - -/** - * Whether the flyout is visible. - * @type {boolean} - * @private - */ -Blockly.Flyout.prototype.isVisible_ = false; - -/** - * Whether the workspace containing this flyout is visible. - * @type {boolean} - * @private - */ -Blockly.Flyout.prototype.containerVisible_ = true; - -/** - * Corner radius of the flyout background. - * @type {number} - * @const - */ -Blockly.Flyout.prototype.CORNER_RADIUS = 0; - -/** - * Margin around the edges of the blocks in the flyout. - * @type {number} - * @const - */ -Blockly.Flyout.prototype.MARGIN = 12; - -// TODO: Move GAP_X and GAP_Y to their appropriate files. - -/** - * Gap between items in horizontal flyouts. Can be overridden with the "sep" - * element. - * @const {number} - */ -Blockly.Flyout.prototype.GAP_X = Blockly.Flyout.prototype.MARGIN * 3; - -/** - * Gap between items in vertical flyouts. Can be overridden with the "sep" - * element. - * @const {number} - */ -Blockly.Flyout.prototype.GAP_Y = Blockly.Flyout.prototype.MARGIN; - -/** - * Top/bottom padding between scrollbar and edge of flyout background. - * @type {number} - * @const - */ -Blockly.Flyout.prototype.SCROLLBAR_PADDING = 2; - -/** - * Width of flyout. - * @type {number} - * @protected - */ -Blockly.Flyout.prototype.width_ = 0; - -/** - * Height of flyout. - * @type {number} - * @protected - */ -Blockly.Flyout.prototype.height_ = 0; - -/** - * Width of flyout contents. - * @type {number} - * @private - */ -Blockly.Flyout.prototype.contentWidth_ = 0; - -/** - * Height of flyout contents. - * @type {number} - * @private - */ -Blockly.Flyout.prototype.contentHeight_ = 0; - -/** - * Vertical offset of flyout. - * @type {number} - * @private - */ -Blockly.Flyout.prototype.verticalOffset_ = 0; - -/** - * Range of a drag angle from a flyout considered "dragging toward workspace". - * Drags that are within the bounds of this many degrees from the orthogonal - * line to the flyout edge are considered to be "drags toward the workspace". - * Example: - * Flyout Edge Workspace - * [block] / <-within this angle, drags "toward workspace" | - * [block] ---- orthogonal to flyout boundary ---- | - * [block] \ | - * The angle is given in degrees from the orthogonal. - * - * This is used to know when to create a new block and when to scroll the - * flyout. Setting it to 360 means that all drags create a new block. - * @type {number} - * @protected -*/ -Blockly.Flyout.prototype.dragAngleRange_ = 70; - -/** - * The fraction of the distance to the scroll target to move the flyout on - * each animation frame, when auto-scrolling. Values closer to 1.0 will make - * the scroll animation complete faster. Use 1.0 for no animation. - * @type {number} - */ -Blockly.Flyout.prototype.scrollAnimationFraction = 0.3; - -/** - * Whether to recycle blocks when refreshing the flyout. When false, do not allow - * anything to be recycled. The default is to recycle. - * @type {boolean} - * @private - */ -Blockly.Flyout.prototype.recyclingEnabled_ = true; - -/** - * Creates the flyout's DOM. Only needs to be called once. The flyout can - * either exist as its own svg element or be a g element nested inside a - * separate svg element. - * @param {string} tagName The type of tag to put the flyout in. This - * should be or . - * @return {!Element} The flyout's SVG group. - */ -Blockly.Flyout.prototype.createDom = function(tagName) { - /* - - - - - */ - // Setting style to display:none to start. The toolbox and flyout - // hide/show code will set up proper visibility and size later. - this.svgGroup_ = Blockly.utils.createSvgElement(tagName, - {'class': 'blocklyFlyout', 'style': 'display: none'}, null); - this.svgBackground_ = Blockly.utils.createSvgElement('path', - {'class': 'blocklyFlyoutBackground'}, this.svgGroup_); - this.svgGroup_.appendChild(this.workspace_.createDom()); - return this.svgGroup_; -}; - -/** - * Initializes the flyout. - * @param {!Blockly.Workspace} targetWorkspace The workspace in which to create - * new blocks. - */ -Blockly.Flyout.prototype.init = function(targetWorkspace) { - this.targetWorkspace_ = targetWorkspace; - this.workspace_.targetWorkspace = targetWorkspace; - // Add scrollbar. - this.scrollbar_ = new Blockly.Scrollbar(this.workspace_, - this.horizontalLayout_, false, 'blocklyFlyoutScrollbar'); - - this.position(); - - Array.prototype.push.apply(this.eventWrappers_, - Blockly.bindEventWithChecks_(this.svgGroup_, 'wheel', this, this.wheel_)); - // Dragging the flyout up and down (or left and right). - Array.prototype.push.apply(this.eventWrappers_, - Blockly.bindEventWithChecks_( - this.svgGroup_, 'mousedown', this, this.onMouseDown_)); - - // A flyout connected to a workspace doesn't have its own current gesture. - this.workspace_.getGesture = - this.targetWorkspace_.getGesture.bind(this.targetWorkspace_); - - // Get variables from the main workspace rather than the target workspace. - this.workspace_.variableMap_ = this.targetWorkspace_.getVariableMap(); - - this.workspace_.createPotentialVariableMap(); -}; - -/** - * Dispose of this flyout. - * Unlink from all DOM elements to prevent memory leaks. - */ -Blockly.Flyout.prototype.dispose = function() { - this.hide(); - Blockly.unbindEvent_(this.eventWrappers_); - if (this.scrollbar_) { - this.scrollbar_.dispose(); - this.scrollbar_ = null; - } - if (this.workspace_) { - this.workspace_.targetWorkspace = null; - this.workspace_.dispose(); - this.workspace_ = null; - } - if (this.svgGroup_) { - goog.dom.removeNode(this.svgGroup_); - this.svgGroup_ = null; - } - this.parentToolbox_ = null; - this.svgBackground_ = null; - this.targetWorkspace_ = null; -}; - -/** - * Set the parent toolbox of this flyout. - * @param {!Blockly.Toolbox} toolbox The toolbox that owns this flyout. - */ -Blockly.Flyout.prototype.setParentToolbox = function(toolbox) { - this.parentToolbox_ = toolbox; -}; - -/** - * Get the width of the flyout. - * @return {number} The width of the flyout. - */ -Blockly.Flyout.prototype.getWidth = function() { - return this.DEFAULT_WIDTH; -}; - -/** - * Get the height of the flyout. - * @return {number} The width of the flyout. - */ -Blockly.Flyout.prototype.getHeight = function() { - return this.height_; -}; - -/** - * Get the workspace inside the flyout. - * @return {!Blockly.WorkspaceSvg} The workspace inside the flyout. - * @package - */ -Blockly.Flyout.prototype.getWorkspace = function() { - return this.workspace_; -}; - -/** - * Is the flyout visible? - * @return {boolean} True if visible. - */ -Blockly.Flyout.prototype.isVisible = function() { - return this.isVisible_; -}; - -/** - * Set whether the flyout is visible. A value of true does not necessarily mean - * that the flyout is shown. It could be hidden because its container is hidden. - * @param {boolean} visible True if visible. - */ -Blockly.Flyout.prototype.setVisible = function(visible) { - var visibilityChanged = (visible != this.isVisible()); - - this.isVisible_ = visible; - if (visibilityChanged) { - this.updateDisplay_(); - } -}; - -/** - * Set whether this flyout's container is visible. - * @param {boolean} visible Whether the container is visible. - */ -Blockly.Flyout.prototype.setContainerVisible = function(visible) { - var visibilityChanged = (visible != this.containerVisible_); - this.containerVisible_ = visible; - if (visibilityChanged) { - this.updateDisplay_(); - } -}; - -/** - * Update the display property of the flyout based whether it thinks it should - * be visible and whether its containing workspace is visible. - * @private - */ -Blockly.Flyout.prototype.updateDisplay_ = function() { - var show = true; - if (!this.containerVisible_) { - show = false; - } else { - show = this.isVisible(); - } - this.svgGroup_.style.display = show ? 'block' : 'none'; - // Update the scrollbar's visiblity too since it should mimic the - // flyout's visibility. - this.scrollbar_.setContainerVisible(show); -}; - -/** - * Hide and empty the flyout. - */ -Blockly.Flyout.prototype.hide = function() { - if (!this.isVisible()) { - return; - } - this.setVisible(false); - // Delete all the event listeners. - for (var x = 0, listen; listen = this.listeners_[x]; x++) { - Blockly.unbindEvent_(listen); - } - this.listeners_.length = 0; - if (this.reflowWrapper_) { - this.workspace_.removeChangeListener(this.reflowWrapper_); - this.reflowWrapper_ = null; - } - // Do NOT delete the blocks here. Wait until Flyout.show. - // https://neil.fraser.name/news/2014/08/09/ -}; - -/** - * Show and populate the flyout. - * @param {!Array|string} xmlList List of blocks to show. - * Variables and procedures have a custom set of blocks. - */ -Blockly.Flyout.prototype.show = function(xmlList) { - this.workspace_.setResizesEnabled(false); - this.hide(); - this.clearOldBlocks_(); - - this.setVisible(true); - // Create the blocks to be shown in this flyout. - var contents = []; - var gaps = []; - this.permanentlyDisabled_.length = 0; - for (var i = 0, xml; xml = xmlList[i]; i++) { - // Handle dynamic categories, represented by a name instead of a list of XML. - // Look up the correct category generation function and call that to get a - // valid XML list. - if (typeof xml === 'string') { - var fnToApply = this.workspace_.targetWorkspace.getToolboxCategoryCallback( - xmlList[i]); - var newList = fnToApply(this.workspace_.targetWorkspace); - // Insert the new list of blocks in the middle of the list. - // We use splice to insert at index i, and remove a single element - // (the placeholder string). Because the spread operator (...) is not - // available, use apply and concat the array. - xmlList.splice.apply(xmlList, [i, 1].concat(newList)); - xml = xmlList[i]; - } - if (xml.tagName) { - var tagName = xml.tagName.toUpperCase(); - var default_gap = this.horizontalLayout_ ? this.GAP_X : this.GAP_Y; - if (tagName == 'BLOCK') { - - // We assume that in a flyout, the same block id (or type if missing id) means - // the same output BlockSVG. - - // Look for a block that matches the id or type, our createBlock will assign - // id = type if none existed. - var id = xml.getAttribute('id') || xml.getAttribute('type'); - var recycled = this.recycleBlocks_.findIndex(function(block) { - return block.id === id; - }); - - - // If we found a recycled item, reuse the BlockSVG from last time. - // Otherwise, convert the XML block to a BlockSVG. - var curBlock; - if (recycled > -1) { - curBlock = this.recycleBlocks_.splice(recycled, 1)[0]; - } else { - curBlock = Blockly.Xml.domToBlock(xml, this.workspace_); - } - - if (curBlock.disabled) { - // Record blocks that were initially disabled. - // Do not enable these blocks as a result of capacity filtering. - this.permanentlyDisabled_.push(curBlock); - } - contents.push({type: 'block', block: curBlock}); - var gap = parseInt(xml.getAttribute('gap'), 10); - gaps.push(isNaN(gap) ? default_gap : gap); - } else if (xml.tagName.toUpperCase() == 'SEP') { - // Change the gap between two blocks. - // - // The default gap is 24, can be set larger or smaller. - // This overwrites the gap attribute on the previous block. - // Note that a deprecated method is to add a gap to a block. - // - var newGap = parseInt(xml.getAttribute('gap'), 10); - // Ignore gaps before the first block. - if (!isNaN(newGap) && gaps.length > 0) { - gaps[gaps.length - 1] = newGap; - } else { - gaps.push(default_gap); - } - } else if ((tagName == 'LABEL') && (xml.getAttribute('showStatusButton') == 'true')) { - var curButton = new Blockly.FlyoutExtensionCategoryHeader(this.workspace_, - this.targetWorkspace_, xml); - contents.push({type: 'button', button: curButton}); - gaps.push(default_gap); - } else if (tagName == 'BUTTON' || tagName == 'LABEL') { - // Labels behave the same as buttons, but are styled differently. - var isLabel = tagName == 'LABEL'; - var curButton = new Blockly.FlyoutButton(this.workspace_, - this.targetWorkspace_, xml, isLabel); - contents.push({type: 'button', button: curButton}); - gaps.push(default_gap); - } - } - } - - this.emptyRecycleBlocks_(); - - this.layout_(contents, gaps); - - // IE 11 is an incompetent browser that fails to fire mouseout events. - // When the mouse is over the background, deselect all blocks. - var deselectAll = function() { - var topBlocks = this.workspace_.getTopBlocks(false); - for (var i = 0, block; block = topBlocks[i]; i++) { - block.removeSelect(); - } - }; - - this.listeners_.push(Blockly.bindEvent_(this.svgBackground_, 'mouseover', - this, deselectAll)); - - this.workspace_.setResizesEnabled(true); - this.reflow(); - - // Correctly position the flyout's scrollbar when it opens. - this.position(); - - this.reflowWrapper_ = this.reflow.bind(this); - this.workspace_.addChangeListener(this.reflowWrapper_); - - this.recordCategoryScrollPositions_(); -}; - -/** - * Empty out the recycled blocks, properly destroying everything. - * @private - */ -Blockly.Flyout.prototype.emptyRecycleBlocks_ = function() { - // Clean out the old recycle bin. - var oldBlocks = this.recycleBlocks_; - this.recycleBlocks_ = []; - for (var i = 0; i < oldBlocks.length; i++) { - oldBlocks[i].dispose(false, false); - } -}; - -/** - * Store an array of category names, ids, scrollbar positions, and category lengths. - * This is used when scrolling the flyout to cause a category to be selected. - * @private - */ -Blockly.Flyout.prototype.recordCategoryScrollPositions_ = function() { - this.categoryScrollPositions = []; - // Record category names and positions using the text label at the top of each one. - for (var i = 0; i < this.buttons_.length; i++) { - if (this.buttons_[i].getIsCategoryLabel()) { - var categoryLabel = this.buttons_[i]; - this.categoryScrollPositions.push({ - categoryName: categoryLabel.getText(), - position: this.horizontalLayout_ ? - categoryLabel.getPosition().x : categoryLabel.getPosition().y - }); - } - } - // Record the length of each category, setting the final one to 0. - var numCategories = this.categoryScrollPositions.length; - if (numCategories > 0) { - for (var i = 0; i < numCategories - 1; i++) { - var currentPos = this.categoryScrollPositions[i].position; - var nextPos = this.categoryScrollPositions[i + 1].position; - var length = nextPos - currentPos; - this.categoryScrollPositions[i].length = length; - } - this.categoryScrollPositions[numCategories - 1].length = 0; - // Record the id of each category. - for (var i = 0; i < numCategories; i++) { - var category = this.parentToolbox_.getCategoryByIndex(i); - if (category && category.id_) { - this.categoryScrollPositions[i].categoryId = category.id_; - } - } - } -}; - -/** - * Select a category using the scroll position. - * @param {number} pos The scroll position in pixels. - * @package - */ -Blockly.Flyout.prototype.selectCategoryByScrollPosition = function(pos) { - // If we are currently auto-scrolling, due to selecting a category by clicking on it, - // do not update the category selection. - if (this.scrollTarget) { - return; - } - var workspacePos = Math.round(pos / this.workspace_.scale); - // Traverse the array of scroll positions in reverse, so we can select the furthest - // category that the scroll position is beyond. - for (var i = this.categoryScrollPositions.length - 1; i >= 0; i--) { - if (workspacePos >= this.categoryScrollPositions[i].position) { - this.parentToolbox_.selectCategoryById(this.categoryScrollPositions[i].categoryId); - return; - } - } -}; - -/** - * Step the scrolling animation by scrolling a fraction of the way to - * a scroll target, and request the next frame if necessary. - * @package - */ -Blockly.Flyout.prototype.stepScrollAnimation = function() { - if (!this.scrollTarget) { - return; - } - var scrollPos = this.horizontalLayout_ ? - -this.workspace_.scrollX : -this.workspace_.scrollY; - var diff = this.scrollTarget - scrollPos; - if (Math.abs(diff) < 1) { - this.scrollbar_.set(this.scrollTarget); - this.scrollTarget = null; - return; - } - this.scrollbar_.set(scrollPos + diff * this.scrollAnimationFraction); - - // Polyfilled by goog.dom.animationFrame.polyfill - requestAnimationFrame(this.stepScrollAnimation.bind(this)); -}; - -/** - * Get the scaled scroll position. - * @return {number} The current scroll position. - */ -Blockly.Flyout.prototype.getScrollPos = function() { - var pos = this.horizontalLayout_ ? - -this.workspace_.scrollX : -this.workspace_.scrollY; - return pos / this.workspace_.scale; -}; - -/** - * Set the scroll position, scaling it. - * @param {number} pos The scroll position to set. - */ -Blockly.Flyout.prototype.setScrollPos = function(pos) { - this.scrollbar_.set(pos * this.workspace_.scale); -}; - -/** - * Set whether the flyout can recycle blocks. A value of true allows blocks to be recycled. - * @param {boolean} recycle True if recycling is possible. - */ -Blockly.Flyout.prototype.setRecyclingEnabled = function(recycle) { - this.recyclingEnabled_ = recycle; -}; - -/** - * Delete blocks and background buttons from a previous showing of the flyout. - * @private - */ -Blockly.Flyout.prototype.clearOldBlocks_ = function() { - // Delete any blocks from a previous showing. - var oldBlocks = this.workspace_.getTopBlocks(false); - for (var i = 0, block; block = oldBlocks[i]; i++) { - if (block.workspace == this.workspace_) { - if (this.recyclingEnabled_ && - Blockly.scratchBlocksUtils.blockIsRecyclable(block)) { - this.recycleBlock_(block); - } else { - block.dispose(false, false); - } - } - } - // Delete any background buttons from a previous showing. - for (var j = 0; j < this.backgroundButtons_.length; j++) { - var rect = this.backgroundButtons_[j]; - if (rect) goog.dom.removeNode(rect); - } - this.backgroundButtons_.length = 0; - - for (var i = 0, button; button = this.buttons_[i]; i++) { - button.dispose(); - } - this.buttons_.length = 0; - - // Clear potential variables from the previous showing. - this.workspace_.getPotentialVariableMap().clear(); -}; - -/** - * Add listeners to a block that has been added to the flyout. - * @param {!Element} root The root node of the SVG group the block is in. - * @param {!Blockly.Block} block The block to add listeners for. - * @param {!Element} rect The invisible rectangle under the block that acts as - * a button for that block. - * @private - */ -Blockly.Flyout.prototype.addBlockListeners_ = function(root, block, rect) { - this.listeners_.push(Blockly.bindEventWithChecks_(root, 'mousedown', null, - this.blockMouseDown_(block))); - this.listeners_.push(Blockly.bindEventWithChecks_(rect, 'mousedown', null, - this.blockMouseDown_(block))); - this.listeners_.push(Blockly.bindEvent_(root, 'mouseover', block, - block.addSelect)); - this.listeners_.push(Blockly.bindEvent_(root, 'mouseout', block, - block.removeSelect)); - this.listeners_.push(Blockly.bindEvent_(rect, 'mouseover', block, - block.addSelect)); - this.listeners_.push(Blockly.bindEvent_(rect, 'mouseout', block, - block.removeSelect)); -}; - -/** - * Handle a mouse-down on an SVG block in a non-closing flyout. - * @param {!Blockly.Block} block The flyout block to copy. - * @return {!Function} Function to call when block is clicked. - * @private - */ -Blockly.Flyout.prototype.blockMouseDown_ = function(block) { - var flyout = this; - return function(e) { - var gesture = flyout.targetWorkspace_.getGesture(e); - if (gesture) { - gesture.setStartBlock(block); - gesture.handleFlyoutStart(e, flyout); - } - }; -}; - -/** - * Mouse down on the flyout background. Start a scroll drag. - * @param {!Event} e Mouse down event. - * @private - */ -Blockly.Flyout.prototype.onMouseDown_ = function(e) { - var gesture = this.targetWorkspace_.getGesture(e); - if (gesture) { - gesture.handleFlyoutStart(e, this); - } -}; - -/** - * Create a copy of this block on the workspace. - * @param {!Blockly.BlockSvg} originalBlock The block to copy from the flyout. - * @return {Blockly.BlockSvg} The newly created block, or null if something - * went wrong with deserialization. - * @package - */ -Blockly.Flyout.prototype.createBlock = function(originalBlock) { - var newBlock = null; - Blockly.Events.disable(); - var variablesBeforeCreation = this.targetWorkspace_.getAllVariables(); - this.targetWorkspace_.setResizesEnabled(false); - try { - newBlock = this.placeNewBlock_(originalBlock); - // Close the flyout. - Blockly.hideChaff(); - } finally { - Blockly.Events.enable(); - } - - var newVariables = Blockly.Variables.getAddedVariables(this.targetWorkspace_, - variablesBeforeCreation); - - if (Blockly.Events.isEnabled()) { - Blockly.Events.setGroup(true); - Blockly.Events.fire(new Blockly.Events.Create(newBlock)); - // Fire a VarCreate event for each (if any) new variable created. - for (var i = 0; i < newVariables.length; i++) { - var thisVariable = newVariables[i]; - Blockly.Events.fire(new Blockly.Events.VarCreate(thisVariable)); - } - } - if (this.autoClose) { - this.hide(); - } - return newBlock; -}; - -/** - * Reflow blocks and their buttons. - */ -Blockly.Flyout.prototype.reflow = function() { - if (this.reflowWrapper_) { - this.workspace_.removeChangeListener(this.reflowWrapper_); - } - var blocks = this.workspace_.getTopBlocks(false); - this.reflowInternal_(blocks); - if (this.reflowWrapper_) { - this.workspace_.addChangeListener(this.reflowWrapper_); - } -}; - -/** - * @return {boolean} True if this flyout may be scrolled with a scrollbar or by - * dragging. - * @package - */ -Blockly.Flyout.prototype.isScrollable = function() { - return this.scrollbar_ ? this.scrollbar_.isVisible() : false; -}; - -/** - * Copy a block from the flyout to the workspace and position it correctly. - * @param {!Blockly.Block} oldBlock The flyout block to copy. - * @return {!Blockly.Block} The new block in the main workspace. - * @private - */ -Blockly.Flyout.prototype.placeNewBlock_ = function(oldBlock) { - var targetWorkspace = this.targetWorkspace_; - var svgRootOld = oldBlock.getSvgRoot(); - if (!svgRootOld) { - throw 'oldBlock is not rendered.'; - } - - // Create the new block by cloning the block in the flyout (via XML). - var xml = Blockly.Xml.blockToDom(oldBlock); - // The target workspace would normally resize during domToBlock, which will - // lead to weird jumps. Save it for terminateDrag. - targetWorkspace.setResizesEnabled(false); - - // Using domToBlock instead of domToWorkspace means that the new block will be - // placed at position (0, 0) in main workspace units. - var block = Blockly.Xml.domToBlock(xml, targetWorkspace); - var svgRootNew = block.getSvgRoot(); - if (!svgRootNew) { - throw 'block is not rendered.'; - } - - // The offset in pixels between the main workspace's origin and the upper left - // corner of the injection div. - var mainOffsetPixels = targetWorkspace.getOriginOffsetInPixels(); - - // The offset in pixels between the flyout workspace's origin and the upper - // left corner of the injection div. - var flyoutOffsetPixels = this.workspace_.getOriginOffsetInPixels(); - - // The position of the old block in flyout workspace coordinates. - var oldBlockPosWs = oldBlock.getRelativeToSurfaceXY(); - - // The position of the old block in pixels relative to the flyout - // workspace's origin. - var oldBlockPosPixels = oldBlockPosWs.scale(this.workspace_.scale); - - // The position of the old block in pixels relative to the upper left corner - // of the injection div. - var oldBlockOffsetPixels = goog.math.Coordinate.sum(flyoutOffsetPixels, - oldBlockPosPixels); - - // The position of the old block in pixels relative to the origin of the - // main workspace. - var finalOffsetPixels = goog.math.Coordinate.difference(oldBlockOffsetPixels, - mainOffsetPixels); - - // The position of the old block in main workspace coordinates. - var finalOffsetMainWs = finalOffsetPixels.scale(1 / targetWorkspace.scale); - - block.moveBy(finalOffsetMainWs.x, finalOffsetMainWs.y); - return block; -}; - -/** - * Put a previously created block into the recycle bin, used during large - * workspace swaps to limit the number of new dom elements we need to create - * - * @param {!Blockly.BlockSvg} block The block to recycle. - * @private - */ -Blockly.Flyout.prototype.recycleBlock_ = function(block) { - var xy = block.getRelativeToSurfaceXY(); - block.moveBy(-xy.x, -xy.y); - this.recycleBlocks_.push(block); -}; diff --git a/core/flyout_button.js b/core/flyout_button.js deleted file mode 100644 index 26f7d8f39a..0000000000 --- a/core/flyout_button.js +++ /dev/null @@ -1,322 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Class for a button in the flyout. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.FlyoutButton'); - -goog.require('goog.dom'); -goog.require('goog.math.Coordinate'); - - -/** - * Class for a button or label in the flyout. Labels behave the same as buttons, - * but are styled differently. - * @param {!Blockly.WorkspaceSvg} workspace The workspace in which to place this - * button. - * @param {!Blockly.WorkspaceSvg} targetWorkspace The flyout's target workspace. - * @param {!Element} xml The XML specifying the label/button. - * @param {boolean} isLabel Whether this button should be styled as a label. - * @constructor - */ -Blockly.FlyoutButton = function(workspace, targetWorkspace, xml, isLabel) { - - this.init(workspace, targetWorkspace, xml, isLabel); - - /** - * Function to call when this button is clicked. - * @type {function(!Blockly.FlyoutButton)} - * @private - */ - this.callback_ = null; - - var callbackKey = xml.getAttribute('callbackKey'); - if (this.isLabel_ && callbackKey) { - console.warn('Labels should not have callbacks. Label text: ' + this.text_); - } else if (!this.isLabel_ && - !(callbackKey && targetWorkspace.getButtonCallback(callbackKey))) { - console.warn('Buttons should have callbacks. Button text: ' + this.text_); - } else { - this.callback_ = targetWorkspace.getButtonCallback(callbackKey); - } -}; - -/** - * The margin around the text in the button. - */ -Blockly.FlyoutButton.MARGIN = 40; - -/** - * The width of the button's rect. - * @type {number} - */ -Blockly.FlyoutButton.prototype.width = 0; - -/** - * The height of the button's rect. - * @type {number} - */ -Blockly.FlyoutButton.prototype.height = 40; // Can't be computed like the width - -/** - * Opaque data that can be passed to Blockly.unbindEvent_. - * @type {Array.} - * @private - */ -Blockly.FlyoutButton.prototype.onMouseUpWrapper_ = null; - -/** -* Initialize the button or label. This is a helper function to so that the -* constructor can be overridden. -* @param {!Blockly.WorkspaceSvg} workspace The workspace in which to place this -* button. -* @param {!Blockly.WorkspaceSvg} targetWorkspace The flyout's target workspace. -* @param {!Element} xml The XML specifying the label/button. -* @param {boolean} isLabel Whether this button should be styled as a label. - */ -Blockly.FlyoutButton.prototype.init = function( - workspace, targetWorkspace, xml, isLabel) { - - /** - * @type {!Blockly.WorkspaceSvg} - * @private - */ - this.workspace_ = workspace; - - /** - * @type {!Blockly.Workspace} - * @private - */ - this.targetWorkspace_ = targetWorkspace; - - /** - * @type {string} - * @private - */ - this.text_ = xml.getAttribute('text'); - - /** - * @type {!goog.math.Coordinate} - * @private - */ - this.position_ = new goog.math.Coordinate(0, 0); - - /** - * Whether this button should be styled as a label. - * @type {boolean} - * @private - */ - this.isLabel_ = isLabel; - - /** - * Whether this button is a label at the top of a category. - * @type {boolean} - * @private - */ - this.isCategoryLabel_ = xml.getAttribute('category-label') === 'true'; - - /** - * If specified, a CSS class to add to this button. - * @type {?string} - * @private - */ - this.cssClass_ = xml.getAttribute('web-class') || null; -}; - -/** - * Create the button elements. - * @return {!Element} The button's SVG group. - */ -Blockly.FlyoutButton.prototype.createDom = function() { - var cssClass = this.isLabel_ ? 'blocklyFlyoutLabel' : 'blocklyFlyoutButton'; - if (this.cssClass_) { - cssClass += ' ' + this.cssClass_; - } - - this.svgGroup_ = Blockly.utils.createSvgElement('g', {'class': cssClass}, - this.workspace_.getCanvas()); - - this.addTextSvg(this.isLabel_); - - this.mouseUpWrapper_ = Blockly.bindEventWithChecks_(this.svgGroup_, 'mouseup', - this, this.onMouseUp_); - return this.svgGroup_; -}; - -/** - * Add the text element for the label or button. - * @param {boolean} isLabel True if this is a label and not button. - * @package - */ -Blockly.FlyoutButton.prototype.addTextSvg = function(isLabel) { - if (!isLabel) { - // Shadow rectangle (light source does not mirror in RTL). - var shadow = Blockly.utils.createSvgElement('rect', - { - 'class': 'blocklyFlyoutButtonShadow', - 'rx': 4, - 'ry': 4, - 'x': 1, - 'y': 1 - }, - this.svgGroup_); - } - // Background rectangle. - var rect = Blockly.utils.createSvgElement('rect', - { - 'class': isLabel ? - 'blocklyFlyoutLabelBackground' : 'blocklyFlyoutButtonBackground', - 'rx': 4, 'ry': 4 - }, - this.svgGroup_); - - var svgText = Blockly.utils.createSvgElement('text', - { - 'class': isLabel ? 'blocklyFlyoutLabelText' : 'blocklyText', - 'x': 0, - 'y': 0, - 'text-anchor': 'middle' - }, - this.svgGroup_); - svgText.textContent = Blockly.utils.replaceMessageReferences(this.text_); - - this.width = Blockly.Field.getCachedWidth(svgText); - - if (!isLabel) { - this.width += 2 * Blockly.FlyoutButton.MARGIN; - shadow.setAttribute('width', this.width); - shadow.setAttribute('height', this.height); - } - - rect.setAttribute('width', this.width); - rect.setAttribute('height', this.height); - - svgText.setAttribute('text-anchor', 'middle'); - svgText.setAttribute('dominant-baseline', 'central'); - svgText.setAttribute('dy', goog.userAgent.EDGE_OR_IE ? - Blockly.Field.IE_TEXT_OFFSET : '0'); - svgText.setAttribute('x', this.width / 2); - svgText.setAttribute('y', this.height / 2); -}; - -/** - * Correctly position the flyout button and make it visible. - */ -Blockly.FlyoutButton.prototype.show = function() { - this.updateTransform_(); - this.svgGroup_.setAttribute('display', 'block'); -}; - -/** - * Update SVG attributes to match internal state. - * @private - */ -Blockly.FlyoutButton.prototype.updateTransform_ = function() { - this.svgGroup_.setAttribute('transform', - 'translate(' + this.position_.x + ',' + this.position_.y + ')'); -}; - -/** - * Move the button to the given x, y coordinates. - * @param {number} x The new x coordinate. - * @param {number} y The new y coordinate. - */ -Blockly.FlyoutButton.prototype.moveTo = function(x, y) { - this.position_.x = x; - this.position_.y = y; - this.updateTransform_(); -}; - -/** - * Get the button's target workspace. - * @return {!Blockly.WorkspaceSvg} The target workspace of the flyout where this - * button resides. - */ -Blockly.FlyoutButton.prototype.getTargetWorkspace = function() { - return this.targetWorkspace_; -}; - -/** - * Get whether this button is a label at the top of a category. - * @return {boolean} True if it is a category label. - * @package - */ -Blockly.FlyoutButton.prototype.getIsCategoryLabel = function() { - return this.isCategoryLabel_; -}; - -/** - * Get the text of this button. - * @return {string} The text on the button. - * @package - */ -Blockly.FlyoutButton.prototype.getText = function() { - return this.text_; -}; - -/** - * Get the position of this button. - * @return {!goog.math.Coordinate} The button position. - * @package - */ -Blockly.FlyoutButton.prototype.getPosition = function() { - return this.position_; -}; - -/** - * Dispose of this button. - */ -Blockly.FlyoutButton.prototype.dispose = function() { - if (this.onMouseUpWrapper_) { - Blockly.unbindEvent_(this.onMouseUpWrapper_); - } - if (this.svgGroup_) { - goog.dom.removeNode(this.svgGroup_); - this.svgGroup_ = null; - } - this.workspace_ = null; - this.targetWorkspace_ = null; -}; - -/** - * Do something when the button is clicked. - * @param {!Event} e Mouse up event. - * @private - */ -Blockly.FlyoutButton.prototype.onMouseUp_ = function(e) { - var gesture = this.targetWorkspace_.getGesture(e); - if (gesture) { - // If we're in the middle of dragging something (blocks, workspace, etc.) ignore the button. - // Otherwise, cancel the gesture. - if (gesture.isDragging()) { - return; - } - gesture.cancel(); - } - - // Call the callback registered to this button. - if (this.callback_) { - this.callback_(this); - } -}; diff --git a/core/flyout_dragger.js b/core/flyout_dragger.js deleted file mode 100644 index c3909eaf2f..0000000000 --- a/core/flyout_dragger.js +++ /dev/null @@ -1,83 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Methods for dragging a flyout visually. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.FlyoutDragger'); - -goog.require('Blockly.WorkspaceDragger'); - -goog.require('goog.asserts'); -goog.require('goog.math.Coordinate'); - - -/** - * Class for a flyout dragger. It moves a flyout workspace around when it is - * being dragged by a mouse or touch. - * Note that the workspace itself manages whether or not it has a drag surface - * and how to do translations based on that. This simply passes the right - * commands based on events. - * @param {!Blockly.Flyout} flyout The flyout to drag. - * @constructor - */ -Blockly.FlyoutDragger = function(flyout) { - Blockly.FlyoutDragger.superClass_.constructor.call(this, - flyout.getWorkspace()); - - /** - * The scrollbar to update to move the flyout. - * Unlike the main workspace, the flyout has only one scrollbar, in either the - * horizontal or the vertical direction. - * @type {!Blockly.Scrollbar} - * @private - */ - this.scrollbar_ = flyout.scrollbar_; - - /** - * Whether the flyout scrolls horizontally. If false, the flyout scrolls - * vertically. - * @type {boolean} - * @private - */ - this.horizontalLayout_ = flyout.horizontalLayout_; -}; -goog.inherits(Blockly.FlyoutDragger, Blockly.WorkspaceDragger); - -/** - * Move the appropriate scrollbar to drag the flyout. - * Since flyouts only scroll in one direction at a time, this will discard one - * of the calculated values. - * x and y are in pixels. - * @param {number} x The new x position to move the scrollbar to. - * @param {number} y The new y position to move the scrollbar to. - * @private - */ -Blockly.FlyoutDragger.prototype.updateScroll_ = function(x, y) { - // Move the scrollbar and the flyout will scroll automatically. - if (this.horizontalLayout_) { - this.scrollbar_.set(x); - } else { - this.scrollbar_.set(y); - } -}; diff --git a/core/flyout_extension_category_header.js b/core/flyout_extension_category_header.js deleted file mode 100644 index b97aa04b55..0000000000 --- a/core/flyout_extension_category_header.js +++ /dev/null @@ -1,159 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2018 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Class for a category header in the flyout for Scratch - * extensions which can display a textual label and a status button. - * @author ericr@media.mit.edu (Eric Rosenbaum) - */ -'use strict'; - -goog.provide('Blockly.FlyoutExtensionCategoryHeader'); - -goog.require('Blockly.FlyoutButton'); - -/** - * Class for a category header in the flyout for Scratch extensions which can - * display a textual label and a status button. - * @param {!Blockly.WorkspaceSvg} workspace The workspace in which to place this - * header. - * @param {!Blockly.WorkspaceSvg} targetWorkspace The flyout's target workspace. - * @param {!Element} xml The XML specifying the header. - * @extends {Blockly.FlyoutButton} - * @constructor - */ -Blockly.FlyoutExtensionCategoryHeader = function(workspace, targetWorkspace, xml) { - - this.init(workspace, targetWorkspace, xml, false); - - /** - * @type {number} - * @private - */ - this.flyoutWidth_ = this.targetWorkspace_.getFlyout().getWidth(); - - /** - * @type {string} - */ - this.extensionId = xml.getAttribute('id'); - - /** - * Whether this is a label at the top of a category. - * @type {boolean} - * @private - */ - this.isCategoryLabel_ = true; -}; -goog.inherits(Blockly.FlyoutExtensionCategoryHeader, Blockly.FlyoutButton); - -/** - * Create the label and button elements. - * @return {!Element} The SVG group. - */ -Blockly.FlyoutExtensionCategoryHeader.prototype.createDom = function() { - var cssClass = 'blocklyFlyoutLabel'; - - this.svgGroup_ = Blockly.utils.createSvgElement('g', {'class': cssClass}, - this.workspace_.getCanvas()); - - this.addTextSvg(true); - - this.refreshStatus(); - - var statusButtonWidth = 30; - var marginX = 20; - var marginY = 5; - var touchPadding = 16; - - var statusButtonX = this.workspace_.RTL ? (marginX - this.flyoutWidth_ + statusButtonWidth) : - (this.flyoutWidth_ - statusButtonWidth - marginX) / this.workspace_.scale; - - if (this.imageSrc_) { - /** @type {SVGElement} */ - this.imageElement_ = Blockly.utils.createSvgElement( - 'image', - { - 'class': 'blocklyFlyoutButton', - 'height': statusButtonWidth + 'px', - 'width': statusButtonWidth + 'px', - 'x': statusButtonX + 'px', - 'y': marginY + 'px' - }, - this.svgGroup_); - this.imageElementBackground_ = Blockly.utils.createSvgElement( - 'rect', - { - 'class': 'blocklyTouchTargetBackground', - 'height': statusButtonWidth + 2 * touchPadding + 'px', - 'width': statusButtonWidth + 2 * touchPadding + 'px', - 'x': (statusButtonX - touchPadding) + 'px', - 'y': (marginY - touchPadding) + 'px' - }, - this.svgGroup_); - this.setImageSrc(this.imageSrc_); - } - - this.callback_ = Blockly.statusButtonCallback.bind(this, this.extensionId); - - this.mouseUpWrapper_ = Blockly.bindEventWithChecks_(this.imageElementBackground_, 'mouseup', - this, this.onMouseUp_); - return this.svgGroup_; -}; - -/** - * Set the image on the status button using a status string. - */ -Blockly.FlyoutExtensionCategoryHeader.prototype.refreshStatus = function() { - var status = Blockly.FlyoutExtensionCategoryHeader.getExtensionState(this.extensionId); - var basePath = Blockly.mainWorkspace.options.pathToMedia; - if (status == Blockly.StatusButtonState.READY) { - this.setImageSrc(basePath + 'status-ready.svg'); - } - if (status == Blockly.StatusButtonState.NOT_READY) { - this.setImageSrc(basePath + 'status-not-ready.svg'); - } -}; - -/** - * Set the source URL of the image for the button. - * @param {?string} src New source. - * @package - */ -Blockly.FlyoutExtensionCategoryHeader.prototype.setImageSrc = function(src) { - if (src === null) { - // No change if null. - return; - } - this.imageSrc_ = src; - if (this.imageElement_) { - this.imageElement_.setAttributeNS('http://www.w3.org/1999/xlink', - 'xlink:href', this.imageSrc_ || ''); - } -}; - -/** - * Gets the extension state. Overridden externally. - * @param {string} extensionId The ID of the extension in question. - * @return {Blockly.StatusButtonState} The state of the extension. - * @public - */ -Blockly.FlyoutExtensionCategoryHeader.getExtensionState = function(/* extensionId */) { - return Blockly.StatusButtonState.NOT_READY; -}; diff --git a/core/flyout_horizontal.js b/core/flyout_horizontal.js deleted file mode 100644 index b80d844643..0000000000 --- a/core/flyout_horizontal.js +++ /dev/null @@ -1,475 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2011 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Flyout tray containing blocks which may be created. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.HorizontalFlyout'); - -goog.require('Blockly.Block'); -goog.require('Blockly.Comment'); -goog.require('Blockly.Events'); -goog.require('Blockly.FlyoutButton'); -goog.require('Blockly.Flyout'); -goog.require('Blockly.WorkspaceSvg'); -goog.require('goog.dom'); -goog.require('goog.dom.animationFrame.polyfill'); -goog.require('goog.events'); -goog.require('goog.math.Rect'); -goog.require('goog.userAgent'); - - -/** - * Class for a flyout. - * @param {!Object} workspaceOptions Dictionary of options for the workspace. - * @extends {Blockly.Flyout} - * @constructor - */ -Blockly.HorizontalFlyout = function(workspaceOptions) { - workspaceOptions.getMetrics = this.getMetrics_.bind(this); - workspaceOptions.setMetrics = this.setMetrics_.bind(this); - - Blockly.HorizontalFlyout.superClass_.constructor.call(this, workspaceOptions); - /** - * Flyout should be laid out horizontally vs vertically. - * @type {boolean} - * @private - */ - this.horizontalLayout_ = true; -}; -goog.inherits(Blockly.HorizontalFlyout, Blockly.Flyout); - -/** - * Return an object with all the metrics required to size scrollbars for the - * flyout. The following properties are computed: - * .viewHeight: Height of the visible rectangle, - * .viewWidth: Width of the visible rectangle, - * .contentHeight: Height of the contents, - * .contentWidth: Width of the contents, - * .viewTop: Offset of top edge of visible rectangle from parent, - * .contentTop: Offset of the top-most content from the y=0 coordinate, - * .absoluteTop: Top-edge of view. - * .viewLeft: Offset of the left edge of visible rectangle from parent, - * .contentLeft: Offset of the left-most content from the x=0 coordinate, - * .absoluteLeft: Left-edge of view. - * @return {Object} Contains size and position metrics of the flyout. - * @private - */ -Blockly.HorizontalFlyout.prototype.getMetrics_ = function() { - if (!this.isVisible()) { - // Flyout is hidden. - return null; - } - - try { - var optionBox = this.workspace_.getCanvas().getBBox(); - } catch (e) { - // Firefox has trouble with hidden elements (Bug 528969). - var optionBox = {height: 0, y: 0, width: 0, x: 0}; - } - - var absoluteTop = this.SCROLLBAR_PADDING; - var absoluteLeft = this.SCROLLBAR_PADDING; - if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) { - absoluteTop = 0; - } - var viewHeight = this.height_; - if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP) { - viewHeight += this.MARGIN; - } - var viewWidth = this.width_ - 2 * this.SCROLLBAR_PADDING; - - var metrics = { - viewHeight: viewHeight, - viewWidth: viewWidth, - contentHeight: optionBox.height * this.workspace_.scale + 2 * this.MARGIN, - contentWidth: optionBox.width * this.workspace_.scale + 2 * this.MARGIN, - viewTop: -this.workspace_.scrollY, - viewLeft: -this.workspace_.scrollX, - contentTop: optionBox.y, - contentLeft: optionBox.x, - absoluteTop: absoluteTop, - absoluteLeft: absoluteLeft - }; - return metrics; -}; - -/** - * Sets the translation of the flyout to match the scrollbars. - * @param {!Object} xyRatio Contains a y property which is a float - * between 0 and 1 specifying the degree of scrolling and a - * similar x property. - * @private - */ -Blockly.HorizontalFlyout.prototype.setMetrics_ = function(xyRatio) { - var metrics = this.getMetrics_(); - // This is a fix to an apparent race condition. - if (!metrics) { - return; - } - - if (goog.isNumber(xyRatio.x)) { - this.workspace_.scrollX = -metrics.contentWidth * xyRatio.x; - } - - this.workspace_.translate(this.workspace_.scrollX + metrics.absoluteLeft, - this.workspace_.scrollY + metrics.absoluteTop); - - if (this.categoryScrollPositions) { - this.selectCategoryByScrollPosition(-this.workspace_.scrollX); - } -}; - -/** - * Move the flyout to the edge of the workspace. - */ -Blockly.HorizontalFlyout.prototype.position = function() { - if (!this.isVisible()) { - return; - } - var targetWorkspaceMetrics = this.targetWorkspace_.getMetrics(); - if (!targetWorkspaceMetrics) { - // Hidden components will return null. - return; - } - var edgeWidth = this.horizontalLayout_ ? - targetWorkspaceMetrics.viewWidth - 2 * this.CORNER_RADIUS : - this.width_ - this.CORNER_RADIUS; - - var edgeHeight = this.horizontalLayout_ ? - this.height_ - this.CORNER_RADIUS : - targetWorkspaceMetrics.viewHeight - 2 * this.CORNER_RADIUS; - - this.setBackgroundPath_(edgeWidth, edgeHeight); - - var x = targetWorkspaceMetrics.absoluteLeft; - if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT) { - x += targetWorkspaceMetrics.viewWidth; - x -= this.width_; - } - - var y = targetWorkspaceMetrics.absoluteTop; - if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) { - y += targetWorkspaceMetrics.viewHeight; - y -= this.height_; - } - - // Record the height for Blockly.Flyout.getMetrics_, or width if the layout is - // horizontal. - if (this.horizontalLayout_) { - this.width_ = targetWorkspaceMetrics.viewWidth; - } else { - this.height_ = targetWorkspaceMetrics.viewHeight; - } - - this.svgGroup_.setAttribute("width", this.width_); - this.svgGroup_.setAttribute("height", this.height_); - var transform = 'translate(' + x + 'px,' + y + 'px)'; - Blockly.utils.setCssTransform(this.svgGroup_, transform); - - // Update the scrollbar (if one exists). - if (this.scrollbar_) { - // Set the scrollbars origin to be the top left of the flyout. - this.scrollbar_.setOrigin(x, y); - this.scrollbar_.resize(); - } - // The blocks need to be visible in order to be laid out and measured correctly, but we don't - // want the flyout to show up until it's properly sized. - // Opacity is set to zero in show(). - this.svgGroup_.style.opacity = 1; -}; - -/** - * Create and set the path for the visible boundaries of the flyout. - * @param {number} width The width of the flyout, not including the - * rounded corners. - * @param {number} height The height of the flyout, not including - * rounded corners. - * @private - */ -Blockly.HorizontalFlyout.prototype.setBackgroundPath_ = function(width, height) { - var atTop = this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP; - // Start at top left. - var path = ['M 0,' + (atTop ? 0 : this.CORNER_RADIUS)]; - - if (atTop) { - // Top. - path.push('h', width + 2 * this.CORNER_RADIUS); - // Right. - path.push('v', height); - // Bottom. - path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1, - -this.CORNER_RADIUS, this.CORNER_RADIUS); - path.push('h', -1 * width); - // Left. - path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1, - -this.CORNER_RADIUS, -this.CORNER_RADIUS); - path.push('z'); - } else { - // Top. - path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1, - this.CORNER_RADIUS, -this.CORNER_RADIUS); - path.push('h', width); - // Right. - path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1, - this.CORNER_RADIUS, this.CORNER_RADIUS); - path.push('v', height); - // Bottom. - path.push('h', -width - 2 * this.CORNER_RADIUS); - // Left. - path.push('z'); - } - this.svgBackground_.setAttribute('d', path.join(' ')); -}; - -/** - * Scroll the flyout to the top. - */ -Blockly.HorizontalFlyout.prototype.scrollToStart = function() { - this.scrollbar_.set(this.RTL ? Infinity : 0); -}; - -/** - * Scroll the flyout to a position. - * @param {number} pos The targeted scroll position. - * @package - */ -Blockly.HorizontalFlyout.prototype.scrollTo = function(pos) { - this.scrollTarget = pos * this.workspace_.scale; - - // Make sure not to set the scroll target past the farthest point we can - // scroll to, i.e. the content width minus the view width - var metrics = this.workspace_.getMetrics(); - var contentWidth = metrics.contentWidth; - var viewWidth = metrics.viewWidth; - this.scrollTarget = Math.min(this.scrollTarget, contentWidth - viewWidth); - - this.stepScrollAnimation(); -}; - -/** - * Scroll the flyout. - * @param {!Event} e Mouse wheel scroll event. - * @private - */ -Blockly.HorizontalFlyout.prototype.wheel_ = function(e) { - // remove scrollTarget to stop auto scrolling in stepScrollAnimation - this.scrollTarget = null; - - var delta = e.deltaX; - - // If we're scrolling more vertically than horizontally, use the vertical - // scroll delta instead. This allows people using a mouse wheel (which can - // only scroll vertically) to scroll the horizontal flyout. It also allows - // trackpad users to scroll it by scrolling either horizontally or - // vertically. - if (Math.abs(e.deltaY) > Math.abs(delta)) { - delta = e.deltaY; - } - - if (delta) { - // Firefox's mouse wheel deltas are a tenth that of Chrome/Safari. - // DeltaMode is 1 for a mouse wheel, but not for a trackpad scroll event - if (goog.userAgent.GECKO && (e.deltaMode === 1)) { - delta *= 10; - } - var metrics = this.getMetrics_(); - var pos = metrics.viewLeft + delta; - var limit = metrics.contentWidth - metrics.viewWidth; - pos = Math.min(pos, limit); - pos = Math.max(pos, 0); - this.scrollbar_.set(pos); - // When the flyout moves from a wheel event, hide WidgetDiv and DropDownDiv. - Blockly.WidgetDiv.hide(true); - Blockly.DropDownDiv.hideWithoutAnimation(); - } - - // Don't scroll the page. - e.preventDefault(); - // Don't propagate mousewheel event (zooming). - e.stopPropagation(); -}; - -/** - * Lay out the blocks in the flyout. - * @param {!Array.} contents The blocks and buttons to lay out. - * @param {!Array.} gaps The visible gaps between blocks. - * @private - */ -Blockly.HorizontalFlyout.prototype.layout_ = function(contents, gaps) { - this.workspace_.scale = this.targetWorkspace_.scale; - var margin = this.MARGIN; - var cursorX = margin; - var cursorY = margin; - if (this.RTL) { - contents = contents.reverse(); - } - - for (var i = 0, item; item = contents[i]; i++) { - if (item.type == 'block') { - var block = item.block; - var allBlocks = block.getDescendants(false); - for (var j = 0, child; child = allBlocks[j]; j++) { - // Mark blocks as being inside a flyout. This is used to detect and - // prevent the closure of the flyout if the user right-clicks on such a - // block. - child.isInFlyout = true; - } - var root = block.getSvgRoot(); - var blockHW = block.getHeightWidth(); - - var moveX = cursorX; - if (this.RTL) { - moveX += blockHW.width; - } - - block.moveBy(moveX, cursorY); - cursorX += blockHW.width + gaps[i]; - - // Create an invisible rectangle under the block to act as a button. Just - // using the block as a button is poor, since blocks have holes in them. - var rect = Blockly.utils.createSvgElement('rect', {'fill-opacity': 0}, null); - rect.tooltip = block; - Blockly.Tooltip.bindMouseEvents(rect); - // Add the rectangles under the blocks, so that the blocks' tooltips work. - this.workspace_.getCanvas().insertBefore(rect, block.getSvgRoot()); - block.flyoutRect_ = rect; - this.backgroundButtons_[i] = rect; - - this.addBlockListeners_(root, block, rect); - } else if (item.type == 'button') { - var button = item.button; - var buttonSvg = button.createDom(); - button.moveTo(cursorX, cursorY); - button.show(); - // Clicking on a flyout button or label is a lot like clicking on the - // flyout background. - this.listeners_.push(Blockly.bindEventWithChecks_(buttonSvg, 'mousedown', - this, this.onMouseDown_)); - - - this.buttons_.push(button); - cursorX += (button.width + gaps[i]); - } - } -}; - -/** - * Determine if a drag delta is toward the workspace, based on the position - * and orientation of the flyout. This to decide if a new block should be - * created or if the flyout should scroll. - * @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has - * moved from the position at mouse down, in pixel units. - * @return {boolean} true if the drag is toward the workspace. - * @package - */ -Blockly.HorizontalFlyout.prototype.isDragTowardWorkspace = function(currentDragDeltaXY) { - var dx = currentDragDeltaXY.x; - var dy = currentDragDeltaXY.y; - // Direction goes from -180 to 180, with 0 toward the right and 90 on top. - var dragDirection = Math.atan2(dy, dx) / Math.PI * 180; - - var draggingTowardWorkspace = false; - var range = this.dragAngleRange_; - if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP) { - // Horizontal at top. - if (dragDirection < 90 + range && dragDirection > 90 - range) { - draggingTowardWorkspace = true; - } - } else { - // Horizontal at bottom. - if (dragDirection > -90 - range && dragDirection < -90 + range) { - draggingTowardWorkspace = true; - } - } - return draggingTowardWorkspace; -}; - -/** - * Return the deletion rectangle for this flyout in viewport coordinates. - * @return {goog.math.Rect} Rectangle in which to delete. - */ -Blockly.HorizontalFlyout.prototype.getClientRect = function() { - if (!this.svgGroup_) { - return null; - } - - var flyoutRect = this.svgGroup_.getBoundingClientRect(); - // BIG_NUM is offscreen padding so that blocks dragged beyond the shown flyout - // area are still deleted. Must be larger than the largest screen size, - // but be smaller than half Number.MAX_SAFE_INTEGER (not available on IE). - var BIG_NUM = 1000000000; - var y = flyoutRect.top; - var height = flyoutRect.height; - - if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP) { - return new goog.math.Rect(-BIG_NUM, y - BIG_NUM, BIG_NUM * 2, - BIG_NUM + height); - } else if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) { - return new goog.math.Rect(-BIG_NUM, y, BIG_NUM * 2, - BIG_NUM + height); - } -}; - -/** - * Compute height of flyout. Position button under each block. - * For RTL: Lay out the blocks right-aligned. - * @param {!Array} blocks The blocks to reflow. - */ -Blockly.HorizontalFlyout.prototype.reflowInternal_ = function(blocks) { - this.workspace_.scale = this.targetWorkspace_.scale; - var flyoutHeight = 0; - for (var i = 0, block; block = blocks[i]; i++) { - flyoutHeight = Math.max(flyoutHeight, block.getHeightWidth().height); - } - flyoutHeight += this.MARGIN * 1.5; - flyoutHeight *= this.workspace_.scale; - flyoutHeight += Blockly.Scrollbar.scrollbarThickness; - if (this.height_ != flyoutHeight) { - for (var i = 0, block; block = blocks[i]; i++) { - var blockHW = block.getHeightWidth(); - if (block.flyoutRect_) { - block.flyoutRect_.setAttribute('width', blockHW.width); - block.flyoutRect_.setAttribute('height', blockHW.height); - // Rectangles behind blocks with output tabs are shifted a bit. - var blockXY = block.getRelativeToSurfaceXY(); - block.flyoutRect_.setAttribute('y', blockXY.y); - block.flyoutRect_.setAttribute('x', - this.RTL ? blockXY.x - blockHW.width : blockXY.x); - // For hat blocks we want to shift them down by the hat height - // since the y coordinate is the corner, not the top of the hat. - var hatOffset = - block.startHat_ ? Blockly.BlockSvg.START_HAT_HEIGHT : 0; - if (hatOffset) { - block.moveBy(0, hatOffset); - } - block.flyoutRect_.setAttribute('y', blockXY.y); - } - } - // Record the height for .getMetrics_ and .position. - this.height_ = flyoutHeight; - // Call this since it is possible the trash and zoom buttons need - // to move. e.g. on a bottom positioned flyout when zoom is clicked. - this.targetWorkspace_.resize(); - } -}; diff --git a/core/flyout_vertical.js b/core/flyout_vertical.js deleted file mode 100644 index 8b6a45169c..0000000000 --- a/core/flyout_vertical.js +++ /dev/null @@ -1,770 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Layout code for a vertical variant of the flyout. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.VerticalFlyout'); - -goog.require('Blockly.Block'); -goog.require('Blockly.Comment'); -goog.require('Blockly.Events'); -goog.require('Blockly.Flyout'); -goog.require('Blockly.FlyoutButton'); -goog.require('Blockly.utils'); -goog.require('Blockly.WorkspaceSvg'); -goog.require('goog.dom'); -goog.require('goog.dom.animationFrame.polyfill'); -goog.require('goog.events'); -goog.require('goog.math.Rect'); -goog.require('goog.userAgent'); - - -/** - * Class for a flyout. - * @param {!Object} workspaceOptions Dictionary of options for the workspace. - * @extends {Blockly.Flyout} - * @constructor - */ -Blockly.VerticalFlyout = function(workspaceOptions) { - workspaceOptions.getMetrics = this.getMetrics_.bind(this); - workspaceOptions.setMetrics = this.setMetrics_.bind(this); - - Blockly.VerticalFlyout.superClass_.constructor.call(this, workspaceOptions); - /** - * Flyout should be laid out vertically. - * @type {boolean} - * @private - */ - this.horizontalLayout_ = false; - - /** - * Map of checkboxes that correspond to monitored blocks. - * Each element is an object containing the SVG for the checkbox, a boolean - * for its checked state, and the block the checkbox is associated with. - * @type {!Object.} - * @private - */ - this.checkboxes_ = {}; -}; -goog.inherits(Blockly.VerticalFlyout, Blockly.Flyout); - -/** - * Does the flyout automatically close when a block is created? - * @type {boolean} - */ -Blockly.VerticalFlyout.prototype.autoClose = false; - -/** - * The width of the flyout, if not otherwise specified. - * @type {number} - */ -Blockly.VerticalFlyout.prototype.DEFAULT_WIDTH = 250; - -/** - * Size of a checkbox next to a variable reporter. - * @type {number} - * @const - */ -Blockly.VerticalFlyout.prototype.CHECKBOX_SIZE = 25; - -/** - * Amount of touchable padding around reporter checkboxes. - * @type {number} - * @const - */ -Blockly.VerticalFlyout.prototype.CHECKBOX_TOUCH_PADDING = 12; - -/** - * SVG path data for checkmark in checkbox. - * @type {string} - * @const - */ -Blockly.VerticalFlyout.prototype.CHECKMARK_PATH = - 'M' + Blockly.VerticalFlyout.prototype.CHECKBOX_SIZE / 4 + - ' ' + Blockly.VerticalFlyout.prototype.CHECKBOX_SIZE / 2 + - 'L' + 5 * Blockly.VerticalFlyout.prototype.CHECKBOX_SIZE / 12 + - ' ' + 2 * Blockly.VerticalFlyout.prototype.CHECKBOX_SIZE / 3 + - 'L' + 3 * Blockly.VerticalFlyout.prototype.CHECKBOX_SIZE / 4 + - ' ' + Blockly.VerticalFlyout.prototype.CHECKBOX_SIZE / 3; - -/** - * Size of the checkbox corner radius - * @type {number} - * @const - */ -Blockly.VerticalFlyout.prototype.CHECKBOX_CORNER_RADIUS = 5; - -/** - * Space above and around the checkbox. - * @type {number} - * @const - */ -Blockly.VerticalFlyout.prototype.CHECKBOX_MARGIN = Blockly.Flyout.prototype.MARGIN; - -/** - * Total additional width of a row that contains a checkbox. - * @type {number} - * @const - */ -Blockly.VerticalFlyout.prototype.CHECKBOX_SPACE_X = - Blockly.VerticalFlyout.prototype.CHECKBOX_SIZE + - 2 * Blockly.VerticalFlyout.prototype.CHECKBOX_MARGIN; - -/** - * Initializes the flyout. - * @param {!Blockly.Workspace} targetWorkspace The workspace in which to create - * new blocks. - */ -Blockly.VerticalFlyout.prototype.init = function(targetWorkspace) { - Blockly.VerticalFlyout.superClass_.init.call(this, targetWorkspace); - this.workspace_.scale = targetWorkspace.scale; -}; - -/** - * Creates the flyout's DOM. Only needs to be called once. - * @param {string} tagName HTML element - * @return {!Element} The flyout's SVG group. - */ -Blockly.VerticalFlyout.prototype.createDom = function(tagName) { - Blockly.VerticalFlyout.superClass_.createDom.call(this, tagName); - - /* - - - - - - - */ - this.defs_ = Blockly.utils.createSvgElement('defs', {}, this.svgGroup_); - var clipPath = Blockly.utils.createSvgElement('clipPath', - {'id':'blocklyBlockMenuClipPath'}, this.defs_); - this.clipRect_ = Blockly.utils.createSvgElement('rect', - { - 'id': 'blocklyBlockMenuClipRect', - 'height': '0', - 'width': '0', - 'y': '0', - 'x': '0' - }, - clipPath); - this.workspace_.svgGroup_.setAttribute( - 'clip-path', 'url(#blocklyBlockMenuClipPath)'); - - return this.svgGroup_; -}; - -/** - * Calculate the bounding box of the flyout. - * - * @return {Object} Contains the position and size of the bounding - * box containing the elements (blocks, buttons, labels) in the flyout. - */ -Blockly.VerticalFlyout.prototype.getContentBoundingBox_ = function() { - var contentBounds = this.workspace_.getBlocksBoundingBox(); - var bounds = { - xMin: contentBounds.x, - yMin: contentBounds.y, - xMax: contentBounds.x + contentBounds.width, - yMax: contentBounds.y + contentBounds.height - }; - - // Check if any of the buttons/labels are outside the blocks bounding box. - for (var i = 0; i < this.buttons_.length; i ++) { - var button = this.buttons_[i]; - var buttonPosition = button.getPosition(); - if (buttonPosition.x < bounds.xMin) { - bounds.xMin = buttonPosition.x; - } - if (buttonPosition.y < bounds.yMin) { - bounds.yMin = buttonPosition.y; - } - // Button extends past the bounding box to the right. - if (buttonPosition.x + button.width > bounds.xMax) { - bounds.xMax = buttonPosition.x + button.width; - } - - // Button extends past the bounding box on the bottom - if (buttonPosition.y + button.height > bounds.yMax) { - bounds.yMax = buttonPosition.y + button.height; - } - } - - return { - x: bounds.xMin, - y: bounds.yMin, - width: bounds.xMax - bounds.xMin, - height: bounds.yMax - bounds.yMin, - }; -}; - -/** - * Return an object with all the metrics required to size scrollbars for the - * flyout. The following properties are computed: - * .viewHeight: Height of the visible rectangle, - * .viewWidth: Width of the visible rectangle, - * .contentHeight: Height of the contents, - * .contentWidth: Width of the contents, - * .viewTop: Offset of top edge of visible rectangle from parent, - * .contentTop: Offset of the top-most content from the y=0 coordinate, - * .absoluteTop: Top-edge of view. - * .viewLeft: Offset of the left edge of visible rectangle from parent, - * .contentLeft: Offset of the left-most content from the x=0 coordinate, - * .absoluteLeft: Left-edge of view. - * @return {Object} Contains size and position metrics of the flyout. - * @private - */ -Blockly.VerticalFlyout.prototype.getMetrics_ = function() { - if (!this.isVisible()) { - // Flyout is hidden. - return null; - } - - var optionBox = this.getContentBoundingBox_(); - - // Padding for the end of the scrollbar. - var absoluteTop = this.SCROLLBAR_PADDING; - var absoluteLeft = 0; - - var viewHeight = this.height_ - 2 * this.SCROLLBAR_PADDING; - var viewWidth = this.getWidth() - this.SCROLLBAR_PADDING; - - // Add padding to the bottom of the flyout, so we can scroll to the top of - // the last category. - var contentHeight = optionBox.height * this.workspace_.scale; - this.recordCategoryScrollPositions_(); - var bottomPadding = this.MARGIN; - if (this.categoryScrollPositions.length > 0) { - var lastLabel = this.categoryScrollPositions[ - this.categoryScrollPositions.length - 1]; - var lastPos = lastLabel.position * this.workspace_.scale; - var lastCategoryHeight = contentHeight - lastPos; - if (lastCategoryHeight < viewHeight) { - bottomPadding = viewHeight - lastCategoryHeight; - } - } - - var metrics = { - viewHeight: viewHeight, - viewWidth: viewWidth, - contentHeight: contentHeight + bottomPadding, - contentWidth: optionBox.width * this.workspace_.scale + 2 * this.MARGIN, - viewTop: -this.workspace_.scrollY + optionBox.y, - viewLeft: -this.workspace_.scrollX, - contentTop: optionBox.y, - contentLeft: optionBox.x, - absoluteTop: absoluteTop, - absoluteLeft: absoluteLeft - }; - return metrics; -}; - -/** - * Sets the translation of the flyout to match the scrollbars. - * @param {!Object} xyRatio Contains a y property which is a float - * between 0 and 1 specifying the degree of scrolling and a - * similar x property. - * @private - */ -Blockly.VerticalFlyout.prototype.setMetrics_ = function(xyRatio) { - var metrics = this.getMetrics_(); - // This is a fix to an apparent race condition. - if (!metrics) { - return; - } - if (goog.isNumber(xyRatio.y)) { - this.workspace_.scrollY = -metrics.contentHeight * xyRatio.y; - } - this.workspace_.translate(this.workspace_.scrollX + metrics.absoluteLeft, - this.workspace_.scrollY + metrics.absoluteTop); - - this.clipRect_.setAttribute('height', Math.max(0, metrics.viewHeight) + 'px'); - this.clipRect_.setAttribute('width', metrics.viewWidth + 'px'); - - if (this.categoryScrollPositions) { - this.selectCategoryByScrollPosition(-this.workspace_.scrollY); - } -}; - -/** - * Move the flyout to the edge of the workspace. - */ -Blockly.VerticalFlyout.prototype.position = function() { - if (!this.isVisible()) { - return; - } - var targetWorkspaceMetrics = this.targetWorkspace_.getMetrics(); - if (!targetWorkspaceMetrics) { - // Hidden components will return null. - return; - } - - // This version of the flyout does not change width to fit its contents. - // Instead it matches the width of its parent or uses a default value. - this.width_ = this.getWidth(); - - if (this.parentToolbox_) { - var toolboxWidth = this.parentToolbox_.getWidth(); - var categoryWidth = toolboxWidth - this.width_; - var x = this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT ? - targetWorkspaceMetrics.viewWidth : categoryWidth; - var y = 0; - } else { - var x = this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT ? - targetWorkspaceMetrics.viewWidth - this.width_ : 0; - var y = 0; - } - - // Record the height for Blockly.Flyout.getMetrics_ - this.height_ = Math.max(0, targetWorkspaceMetrics.viewHeight - y); - - this.setBackgroundPath_(this.width_, this.height_); - - this.svgGroup_.setAttribute("width", this.width_); - this.svgGroup_.setAttribute("height", this.height_); - var transform = 'translate(' + x + 'px,' + y + 'px)'; - Blockly.utils.setCssTransform(this.svgGroup_, transform); - - // Update the scrollbar (if one exists). - if (this.scrollbar_) { - // Set the scrollbars origin to be the top left of the flyout. - this.scrollbar_.setOrigin(x, y); - this.scrollbar_.resize(); - } - // The blocks need to be visible in order to be laid out and measured - // correctly, but we don't want the flyout to show up until it's properly - // sized. Opacity is set to zero in show(). - this.svgGroup_.style.opacity = 1; -}; - -/** - * Create and set the path for the visible boundaries of the flyout. - * @param {number} width The width of the flyout, not including the - * rounded corners. - * @param {number} height The height of the flyout, not including - * rounded corners. - * @private - */ -Blockly.VerticalFlyout.prototype.setBackgroundPath_ = function(width, height) { - var atRight = this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT; - // Decide whether to start on the left or right. - var path = ['M ' + 0 + ',0']; - // Top. - path.push('h', width); - // Rounded corner. - path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, - atRight ? 0 : 1, - atRight ? -this.CORNER_RADIUS : this.CORNER_RADIUS, - this.CORNER_RADIUS); - // Side closest to workspace. - path.push('v', Math.max(0, height - this.CORNER_RADIUS * 2)); - // Rounded corner. - path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, - atRight ? 0 : 1, - atRight ? this.CORNER_RADIUS : -this.CORNER_RADIUS, - this.CORNER_RADIUS); - // Bottom. - path.push('h', -width); - path.push('z'); - this.svgBackground_.setAttribute('d', path.join(' ')); -}; - -/** - * Scroll the flyout to the top. - */ -Blockly.VerticalFlyout.prototype.scrollToStart = function() { - this.scrollbar_.set(0); -}; - -/** - * Scroll the flyout to a position. - * @param {number} pos The targeted scroll position in workspace coordinates. - * @package - */ -Blockly.VerticalFlyout.prototype.scrollTo = function(pos) { - this.scrollTarget = pos * this.workspace_.scale; - - // Make sure not to set the scroll target below the lowest point we can - // scroll to, i.e. the content height minus the view height - var metrics = this.workspace_.getMetrics(); - var contentHeight = metrics.contentHeight; - var viewHeight = metrics.viewHeight; - this.scrollTarget = Math.min(this.scrollTarget, contentHeight - viewHeight); - - this.stepScrollAnimation(); -}; - -/** - * Scroll the flyout. - * @param {!Event} e Mouse wheel scroll event. - * @private - */ -Blockly.VerticalFlyout.prototype.wheel_ = function(e) { - // remove scrollTarget to stop auto scrolling in stepScrollAnimation - this.scrollTarget = null; - - var delta = e.deltaY; - - if (delta) { - // Firefox's mouse wheel deltas are a tenth that of Chrome/Safari. - // DeltaMode is 1 for a mouse wheel, but not for a trackpad scroll event - if (goog.userAgent.GECKO && (e.deltaMode === 1)) { - delta *= 10; - } - var metrics = this.getMetrics_(); - var pos = (metrics.viewTop - metrics.contentTop) + delta; - var limit = metrics.contentHeight - metrics.viewHeight; - pos = Math.min(pos, limit); - pos = Math.max(pos, 0); - this.scrollbar_.set(pos); - // When the flyout moves from a wheel event, hide WidgetDiv and DropDownDiv. - Blockly.WidgetDiv.hide(true); - Blockly.DropDownDiv.hideWithoutAnimation(); - } - - // Don't scroll the page. - e.preventDefault(); - // Don't propagate mousewheel event (zooming). - e.stopPropagation(); -}; - -/** - * Delete blocks and background buttons from a previous showing of the flyout. - * @private - */ -Blockly.VerticalFlyout.prototype.clearOldBlocks_ = function() { - Blockly.VerticalFlyout.superClass_.clearOldBlocks_.call(this); - - // Do the same for checkboxes. - for (var checkboxId in this.checkboxes_) { - if (!Object.prototype.hasOwnProperty.call(this.checkboxes_, checkboxId)) { - continue; - } - var checkbox = this.checkboxes_[checkboxId]; - checkbox.block.flyoutCheckbox = null; - goog.dom.removeNode(checkbox.svgRoot); - } - this.checkboxes_ = {}; -}; - -/** - * Add listeners to a block that has been added to the flyout. - * @param {Element} root The root node of the SVG group the block is in. - * @param {!Blockly.Block} block The block to add listeners for. - * @param {!Element} rect The invisible rectangle under the block that acts as - * a button for that block. - * @private - */ -Blockly.VerticalFlyout.prototype.addBlockListeners_ = function(root, block, - rect) { - Blockly.VerticalFlyout.superClass_.addBlockListeners_.call(this, root, block, - rect); - if (block.flyoutCheckbox) { - this.listeners_.push(Blockly.bindEvent_(block.flyoutCheckbox.svgRoot, - 'mousedown', null, this.checkboxClicked_(block.flyoutCheckbox))); - } -}; - -/** - * Lay out the blocks in the flyout. - * @param {!Array.} contents The blocks and buttons to lay out. - * @param {!Array.} gaps The visible gaps between blocks. - * @private - */ -Blockly.VerticalFlyout.prototype.layout_ = function(contents, gaps) { - var margin = this.MARGIN; - var flyoutWidth = this.getWidth() / this.workspace_.scale; - var cursorX = margin; - var cursorY = margin; - - for (var i = 0, item; item = contents[i]; i++) { - if (item.type == 'block') { - var block = item.block; - var allBlocks = block.getDescendants(false); - for (var j = 0, child; child = allBlocks[j]; j++) { - // Mark blocks as being inside a flyout. This is used to detect and - // prevent the closure of the flyout if the user right-clicks on such a - // block. - child.isInFlyout = true; - } - var root = block.getSvgRoot(); - var blockHW = block.getHeightWidth(); - - // Figure out where the block goes, taking into account its size, whether - // we're in RTL mode, and whether it has a checkbox. - var oldX = block.getRelativeToSurfaceXY().x; - var newX = flyoutWidth - this.MARGIN; - - var moveX = this.RTL ? newX - oldX : margin; - if (block.hasCheckboxInFlyout()) { - this.createCheckbox_(block, cursorX, cursorY, blockHW); - if (this.RTL) { - moveX -= (this.CHECKBOX_SIZE + this.CHECKBOX_MARGIN); - } else { - moveX += this.CHECKBOX_SIZE + this.CHECKBOX_MARGIN; - } - } - - // The block moves a bit extra for the hat, but the block's rectangle - // doesn't. That's because the hat actually extends up from 0. - block.moveBy(moveX, - cursorY + (block.startHat_ ? Blockly.BlockSvg.START_HAT_HEIGHT : 0)); - - var rect = this.createRect_(block, this.RTL ? moveX - blockHW.width : moveX, cursorY, blockHW, i); - - this.addBlockListeners_(root, block, rect); - - cursorY += blockHW.height + gaps[i] + (block.startHat_ ? Blockly.BlockSvg.START_HAT_HEIGHT : 0); - } else if (item.type == 'button') { - var button = item.button; - var buttonSvg = button.createDom(); - if (this.RTL) { - button.moveTo(flyoutWidth - this.MARGIN - button.width, cursorY); - } else { - button.moveTo(cursorX, cursorY); - } - button.show(); - // Clicking on a flyout button or label is a lot like clicking on the - // flyout background. - this.listeners_.push(Blockly.bindEventWithChecks_( - buttonSvg, 'mousedown', this, this.onMouseDown_)); - - this.buttons_.push(button); - cursorY += button.height + gaps[i]; - } - } -}; - -/** - * Create and place a rectangle corresponding to the given block. - * @param {!Blockly.Block} block The block to associate the rect to. - * @param {number} x The x position of the cursor during this layout pass. - * @param {number} y The y position of the cursor during this layout pass. - * @param {!{height: number, width: number}} blockHW The height and width of the - * block. - * @param {number} index The index into the background buttons list where this - * rect should be placed. - * @return {!SVGElement} Newly created SVG element for the rectangle behind the - * block. - * @private - */ -Blockly.VerticalFlyout.prototype.createRect_ = function(block, x, y, - blockHW, index) { - // Create an invisible rectangle under the block to act as a button. Just - // using the block as a button is poor, since blocks have holes in them. - var rect = Blockly.utils.createSvgElement('rect', - { - 'fill-opacity': 0, - 'x': x, - 'y': y, - 'height': blockHW.height, - 'width': blockHW.width - }, null); - rect.tooltip = block; - Blockly.Tooltip.bindMouseEvents(rect); - // Add the rectangles under the blocks, so that the blocks' tooltips work. - this.workspace_.getCanvas().insertBefore(rect, block.getSvgRoot()); - - block.flyoutRect_ = rect; - this.backgroundButtons_[index] = rect; - return rect; -}; - -/** - * Create and place a checkbox corresponding to the given block. - * @param {!Blockly.Block} block The block to associate the checkbox to. - * @param {number} cursorX The x position of the cursor during this layout pass. - * @param {number} cursorY The y position of the cursor during this layout pass. - * @param {!{height: number, width: number}} blockHW The height and width of the - * block. - * @private - */ -Blockly.VerticalFlyout.prototype.createCheckbox_ = function(block, cursorX, - cursorY, blockHW) { - var checkboxState = Blockly.VerticalFlyout.getCheckboxState(block.id); - var svgRoot = block.getSvgRoot(); - var extraSpace = this.CHECKBOX_SIZE + this.CHECKBOX_MARGIN; - var width = this.RTL ? this.getWidth() / this.workspace_.scale - extraSpace : cursorX; - var height = cursorY + blockHW.height / 2 - this.CHECKBOX_SIZE / 2; - var touchMargin = this.CHECKBOX_TOUCH_PADDING; - var checkboxGroup = Blockly.utils.createSvgElement('g', - { - 'transform': 'translate(' + width + ', ' + height + ')' - }, null); - Blockly.utils.createSvgElement('rect', - { - 'class': 'blocklyFlyoutCheckbox', - 'height': this.CHECKBOX_SIZE, - 'width': this.CHECKBOX_SIZE, - 'rx': this.CHECKBOX_CORNER_RADIUS, - 'ry': this.CHECKBOX_CORNER_RADIUS - }, checkboxGroup); - Blockly.utils.createSvgElement('path', - { - 'class': 'blocklyFlyoutCheckboxPath', - 'd': this.CHECKMARK_PATH - }, checkboxGroup); - Blockly.utils.createSvgElement('rect', - { - 'class': 'blocklyTouchTargetBackground', - 'x': -touchMargin + 'px', - 'y': -touchMargin + 'px', - 'height': this.CHECKBOX_SIZE + 2 * touchMargin, - 'width': this.CHECKBOX_SIZE + 2 * touchMargin, - }, checkboxGroup); - var checkboxObj = {svgRoot: checkboxGroup, clicked: checkboxState, block: block}; - - if (checkboxState) { - Blockly.utils.addClass((checkboxObj.svgRoot), 'checked'); - } - - block.flyoutCheckbox = checkboxObj; - this.workspace_.getCanvas().insertBefore(checkboxGroup, svgRoot); - this.checkboxes_[block.id] = checkboxObj; -}; - -/** - * Respond to a click on a checkbox in the flyout. - * @param {!Object} checkboxObj An object containing the svg element of the - * checkbox, a boolean for the state of the checkbox, and the block the - * checkbox is associated with. - * @return {!Function} Function to call when checkbox is clicked. - * @private - */ -Blockly.VerticalFlyout.prototype.checkboxClicked_ = function(checkboxObj) { - return function(e) { - this.setCheckboxState(checkboxObj.block.id, !checkboxObj.clicked); - // This event has been handled. No need to bubble up to the document. - e.stopPropagation(); - e.preventDefault(); - }.bind(this); -}; - -/** - * Set the state of a checkbox by block ID. - * @param {string} blockId ID of the block whose checkbox should be set - * @param {boolean} value Value to set the checkbox to. - * @public - */ -Blockly.VerticalFlyout.prototype.setCheckboxState = function(blockId, value) { - var checkboxObj = this.checkboxes_[blockId]; - if (!checkboxObj || checkboxObj.clicked === value) { - return; - } - - var oldValue = checkboxObj.clicked; - checkboxObj.clicked = value; - - if (checkboxObj.clicked) { - Blockly.utils.addClass(checkboxObj.svgRoot, 'checked'); - } else { - Blockly.utils.removeClass(checkboxObj.svgRoot, 'checked'); - } - - Blockly.Events.fire(new Blockly.Events.Change( - checkboxObj.block, 'checkbox', null, oldValue, value)); -}; - -/** - * Determine if a drag delta is toward the workspace, based on the position - * and orientation of the flyout. This to decide if a new block should be - * created or if the flyout should scroll. - * @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has - * moved from the position at mouse down, in pixel units. - * @return {boolean} true if the drag is toward the workspace. - * @package - */ -Blockly.VerticalFlyout.prototype.isDragTowardWorkspace = function(currentDragDeltaXY) { - var dx = currentDragDeltaXY.x; - var dy = currentDragDeltaXY.y; - // Direction goes from -180 to 180, with 0 toward the right and 90 on top. - var dragDirection = Math.atan2(dy, dx) / Math.PI * 180; - - var draggingTowardWorkspace = false; - var range = this.dragAngleRange_; - if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_LEFT) { - // Vertical at left. - if (dragDirection < range && dragDirection > -range) { - draggingTowardWorkspace = true; - } - } else { - // Vertical at right. - if (dragDirection < -180 + range || dragDirection > 180 - range) { - draggingTowardWorkspace = true; - } - } - return draggingTowardWorkspace; -}; - -/** - * Return the deletion rectangle for this flyout in viewport coordinates. - * Deletion area is the height of the flyout, but extends to the left (in LTR) - * by a lot in order to allow for deleting blocks when dragged beyond the left - * window edge. In RTL, the delete area extends off to the right. - * The top/bottom do not extend to allow dragging blocks outside of the workspace - * to be dropped (e.g. to the backpack). - * @return {goog.math.Rect} Rectangle in which to delete. - */ -Blockly.VerticalFlyout.prototype.getClientRect = function() { - if (!this.svgGroup_) { - return null; - } - - var flyoutRect = this.svgGroup_.getBoundingClientRect(); - // BIG_NUM is offscreen padding so that blocks dragged beyond the shown flyout - // area are still deleted. Must be larger than the largest screen size, - // but be smaller than half Number.MAX_SAFE_INTEGER (not available on IE). - var BIG_NUM = 1000000000; - var x = flyoutRect.left; - var y = flyoutRect.top; - var width = flyoutRect.width; - var height = flyoutRect.height; - - if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_LEFT) { - return new goog.math.Rect(x - BIG_NUM, y, BIG_NUM + width, height); - } else { // Right - return new goog.math.Rect(x, y, BIG_NUM + width, height); - } -}; - -/** - * Compute width of flyout. Position button under each block. - * For RTL: Lay out the blocks right-aligned. - * @param {!Array} blocks The blocks to reflow. - */ -Blockly.VerticalFlyout.prototype.reflowInternal_ = function(/* blocks */) { - // This is a no-op because the flyout is a fixed size. - return; -}; - -/** - * Gets the checkbox state for a block - * @param {string} blockId The ID of the block in question. - * @return {boolean} Whether the block is checked. - * @public - */ -Blockly.VerticalFlyout.getCheckboxState = function(/* blockId */) { - return false; -}; diff --git a/core/generator.js b/core/generator.js deleted file mode 100644 index b567304f83..0000000000 --- a/core/generator.js +++ /dev/null @@ -1,426 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Utility functions for generating executable code from - * Blockly code. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.Generator'); - -goog.require('Blockly.Block'); -goog.require('goog.asserts'); - - -/** - * Class for a code generator that translates the blocks into a language. - * @param {string} name Language name of this generator. - * @constructor - */ -Blockly.Generator = function(name) { - this.name_ = name; - this.FUNCTION_NAME_PLACEHOLDER_REGEXP_ = - new RegExp(this.FUNCTION_NAME_PLACEHOLDER_, 'g'); -}; - -/** - * Category to separate generated function names from variables and procedures. - */ -Blockly.Generator.NAME_TYPE = 'generated_function'; - -/** - * Arbitrary code to inject into locations that risk causing infinite loops. - * Any instances of '%1' will be replaced by the block ID that failed. - * E.g. ' checkTimeout(%1);\n' - * @type {?string} - */ -Blockly.Generator.prototype.INFINITE_LOOP_TRAP = null; - -/** - * Arbitrary code to inject before every statement. - * Any instances of '%1' will be replaced by the block ID of the statement. - * E.g. 'highlight(%1);\n' - * @type {?string} - */ -Blockly.Generator.prototype.STATEMENT_PREFIX = null; - -/** - * The method of indenting. Defaults to two spaces, but language generators - * may override this to increase indent or change to tabs. - * @type {string} - */ -Blockly.Generator.prototype.INDENT = ' '; - -/** - * Maximum length for a comment before wrapping. Does not account for - * indenting level. - * @type {number} - */ -Blockly.Generator.prototype.COMMENT_WRAP = 60; - -/** - * List of outer-inner pairings that do NOT require parentheses. - * @type {!Array.>} - */ -Blockly.Generator.prototype.ORDER_OVERRIDES = []; - -/** - * Generate code for all blocks in the workspace to the specified language. - * @param {Blockly.Workspace} workspace Workspace to generate code from. - * @return {string} Generated code. - */ -Blockly.Generator.prototype.workspaceToCode = function(workspace) { - if (!workspace) { - // Backwards compatibility from before there could be multiple workspaces. - console.warn('No workspace specified in workspaceToCode call. Guessing.'); - workspace = Blockly.getMainWorkspace(); - } - var code = []; - this.init(workspace); - var blocks = workspace.getTopBlocks(true); - for (var x = 0, block; block = blocks[x]; x++) { - var line = this.blockToCode(block); - if (goog.isArray(line)) { - // Value blocks return tuples of code and operator order. - // Top-level blocks don't care about operator order. - line = line[0]; - } - if (line) { - if (block.outputConnection && this.scrubNakedValue) { - // This block is a naked value. Ask the language's code generator if - // it wants to append a semicolon, or something. - line = this.scrubNakedValue(line); - } - code.push(line); - } - } - code = code.join('\n'); // Blank line between each section. - code = this.finish(code); - // Final scrubbing of whitespace. - code = code.replace(/^\s+\n/, ''); - code = code.replace(/\n\s+$/, '\n'); - code = code.replace(/[ \t]+\n/g, '\n'); - return code; -}; - -// The following are some helpful functions which can be used by multiple -// languages. - -/** - * Prepend a common prefix onto each line of code. - * @param {string} text The lines of code. - * @param {string} prefix The common prefix. - * @return {string} The prefixed lines of code. - */ -Blockly.Generator.prototype.prefixLines = function(text, prefix) { - return prefix + text.replace(/(?!\n$)\n/g, '\n' + prefix); -}; - -/** - * Recursively spider a tree of blocks, returning all their comments. - * @param {!Blockly.Block} block The block from which to start spidering. - * @return {string} Concatenated list of comments. - */ -Blockly.Generator.prototype.allNestedComments = function(block) { - var comments = []; - var blocks = block.getDescendants(true); - for (var i = 0; i < blocks.length; i++) { - var comment = blocks[i].getCommentText(); - if (comment) { - comments.push(comment); - } - } - // Append an empty string to create a trailing line break when joined. - if (comments.length) { - comments.push(''); - } - return comments.join('\n'); -}; - -/** - * Generate code for the specified block (and attached blocks). - * @param {Blockly.Block} block The block to generate code for. - * @return {string|!Array} For statement blocks, the generated code. - * For value blocks, an array containing the generated code and an - * operator order value. Returns '' if block is null. - */ -Blockly.Generator.prototype.blockToCode = function(block) { - if (!block) { - return ''; - } - if (block.disabled) { - // Skip past this block if it is disabled. - return this.blockToCode(block.getNextBlock()); - } - - var func = this[block.type]; - goog.asserts.assertFunction(func, - 'Language "%s" does not know how to generate code for block type "%s".', - this.name_, block.type); - // First argument to func.call is the value of 'this' in the generator. - // Prior to 24 September 2013 'this' was the only way to access the block. - // The current prefered method of accessing the block is through the second - // argument to func.call, which becomes the first parameter to the generator. - var code = func.call(block, block); - if (goog.isArray(code)) { - // Value blocks return tuples of code and operator order. - goog.asserts.assert(block.outputConnection, - 'Expecting string from statement block "%s".', block.type); - return [this.scrub_(block, code[0]), code[1]]; - } else if (goog.isString(code)) { - var id = block.id.replace(/\$/g, '$$$$'); // Issue 251. - if (this.STATEMENT_PREFIX) { - code = this.STATEMENT_PREFIX.replace(/%1/g, '\'' + id + '\'') + - code; - } - return this.scrub_(block, code); - } else if (code === null) { - // Block has handled code generation itself. - return ''; - } else { - goog.asserts.fail('Invalid code generated: %s', code); - } -}; - -/** - * Generate code representing the specified value input. - * @param {!Blockly.Block} block The block containing the input. - * @param {string} name The name of the input. - * @param {number} outerOrder The maximum binding strength (minimum order value) - * of any operators adjacent to "block". - * @return {string} Generated code or '' if no blocks are connected or the - * specified input does not exist. - */ -Blockly.Generator.prototype.valueToCode = function(block, name, outerOrder) { - if (isNaN(outerOrder)) { - goog.asserts.fail('Expecting valid order from block "%s".', block.type); - } - var targetBlock = block.getInputTargetBlock(name); - if (!targetBlock) { - return ''; - } - var tuple = this.blockToCode(targetBlock); - if (tuple === '') { - // Disabled block. - return ''; - } - // Value blocks must return code and order of operations info. - // Statement blocks must only return code. - goog.asserts.assertArray(tuple, 'Expecting tuple from value block "%s".', - targetBlock.type); - var code = tuple[0]; - var innerOrder = tuple[1]; - if (isNaN(innerOrder)) { - goog.asserts.fail('Expecting valid order from value block "%s".', - targetBlock.type); - } - if (!code) { - return ''; - } - - // Add parentheses if needed. - var parensNeeded = false; - var outerOrderClass = Math.floor(outerOrder); - var innerOrderClass = Math.floor(innerOrder); - if (outerOrderClass <= innerOrderClass) { - if (outerOrderClass == innerOrderClass && - (outerOrderClass == 0 || outerOrderClass == 99)) { - // Don't generate parens around NONE-NONE and ATOMIC-ATOMIC pairs. - // 0 is the atomic order, 99 is the none order. No parentheses needed. - // In all known languages multiple such code blocks are not order - // sensitive. In fact in Python ('a' 'b') 'c' would fail. - } else { - // The operators outside this code are stronger than the operators - // inside this code. To prevent the code from being pulled apart, - // wrap the code in parentheses. - parensNeeded = true; - // Check for special exceptions. - for (var i = 0; i < this.ORDER_OVERRIDES.length; i++) { - if (this.ORDER_OVERRIDES[i][0] == outerOrder && - this.ORDER_OVERRIDES[i][1] == innerOrder) { - parensNeeded = false; - break; - } - } - } - } - if (parensNeeded) { - // Technically, this should be handled on a language-by-language basis. - // However all known (sane) languages use parentheses for grouping. - code = '(' + code + ')'; - } - return code; -}; - -/** - * Generate code representing the statement. Indent the code. - * @param {!Blockly.Block} block The block containing the input. - * @param {string} name The name of the input. - * @return {string} Generated code or '' if no blocks are connected. - */ -Blockly.Generator.prototype.statementToCode = function(block, name) { - var targetBlock = block.getInputTargetBlock(name); - var code = this.blockToCode(targetBlock); - // Value blocks must return code and order of operations info. - // Statement blocks must only return code. - goog.asserts.assertString(code, 'Expecting code from statement block "%s".', - targetBlock && targetBlock.type); - if (code) { - code = this.prefixLines(/** @type {string} */ (code), this.INDENT); - } - return code; -}; - -/** - * Add an infinite loop trap to the contents of a loop. - * If loop is empty, add a statment prefix for the loop block. - * @param {string} branch Code for loop contents. - * @param {string} id ID of enclosing block. - * @return {string} Loop contents, with infinite loop trap added. - */ -Blockly.Generator.prototype.addLoopTrap = function(branch, id) { - id = id.replace(/\$/g, '$$$$'); // Issue 251. - if (this.INFINITE_LOOP_TRAP) { - branch = this.INFINITE_LOOP_TRAP.replace(/%1/g, '\'' + id + '\'') + branch; - } - if (this.STATEMENT_PREFIX) { - branch += this.prefixLines(this.STATEMENT_PREFIX.replace(/%1/g, - '\'' + id + '\''), this.INDENT); - } - return branch; -}; - -/** - * Comma-separated list of reserved words. - * @type {string} - * @private - */ -Blockly.Generator.prototype.RESERVED_WORDS_ = ''; - -/** - * Add one or more words to the list of reserved words for this language. - * @param {string} words Comma-separated list of words to add to the list. - * No spaces. Duplicates are ok. - */ -Blockly.Generator.prototype.addReservedWords = function(words) { - this.RESERVED_WORDS_ += words + ','; -}; - -/** - * This is used as a placeholder in functions defined using - * Blockly.Generator.provideFunction_. It must not be legal code that could - * legitimately appear in a function definition (or comment), and it must - * not confuse the regular expression parser. - * @type {string} - * @private - */ -Blockly.Generator.prototype.FUNCTION_NAME_PLACEHOLDER_ = '{leCUI8hutHZI4480Dc}'; - -/** - * Define a function to be included in the generated code. - * The first time this is called with a given desiredName, the code is - * saved and an actual name is generated. Subsequent calls with the - * same desiredName have no effect but have the same return value. - * - * It is up to the caller to make sure the same desiredName is not - * used for different code values. - * - * The code gets output when Blockly.Generator.finish() is called. - * - * @param {string} desiredName The desired name of the function (e.g., isPrime). - * @param {!Array.} code A list of statements. Use ' ' for indents. - * @return {string} The actual name of the new function. This may differ - * from desiredName if the former has already been taken by the user. - * @private - */ -Blockly.Generator.prototype.provideFunction_ = function(desiredName, code) { - if (!this.definitions_[desiredName]) { - var functionName = this.variableDB_.getDistinctName(desiredName, - Blockly.Procedures.NAME_TYPE); - this.functionNames_[desiredName] = functionName; - var codeText = code.join('\n').replace( - this.FUNCTION_NAME_PLACEHOLDER_REGEXP_, functionName); - // Change all ' ' indents into the desired indent. - // To avoid an infinite loop of replacements, change all indents to '\0' - // character first, then replace them all with the indent. - // We are assuming that no provided functions contain a literal null char. - var oldCodeText; - while (oldCodeText != codeText) { - oldCodeText = codeText; - codeText = codeText.replace(/^(( {2})*) {2}/gm, '$1\0'); - } - codeText = codeText.replace(/\0/g, this.INDENT); - this.definitions_[desiredName] = codeText; - } - return this.functionNames_[desiredName]; -}; - -/** - * Hook for code to run before code generation starts. - * Subclasses may override this, e.g. to initialise the database of variable - * names. - * @param {!Blockly.Workspace} _workspace Workspace to generate code from. - */ -Blockly.Generator.prototype.init = function(_workspace) { - // Optionally override -}; - -/** - * Common tasks for generating code from blocks. This is called from - * blockToCode and is called on every block, not just top level blocks. - * Subclasses may override this, e.g. to generate code for statements following - * the block, or to handle comments for the specified block and any connected - * value blocks. - * @param {!Blockly.Block} _block The current block. - * @param {string} code The JavaScript code created for this block. - * @return {string} JavaScript code with comments and subsequent blocks added. - * @private - */ -Blockly.Generator.prototype.scrub_ = function(_block, code) { - // Optionally override - return code; -}; - -/** - * Hook for code to run at end of code generation. - * Subclasses may override this, e.g. to prepend the generated code with the - * variable definitions. - * @param {string} code Generated code. - * @return {string} Completed code. - */ -Blockly.Generator.prototype.finish = function(code) { - // Optionally override - return code; -}; - -/** - * Naked values are top-level blocks with outputs that aren't plugged into - * anything. - * Subclasses may override this, e.g. if their language does not allow - * naked values. - * @param {string} line Line of generated code. - * @return {string} Legal line of code. - */ -Blockly.Generator.prototype.scrubNakedValue = function(line) { - // Optionally override - return line; -}; diff --git a/core/gesture.js b/core/gesture.js deleted file mode 100644 index 1e9ec9a49d..0000000000 --- a/core/gesture.js +++ /dev/null @@ -1,1011 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview The class representing an in-progress gesture, usually a drag - * or a tap. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.Gesture'); - -goog.require('Blockly.BlockAnimations'); -goog.require('Blockly.BlockDragger'); -goog.require('Blockly.BubbleDragger'); -goog.require('Blockly.constants'); -goog.require('Blockly.Events.Ui'); -goog.require('Blockly.FlyoutDragger'); -goog.require('Blockly.scratchBlocksUtils'); -goog.require('Blockly.Tooltip'); -goog.require('Blockly.Touch'); -goog.require('Blockly.WorkspaceDragger'); - -goog.require('goog.asserts'); -goog.require('goog.math.Coordinate'); - - -/* - * Note: In this file "start" refers to touchstart, mousedown, and pointerstart - * events. "End" refers to touchend, mouseup, and pointerend events. - */ -// TODO: Consider touchcancel/pointercancel. - - -/** - * Class for one gesture. - * @param {!Event} e The event that kicked off this gesture. - * @param {!Blockly.WorkspaceSvg} creatorWorkspace The workspace that created - * this gesture and has a reference to it. - * @constructor - */ -Blockly.Gesture = function(e, creatorWorkspace) { - - /** - * The position of the mouse when the gesture started. Units are css pixels, - * with (0, 0) at the top left of the browser window (mouseEvent clientX/Y). - * @type {goog.math.Coordinate} - */ - this.mouseDownXY_ = null; - - /** - * How far the mouse has moved during this drag, in pixel units. - * (0, 0) is at this.mouseDownXY_. - * @type {goog.math.Coordinate} - * @private - */ - this.currentDragDeltaXY_ = null; - - /** - * The bubble that the gesture started on, or null if it did not start on a - * bubble. - * @type {Blockly.Bubble} - * @private - */ - this.startBubble_ = null; - - /** - * The field that the gesture started on, or null if it did not start on a - * field. - * @type {Blockly.Field} - * @private - */ - this.startField_ = null; - - /** - * The block that the gesture started on, or null if it did not start on a - * block. - * @type {Blockly.BlockSvg} - * @private - */ - this.startBlock_ = null; - - /** - * The block that this gesture targets. If the gesture started on a - * shadow block, this is the first non-shadow parent of the block. If the - * gesture started in the flyout, this is the root block of the block group - * that was clicked or dragged. - * @type {Blockly.BlockSvg} - * @private - */ - this.targetBlock_ = null; - - /** - * The workspace that the gesture started on. There may be multiple - * workspaces on a page; this is more accurate than using - * Blockly.getMainWorkspace(). - * @type {Blockly.WorkspaceSvg} - * @private - */ - this.startWorkspace_ = null; - - /** - * The workspace that created this gesture. This workspace keeps a reference - * to the gesture, which will need to be cleared at deletion. - * This may be different from the start workspace. For instance, a flyout is - * a workspace, but its parent workspace manages gestures for it. - * @type {Blockly.WorkspaceSvg} - * @private - */ - this.creatorWorkspace_ = creatorWorkspace; - - /** - * Whether the pointer has at any point moved out of the drag radius. - * A gesture that exceeds the drag radius is a drag even if it ends exactly at - * its start point. - * @type {boolean} - * @private - */ - this.hasExceededDragRadius_ = false; - - /** - * Whether the workspace is currently being dragged. - * @type {boolean} - * @private - */ - this.isDraggingWorkspace_ = false; - - /** - * Whether the block is currently being dragged. - * @type {boolean} - * @private - */ - this.isDraggingBlock_ = false; - - /** - * Whether the bubble is currently being dragged. - * @type {boolean} - * @private - */ - this.isDraggingBubble_ = false; - - /** - * The event that most recently updated this gesture. - * @type {!Event} - * @private - */ - this.mostRecentEvent_ = e; - - /** - * A handle to use to unbind a mouse move listener at the end of a drag. - * Opaque data returned from Blockly.bindEventWithChecks_. - * @type {Array.} - * @private - */ - this.onMoveWrapper_ = null; - - /** - * A handle to use to unbind a mouse up listener at the end of a drag. - * Opaque data returned from Blockly.bindEventWithChecks_. - * @type {Array.} - * @private - */ - this.onUpWrapper_ = null; - - /** - * The object tracking a bubble drag, or null if none is in progress. - * @type {Blockly.BubbleDragger} - * @private - */ - this.bubbleDragger_ = null; - - /** - * The object tracking a block drag, or null if none is in progress. - * @type {Blockly.BlockDragger} - * @private - */ - this.blockDragger_ = null; - - /** - * The object tracking a workspace or flyout workspace drag, or null if none - * is in progress. - * @type {Blockly.WorkspaceDragger} - * @private - */ - this.workspaceDragger_ = null; - - /** - * The flyout a gesture started in, if any. - * @type {Blockly.Flyout} - * @private - */ - this.flyout_ = null; - - /** - * Boolean for sanity-checking that some code is only called once. - * @type {boolean} - * @private - */ - this.calledUpdateIsDragging_ = false; - - /** - * Boolean for sanity-checking that some code is only called once. - * @type {boolean} - * @private - */ - this.hasStarted_ = false; - - /** - * Boolean used internally to break a cycle in disposal. - * @type {boolean} - * @private - */ - this.isEnding_ = false; - - /** - * True if dragging from the target block should duplicate the target block - * and drag the duplicate instead. This has a lot of side effects. - * @type {boolean} - * @private - */ - this.shouldDuplicateOnDrag_ = false; -}; - -/** - * Sever all links from this object. - * @package - */ -Blockly.Gesture.prototype.dispose = function() { - Blockly.Touch.clearTouchIdentifier(); - Blockly.Tooltip.unblock(); - // Clear the owner's reference to this gesture. - this.creatorWorkspace_.clearGesture(); - - if (this.onMoveWrapper_) { - Blockly.unbindEvent_(this.onMoveWrapper_); - } - if (this.onUpWrapper_) { - Blockly.unbindEvent_(this.onUpWrapper_); - } - - - this.startField_ = null; - this.startBlock_ = null; - this.targetBlock_ = null; - this.startWorkspace_ = null; - this.flyout_ = null; - - if (this.blockDragger_) { - this.blockDragger_.dispose(); - this.blockDragger_ = null; - } - if (this.workspaceDragger_) { - this.workspaceDragger_.dispose(); - this.workspaceDragger_ = null; - } - if (this.bubbleDragger_) { - this.bubbleDragger_.dispose(); - this.bubbleDragger_ = null; - } -}; - -/** - * Update internal state based on an event. - * @param {!Event} e The most recent mouse or touch event. - * @private - */ -Blockly.Gesture.prototype.updateFromEvent_ = function(e) { - var currentXY = new goog.math.Coordinate(e.clientX, e.clientY); - var changed = this.updateDragDelta_(currentXY); - // Exceeded the drag radius for the first time. - if (changed) { - this.updateIsDragging_(); - Blockly.longStop_(); - } - this.mostRecentEvent_ = e; -}; - -/** - * DO MATH to set currentDragDeltaXY_ based on the most recent mouse position. - * @param {!goog.math.Coordinate} currentXY The most recent mouse/pointer - * position, in pixel units, with (0, 0) at the window's top left corner. - * @return {boolean} True if the drag just exceeded the drag radius for the - * first time. - * @private - */ -Blockly.Gesture.prototype.updateDragDelta_ = function(currentXY) { - this.currentDragDeltaXY_ = goog.math.Coordinate.difference(currentXY, - this.mouseDownXY_); - - if (!this.hasExceededDragRadius_) { - var currentDragDelta = goog.math.Coordinate.magnitude( - this.currentDragDeltaXY_); - - // The flyout has a different drag radius from the rest of Blockly. - var limitRadius = this.flyout_ ? Blockly.FLYOUT_DRAG_RADIUS : - Blockly.DRAG_RADIUS; - - this.hasExceededDragRadius_ = currentDragDelta > limitRadius; - return this.hasExceededDragRadius_; - } - return false; -}; - -/** - * Update this gesture to record whether a block is being dragged from the - * flyout. - * This function should be called on a mouse/touch move event the first time the - * drag radius is exceeded. It should be called no more than once per gesture. - * If a block should be dragged from the flyout this function creates the new - * block on the main workspace and updates targetBlock_ and startWorkspace_. - * @return {boolean} True if a block is being dragged from the flyout. - * @private - */ -Blockly.Gesture.prototype.updateIsDraggingFromFlyout_ = function() { - // Disabled blocks may not be dragged from the flyout. - if (this.targetBlock_.disabled) { - return false; - } - if (!this.flyout_.isScrollable() || - this.flyout_.isDragTowardWorkspace(this.currentDragDeltaXY_)) { - this.startWorkspace_ = this.flyout_.targetWorkspace_; - this.startWorkspace_.updateScreenCalculationsIfScrolled(); - // Start the event group now, so that the same event group is used for block - // creation and block dragging. - if (!Blockly.Events.getGroup()) { - Blockly.Events.setGroup(true); - } - // The start block is no longer relevant, because this is a drag. - this.startBlock_ = null; - this.targetBlock_ = this.flyout_.createBlock(this.targetBlock_); - this.targetBlock_.select(); - return true; - } - return false; -}; - -/** - * Update this gesture to record whether a bubble is being dragged. - * This function should be called on a mouse/touch move event the first time the - * drag radius is exceeded. It should be called no more than once per gesture. - * If a bubble should be dragged this function creates the necessary - * BubbleDragger and starts the drag. - * @return {boolean} true if a bubble is being dragged. - * @private - */ -Blockly.Gesture.prototype.updateIsDraggingBubble_ = function() { - if (!this.startBubble_) { - return false; - } - - this.isDraggingBubble_ = true; - this.startDraggingBubble_(); - return true; -}; - -/** - * Update this gesture to record whether a block is being dragged. - * This function should be called on a mouse/touch move event the first time the - * drag radius is exceeded. It should be called no more than once per gesture. - * If a block should be dragged, either from the flyout or in the workspace, - * this function creates the necessary BlockDragger and starts the drag. - * @return {boolean} true if a block is being dragged. - * @private - */ -Blockly.Gesture.prototype.updateIsDraggingBlock_ = function() { - if (!this.targetBlock_) { - return false; - } - - if (this.flyout_) { - this.isDraggingBlock_ = this.updateIsDraggingFromFlyout_(); - } else if (this.targetBlock_.isMovable() || this.shouldDuplicateOnDrag_){ - this.isDraggingBlock_ = true; - } - - if (this.isDraggingBlock_) { - this.startDraggingBlock_(); - return true; - } - return false; -}; - -/** - * Update this gesture to record whether a workspace is being dragged. - * This function should be called on a mouse/touch move event the first time the - * drag radius is exceeded. It should be called no more than once per gesture. - * If a workspace is being dragged this function creates the necessary - * WorkspaceDragger or FlyoutDragger and starts the drag. - * @private - */ -Blockly.Gesture.prototype.updateIsDraggingWorkspace_ = function() { - var wsMovable = this.flyout_ ? this.flyout_.isScrollable() : - this.startWorkspace_ && this.startWorkspace_.isDraggable(); - - if (!wsMovable) { - return; - } - - if (this.flyout_) { - this.workspaceDragger_ = new Blockly.FlyoutDragger(this.flyout_); - } else { - this.workspaceDragger_ = new Blockly.WorkspaceDragger(this.startWorkspace_); - } - - this.isDraggingWorkspace_ = true; - this.workspaceDragger_.startDrag(); -}; - -/** - * Update this gesture to record whether anything is being dragged. - * This function should be called on a mouse/touch move event the first time the - * drag radius is exceeded. It should be called no more than once per gesture. - * @private - */ -Blockly.Gesture.prototype.updateIsDragging_ = function() { - // Sanity check. - goog.asserts.assert(!this.calledUpdateIsDragging_, - 'updateIsDragging_ should only be called once per gesture.'); - this.calledUpdateIsDragging_ = true; - - // First check if it was a bubble drag. Bubbles always sit on top of blocks. - if (this.updateIsDraggingBubble_()) { - return; - } - // Then check if it was a block drag. - if (this.updateIsDraggingBlock_()) { - return; - } - // Then check if it's a workspace drag. - this.updateIsDraggingWorkspace_(); -}; - -/** - * Create a block dragger and start dragging the selected block. - * @private - */ -Blockly.Gesture.prototype.startDraggingBlock_ = function() { - if (this.shouldDuplicateOnDrag_) { - this.duplicateOnDrag_(); - } - this.blockDragger_ = new Blockly.BlockDragger(this.targetBlock_, - this.startWorkspace_); - this.blockDragger_.startBlockDrag(this.currentDragDeltaXY_); - this.blockDragger_.dragBlock(this.mostRecentEvent_, - this.currentDragDeltaXY_); -}; - -/** - * Create a bubble dragger and start dragging the selected bubble. - * TODO (fenichel): Possibly combine this and startDraggingBlock_. - * @private - */ -Blockly.Gesture.prototype.startDraggingBubble_ = function() { - this.bubbleDragger_ = new Blockly.BubbleDragger(this.startBubble_, - this.startWorkspace_); - this.bubbleDragger_.startBubbleDrag(); - this.bubbleDragger_.dragBubble(this.mostRecentEvent_, - this.currentDragDeltaXY_); -}; -/** - * Start a gesture: update the workspace to indicate that a gesture is in - * progress and bind mousemove and mouseup handlers. - * @param {!Event} e A mouse down or touch start event. - * @package - */ -Blockly.Gesture.prototype.doStart = function(e) { - if (Blockly.utils.isTargetInput(e)) { - this.cancel(); - return; - } - this.hasStarted_ = true; - - Blockly.BlockAnimations.disconnectUiStop(); - this.startWorkspace_.updateScreenCalculationsIfScrolled(); - if (this.startWorkspace_.isMutator) { - // Mutator's coordinate system could be out of date because the bubble was - // dragged, the block was moved, the parent workspace zoomed, etc. - this.startWorkspace_.resize(); - } - this.startWorkspace_.markFocused(); - this.mostRecentEvent_ = e; - - // Hide chaff also hides the flyout, so don't do it if the click is in a flyout. - Blockly.hideChaff(!!this.flyout_); - Blockly.Tooltip.block(); - - if (this.targetBlock_) { - this.targetBlock_.select(); - } - - if (Blockly.utils.isRightButton(e)) { - this.handleRightClick(e); - return; - } - - if (goog.string.caseInsensitiveEquals(e.type, 'touchstart')) { - Blockly.longStart_(e, this); - } - - this.mouseDownXY_ = new goog.math.Coordinate(e.clientX, e.clientY); - this.currentDragDeltaXY_ = new goog.math.Coordinate(0, 0); - - this.bindMouseEvents(e); -}; - -/** - * Bind gesture events. - * @param {!Event} e A mouse down or touch start event. - * @package - */ -Blockly.Gesture.prototype.bindMouseEvents = function(e) { - this.onMoveWrapper_ = Blockly.bindEventWithChecks_( - document, 'mousemove', null, this.handleMove.bind(this)); - this.onUpWrapper_ = Blockly.bindEventWithChecks_( - document, 'mouseup', null, this.handleUp.bind(this)); - - e.preventDefault(); - e.stopPropagation(); -}; - -/** - * Handle a mouse move or touch move event. - * @param {!Event} e A mouse move or touch move event. - * @package - */ -Blockly.Gesture.prototype.handleMove = function(e) { - var stopPropagation = true; - this.updateFromEvent_(e); - if (this.isDraggingWorkspace_) { - this.workspaceDragger_.drag(this.currentDragDeltaXY_); - } else if (this.isDraggingBlock_) { - if (this.blockDragger_.dragBlock( - this.mostRecentEvent_, this.currentDragDeltaXY_)) { - stopPropagation = false; - } - } else if (this.isDraggingBubble_) { - this.bubbleDragger_.dragBubble(this.mostRecentEvent_, - this.currentDragDeltaXY_); - } - - if (stopPropagation) { - e.preventDefault(); - e.stopPropagation(); - } -}; - -/** - * Handle a mouse up or touch end event. - * @param {!Event} e A mouse up or touch end event. - * @package - */ -Blockly.Gesture.prototype.handleUp = function(e) { - this.updateFromEvent_(e); - Blockly.longStop_(); - - if (this.isEnding_) { - return; - } - this.isEnding_ = true; - // The ordering of these checks is important: drags have higher priority than - // clicks. Fields have higher priority than blocks; blocks have higher - // priority than workspaces. - // The ordering within drags does not matter, because the three types of - // dragging are exclusive. - if (this.isDraggingBubble_) { - this.bubbleDragger_.endBubbleDrag(e, this.currentDragDeltaXY_); - } else if (this.isDraggingBlock_) { - this.blockDragger_.endBlockDrag(e, this.currentDragDeltaXY_); - } else if (this.isDraggingWorkspace_) { - this.workspaceDragger_.endDrag(this.currentDragDeltaXY_); - } else if (this.isBubbleClick_()) { - // Bubbles are in front of all fields and blocks. - this.doBubbleClick_(); - } else if (this.isFieldClick_()) { - this.doFieldClick_(); - } else if (this.isBlockClick_()) { - this.doBlockClick_(); - } else if (this.isWorkspaceClick_()) { - this.doWorkspaceClick_(); - } - - e.preventDefault(); - e.stopPropagation(); - - this.dispose(); -}; - -/** - * Cancel an in-progress gesture. If a workspace or block drag is in progress, - * end the drag at the most recent location. - * @package - */ -Blockly.Gesture.prototype.cancel = function() { - // Disposing of a block cancels in-progress drags, but dragging to a delete - // area disposes of a block and leads to recursive disposal. Break that cycle. - if (this.isEnding_) { - console.log('Trying to cancel a gesture recursively.'); - return; - } - this.isEnding_ = true; - Blockly.longStop_(); - if (this.isDraggingBubble_) { - this.bubbleDragger_.endBubbleDrag(this.mostRecentEvent_, - this.currentDragDeltaXY_); - } else if (this.isDraggingBlock_) { - this.blockDragger_.endBlockDrag(this.mostRecentEvent_, - this.currentDragDeltaXY_); - } else if (this.isDraggingWorkspace_) { - this.workspaceDragger_.endDrag(this.currentDragDeltaXY_); - } - this.dispose(); -}; - -/** - * Handle a real or faked right-click event by showing a context menu. - * @param {!Event} e A mouse move or touch move event. - * @package - */ -Blockly.Gesture.prototype.handleRightClick = function(e) { - if (this.targetBlock_) { - this.bringBlockToFront_(); - Blockly.hideChaff(this.flyout_); - this.targetBlock_.showContextMenu_(e); - } else if (this.startBubble_) { - this.startBubble_.showContextMenu_(e); - } else if (this.startWorkspace_ && !this.flyout_) { - Blockly.hideChaff(); - this.startWorkspace_.showContextMenu_(e); - } - - // TODO: Handle right-click on a bubble. - e.preventDefault(); - e.stopPropagation(); - - this.dispose(); -}; - -/** - * Handle a mousedown/touchstart event on a workspace. - * @param {!Event} e A mouse down or touch start event. - * @param {!Blockly.Workspace} ws The workspace the event hit. - * @package - */ -Blockly.Gesture.prototype.handleWsStart = function(e, ws) { - goog.asserts.assert(!this.hasStarted_, - 'Tried to call gesture.handleWsStart, but the gesture had already been ' + - 'started.'); - this.setStartWorkspace_(ws); - this.mostRecentEvent_ = e; - this.doStart(e); -}; - -/** - * Handle a mousedown/touchstart event on a flyout. - * @param {!Event} e A mouse down or touch start event. - * @param {!Blockly.Flyout} flyout The flyout the event hit. - * @package - */ -Blockly.Gesture.prototype.handleFlyoutStart = function(e, flyout) { - goog.asserts.assert(!this.hasStarted_, - 'Tried to call gesture.handleFlyoutStart, but the gesture had already ' + - 'been started.'); - this.setStartFlyout_(flyout); - this.handleWsStart(e, flyout.getWorkspace()); -}; - -/** - * Handle a mousedown/touchstart event on a block. - * @param {!Event} e A mouse down or touch start event. - * @param {!Blockly.BlockSvg} block The block the event hit. - * @package - */ -Blockly.Gesture.prototype.handleBlockStart = function(e, block) { - goog.asserts.assert(!this.hasStarted_, - 'Tried to call gesture.handleBlockStart, but the gesture had already ' + - 'been started.'); - this.setStartBlock(block); - this.mostRecentEvent_ = e; -}; - -/** - * Handle a mousedown/touchstart event on a bubble. - * @param {!Event} e A mouse down or touch start event. - * @param {!Blockly.Bubble} bubble The bubble the event hit. - * @package - */ -Blockly.Gesture.prototype.handleBubbleStart = function(e, bubble) { - goog.asserts.assert(!this.hasStarted_, - 'Tried to call gesture.handleBubbleStart, but the gesture had already ' + - 'been started.'); - this.setStartBubble(bubble); - this.mostRecentEvent_ = e; -}; - -/* Begin functions defining what actions to take to execute clicks on each type - * of target. Any developer wanting to add behaviour on clicks should modify - * only this code. */ - -/** - * Execute a bubble click. - * @private - */ -Blockly.Gesture.prototype.doBubbleClick_ = function() { - // TODO (github.com/google/blockly/issues/1673): Consistent handling of single - // clicks. - this.startBubble_.setFocus && this.startBubble_.setFocus(); - this.startBubble_.select && this.startBubble_.select(); -}; - -/** - * Execute a field click. - * @private - */ -Blockly.Gesture.prototype.doFieldClick_ = function() { - this.startField_.showEditor_(); - this.bringBlockToFront_(); -}; - -/** - * Execute a block click. - * @private - */ -Blockly.Gesture.prototype.doBlockClick_ = function() { - // Block click in an autoclosing flyout. - if (this.flyout_ && this.flyout_.autoClose) { - if (!this.targetBlock_.disabled) { - if (!Blockly.Events.getGroup()) { - Blockly.Events.setGroup(true); - } - var newBlock = this.flyout_.createBlock(this.targetBlock_); - newBlock.scheduleSnapAndBump(); - } - } else { - // A field is being edited if either the WidgetDiv or DropDownDiv is currently open. - // If a field is being edited, don't fire any click events. - var fieldEditing = Blockly.WidgetDiv.isVisible() || Blockly.DropDownDiv.isVisible(); - if (!fieldEditing) { - Blockly.Events.fire( - new Blockly.Events.Ui(this.startBlock_, 'click', undefined, undefined)); - // Scratch-specific: also fire a "stack click" event for this stack. - // This is used to toggle the stack when any block in the stack is clicked. - var rootBlock = this.startBlock_.getRootBlock(); - Blockly.Events.fire( - new Blockly.Events.Ui(rootBlock, 'stackclick', undefined, undefined)); - } - } - this.bringBlockToFront_(); - Blockly.Events.setGroup(false); -}; - -/** - * Execute a workspace click. - * @private - */ -Blockly.Gesture.prototype.doWorkspaceClick_ = function() { - if (Blockly.selected) { - Blockly.selected.unselect(); - } -}; - -/* End functions defining what actions to take to execute clicks on each type - * of target. */ - -// TODO (fenichel): Move bubbles to the front. -/** - * Move the dragged/clicked block to the front of the workspace so that it is - * not occluded by other blocks. - * @private - */ -Blockly.Gesture.prototype.bringBlockToFront_ = function() { - // Blocks in the flyout don't overlap, so skip the work. - if (this.targetBlock_ && !this.flyout_) { - this.targetBlock_.bringToFront(); - } -}; - -/* Begin functions for populating a gesture at mouse down. */ - -/** - * Record the field that a gesture started on. - * @param {Blockly.Field} field The field the gesture started on. - * @package - */ -Blockly.Gesture.prototype.setStartField = function(field) { - goog.asserts.assert(!this.hasStarted_, - 'Tried to call gesture.setStartField, but the gesture had already been ' + - 'started.'); - if (!this.startField_) { - this.startField_ = field; - } -}; - -/** - * Record the bubble that a gesture started on - * @param {Blockly.Bubble} bubble The bubble the gesture started on. - * @package - */ -Blockly.Gesture.prototype.setStartBubble = function(bubble) { - if (!this.startBubble_) { - this.startBubble_ = bubble; - } -}; - -/** - * Record the block that a gesture started on, and set the target block - * appropriately. - * @param {Blockly.BlockSvg} block The block the gesture started on. - * @package - */ -Blockly.Gesture.prototype.setStartBlock = function(block) { - // If the gesture already went through a bubble, don't set the start block. - if (!this.startBlock_ && !this.startBubble_) { - this.startBlock_ = block; - this.shouldDuplicateOnDrag_ = - Blockly.scratchBlocksUtils.isShadowArgumentReporter(block); - if (block.isInFlyout && block != block.getRootBlock()) { - this.setTargetBlock_(block.getRootBlock()); - } else { - this.setTargetBlock_(block); - } - } -}; - -/** - * Record the block that a gesture targets, meaning the block that will be - * dragged if this turns into a drag. If this block is a shadow, that will be - * its first non-shadow parent. - * @param {Blockly.BlockSvg} block The block the gesture targets. - * @private - */ -Blockly.Gesture.prototype.setTargetBlock_ = function(block) { - if (block.isShadow() && !this.shouldDuplicateOnDrag_) { - this.setTargetBlock_(block.getParent()); - } else { - this.targetBlock_ = block; - } -}; - -/** - * Record the workspace that a gesture started on. - * @param {Blockly.WorkspaceSvg} ws The workspace the gesture started on. - * @private - */ -Blockly.Gesture.prototype.setStartWorkspace_ = function(ws) { - if (!this.startWorkspace_) { - this.startWorkspace_ = ws; - } -}; - -/** - * Record the flyout that a gesture started on. - * @param {Blockly.Flyout} flyout The flyout the gesture started on. - * @private - */ -Blockly.Gesture.prototype.setStartFlyout_ = function(flyout) { - if (!this.flyout_) { - this.flyout_ = flyout; - } -}; - -/* End functions for populating a gesture at mouse down. */ - -/* Begin helper functions defining types of clicks. Any developer wanting - * to change the definition of a click should modify only this code. */ - -/** - * Whether this gesture is a click on a bubble. This should only be called when - * ending a gesture (mouse up, touch end). - * @return {boolean} whether this gesture was a click on a bubble. - * @private - */ -Blockly.Gesture.prototype.isBubbleClick_ = function() { - // A bubble click starts on a bubble and never escapes the drag radius. - var hasStartBubble = !!this.startBubble_; - return hasStartBubble && !this.hasExceededDragRadius_; -}; - -/** - * Whether this gesture is a click on a block. This should only be called when - * ending a gesture (mouse up, touch end). - * @return {boolean} whether this gesture was a click on a block. - * @private - */ -Blockly.Gesture.prototype.isBlockClick_ = function() { - // A block click starts on a block, never escapes the drag radius, and is not - // a field click. - var hasStartBlock = !!this.startBlock_; - return hasStartBlock && !this.hasExceededDragRadius_ && !this.isFieldClick_(); -}; - -/** - * Whether this gesture is a click on a field. This should only be called when - * ending a gesture (mouse up, touch end). - * @return {boolean} whether this gesture was a click on a field. - * @private - */ -Blockly.Gesture.prototype.isFieldClick_ = function() { - var fieldEditable = this.startField_ ? - this.startField_.isCurrentlyEditable() : false; - return fieldEditable && !this.hasExceededDragRadius_; -}; - -/** - * Whether this gesture is a click on a workspace. This should only be called - * when ending a gesture (mouse up, touch end). - * @return {boolean} whether this gesture was a click on a workspace. - * @private - */ -Blockly.Gesture.prototype.isWorkspaceClick_ = function() { - var onlyTouchedWorkspace = !this.startBlock_ && !this.startBubble_ && - !this.startField_; - return onlyTouchedWorkspace && !this.hasExceededDragRadius_; -}; - -/* End helper functions defining types of clicks. */ - -/** - * Whether this gesture is a drag of either a workspace or block. - * This function is called externally to block actions that cannot be taken - * mid-drag (e.g. using the keyboard to delete the selected blocks). - * @return {boolean} true if this gesture is a drag of a workspace or block. - * @package - */ -Blockly.Gesture.prototype.isDragging = function() { - return this.isDraggingWorkspace_ || this.isDraggingBlock_ || - this.isDraggingBubble_; -}; - -/** - * Whether this gesture has already been started. In theory every mouse down - * has a corresponding mouse up, but in reality it is possible to lose a - * mouse up, leaving an in-process gesture hanging. - * @return {boolean} whether this gesture was a click on a workspace. - * @package - */ -Blockly.Gesture.prototype.hasStarted = function() { - return this.hasStarted_; -}; - -/* Scratch-specific */ - -/** - * Don't even think about using this function before talking to rachel-fenichel. - * - * Force a drag to start without clicking and dragging the block itself. Used - * to attach duplicated blocks to the mouse pointer. - * @param {!Object} fakeEvent An object with the properties needed to start a - * drag, including clientX and clientY. - * @param {!Blockly.BlockSvg} block The block to start dragging. - * @package - */ -Blockly.Gesture.prototype.forceStartBlockDrag = function(fakeEvent, block) { - this.handleBlockStart(fakeEvent, block); - this.handleWsStart(fakeEvent, block.workspace); - this.isDraggingBlock_ = true; - this.hasExceededDragRadius_ = true; - this.startDraggingBlock_(); -}; - -/** - * Duplicate the target block and start dragging the duplicated block. - * This should be done once we are sure that it is a block drag, and no earlier. - * Specifically for argument reporters in custom block defintions. - * @private - */ -Blockly.Gesture.prototype.duplicateOnDrag_ = function() { - var newBlock = null; - Blockly.Events.disable(); - try { - // Note: targetBlock_ should have no children. If it has children we would - // need to update shadow block IDs to avoid problems in the VM. - // Resizes will be reenabled at the end of the drag. - this.startWorkspace_.setResizesEnabled(false); - var xmlBlock = Blockly.Xml.blockToDom(this.targetBlock_); - newBlock = Blockly.Xml.domToBlock(xmlBlock, this.startWorkspace_); - - // Move the duplicate to original position. - var xy = this.targetBlock_.getRelativeToSurfaceXY(); - newBlock.moveBy(xy.x, xy.y); - newBlock.setShadow(false); - } finally { - Blockly.Events.enable(); - } - if (!newBlock) { - // Something went wrong. - console.error('Something went wrong while duplicating a block.'); - return; - } - if (Blockly.Events.isEnabled()) { - Blockly.Events.fire(new Blockly.Events.BlockCreate(newBlock)); - } - newBlock.select(); - this.targetBlock_ = newBlock; -}; diff --git a/core/grid.js b/core/grid.js deleted file mode 100644 index 781bf22995..0000000000 --- a/core/grid.js +++ /dev/null @@ -1,227 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Object for configuring and updating a workspace grid in - * Blockly. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.Grid'); - -goog.require('Blockly.utils'); - -goog.require('goog.userAgent'); - - -/** - * Class for a workspace's grid. - * @param {!SVGElement} pattern The grid's SVG pattern, created during injection. - * @param {!Object} options A dictionary of normalized options for the grid. - * See grid documentation: - * https://developers.google.com/blockly/guides/configure/web/grid - * @constructor - */ -Blockly.Grid = function(pattern, options) { - /** - * The grid's SVG pattern, created during injection. - * @type {!SVGElement} - * @private - */ - this.gridPattern_ = pattern; - - /** - * The spacing of the grid lines (in px). - * @type {number} - * @private - */ - this.spacing_ = options['spacing']; - - /** - * How long the grid lines should be (in px). - * @type {number} - * @private - */ - this.length_ = options['length']; - - /** - * The horizontal grid line, if it exists. - * @type {SVGElement} - * @private - */ - this.line1_ = pattern.firstChild; - - /** - * The vertical grid line, if it exists. - * @type {SVGElement} - * @private - */ - this.line2_ = this.line1_ && this.line1_.nextSibling; - - /** - * Whether blocks should snap to the grid. - * @type {boolean} - * @private - */ - this.snapToGrid_ = options['snap']; -}; - -/** - * The scale of the grid, used to set stroke width on grid lines. - * This should always be the same as the workspace scale. - * @type {number} - * @private - */ -Blockly.Grid.prototype.scale_ = 1; - -/** - * Dispose of this grid and unlink from the DOM. - * @package - */ -Blockly.Grid.prototype.dispose = function() { - this.gridPattern_ = null; -}; - -/** - * Whether blocks should snap to the grid, based on the initial configuration. - * @return {boolean} True if blocks should snap, false otherwise. - * @package - */ -Blockly.Grid.prototype.shouldSnap = function() { - return this.snapToGrid_; -}; - -/** - * Get the spacing of the grid points (in px). - * @return {number} The spacing of the grid points. - * @package - */ -Blockly.Grid.prototype.getSpacing = function() { - return this.spacing_; -}; - -/** - * Get the id of the pattern element, which should be randomized to avoid - * conflicts with other Blockly instances on the page. - * @return {string} The pattern ID. - * @package - */ -Blockly.Grid.prototype.getPatternId = function() { - return this.gridPattern_.id; -}; - -/** - * Update the grid with a new scale. - * @param {number} scale The new workspace scale. - * @package - */ -Blockly.Grid.prototype.update = function(scale) { - this.scale_ = scale; - // MSIE freaks if it sees a 0x0 pattern, so set empty patterns to 100x100. - var safeSpacing = (this.spacing_ * scale) || 100; - - this.gridPattern_.setAttribute('width', safeSpacing); - this.gridPattern_.setAttribute('height', safeSpacing); - - var half = Math.floor(this.spacing_ / 2) + 0.5; - var start = half - this.length_ / 2; - var end = half + this.length_ / 2; - - half *= scale; - start *= scale; - end *= scale; - - this.setLineAttributes_(this.line1_, scale, start, end, half, half); - this.setLineAttributes_(this.line2_, scale, half, half, start, end); -}; - -/** - * Set the attributes on one of the lines in the grid. Use this to update the - * length and stroke width of the grid lines. - * @param {!SVGElement} line Which line to update. - * @param {number} width The new stroke size (in px). - * @param {number} x1 The new x start position of the line (in px). - * @param {number} x2 The new x end position of the line (in px). - * @param {number} y1 The new y start position of the line (in px). - * @param {number} y2 The new y end position of the line (in px). - * @private - */ -Blockly.Grid.prototype.setLineAttributes_ = function(line, width, x1, x2, y1, y2) { - if (line) { - line.setAttribute('stroke-width', width); - line.setAttribute('x1', x1); - line.setAttribute('y1', y1); - line.setAttribute('x2', x2); - line.setAttribute('y2', y2); - } -}; - -/** - * Move the grid to a new x and y position, and make sure that change is visible. - * @param {number} x The new x position of the grid (in px). - * @param {number} y The new y position ofthe grid (in px). - * @package - */ -Blockly.Grid.prototype.moveTo = function(x, y) { - this.gridPattern_.setAttribute('x', x); - this.gridPattern_.setAttribute('y', y); - - if (goog.userAgent.IE || goog.userAgent.EDGE) { - // IE/Edge doesn't notice that the x/y offsets have changed. - // Force an update. - this.update(this.scale_); - } -}; - -/** - * Create the DOM for the grid described by options. - * @param {string} rnd A random ID to append to the pattern's ID. - * @param {!Object} gridOptions The object containing grid configuration. - * @param {!SVGElement} defs The root SVG element for this workspace's defs. - * @return {!SVGElement} The SVG element for the grid pattern. - * @package - */ -Blockly.Grid.createDom = function(rnd, gridOptions, defs) { - /* - - - - - */ - var gridPattern = Blockly.utils.createSvgElement('pattern', - { - 'id': 'blocklyGridPattern' + rnd, - 'patternUnits': 'userSpaceOnUse' - }, defs); - if (gridOptions['length'] > 0 && gridOptions['spacing'] > 0) { - Blockly.utils.createSvgElement('line', - {'stroke': gridOptions['colour']}, gridPattern); - if (gridOptions['length'] > 1) { - Blockly.utils.createSvgElement('line', - {'stroke': gridOptions['colour']}, gridPattern); - } - // x1, y1, x1, x2 properties will be set later in update. - } else { - // Edge 16 doesn't handle empty patterns - Blockly.utils.createSvgElement('line', {}, gridPattern); - } - return gridPattern; -}; diff --git a/core/icon.js b/core/icon.js deleted file mode 100644 index b51d90efd1..0000000000 --- a/core/icon.js +++ /dev/null @@ -1,205 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2013 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Object representing an icon on a block. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.Icon'); - -goog.require('goog.dom'); -goog.require('goog.math.Coordinate'); - - -/** - * Class for an icon. - * @param {Blockly.Block} block The block associated with this icon. - * @constructor - */ -Blockly.Icon = function(block) { - this.block_ = block; -}; - -/** - * Does this icon get hidden when the block is collapsed. - */ -Blockly.Icon.prototype.collapseHidden = true; - -/** - * Height and width of icons. - */ -Blockly.Icon.prototype.SIZE = 17; - -/** - * Bubble UI (if visible). - * @type {Blockly.Bubble} - * @protected - */ -Blockly.Icon.prototype.bubble_ = null; - -/** - * Absolute coordinate of icon's center. - * @type {goog.math.Coordinate} - * @protected - */ -Blockly.Icon.prototype.iconXY_ = null; - -/** - * Create the icon on the block. - */ -Blockly.Icon.prototype.createIcon = function() { - if (this.iconGroup_) { - // Icon already exists. - return; - } - /* Here's the markup that will be generated: - - ... - - */ - this.iconGroup_ = Blockly.utils.createSvgElement('g', - {'class': 'blocklyIconGroup'}, null); - if (this.block_.isInFlyout) { - Blockly.utils.addClass( - /** @type {!Element} */ (this.iconGroup_), 'blocklyIconGroupReadonly'); - } - this.drawIcon_(this.iconGroup_); - - this.block_.getSvgRoot().appendChild(this.iconGroup_); - Blockly.bindEventWithChecks_( - this.iconGroup_, 'mouseup', this, this.iconClick_); - this.updateEditable(); -}; - -/** - * Dispose of this icon. - */ -Blockly.Icon.prototype.dispose = function() { - // Dispose of and unlink the icon. - goog.dom.removeNode(this.iconGroup_); - this.iconGroup_ = null; - // Dispose of and unlink the bubble. - this.setVisible(false); - this.block_ = null; -}; - -/** - * Add or remove the UI indicating if this icon may be clicked or not. - */ -Blockly.Icon.prototype.updateEditable = function() { -}; - -/** - * Is the associated bubble visible? - * @return {boolean} True if the bubble is visible. - */ -Blockly.Icon.prototype.isVisible = function() { - return !!this.bubble_; -}; - -/** - * Clicking on the icon toggles if the bubble is visible. - * @param {!Event} e Mouse click event. - * @protected - */ -Blockly.Icon.prototype.iconClick_ = function(e) { - if (this.block_.workspace.isDragging()) { - // Drag operation is concluding. Don't open the editor. - return; - } - if (!this.block_.isInFlyout && !Blockly.utils.isRightButton(e)) { - this.setVisible(!this.isVisible()); - } -}; - -/** - * Change the colour of the associated bubble to match its block. - */ -Blockly.Icon.prototype.updateColour = function() { - if (this.isVisible()) { - this.bubble_.setColour(this.block_.getColour()); - } -}; - -/** - * Render the icon. - * @param {number} cursorX Horizontal offset at which to position the icon. - * @return {number} Horizontal offset for next item to draw. - */ -Blockly.Icon.prototype.renderIcon = function(cursorX) { - if (this.collapseHidden && this.block_.isCollapsed()) { - this.iconGroup_.setAttribute('display', 'none'); - return cursorX; - } - this.iconGroup_.setAttribute('display', 'block'); - - var TOP_MARGIN = 5; - var width = this.SIZE; - if (this.block_.RTL) { - cursorX -= width; - } - this.iconGroup_.setAttribute('transform', - 'translate(' + cursorX + ',' + TOP_MARGIN + ')'); - this.computeIconLocation(); - if (this.block_.RTL) { - cursorX -= Blockly.BlockSvg.SEP_SPACE_X; - } else { - cursorX += width + Blockly.BlockSvg.SEP_SPACE_X; - } - return cursorX; -}; - -/** - * Notification that the icon has moved. Update the arrow accordingly. - * @param {!goog.math.Coordinate} xy Absolute location in workspace coordinates. - */ -Blockly.Icon.prototype.setIconLocation = function(xy) { - this.iconXY_ = xy; - if (this.isVisible()) { - this.bubble_.setAnchorLocation(xy); - } -}; - -/** - * Notification that the icon has moved, but we don't really know where. - * Recompute the icon's location from scratch. - */ -Blockly.Icon.prototype.computeIconLocation = function() { - // Find coordinates for the centre of the icon and update the arrow. - var blockXY = this.block_.getRelativeToSurfaceXY(); - var iconXY = Blockly.utils.getRelativeXY(this.iconGroup_); - var newXY = new goog.math.Coordinate( - blockXY.x + iconXY.x + this.SIZE / 2, - blockXY.y + iconXY.y + this.SIZE / 2); - if (!goog.math.Coordinate.equals(this.getIconLocation(), newXY)) { - this.setIconLocation(newXY); - } -}; - -/** - * Returns the center of the block's icon relative to the surface. - * @return {!goog.math.Coordinate} Object with x and y properties in workspace - * coordinates. - */ -Blockly.Icon.prototype.getIconLocation = function() { - return this.iconXY_; -}; diff --git a/core/inject.js b/core/inject.js deleted file mode 100644 index 7b0dbddc7b..0000000000 --- a/core/inject.js +++ /dev/null @@ -1,491 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2011 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Functions for injecting Blockly into a web page. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.inject'); - -goog.require('Blockly.BlockDragSurfaceSvg'); -goog.require('Blockly.Css'); -goog.require('Blockly.constants'); -goog.require('Blockly.DropDownDiv'); -goog.require('Blockly.Grid'); -goog.require('Blockly.Options'); -goog.require('Blockly.WorkspaceSvg'); -goog.require('Blockly.WorkspaceDragSurfaceSvg'); -goog.require('goog.dom'); -goog.require('goog.ui.Component'); -goog.require('goog.userAgent'); - -/** - * Inject a Blockly editor into the specified container element (usually a div). - * @param {!Element|string} container Containing element, or its ID, - * or a CSS selector. - * @param {Object=} opt_options Optional dictionary of options. - * @return {!Blockly.Workspace} Newly created main workspace. - */ -Blockly.inject = function(container, opt_options) { - if (goog.isString(container)) { - container = document.getElementById(container) || - document.querySelector(container); - } - // Verify that the container is in document. - if (!goog.dom.contains(document, container)) { - throw 'Error: container is not in current document.'; - } - var options = new Blockly.Options(opt_options || {}); - var subContainer = goog.dom.createDom('div', 'injectionDiv'); - container.appendChild(subContainer); - - // Open the Field text cache and leave it open. See this issue for more information - // https://github.com/LLK/scratch-blocks/issues/1004 - Blockly.Field.startCache(); - - var svg = Blockly.createDom_(subContainer, options); - - // Create surfaces for dragging things. These are optimizations - // so that the broowser does not repaint during the drag. - var blockDragSurface = new Blockly.BlockDragSurfaceSvg(subContainer); - var workspaceDragSurface = new Blockly.WorkspaceDragSurfaceSvg(subContainer); - - var workspace = Blockly.createMainWorkspace_(svg, options, blockDragSurface, - workspaceDragSurface); - Blockly.init_(workspace); - Blockly.mainWorkspace = workspace; - - Blockly.svgResize(workspace); - return workspace; -}; - -/** - * Create the SVG image. - * @param {!Element} container Containing element. - * @param {!Blockly.Options} options Dictionary of options. - * @return {!Element} Newly created SVG image. - * @private - */ -Blockly.createDom_ = function(container, options) { - // Sadly browsers (Chrome vs Firefox) are currently inconsistent in laying - // out content in RTL mode. Therefore Blockly forces the use of LTR, - // then manually positions content in RTL as needed. - container.setAttribute('dir', 'LTR'); - // Closure can be trusted to create HTML widgets with the proper direction. - goog.ui.Component.setDefaultRightToLeft(options.RTL); - - // Load CSS. - Blockly.Css.inject(options.hasCss, options.pathToMedia); - - // Build the SVG DOM. - /* - - ... - - */ - var svg = Blockly.utils.createSvgElement('svg', { - 'xmlns': 'http://www.w3.org/2000/svg', - 'xmlns:html': 'http://www.w3.org/1999/xhtml', - 'xmlns:xlink': 'http://www.w3.org/1999/xlink', - 'version': '1.1', - 'class': 'blocklySvg' - }, container); - /* - - ... filters go here ... - - */ - var defs = Blockly.utils.createSvgElement('defs', {}, svg); - // Each filter/pattern needs a unique ID for the case of multiple Blockly - // instances on a page. Browser behaviour becomes undefined otherwise. - // https://neil.fraser.name/news/2015/11/01/ - // TODO (tmickel): Look into whether block highlighting still works. - // Reference commit: - // https://github.com/google/blockly/commit/144be4d49f36fdba260a26edbd170ae75bbc37a6 - var rnd = String(Math.random()).substring(2); - - // Using a dilate distorts the block shape. - // Instead use a gaussian blur, and then set all alpha to 1 with a transfer. - var stackGlowFilter = Blockly.utils.createSvgElement('filter', - { - 'id': 'blocklyStackGlowFilter' + rnd, - 'height': '160%', - 'width': '180%', - y: '-30%', - x: '-40%' - }, - defs); - options.stackGlowBlur = Blockly.utils.createSvgElement('feGaussianBlur', - { - 'in': 'SourceGraphic', - 'stdDeviation': Blockly.Colours.stackGlowSize - }, - stackGlowFilter); - // Set all gaussian blur pixels to 1 opacity before applying flood - var componentTransfer = Blockly.utils.createSvgElement('feComponentTransfer', {'result': 'outBlur'}, stackGlowFilter); - Blockly.utils.createSvgElement('feFuncA', - { - 'type': 'table', - 'tableValues': '0' + goog.string.repeat(' 1', 16) - }, - componentTransfer); - // Color the highlight - Blockly.utils.createSvgElement('feFlood', - { - 'flood-color': Blockly.Colours.stackGlow, - 'flood-opacity': Blockly.Colours.stackGlowOpacity, - 'result': 'outColor' - }, - stackGlowFilter); - Blockly.utils.createSvgElement('feComposite', - { - 'in': 'outColor', - 'in2': 'outBlur', - 'operator': 'in', - 'result': 'outGlow' - }, - stackGlowFilter); - Blockly.utils.createSvgElement('feComposite', - { - 'in': 'SourceGraphic', - 'in2': 'outGlow', - 'operator': 'over' - }, - stackGlowFilter); - - // Filter for replacement marker - var replacementGlowFilter = Blockly.utils.createSvgElement('filter', - { - 'id': 'blocklyReplacementGlowFilter' + rnd, - 'height': '160%', - 'width': '180%', - y: '-30%', - x: '-40%' - }, - defs); - Blockly.utils.createSvgElement('feGaussianBlur', - { - 'in': 'SourceGraphic', - 'stdDeviation': Blockly.Colours.replacementGlowSize - }, - replacementGlowFilter); - // Set all gaussian blur pixels to 1 opacity before applying flood - var componentTransfer = Blockly.utils.createSvgElement('feComponentTransfer', - {'result': 'outBlur'}, replacementGlowFilter); - Blockly.utils.createSvgElement('feFuncA', - { - 'type': 'table', - 'tableValues': '0' + goog.string.repeat(' 1', 16) - }, - componentTransfer); - // Color the highlight - Blockly.utils.createSvgElement('feFlood', - { - 'flood-color': Blockly.Colours.replacementGlow, - 'flood-opacity': Blockly.Colours.replacementGlowOpacity, - 'result': 'outColor' - }, - replacementGlowFilter); - Blockly.utils.createSvgElement('feComposite', - { - 'in': 'outColor', - 'in2': 'outBlur', - 'operator': 'in', - 'result': 'outGlow' - }, - replacementGlowFilter); - Blockly.utils.createSvgElement('feComposite', - { - 'in': 'SourceGraphic', - 'in2': 'outGlow', - 'operator': 'over' - }, - replacementGlowFilter); - /* - - - - - */ - var disabledPattern = Blockly.utils.createSvgElement('pattern', - { - 'id': 'blocklyDisabledPattern' + rnd, - 'patternUnits': 'userSpaceOnUse', - 'width': 10, - 'height': 10 - }, - defs); - Blockly.utils.createSvgElement('rect', - { - 'width': 10, - 'height': 10, - 'fill': '#aaa' - }, - disabledPattern); - Blockly.utils.createSvgElement('path', - { - 'd': 'M 0 0 L 10 10 M 10 0 L 0 10', - 'stroke': '#cc0' - }, - disabledPattern); - options.stackGlowFilterId = stackGlowFilter.id; - options.replacementGlowFilterId = replacementGlowFilter.id; - options.disabledPatternId = disabledPattern.id; - - options.gridPattern = Blockly.Grid.createDom(rnd, options.gridOptions, defs); - return svg; -}; - -/** - * Create a main workspace and add it to the SVG. - * @param {!Element} svg SVG element with pattern defined. - * @param {!Blockly.Options} options Dictionary of options. - * @param {!Blockly.BlockDragSurfaceSvg} blockDragSurface Drag surface SVG - * for the blocks. - * @param {!Blockly.WorkspaceDragSurfaceSvg} workspaceDragSurface Drag surface - * SVG for the workspace. - * @return {!Blockly.Workspace} Newly created main workspace. - * @private - */ -Blockly.createMainWorkspace_ = function(svg, options, blockDragSurface, workspaceDragSurface) { - options.parentWorkspace = null; - var mainWorkspace = new Blockly.WorkspaceSvg(options, blockDragSurface, workspaceDragSurface); - mainWorkspace.scale = options.zoomOptions.startScale; - svg.appendChild(mainWorkspace.createDom('blocklyMainBackground')); - - if (!options.hasCategories && options.languageTree) { - // Add flyout as an that is a sibling of the workspace svg. - var flyout = mainWorkspace.addFlyout_('svg'); - Blockly.utils.insertAfter(flyout, svg); - } - - // A null translation will also apply the correct initial scale. - mainWorkspace.translate(0, 0); - Blockly.mainWorkspace = mainWorkspace; - - if (!options.readOnly && !options.hasScrollbars) { - var workspaceChanged = function() { - if (!mainWorkspace.isDragging()) { - var metrics = mainWorkspace.getMetrics(); - var edgeLeft = metrics.viewLeft + metrics.absoluteLeft; - var edgeTop = metrics.viewTop + metrics.absoluteTop; - if (metrics.contentTop < edgeTop || - metrics.contentTop + metrics.contentHeight > - metrics.viewHeight + edgeTop || - metrics.contentLeft < - (options.RTL ? metrics.viewLeft : edgeLeft) || - metrics.contentLeft + metrics.contentWidth > (options.RTL ? - metrics.viewWidth : metrics.viewWidth + edgeLeft)) { - // One or more blocks may be out of bounds. Bump them back in. - var MARGIN = 25; - var blocks = mainWorkspace.getTopBlocks(false); - for (var b = 0, block; block = blocks[b]; b++) { - var blockXY = block.getRelativeToSurfaceXY(); - var blockHW = block.getHeightWidth(); - // Bump any block that's above the top back inside. - var overflowTop = edgeTop + MARGIN - blockHW.height - blockXY.y; - if (overflowTop > 0) { - block.moveBy(0, overflowTop); - } - // Bump any block that's below the bottom back inside. - var overflowBottom = - edgeTop + metrics.viewHeight - MARGIN - blockXY.y; - if (overflowBottom < 0) { - block.moveBy(0, overflowBottom); - } - // Bump any block that's off the left back inside. - var overflowLeft = MARGIN + edgeLeft - - blockXY.x - (options.RTL ? 0 : blockHW.width); - if (overflowLeft > 0) { - block.moveBy(overflowLeft, 0); - } - // Bump any block that's off the right back inside. - var overflowRight = edgeLeft + metrics.viewWidth - MARGIN - - blockXY.x + (options.RTL ? blockHW.width : 0); - if (overflowRight < 0) { - block.moveBy(overflowRight, 0); - } - } - } - } - }; - mainWorkspace.addChangeListener(workspaceChanged); - } - // The SVG is now fully assembled. - Blockly.svgResize(mainWorkspace); - Blockly.WidgetDiv.createDom(); - Blockly.DropDownDiv.createDom(); - Blockly.Tooltip.createDom(); - return mainWorkspace; -}; - -/** - * Initialize Blockly with various handlers. - * @param {!Blockly.Workspace} mainWorkspace Newly created main workspace. - * @private - */ -Blockly.init_ = function(mainWorkspace) { - var options = mainWorkspace.options; - var svg = mainWorkspace.getParentSvg(); - - // Suppress the browser's context menu. - Blockly.bindEventWithChecks_(svg.parentNode, 'contextmenu', null, - function(e) { - if (!Blockly.utils.isTargetInput(e)) { - e.preventDefault(); - } - }); - - var workspaceResizeHandler = Blockly.bindEventWithChecks_(window, 'resize', - null, - function() { - Blockly.hideChaffOnResize(true); - Blockly.svgResize(mainWorkspace); - }); - mainWorkspace.setResizeHandlerWrapper(workspaceResizeHandler); - - Blockly.inject.bindDocumentEvents_(); - - if (options.languageTree) { - if (mainWorkspace.toolbox_) { - mainWorkspace.toolbox_.init(mainWorkspace); - } else if (mainWorkspace.flyout_) { - // Build a fixed flyout with the root blocks. - mainWorkspace.flyout_.init(mainWorkspace); - mainWorkspace.flyout_.show(options.languageTree.childNodes); - mainWorkspace.flyout_.scrollToStart(); - // Translate the workspace to avoid the fixed flyout. - if (options.horizontalLayout) { - mainWorkspace.scrollY = mainWorkspace.flyout_.height_; - if (options.toolboxPosition == Blockly.TOOLBOX_AT_BOTTOM) { - mainWorkspace.scrollY *= -1; - } - } else { - mainWorkspace.scrollX = mainWorkspace.flyout_.width_; - if (options.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { - mainWorkspace.scrollX *= -1; - } - } - mainWorkspace.translate(mainWorkspace.scrollX, mainWorkspace.scrollY); - } - } - - if (options.hasScrollbars) { - mainWorkspace.scrollbar = new Blockly.ScrollbarPair(mainWorkspace); - mainWorkspace.scrollbar.resize(); - } - - // Load the sounds. - if (options.hasSounds) { - Blockly.inject.loadSounds_(options.pathToMedia, mainWorkspace); - } -}; - -/** - * Bind document events, but only once. Destroying and reinjecting Blockly - * should not bind again. - * Bind events for scrolling the workspace. - * Most of these events should be bound to the SVG's surface. - * However, 'mouseup' has to be on the whole document so that a block dragged - * out of bounds and released will know that it has been released. - * Also, 'keydown' has to be on the whole document since the browser doesn't - * understand a concept of focus on the SVG image. - * @private - */ -Blockly.inject.bindDocumentEvents_ = function() { - if (!Blockly.documentEventsBound_) { - Blockly.bindEventWithChecks_(document, 'keydown', null, Blockly.onKeyDown_); - // longStop needs to run to stop the context menu from showing up. It - // should run regardless of what other touch event handlers have run. - Blockly.bindEvent_(document, 'touchend', null, Blockly.longStop_); - Blockly.bindEvent_(document, 'touchcancel', null, Blockly.longStop_); - // Some iPad versions don't fire resize after portrait to landscape change. - if (goog.userAgent.IPAD) { - Blockly.bindEventWithChecks_(window, 'orientationchange', document, - function() { - // TODO(#397): Fix for multiple blockly workspaces. - Blockly.svgResize(Blockly.getMainWorkspace()); - }); - } - } - Blockly.documentEventsBound_ = true; -}; - -/** - * Load sounds for the given workspace. - * @param {string} pathToMedia The path to the media directory. - * @param {!Blockly.Workspace} workspace The workspace to load sounds for. - * @private - */ -Blockly.inject.loadSounds_ = function(pathToMedia, workspace) { - var audioMgr = workspace.getAudioManager(); - audioMgr.load( - [ - pathToMedia + 'click.mp3', - pathToMedia + 'click.wav', - pathToMedia + 'click.ogg' - ], - 'click'); - audioMgr.load( - [ - pathToMedia + 'delete.mp3', - pathToMedia + 'delete.ogg', - pathToMedia + 'delete.wav' - ], - 'delete'); - - // Bind temporary hooks that preload the sounds. - var soundBinds = []; - var unbindSounds = function() { - while (soundBinds.length) { - Blockly.unbindEvent_(soundBinds.pop()); - } - audioMgr.preload(); - }; - - // opt_noCaptureIdentifier is true because this is an action to take on a - // click, not a drag. - // Android ignores any sound not loaded as a result of a user action. - soundBinds.push( - Blockly.bindEventWithChecks_(document, 'mousemove', null, unbindSounds, - /* opt_noCaptureIdentifier */ true)); - soundBinds.push( - Blockly.bindEventWithChecks_(document, 'touchstart', null, unbindSounds, - /* opt_noCaptureIdentifier */ true)); -}; - -/** - * Modify the block tree on the existing toolbox. - * @param {Node|string} tree DOM tree of blocks, or text representation of same. - * @deprecated April 2015 - */ -Blockly.updateToolbox = function(tree) { - console.warn('Deprecated call to Blockly.updateToolbox, ' + - 'use workspace.updateToolbox instead.'); - Blockly.getMainWorkspace().updateToolbox(tree); -}; diff --git a/core/input.js b/core/input.js deleted file mode 100644 index 91bb4f12bf..0000000000 --- a/core/input.js +++ /dev/null @@ -1,285 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Object representing an input (value, statement, or dummy). - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.Input'); - -goog.require('Blockly.Connection'); -goog.require('Blockly.FieldLabel'); -goog.require('goog.asserts'); - - -/** - * Class for an input with an optional field. - * @param {number} type The type of the input. - * @param {string} name Language-neutral identifier which may used to find this - * input again. - * @param {!Blockly.Block} block The block containing this input. - * @param {Blockly.Connection} connection Optional connection for this input. - * @constructor - */ -Blockly.Input = function(type, name, block, connection) { - if (type != Blockly.DUMMY_INPUT && !name) { - throw 'Value inputs and statement inputs must have non-empty name.'; - } - /** @type {number} */ - this.type = type; - /** @type {string} */ - this.name = name; - /** - * @type {!Blockly.Block} - * @private - */ - this.sourceBlock_ = block; - /** @type {Blockly.Connection} */ - this.connection = connection; - /** @type {!Array.} */ - this.fieldRow = []; - - /** - * The shape that is displayed when this input is rendered but not filled. - * @type {SVGElement} - * @package - */ - this.outlinePath = null; -}; - -/** - * Alignment of input's fields (left, right or centre). - * @type {number} - */ -Blockly.Input.prototype.align = Blockly.ALIGN_LEFT; - -/** - * Is the input visible? - * @type {boolean} - * @private - */ -Blockly.Input.prototype.visible_ = true; - -/** - * Add a field (or label from string), and all prefix and suffix fields, to the - * end of the input's field row. - * @param {string|!Blockly.Field} field Something to add as a field. - * @param {string=} opt_name Language-neutral identifier which may used to find - * this field again. Should be unique to the host block. - * @return {!Blockly.Input} The input being append to (to allow chaining). - */ -Blockly.Input.prototype.appendField = function(field, opt_name) { - this.insertFieldAt(this.fieldRow.length, field, opt_name); - return this; -}; - -/** - * Inserts a field (or label from string), and all prefix and suffix fields, at - * the location of the input's field row. - * @param {number} index The index at which to insert field. - * @param {string|!Blockly.Field} field Something to add as a field. - * @param {string=} opt_name Language-neutral identifier which may used to find - * this field again. Should be unique to the host block. - * @return {number} The index following the last inserted field. - */ -Blockly.Input.prototype.insertFieldAt = function(index, field, opt_name) { - if (index < 0 || index > this.fieldRow.length) { - throw new Error('index ' + index + ' out of bounds.'); - } - - // Empty string, Null or undefined generates no field, unless field is named. - if (!field && !opt_name) { - return this; - } - // Generate a FieldLabel when given a plain text field. - if (goog.isString(field)) { - field = new Blockly.FieldLabel(/** @type {string} */ (field)); - } - field.setSourceBlock(this.sourceBlock_); - if (this.sourceBlock_.rendered) { - field.init(); - } - field.name = opt_name; - - if (field.prefixField) { - // Add any prefix. - index = this.insertFieldAt(index, field.prefixField); - } - // Add the field to the field row. - this.fieldRow.splice(index, 0, field); - ++index; - if (field.suffixField) { - // Add any suffix. - index = this.insertFieldAt(index, field.suffixField); - } - - if (this.sourceBlock_.rendered) { - this.sourceBlock_.render(); - // Adding a field will cause the block to change shape. - this.sourceBlock_.bumpNeighbours_(); - } - return index; -}; - -/** - * Remove a field from this input. - * @param {string} name The name of the field. - * @throws {goog.asserts.AssertionError} if the field is not present. - */ -Blockly.Input.prototype.removeField = function(name) { - for (var i = 0, field; field = this.fieldRow[i]; i++) { - if (field.name === name) { - field.dispose(); - this.fieldRow.splice(i, 1); - if (this.sourceBlock_.rendered) { - this.sourceBlock_.render(); - // Removing a field will cause the block to change shape. - this.sourceBlock_.bumpNeighbours_(); - } - return; - } - } - goog.asserts.fail('Field "%s" not found.', name); -}; - -/** - * Gets whether this input is visible or not. - * @return {boolean} True if visible. - */ -Blockly.Input.prototype.isVisible = function() { - return this.visible_; -}; - -/** - * Sets whether this input is visible or not. - * Used to collapse/uncollapse a block. - * @param {boolean} visible True if visible. - * @return {!Array.} List of blocks to render. - */ -Blockly.Input.prototype.setVisible = function(visible) { - var renderList = []; - if (this.visible_ == visible) { - return renderList; - } - this.visible_ = visible; - - var display = visible ? 'block' : 'none'; - for (var y = 0, field; field = this.fieldRow[y]; y++) { - field.setVisible(visible); - } - if (this.connection) { - // Has a connection. - if (visible) { - renderList = this.connection.unhideAll(); - } else { - this.connection.hideAll(); - } - var child = this.connection.targetBlock(); - if (child) { - child.getSvgRoot().style.display = display; - if (!visible) { - child.rendered = false; - } - } - } - return renderList; -}; - -/** - * Change a connection's compatibility. - * @param {string|Array.|null} check Compatible value type or - * list of value types. Null if all types are compatible. - * @return {!Blockly.Input} The input being modified (to allow chaining). - */ -Blockly.Input.prototype.setCheck = function(check) { - if (!this.connection) { - throw 'This input does not have a connection.'; - } - this.connection.setCheck(check); - return this; -}; - -/** - * Change the alignment of the connection's field(s). - * @param {number} align One of Blockly.ALIGN_LEFT, ALIGN_CENTRE, ALIGN_RIGHT. - * In RTL mode directions are reversed, and ALIGN_RIGHT aligns to the left. - * @return {!Blockly.Input} The input being modified (to allow chaining). - */ -Blockly.Input.prototype.setAlign = function(align) { - this.align = align; - if (this.sourceBlock_.rendered) { - this.sourceBlock_.render(); - } - return this; -}; - -/** - * Initialize the fields on this input. - */ -Blockly.Input.prototype.init = function() { - if (!this.sourceBlock_.workspace.rendered) { - return; // Headless blocks don't need fields initialized. - } - for (var i = 0; i < this.fieldRow.length; i++) { - this.fieldRow[i].init(this.sourceBlock_); - } -}; - -/** - * Sever all links to this input. - */ -Blockly.Input.prototype.dispose = function() { - if (this.outlinePath) { - goog.dom.removeNode(this.outlinePath); - } - for (var i = 0, field; field = this.fieldRow[i]; i++) { - field.dispose(); - } - if (this.connection) { - this.connection.dispose(); - } - this.sourceBlock_ = null; -}; - -/** - * Create the input shape path element and attach it to the given SVG element. - * @param {!SVGElement} svgRoot The parent on which ot append the new element. - * @package - */ -Blockly.Input.prototype.initOutlinePath = function(svgRoot) { - if (!this.sourceBlock_.workspace.rendered) { - return; // Headless blocks don't need field outlines. - } - if (this.outlinePath) { - return; - } - if (this.type == Blockly.INPUT_VALUE) { - this.outlinePath = Blockly.utils.createSvgElement( - 'path', - { - 'class': 'blocklyPath', - 'style': 'visibility: hidden', // Hide by default - shown when not connected. - 'd': '' // IE doesn't like paths without the data definition, set an empty default - }, - svgRoot); - } -}; diff --git a/core/insertion_marker_manager.js b/core/insertion_marker_manager.js deleted file mode 100644 index 2136a43f33..0000000000 --- a/core/insertion_marker_manager.js +++ /dev/null @@ -1,678 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Class that controls updates to connections during drags. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.InsertionMarkerManager'); - -goog.require('Blockly.BlockAnimations'); -goog.require('Blockly.Events.BlockMove'); -goog.require('Blockly.RenderedConnection'); - -goog.require('goog.math.Coordinate'); - - -/** - * Class that controls updates to connections during drags. It is primarily - * responsible for finding the closest eligible connection and highlighting or - * unhiglighting it as needed during a drag. - * @param {!Blockly.BlockSvg} block The top block in the stack being dragged. - * @constructor - */ -Blockly.InsertionMarkerManager = function(block) { - Blockly.selected = block; - - /** - * The top block in the stack being dragged. - * Does not change during a drag. - * @type {!Blockly.Block} - * @private - */ - this.topBlock_ = block; - - /** - * The workspace on which these connections are being dragged. - * Does not change during a drag. - * @type {!Blockly.WorkspaceSvg} - * @private - */ - this.workspace_ = block.workspace; - - /** - * The last connection on the stack, if it's not the last connection on the - * first block. - * Set in initAvailableConnections, if at all. - * @type {Blockly.RenderedConnection} - * @private - */ - this.lastOnStack_ = null; - - /** - * The insertion marker corresponding to the last block in the stack, if - * that's not the same as the first block in the stack. - * Set in initAvailableConnections, if at all - * @type {Blockly.BlockSvg} - * @private - */ - this.lastMarker_ = null; - - /** - * The insertion marker that shows up between blocks to show where a block - * would go if dropped immediately. - * This is the scratch-blocks equivalent of connection highlighting. - * @type {Blockly.BlockSvg} - * @private - */ - this.firstMarker_ = this.createMarkerBlock_(this.topBlock_); - - /** - * The connection that this block would connect to if released immediately. - * Updated on every mouse move. - * This is not on any of the blocks that are being dragged. - * @type {Blockly.RenderedConnection} - * @private - */ - this.closestConnection_ = null; - - /** - * The connection that would connect to this.closestConnection_ if this block - * were released immediately. - * Updated on every mouse move. - * This is on the top block that is being dragged or the last block in the - * dragging stack. - * @type {Blockly.RenderedConnection} - * @private - */ - this.localConnection_ = null; - - /** - * Whether the block would be deleted if it were dropped immediately. - * Updated on every mouse move. - * @type {boolean} - * @private - */ - this.wouldDeleteBlock_ = false; - - /** - * Connection on the insertion marker block that corresponds to - * this.localConnection_ on the currently dragged block. - * This is part of the scratch-blocks equivalent of connection highlighting. - * @type {Blockly.RenderedConnection} - * @private - */ - this.markerConnection_ = null; - - /** - * Whether we are currently highlighting the block (shadow or real) that would - * be replaced if the drag were released immediately. - * @type {boolean} - * @private - */ - this.highlightingBlock_ = false; - - /** - * The block that is being highlighted for replacement, or null. - * @type {Blockly.BlockSvg} - * @private - */ - this.highlightedBlock_ = null; - - /** - * The connections on the dragging blocks that are available to connect to - * other blocks. This includes all open connections on the top block, as well - * as the last connection on the block stack. - * Does not change during a drag. - * @type {!Array.} - * @private - */ - this.availableConnections_ = this.initAvailableConnections_(); -}; - -/** - * Sever all links from this object. - * @package - */ -Blockly.InsertionMarkerManager.prototype.dispose = function() { - this.topBlock_ = null; - this.workspace_ = null; - this.availableConnections_.length = 0; - this.closestConnection_ = null; - this.localConnection_ = null; - - Blockly.Events.disable(); - try { - if (this.firstMarker_) { - this.firstMarker_.dispose(); - this.firstMarker_ = null; - } - if (this.lastMarker_) { - this.lastMarker_.dispose(); - this.lastMarker_ = null; - } - } finally { - Blockly.Events.enable(); - } - - this.highlightedBlock_ = null; -}; - -/** - * Return whether the block would be deleted if dropped immediately, based on - * information from the most recent move event. - * @return {boolean} true if the block would be deleted if dropped immediately. - * @package - */ -Blockly.InsertionMarkerManager.prototype.wouldDeleteBlock = function() { - return this.wouldDeleteBlock_; -}; - -/** - * Return whether the block would be connected if dropped immediately, based on - * information from the most recent move event. - * @return {boolean} True if the block would be connected if dropped - * immediately. - * @package - */ -Blockly.InsertionMarkerManager.prototype.wouldConnectBlock = function() { - return !!this.closestConnection_; -}; - -/** - * Connect to the closest connection and render the results. - * This should be called at the end of a drag. - * @package - */ -Blockly.InsertionMarkerManager.prototype.applyConnections = function() { - if (this.closestConnection_) { - // Don't fire events for insertion markers. - Blockly.Events.disable(); - this.hidePreview_(); - Blockly.Events.enable(); - // Connect two blocks together. - this.localConnection_.connect(this.closestConnection_); - if (this.topBlock_.rendered) { - // Trigger a connection animation. - // Determine which connection is inferior (lower in the source stack). - var inferiorConnection = this.localConnection_.isSuperior() ? - this.closestConnection_ : this.localConnection_; - Blockly.BlockAnimations.connectionUiEffect( - inferiorConnection.getSourceBlock()); - // Bring the just-edited stack to the front. - var rootBlock = this.topBlock_.getRootBlock(); - rootBlock.bringToFront(); - } - } -}; - -/** - * Update highlighted connections based on the most recent move location. - * @param {!goog.math.Coordinate} dxy Position relative to drag start, - * in workspace units. - * @param {?number} deleteArea One of {@link Blockly.DELETE_AREA_TRASH}, - * {@link Blockly.DELETE_AREA_TOOLBOX}, or {@link Blockly.DELETE_AREA_NONE}. - * @package - */ -Blockly.InsertionMarkerManager.prototype.update = function(dxy, deleteArea) { - var candidate = this.getCandidate_(dxy); - - this.wouldDeleteBlock_ = this.shouldDelete_(candidate, deleteArea); - var shouldUpdate = this.wouldDeleteBlock_ || - this.shouldUpdatePreviews_(candidate, dxy); - - if (shouldUpdate) { - // Don't fire events for insertion marker creation or movement. - Blockly.Events.disable(); - this.maybeHidePreview_(candidate); - this.maybeShowPreview_(candidate); - Blockly.Events.enable(); - } -}; - -/**** Begin initialization functions ****/ - -/** - * Create an insertion marker that represents the given block. - * @param {!Blockly.BlockSvg} sourceBlock The block that the insertion marker - * will represent. - * @return {!Blockly.BlockSvg} The insertion marker that represents the given - * block. - * @private - */ -Blockly.InsertionMarkerManager.prototype.createMarkerBlock_ = function(sourceBlock) { - var imType = sourceBlock.type; - - Blockly.Events.disable(); - try { - var result = this.workspace_.newBlock(imType); - result.setInsertionMarker(true, sourceBlock.width); - if (sourceBlock.mutationToDom) { - var oldMutationDom = sourceBlock.mutationToDom(); - if (oldMutationDom) { - result.domToMutation(oldMutationDom); - } - } - result.initSvg(); - } finally { - Blockly.Events.enable(); - } - - return result; -}; - -/** - * Populate the list of available connections on this block stack. This should - * only be called once, at the beginning of a drag. - * If the stack has more than one block, this function will populate - * lastOnStack_ and create the corresponding insertion marker. - * @return {!Array.} a list of available - * connections. - * @private - */ -Blockly.InsertionMarkerManager.prototype.initAvailableConnections_ = function() { - var available = this.topBlock_.getConnections_(false); - // Also check the last connection on this stack - var lastOnStack = this.topBlock_.lastConnectionInStack(); - if (lastOnStack && lastOnStack != this.topBlock_.nextConnection) { - available.push(lastOnStack); - this.lastOnStack_ = lastOnStack; - this.lastMarker_ = this.createMarkerBlock_(lastOnStack.sourceBlock_); - } - return available; -}; - -/**** End initialization functions ****/ - - -/** - * Whether the previews (insertion marker and replacement marker) should be - * updated based on the closest candidate and the current drag distance. - * @param {!Object} candidate An object containing a local connection, a closest - * connection, and a radius. Returned by getCandidate_. - * @param {!goog.math.Coordinate} dxy Position relative to drag start, - * in workspace units. - * @return {boolean} whether the preview should be updated. - * @private - */ -Blockly.InsertionMarkerManager.prototype.shouldUpdatePreviews_ = function( - candidate, dxy) { - var candidateLocal = candidate.local; - var candidateClosest = candidate.closest; - var radius = candidate.radius; - - // Found a connection! - if (candidateLocal && candidateClosest) { - if (candidateLocal.type == Blockly.OUTPUT_VALUE) { - // Always update previews for output connections. - return true; - } - // We're already showing an insertion marker. - // Decide whether the new connection has higher priority. - if (this.localConnection_ && this.closestConnection_) { - // The connection was the same as the current connection. - if (this.closestConnection_ == candidateClosest) { - return false; - } - var xDiff = this.localConnection_.x_ + dxy.x - this.closestConnection_.x_; - var yDiff = this.localConnection_.y_ + dxy.y - this.closestConnection_.y_; - var curDistance = Math.sqrt(xDiff * xDiff + yDiff * yDiff); - // Slightly prefer the existing preview over a new preview. - return !(candidateClosest && radius > curDistance - - Blockly.CURRENT_CONNECTION_PREFERENCE); - } else if (!this.localConnection_ && !this.closestConnection_) { - // We weren't showing a preview before, but we should now. - return true; - } else { - console.error('Only one of localConnection_ and closestConnection_ was set.'); - } - } else { // No connection found. - // Only need to update if we were showing a preview before. - return !!(this.localConnection_ && this.closestConnection_); - } - - console.error('Returning true from shouldUpdatePreviews, but it\'s not clear why.'); - return true; -}; - -/** - * Find the nearest valid connection, which may be the same as the current - * closest connection. - * @param {!goog.math.Coordinate} dxy Position relative to drag start, - * in workspace units. - * @return {!Object} candidate An object containing a local connection, a closest - * connection, and a radius. - */ -Blockly.InsertionMarkerManager.prototype.getCandidate_ = function(dxy) { - var radius = this.getStartRadius_(); - var candidateClosest = null; - var candidateLocal = null; - - for (var i = 0; i < this.availableConnections_.length; i++) { - var myConnection = this.availableConnections_[i]; - var neighbour = myConnection.closest(radius, dxy); - if (neighbour.connection) { - candidateClosest = neighbour.connection; - candidateLocal = myConnection; - radius = neighbour.radius; - } - } - return { - closest: candidateClosest, - local: candidateLocal, - radius: radius - }; -}; - -/** - * Decide the radius at which to start searching for the closest connection. - * @return {number} The radius at which to start the search for the closest - * connection. - * @private - */ -Blockly.InsertionMarkerManager.prototype.getStartRadius_ = function() { - // If there is already a connection highlighted, - // increase the radius we check for making new connections. - // Why? When a connection is highlighted, blocks move around when the insertion - // marker is created, which could cause the connection became out of range. - // By increasing radiusConnection when a connection already exists, - // we never "lose" the connection from the offset. - if (this.closestConnection_ && this.localConnection_) { - return Blockly.CONNECTING_SNAP_RADIUS; - } - return Blockly.SNAP_RADIUS; -}; - -/** - * Whether ending the drag would replace a block or insert a block. - * @return {boolean} True if dropping the block immediately would replace - * another block. False if dropping the block immediately would result in - * the block being inserted in a block stack. - * @private - */ -Blockly.InsertionMarkerManager.prototype.shouldReplace_ = function() { - var closest = this.closestConnection_; - var local = this.localConnection_; - - // Dragging a block over an existing block in an input should replace the - // existing block and bump it out. - if (local.type == Blockly.OUTPUT_VALUE) { - return true; // Replace. - } - - // Connecting to a statement input of c-block is an insertion, even if that - // c-block is terminal (e.g. forever). - if (local == local.sourceBlock_.getFirstStatementConnection()) { - return false; // Insert. - } - - // Dragging a terminal block over another (connected) terminal block will - // replace, not insert. - var isTerminalBlock = !this.topBlock_.nextConnection; - var isConnectedTerminal = isTerminalBlock && - local.type == Blockly.PREVIOUS_STATEMENT && closest.isConnected(); - if (isConnectedTerminal) { - return true; // Replace. - } - - // Otherwise it's an insertion. - return false; -}; - -/** - * Whether ending the drag would delete the block. - * @param {!Object} candidate An object containing a local connection, a closest - * connection, and a radius. - * @param {?number} deleteArea One of {@link Blockly.DELETE_AREA_TRASH}, - * {@link Blockly.DELETE_AREA_TOOLBOX}, or {@link Blockly.DELETE_AREA_NONE}. - * @return {boolean} True if dropping the block immediately would replace - * delete the block. False otherwise. - * @private - */ -Blockly.InsertionMarkerManager.prototype.shouldDelete_ = function(candidate, - deleteArea) { - // Prefer connecting over dropping into the trash can, but prefer dragging to - // the toolbox over connecting to other blocks. - var wouldConnect = candidate && !!candidate.closest && - deleteArea != Blockly.DELETE_AREA_TOOLBOX; - var wouldDelete = !!deleteArea && !this.topBlock_.getParent() && - this.topBlock_.isDeletable(); - - return wouldDelete && !wouldConnect; -}; - -/**** Begin preview visibility functions ****/ - -/** - * Show an insertion marker or replacement highlighting during a drag, if - * needed. - * At the beginning of this function, this.localConnection_ and - * this.closestConnection_ should both be null. - * @param {!Object} candidate An object containing a local connection, a closest - * connection, and a radius. - * @private - */ -Blockly.InsertionMarkerManager.prototype.maybeShowPreview_ = function(candidate) { - // Nope, don't add a marker. - if (this.wouldDeleteBlock_) { - return; - } - var closest = candidate.closest; - var local = candidate.local; - - // Nothing to connect to. - if (!closest) { - return; - } - - // Something went wrong and we're trying to connect to an invalid connection. - if (closest == this.closestConnection_ || - closest.sourceBlock_.isInsertionMarker()) { - return; - } - // Add an insertion marker or replacement marker. - this.closestConnection_ = closest; - this.localConnection_ = local; - this.showPreview_(); -}; - -/** - * A preview should be shown. This function figures out if it should be a block - * highlight or an insertion marker, and shows the appropriate one. - * @private - */ -Blockly.InsertionMarkerManager.prototype.showPreview_ = function() { - if (this.shouldReplace_()) { - this.highlightBlock_(); - } else { // Should insert - this.connectMarker_(); - } -}; - -/** - * Show an insertion marker or replacement highlighting during a drag, if - * needed. - * At the end of this function, this.localConnection_ and - * this.closestConnection_ should both be null. - * @param {!Object} candidate An object containing a local connection, a closest - * connection, and a radius. - * @private - */ -Blockly.InsertionMarkerManager.prototype.maybeHidePreview_ = function(candidate) { - // If there's no new preview, remove the old one but don't bother deleting it. - // We might need it later, and this saves disposing of it and recreating it. - if (!candidate.closest) { - this.hidePreview_(); - } - // If there's a new preview and there was an preview before, and either - // connection has changed, remove the old preview. - var hadPreview = this.closestConnection_ && this.localConnection_; - var closestChanged = this.closestConnection_ != candidate.closest; - var localChanged = this.localConnection_ != candidate.local; - - // Also hide if we had a preview before but now we're going to delete instead. - if (hadPreview && (closestChanged || localChanged || this.wouldDeleteBlock_)) { - this.hidePreview_(); - } - - // Either way, clear out old state. - this.markerConnection_ = null; - this.closestConnection_ = null; - this.localConnection_ = null; -}; - -/** - * A preview should be hidden. This function figures out if it is a block - * highlight or an insertion marker, and hides the appropriate one. - * @private - */ -Blockly.InsertionMarkerManager.prototype.hidePreview_ = function() { - if (this.highlightingBlock_) { - this.unhighlightBlock_(); - } else if (this.markerConnection_) { - this.disconnectMarker_(); - } -}; - -/**** End preview visibility functions ****/ - -/**** Begin block highlighting functions ****/ - -/** - * Add highlighting showing which block will be replaced. - * Scratch-specific code, where "highlighting" applies to a block rather than - * a connection. - */ -Blockly.InsertionMarkerManager.prototype.highlightBlock_ = function() { - var closest = this.closestConnection_; - var local = this.localConnection_; - if (closest.targetBlock()) { - this.highlightedBlock_ = closest.targetBlock(); - closest.targetBlock().highlightForReplacement(true); - } else if(local.type == Blockly.OUTPUT_VALUE) { - this.highlightedBlock_ = closest.sourceBlock_; - closest.sourceBlock_.highlightShapeForInput(closest, true); - } - this.highlightingBlock_ = true; -}; - -/** - * Get rid of the highlighting marking the block that will be replaced. - * Scratch-specific code, where "highlighting" applies to a block rather than - * a connection. - */ -Blockly.InsertionMarkerManager.prototype.unhighlightBlock_ = function() { - var closest = this.closestConnection_; - // If there's no block in place, but we're still connecting to a value input, - // then we must have been highlighting an input shape. - if (closest.type == Blockly.INPUT_VALUE && !closest.isConnected()) { - this.highlightedBlock_.highlightShapeForInput(closest, false); - } else { - this.highlightedBlock_.highlightForReplacement(false); - } - this.highlightedBlock_ = null; - this.highlightingBlock_ = false; -}; - -/**** End block highlighting functions ****/ - -/**** Begin insertion marker display functions ****/ - -/** - * Disconnect the insertion marker block in a manner that returns the stack to - * original state. - * @private - */ -Blockly.InsertionMarkerManager.prototype.disconnectMarker_ = function() { - if (!this.markerConnection_) { - console.log('No insertion marker connection to disconnect'); - return; - } - - var imConn = this.markerConnection_; - var imBlock = imConn.sourceBlock_; - var markerNext = imBlock.nextConnection; - var markerPrev = imBlock.previousConnection; - - - // The insertion marker is the first block in a stack, either because it - // doesn't have a previous connection or because the previous connection is - // not connected. Unplug won't do anything in that case. Instead, unplug the - // following block. - if (imConn == markerNext && !(markerPrev && markerPrev.targetConnection)) { - imConn.targetBlock().unplug(false); - } - // Inside of a C-block, first statement connection. - else if (imConn.type == Blockly.NEXT_STATEMENT && imConn != markerNext) { - var innerConnection = imConn.targetConnection; - innerConnection.sourceBlock_.unplug(false); - - var previousBlockNextConnection = - markerPrev ? markerPrev.targetConnection : null; - - imBlock.unplug(true); - if (previousBlockNextConnection) { - previousBlockNextConnection.connect(innerConnection); - } - } else { - imBlock.unplug(true /* healStack */); - } - - if (imConn.targetConnection) { - throw 'markerConnection_ still connected at the end of disconnectInsertionMarker'; - } - - this.markerConnection_ = null; - imBlock.getSvgRoot().setAttribute('visibility', 'hidden'); -}; - -/** - * Add an insertion marker connected to the appropriate blocks. - * @private - */ -Blockly.InsertionMarkerManager.prototype.connectMarker_ = function() { - var local = this.localConnection_; - var closest = this.closestConnection_; - - var isLastInStack = this.lastOnStack_ && local == this.lastOnStack_; - var imBlock = isLastInStack ? this.lastMarker_ : this.firstMarker_; - var imConn = imBlock.getMatchingConnection(local.sourceBlock_, local); - - goog.asserts.assert(imConn != this.markerConnection_, - 'Made it to connectMarker_ even though the marker isn\'t changing'); - - // Render disconnected from everything else so that we have a valid - // connection location. - imBlock.render(); - imBlock.rendered = true; - imBlock.getSvgRoot().setAttribute('visibility', 'visible'); - - // TODO: positionNewBlock should be on Blockly.BlockSvg, not prototype, - // because it doesn't rely on anything in the block it's called on. - imBlock.positionNewBlock(imBlock, imConn, closest); - - // Connect() also renders the insertion marker. - imConn.connect(closest); - this.markerConnection_ = imConn; -}; - -/**** End insertion marker display functions ****/ diff --git a/core/msg.js b/core/msg.js deleted file mode 100644 index 4ebcad1ab7..0000000000 --- a/core/msg.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2013 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Empty name space for the Message singleton. - * @author scr@google.com (Sheridan Rawlins) - */ -'use strict'; - -/** - * Name space for the Msg singleton. - * Msg gets populated in the message files. - */ -goog.provide('Blockly.Msg'); - - -/** - * Back up original getMsg function. - * @type {!Function} - */ -goog.getMsgOrig = goog.getMsg; - -/** - * Gets a localized message. - * Overrides the default Closure function to check for a Blockly.Msg first. - * Used infrequently, only known case is TODAY button in date picker. - * @param {string} str Translatable string, places holders in the form {$foo}. - * @param {Object=} opt_values Maps place holder name to value. - * @return {string} message with placeholders filled. - * @suppress {duplicate} - */ -goog.getMsg = function(str, opt_values) { - var key = goog.getMsg.blocklyMsgMap[str]; - if (key) { - str = Blockly.Msg[key]; - } - return goog.getMsgOrig(str, opt_values); -}; - -/** - * Mapping of Closure messages to Blockly.Msg names. - */ -goog.getMsg.blocklyMsgMap = { - 'Today': 'TODAY' -}; diff --git a/core/mutator.js b/core/mutator.js deleted file mode 100644 index b85afde174..0000000000 --- a/core/mutator.js +++ /dev/null @@ -1,426 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Object representing a mutator dialog. A mutator allows the - * user to change the shape of a block using a nested blocks editor. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.Mutator'); - -goog.require('Blockly.Bubble'); -goog.require('Blockly.Events.BlockChange'); -goog.require('Blockly.Events.Ui'); -goog.require('Blockly.Icon'); -goog.require('Blockly.WorkspaceSvg'); -goog.require('goog.dom'); - - -/** - * Class for a mutator dialog. - * @param {!Array.} quarkNames List of names of sub-blocks for flyout. - * @extends {Blockly.Icon} - * @constructor - */ -Blockly.Mutator = function(quarkNames) { - Blockly.Mutator.superClass_.constructor.call(this, null); - this.quarkNames_ = quarkNames; -}; -goog.inherits(Blockly.Mutator, Blockly.Icon); - -/** - * Width of workspace. - * @private - */ -Blockly.Mutator.prototype.workspaceWidth_ = 0; - -/** - * Height of workspace. - * @private - */ -Blockly.Mutator.prototype.workspaceHeight_ = 0; - -/** - * Draw the mutator icon. - * @param {!Element} group The icon group. - * @private - */ -Blockly.Mutator.prototype.drawIcon_ = function(group) { - // Square with rounded corners. - Blockly.utils.createSvgElement('rect', - { - 'class': 'blocklyIconShape', - 'rx': '4', - 'ry': '4', - 'height': '16', - 'width': '16' - }, - group); - // Gear teeth. - Blockly.utils.createSvgElement('path', - { - 'class': 'blocklyIconSymbol', - 'd': 'm4.203,7.296 0,1.368 -0.92,0.677 -0.11,0.41 0.9,1.559 0.41,' + - '0.11 1.043,-0.457 1.187,0.683 0.127,1.134 0.3,0.3 1.8,0 0.3,' + - '-0.299 0.127,-1.138 1.185,-0.682 1.046,0.458 0.409,-0.11 0.9,' + - '-1.559 -0.11,-0.41 -0.92,-0.677 0,-1.366 0.92,-0.677 0.11,' + - '-0.41 -0.9,-1.559 -0.409,-0.109 -1.046,0.458 -1.185,-0.682 ' + - '-0.127,-1.138 -0.3,-0.299 -1.8,0 -0.3,0.3 -0.126,1.135 -1.187,' + - '0.682 -1.043,-0.457 -0.41,0.11 -0.899,1.559 0.108,0.409z' - }, - group); - // Axle hole. - Blockly.utils.createSvgElement( - 'circle', - { - 'class': 'blocklyIconShape', - 'r': '2.7', - 'cx': '8', - 'cy': '8' - }, - group); -}; - -/** - * Clicking on the icon toggles if the mutator bubble is visible. - * Disable if block is uneditable. - * @param {!Event} e Mouse click event. - * @private - * @override - */ -Blockly.Mutator.prototype.iconClick_ = function(e) { - if (this.block_.isEditable()) { - Blockly.Icon.prototype.iconClick_.call(this, e); - } -}; - -/** - * Create the editor for the mutator's bubble. - * @return {!Element} The top-level node of the editor. - * @private - */ -Blockly.Mutator.prototype.createEditor_ = function() { - /* Create the editor. Here's the markup that will be generated: - - [Workspace] - - */ - this.svgDialog_ = Blockly.utils.createSvgElement('svg', - {'x': Blockly.Bubble.BORDER_WIDTH, 'y': Blockly.Bubble.BORDER_WIDTH}, - null); - // Convert the list of names into a list of XML objects for the flyout. - if (this.quarkNames_.length) { - var quarkXml = goog.dom.createDom('xml'); - for (var i = 0, quarkName; quarkName = this.quarkNames_[i]; i++) { - quarkXml.appendChild(goog.dom.createDom('block', {'type': quarkName})); - } - } else { - var quarkXml = null; - } - var workspaceOptions = { - languageTree: quarkXml, - parentWorkspace: this.block_.workspace, - pathToMedia: this.block_.workspace.options.pathToMedia, - RTL: this.block_.RTL, - toolboxPosition: this.block_.RTL ? Blockly.TOOLBOX_AT_RIGHT : - Blockly.TOOLBOX_AT_LEFT, - horizontalLayout: false, - getMetrics: this.getFlyoutMetrics_.bind(this), - setMetrics: null - }; - this.workspace_ = new Blockly.WorkspaceSvg(workspaceOptions, this.block_.workspace.dragSurface); - this.workspace_.isMutator = true; - - // Mutator flyouts go inside the mutator workspace's rather than in - // a top level svg. Instead of handling scale themselves, mutators - // inherit scale from the parent workspace. - // To fix this, scale needs to be applied at a different level in the dom. - var flyoutSvg = this.workspace_.addFlyout_('g'); - var background = this.workspace_.createDom('blocklyMutatorBackground'); - - // Insert the flyout after the but before the block canvas so that - // the flyout is underneath in z-order. This makes blocks layering during - // dragging work properly. - background.insertBefore(flyoutSvg, this.workspace_.svgBlockCanvas_); - this.svgDialog_.appendChild(background); - - return this.svgDialog_; -}; - -/** - * Add or remove the UI indicating if this icon may be clicked or not. - */ -Blockly.Mutator.prototype.updateEditable = function() { - if (!this.block_.isInFlyout) { - if (this.block_.isEditable()) { - if (this.iconGroup_) { - Blockly.utils.removeClass( - /** @type {!Element} */ (this.iconGroup_), - 'blocklyIconGroupReadonly'); - } - } else { - // Close any mutator bubble. Icon is not clickable. - this.setVisible(false); - if (this.iconGroup_) { - Blockly.utils.addClass( - /** @type {!Element} */ (this.iconGroup_), - 'blocklyIconGroupReadonly'); - } - } - } - // Default behaviour for an icon. - Blockly.Icon.prototype.updateEditable.call(this); -}; - -/** - * Callback function triggered when the bubble has resized. - * Resize the workspace accordingly. - * @private - */ -Blockly.Mutator.prototype.resizeBubble_ = function() { - var doubleBorderWidth = 2 * Blockly.Bubble.BORDER_WIDTH; - var workspaceSize = this.workspace_.getCanvas().getBBox(); - var width; - if (this.block_.RTL) { - width = -workspaceSize.x; - } else { - width = workspaceSize.width + workspaceSize.x; - } - var height = workspaceSize.height + doubleBorderWidth * 3; - if (this.workspace_.flyout_) { - var flyoutMetrics = this.workspace_.flyout_.getMetrics_(); - height = Math.max(height, flyoutMetrics.contentHeight + 20); - } - width += doubleBorderWidth * 3; - // Only resize if the size difference is significant. Eliminates shuddering. - if (Math.abs(this.workspaceWidth_ - width) > doubleBorderWidth || - Math.abs(this.workspaceHeight_ - height) > doubleBorderWidth) { - // Record some layout information for getFlyoutMetrics_. - this.workspaceWidth_ = width; - this.workspaceHeight_ = height; - // Resize the bubble. - this.bubble_.setBubbleSize( - width + doubleBorderWidth, height + doubleBorderWidth); - this.svgDialog_.setAttribute('width', this.workspaceWidth_); - this.svgDialog_.setAttribute('height', this.workspaceHeight_); - } - - if (this.block_.RTL) { - // Scroll the workspace to always left-align. - var translation = 'translate(' + this.workspaceWidth_ + ',0)'; - this.workspace_.getCanvas().setAttribute('transform', translation); - } - this.workspace_.resize(); -}; - -/** - * Show or hide the mutator bubble. - * @param {boolean} visible True if the bubble should be visible. - */ -Blockly.Mutator.prototype.setVisible = function(visible) { - if (visible == this.isVisible()) { - // No change. - return; - } - Blockly.Events.fire( - new Blockly.Events.Ui(this.block_, 'mutatorOpen', !visible, visible)); - if (visible) { - // Create the bubble. - this.bubble_ = new Blockly.Bubble( - /** @type {!Blockly.WorkspaceSvg} */ (this.block_.workspace), - this.createEditor_(), this.block_.svgPath_, this.iconXY_, null, null); - var tree = this.workspace_.options.languageTree; - if (tree) { - this.workspace_.flyout_.init(this.workspace_); - this.workspace_.flyout_.show(tree.childNodes); - } - - this.rootBlock_ = this.block_.decompose(this.workspace_); - var blocks = this.rootBlock_.getDescendants(false); - for (var i = 0, child; child = blocks[i]; i++) { - child.render(); - } - // The root block should not be dragable or deletable. - this.rootBlock_.setMovable(false); - this.rootBlock_.setDeletable(false); - if (this.workspace_.flyout_) { - var margin = this.workspace_.flyout_.CORNER_RADIUS * 2; - var x = this.workspace_.flyout_.width_ + margin; - } else { - var margin = 16; - var x = margin; - } - if (this.block_.RTL) { - x = -x; - } - this.rootBlock_.moveBy(x, margin); - // Save the initial connections, then listen for further changes. - if (this.block_.saveConnections) { - var thisMutator = this; - this.block_.saveConnections(this.rootBlock_); - this.sourceListener_ = function() { - thisMutator.block_.saveConnections(thisMutator.rootBlock_); - }; - this.block_.workspace.addChangeListener(this.sourceListener_); - } - this.resizeBubble_(); - // When the mutator's workspace changes, update the source block. - this.workspace_.addChangeListener(this.workspaceChanged_.bind(this)); - this.updateColour(); - } else { - // Dispose of the bubble. - this.svgDialog_ = null; - this.workspace_.dispose(); - this.workspace_ = null; - this.rootBlock_ = null; - this.bubble_.dispose(); - this.bubble_ = null; - this.workspaceWidth_ = 0; - this.workspaceHeight_ = 0; - if (this.sourceListener_) { - this.block_.workspace.removeChangeListener(this.sourceListener_); - this.sourceListener_ = null; - } - } -}; - -/** - * Update the source block when the mutator's blocks are changed. - * Bump down any block that's too high. - * Fired whenever a change is made to the mutator's workspace. - * @private - */ -Blockly.Mutator.prototype.workspaceChanged_ = function() { - if (!this.workspace_.isDragging()) { - var blocks = this.workspace_.getTopBlocks(false); - var MARGIN = 20; - for (var b = 0, block; block = blocks[b]; b++) { - var blockXY = block.getRelativeToSurfaceXY(); - var blockHW = block.getHeightWidth(); - if (blockXY.y + blockHW.height < MARGIN) { - // Bump any block that's above the top back inside. - block.moveBy(0, MARGIN - blockHW.height - blockXY.y); - } - } - } - - // When the mutator's workspace changes, update the source block. - if (this.rootBlock_.workspace == this.workspace_) { - Blockly.Events.setGroup(true); - var block = this.block_; - var oldMutationDom = block.mutationToDom(); - var oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom); - // Switch off rendering while the source block is rebuilt. - var savedRendered = block.rendered; - block.rendered = false; - // Allow the source block to rebuild itself. - block.compose(this.rootBlock_); - // Restore rendering and show the changes. - block.rendered = savedRendered; - // Mutation may have added some elements that need initializing. - block.initSvg(); - var newMutationDom = block.mutationToDom(); - var newMutation = newMutationDom && Blockly.Xml.domToText(newMutationDom); - if (oldMutation != newMutation) { - Blockly.Events.fire(new Blockly.Events.BlockChange( - block, 'mutation', null, oldMutation, newMutation)); - // Ensure that any bump is part of this mutation's event group. - var group = Blockly.Events.getGroup(); - setTimeout(function() { - Blockly.Events.setGroup(group); - block.bumpNeighbours_(); - Blockly.Events.setGroup(false); - }, Blockly.BUMP_DELAY); - } - if (block.rendered) { - block.render(); - } - // Don't update the bubble until the drag has ended, to avoid moving blocks - // under the cursor. - if (!this.workspace_.isDragging()) { - this.resizeBubble_(); - } - Blockly.Events.setGroup(false); - } -}; - -/** - * Return an object with all the metrics required to size scrollbars for the - * mutator flyout. The following properties are computed: - * .viewHeight: Height of the visible rectangle, - * .viewWidth: Width of the visible rectangle, - * .absoluteTop: Top-edge of view. - * .absoluteLeft: Left-edge of view. - * @return {!Object} Contains size and position metrics of mutator dialog's - * workspace. - * @private - */ -Blockly.Mutator.prototype.getFlyoutMetrics_ = function() { - return { - viewHeight: this.workspaceHeight_, - viewWidth: this.workspaceWidth_, - absoluteTop: 0, - absoluteLeft: 0 - }; -}; - -/** - * Dispose of this mutator. - */ -Blockly.Mutator.prototype.dispose = function() { - this.block_.mutator = null; - Blockly.Icon.prototype.dispose.call(this); -}; - -/** - * Reconnect an block to a mutated input. - * @param {Blockly.Connection} connectionChild Connection on child block. - * @param {!Blockly.Block} block Parent block. - * @param {string} inputName Name of input on parent block. - * @return {boolean} True iff a reconnection was made, false otherwise. - */ -Blockly.Mutator.reconnect = function(connectionChild, block, inputName) { - if (!connectionChild || !connectionChild.getSourceBlock().workspace) { - return false; // No connection or block has been deleted. - } - var connectionParent = block.getInput(inputName).connection; - var currentParent = connectionChild.targetBlock(); - if ((!currentParent || currentParent == block) && - connectionParent.targetConnection != connectionChild) { - if (connectionParent.isConnected()) { - // There's already something connected here. Get rid of it. - connectionParent.disconnect(); - } - connectionParent.connect(connectionChild); - return true; - } - return false; -}; - -// Export symbols that would otherwise be renamed by Closure compiler. -if (!goog.global['Blockly']) { - goog.global['Blockly'] = {}; -} -if (!goog.global['Blockly']['Mutator']) { - goog.global['Blockly']['Mutator'] = {}; -} -goog.global['Blockly']['Mutator']['reconnect'] = Blockly.Mutator.reconnect; diff --git a/core/names.js b/core/names.js deleted file mode 100644 index b76978b62c..0000000000 --- a/core/names.js +++ /dev/null @@ -1,198 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Utility functions for handling variables and procedure names. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.Names'); - - -/** - * Class for a database of entity names (variables, functions, 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. - * @constructor - */ -Blockly.Names = function(reservedWords, opt_variablePrefix) { - this.variablePrefix_ = opt_variablePrefix || ''; - this.reservedDict_ = Object.create(null); - if (reservedWords) { - var splitWords = reservedWords.split(','); - for (var i = 0; i < splitWords.length; i++) { - this.reservedDict_[splitWords[i]] = true; - } - } - this.reset(); -}; - -/** - * Constant to separate developer variable names from user-defined variable - * names when running generators. - * A developer variable will be declared as a global in the generated code, but - * will never be shown to the user in the workspace or stored in the variable - * map. - */ -Blockly.Names.DEVELOPER_VARIABLE_TYPE = 'DEVELOPER_VARIABLE'; - -/** - * 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 type name to disambiguate. - * getName('foo', 'variable') -> 'foo' - * getName('foo', 'procedure') -> 'foo2' - */ - -/** - * Empty the database and start from scratch. The reserved words are kept. - */ -Blockly.Names.prototype.reset = function() { - this.db_ = Object.create(null); - this.dbReverse_ = Object.create(null); - this.variableMap_ = null; -}; - -/** - * Set the variable map that maps from variable name to variable object. - * @param {!Blockly.VariableMap} map The map to track. - * @package - */ -Blockly.Names.prototype.setVariableMap = function(map) { - this.variableMap_ = map; -}; - -/** - * Get the name for a user-defined variable, based on its ID. - * This should only be used for variables of type Blockly.Variables.NAME_TYPE. - * @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 - */ -Blockly.Names.prototype.getNameForUserVariable_ = function(id) { - if (!this.variableMap_) { - console.log('Deprecated call to Blockly.Names.prototype.getName without ' + - 'defining a variable map. To fix, add the folowing code in your ' + - 'generator\'s init() function:\n' + - 'Blockly.YourGeneratorName.variableDB_.setVariableMap(' + - 'workspace.getVariableMap());'); - return null; - } - var variable = this.variableMap_.getVariableById(id); - if (variable) { - return variable.name; - } else { - return null; - } -}; - -/** - * Convert a Blockly entity name to a legal exportable entity name. - * @param {string} name The Blockly entity name (no constraints). - * @param {string} type The type of entity in Blockly - * ('VARIABLE', 'PROCEDURE', 'BUILTIN', etc...). - * @return {string} An entity name that is legal in the exported language. - */ -Blockly.Names.prototype.getName = function(name, type) { - if (type == Blockly.Variables.NAME_TYPE) { - var varName = this.getNameForUserVariable_(name); - if (varName) { - name = varName; - } - } - var normalized = name.toLowerCase() + '_' + type; - - var isVarType = type == Blockly.Variables.NAME_TYPE || - type == Blockly.Names.DEVELOPER_VARIABLE_TYPE; - - var prefix = isVarType ? this.variablePrefix_ : ''; - if (normalized in this.db_) { - return prefix + this.db_[normalized]; - } - var safeName = this.getDistinctName(name, type); - this.db_[normalized] = safeName.substr(prefix.length); - return safeName; -}; - -/** - * 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} type The type of entity in Blockly - * ('VARIABLE', 'PROCEDURE', 'BUILTIN', etc...). - * @return {string} An entity name that is legal in the exported language. - */ -Blockly.Names.prototype.getDistinctName = function(name, type) { - var safeName = this.safeName_(name); - var i = ''; - while (this.dbReverse_[safeName + i] || - (safeName + i) in this.reservedDict_) { - // Collision with existing name. Create a unique name. - i = i ? i + 1 : 2; - } - safeName += i; - this.dbReverse_[safeName] = true; - var isVarType = type == Blockly.Variables.NAME_TYPE || - type == Blockly.Names.DEVELOPER_VARIABLE_TYPE; - var prefix = isVarType ? this.variablePrefix_ : ''; - return prefix + safeName; -}; - -/** - * 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 - * variables. - * @param {string} name Potentially illegal entity name. - * @return {string} Safe entity name. - * @private - */ -Blockly.Names.prototype.safeName_ = function(name) { - if (!name) { - name = 'unnamed'; - } else { - // Unfortunately names in non-latin characters will look like - // _E9_9F_B3_E4_B9_90 which is pretty meaningless. - // https://github.com/google/blockly/issues/1654 - name = encodeURI(name.replace(/ /g, '_')).replace(/[^\w]/g, '_'); - // Most languages don't allow names with leading numbers. - if ('0123456789'.indexOf(name[0]) != -1) { - name = 'my_' + name; - } - } - return name; -}; - -/** - * 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. - */ -Blockly.Names.equals = function(name1, name2) { - return name1.toLowerCase() == name2.toLowerCase(); -}; diff --git a/core/options.js b/core/options.js deleted file mode 100644 index 6e3b79e47a..0000000000 --- a/core/options.js +++ /dev/null @@ -1,244 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Object that controls settings for the workspace. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.Options'); -goog.require('Blockly.Colours'); - - -/** - * Parse the user-specified options, using reasonable defaults where behaviour - * is unspecified. - * @param {!Object} options Dictionary of options. Specification: - * https://developers.google.com/blockly/guides/get-started/web#configuration - * @constructor - */ -Blockly.Options = function(options) { - var readOnly = !!options['readOnly']; - if (readOnly) { - var languageTree = null; - var hasCategories = false; - var hasTrashcan = false; - var hasCollapse = false; - var hasComments = false; - var hasDisable = false; - var hasSounds = false; - } else { - if (!options['toolbox'] && Blockly.Blocks.defaultToolbox) { - var oParser = new DOMParser(); - var dom = oParser.parseFromString(Blockly.Blocks.defaultToolbox, 'text/xml'); - options['toolbox'] = dom.documentElement; - } - var languageTree = Blockly.Options.parseToolboxTree(options['toolbox']); - var hasCategories = Boolean(languageTree && - languageTree.getElementsByTagName('category').length); - var hasTrashcan = options['trashcan']; - if (hasTrashcan === undefined) { - hasTrashcan = false; - } - var hasCollapse = options['collapse']; - if (hasCollapse === undefined) { - hasCollapse = hasCategories; - } - var hasComments = options['comments']; - if (hasComments === undefined) { - hasComments = hasCategories; - } - var hasDisable = options['disable']; - if (hasDisable === undefined) { - hasDisable = hasCategories; - } - var hasSounds = options['sounds']; - if (hasSounds === undefined) { - hasSounds = true; - } - } - var rtl = !!options['rtl']; - var horizontalLayout = options['horizontalLayout']; - if (horizontalLayout === undefined) { - horizontalLayout = false; - } - var toolboxAtStart = options['toolboxPosition']; - if (toolboxAtStart === 'end') { - toolboxAtStart = false; - } else { - toolboxAtStart = true; - } - - if (horizontalLayout) { - var toolboxPosition = toolboxAtStart ? - Blockly.TOOLBOX_AT_TOP : Blockly.TOOLBOX_AT_BOTTOM; - } else { - var toolboxPosition = (toolboxAtStart == rtl) ? - Blockly.TOOLBOX_AT_RIGHT : Blockly.TOOLBOX_AT_LEFT; - } - - var hasScrollbars = options['scrollbars']; - if (hasScrollbars === undefined) { - hasScrollbars = hasCategories; - } - var hasCss = options['css']; - if (hasCss === undefined) { - hasCss = true; - } - var pathToMedia = 'https://blockly-demo.appspot.com/static/media/'; - if (options['media']) { - pathToMedia = options['media']; - } else if (options['path']) { - // 'path' is a deprecated option which has been replaced by 'media'. - pathToMedia = options['path'] + 'media/'; - } - if (options['oneBasedIndex'] === undefined) { - var oneBasedIndex = true; - } else { - var oneBasedIndex = !!options['oneBasedIndex']; - } - - Blockly.Colours.overrideColours(options['colours']); - - this.RTL = rtl; - this.oneBasedIndex = oneBasedIndex; - this.collapse = hasCollapse; - this.comments = hasComments; - this.disable = hasDisable; - this.readOnly = readOnly; - this.pathToMedia = pathToMedia; - this.hasCategories = hasCategories; - this.hasScrollbars = hasScrollbars; - this.hasTrashcan = hasTrashcan; - this.hasSounds = hasSounds; - this.hasCss = hasCss; - this.horizontalLayout = horizontalLayout; - this.languageTree = languageTree; - this.gridOptions = Blockly.Options.parseGridOptions_(options); - this.zoomOptions = Blockly.Options.parseZoomOptions_(options); - this.toolboxPosition = toolboxPosition; -}; - -/** - * The parent of the current workspace, or null if there is no parent workspace. - * @type {Blockly.Workspace} - **/ -Blockly.Options.prototype.parentWorkspace = null; - -/** - * If set, sets the translation of the workspace to match the scrollbars. - */ -Blockly.Options.prototype.setMetrics = null; - -/** - * Return an object with the metrics required to size the workspace. - * @return {Object} Contains size and position metrics, or null. - */ -Blockly.Options.prototype.getMetrics = null; - -/** - * 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 {!Object} A dictionary of normalized options. - * @private - */ -Blockly.Options.parseZoomOptions_ = function(options) { - var zoom = options['zoom'] || {}; - var zoomOptions = {}; - if (zoom['controls'] === undefined) { - zoomOptions.controls = false; - } else { - zoomOptions.controls = !!zoom['controls']; - } - if (zoom['wheel'] === undefined) { - zoomOptions.wheel = false; - } else { - zoomOptions.wheel = !!zoom['wheel']; - } - if (zoom['startScale'] === undefined) { - zoomOptions.startScale = 1; - } else { - zoomOptions.startScale = parseFloat(zoom['startScale']); - } - if (zoom['maxScale'] === undefined) { - zoomOptions.maxScale = 3; - } else { - zoomOptions.maxScale = parseFloat(zoom['maxScale']); - } - if (zoom['minScale'] === undefined) { - zoomOptions.minScale = 0.3; - } else { - zoomOptions.minScale = parseFloat(zoom['minScale']); - } - if (zoom['scaleSpeed'] === undefined) { - zoomOptions.scaleSpeed = 1.2; - } else { - zoomOptions.scaleSpeed = parseFloat(zoom['scaleSpeed']); - } - return zoomOptions; -}; - -/** - * 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 {!Object} A dictionary of normalized options. - * @private - */ -Blockly.Options.parseGridOptions_ = function(options) { - var grid = options['grid'] || {}; - var gridOptions = {}; - gridOptions.spacing = parseFloat(grid['spacing']) || 0; - gridOptions.colour = grid['colour'] || '#888'; - gridOptions.length = parseFloat(grid['length']) || 1; - gridOptions.snap = gridOptions.spacing > 0 && !!grid['snap']; - return gridOptions; -}; - -/** - * Parse the provided toolbox tree into a consistent DOM format. - * @param {Node|string} tree DOM tree of blocks, or text representation of same. - * @return {Node} DOM tree of blocks, or null. - */ -Blockly.Options.parseToolboxTree = function(tree) { - if (tree) { - if (typeof tree != 'string') { - if (typeof XSLTProcessor == 'undefined' && tree.outerHTML) { - // In this case the tree will not have been properly built by the - // browser. The HTML will be contained in the element, but it will - // not have the proper DOM structure since the browser doesn't support - // XSLTProcessor (XML -> HTML). This is the case in IE 9+. - tree = tree.outerHTML; - } else if (!(tree instanceof Element)) { - tree = null; - } - } - if (typeof tree == 'string') { - tree = Blockly.Xml.textToDom(tree); - } - } else { - tree = null; - } - return tree; -}; diff --git a/core/procedures.js b/core/procedures.js deleted file mode 100644 index 5c4f81ef2e..0000000000 --- a/core/procedures.js +++ /dev/null @@ -1,577 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Utility functions for handling procedures. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -/** - * @name Blockly.Procedures - * @namespace - **/ -goog.provide('Blockly.Procedures'); - -goog.require('Blockly.Blocks'); -goog.require('Blockly.constants'); -goog.require('Blockly.Events.BlockChange'); -goog.require('Blockly.Field'); -goog.require('Blockly.Names'); -goog.require('Blockly.Workspace'); - - -/** - * Constant to separate procedure names from variables and generated functions - * when running generators. - * @deprecated Use Blockly.PROCEDURE_CATEGORY_NAME - */ -Blockly.Procedures.NAME_TYPE = Blockly.PROCEDURE_CATEGORY_NAME; - -/** - * Find all user-created procedure definitions in a workspace. - * @param {!Blockly.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. - */ -Blockly.Procedures.allProcedures = function(root) { - var blocks = root.getAllBlocks(); - var proceduresReturn = []; - var proceduresNoReturn = []; - for (var i = 0; i < blocks.length; i++) { - if (blocks[i].getProcedureDef) { - var tuple = blocks[i].getProcedureDef(); - if (tuple) { - if (tuple[2]) { - proceduresReturn.push(tuple); - } else { - proceduresNoReturn.push(tuple); - } - } - } - } - proceduresNoReturn.sort(Blockly.Procedures.procTupleComparator_); - proceduresReturn.sort(Blockly.Procedures.procTupleComparator_); - return [proceduresNoReturn, proceduresReturn]; -}; - -/** - * Find all user-created procedure definition mutations in a workspace. - * @param {!Blockly.Workspace} root Root workspace. - * @return {!Array.} Array of mutation xml elements. - * @package - */ -Blockly.Procedures.allProcedureMutations = function(root) { - var blocks = root.getAllBlocks(); - var mutations = []; - for (var i = 0; i < blocks.length; i++) { - if (blocks[i].type == Blockly.PROCEDURES_PROTOTYPE_BLOCK_TYPE) { - var mutation = blocks[i].mutationToDom(/* opt_generateShadows */ true); - if (mutation) { - mutations.push(mutation); - } - } - } - return mutations; -}; - -/** - * Sorts an array of procedure definition mutations alphabetically. - * (Does not mutate the given array.) - * @param {!Array.} mutations Array of mutation xml elements. - * @return {!Array.} Sorted array of mutation xml elements. - * @private - */ -Blockly.Procedures.sortProcedureMutations_ = function(mutations) { - var newMutations = mutations.slice(); - - newMutations.sort(function(a, b) { - var procCodeA = a.getAttribute('proccode'); - var procCodeB = b.getAttribute('proccode'); - - return Blockly.scratchBlocksUtils.compareStrings(procCodeA, procCodeB); - }); - - return newMutations; -}; - -/** - * Comparison function for case-insensitive sorting of the first element of - * a tuple. - * @param {!Array} ta First tuple. - * @param {!Array} tb Second tuple. - * @return {number} -1, 0, or 1 to signify greater than, equality, or less than. - * @private - */ -Blockly.Procedures.procTupleComparator_ = function(ta, tb) { - return Blockly.scratchBlocksUtils.compareStrings(ta[0], tb[0]); -}; - -/** - * Ensure two identically-named procedures don't exist. - * @param {string} name Proposed procedure name. - * @param {!Blockly.Block} block Block to disambiguate. - * @return {string} Non-colliding name. - */ -Blockly.Procedures.findLegalName = function(name, block) { - if (block.isInFlyout) { - // Flyouts can have multiple procedures called 'do something'. - return name; - } - while (!Blockly.Procedures.isLegalName_(name, block.workspace, block)) { - // Collision with another procedure. - var r = name.match(/^(.*?)(\d+)$/); - if (!r) { - name += '2'; - } else { - name = r[1] + (parseInt(r[2], 10) + 1); - } - } - return name; -}; - -/** - * Does this procedure have a legal name? Illegal names include names of - * procedures already defined. - * @param {string} name The questionable name. - * @param {!Blockly.Workspace} workspace The workspace to scan for collisions. - * @param {Blockly.Block=} opt_exclude Optional block to exclude from - * comparisons (one doesn't want to collide with oneself). - * @return {boolean} True if the name is legal. - * @private - */ -Blockly.Procedures.isLegalName_ = function(name, workspace, opt_exclude) { - return !Blockly.Procedures.isNameUsed(name, workspace, opt_exclude); -}; - -/** - * Return if the given name is already a procedure name. - * @param {string} name The questionable name. - * @param {!Blockly.Workspace} workspace The workspace to scan for collisions. - * @param {Blockly.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. - */ -Blockly.Procedures.isNameUsed = function(name, workspace, opt_exclude) { - var blocks = workspace.getAllBlocks(); - // Iterate through every block and check the name. - for (var i = 0; i < blocks.length; i++) { - if (blocks[i] == opt_exclude) { - continue; - } - if (blocks[i].getProcedureDef) { - var procName = blocks[i].getProcedureDef(); - if (Blockly.Names.equals(procName[0], name)) { - return false; - } - } - } - return true; -}; - -/** - * Rename a procedure. Called by the editable field. - * @param {string} name The proposed new name. - * @return {string} The accepted name. - * @this {Blockly.Field} - */ -Blockly.Procedures.rename = function(name) { - // Strip leading and trailing whitespace. Beyond this, all names are legal. - name = name.replace(/^[\s\xa0]+|[\s\xa0]+$/g, ''); - - // Ensure two identically-named procedures don't exist. - var legalName = Blockly.Procedures.findLegalName(name, this.sourceBlock_); - var oldName = this.text_; - if (oldName != name && oldName != legalName) { - // Rename any callers. - var blocks = this.sourceBlock_.workspace.getAllBlocks(); - for (var i = 0; i < blocks.length; i++) { - if (blocks[i].renameProcedure) { - blocks[i].renameProcedure(oldName, legalName); - } - } - } - return legalName; -}; - -/** - * Construct the blocks required by the flyout for the procedure category. - * @param {!Blockly.Workspace} workspace The workspace contianing procedures. - * @return {!Array.} Array of XML block elements. - */ -Blockly.Procedures.flyoutCategory = function(workspace) { - var xmlList = []; - - Blockly.Procedures.addCreateButton_(workspace, xmlList); - - // Create call blocks for each procedure defined in the workspace - var mutations = Blockly.Procedures.allProcedureMutations(workspace); - mutations = Blockly.Procedures.sortProcedureMutations_(mutations); - for (var i = 0; i < mutations.length; i++) { - var mutation = mutations[i]; - // - // - // - var block = goog.dom.createDom('block'); - block.setAttribute('type', 'procedures_call'); - block.setAttribute('gap', 16); - block.appendChild(mutation); - xmlList.push(block); - } - return xmlList; -}; - -/** - * Create the "Make a Block..." button. - * @param {!Blockly.Workspace} workspace The workspace contianing procedures. - * @param {!Array.} xmlList Array of XML block elements to add to. - * @private - */ -Blockly.Procedures.addCreateButton_ = function(workspace, xmlList) { - var button = goog.dom.createDom('button'); - var msg = Blockly.Msg.NEW_PROCEDURE; - var callbackKey = 'CREATE_PROCEDURE'; - var callback = function() { - Blockly.Procedures.createProcedureDefCallback_(workspace); - }; - button.setAttribute('text', msg); - button.setAttribute('callbackKey', callbackKey); - workspace.registerButtonCallback(callbackKey, callback); - xmlList.push(button); -}; - -/** - * Find all callers of a named procedure. - * @param {string} name Name of procedure (procCode in scratch-blocks). - * @param {!Blockly.Workspace} ws The workspace to find callers in. - * @param {!Blockly.Block} definitionRoot The root of the stack where the - * procedure is defined. - * @param {boolean} allowRecursive True if the search should include recursive - * procedure calls. False if the search should ignore the stack starting - * with definitionRoot. - * @return {!Array.} Array of caller blocks. - * @package - */ -Blockly.Procedures.getCallers = function(name, ws, definitionRoot, - allowRecursive) { - var allBlocks = []; - var topBlocks = ws.getTopBlocks(); - - // Start by deciding which stacks to investigate. - for (var i = 0; i < topBlocks.length; i++) { - var block = topBlocks[i]; - if (block.id == definitionRoot.id && !allowRecursive) { - continue; - } - allBlocks.push.apply(allBlocks, block.getDescendants(false)); - } - - var callers = []; - for (var i = 0; i < allBlocks.length; i++) { - var block = allBlocks[i]; - if (block.type == Blockly.PROCEDURES_CALL_BLOCK_TYPE ) { - var procCode = block.getProcCode(); - if (procCode && procCode == name) { - callers.push(block); - } - } - } - return callers; -}; - -/** - * Find and edit all callers with a procCode using a new mutation. - * @param {string} name Name of procedure (procCode in scratch-blocks). - * @param {!Blockly.Workspace} ws The workspace to find callers in. - * @param {!Element} mutation New mutation for the callers. - * @package - */ -Blockly.Procedures.mutateCallersAndPrototype = function(name, ws, mutation) { - var defineBlock = Blockly.Procedures.getDefineBlock(name, ws); - var prototypeBlock = Blockly.Procedures.getPrototypeBlock(name, ws); - if (defineBlock && prototypeBlock) { - var callers = Blockly.Procedures.getCallers(name, - defineBlock.workspace, defineBlock, true /* allowRecursive */); - callers.push(prototypeBlock); - Blockly.Events.setGroup(true); - for (var i = 0, caller; caller = callers[i]; i++) { - var oldMutationDom = caller.mutationToDom(); - var oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom); - caller.domToMutation(mutation); - var newMutationDom = caller.mutationToDom(); - var newMutation = newMutationDom && Blockly.Xml.domToText(newMutationDom); - if (oldMutation != newMutation) { - Blockly.Events.fire(new Blockly.Events.BlockChange( - caller, 'mutation', null, oldMutation, newMutation)); - } - } - Blockly.Events.setGroup(false); - } else { - alert('No define block on workspace'); // TODO decide what to do about this. - } -}; - -/** - * Find the definition block for the named procedure. - * @param {string} procCode The identifier of the procedure. - * @param {!Blockly.Workspace} workspace The workspace to search. - * @return {Blockly.Block} The procedure definition block, or null not found. - * @package - */ -Blockly.Procedures.getDefineBlock = function(procCode, workspace) { - // Assume that a procedure definition is a top block. - var blocks = workspace.getTopBlocks(false); - for (var i = 0; i < blocks.length; i++) { - if (blocks[i].type == Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE) { - var prototypeBlock = blocks[i].getInput('custom_block').connection.targetBlock(); - if (prototypeBlock.getProcCode && prototypeBlock.getProcCode() == procCode) { - return blocks[i]; - } - } - } - return null; -}; - -/** - * Find the prototype block for the named procedure. - * @param {string} procCode The identifier of the procedure. - * @param {!Blockly.Workspace} workspace The workspace to search. - * @return {Blockly.Block} The procedure prototype block, or null not found. - * @package - */ -Blockly.Procedures.getPrototypeBlock = function(procCode, workspace) { - var defineBlock = Blockly.Procedures.getDefineBlock(procCode, workspace); - if (defineBlock) { - return defineBlock.getInput('custom_block').connection.targetBlock(); - } - return null; -}; - -/** - * Create a mutation for a brand new custom procedure. - * @return {Element} The mutation for a new custom procedure - * @package - */ -Blockly.Procedures.newProcedureMutation = function() { - var mutationText = '' + - '' + - '' + - ''; - return Blockly.Xml.textToDom(mutationText).firstChild; -}; - -/** - * Callback to create a new procedure custom command block. - * @param {!Blockly.Workspace} workspace The workspace to create the new procedure on. - * @private - */ -Blockly.Procedures.createProcedureDefCallback_ = function(workspace) { - Blockly.Procedures.externalProcedureDefCallback( - Blockly.Procedures.newProcedureMutation(), - Blockly.Procedures.createProcedureCallbackFactory_(workspace) - ); -}; - -/** - * Callback factory for adding a new custom procedure from a mutation. - * @param {!Blockly.Workspace} workspace The workspace to create the new procedure on. - * @return {function(?Element)} callback for creating the new custom procedure. - * @private - */ -Blockly.Procedures.createProcedureCallbackFactory_ = function(workspace) { - return function(mutation) { - if (mutation) { - var blockText = '' + - '' + - '' + - '' + - Blockly.Xml.domToText(mutation) + - '' + - '' + - '' + - ''; - var blockDom = Blockly.Xml.textToDom(blockText).firstChild; - Blockly.Events.setGroup(true); - var block = Blockly.Xml.domToBlock(blockDom, workspace); - var scale = workspace.scale; // To convert from pixel units to workspace units - // Position the block so that it is at the top left of the visible workspace, - // padded from the edge by 30 units. Position in the top right if RTL. - var posX = -workspace.scrollX; - if (workspace.RTL) { - posX += workspace.getMetrics().contentWidth - 30; - } else { - posX += 30; - } - block.moveBy(posX / scale, (-workspace.scrollY + 30) / scale); - block.scheduleSnapAndBump(); - Blockly.Events.setGroup(false); - } - }; -}; - -/** - * Callback to open the modal for editing custom procedures. - * @param {!Blockly.Block} block The block that was right-clicked. - * @private - */ -Blockly.Procedures.editProcedureCallback_ = function(block) { - // Edit can come from one of three block types (call, define, prototype) - // Normalize by setting the block to the prototype block for the procedure. - if (block.type == Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE) { - var input = block.getInput('custom_block'); - if (!input) { - alert('Bad input'); // TODO: Decide what to do about this. - return; - } - var conn = input.connection; - if (!conn) { - alert('Bad connection'); // TODO: Decide what to do about this. - return; - } - var innerBlock = conn.targetBlock(); - if (!innerBlock || - !innerBlock.type == Blockly.PROCEDURES_PROTOTYPE_BLOCK_TYPE) { - alert('Bad inner block'); // TODO: Decide what to do about this. - return; - } - block = innerBlock; - } else if (block.type == Blockly.PROCEDURES_CALL_BLOCK_TYPE) { - // This is a call block, find the prototype corresponding to the procCode. - // Make sure to search the correct workspace, call block can be in flyout. - var workspaceToSearch = block.workspace.isFlyout ? - block.workspace.targetWorkspace : block.workspace; - block = Blockly.Procedures.getPrototypeBlock( - block.getProcCode(), workspaceToSearch); - } - // Block now refers to the procedure prototype block, it is safe to proceed. - Blockly.Procedures.externalProcedureDefCallback( - block.mutationToDom(), - Blockly.Procedures.editProcedureCallbackFactory_(block) - ); -}; - -/** - * Callback factory for editing an existing custom procedure. - * @param {!Blockly.Block} block The procedure prototype block being edited. - * @return {function(?Element)} Callback for editing the custom procedure. - * @private - */ -Blockly.Procedures.editProcedureCallbackFactory_ = function(block) { - return function(mutation) { - if (mutation) { - Blockly.Procedures.mutateCallersAndPrototype(block.getProcCode(), - block.workspace, mutation); - } - }; -}; - -/** - * Callback to create a new procedure custom command block. - * @public - */ -Blockly.Procedures.externalProcedureDefCallback = function(/** mutator, callback */) { - alert('External procedure editor must be override Blockly.Procedures.externalProcedureDefCallback'); -}; - -/** - * Make a context menu option for editing a custom procedure. - * This appears in the context menu for procedure definitions and procedure - * calls. - * @param {!Blockly.BlockSvg} block The block where the right-click originated. - * @return {!Object} A menu option, containing text, enabled, and a callback. - * @package - */ -Blockly.Procedures.makeEditOption = function(block) { - var editOption = { - enabled: true, - text: Blockly.Msg.EDIT_PROCEDURE, - callback: function() { - Blockly.Procedures.editProcedureCallback_(block); - } - }; - return editOption; -}; - -/** - * Callback to show the procedure definition corresponding to a custom command - * block. - * TODO(#1136): Implement. - * @param {!Blockly.Block} block The block that was right-clicked. - * @private - */ -Blockly.Procedures.showProcedureDefCallback_ = function(block) { - alert('TODO(#1136): implement showing procedure definition (procCode was "' + - block.procCode_ + '")'); -}; - -/** - * Make a context menu option for showing the definition for a custom procedure, - * based on a right-click on a custom command block. - * @param {!Blockly.BlockSvg} block The block where the right-click originated. - * @return {!Object} A menu option, containing text, enabled, and a callback. - * @package - */ -Blockly.Procedures.makeShowDefinitionOption = function(block) { - var option = { - enabled: true, - text: Blockly.Msg.SHOW_PROCEDURE_DEFINITION, - callback: function() { - Blockly.Procedures.showProcedureDefCallback_(block); - } - }; - return option; -}; - -/** - * Callback to try to delete a custom block definitions. - * @param {string} procCode The identifier of the procedure to delete. - * @param {!Blockly.Block} definitionRoot The root block of the stack that - * defines the custom procedure. - * @return {boolean} True if the custom procedure was deleted, false otherwise. - * @package - */ -Blockly.Procedures.deleteProcedureDefCallback = function(procCode, - definitionRoot) { - var callers = Blockly.Procedures.getCallers(procCode, - definitionRoot.workspace, definitionRoot, false /* allowRecursive */); - if (callers.length > 0) { - return false; - } - - var workspace = definitionRoot.workspace; - - // Delete the whole stack. - Blockly.Events.setGroup(true); - definitionRoot.dispose(); - Blockly.Events.setGroup(false); - - // TODO (#1354) Update this function when '_' is removed - // Refresh toolbox, so caller doesn't appear there anymore - workspace.refreshToolboxSelection_(); - - return true; -}; diff --git a/core/rendered_connection.js b/core/rendered_connection.js deleted file mode 100644 index 2d56842a51..0000000000 --- a/core/rendered_connection.js +++ /dev/null @@ -1,417 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Components for creating connections between blocks. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.RenderedConnection'); - -goog.require('Blockly.Connection'); - - -/** - * Class for a connection between blocks that may be rendered on screen. - * @param {!Blockly.Block} source The block establishing this connection. - * @param {number} type The type of the connection. - * @extends {Blockly.Connection} - * @constructor - */ -Blockly.RenderedConnection = function(source, type) { - Blockly.RenderedConnection.superClass_.constructor.call(this, source, type); - - /** - * Workspace units, (0, 0) is top left of block. - * @type {!goog.math.Coordinate} - * @private - */ - this.offsetInBlock_ = new goog.math.Coordinate(0, 0); -}; -goog.inherits(Blockly.RenderedConnection, Blockly.Connection); - -/** - * Returns the distance between this connection and another connection in - * workspace units. - * @param {!Blockly.Connection} otherConnection The other connection to measure - * the distance to. - * @return {number} The distance between connections, in workspace units. - */ -Blockly.RenderedConnection.prototype.distanceFrom = function(otherConnection) { - var xDiff = this.x_ - otherConnection.x_; - var yDiff = this.y_ - otherConnection.y_; - return Math.sqrt(xDiff * xDiff + yDiff * yDiff); -}; - -/** - * Move the block(s) belonging to the connection to a point where they don't - * visually interfere with the specified connection. - * @param {!Blockly.Connection} staticConnection The connection to move away - * from. - * @private - */ -Blockly.RenderedConnection.prototype.bumpAwayFrom_ = function(staticConnection) { - if (this.sourceBlock_.workspace.isDragging()) { - // Don't move blocks around while the user is doing the same. - return; - } - // Move the root block. - var rootBlock = this.sourceBlock_.getRootBlock(); - if (rootBlock.isInFlyout) { - // Don't move blocks around in a flyout. - return; - } - var reverse = false; - if (!rootBlock.isMovable()) { - // Can't bump an uneditable block away. - // Check to see if the other block is movable. - rootBlock = staticConnection.getSourceBlock().getRootBlock(); - if (!rootBlock.isMovable()) { - return; - } - // Swap the connections and move the 'static' connection instead. - staticConnection = this; - reverse = true; - } - // Raise it to the top for extra visibility. - var selected = Blockly.selected == rootBlock; - selected || rootBlock.addSelect(); - var dx = (staticConnection.x_ + Blockly.SNAP_RADIUS) - this.x_; - var dy = (staticConnection.y_ + Blockly.SNAP_RADIUS) - this.y_; - if (reverse) { - // When reversing a bump due to an uneditable block, bump up. - dy = -dy; - } - if (rootBlock.RTL) { - dx = -dx; - } - rootBlock.moveBy(dx, dy); - selected || rootBlock.removeSelect(); -}; - -/** - * 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. - */ -Blockly.RenderedConnection.prototype.moveTo = function(x, y) { - // Remove it from its old location in the database (if already present) - if (this.inDB_) { - this.db_.removeConnection_(this); - } - this.x_ = x; - this.y_ = y; - // Insert it into its new location in the database. - if (!this.hidden_) { - this.db_.addConnection(this); - } -}; - -/** - * 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. - */ -Blockly.RenderedConnection.prototype.moveBy = function(dx, dy) { - this.moveTo(this.x_ + dx, this.y_ + dy); -}; - -/** - * Move this connection to the location given by its offset within the block and - * the location of the block's top left corner. - * @param {!goog.math.Coordinate} blockTL The location of the top left corner - * of the block, in workspace coordinates. - */ -Blockly.RenderedConnection.prototype.moveToOffset = function(blockTL) { - this.moveTo(blockTL.x + this.offsetInBlock_.x, - blockTL.y + this.offsetInBlock_.y); -}; - -/** - * 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. - */ -Blockly.RenderedConnection.prototype.setOffsetInBlock = function(x, y) { - this.offsetInBlock_.x = x; - this.offsetInBlock_.y = y; -}; - -/** - * Move the blocks on either side of this connection right next to each other. - * @private - */ -Blockly.RenderedConnection.prototype.tighten_ = function() { - var dx = this.targetConnection.x_ - this.x_; - var dy = this.targetConnection.y_ - this.y_; - if (dx != 0 || dy != 0) { - var block = this.targetBlock(); - var svgRoot = block.getSvgRoot(); - if (!svgRoot) { - throw 'block is not rendered.'; - } - // Workspace coordinates. - var xy = Blockly.utils.getRelativeXY(svgRoot); - block.getSvgRoot().setAttribute('transform', - 'translate(' + (xy.x - dx) + ',' + (xy.y - dy) + ')'); - block.moveConnections_(-dx, -dy); - } -}; - -/** - * Find the closest compatible connection to this connection. - * All parameters are in workspace units. - * @param {number} maxLimit The maximum radius to another connection. - * @param {!goog.math.Coordinate} dxy Offset between this connection's location - * in the database and the current location (as a result of dragging). - * @return {!{connection: ?Blockly.Connection, radius: number}} Contains two - * properties: 'connection' which is either another connection or null, - * and 'radius' which is the distance. - */ -Blockly.RenderedConnection.prototype.closest = function(maxLimit, dxy) { - return this.dbOpposite_.searchForClosest(this, maxLimit, dxy); -}; - -/** - * Add highlighting around this connection. - */ -Blockly.RenderedConnection.prototype.highlight = function() { - var steps; - steps = 'm -20,0 h 5 ' + Blockly.BlockSvg.NOTCH_PATH_LEFT + ' h 5'; - var xy = this.sourceBlock_.getRelativeToSurfaceXY(); - var x = this.x_ - xy.x; - var y = this.y_ - xy.y; - Blockly.Connection.highlightedPath_ = Blockly.utils.createSvgElement( - 'path', - { - 'class': 'blocklyHighlightedConnectionPath', - 'd': steps, - transform: 'translate(' + x + ',' + y + ')' + - (this.sourceBlock_.RTL ? ' scale(-1 1)' : '') - }, - this.sourceBlock_.getSvgRoot()); -}; - -/** - * Unhide this connection, as well as all down-stream connections on any block - * attached to this connection. This happens when a block is expanded. - * Also unhides down-stream comments. - * @return {!Array.} List of blocks to render. - */ -Blockly.RenderedConnection.prototype.unhideAll = function() { - this.setHidden(false); - // All blocks that need unhiding must be unhidden before any rendering takes - // place, since rendering requires knowing the dimensions of lower blocks. - // Also, since rendering a block renders all its parents, we only need to - // render the leaf nodes. - var renderList = []; - if (this.type != Blockly.INPUT_VALUE && this.type != Blockly.NEXT_STATEMENT) { - // Only spider down. - return renderList; - } - var block = this.targetBlock(); - if (block) { - var connections; - if (block.isCollapsed()) { - // This block should only be partially revealed since it is collapsed. - connections = []; - block.outputConnection && connections.push(block.outputConnection); - block.nextConnection && connections.push(block.nextConnection); - block.previousConnection && connections.push(block.previousConnection); - } else { - // Show all connections of this block. - connections = block.getConnections_(true); - } - for (var i = 0; i < connections.length; i++) { - renderList.push.apply(renderList, connections[i].unhideAll()); - } - if (!renderList.length) { - // Leaf block. - renderList[0] = block; - } - } - return renderList; -}; - -/** - * Remove the highlighting around this connection. - */ -Blockly.RenderedConnection.prototype.unhighlight = function() { - goog.dom.removeNode(Blockly.Connection.highlightedPath_); - delete Blockly.Connection.highlightedPath_; -}; - -/** - * Set whether this connections is hidden (not tracked in a database) or not. - * @param {boolean} hidden True if connection is hidden. - */ -Blockly.RenderedConnection.prototype.setHidden = function(hidden) { - this.hidden_ = hidden; - if (hidden && this.inDB_) { - this.db_.removeConnection_(this); - } else if (!hidden && !this.inDB_) { - this.db_.addConnection(this); - } -}; - -/** - * Hide this connection, as well as all down-stream connections on any block - * attached to this connection. This happens when a block is collapsed. - * Also hides down-stream comments. - */ -Blockly.RenderedConnection.prototype.hideAll = function() { - this.setHidden(true); - if (this.targetConnection) { - var blocks = this.targetBlock().getDescendants(false); - for (var i = 0; i < blocks.length; i++) { - var block = blocks[i]; - // Hide all connections of all children. - var connections = block.getConnections_(true); - for (var j = 0; j < connections.length; j++) { - connections[j].setHidden(true); - } - // Close all bubbles of all children. - var icons = block.getIcons(); - for (var j = 0; j < icons.length; j++) { - icons[j].setVisible(false); - } - } - } -}; - -/** - * Check if the two connections can be dragged to connect to each other. - * @param {!Blockly.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. - */ -Blockly.RenderedConnection.prototype.isConnectionAllowed = function(candidate, - maxRadius) { - if (this.distanceFrom(candidate) > maxRadius) { - return false; - } - - return Blockly.RenderedConnection.superClass_.isConnectionAllowed.call(this, - candidate); -}; - -/** - * Disconnect two blocks that are connected by this connection. - * @param {!Blockly.Block} parentBlock The superior block. - * @param {!Blockly.Block} childBlock The inferior block. - * @private - */ -Blockly.RenderedConnection.prototype.disconnectInternal_ = function(parentBlock, - childBlock) { - Blockly.RenderedConnection.superClass_.disconnectInternal_.call(this, - parentBlock, childBlock); - // Rerender the parent so that it may reflow. - if (parentBlock.rendered) { - parentBlock.render(); - } - if (childBlock.rendered) { - childBlock.updateDisabled(); - childBlock.render(); - } -}; - -/** - * Respawn the shadow block if there was one connected to the this connection. - * Render/rerender blocks as needed. - * @private - */ -Blockly.RenderedConnection.prototype.respawnShadow_ = function() { - var parentBlock = this.getSourceBlock(); - // Respawn the shadow block if there is one. - var shadow = this.getShadowDom(); - if (parentBlock.workspace && shadow && Blockly.Events.recordUndo) { - Blockly.RenderedConnection.superClass_.respawnShadow_.call(this); - var blockShadow = this.targetBlock(); - if (!blockShadow) { - throw 'Couldn\'t respawn the shadow block that should exist here.'; - } - blockShadow.initSvg(); - blockShadow.render(false); - if (parentBlock.rendered) { - parentBlock.render(); - } - } -}; - -/** - * 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. - * @private - */ -Blockly.RenderedConnection.prototype.neighbours_ = function(maxLimit) { - return this.dbOpposite_.getNeighbours(this, maxLimit); -}; - -/** - * Connect two connections together. This is the connection on the superior - * block. Rerender blocks as needed. - * @param {!Blockly.Connection} childConnection Connection on inferior block. - * @private - */ -Blockly.RenderedConnection.prototype.connect_ = function(childConnection) { - Blockly.RenderedConnection.superClass_.connect_.call(this, childConnection); - - var parentConnection = this; - var parentBlock = parentConnection.getSourceBlock(); - var childBlock = childConnection.getSourceBlock(); - - if (parentBlock.rendered) { - parentBlock.updateDisabled(); - } - if (childBlock.rendered) { - childBlock.updateDisabled(); - } - if (parentBlock.rendered && childBlock.rendered) { - if (parentConnection.type == Blockly.NEXT_STATEMENT || - parentConnection.type == Blockly.PREVIOUS_STATEMENT) { - // Child block may need to square off its corners if it is in a stack. - // Rendering a child will render its parent. - childBlock.render(); - } else { - // Child block does not change shape. Rendering the parent node will - // move its connected children into position. - parentBlock.render(); - } - } -}; - -/** - * Function to be called when this connection's compatible types have changed. - * @private - */ -Blockly.RenderedConnection.prototype.onCheckChanged_ = function() { - // The new value type may not be compatible with the existing connection. - if (this.isConnected() && !this.checkType_(this.targetConnection)) { - var child = this.isSuperior() ? this.targetBlock() : this.sourceBlock_; - child.unplug(); - // Bump away. - this.sourceBlock_.bumpNeighbours_(); - } -}; diff --git a/core/scratch_block_comment.js b/core/scratch_block_comment.js deleted file mode 100644 index bb60524e6d..0000000000 --- a/core/scratch_block_comment.js +++ /dev/null @@ -1,653 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2018 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Object representing a code comment. - * @author kchadha@scratch.mit.edu (Karishma Chadha) - */ -'use strict'; - -goog.provide('Blockly.ScratchBlockComment'); - -goog.require('Blockly.Comment'); -goog.require('Blockly.Events.BlockChange'); -goog.require('Blockly.Events.Ui'); -goog.require('Blockly.Icon'); -goog.require('Blockly.ScratchBubble'); - -goog.require('goog.math.Coordinate'); -goog.require('goog.userAgent'); - - -/** - * Class for a comment. - * @param {!Blockly.Block} block The block associated with this comment. - * @param {string} text The text content of this comment. - * @param {string=} id Optional uid for comment; a new one will be generated if - * not provided. - * @param {number=} x Initial x position for comment, in workspace coordinates. - * @param {number=} y Initial y position for comment, in workspace coordinates. - * @param {boolean=} minimized Whether or not this comment is minimized - * (only the top bar displays), defaults to false. - * @extends {Blockly.Comment} - * @constructor - */ -Blockly.ScratchBlockComment = function(block, text, id, x, y, minimized) { - Blockly.ScratchBlockComment.superClass_.constructor.call(this, block); - /** - * The text content of this comment. - * @type {string} - * @private - */ - this.text_ = text; - - var xIsValidNumber = typeof x == 'number' && !isNaN(x); - var yIsValidNumber = typeof y == 'number' && !isNaN(y); - - /** - * Whether this comment needs to be auto-positioned (based on provided values - * for x and y position). - * @type {boolean} - * @private - */ - this.needsAutoPositioning_ = !xIsValidNumber && !yIsValidNumber; - // If both of the given x and y params are invalid, this comment needs to be auto positioned. - - /** - * The x position of this comment in workspace coordinates. Default to 0 if - * x position is not provided or is not a valid number. - * @type {number} - * @private - */ - this.x_ = xIsValidNumber ? x : 0; - /** - * The y position of this comment in workspace coordinates. Default to 0 if - * y position is not provided or is not a valid number. - * @type {number} - * @private - */ - this.y_ = yIsValidNumber ? y : 0; - /** - * Whether this comment is minimized. - * @type {boolean} - * @private - */ - this.isMinimized_ = minimized || false; - - /** - * The workspace this comment belongs to. - * @type {Blockly.Workspace} - * @package - */ - this.workspace = block.workspace; - /** - * The unique identifier for this comment. - * @type {string} - * @package - */ - this.id = goog.isString(id) && !this.workspace.getCommentById(id) ? - id : Blockly.utils.genUid(); - this.workspace.addTopComment(this); - - /** - * The id of the block this comment belongs to. - * @type {string} - * @package - */ - this.blockId = block.id; - - if (!block.rendered) { - Blockly.ScratchBlockComment.fireCreateEvent(this); - } - // If the block is rendered, fire event the create event when the comment is made - // visible -}; -goog.inherits(Blockly.ScratchBlockComment, Blockly.Comment); - -/** - * Width of bubble. - * @private - */ -Blockly.ScratchBlockComment.prototype.width_ = 200; - -/** - * Height of bubble. - * @private - */ -Blockly.ScratchBlockComment.prototype.height_ = 200; - -/** - * Comment Icon Size. - * @package - */ -Blockly.ScratchBlockComment.prototype.SIZE = 0; - -/** - * Offset for text area in comment bubble. - * @private - */ -Blockly.ScratchBlockComment.TEXTAREA_OFFSET = 12; - -/** - * Maximum lable length (actual label length will include - * one additional character, the ellipsis). - * @private - */ -Blockly.ScratchBlockComment.MAX_LABEL_LENGTH = 12; - -/** - * Maximum character length for comment text. - * @private - */ -Blockly.ScratchBlockComment.COMMENT_TEXT_LIMIT = 8000; - -/** - * Width that a minimized comment should have. - * @private - */ -Blockly.ScratchBlockComment.MINIMIZE_WIDTH = 200; - -/** - * Draw the comment icon. - * @param {!Element} _group The icon group. - * @private - */ -Blockly.ScratchBlockComment.prototype.drawIcon_ = function(_group) { - // NO-OP -- Don't render a comment icon for Scratch block comments -}; - -// Override renderIcon from Blocky.Icon so that the comment bubble is -// anchored correctly on the block. This function takes in the top margin -// as an input instead of setting an arbitrary one. -/** - * Render the icon. - * @param {number} cursorX Horizontal offset at which to position the icon. - * @param {number} topMargin Vertical offset from the top of the block to position the icon. - * @return {number} Horizontal offset for next item to draw. - * @package - */ -Blockly.ScratchBlockComment.prototype.renderIcon = function(cursorX, topMargin) { - if (this.collapseHidden && this.block_.isCollapsed()) { - this.iconGroup_.setAttribute('display', 'none'); - return cursorX; - } - this.iconGroup_.setAttribute('display', 'block'); - - var width = this.SIZE; - if (this.block_.RTL) { - cursorX -= width; - } - this.iconGroup_.setAttribute('transform', - 'translate(' + cursorX + ',' + topMargin + ')'); - this.computeIconLocation(); - if (this.block_.RTL) { - cursorX -= Blockly.BlockSvg.SEP_SPACE_X; - } else { - cursorX += width + Blockly.BlockSvg.SEP_SPACE_X; - } - return cursorX; -}; - -/** - * Create the editor for the comment's bubble. - * @return {{commentEditor: !Element, labelText: !string}} The components used - * to render the comment editing/writing area and the truncated label text - * to display in the minimized comment top bar. - * @private - */ -Blockly.ScratchBlockComment.prototype.createEditor_ = function() { - this.foreignObject_ = Blockly.utils.createSvgElement('foreignObject', - { - 'x': Blockly.ScratchBubble.BORDER_WIDTH, - 'y': Blockly.ScratchBubble.BORDER_WIDTH + Blockly.ScratchBubble.TOP_BAR_HEIGHT, - 'class': 'scratchCommentForeignObject' - }, - null); - var body = document.createElementNS(Blockly.HTML_NS, 'body'); - body.setAttribute('xmlns', Blockly.HTML_NS); - body.className = 'blocklyMinimalBody scratchCommentBody'; - var textarea = document.createElementNS(Blockly.HTML_NS, 'textarea'); - textarea.className = 'scratchCommentTextarea scratchCommentText'; - textarea.setAttribute('dir', this.block_.RTL ? 'RTL' : 'LTR'); - textarea.setAttribute('maxlength', Blockly.ScratchBlockComment.COMMENT_TEXT_LIMIT); - textarea.setAttribute('placeholder', Blockly.Msg.WORKSPACE_COMMENT_DEFAULT_TEXT); - body.appendChild(textarea); - this.textarea_ = textarea; - this.textarea_.style.margin = (Blockly.ScratchBlockComment.TEXTAREA_OFFSET) + 'px'; - this.foreignObject_.appendChild(body); - Blockly.bindEventWithChecks_(textarea, 'mousedown', this, - this.textareaFocus_, true, true); // noCapture and do not prevent default - // Don't zoom with mousewheel. - Blockly.bindEventWithChecks_(textarea, 'wheel', this, function(e) { - e.stopPropagation(); - }); - Blockly.bindEventWithChecks_(textarea, 'change', this, function(_e) { - if (this.text_ != textarea.value) { - Blockly.Events.fire(new Blockly.Events.CommentChange( - this, {text: this.text_}, {text: textarea.value})); - this.text_ = textarea.value; - } - }); - - // Label for comment top bar when comment is minimized - this.label_ = this.getLabelText(); - - return { - commentEditor: this.foreignObject_, - labelText: this.label_ - }; -}; - -/** - * Handle text area click, make sure to stop propagation to allow default selection behavior. - * @param {!Event} e Mouse up event. - * @private - */ -Blockly.ScratchBlockComment.prototype.textareaFocus_ = function(e) { - Blockly.ScratchBlockComment.superClass_.textareaFocus_.call(this, e); - // Stop event from propagating to the workspace to make sure preventDefault _is not called_. - e.stopPropagation(); -}; - - -/** - * Callback function triggered when the bubble has resized. - * Resize the text area accordingly. - * @private - */ -Blockly.ScratchBlockComment.prototype.resizeBubble_ = function() { - if (this.isVisible() && !this.isMinimized_) { - var size = this.bubble_.getBubbleSize(); - var doubleBorderWidth = 2 * Blockly.ScratchBubble.BORDER_WIDTH; - var textOffset = Blockly.ScratchBlockComment.TEXTAREA_OFFSET * 2; - this.foreignObject_.setAttribute('width', size.width - doubleBorderWidth); - this.foreignObject_.setAttribute('height', size.height - doubleBorderWidth - Blockly.ScratchBubble.TOP_BAR_HEIGHT); - this.textarea_.style.width = (size.width - textOffset) + 'px'; - this.textarea_.style.height = (size.height - doubleBorderWidth - - Blockly.ScratchBubble.TOP_BAR_HEIGHT - textOffset) + 'px'; - - // Actually set the size! - this.width_ = size.width; - this.height_ = size.height; - } -}; - -/** - * Change the colour of the associated bubble to match its block. - * @package - */ -Blockly.ScratchBlockComment.prototype.updateColour = function() { - if (this.isVisible()) { - this.bubble_.setColour(this.block_.getColourTertiary()); - } -}; - -/** - * Auto position this comment given information about the block that owns this - * comment and the comment state, if this block needs auto positioning. - * @private - */ -Blockly.ScratchBlockComment.prototype.autoPosition_ = function() { - if (!this.needsAutoPositioning_) return; - if (this.isMinimized_) { - var minimizedOffset = 4 * Blockly.BlockSvg.GRID_UNIT; - this.x_ = this.block_.RTL ? - this.iconXY_.x - this.getBubbleSize().width - minimizedOffset : - this.iconXY_.x + minimizedOffset; - this.y_ = this.iconXY_.y - (Blockly.ScratchBubble.TOP_BAR_HEIGHT / 2); - } else { - // Position comment so that the expanded bubble does not overlap - // blocks below it in the stack that are wider than this block - // Overhang is the difference between this blocks trailing edge and - // the largest block below (zero if this block is the widest) - var thisBlockWidth = Math.floor(this.block_.svgPath_.getBBox().width); - var fullStackWidth = Math.floor(this.block_.getHeightWidth().width); - var overhang = fullStackWidth - thisBlockWidth; - var offset = 8 * Blockly.BlockSvg.GRID_UNIT; - this.x_ = this.block_.RTL ? - this.iconXY_.x - this.width_ - overhang - offset : - this.iconXY_.x + overhang + offset; - this.y_ = this.iconXY_.y - (Blockly.ScratchBubble.TOP_BAR_HEIGHT / 2); - } -}; - -/** - * Show or hide the comment bubble. - * @param {boolean} visible True if the bubble should be visible. - * @package - */ -Blockly.ScratchBlockComment.prototype.setVisible = function(visible) { - if (visible == this.isVisible()) { - // No change. - return; - } - if ((!this.block_.isEditable() && !this.textarea_) || goog.userAgent.IE) { - // Steal the code from warnings to make an uneditable text bubble. - // MSIE does not support foreignobject; textareas are impossible. - // http://msdn.microsoft.com/en-us/library/hh834675%28v=vs.85%29.aspx - // Always treat comments in IE as uneditable. - Blockly.Warning.prototype.setVisible.call(this, visible); - return; - } - // Save the bubble stats before the visibility switch. - var text = this.getText(); - var size = this.getBubbleSize(); - if (visible) { - // Auto position this comment, if necessary. - if (this.needsAutoPositioning_) { - this.autoPosition_(); - // This comment has been auto-positioned so reset the flag - this.needsAutoPositioning_ = false; - } - - // Create the bubble. - this.bubble_ = new Blockly.ScratchBubble( - this, /** @type {!Blockly.WorkspaceSvg} */ (this.block_.workspace), - this.createEditor_(), this.iconXY_, this.width_, this.height_, - this.x_, this.y_, this.isMinimized_); - this.bubble_.setAutoLayout(false); - this.bubble_.registerResizeEvent(this.resizeBubble_.bind(this)); - this.bubble_.registerMinimizeToggleEvent(this.toggleMinimize_.bind(this)); - this.bubble_.registerDeleteEvent(this.dispose.bind(this)); - this.bubble_.registerContextMenuCallback(this.showContextMenu_.bind(this)); - this.updateColour(); - } else { - // Dispose of the bubble. - this.bubble_.dispose(); - this.bubble_ = null; - this.textarea_ = null; - this.foreignObject_ = null; - this.label_ = null; - } - // Restore the bubble stats after the visibility switch. - this.setText(text); - this.setBubbleSize(size.width, size.height); - if (visible) { - Blockly.ScratchBlockComment.fireCreateEvent(this); - } -}; - -/** - * Toggle the minimization state of this comment. - * @private - */ -Blockly.ScratchBlockComment.prototype.toggleMinimize_ = function() { - this.setMinimized(!this.isMinimized_); -}; - -/** - * Set the minimized state for this comment. - * @param {boolean} minimize Whether the comment should be minimized - * @package - */ -Blockly.ScratchBlockComment.prototype.setMinimized = function(minimize) { - if (this.isMinimized_ == minimize) { - return; - } - Blockly.Events.fire(new Blockly.Events.CommentChange(this, - {minimized: this.isMinimized_}, {minimized: minimize})); - this.isMinimized_ = minimize; - if (minimize) { - this.bubble_.setMinimized(true, this.getLabelText()); - this.setBubbleSize(Blockly.ScratchBlockComment.MINIMIZE_WIDTH, - Blockly.ScratchBubble.TOP_BAR_HEIGHT); - // Note we are not updating this.width_ or this.height_ here - // because we want to keep track of the width/height of the - // maximized comment - } else { - this.bubble_.setMinimized(false); - this.setText(this.text_); - this.setBubbleSize(this.width_, this.height_); - } -}; - -/** - * Size this comment's bubble. - * @param {number} width Width of the bubble. - * @param {number} height Height of the bubble. - * @package - */ -Blockly.ScratchBlockComment.prototype.setBubbleSize = function(width, height) { - if (this.bubble_) { - if (this.isMinimized_) { - this.bubble_.setBubbleSize(Blockly.ScratchBlockComment.MINIMIZE_WIDTH, - Blockly.ScratchBubble.TOP_BAR_HEIGHT); - } else { - this.bubble_.setBubbleSize(width, height); - } - } -}; - -/** - * Set the un-minimized size of this comment. If the comment has an un-minimized - * bubble, also set the bubble's size. - * @param {number} width Width of the unminimized comment. - * @param {number} height Height of the unminimized comment. - * @package - */ -Blockly.ScratchBlockComment.prototype.setSize = function(width, height) { - var oldWidth = this.width_; - var oldHeight = this.height_; - - if (!this.isMinimized_) { - this.setBubbleSize(width, height); - } - - this.height_ = height; - this.width_ = width; - - if (oldWidth != this.width_ || oldHeight != this.height_) { - Blockly.Events.fire(new Blockly.Events.CommentChange( - this, - {width: oldWidth, height: oldHeight}, - {width: this.width_, height: this.height_})); - } -}; - -/** - * Get the truncated text for this comment to display in the minimized - * top bar. - * @return {string} The truncated comment text - * @package - */ -Blockly.ScratchBlockComment.prototype.getLabelText = function() { - if (this.text_.length > Blockly.ScratchBlockComment.MAX_LABEL_LENGTH) { - if (this.block_.RTL) { - return '\u2026' + this.text_.slice(0, Blockly.ScratchBlockComment.MAX_LABEL_LENGTH); - } - return this.text_.slice(0, Blockly.ScratchBlockComment.MAX_LABEL_LENGTH) + '\u2026'; - } else { - return this.text_; - } -}; - -/** - * Set this comment's text. - * @param {string} text Comment text. - * @package - */ -Blockly.ScratchBlockComment.prototype.setText = function(text) { - if (this.text_ != text) { - Blockly.Events.fire(new Blockly.Events.CommentChange( - this, {text: this.text_}, {text: text})); - this.text_ = text; - } - if (this.textarea_) { - this.textarea_.value = text; - } -}; - -/** - * Move this comment to a position given x and y coordinates. - * @param {number} x The x-coordinate on the workspace. - * @param {number} y The y-coordinate on the workspace. - * @package - */ -Blockly.ScratchBlockComment.prototype.moveTo = function(x, y) { - var event = new Blockly.Events.CommentMove(this); - if (this.bubble_) { - this.bubble_.moveTo(x, y); - } - this.x_ = x; - this.y_ = y; - event.recordNew(); - Blockly.Events.fire(event); -}; - -/** - * Get the x and y position of this comment. - * @return {goog.math.Coordinate} The XY position - * @package - */ -Blockly.ScratchBlockComment.prototype.getXY = function() { - if (this.bubble_) { - return this.bubble_.getRelativeToSurfaceXY(); - } - // Auto position this comment if iconXY_ is provided - // (auto positioning will only occur if it is necessary). - if (this.needsAutoPositioning_ && this.iconXY_) { - this.autoPosition_(); - // Do not reset the needsAutoPositioning flag here. This will be reset - // after the comment has been made visible and the re-auto positioned, - // because the block may have moved by that point. - } - return new goog.math.Coordinate(this.x_, this.y_); -}; - -/** - * Get the height and width of this comment. - * Note: this does not use the current bubble size because - * the bubble may be minimized. - * @return {{height: number, width: number}} The height and width of - * this comment when it is full size. These numbers do not change - * as the workspace zoom changes. - * @package - */ -Blockly.ScratchBlockComment.prototype.getHeightWidth = function() { - return {height: this.height_, width: this.width_}; -}; - -/** - * Returns the coordinates of a bounding box describing the dimensions of this - * comment. - * Coordinate system: workspace coordinates. - * @return {!{topLeft: goog.math.Coordinate, bottomRight: goog.math.Coordinate}} - * Object with top left and bottom right coordinates of the bounding box. - * @package - */ -Blockly.ScratchBlockComment.prototype.getBoundingRectangle = function() { - var commentXY = this.getXY(); - var commentBounds = this.getBubbleSize(); - var topLeft; - var bottomRight; - if (this.workspace.RTL) { - // TODO (#1562) for some reason this doesn't work with workspace scroll in RTL - topLeft = new goog.math.Coordinate(commentXY.x - commentBounds.width, - commentXY.y); - bottomRight = new goog.math.Coordinate(commentXY.x, - commentXY.y + commentBounds.height); - } else { - topLeft = new goog.math.Coordinate(commentXY.x, commentXY.y); - bottomRight = new goog.math.Coordinate(commentXY.x + commentBounds.width, - commentXY.y + commentBounds.height); - } - return {topLeft: topLeft, bottomRight: bottomRight}; -}; - -/** - * Check whether this comment is currently minimized. - * @return {boolean} True if minimized - * @package - */ -Blockly.ScratchBlockComment.prototype.isMinimized = function() { - return this.isMinimized_; -}; - -/** - * Show the context menu for this comment's bubble. - * @param {!Event} e The mouse event - * @private - */ -Blockly.ScratchBlockComment.prototype.showContextMenu_ = function(e) { - var menuOptions = []; - menuOptions.push(Blockly.ContextMenu.commentDeleteOption(this, Blockly.Msg.DELETE)); - Blockly.ContextMenu.show(e, menuOptions, this.block_.RTL); -}; - -/** - * 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 - */ -Blockly.ScratchBlockComment.prototype.toXmlWithXY = function() { - var element = goog.dom.createDom('comment'); - element.setAttribute('id', this.id); - element.textContent = this.text_; - element.setAttribute('x', Math.round( - this.workspace.RTL ? this.workspace.getWidth() - this.x_ : this.x_)); - element.setAttribute('y', Math.round(this.y_)); - element.setAttribute('h', this.height_); - element.setAttribute('w', this.width_); - return element; -}; - -/** - * Fire a create event for the given workspace comment, if comments are enabled. - * @param {!Blockly.WorkspaceComment} comment The comment that was just created. - * @package - */ -Blockly.ScratchBlockComment.fireCreateEvent = function(comment) { - if (Blockly.Events.isEnabled()) { - var existingGroup = Blockly.Events.getGroup(); - if (!existingGroup) { - Blockly.Events.setGroup(true); - } - try { - Blockly.Events.fire(new Blockly.Events.CommentCreate(comment)); - } finally { - if (!existingGroup) { - Blockly.Events.setGroup(false); - } - } - } -}; - -/** - * Dispose of this comment. - */ -Blockly.ScratchBlockComment.prototype.dispose = function() { - if (Blockly.Events.isEnabled()) { - // Emit delete event before disposal begins so that the - // event's reference to this comment contains all the relevant - // information (for undoing this event) - Blockly.Events.fire(new Blockly.Events.CommentDelete(this)); - } - this.block_.comment = null; - this.workspace.removeTopComment(this); - Blockly.Icon.prototype.dispose.call(this); -}; - -/** - * Focus this comments textarea. - */ -Blockly.ScratchBlockComment.prototype.focus = function() { - this.textarea_.focus(); -}; diff --git a/core/scratch_blocks_utils.js b/core/scratch_blocks_utils.js deleted file mode 100644 index 99f492b427..0000000000 --- a/core/scratch_blocks_utils.js +++ /dev/null @@ -1,244 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2018 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Utility methods for Scratch Blocks but not Blockly. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -/** - * @name Blockly.scratchBlocksUtils - * @namespace - **/ -goog.provide('Blockly.scratchBlocksUtils'); - - -/** - * Measure some text using a canvas in-memory. - * Does not exist in Blockly, but needed in scratch-blocks - * @param {string} fontSize E.g., '10pt' - * @param {string} fontFamily E.g., 'Arial' - * @param {string} fontWeight E.g., '600' - * @param {string} text The actual text to measure - * @return {number} Width of the text in px. - * @package - */ -Blockly.scratchBlocksUtils.measureText = function(fontSize, fontFamily, - fontWeight, text) { - var canvas = document.createElement('canvas'); - var context = canvas.getContext('2d'); - context.font = fontWeight + ' ' + fontSize + ' ' + fontFamily; - return context.measureText(text).width; -}; - -/** - * Encode a string's HTML entities. - * E.g., -> <a> - * Does not exist in Blockly, but needed in scratch-blocks - * @param {string} rawStr Unencoded raw string to encode. - * @return {string} String with HTML entities encoded. - * @package - */ -Blockly.scratchBlocksUtils.encodeEntities = function(rawStr) { - // CC-BY-SA https://stackoverflow.com/questions/18749591/encode-html-entities-in-javascript - return rawStr.replace(/[\u00A0-\u9999<>&]/gim, function(i) { - return '&#' + i.charCodeAt(0) + ';'; - }); -}; - -/** - * Re-assign obscured shadow blocks new IDs to prevent collisions - * Scratch specific to help the VM handle deleting obscured shadows. - * @param {Blockly.Block} block the root block to be processed. - * @package - */ -Blockly.scratchBlocksUtils.changeObscuredShadowIds = function(block) { - var blocks = block.getDescendants(false); - for (var i = blocks.length - 1; i >= 0; i--) { - var descendant = blocks[i]; - for (var j = 0; j < descendant.inputList.length; j++) { - var connection = descendant.inputList[j].connection; - if (connection) { - var shadowDom = connection.getShadowDom(); - if (shadowDom) { - shadowDom.setAttribute('id', Blockly.utils.genUid()); - connection.setShadowDom(shadowDom); - } - } - } - } -}; - -/** - * Whether a block is both a shadow block and an argument reporter. These - * blocks have special behaviour in scratch-blocks: they're duplicated when - * dragged, and they are rendered slightly differently from normal shadow - * blocks. - * @param {!Blockly.BlockSvg} block The block that should be used to make this - * decision. - * @return {boolean} True if the block should be duplicated on drag. - * @package - */ -Blockly.scratchBlocksUtils.isShadowArgumentReporter = function(block) { - return (block.isShadow() && (block.type == 'argument_reporter_boolean' || - block.type == 'argument_reporter_string_number')); -}; - -/** - * Compare strings with natural number sorting. - * @param {string} str1 First input. - * @param {string} str2 Second input. - * @return {number} -1, 0, or 1 to signify greater than, equality, or less than. - */ -Blockly.scratchBlocksUtils.compareStrings = function(str1, str2) { - return str1.localeCompare(str2, [], { - sensitivity: 'base', - numeric: true - }); -}; - -/** - * Determine if this block can be recycled in the flyout. Blocks that have no - * variablees and are not dynamic shadows can be recycled. - * @param {Blockly.Block} block The block to check. - * @return {boolean} True if the block can be recycled. - * @package - */ -Blockly.scratchBlocksUtils.blockIsRecyclable = function(block) { - // If the block needs to parse mutations, never recycle. - if (block.mutationToDom && block.domToMutation) { - return false; - } - - for (var i = 0; i < block.inputList.length; i++) { - var input = block.inputList[i]; - for (var j = 0; j < input.fieldRow.length; j++) { - var field = input.fieldRow[j]; - // No variables. - if (field instanceof Blockly.FieldVariable || - field instanceof Blockly.FieldVariableGetter) { - return false; - } - if (field instanceof Blockly.FieldDropdown || - field instanceof Blockly.FieldNumberDropdown || - field instanceof Blockly.FieldTextDropdown) { - if (field.isOptionListDynamic()) { - return false; - } - } - } - // Check children. - if (input.connection) { - var child = input.connection.targetBlock(); - if (child && !Blockly.scratchBlocksUtils.blockIsRecyclable(child)) { - return false; - } - } - } - return true; -}; - - -/** - * Creates a callback function for a click on the "duplicate" context menu - * option in Scratch Blocks. The block is duplicated and attached to the mouse, - * which acts as though it were pressed and mid-drag. Clicking the mouse - * releases the new dragging block. - * @param {!Blockly.BlockSvg} oldBlock The block that will be duplicated. - * @param {!Event} event Event that caused the context menu to open. - * @return {Function} A callback function that duplicates the block and starts a - * drag. - * @package - */ -Blockly.scratchBlocksUtils.duplicateAndDragCallback = function(oldBlock, event) { - var isMouseEvent = Blockly.Touch.getTouchIdentifierFromEvent(event) === 'mouse'; - return function(e) { - // Give the context menu a chance to close. - setTimeout(function() { - var ws = oldBlock.workspace; - var svgRootOld = oldBlock.getSvgRoot(); - if (!svgRootOld) { - throw new Error('oldBlock is not rendered.'); - } - - // Create the new block by cloning the block in the flyout (via XML). - var xml = Blockly.Xml.blockToDom(oldBlock); - // The target workspace would normally resize during domToBlock, which - // will lead to weird jumps. - // Resizing will be enabled when the drag ends. - ws.setResizesEnabled(false); - - // Disable events and manually emit events after the block has been - // positioned and has had its shadow IDs fixed (Scratch-specific). - Blockly.Events.disable(); - try { - // Using domToBlock instead of domToWorkspace means that the new block - // will be placed at position (0, 0) in main workspace units. - var newBlock = Blockly.Xml.domToBlock(xml, ws); - - // Scratch-specific: Give shadow dom new IDs to prevent duplicating on paste - Blockly.scratchBlocksUtils.changeObscuredShadowIds(newBlock); - - var svgRootNew = newBlock.getSvgRoot(); - if (!svgRootNew) { - throw new Error('newBlock is not rendered.'); - } - - // The position of the old block in workspace coordinates. - var oldBlockPosWs = oldBlock.getRelativeToSurfaceXY(); - - // Place the new block as the same position as the old block. - // TODO: Offset by the difference between the mouse position and the upper - // left corner of the block. - newBlock.moveBy(oldBlockPosWs.x, oldBlockPosWs.y); - if (!isMouseEvent) { - var offsetX = ws.RTL ? -100 : 100; - var offsetY = 100; - newBlock.moveBy(offsetX, offsetY); // Just offset the block for touch. - } - } finally { - Blockly.Events.enable(); - } - if (Blockly.Events.isEnabled()) { - Blockly.Events.fire(new Blockly.Events.BlockCreate(newBlock)); - } - - if (isMouseEvent) { - // e is not a real mouseEvent/touchEvent/pointerEvent. It's an event - // created by the context menu and has the coordinates of the mouse - // click that opened the context menu. - var fakeEvent = { - clientX: event.clientX, - clientY: event.clientY, - type: 'mousedown', - preventDefault: function() { - e.preventDefault(); - }, - stopPropagation: function() { - e.stopPropagation(); - }, - target: e.target - }; - ws.startDragWithFakeEvent(fakeEvent, newBlock); - } - }, 0); - }; -}; diff --git a/core/scratch_bubble.js b/core/scratch_bubble.js deleted file mode 100644 index 8c80345d78..0000000000 --- a/core/scratch_bubble.js +++ /dev/null @@ -1,696 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2018 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Object representing a UI bubble. - * @author kchadha@scratch.mit.edu (Karishma Chadha) - */ -'use strict'; - -goog.provide('Blockly.ScratchBubble'); - -goog.require('Blockly.Touch'); -goog.require('Blockly.Workspace'); -goog.require('goog.dom'); -goog.require('goog.math'); -goog.require('goog.math.Coordinate'); -goog.require('goog.userAgent'); - - -/** - * Class for Scratch comment UI bubble. - * @param {!Blockly.ScratchBlockComment} comment The comment this bubble belongs - * to. - * @param {!Blockly.WorkspaceSvg} workspace The workspace on which to draw the - * bubble. - * @param {!Element} content SVG content for the bubble. - * @param {!goog.math.Coordinate} anchorXY Absolute position of bubble's anchor - * point. - * @param {?number} bubbleWidth Width of bubble, or null if not resizable. - * @param {?number} bubbleHeight Height of bubble, or null if not resizable. - * @param {?number} bubbleX X position of bubble - * @param {?number} bubbleY Y position of bubble - * @param {?boolean} minimized Whether or not this comment bubble is minimized - * (only the top bar displays), defaults to false if not provided. - * @extends {Blockly.Bubble} - * @constructor - */ -Blockly.ScratchBubble = function(comment, workspace, content, anchorXY, - bubbleWidth, bubbleHeight, bubbleX, bubbleY, minimized) { - - // Needed for Events - /** - * The comment this bubble belongs to. - * @type {Blockly.ScratchBlockComment} - * @package - */ - this.comment = comment; - - this.workspace_ = workspace; - this.content_ = content; - this.x = bubbleX; - this.y = bubbleY; - this.isMinimized_ = minimized || false; - var canvas = workspace.getBubbleCanvas(); - canvas.appendChild(this.createDom_(content, !!(bubbleWidth && bubbleHeight), - this.isMinimized_)); - - this.setAnchorLocation(anchorXY); - if (!bubbleWidth || !bubbleHeight) { - var bBox = /** @type {SVGLocatable} */ (this.content_).getBBox(); - bubbleWidth = bBox.width + 2 * Blockly.ScratchBubble.BORDER_WIDTH; - bubbleHeight = bBox.height + 2 * Blockly.ScratchBubble.BORDER_WIDTH; - } - this.setBubbleSize(bubbleWidth, bubbleHeight); - - // Render the bubble. - this.positionBubble_(); - this.renderArrow_(); - this.rendered_ = true; - - if (!workspace.options.readOnly) { - Blockly.bindEventWithChecks_( - this.minimizeArrow_, 'mousedown', this, this.minimizeArrowMouseDown_, true); - Blockly.bindEventWithChecks_( - this.minimizeArrow_, 'mouseout', this, this.minimizeArrowMouseOut_, true); - Blockly.bindEventWithChecks_( - this.minimizeArrow_, 'mouseup', this, this.minimizeArrowMouseUp_, true); - Blockly.bindEventWithChecks_( - this.deleteIcon_, 'mousedown', this, this.deleteMouseDown_, true); - Blockly.bindEventWithChecks_( - this.deleteIcon_, 'mouseout', this, this.deleteMouseOut_, true); - Blockly.bindEventWithChecks_( - this.deleteIcon_, 'mouseup', this, this.deleteMouseUp_, true); - Blockly.bindEventWithChecks_( - this.commentTopBar_, 'mousedown', this, this.bubbleMouseDown_); - Blockly.bindEventWithChecks_( - this.bubbleBack_, 'mousedown', this, this.bubbleMouseDown_); - if (this.resizeGroup_) { - Blockly.bindEventWithChecks_( - this.resizeGroup_, 'mousedown', this, this.resizeMouseDown_); - Blockly.bindEventWithChecks_( - this.resizeGroup_, 'mouseup', this, this.resizeMouseUp_); - } - } - - this.setAutoLayout(false); - this.moveTo(this.x, this.y); -}; -goog.inherits(Blockly.ScratchBubble, Blockly.Bubble); - -/** - * Width of the border around the bubble. - * @package - */ -Blockly.ScratchBubble.BORDER_WIDTH = 1; - -/** - * Thickness of the line connecting the bubble - * to the block. - * @private - */ -Blockly.ScratchBubble.LINE_THICKNESS = 1; - -/** - * The height of the comment top bar. - * @package - */ -Blockly.ScratchBubble.TOP_BAR_HEIGHT = 32; - -/** - * The size of the minimize arrow icon in the comment top bar. - * @private - */ -Blockly.ScratchBubble.MINIMIZE_ICON_SIZE = 32; - -/** - * The size of the delete icon in the comment top bar. - * @private - */ -Blockly.ScratchBubble.DELETE_ICON_SIZE = 32; - -/** - * The inset for the top bar icons. - * @private - */ -Blockly.ScratchBubble.TOP_BAR_ICON_INSET = 0; - - -/** - * The inset for the top bar icons. - * @private - */ -Blockly.ScratchBubble.RESIZE_SIZE = 16; - -/** - * The bottom corner padding of the resize handle touch target. - * Extends slightly outside the comment box. - * @private - */ -Blockly.ScratchBubble.RESIZE_CORNER_PAD = 4; - -/** - * The top/side padding around resize handle touch target. - * Extends about one extra "diagonal" above resize handle. - * @private - */ -Blockly.ScratchBubble.RESIZE_OUTER_PAD = 8; - -/** - * Create the bubble's DOM. - * @param {!Element} content SVG content for the bubble. - * @param {boolean} hasResize Add diagonal resize gripper if true. - * @param {boolean} minimized Whether the bubble is minimized - * @return {!Element} The bubble's SVG group. - * @private - */ -Blockly.ScratchBubble.prototype.createDom_ = function(content, hasResize, minimized) { - this.bubbleGroup_ = Blockly.utils.createSvgElement('g', {}, null); - this.bubbleArrow_ = Blockly.utils.createSvgElement('line', - {'stroke-linecap': 'round'}, - this.bubbleGroup_); - this.bubbleBack_ = Blockly.utils.createSvgElement('rect', - { - 'class': 'blocklyDraggable scratchCommentRect', - 'x': 0, - 'y': 0, - 'rx': 4 * Blockly.ScratchBubble.BORDER_WIDTH, - 'ry': 4 * Blockly.ScratchBubble.BORDER_WIDTH - }, - this.bubbleGroup_); - - this.labelText_ = content.labelText; - this.createCommentTopBar_(); - - // Comment Text Editor - this.commentEditor_ = content.commentEditor; - this.bubbleGroup_.appendChild(this.commentEditor_); - - // Comment Resize Handle - if (hasResize) { - this.createResizeHandle_(); - } else { - this.resizeGroup_ = null; - } - - // Show / hide relevant things based on minimized state - if (minimized) { - this.minimizeArrow_.setAttributeNS('http://www.w3.org/1999/xlink', - 'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'comment-arrow-up.svg'); - this.commentEditor_.setAttribute('display', 'none'); - this.resizeGroup_.setAttribute('display', 'none'); - } else { - this.minimizeArrow_.setAttributeNS('http://www.w3.org/1999/xlink', - 'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'comment-arrow-down.svg'); - this.topBarLabel_.setAttribute('display', 'none'); - } - - return this.bubbleGroup_; -}; - -/** - * Create the comment top bar and its contents. - * @private - */ -Blockly.ScratchBubble.prototype.createCommentTopBar_ = function() { - this.commentTopBar_ = Blockly.utils.createSvgElement('rect', - { - 'class': 'blocklyDraggable scratchCommentTopBar', - 'rx': Blockly.ScratchBubble.BORDER_WIDTH, - 'ry': Blockly.ScratchBubble.BORDER_WIDTH, - 'height': Blockly.ScratchBubble.TOP_BAR_HEIGHT - }, this.bubbleGroup_); - - this.createTopBarIcons_(); - this.createTopBarLabel_(); -}; - -/** - * Create the minimize toggle and delete icons that in the comment top bar. - * @private - */ -Blockly.ScratchBubble.prototype.createTopBarIcons_ = function() { - var topBarMiddleY = (Blockly.ScratchBubble.TOP_BAR_HEIGHT / 2) + - Blockly.ScratchBubble.BORDER_WIDTH; - - // Minimize Toggle Icon in Comment Top Bar - var xInset = Blockly.ScratchBubble.TOP_BAR_ICON_INSET; - this.minimizeArrow_ = Blockly.utils.createSvgElement('image', - { - 'x': xInset, - 'y': topBarMiddleY - Blockly.ScratchBubble.MINIMIZE_ICON_SIZE / 2, - 'width': Blockly.ScratchBubble.MINIMIZE_ICON_SIZE, - 'height': Blockly.ScratchBubble.MINIMIZE_ICON_SIZE - }, this.bubbleGroup_); - - // Delete Icon in Comment Top Bar - this.deleteIcon_ = Blockly.utils.createSvgElement('image', - { - 'x': xInset, - 'y': topBarMiddleY - Blockly.ScratchBubble.DELETE_ICON_SIZE / 2, - 'width': Blockly.ScratchBubble.DELETE_ICON_SIZE, - 'height': Blockly.ScratchBubble.DELETE_ICON_SIZE - }, this.bubbleGroup_); - this.deleteIcon_.setAttributeNS('http://www.w3.org/1999/xlink', - 'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'delete-x.svg'); -}; - -/** - * Create the comment top bar label. This is the truncated comment text - * that shows when comment is minimized. - * @private - */ -Blockly.ScratchBubble.prototype.createTopBarLabel_ = function() { - this.topBarLabel_ = Blockly.utils.createSvgElement('text', - { - 'class': 'scratchCommentText', - 'x': this.width_ / 2, - 'y': (Blockly.ScratchBubble.TOP_BAR_HEIGHT / 2) + Blockly.ScratchBubble.BORDER_WIDTH, - 'text-anchor': 'middle', - 'dominant-baseline': 'middle' - }, this.bubbleGroup_); - - var labelTextNode = document.createTextNode(this.labelText_); - this.topBarLabel_.appendChild(labelTextNode); -}; - -/** - * Create the comment resize handle. - * @private - */ -Blockly.ScratchBubble.prototype.createResizeHandle_ = function() { - this.resizeGroup_ = Blockly.utils.createSvgElement('g', - {'class': this.workspace_.RTL ? - 'scratchCommentResizeSW' : 'scratchCommentResizeSE'}, - this.bubbleGroup_); - var resizeSize = Blockly.ScratchBubble.RESIZE_SIZE; - var outerPad = Blockly.ScratchBubble.RESIZE_OUTER_PAD; - var cornerPad = Blockly.ScratchBubble.RESIZE_CORNER_PAD; - // Build an (invisible) triangle that will catch resizes. It is padded on the - // top/left by outerPad, and padded down/right by cornerPad. - Blockly.utils.createSvgElement('polygon', - { - 'points': [ - -outerPad, resizeSize + cornerPad, - resizeSize + cornerPad, resizeSize + cornerPad, - resizeSize + cornerPad, -outerPad - ].join(' ') - }, - this.resizeGroup_); - Blockly.utils.createSvgElement('line', - { - 'class': 'blocklyResizeLine', - 'x1': resizeSize / 3, 'y1': resizeSize - 1, - 'x2': resizeSize - 1, 'y2': resizeSize / 3 - }, this.resizeGroup_); - Blockly.utils.createSvgElement('line', - { - 'class': 'blocklyResizeLine', - 'x1': resizeSize * 2 / 3, - 'y1': resizeSize - 1, - 'x2': resizeSize - 1, - 'y2': resizeSize * 2 / 3 - }, this.resizeGroup_); -}; - -/** - * Show the context menu for this bubble. - * @param {!Event} e Mouse event. - * @private - */ -Blockly.ScratchBubble.prototype.showContextMenu_ = function(e) { - if (this.workspace_.options.readOnly) { - return; - } - - if (this.contextMenuCallback_) { - this.contextMenuCallback_(e); - } -}; - -/** - * Handle a mouse-down on bubble's minimize icon. - * @param {!Event} e Mouse up event. - * @private - */ -Blockly.ScratchBubble.prototype.minimizeArrowMouseDown_ = function(e) { - // Set a property indicating that this comment's minimize arrow got a mouse - // down event. This property will get reset if the mouse leaves the icon or - // when a mouse up occurs on this icon after this mouse down. - this.shouldToggleMinimize_ = true; - e.stopPropagation(); -}; - -/** - * Handle a mouse-out on bubble's minimize icon. - * @param {!Event} _e Mouse up event. - * @private - */ -Blockly.ScratchBubble.prototype.minimizeArrowMouseOut_ = function(_e) { - // If the mouse has left the minimize arrow icon, the - // shouldToggleMinimize property should get reset to false. - this.shouldToggleMinimize_ = false; -}; - -/** - * Handle a mouse-up on bubble's minimize icon. - * @param {!Event} e Mouse up event. - * @private - */ -Blockly.ScratchBubble.prototype.minimizeArrowMouseUp_ = function(e) { - // First check if this icon had a mouse down event - // on it and that the mouse never left the icon - if (this.shouldToggleMinimize_) { - this.shouldToggleMinimize_ = false; - - if (this.minimizeToggleCallback_) { - this.minimizeToggleCallback_.call(this); - } - } - e.stopPropagation(); -}; - -/** - * Handle a mouse-down on bubble's delete icon. - * @param {!Event} e Mouse up event. - * @private - */ -Blockly.ScratchBubble.prototype.deleteMouseDown_ = function(e) { - this.shouldDelete_ = true; - e.stopPropagation(); -}; - -/** - * Handle a mouse-out on bubble's delete icon. - * @param {!Event} _e Mouse out event. - * @private - */ -Blockly.ScratchBubble.prototype.deleteMouseOut_ = function(_e) { - // If the mouse has left the delete icon, the shouldDelete_ property - // should get reset to false. - this.shouldDelete_ = false; -}; - -/** - * Handle a mouse-up on bubble's delete icon. - * @param {!Event} e Mouse up event. - * @private - */ -Blockly.ScratchBubble.prototype.deleteMouseUp_ = function(e) { - // First check that this is actually the same icon that had a mouse down event - // on it and that the mouse never left the icon - if (this.shouldDelete_) { - this.shouldDelete_ = false; - - if (this.deleteCallback_) { - this.deleteCallback_.call(this); - } - } - e.stopPropagation(); -}; - -/** - * Handle a mouse-down on bubble's resize corner. - * @param {!Event} e Mouse down event. - * @private - */ -Blockly.ScratchBubble.prototype.resizeMouseDown_ = function(e) { - this.resizeStartSize_ = {width: this.width_, height: this.height_}; - this.workspace_.setResizesEnabled(false); - Blockly.ScratchBubble.superClass_.resizeMouseDown_.call(this, e); -}; - -/** - * Handle a mouse-up on bubble's resize corner. - * @param {!Event} _e Mouse up event. - * @private - */ -Blockly.ScratchBubble.prototype.resizeMouseUp_ = function(_e) { - var oldHW = this.resizeStartSize_; - this.resizeStartSize_ = null; - if (this.width_ == oldHW.width && this.height_ == oldHW.height) { - return; - } - // Fire a change event for the new width/height after - // resize mouse up - Blockly.Events.fire(new Blockly.Events.CommentChange( - this.comment, {width: oldHW.width , height: oldHW.height}, - {width: this.width_, height: this.height_})); - - this.workspace_.setResizesEnabled(true); -}; - -/** - * Set the minimized state of the bubble. - * @param {boolean} minimize Whether the bubble should be minimized - * @param {?string} labelText Optional label text for the comment top bar - * when it is minimized. - * @package - */ -Blockly.ScratchBubble.prototype.setMinimized = function(minimize, labelText) { - if (minimize == this.isMinimized_) { - return; - } - if (minimize) { - this.isMinimized_ = true; - // Change minimize icon - this.minimizeArrow_.setAttributeNS('http://www.w3.org/1999/xlink', - 'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'comment-arrow-up.svg'); - // Hide text area - this.commentEditor_.setAttribute('display', 'none'); - // Hide resize handle if it exists - if (this.resizeGroup_) { - this.resizeGroup_.setAttribute('display', 'none'); - } - if (labelText && this.labelText_ != labelText) { - // Update label and display - this.topBarLabel_.textContent = labelText; - } - Blockly.utils.removeAttribute(this.topBarLabel_, 'display'); - } else { - this.isMinimized_ = false; - // Change minimize icon - this.minimizeArrow_.setAttributeNS('http://www.w3.org/1999/xlink', - 'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'comment-arrow-down.svg'); - // Hide label - this.topBarLabel_.setAttribute('display', 'none'); - // Show text area - Blockly.utils.removeAttribute(this.commentEditor_, 'display'); - // Display resize handle if it exists - if (this.resizeGroup_) { - Blockly.utils.removeAttribute(this.resizeGroup_, 'display'); - } - } -}; - -/** - * Register a function as a callback event for when the bubble is minimized. - * @param {!Function} callback The function to call on resize. - * @package - */ -Blockly.ScratchBubble.prototype.registerMinimizeToggleEvent = function(callback) { - this.minimizeToggleCallback_ = callback; -}; - -/** - * Register a function as a callback event for when the bubble is resized. - * @param {!Function} callback The function to call on resize. - * @package - */ -Blockly.ScratchBubble.prototype.registerDeleteEvent = function(callback) { - this.deleteCallback_ = callback; -}; - -/** - * Register a function as a callback to show the context menu for this comment. - * @param {!Function} callback The function to call on resize. - * @package - */ -Blockly.ScratchBubble.prototype.registerContextMenuCallback = function(callback) { - this.contextMenuCallback_ = callback; -}; - -/** - * Notification that the anchor has moved. - * Update the arrow and bubble accordingly. - * @param {!goog.math.Coordinate} xy Absolute location. - * @package - */ -Blockly.ScratchBubble.prototype.setAnchorLocation = function(xy) { - this.anchorXY_ = xy; - if (this.rendered_) { - this.positionBubble_(); - } -}; - -/** - * Move the bubble group to the specified location in workspace coordinates. - * @param {number} x The x position to move to. - * @param {number} y The y position to move to. - * @package - */ -Blockly.ScratchBubble.prototype.moveTo = function(x, y) { - Blockly.ScratchBubble.superClass_.moveTo.call(this, x, y); - this.updatePosition_(x, y); -}; - -/** - * Size this bubble. - * @param {number} width Width of the bubble. - * @param {number} height Height of the bubble. - * @package - */ -Blockly.ScratchBubble.prototype.setBubbleSize = function(width, height) { - var doubleBorderWidth = 2 * Blockly.ScratchBubble.BORDER_WIDTH; - // Minimum size of a bubble. - width = Math.max(width, doubleBorderWidth + 50); - height = Math.max(height, Blockly.ScratchBubble.TOP_BAR_HEIGHT); - this.width_ = width; - this.height_ = height; - this.bubbleBack_.setAttribute('width', width); - this.bubbleBack_.setAttribute('height', height); - this.commentTopBar_.setAttribute('width', width); - this.commentTopBar_.setAttribute('height', Blockly.ScratchBubble.TOP_BAR_HEIGHT); - if (this.workspace_.RTL) { - this.minimizeArrow_.setAttribute('x', width - - (Blockly.ScratchBubble.MINIMIZE_ICON_SIZE) - - Blockly.ScratchBubble.TOP_BAR_ICON_INSET); - } else { - this.deleteIcon_.setAttribute('x', width - - Blockly.ScratchBubble.DELETE_ICON_SIZE - - Blockly.ScratchBubble.TOP_BAR_ICON_INSET); - } - if (this.resizeGroup_) { - var resizeSize = Blockly.ScratchBubble.RESIZE_SIZE; - if (this.workspace_.RTL) { - // Mirror the resize group. - this.resizeGroup_.setAttribute('transform', 'translate(' + - (resizeSize + doubleBorderWidth) + ',' + - (this.height_ - doubleBorderWidth - resizeSize) + ') scale(-1, 1)'); - } else { - this.resizeGroup_.setAttribute('transform', 'translate(' + - (this.width_ - doubleBorderWidth - resizeSize) + ',' + - (this.height_ - doubleBorderWidth - resizeSize) + ')'); - } - } - if (this.isMinimized_) { - this.topBarLabel_.setAttribute('x', this.width_ / 2); - this.topBarLabel_.setAttribute('y', this.height_ / 2); - } - if (this.rendered_) { - this.positionBubble_(); - this.renderArrow_(); - } - // Allow the contents to resize. - if (this.resizeCallback_) { - this.resizeCallback_(); - } -}; - -/** - * Draw the line between the bubble and the origin. - * @private - */ -Blockly.ScratchBubble.prototype.renderArrow_ = function() { - // Find the relative coordinates of the top bar center of the bubble. - var relBubbleX = this.width_ / 2; - var relBubbleY = Blockly.ScratchBubble.TOP_BAR_HEIGHT / 2; - // Find the relative coordinates of the center of the anchor. - var relAnchorX = -this.relativeLeft_; - var relAnchorY = -this.relativeTop_; - if (relBubbleX != relAnchorX || relBubbleY != relAnchorY) { - // Compute the angle of the arrow's line. - var rise = relAnchorY - relBubbleY; - var run = relAnchorX - relBubbleX; - if (this.workspace_.RTL) { - run *= -1; - run -= this.width_; - } - - var baseX1 = relBubbleX; - var baseY1 = relBubbleY; - - this.bubbleArrow_.setAttribute('x1', baseX1); - this.bubbleArrow_.setAttribute('y1', baseY1); - this.bubbleArrow_.setAttribute('x2', baseX1 + run); - this.bubbleArrow_.setAttribute('y2', baseY1 + rise); - this.bubbleArrow_.setAttribute('stroke-width', Blockly.ScratchBubble.LINE_THICKNESS); - } -}; - -/** - * Change the colour of a bubble. - * @param {string} hexColour Hex code of colour. - * @package - */ -Blockly.ScratchBubble.prototype.setColour = function(hexColour) { - this.bubbleBack_.setAttribute('stroke', hexColour); - this.bubbleArrow_.setAttribute('stroke', hexColour); -}; - -/** - * Move this bubble during a drag, taking into account whether or not there is - * a drag surface. - * @param {?Blockly.BlockDragSurfaceSvg} dragSurface The surface that carries - * rendered items during a drag, or null if no drag surface is in use. - * @param {!goog.math.Coordinate} newLoc The location to translate to, in - * workspace coordinates. - * @package - */ -Blockly.ScratchBubble.prototype.moveDuringDrag = function(dragSurface, newLoc) { - if (dragSurface) { - dragSurface.translateSurface(newLoc.x, newLoc.y); - this.updatePosition_(newLoc.x, newLoc.y); - } else { - this.moveTo(newLoc.x, newLoc.y); - } -}; - -/** - * Update the relative left and top of the bubble after a move. - * @param {number} x The x position of the bubble - * @param {number} y The y position of the bubble - * @private - */ -Blockly.ScratchBubble.prototype.updatePosition_ = function(x, y) { - // Relative left is the distance *and* direction to get from the comment - // anchor position on the block to the starting edge of the comment (e.g. - // the left edge of the comment in LTR and the right edge of the comment in RTL) - if (this.workspace_.RTL) { - // we want relativeLeft_ to actually be the distance from the anchor point to the *right* edge of the comment in RTL - this.relativeLeft_ = this.anchorXY_.x - x; - } else { - this.relativeLeft_ = x - this.anchorXY_.x; - } - this.relativeTop_ = y - this.anchorXY_.y; - this.renderArrow_(); -}; - -/** - * Dispose of this bubble. - * @package - */ -Blockly.ScratchBubble.prototype.dispose = function() { - Blockly.ScratchBubble.superClass_.dispose.call(this); - this.topBarLabel_ = null; - this.commentTopBar_ = null; - this.minimizeArrow_ = null; - this.deleteIcon_ = null; -}; diff --git a/core/scratch_events.js b/core/scratch_events.js deleted file mode 100644 index 56f340bdc9..0000000000 --- a/core/scratch_events.js +++ /dev/null @@ -1,131 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2018 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Events fired as a result of UI actions in a Scratch-Blocks - * editor that are not fired in Blockly. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.Events.DragBlockOutside'); -goog.provide('Blockly.Events.EndBlockDrag'); - -goog.require('Blockly.Events'); -goog.require('Blockly.Events.BlockBase'); - -goog.require('goog.array'); -goog.require('goog.math.Coordinate'); - -/** - * Class for a block drag event. Fired when block dragged into or out of - * the blocks UI. - * @param {Blockly.Block} block The moved block. Null for a blank event. - * @extends {Blockly.Events.BlockBase} - * @constructor - */ -Blockly.Events.DragBlockOutside = function(block) { - if (!block) { - return; // Blank event to be populated by fromJson. - } - Blockly.Events.DragBlockOutside.superClass_.constructor.call(this, block); - this.recordUndo = false; -}; -goog.inherits(Blockly.Events.DragBlockOutside, Blockly.Events.BlockBase); - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.DragBlockOutside.prototype.type = Blockly.Events.DRAG_OUTSIDE; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.DragBlockOutside.prototype.toJson = function() { - var json = Blockly.Events.DragBlockOutside.superClass_.toJson.call(this); - if (this.isOutside) { - json['isOutside'] = this.isOutside; - } - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.DragBlockOutside.prototype.fromJson = function(json) { - Blockly.Events.DragBlockOutside.superClass_.fromJson.call(this, json); - this.isOutside = json['isOutside']; -}; - -/** - * Class for a block end drag event. - * @param {Blockly.Block} block The moved block. Null for a blank event. - * @param {boolean} isOutside True if the moved block is outside of the - * blocks workspace. - * @extends {Blockly.Events.BlockBase} - * @constructor - */ -Blockly.Events.EndBlockDrag = function(block, isOutside) { - if (!block) { - return; // Blank event to be populated by fromJson. - } - Blockly.Events.EndBlockDrag.superClass_.constructor.call(this, block); - this.isOutside = isOutside; - // If drag ends outside the blocks workspace, send the block XML - if (isOutside) { - this.xml = Blockly.Xml.blockToDom(block, true /* opt_noId */); - } - this.recordUndo = false; -}; -goog.inherits(Blockly.Events.EndBlockDrag, Blockly.Events.BlockBase); - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.EndBlockDrag.prototype.type = Blockly.Events.END_DRAG; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.EndBlockDrag.prototype.toJson = function() { - var json = Blockly.Events.EndBlockDrag.superClass_.toJson.call(this); - if (this.isOutside) { - json['isOutside'] = this.isOutside; - } - if (this.xml) { - json['xml'] = this.xml; - } - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.EndBlockDrag.prototype.fromJson = function(json) { - Blockly.Events.EndBlockDrag.superClass_.fromJson.call(this, json); - this.isOutside = json['isOutside']; - this.xml = json['xml']; -}; diff --git a/core/scratch_msgs.js b/core/scratch_msgs.js deleted file mode 100644 index f59588f841..0000000000 --- a/core/scratch_msgs.js +++ /dev/null @@ -1,85 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2018 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Scratch Messages singleton, with function to override Blockly.Msg values. - * @author chrisg@media.mit.edu (Chris Garrity) - */ -'use strict'; - -/** - * Name space for the ScratchMsgs singleton. - * Msg gets populated in the message files. - */ -goog.provide('Blockly.ScratchMsgs'); - -goog.require('Blockly.Msg'); - - -/** - * The object containing messages for all locales - loaded from msg/scratch_msgs. - * @type {Object} - */ -Blockly.ScratchMsgs.locales = {}; - -/** - * The current locale. - * @type {String} - * @private - */ -Blockly.ScratchMsgs.currentLocale_ = 'en'; - -/** - * Change the Blockly.Msg strings to a new Locale - * Does not exist in Blockly, but needed in scratch-blocks - * @param {string} locale E.g., 'de', or 'zh-tw' - * @package - */ -Blockly.ScratchMsgs.setLocale = function(locale) { - if (Object.keys(Blockly.ScratchMsgs.locales).includes(locale)) { - Blockly.ScratchMsgs.currentLocale_ = locale; - Blockly.Msg = Object.assign({}, Blockly.Msg, Blockly.ScratchMsgs.locales[locale]); - } else { - // keep current locale - console.warn('Ignoring unrecognized locale: ' + locale); - } -}; - -/** - * Gets a localized message, for use in the Scratch VM with json init. - * Does not interpolate placeholders. Provided to allow default values in - * dynamic menus, for example, 'next backdrop', or 'random position' - * @param {string} msgId id for the message, key in Msg table. - * @param {string} defaultMsg string to use if the id isn't found. - * @param {string} useLocale optional locale to use in place of currentLocale_. - * @return {string} message with placeholders filled. - * @package - */ -Blockly.ScratchMsgs.translate = function(msgId, defaultMsg, useLocale) { - var locale = useLocale || Blockly.ScratchMsgs.currentLocale_; - - if (Object.keys(Blockly.ScratchMsgs.locales).includes(locale)) { - var messages = Blockly.ScratchMsgs.locales[locale]; - if (Object.keys(messages).includes(msgId)) { - return messages[msgId]; - } - } - return defaultMsg; -}; diff --git a/core/scrollbar.js b/core/scrollbar.js deleted file mode 100644 index c5431291a2..0000000000 --- a/core/scrollbar.js +++ /dev/null @@ -1,875 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2011 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Library for creating scrollbars. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.Scrollbar'); -goog.provide('Blockly.ScrollbarPair'); - -goog.require('goog.dom'); -goog.require('goog.events'); - - -/** - * A note on units: most of the numbers that are in CSS pixels are scaled if the - * scrollbar is in a mutator. - */ - -/** - * Class for a pair of scrollbars. Horizontal and vertical. - * @param {!Blockly.Workspace} workspace Workspace to bind the scrollbars to. - * @constructor - */ -Blockly.ScrollbarPair = function(workspace) { - this.workspace_ = workspace; - this.hScroll = new Blockly.Scrollbar( - workspace, true, true, 'blocklyMainWorkspaceScrollbar'); - this.vScroll = new Blockly.Scrollbar( - workspace, false, true, 'blocklyMainWorkspaceScrollbar'); - this.corner_ = Blockly.utils.createSvgElement( - 'rect', - { - 'height': Blockly.Scrollbar.scrollbarThickness, - 'width': Blockly.Scrollbar.scrollbarThickness, - 'class': 'blocklyScrollbarBackground' - }, - null); - Blockly.utils.insertAfter(this.corner_, workspace.getBubbleCanvas()); -}; - -/** - * Previously recorded metrics from the workspace. - * @type {Object} - * @private - */ -Blockly.ScrollbarPair.prototype.oldHostMetrics_ = null; - -/** - * Dispose of this pair of scrollbars. - * Unlink from all DOM elements to prevent memory leaks. - */ -Blockly.ScrollbarPair.prototype.dispose = function() { - goog.dom.removeNode(this.corner_); - this.corner_ = null; - this.workspace_ = null; - this.oldHostMetrics_ = null; - this.hScroll.dispose(); - this.hScroll = null; - this.vScroll.dispose(); - this.vScroll = null; -}; - -/** - * Recalculate both of the scrollbars' locations and lengths. - * Also reposition the corner rectangle. - */ -Blockly.ScrollbarPair.prototype.resize = function() { - // Look up the host metrics once, and use for both scrollbars. - var hostMetrics = this.workspace_.getMetrics(); - if (!hostMetrics) { - // Host element is likely not visible. - return; - } - - // Only change the scrollbars if there has been a change in metrics. - var resizeH = false; - var resizeV = false; - if (!this.oldHostMetrics_ || - this.oldHostMetrics_.viewWidth != hostMetrics.viewWidth || - this.oldHostMetrics_.viewHeight != hostMetrics.viewHeight || - this.oldHostMetrics_.absoluteTop != hostMetrics.absoluteTop || - this.oldHostMetrics_.absoluteLeft != hostMetrics.absoluteLeft) { - // The window has been resized or repositioned. - resizeH = true; - resizeV = true; - } else { - // Has the content been resized or moved? - if (!this.oldHostMetrics_ || - this.oldHostMetrics_.contentWidth != hostMetrics.contentWidth || - this.oldHostMetrics_.viewLeft != hostMetrics.viewLeft || - this.oldHostMetrics_.contentLeft != hostMetrics.contentLeft) { - resizeH = true; - } - if (!this.oldHostMetrics_ || - this.oldHostMetrics_.contentHeight != hostMetrics.contentHeight || - this.oldHostMetrics_.viewTop != hostMetrics.viewTop || - this.oldHostMetrics_.contentTop != hostMetrics.contentTop) { - resizeV = true; - } - } - if (resizeH) { - this.hScroll.resize(hostMetrics); - } - if (resizeV) { - this.vScroll.resize(hostMetrics); - } - - // Reposition the corner square. - if (!this.oldHostMetrics_ || - this.oldHostMetrics_.viewWidth != hostMetrics.viewWidth || - this.oldHostMetrics_.absoluteLeft != hostMetrics.absoluteLeft) { - this.corner_.setAttribute('x', this.vScroll.position_.x); - } - if (!this.oldHostMetrics_ || - this.oldHostMetrics_.viewHeight != hostMetrics.viewHeight || - this.oldHostMetrics_.absoluteTop != hostMetrics.absoluteTop) { - this.corner_.setAttribute('y', this.hScroll.position_.y); - } - - // Cache the current metrics to potentially short-cut the next resize event. - this.oldHostMetrics_ = hostMetrics; -}; - -/** - * Set the handles of both scrollbars to be at a certain position in CSS pixels - * relative to their parents. - * @param {number} x Horizontal scroll value. - * @param {number} y Vertical scroll value. - */ -Blockly.ScrollbarPair.prototype.set = function(x, y) { - // This function is equivalent to: - // this.hScroll.set(x); - // this.vScroll.set(y); - // However, that calls setMetrics twice which causes a chain of - // getAttribute->setAttribute->getAttribute resulting in an extra layout pass. - // Combining them speeds up rendering. - var xyRatio = {}; - - var hHandlePosition = x * this.hScroll.ratio_; - var vHandlePosition = y * this.vScroll.ratio_; - - var hBarLength = this.hScroll.scrollViewSize_; - var vBarLength = this.vScroll.scrollViewSize_; - - xyRatio.x = this.getRatio_(hHandlePosition, hBarLength); - xyRatio.y = this.getRatio_(vHandlePosition, vBarLength); - this.workspace_.setMetrics(xyRatio); - - this.hScroll.setHandlePosition(hHandlePosition); - this.vScroll.setHandlePosition(vHandlePosition); -}; - -/** - * Helper to calculate the ratio of handle position to scrollbar view size. - * @param {number} handlePosition The value of the handle. - * @param {number} viewSize The total size of the scrollbar's view. - * @return {number} Ratio. - * @private - */ -Blockly.ScrollbarPair.prototype.getRatio_ = function(handlePosition, viewSize) { - var ratio = handlePosition / viewSize; - if (isNaN(ratio)) { - return 0; - } - return ratio; -}; - -// -------------------------------------------------------------------- - -/** - * Class for a pure SVG scrollbar. - * This technique offers a scrollbar that is guaranteed to work, but may not - * look or behave like the system's scrollbars. - * @param {!Blockly.Workspace} workspace Workspace to bind the scrollbar to. - * @param {boolean} horizontal True if horizontal, false if vertical. - * @param {boolean=} opt_pair True if scrollbar is part of a horiz/vert pair. - * @param {string=} opt_class A class to be applied to this scrollbar. - * @constructor - */ -Blockly.Scrollbar = function(workspace, horizontal, opt_pair, opt_class) { - this.workspace_ = workspace; - this.pair_ = opt_pair || false; - this.horizontal_ = horizontal; - this.oldHostMetrics_ = null; - - this.createDom_(opt_class); - - /** - * The upper left corner of the scrollbar's SVG group in CSS pixels relative - * to the scrollbar's origin. This is usually relative to the injection div - * origin. - * @type {goog.math.Coordinate} - * @private - */ - this.position_ = new goog.math.Coordinate(0, 0); - - // Store the thickness in a temp variable for readability. - var scrollbarThickness = Blockly.Scrollbar.scrollbarThickness; - if (horizontal) { - this.svgBackground_.setAttribute('height', scrollbarThickness); - this.outerSvg_.setAttribute('height', scrollbarThickness); - this.svgHandle_.setAttribute('height', scrollbarThickness - 5); - this.svgHandle_.setAttribute('y', 2.5); - - this.lengthAttribute_ = 'width'; - this.positionAttribute_ = 'x'; - } else { - this.svgBackground_.setAttribute('width', scrollbarThickness); - this.outerSvg_.setAttribute('width', scrollbarThickness); - this.svgHandle_.setAttribute('width', scrollbarThickness - 5); - this.svgHandle_.setAttribute('x', 2.5); - - this.lengthAttribute_ = 'height'; - this.positionAttribute_ = 'y'; - } - var scrollbar = this; - this.onMouseDownBarWrapper_ = Blockly.bindEventWithChecks_( - this.svgBackground_, 'mousedown', scrollbar, scrollbar.onMouseDownBar_); - this.onMouseDownHandleWrapper_ = Blockly.bindEventWithChecks_(this.svgHandle_, - 'mousedown', scrollbar, scrollbar.onMouseDownHandle_); -}; - -/** - * The location of the origin of the workspace that the scrollbar is in, - * measured in CSS pixels relative to the injection div origin. This is usually - * (0, 0). When the scrollbar is in a flyout it may have a different origin. - * @type {goog.math.Coordinate} - * @private - */ -Blockly.Scrollbar.prototype.origin_ = new goog.math.Coordinate(0, 0); - -/** - * Whether or not the origin of the scrollbar has changed. Used - * to help decide whether or not the reflow/resize calls need to happen. - * @type {boolean} - * @private - */ -Blockly.Scrollbar.prototype.originHasChanged_ = true; - -/** - * The size of the area within which the scrollbar handle can move, in CSS - * pixels. - * @type {number} - * @private - */ -Blockly.Scrollbar.prototype.scrollViewSize_ = 0; - -/** - * The length of the scrollbar handle in CSS pixels. - * @type {number} - * @private - */ -Blockly.Scrollbar.prototype.handleLength_ = 0; - -/** - * The offset of the start of the handle from the scrollbar position, in CSS - * pixels. - * @type {number} - * @private - */ -Blockly.Scrollbar.prototype.handlePosition_ = 0; - -/** - * Whether the scrollbar handle is visible. - * @type {boolean} - * @private - */ -Blockly.Scrollbar.prototype.isVisible_ = true; - -/** - * Whether the workspace containing this scrollbar is visible. - * @type {boolean} - * @private - */ -Blockly.Scrollbar.prototype.containerVisible_ = true; - -/** - * Width of vertical scrollbar or height of horizontal scrollbar in CSS pixels. - * Scrollbars should be larger on touch devices. - */ -Blockly.Scrollbar.scrollbarThickness = 11; -if (goog.events.BrowserFeature.TOUCH_ENABLED) { - Blockly.Scrollbar.scrollbarThickness = 14; -} - -/** - * @param {!Object} first An object containing computed measurements of a - * workspace. - * @param {!Object} second Another object containing computed measurements of a - * workspace. - * @return {boolean} Whether the two sets of metrics are equivalent. - * @private - */ -Blockly.Scrollbar.metricsAreEquivalent_ = function(first, second) { - if (!(first && second)) { - return false; - } - - if (first.viewWidth != second.viewWidth || - first.viewHeight != second.viewHeight || - first.viewLeft != second.viewLeft || - first.viewTop != second.viewTop || - first.absoluteTop != second.absoluteTop || - first.absoluteLeft != second.absoluteLeft || - first.contentWidth != second.contentWidth || - first.contentHeight != second.contentHeight || - first.contentLeft != second.contentLeft || - first.contentTop != second.contentTop) { - return false; - } - - return true; -}; - -/** - * Dispose of this scrollbar. - * Unlink from all DOM elements to prevent memory leaks. - */ -Blockly.Scrollbar.prototype.dispose = function() { - this.cleanUp_(); - Blockly.unbindEvent_(this.onMouseDownBarWrapper_); - this.onMouseDownBarWrapper_ = null; - Blockly.unbindEvent_(this.onMouseDownHandleWrapper_); - this.onMouseDownHandleWrapper_ = null; - - goog.dom.removeNode(this.outerSvg_); - this.outerSvg_ = null; - this.svgGroup_ = null; - this.svgBackground_ = null; - this.svgHandle_ = null; - this.workspace_ = null; -}; - -/** - * Set the length of the scrollbar's handle and change the SVG attribute - * accordingly. - * @param {number} newLength The new scrollbar handle length in CSS pixels. - */ -Blockly.Scrollbar.prototype.setHandleLength_ = function(newLength) { - this.handleLength_ = newLength; - this.svgHandle_.setAttribute(this.lengthAttribute_, this.handleLength_); -}; - -/** - * Set the offset of the scrollbar's handle from the scrollbar's position, and - * change the SVG attribute accordingly. - * @param {number} newPosition The new scrollbar handle offset in CSS pixels. - */ -Blockly.Scrollbar.prototype.setHandlePosition = function(newPosition) { - this.handlePosition_ = newPosition; - this.svgHandle_.setAttribute(this.positionAttribute_, this.handlePosition_); -}; - -/** - * Set the size of the scrollbar's background and change the SVG attribute - * accordingly. - * @param {number} newSize The new scrollbar background length in CSS pixels. - * @private - */ -Blockly.Scrollbar.prototype.setScrollViewSize_ = function(newSize) { - this.scrollViewSize_ = newSize; - this.outerSvg_.setAttribute(this.lengthAttribute_, this.scrollViewSize_); - this.svgBackground_.setAttribute(this.lengthAttribute_, this.scrollViewSize_); -}; - -/** - * Set whether this scrollbar's container is visible. - * @param {boolean} visible Whether the container is visible. - */ -Blockly.ScrollbarPair.prototype.setContainerVisible = function(visible) { - this.hScroll.setContainerVisible(visible); - this.vScroll.setContainerVisible(visible); -}; - -/** - * Set the position of the scrollbar's SVG group in CSS pixels relative to the - * scrollbar's origin. This sets the scrollbar's location within the workspace. - * @param {number} x The new x coordinate. - * @param {number} y The new y coordinate. - * @private - */ -Blockly.Scrollbar.prototype.setPosition_ = function(x, y) { - this.position_.x = x; - this.position_.y = y; - - var tempX = this.position_.x + this.origin_.x; - var tempY = this.position_.y + this.origin_.y; - var transform = 'translate(' + tempX + 'px,' + tempY + 'px)'; - Blockly.utils.setCssTransform(this.outerSvg_, transform); -}; - -/** - * Recalculate the scrollbar's location and its length. - * @param {Object=} opt_metrics A data structure of from the describing all the - * required dimensions. If not provided, it will be fetched from the host - * object. - */ -Blockly.Scrollbar.prototype.resize = function(opt_metrics) { - // Determine the location, height and width of the host element. - var hostMetrics = opt_metrics; - if (!hostMetrics) { - hostMetrics = this.workspace_.getMetrics(); - if (!hostMetrics) { - // Host element is likely not visible. - return; - } - } - - // If the origin has changed (e.g. the toolbox is moving from start to end) - // we want to continue with the resize even if workspace metrics haven't. - if (this.originHasChanged_) { - this.originHasChanged_ = false; - } else if (Blockly.Scrollbar.metricsAreEquivalent_(hostMetrics, - this.oldHostMetrics_)) { - return; - } - this.oldHostMetrics_ = hostMetrics; - - /* hostMetrics is an object with the following properties. - * .viewHeight: Height of the visible rectangle, - * .viewWidth: Width of the visible rectangle, - * .contentHeight: Height of the contents, - * .contentWidth: Width of the content, - * .viewTop: Offset of top edge of visible rectangle from parent, - * .viewLeft: Offset of left edge of visible rectangle from parent, - * .contentTop: Offset of the top-most content from the y=0 coordinate, - * .contentLeft: Offset of the left-most content from the x=0 coordinate, - * .absoluteTop: Top-edge of view. - * .absoluteLeft: Left-edge of view. - */ - if (this.horizontal_) { - this.resizeHorizontal_(hostMetrics); - } else { - this.resizeVertical_(hostMetrics); - } - // Resizing may have caused some scrolling. - this.onScroll_(); -}; - -/** - * Recalculate a horizontal scrollbar's location and length. - * @param {!Object} hostMetrics A data structure describing all the - * required dimensions, possibly fetched from the host object. - * @private - */ -Blockly.Scrollbar.prototype.resizeHorizontal_ = function(hostMetrics) { - // TODO: Inspect metrics to determine if we can get away with just a content - // resize. - this.resizeViewHorizontal(hostMetrics); -}; - -/** - * Recalculate a horizontal scrollbar's location on the screen and path length. - * This should be called when the layout or size of the window has changed. - * @param {!Object} hostMetrics A data structure describing all the - * required dimensions, possibly fetched from the host object. - */ -Blockly.Scrollbar.prototype.resizeViewHorizontal = function(hostMetrics) { - var viewSize = hostMetrics.viewWidth - 1; - if (this.pair_) { - // Shorten the scrollbar to make room for the corner square. - viewSize -= Blockly.Scrollbar.scrollbarThickness; - } - this.setScrollViewSize_(Math.max(0, viewSize)); - - var xCoordinate = hostMetrics.absoluteLeft + 0.5; - if (this.pair_ && this.workspace_.RTL) { - xCoordinate += Blockly.Scrollbar.scrollbarThickness; - } - - // Horizontal toolbar should always be just above the bottom of the workspace. - var yCoordinate = hostMetrics.absoluteTop + hostMetrics.viewHeight - - Blockly.Scrollbar.scrollbarThickness - 0.5; - this.setPosition_(xCoordinate, yCoordinate); - - // If the view has been resized, a content resize will also be necessary. The - // reverse is not true. - this.resizeContentHorizontal(hostMetrics); -}; - -/** - * Recalculate a horizontal scrollbar's location within its path and length. - * This should be called when the contents of the workspace have changed. - * @param {!Object} hostMetrics A data structure describing all the - * required dimensions, possibly fetched from the host object. - */ -Blockly.Scrollbar.prototype.resizeContentHorizontal = function(hostMetrics) { - if (!this.pair_) { - // Only show the scrollbar if needed. - // Ideally this would also apply to scrollbar pairs, but that's a bigger - // headache (due to interactions with the corner square). - this.setVisible(this.scrollViewSize_ < hostMetrics.contentWidth); - } - - this.ratio_ = this.scrollViewSize_ / hostMetrics.contentWidth; - if (this.ratio_ == -Infinity || this.ratio_ == Infinity || - isNaN(this.ratio_)) { - this.ratio_ = 0; - } - - var handleLength = hostMetrics.viewWidth * this.ratio_; - this.setHandleLength_(Math.max(0, handleLength)); - - var handlePosition = (hostMetrics.viewLeft - hostMetrics.contentLeft) * - this.ratio_; - this.setHandlePosition(this.constrainHandle_(handlePosition)); -}; - -/** - * Recalculate a vertical scrollbar's location and length. - * @param {!Object} hostMetrics A data structure describing all the - * required dimensions, possibly fetched from the host object. - * @private - */ -Blockly.Scrollbar.prototype.resizeVertical_ = function(hostMetrics) { - // TODO: Inspect metrics to determine if we can get away with just a content - // resize. - this.resizeViewVertical(hostMetrics); -}; - -/** - * Recalculate a vertical scrollbar's location on the screen and path length. - * This should be called when the layout or size of the window has changed. - * @param {!Object} hostMetrics A data structure describing all the - * required dimensions, possibly fetched from the host object. - */ -Blockly.Scrollbar.prototype.resizeViewVertical = function(hostMetrics) { - var viewSize = hostMetrics.viewHeight - 1; - if (this.pair_) { - // Shorten the scrollbar to make room for the corner square. - viewSize -= Blockly.Scrollbar.scrollbarThickness; - } - this.setScrollViewSize_(Math.max(0, viewSize)); - - var xCoordinate = hostMetrics.absoluteLeft + 0.5; - if (!this.workspace_.RTL) { - xCoordinate += hostMetrics.viewWidth - - Blockly.Scrollbar.scrollbarThickness - 1; - } - var yCoordinate = hostMetrics.absoluteTop + 0.5; - this.setPosition_(xCoordinate, yCoordinate); - - // If the view has been resized, a content resize will also be necessary. The - // reverse is not true. - this.resizeContentVertical(hostMetrics); -}; - -/** - * Recalculate a vertical scrollbar's location within its path and length. - * This should be called when the contents of the workspace have changed. - * @param {!Object} hostMetrics A data structure describing all the - * required dimensions, possibly fetched from the host object. - */ -Blockly.Scrollbar.prototype.resizeContentVertical = function(hostMetrics) { - if (!this.pair_) { - // Only show the scrollbar if needed. - this.setVisible(this.scrollViewSize_ < hostMetrics.contentHeight); - } - - this.ratio_ = this.scrollViewSize_ / hostMetrics.contentHeight; - if (this.ratio_ == -Infinity || this.ratio_ == Infinity || - isNaN(this.ratio_)) { - this.ratio_ = 0; - } - - var handleLength = hostMetrics.viewHeight * this.ratio_; - this.setHandleLength_(Math.max(0, handleLength)); - - var handlePosition = (hostMetrics.viewTop - hostMetrics.contentTop) * - this.ratio_; - this.setHandlePosition(this.constrainHandle_(handlePosition)); -}; - -/** - * Create all the DOM elements required for a scrollbar. - * The resulting widget is not sized. - * @param {string=} opt_class A class to be applied to this scrollbar. - * @private - */ -Blockly.Scrollbar.prototype.createDom_ = function(opt_class) { - /* Create the following DOM: - - - - - - - */ - var className = 'blocklyScrollbar' + - (this.horizontal_ ? 'Horizontal' : 'Vertical'); - if (opt_class) { - className += ' ' + opt_class; - } - this.outerSvg_ = Blockly.utils.createSvgElement( - 'svg', {'class': className}, null); - this.svgGroup_ = Blockly.utils.createSvgElement('g', {}, this.outerSvg_); - this.svgBackground_ = Blockly.utils.createSvgElement( - 'rect', {'class': 'blocklyScrollbarBackground'}, this.svgGroup_); - var radius = Math.floor((Blockly.Scrollbar.scrollbarThickness - 5) / 2); - this.svgHandle_ = Blockly.utils.createSvgElement( - 'rect', - { - 'class': 'blocklyScrollbarHandle', - 'rx': radius, - 'ry': radius - }, - this.svgGroup_); - Blockly.utils.insertAfter(this.outerSvg_, this.workspace_.getParentSvg()); -}; - -/** - * Is the scrollbar visible. Non-paired scrollbars disappear when they aren't - * needed. - * @return {boolean} True if visible. - */ -Blockly.Scrollbar.prototype.isVisible = function() { - return this.isVisible_; -}; - -/** - * Set whether the scrollbar's container is visible and update - * display accordingly if visibility has changed. - * @param {boolean} visible Whether the container is visible - */ -Blockly.Scrollbar.prototype.setContainerVisible = function(visible) { - var visibilityChanged = (visible != this.containerVisible_); - - this.containerVisible_ = visible; - if (visibilityChanged) { - this.updateDisplay_(); - } -}; - -/** - * Set whether the scrollbar is visible. - * Only applies to non-paired scrollbars. - * @param {boolean} visible True if visible. - */ -Blockly.Scrollbar.prototype.setVisible = function(visible) { - var visibilityChanged = (visible != this.isVisible()); - - // Ideally this would also apply to scrollbar pairs, but that's a bigger - // headache (due to interactions with the corner square). - if (this.pair_) { - throw 'Unable to toggle visibility of paired scrollbars.'; - } - this.isVisible_ = visible; - if (visibilityChanged) { - this.updateDisplay_(); - } -}; - -/** - * Update visibility of scrollbar based on whether it thinks it should - * be visible and whether its containing workspace is visible. - * We cannot rely on the containing workspace being hidden to hide us - * because it is not necessarily our parent in the DOM. - */ -Blockly.Scrollbar.prototype.updateDisplay_ = function() { - var show = true; - // Check whether our parent/container is visible. - if (!this.containerVisible_) { - show = false; - } else { - show = this.isVisible(); - } - if (show) { - this.outerSvg_.setAttribute('display', 'block'); - } else { - this.outerSvg_.setAttribute('display', 'none'); - } -}; - -/** - * Scroll by one pageful. - * Called when scrollbar background is clicked. - * @param {!Event} e Mouse down event. - * @private - */ -Blockly.Scrollbar.prototype.onMouseDownBar_ = function(e) { - this.workspace_.markFocused(); - Blockly.Touch.clearTouchIdentifier(); // This is really a click. - this.cleanUp_(); - if (Blockly.utils.isRightButton(e)) { - // Right-click. - // Scrollbars have no context menu. - e.stopPropagation(); - return; - } - var mouseXY = Blockly.utils.mouseToSvg(e, this.workspace_.getParentSvg(), - this.workspace_.getInverseScreenCTM()); - var mouseLocation = this.horizontal_ ? mouseXY.x : mouseXY.y; - - var handleXY = Blockly.utils.getInjectionDivXY_(this.svgHandle_); - var handleStart = this.horizontal_ ? handleXY.x : handleXY.y; - var handlePosition = this.handlePosition_; - - var pageLength = this.handleLength_ * 0.95; - if (mouseLocation <= handleStart) { - // Decrease the scrollbar's value by a page. - handlePosition -= pageLength; - } else if (mouseLocation >= handleStart + this.handleLength_) { - // Increase the scrollbar's value by a page. - handlePosition += pageLength; - } - // When the scrollbars are clicked, hide the WidgetDiv/DropDownDiv without - // animation in anticipation of a workspace move. - Blockly.WidgetDiv.hide(true); - Blockly.DropDownDiv.hideWithoutAnimation(); - - this.setHandlePosition(this.constrainHandle_(handlePosition)); - this.onScroll_(); - e.stopPropagation(); - e.preventDefault(); -}; - -/** - * Start a dragging operation. - * Called when scrollbar handle is clicked. - * @param {!Event} e Mouse down event. - * @private - */ -Blockly.Scrollbar.prototype.onMouseDownHandle_ = function(e) { - this.workspace_.markFocused(); - this.cleanUp_(); - if (Blockly.utils.isRightButton(e)) { - // Right-click. - // Scrollbars have no context menu. - e.stopPropagation(); - return; - } - // Look up the current translation and record it. - this.startDragHandle = this.handlePosition_; - - // Tell the workspace to setup its drag surface since it is about to move. - // onMouseMoveHandle will call onScroll which actually tells the workspace - // to move. - this.workspace_.setupDragSurface(); - - // Record the current mouse position. - this.startDragMouse_ = this.horizontal_ ? e.clientX : e.clientY; - Blockly.Scrollbar.onMouseUpWrapper_ = Blockly.bindEventWithChecks_(document, - 'mouseup', this, this.onMouseUpHandle_); - Blockly.Scrollbar.onMouseMoveWrapper_ = Blockly.bindEventWithChecks_(document, - 'mousemove', this, this.onMouseMoveHandle_); - // When the scrollbars are clicked, hide the WidgetDiv/DropDownDiv without - // animation in anticipation of a workspace move. - Blockly.WidgetDiv.hide(true); - Blockly.DropDownDiv.hideWithoutAnimation(); - - e.stopPropagation(); - e.preventDefault(); -}; - -/** - * Drag the scrollbar's handle. - * @param {!Event} e Mouse up event. - * @private - */ -Blockly.Scrollbar.prototype.onMouseMoveHandle_ = function(e) { - var currentMouse = this.horizontal_ ? e.clientX : e.clientY; - var mouseDelta = currentMouse - this.startDragMouse_; - var handlePosition = this.startDragHandle + mouseDelta; - // Position the bar. - this.setHandlePosition(this.constrainHandle_(handlePosition)); - this.onScroll_(); -}; - -/** - * Release the scrollbar handle and reset state accordingly. - * @private - */ -Blockly.Scrollbar.prototype.onMouseUpHandle_ = function() { - // Tell the workspace to clean up now that the workspace is done moving. - this.workspace_.resetDragSurface(); - Blockly.Touch.clearTouchIdentifier(); - this.cleanUp_(); -}; - -/** - * Hide chaff and stop binding to mouseup and mousemove events. Call this to - * wrap up lose ends associated with the scrollbar. - * @private - */ -Blockly.Scrollbar.prototype.cleanUp_ = function() { - Blockly.hideChaff(true); - if (Blockly.Scrollbar.onMouseUpWrapper_) { - Blockly.unbindEvent_(Blockly.Scrollbar.onMouseUpWrapper_); - Blockly.Scrollbar.onMouseUpWrapper_ = null; - } - if (Blockly.Scrollbar.onMouseMoveWrapper_) { - Blockly.unbindEvent_(Blockly.Scrollbar.onMouseMoveWrapper_); - Blockly.Scrollbar.onMouseMoveWrapper_ = null; - } -}; - -/** - * Constrain the handle's position within the minimum (0) and maximum - * (length of scrollbar) values allowed for the scrollbar. - * @param {number} value Value that is potentially out of bounds, in CSS pixels. - * @return {number} Constrained value, in CSS pixels. - * @private - */ -Blockly.Scrollbar.prototype.constrainHandle_ = function(value) { - if (value <= 0 || isNaN(value) || this.scrollViewSize_ < this.handleLength_) { - value = 0; - } else { - value = Math.min(value, this.scrollViewSize_ - this.handleLength_); - } - return value; -}; - -/** - * Called when scrollbar is moved. - * @private - */ -Blockly.Scrollbar.prototype.onScroll_ = function() { - var ratio = this.handlePosition_ / this.scrollViewSize_; - if (isNaN(ratio)) { - ratio = 0; - } - var xyRatio = {}; - if (this.horizontal_) { - xyRatio.x = ratio; - } else { - xyRatio.y = ratio; - } - this.workspace_.setMetrics(xyRatio); -}; - -/** - * Set the scrollbar handle's position. - * @param {number} value The distance from the top/left end of the bar, in CSS - * pixels. It may be larger than the maximum allowable position of the - * scrollbar handle. - */ -Blockly.Scrollbar.prototype.set = function(value) { - this.setHandlePosition(this.constrainHandle_(value * this.ratio_)); - this.onScroll_(); -}; - -/** - * Record the origin of the workspace that the scrollbar is in, in pixels - * relative to the injection div origin. This is for times when the scrollbar is - * used in an object whose origin isn't the same as the main workspace - * (e.g. in a flyout.) - * @param {number} x The x coordinate of the scrollbar's origin, in CSS pixels. - * @param {number} y The y coordinate of the scrollbar's origin, in CSS pixels. - */ -Blockly.Scrollbar.prototype.setOrigin = function(x, y) { - if (x != this.origin_.x || y != this.origin_.y) { - this.origin_ = new goog.math.Coordinate(x, y); - this.originHasChanged_ = true; - } -}; diff --git a/core/toolbox.js b/core/toolbox.js deleted file mode 100644 index 67fcb0338d..0000000000 --- a/core/toolbox.js +++ /dev/null @@ -1,801 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2011 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Toolbox from whence to create blocks. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.Toolbox'); - -goog.require('Blockly.Events.Ui'); -goog.require('Blockly.HorizontalFlyout'); -goog.require('Blockly.Touch'); -goog.require('Blockly.VerticalFlyout'); -goog.require('goog.dom'); -goog.require('goog.dom.TagName'); -goog.require('goog.events'); -goog.require('goog.events.BrowserFeature'); -goog.require('goog.html.SafeHtml'); -goog.require('goog.html.SafeStyle'); -goog.require('goog.math.Rect'); -goog.require('goog.style'); -goog.require('goog.ui.tree.TreeControl'); -goog.require('goog.ui.tree.TreeNode'); - - -/** - * Class for a Toolbox. - * Creates the toolbox's DOM. - * @param {!Blockly.Workspace} workspace The workspace in which to create new - * blocks. - * @constructor - */ -Blockly.Toolbox = function(workspace) { - /** - * @type {!Blockly.Workspace} - * @private - */ - this.workspace_ = workspace; - - /** - * Whether toolbox categories should be represented by icons instead of text. - * @type {boolean} - * @private - */ - this.iconic_ = false; - - /** - * Is RTL vs LTR. - * @type {boolean} - */ - this.RTL = workspace.options.RTL; - - /** - * Whether the toolbox should be laid out horizontally. - * @type {boolean} - * @private - */ - this.horizontalLayout_ = workspace.options.horizontalLayout; - - /** - * Position of the toolbox and flyout relative to the workspace. - * @type {number} - */ - this.toolboxPosition = workspace.options.toolboxPosition; - -}; - -/** - * Width of the toolbox, which changes only in vertical layout. - * This is the sum of the width of the flyout (250) and the category menu (60). - * @type {number} - */ -Blockly.Toolbox.prototype.width = 310; - -/** - * Height of the toolbox, which changes only in horizontal layout. - * @type {number} - */ -Blockly.Toolbox.prototype.height = 0; - -Blockly.Toolbox.prototype.selectedItem_ = null; - -/** - * Initializes the toolbox. - */ -Blockly.Toolbox.prototype.init = function() { - var workspace = this.workspace_; - var svg = this.workspace_.getParentSvg(); - - /** - * HTML container for the Toolbox menu. - * @type {Element} - */ - this.HtmlDiv = - goog.dom.createDom(goog.dom.TagName.DIV, 'blocklyToolboxDiv'); - this.HtmlDiv.setAttribute('dir', workspace.RTL ? 'RTL' : 'LTR'); - svg.parentNode.insertBefore(this.HtmlDiv, svg); - - // Clicking on toolbox closes popups. - Blockly.bindEventWithChecks_(this.HtmlDiv, 'mousedown', this, - function(e) { - // Cancel any gestures in progress. - this.workspace_.cancelCurrentGesture(); - if (Blockly.utils.isRightButton(e) || e.target == this.HtmlDiv) { - // Close flyout. - Blockly.hideChaff(false); - } else { - // Just close popups. - Blockly.hideChaff(true); - } - Blockly.Touch.clearTouchIdentifier(); // Don't block future drags. - }, /*opt_noCaptureIdentifier*/ false, /*opt_noPreventDefault*/ true); - - this.createFlyout_(); - this.categoryMenu_ = new Blockly.Toolbox.CategoryMenu(this, this.HtmlDiv); - this.populate_(workspace.options.languageTree); - this.position(); -}; - -/** - * Dispose of this toolbox. - */ -Blockly.Toolbox.prototype.dispose = function() { - this.flyout_.dispose(); - this.categoryMenu_.dispose(); - this.categoryMenu_ = null; - goog.dom.removeNode(this.HtmlDiv); - this.workspace_ = null; - this.lastCategory_ = null; -}; - -/** - * Create and configure a flyout based on the main workspace's options. - * @private - */ -Blockly.Toolbox.prototype.createFlyout_ = function() { - var workspace = this.workspace_; - - var options = { - disabledPatternId: workspace.options.disabledPatternId, - parentWorkspace: workspace, - RTL: workspace.RTL, - oneBasedIndex: workspace.options.oneBasedIndex, - horizontalLayout: workspace.horizontalLayout, - toolboxPosition: workspace.options.toolboxPosition, - stackGlowFilterId: workspace.options.stackGlowFilterId - }; - - if (workspace.horizontalLayout) { - this.flyout_ = new Blockly.HorizontalFlyout(options); - } else { - this.flyout_ = new Blockly.VerticalFlyout(options); - } - this.flyout_.setParentToolbox(this); - - goog.dom.insertSiblingAfter( - this.flyout_.createDom('svg'), this.workspace_.getParentSvg()); - this.flyout_.init(workspace); -}; - -/** - * Fill the toolbox with categories and blocks. - * @param {!Node} newTree DOM tree of blocks. - * @private - */ -Blockly.Toolbox.prototype.populate_ = function(newTree) { - this.categoryMenu_.populate(newTree); - this.showAll_(); - this.setSelectedItem(this.categoryMenu_.categories_[0], false); -}; - -/** - * Show all blocks for all categories in the flyout - * @private - */ -Blockly.Toolbox.prototype.showAll_ = function() { - var allContents = []; - for (var i = 0; i < this.categoryMenu_.categories_.length; i++) { - var category = this.categoryMenu_.categories_[i]; - - // create a label node to go at the top of the category - var labelString = ''; - var labelXML = Blockly.Xml.textToDom(labelString); - - allContents.push(labelXML.firstChild); - - allContents = allContents.concat(category.getContents()); - } - this.flyout_.show(allContents); -}; - -/** - * Get the width of the toolbox. - * @return {number} The width of the toolbox. - */ -Blockly.Toolbox.prototype.getWidth = function() { - return this.width; -}; - -/** - * Get the height of the toolbox, not including the block menu. - * @return {number} The height of the toolbox. - */ -Blockly.Toolbox.prototype.getHeight = function() { - return this.categoryMenu_ ? this.categoryMenu_.getHeight() : 0; -}; - -/** - * Move the toolbox to the edge. - */ -Blockly.Toolbox.prototype.position = function() { - var treeDiv = this.HtmlDiv; - if (!treeDiv) { - // Not initialized yet. - return; - } - var svg = this.workspace_.getParentSvg(); - var svgSize = Blockly.svgSize(svg); - if (this.horizontalLayout_) { - treeDiv.style.left = '0'; - treeDiv.style.height = 'auto'; - treeDiv.style.width = svgSize.width + 'px'; - this.height = treeDiv.offsetHeight; - if (this.toolboxPosition == Blockly.TOOLBOX_AT_TOP) { // Top - treeDiv.style.top = '0'; - } else { // Bottom - treeDiv.style.bottom = '0'; - } - } else { - if (this.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { // Right - treeDiv.style.right = '0'; - } else { // Left - treeDiv.style.left = '0'; - } - treeDiv.style.height = '100%'; - } - this.flyout_.position(); -}; - -/** - * Unhighlight any previously specified option. - */ -Blockly.Toolbox.prototype.clearSelection = function() { - this.setSelectedItem(null); -}; - -/** - * Adds a style on the toolbox. Usually used to change the cursor. - * @param {string} style The name of the class to add. - * @package - */ -Blockly.Toolbox.prototype.addStyle = function(style) { - Blockly.utils.addClass(/** @type {!Element} */ (this.HtmlDiv), style); -}; - -/** - * Removes a style from the toolbox. Usually used to change the cursor. - * @param {string} style The name of the class to remove. - * @package - */ -Blockly.Toolbox.prototype.removeStyle = function(style) { - Blockly.utils.removeClass(/** @type {!Element} */ (this.HtmlDiv), style); -}; - -/** - * Return the deletion rectangle for this toolbox. - * @return {goog.math.Rect} Rectangle in which to delete. - */ -Blockly.Toolbox.prototype.getClientRect = function() { - if (!this.HtmlDiv) { - return null; - } - - // If not an auto closing flyout, always use the (larger) flyout client rect - if (!this.flyout_.autoClose) { - return this.flyout_.getClientRect(); - } - - // BIG_NUM is offscreen padding so that blocks dragged beyond the toolbox - // area are still deleted. Must be smaller than Infinity, but larger than - // the largest screen size. - var BIG_NUM = 10000000; - var toolboxRect = this.HtmlDiv.getBoundingClientRect(); - - var x = toolboxRect.left; - var y = toolboxRect.top; - var width = toolboxRect.width; - var height = toolboxRect.height; - - // Assumes that the toolbox is on the SVG edge. If this changes - // (e.g. toolboxes in mutators) then this code will need to be more complex. - if (this.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) { - return new goog.math.Rect(-BIG_NUM, -BIG_NUM, BIG_NUM + x + width, - 2 * BIG_NUM); - } else if (this.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { - return new goog.math.Rect(toolboxRect.right - width, -BIG_NUM, BIG_NUM + width, 2 * BIG_NUM); - } else if (this.toolboxPosition == Blockly.TOOLBOX_AT_TOP) { - return new goog.math.Rect(-BIG_NUM, -BIG_NUM, 2 * BIG_NUM, - BIG_NUM + y + height); - } else { // Bottom - return new goog.math.Rect(0, y, 2 * BIG_NUM, BIG_NUM); - } -}; - -/** - * Update the flyout's contents without closing it. Should be used in response - * to a change in one of the dynamic categories, such as variables or - * procedures. - */ -Blockly.Toolbox.prototype.refreshSelection = function() { - this.showAll_(); -}; - -/** - * @return {Blockly.Toolbox.Category} the currently selected category. - */ -Blockly.Toolbox.prototype.getSelectedItem = function() { - return this.selectedItem_; -}; - -/** - * @return {string} The name of the currently selected category. - */ -Blockly.Toolbox.prototype.getSelectedCategoryName = function() { - return this.selectedItem_.name_; -}; - -/** - * @return {string} The id of the currently selected category. - * @public - */ -Blockly.Toolbox.prototype.getSelectedCategoryId = function() { - return this.selectedItem_.id_; -}; - -/** - * @return {number} The distance flyout is scrolled below the top of the currently - * selected category. - */ -Blockly.Toolbox.prototype.getCategoryScrollOffset = function() { - var categoryPos = this.getCategoryPositionById(this.getSelectedCategoryId()); - return this.flyout_.getScrollPos() - categoryPos; -}; - -/** - * Get the position of a category by name. - * @param {string} name The name of the category. - * @return {number} The position of the category. - */ -Blockly.Toolbox.prototype.getCategoryPositionByName = function(name) { - var scrollPositions = this.flyout_.categoryScrollPositions; - for (var i = 0; i < scrollPositions.length; i++) { - if (name === scrollPositions[i].categoryName) { - return scrollPositions[i].position; - } - } -}; - -/** - * Get the position of a category by id. - * @param {string} id The id of the category. - * @return {number} The position of the category. - * @public - */ -Blockly.Toolbox.prototype.getCategoryPositionById = function(id) { - var scrollPositions = this.flyout_.categoryScrollPositions; - for (var i = 0; i < scrollPositions.length; i++) { - if (id === scrollPositions[i].categoryId) { - return scrollPositions[i].position; - } - } -}; - -/** - * Get the length of a category by name. - * @param {string} name The name of the category. - * @return {number} The length of the category. - */ -Blockly.Toolbox.prototype.getCategoryLengthByName = function(name) { - var scrollPositions = this.flyout_.categoryScrollPositions; - for (var i = 0; i < scrollPositions.length; i++) { - if (name === scrollPositions[i].categoryName) { - return scrollPositions[i].length; - } - } -}; - -/** - * Get the length of a category by id. - * @param {string} id The id of the category. - * @return {number} The length of the category. - * @public - */ -Blockly.Toolbox.prototype.getCategoryLengthById = function(id) { - var scrollPositions = this.flyout_.categoryScrollPositions; - for (var i = 0; i < scrollPositions.length; i++) { - if (id === scrollPositions[i].categoryId) { - return scrollPositions[i].length; - } - } -}; - -/** - * Set the scroll position of the flyout. - * @param {number} pos The position to set. - */ -Blockly.Toolbox.prototype.setFlyoutScrollPos = function(pos) { - this.flyout_.setScrollPos(pos); -}; - - -/** - * Set the currently selected category. - * @param {Blockly.Toolbox.Category} item The category to select. - * @param {boolean=} opt_shouldScroll Whether to scroll to the selected category. Defaults to true. - */ -Blockly.Toolbox.prototype.setSelectedItem = function(item, opt_shouldScroll) { - if (typeof opt_shouldScroll === 'undefined') { - opt_shouldScroll = true; - } - if (this.selectedItem_) { - // They selected a different category but one was already open. Close it. - this.selectedItem_.setSelected(false); - } - this.selectedItem_ = item; - if (this.selectedItem_ != null) { - this.selectedItem_.setSelected(true); - // Scroll flyout to the top of the selected category - var categoryId = item.id_; - if (opt_shouldScroll) { - this.scrollToCategoryById(categoryId); - } - } -}; - -/** - * Select and scroll to a category by name. - * @param {string} name The name of the category to select and scroll to. - */ -Blockly.Toolbox.prototype.setSelectedCategoryByName = function(name) { - this.selectCategoryByName(name); - this.scrollToCategoryByName(name); -}; - -/** - * Select and scroll to a category by id. - * @param {string} id The id of the category to select and scroll to. - * @public - */ -Blockly.Toolbox.prototype.setSelectedCategoryById = function(id) { - this.selectCategoryById(id); - this.scrollToCategoryById(id); -}; - -/** - * Scroll to a category by name. - * @param {string} name The name of the category to scroll to. - * @package - */ -Blockly.Toolbox.prototype.scrollToCategoryByName = function(name) { - var scrollPositions = this.flyout_.categoryScrollPositions; - for (var i = 0; i < scrollPositions.length; i++) { - if (name === scrollPositions[i].categoryName) { - this.flyout_.setVisible(true); - this.flyout_.scrollTo(scrollPositions[i].position); - return; - } - } -}; - -/** - * Scroll to a category by id. - * @param {string} id The id of the category to scroll to. - * @public - */ -Blockly.Toolbox.prototype.scrollToCategoryById = function(id) { - var scrollPositions = this.flyout_.categoryScrollPositions; - for (var i = 0; i < scrollPositions.length; i++) { - if (id === scrollPositions[i].categoryId) { - this.flyout_.setVisible(true); - this.flyout_.scrollTo(scrollPositions[i].position); - return; - } - } -}; - -/** - * Get a category by its index. - * @param {number} index The index of the category. - * @return {Blockly.Toolbox.Category} the category, or null if there are no categories. - * @package - */ -Blockly.Toolbox.prototype.getCategoryByIndex = function(index) { - if (!this.categoryMenu_.categories_) return null; - return this.categoryMenu_.categories_[index]; -}; - -/** - * Select a category by name. - * @param {string} name The name of the category to select. - * @package - */ -Blockly.Toolbox.prototype.selectCategoryByName = function(name) { - for (var i = 0; i < this.categoryMenu_.categories_.length; i++) { - var category = this.categoryMenu_.categories_[i]; - if (name === category.name_) { - this.selectedItem_.setSelected(false); - this.selectedItem_ = category; - this.selectedItem_.setSelected(true); - } - } -}; - -/** - * Select a category by id. - * @param {string} id The id of the category to select. - * @package - */ -Blockly.Toolbox.prototype.selectCategoryById = function(id) { - for (var i = 0; i < this.categoryMenu_.categories_.length; i++) { - var category = this.categoryMenu_.categories_[i]; - if (id === category.id_) { - this.selectedItem_.setSelected(false); - this.selectedItem_ = category; - this.selectedItem_.setSelected(true); - } - } -}; - -/** - * Wrapper function for calling setSelectedItem from a touch handler. - * @param {Blockly.Toolbox.Category} item The category to select. - * @return {function} A function that can be passed to bindEvent. - */ -Blockly.Toolbox.prototype.setSelectedItemFactory = function(item) { - var selectedItem = item; - return function() { - if (!this.workspace_.isDragging()) { - this.setSelectedItem(selectedItem); - Blockly.Touch.clearTouchIdentifier(); - } - }; -}; - -// Category menu -/** - * Class for a table of category titles that will control which category is - * displayed. - * @param {Blockly.Toolbox} parent The toolbox that owns the category menu. - * @param {Element} parentHtml The containing html div. - * @constructor - */ -Blockly.Toolbox.CategoryMenu = function(parent, parentHtml) { - this.parent_ = parent; - this.height_ = 0; - this.parentHtml_ = parentHtml; - this.createDom(); - this.categories_ = []; -}; - -/** - * @return {number} the height of the category menu. - */ -Blockly.Toolbox.CategoryMenu.prototype.getHeight = function() { - return this.height_; -}; - -/** - * Create the DOM for the category menu. - */ -Blockly.Toolbox.CategoryMenu.prototype.createDom = function() { - this.table = goog.dom.createDom('div', this.parent_.horizontalLayout_ ? - 'scratchCategoryMenuHorizontal' : 'scratchCategoryMenu'); - this.parentHtml_.appendChild(this.table); -}; - -/** - * Fill the toolbox with categories and blocks by creating a new - * {Blockly.Toolbox.Category} for every category tag in the toolbox xml. - * @param {Node} domTree DOM tree of blocks, or null. - */ -Blockly.Toolbox.CategoryMenu.prototype.populate = function(domTree) { - if (!domTree) { - return; - } - - // Remove old categories - this.dispose(); - this.createDom(); - var categories = []; - // Find actual categories from the DOM tree. - for (var i = 0, child; child = domTree.childNodes[i]; i++) { - if (!child.tagName || child.tagName.toUpperCase() != 'CATEGORY') { - continue; - } - categories.push(child); - } - - // Create a single column of categories - for (var i = 0; i < categories.length; i++) { - var child = categories[i]; - var row = goog.dom.createDom('div', 'scratchCategoryMenuRow'); - this.table.appendChild(row); - if (child) { - this.categories_.push(new Blockly.Toolbox.Category(this, row, - child)); - } - } - this.height_ = this.table.offsetHeight; -}; - -/** - * Dispose of this Category Menu and all of its children. - */ -Blockly.Toolbox.CategoryMenu.prototype.dispose = function() { - for (var i = 0, category; category = this.categories_[i]; i++) { - category.dispose(); - } - this.categories_ = []; - if (this.table) { - goog.dom.removeNode(this.table); - this.table = null; - } -}; - - -// Category -/** - * Class for the data model of a category in the toolbox. - * @param {Blockly.Toolbox.CategoryMenu} parent The category menu that owns this - * category. - * @param {Element} parentHtml The containing html div. - * @param {Node} domTree DOM tree of blocks. - * @constructor - */ -Blockly.Toolbox.Category = function(parent, parentHtml, domTree) { - this.parent_ = parent; - this.parentHtml_ = parentHtml; - this.name_ = domTree.getAttribute('name'); - this.id_ = domTree.getAttribute('id'); - this.setColour(domTree); - this.custom_ = domTree.getAttribute('custom'); - this.iconURI_ = domTree.getAttribute('iconURI'); - this.showStatusButton_ = domTree.getAttribute('showStatusButton'); - this.contents_ = []; - if (!this.custom_) { - this.parseContents_(domTree); - } - this.createDom(); -}; - -/** - * Dispose of this category and all of its contents. - */ -Blockly.Toolbox.Category.prototype.dispose = function() { - if (this.item_) { - goog.dom.removeNode(this.item_); - this.item = null; - } - this.parent_ = null; - this.parentHtml_ = null; - this.contents_ = null; -}; - -/** - * Used to determine the css classes for the menu item for this category - * based on its current state. - * @private - * @param {boolean=} selected Indication whether the category is currently selected. - * @return {string} The css class names to be applied, space-separated. - */ -Blockly.Toolbox.Category.prototype.getMenuItemClassName_ = function(selected) { - var classNames = [ - 'scratchCategoryMenuItem', - 'scratchCategoryId-' + this.id_, - ]; - if (selected) { - classNames.push('categorySelected'); - } - return classNames.join(' '); -}; - -/** - * Create the DOM for a category in the toolbox. - */ -Blockly.Toolbox.Category.prototype.createDom = function() { - var toolbox = this.parent_.parent_; - this.item_ = goog.dom.createDom('div', - {'class': this.getMenuItemClassName_()}); - this.label_ = goog.dom.createDom('div', - {'class': 'scratchCategoryMenuItemLabel'}, - Blockly.utils.replaceMessageReferences(this.name_)); - if (this.iconURI_) { - this.bubble_ = goog.dom.createDom('div', - {'class': 'scratchCategoryItemIcon'}); - this.bubble_.style.backgroundImage = 'url(' + this.iconURI_ + ')'; - } else { - this.bubble_ = goog.dom.createDom('div', - {'class': 'scratchCategoryItemBubble'}); - this.bubble_.style.backgroundColor = this.colour_; - this.bubble_.style.borderColor = this.secondaryColour_; - } - this.item_.appendChild(this.bubble_); - this.item_.appendChild(this.label_); - this.parentHtml_.appendChild(this.item_); - Blockly.bindEvent_( - this.item_, 'mouseup', toolbox, toolbox.setSelectedItemFactory(this)); -}; - -/** - * Set the selected state of this category. - * @param {boolean} selected Whether this category is selected. - */ -Blockly.Toolbox.Category.prototype.setSelected = function(selected) { - this.item_.className = this.getMenuItemClassName_(selected); -}; - -/** - * Set the contents of this category from DOM. - * @param {Node} domTree DOM tree of blocks. - * @constructor - */ -Blockly.Toolbox.Category.prototype.parseContents_ = function(domTree) { - for (var i = 0, child; child = domTree.childNodes[i]; i++) { - if (!child.tagName) { - // Skip - continue; - } - switch (child.tagName.toUpperCase()) { - case 'BLOCK': - case 'SHADOW': - case 'LABEL': - case 'BUTTON': - case 'SEP': - case 'TEXT': - this.contents_.push(child); - break; - default: - break; - } - } -}; - -/** - * Get the contents of this category. - * @return {!Array|string} xmlList List of blocks to show, or a string with the - * name of a custom category. - */ -Blockly.Toolbox.Category.prototype.getContents = function() { - return this.custom_ ? this.custom_ : this.contents_; -}; - -/** - * Set the colour of the category's background from a DOM node. - * @param {Node} node DOM node with "colour" and "secondaryColour" attribute. - * Colours are a hex string or hue on a colour wheel (0-360). - */ -Blockly.Toolbox.Category.prototype.setColour = function(node) { - var colour = node.getAttribute('colour'); - var secondaryColour = node.getAttribute('secondaryColour'); - if (goog.isString(colour)) { - if (colour.match(/^#[0-9a-fA-F]{6}$/)) { - this.colour_ = colour; - } else { - this.colour_ = Blockly.hueToRgb(colour); - } - if (secondaryColour.match(/^#[0-9a-fA-F]{6}$/)) { - this.secondaryColour_ = secondaryColour; - } else { - this.secondaryColour_ = Blockly.hueToRgb(secondaryColour); - } - this.hasColours_ = true; - } else { - this.colour_ = '#000000'; - this.secondaryColour_ = '#000000'; - } -}; diff --git a/core/tooltip.js b/core/tooltip.js deleted file mode 100644 index 2e3d3da49f..0000000000 --- a/core/tooltip.js +++ /dev/null @@ -1,337 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2011 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Library to create tooltips for Blockly. - * First, call Blockly.Tooltip.init() after onload. - * Second, set the 'tooltip' property on any SVG element that needs a tooltip. - * If the tooltip is a string, then that message will be displayed. - * If the tooltip is an SVG element, then that object's tooltip will be used. - * Third, call Blockly.Tooltip.bindMouseEvents(e) passing the SVG element. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -/** - * @name Blockly.Tooltip - * @namespace - **/ -goog.provide('Blockly.Tooltip'); - -goog.require('goog.dom'); -goog.require('goog.dom.TagName'); - - -/** - * Is a tooltip currently showing? - */ -Blockly.Tooltip.visible = false; - -/** - * Is someone else blocking the tooltip from being shown? - * @type {boolean} - * @private - */ -Blockly.Tooltip.blocked_ = false; - -/** - * Maximum width (in characters) of a tooltip. - */ -Blockly.Tooltip.LIMIT = 50; - -/** - * PID of suspended thread to clear tooltip on mouse out. - * @private - */ -Blockly.Tooltip.mouseOutPid_ = 0; - -/** - * PID of suspended thread to show the tooltip. - * @private - */ -Blockly.Tooltip.showPid_ = 0; - -/** - * Last observed X location of the mouse pointer (freezes when tooltip appears). - * @private - */ -Blockly.Tooltip.lastX_ = 0; - -/** - * Last observed Y location of the mouse pointer (freezes when tooltip appears). - * @private - */ -Blockly.Tooltip.lastY_ = 0; - -/** - * Current element being pointed at. - * @private - */ -Blockly.Tooltip.element_ = null; - -/** - * Once a tooltip has opened for an element, that element is 'poisoned' and - * cannot respawn a tooltip until the pointer moves over a different element. - * @private - */ -Blockly.Tooltip.poisonedElement_ = null; - -/** - * Horizontal offset between mouse cursor and tooltip. - */ -Blockly.Tooltip.OFFSET_X = 0; - -/** - * Vertical offset between mouse cursor and tooltip. - */ -Blockly.Tooltip.OFFSET_Y = 10; - -/** - * Radius mouse can move before killing tooltip. - */ -Blockly.Tooltip.RADIUS_OK = 10; - -/** - * Delay before tooltip appears. - */ -Blockly.Tooltip.HOVER_MS = 750; - -/** - * Horizontal padding between tooltip and screen edge. - */ -Blockly.Tooltip.MARGINS = 5; - -/** - * The HTML container. Set once by Blockly.Tooltip.createDom. - * @type {Element} - */ -Blockly.Tooltip.DIV = null; - -/** - * Create the tooltip div and inject it onto the page. - */ -Blockly.Tooltip.createDom = function() { - if (Blockly.Tooltip.DIV) { - return; // Already created. - } - // Create an HTML container for popup overlays (e.g. editor widgets). - Blockly.Tooltip.DIV = - goog.dom.createDom(goog.dom.TagName.DIV, 'blocklyTooltipDiv'); - document.body.appendChild(Blockly.Tooltip.DIV); -}; - -/** - * Binds the required mouse events onto an SVG element. - * @param {!Element} element SVG element onto which tooltip is to be bound. - */ -Blockly.Tooltip.bindMouseEvents = function(element) { - Blockly.bindEvent_(element, 'mouseover', null, - Blockly.Tooltip.onMouseOver_); - Blockly.bindEvent_(element, 'mouseout', null, - Blockly.Tooltip.onMouseOut_); - - // Don't use bindEvent_ for mousemove since that would create a - // corresponding touch handler, even though this only makes sense in the - // context of a mouseover/mouseout. - element.addEventListener('mousemove', Blockly.Tooltip.onMouseMove_, false); -}; - -/** - * Hide the tooltip if the mouse is over a different object. - * Initialize the tooltip to potentially appear for this object. - * @param {!Event} e Mouse event. - * @private - */ -Blockly.Tooltip.onMouseOver_ = function(e) { - if (Blockly.Tooltip.blocked_) { - // Someone doesn't want us to show tooltips. - return; - } - // If the tooltip is an object, treat it as a pointer to the next object in - // the chain to look at. Terminate when a string or function is found. - var element = e.target; - while (!goog.isString(element.tooltip) && !goog.isFunction(element.tooltip)) { - element = element.tooltip; - } - if (Blockly.Tooltip.element_ != element) { - Blockly.Tooltip.hide(); - Blockly.Tooltip.poisonedElement_ = null; - Blockly.Tooltip.element_ = element; - } - // Forget about any immediately preceding mouseOut event. - clearTimeout(Blockly.Tooltip.mouseOutPid_); -}; - -/** - * Hide the tooltip if the mouse leaves the object and enters the workspace. - * @param {!Event} _e Mouse event. - * @private - */ -Blockly.Tooltip.onMouseOut_ = function(_e) { - if (Blockly.Tooltip.blocked_) { - // Someone doesn't want us to show tooltips. - return; - } - // Moving from one element to another (overlapping or with no gap) generates - // a mouseOut followed instantly by a mouseOver. Fork off the mouseOut - // event and kill it if a mouseOver is received immediately. - // This way the task only fully executes if mousing into the void. - Blockly.Tooltip.mouseOutPid_ = setTimeout(function() { - Blockly.Tooltip.element_ = null; - Blockly.Tooltip.poisonedElement_ = null; - Blockly.Tooltip.hide(); - }, 1); - clearTimeout(Blockly.Tooltip.showPid_); -}; - -/** - * When hovering over an element, schedule a tooltip to be shown. If a tooltip - * is already visible, hide it if the mouse strays out of a certain radius. - * @param {!Event} e Mouse event. - * @private - */ -Blockly.Tooltip.onMouseMove_ = function(e) { - if (!Blockly.Tooltip.element_ || !Blockly.Tooltip.element_.tooltip) { - // No tooltip here to show. - return; - } else if (Blockly.WidgetDiv.isVisible()) { - // Don't display a tooltip if a widget is open (tooltip would be under it). - return; - } else if (Blockly.Tooltip.blocked_) { - // Someone doesn't want us to show tooltips. We are probably handling a - // user gesture, such as a click or drag. - return; - } - if (Blockly.Tooltip.visible) { - // Compute the distance between the mouse position when the tooltip was - // shown and the current mouse position. Pythagorean theorem. - var dx = Blockly.Tooltip.lastX_ - e.pageX; - var dy = Blockly.Tooltip.lastY_ - e.pageY; - if (Math.sqrt(dx * dx + dy * dy) > Blockly.Tooltip.RADIUS_OK) { - Blockly.Tooltip.hide(); - } - } else if (Blockly.Tooltip.poisonedElement_ != Blockly.Tooltip.element_) { - // The mouse moved, clear any previously scheduled tooltip. - clearTimeout(Blockly.Tooltip.showPid_); - // Maybe this time the mouse will stay put. Schedule showing of tooltip. - Blockly.Tooltip.lastX_ = e.pageX; - Blockly.Tooltip.lastY_ = e.pageY; - Blockly.Tooltip.showPid_ = - setTimeout(Blockly.Tooltip.show_, Blockly.Tooltip.HOVER_MS); - } -}; - -/** - * Hide the tooltip. - */ -Blockly.Tooltip.hide = function() { - if (Blockly.Tooltip.visible) { - Blockly.Tooltip.visible = false; - if (Blockly.Tooltip.DIV) { - Blockly.Tooltip.DIV.style.display = 'none'; - } - } - if (Blockly.Tooltip.showPid_) { - clearTimeout(Blockly.Tooltip.showPid_); - } -}; - -/** - * Hide any in-progress tooltips and block showing new tooltips until the next - * call to unblock(). - * @package - */ -Blockly.Tooltip.block = function() { - Blockly.Tooltip.hide(); - Blockly.Tooltip.blocked_ = true; -}; - -/** - * Unblock tooltips: allow them to be scheduled and shown according to their own - * logic. - * @package - */ -Blockly.Tooltip.unblock = function() { - Blockly.Tooltip.blocked_ = false; -}; - -/** - * Create the tooltip and show it. - * @private - */ -Blockly.Tooltip.show_ = function() { - if (Blockly.Tooltip.blocked_) { - // Someone doesn't want us to show tooltips. - return; - } - Blockly.Tooltip.poisonedElement_ = Blockly.Tooltip.element_; - if (!Blockly.Tooltip.DIV) { - return; - } - // Erase all existing text. - goog.dom.removeChildren(/** @type {!Element} */ (Blockly.Tooltip.DIV)); - // Get the new text. - var tip = Blockly.Tooltip.element_.tooltip; - while (goog.isFunction(tip)) { - tip = tip(); - } - tip = Blockly.utils.wrap(tip, Blockly.Tooltip.LIMIT); - // Create new text, line by line. - var lines = tip.split('\n'); - for (var i = 0; i < lines.length; i++) { - var div = document.createElement('div'); - div.appendChild(document.createTextNode(lines[i])); - Blockly.Tooltip.DIV.appendChild(div); - } - var rtl = Blockly.Tooltip.element_.RTL; - var windowSize = goog.dom.getViewportSize(); - // Display the tooltip. - Blockly.Tooltip.DIV.style.direction = rtl ? 'rtl' : 'ltr'; - Blockly.Tooltip.DIV.style.display = 'block'; - Blockly.Tooltip.visible = true; - // Move the tooltip to just below the cursor. - var anchorX = Blockly.Tooltip.lastX_; - if (rtl) { - anchorX -= Blockly.Tooltip.OFFSET_X + Blockly.Tooltip.DIV.offsetWidth; - } else { - anchorX += Blockly.Tooltip.OFFSET_X; - } - var anchorY = Blockly.Tooltip.lastY_ + Blockly.Tooltip.OFFSET_Y; - - if (anchorY + Blockly.Tooltip.DIV.offsetHeight > - windowSize.height + window.scrollY) { - // Falling off the bottom of the screen; shift the tooltip up. - anchorY -= Blockly.Tooltip.DIV.offsetHeight + 2 * Blockly.Tooltip.OFFSET_Y; - } - if (rtl) { - // Prevent falling off left edge in RTL mode. - anchorX = Math.max(Blockly.Tooltip.MARGINS - window.scrollX, anchorX); - } else { - if (anchorX + Blockly.Tooltip.DIV.offsetWidth > - windowSize.width + window.scrollX - 2 * Blockly.Tooltip.MARGINS) { - // Falling off the right edge of the screen; - // clamp the tooltip on the edge. - anchorX = windowSize.width - Blockly.Tooltip.DIV.offsetWidth - - 2 * Blockly.Tooltip.MARGINS; - } - } - Blockly.Tooltip.DIV.style.top = anchorY + 'px'; - Blockly.Tooltip.DIV.style.left = anchorX + 'px'; -}; diff --git a/core/touch.js b/core/touch.js deleted file mode 100644 index debad6b9c4..0000000000 --- a/core/touch.js +++ /dev/null @@ -1,226 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Touch handling for Blockly. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -/** - * @name Blockly.Touch - * @namespace - **/ -goog.provide('Blockly.Touch'); - -goog.require('goog.events'); -goog.require('goog.events.BrowserFeature'); -goog.require('goog.string'); - - -/** - * Which touch events are we currently paying attention to? - * @type {DOMString} - * @private - */ -Blockly.Touch.touchIdentifier_ = null; - -/** - * The TOUCH_MAP lookup dictionary specifies additional touch events to fire, - * in conjunction with mouse events. - * @type {Object} - */ -Blockly.Touch.TOUCH_MAP = {}; -if (goog.events.BrowserFeature.TOUCH_ENABLED) { - Blockly.Touch.TOUCH_MAP = { - 'mousedown': ['touchstart'], - 'mousemove': ['touchmove'], - 'mouseup': ['touchend', 'touchcancel'] - }; -} - -/** - * PID of queued long-press task. - * @private - */ -Blockly.longPid_ = 0; - -/** - * Context menus on touch devices are activated using a long-press. - * Unfortunately the contextmenu touch event is currently (2015) only supported - * by Chrome. This function is fired on any touchstart event, queues a task, - * which after about a second opens the context menu. The tasks is killed - * if the touch event terminates early. - * @param {!Event} e Touch start event. - * @param {Blockly.Gesture} gesture The gesture that triggered this longStart. - * @private - */ -Blockly.longStart_ = function(e, gesture) { - Blockly.longStop_(); - // Punt on multitouch events. - if (e.changedTouches.length != 1) { - return; - } - Blockly.longPid_ = setTimeout(function() { - e.button = 2; // Simulate a right button click. - // e was a touch event. It needs to pretend to be a mouse event. - e.clientX = e.changedTouches[0].clientX; - e.clientY = e.changedTouches[0].clientY; - - // Let the gesture route the right-click correctly. - if (gesture) { - gesture.handleRightClick(e); - } - }, Blockly.LONGPRESS); -}; - -/** - * Nope, that's not a long-press. Either touchend or touchcancel was fired, - * or a drag hath begun. Kill the queued long-press task. - * @private - */ -Blockly.longStop_ = function() { - if (Blockly.longPid_) { - clearTimeout(Blockly.longPid_); - Blockly.longPid_ = 0; - } -}; - -/** - * Clear the touch identifier that tracks which touch stream to pay attention - * to. This ends the current drag/gesture and allows other pointers to be - * captured. - */ -Blockly.Touch.clearTouchIdentifier = function() { - Blockly.Touch.touchIdentifier_ = null; -}; - -/** - * Decide whether Blockly should handle or ignore this event. - * Mouse and touch events require special checks because we only want to deal - * with one touch stream at a time. All other events should always be handled. - * @param {!Event} e The event to check. - * @return {boolean} True if this event should be passed through to the - * registered handler; false if it should be blocked. - */ -Blockly.Touch.shouldHandleEvent = function(e) { - return !Blockly.Touch.isMouseOrTouchEvent(e) || - Blockly.Touch.checkTouchIdentifier(e); -}; - -/** - * Get the touch identifier from the given event. If it was a mouse event, the - * identifier is the string 'mouse'. - * @param {!Event} e Mouse event or touch event. - * @return {string} The touch identifier from the first changed touch, if - * defined. Otherwise 'mouse'. - */ -Blockly.Touch.getTouchIdentifierFromEvent = function(e) { - return (e.changedTouches && e.changedTouches[0] && - e.changedTouches[0].identifier != undefined && - e.changedTouches[0].identifier != null) ? - e.changedTouches[0].identifier : 'mouse'; -}; - -/** - * Check whether the touch identifier on the event matches the current saved - * identifier. If there is no identifier, that means it's a mouse event and - * we'll use the identifier "mouse". This means we won't deal well with - * multiple mice being used at the same time. That seems okay. - * If the current identifier was unset, save the identifier from the - * event. This starts a drag/gesture, during which touch events with other - * identifiers will be silently ignored. - * @param {!Event} e Mouse event or touch event. - * @return {boolean} Whether the identifier on the event matches the current - * saved identifier. - */ -Blockly.Touch.checkTouchIdentifier = function(e) { - var identifier = Blockly.Touch.getTouchIdentifierFromEvent(e); - - // if (Blockly.touchIdentifier_ )is insufficient because Android touch - // identifiers may be zero. - if (Blockly.Touch.touchIdentifier_ != undefined && - Blockly.Touch.touchIdentifier_ != null) { - // We're already tracking some touch/mouse event. Is this from the same - // source? - return Blockly.Touch.touchIdentifier_ == identifier; - } - if (e.type == 'mousedown' || e.type == 'touchstart') { - // No identifier set yet, and this is the start of a drag. Set it and - // return. - Blockly.Touch.touchIdentifier_ = identifier; - return true; - } - // There was no identifier yet, but this wasn't a start event so we're going - // to ignore it. This probably means that another drag finished while this - // pointer was down. - return false; -}; - -/** - * Set an event's clientX and clientY from its first changed touch. Use this to - * make a touch event work in a mouse event handler. - * @param {!Event} e A touch event. - */ -Blockly.Touch.setClientFromTouch = function(e) { - if (Blockly.utils.startsWith(e.type, 'touch')) { - // Map the touch event's properties to the event. - var touchPoint = e.changedTouches[0]; - e.clientX = touchPoint.clientX; - e.clientY = touchPoint.clientY; - } -}; - -/** - * Check whether a given event is a mouse or touch event. - * @param {!Event} e An event. - * @return {boolean} true if it is a mouse or touch event; false otherwise. - */ -Blockly.Touch.isMouseOrTouchEvent = function(e) { - return Blockly.utils.startsWith(e.type, 'touch') || - Blockly.utils.startsWith(e.type, 'mouse'); -}; - -/** - * Split an event into an array of events, one per changed touch or mouse - * point. - * @param {!Event} e A mouse event or a touch event with one or more changed - * touches. - * @return {!Array.} An array of mouse or touch events. Each touch - * event will have exactly one changed touch. - */ -Blockly.Touch.splitEventByTouches = function(e) { - var events = []; - if (e.changedTouches) { - for (var i = 0; i < e.changedTouches.length; i++) { - var newEvent = { - type: e.type, - changedTouches: [e.changedTouches[i]], - target: e.target, - stopPropagation: function(){ e.stopPropagation(); }, - preventDefault: function(){ e.preventDefault(); } - }; - events[i] = newEvent; - } - } else { - events.push(e); - } - return events; -}; diff --git a/core/trashcan.js b/core/trashcan.js deleted file mode 100644 index 4a39ce1ffc..0000000000 --- a/core/trashcan.js +++ /dev/null @@ -1,343 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2011 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Object representing a trash can icon. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.Trashcan'); - -goog.require('goog.dom'); -goog.require('goog.math.Rect'); - - -/** - * Class for a trash can. - * @param {!Blockly.Workspace} workspace The workspace to sit in. - * @constructor - */ -Blockly.Trashcan = function(workspace) { - this.workspace_ = workspace; -}; - -/** - * Width of both the trash can and lid images. - * @type {number} - * @private - */ -Blockly.Trashcan.prototype.WIDTH_ = 47; - -/** - * Height of the trashcan image (minus lid). - * @type {number} - * @private - */ -Blockly.Trashcan.prototype.BODY_HEIGHT_ = 44; - -/** - * Height of the lid image. - * @type {number} - * @private - */ -Blockly.Trashcan.prototype.LID_HEIGHT_ = 16; - -/** - * Distance between trashcan and bottom edge of workspace. - * @type {number} - * @private - */ -Blockly.Trashcan.prototype.MARGIN_BOTTOM_ = 20; - -/** - * Distance between trashcan and right edge of workspace. - * @type {number} - * @private - */ -Blockly.Trashcan.prototype.MARGIN_SIDE_ = 20; - -/** - * Extent of hotspot on all sides beyond the size of the image. - * @type {number} - * @private - */ -Blockly.Trashcan.prototype.MARGIN_HOTSPOT_ = 10; - -/** - * Location of trashcan in sprite image. - * @type {number} - * @private - */ -Blockly.Trashcan.prototype.SPRITE_LEFT_ = 0; - -/** - * Location of trashcan in sprite image. - * @type {number} - * @private - */ -Blockly.Trashcan.prototype.SPRITE_TOP_ = 32; - -/** - * Current open/close state of the lid. - * @type {boolean} - */ -Blockly.Trashcan.prototype.isOpen = false; - -/** - * The SVG group containing the trash can. - * @type {Element} - * @private - */ -Blockly.Trashcan.prototype.svgGroup_ = null; - -/** - * The SVG image element of the trash can lid. - * @type {Element} - * @private - */ -Blockly.Trashcan.prototype.svgLid_ = null; - -/** - * Task ID of opening/closing animation. - * @type {number} - * @private - */ -Blockly.Trashcan.prototype.lidTask_ = 0; - -/** - * Current state of lid opening (0.0 = closed, 1.0 = open). - * @type {number} - * @private - */ -Blockly.Trashcan.prototype.lidOpen_ = 0; - -/** - * Left coordinate of the trash can. - * @type {number} - * @private - */ -Blockly.Trashcan.prototype.left_ = 0; - -/** - * Top coordinate of the trash can. - * @type {number} - * @private - */ -Blockly.Trashcan.prototype.top_ = 0; - -/** - * Create the trash can elements. - * @return {!Element} The trash can's SVG group. - */ -Blockly.Trashcan.prototype.createDom = function() { - /* Here's the markup that will be generated: - - - - - - - - - - - */ - this.svgGroup_ = Blockly.utils.createSvgElement('g', - {'class': 'blocklyTrash'}, null); - var clip; - var rnd = String(Math.random()).substring(2); - clip = Blockly.utils.createSvgElement('clipPath', - {'id': 'blocklyTrashBodyClipPath' + rnd}, - this.svgGroup_); - Blockly.utils.createSvgElement('rect', - { - 'width': this.WIDTH_, - 'height': this.BODY_HEIGHT_, - 'y': this.LID_HEIGHT_ - }, - clip); - var body = Blockly.utils.createSvgElement('image', - { - 'width': Blockly.SPRITE.width, - 'x': -this.SPRITE_LEFT_, - 'height': Blockly.SPRITE.height, - 'y': -this.SPRITE_TOP_, - 'clip-path': 'url(#blocklyTrashBodyClipPath' + rnd + ')' - }, - this.svgGroup_); - body.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', - this.workspace_.options.pathToMedia + Blockly.SPRITE.url); - - clip = Blockly.utils.createSvgElement('clipPath', - {'id': 'blocklyTrashLidClipPath' + rnd}, - this.svgGroup_); - Blockly.utils.createSvgElement('rect', - {'width': this.WIDTH_, 'height': this.LID_HEIGHT_}, clip); - this.svgLid_ = Blockly.utils.createSvgElement('image', - { - 'width': Blockly.SPRITE.width, - 'x': -this.SPRITE_LEFT_, - 'height': Blockly.SPRITE.height, - 'y': -this.SPRITE_TOP_, - 'clip-path': 'url(#blocklyTrashLidClipPath' + rnd + ')' - }, - this.svgGroup_); - this.svgLid_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', - this.workspace_.options.pathToMedia + Blockly.SPRITE.url); - - Blockly.bindEventWithChecks_(this.svgGroup_, 'mouseup', this, this.click); - this.animateLid_(); - return this.svgGroup_; -}; - -/** - * Initialize the trash can. - * @param {number} bottom Distance from workspace bottom to bottom of trashcan. - * @return {number} Distance from workspace bottom to the top of trashcan. - */ -Blockly.Trashcan.prototype.init = function(bottom) { - this.bottom_ = this.MARGIN_BOTTOM_ + bottom; - this.setOpen_(false); - return this.bottom_ + this.BODY_HEIGHT_ + this.LID_HEIGHT_; -}; - -/** - * Dispose of this trash can. - * Unlink from all DOM elements to prevent memory leaks. - */ -Blockly.Trashcan.prototype.dispose = function() { - if (this.svgGroup_) { - goog.dom.removeNode(this.svgGroup_); - this.svgGroup_ = null; - } - this.svgLid_ = null; - this.workspace_ = null; - clearTimeout(this.lidTask_); -}; - -/** - * Move the trash can to the bottom-right corner. - */ -Blockly.Trashcan.prototype.position = function() { - var metrics = this.workspace_.getMetrics(); - if (!metrics) { - // There are no metrics available (workspace is probably not visible). - return; - } - if (this.workspace_.RTL) { - this.left_ = this.MARGIN_SIDE_ + Blockly.Scrollbar.scrollbarThickness; - if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) { - this.left_ += metrics.flyoutWidth; - if (this.workspace_.toolbox_) { - this.left_ += metrics.absoluteLeft; - } - } - } else { - this.left_ = metrics.viewWidth + metrics.absoluteLeft - - this.WIDTH_ - this.MARGIN_SIDE_ - Blockly.Scrollbar.scrollbarThickness; - - if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { - this.left_ -= metrics.flyoutWidth; - } - } - this.top_ = metrics.viewHeight + metrics.absoluteTop - - (this.BODY_HEIGHT_ + this.LID_HEIGHT_) - this.bottom_; - - if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_BOTTOM) { - this.top_ -= metrics.flyoutHeight; - } - this.svgGroup_.setAttribute('transform', - 'translate(' + this.left_ + ',' + this.top_ + ')'); -}; - -/** - * Return the deletion rectangle for this trash can. - * @return {goog.math.Rect} Rectangle in which to delete. - */ -Blockly.Trashcan.prototype.getClientRect = function() { - if (!this.svgGroup_) { - return null; - } - - var trashRect = this.svgGroup_.getBoundingClientRect(); - var left = trashRect.left + this.SPRITE_LEFT_ - this.MARGIN_HOTSPOT_; - var top = trashRect.top + this.SPRITE_TOP_ - this.MARGIN_HOTSPOT_; - var width = this.WIDTH_ + 2 * this.MARGIN_HOTSPOT_; - var height = this.LID_HEIGHT_ + this.BODY_HEIGHT_ + 2 * this.MARGIN_HOTSPOT_; - return new goog.math.Rect(left, top, width, height); - -}; - -/** - * Flip the lid open or shut. - * @param {boolean} state True if open. - * @private - */ -Blockly.Trashcan.prototype.setOpen_ = function(state) { - if (this.isOpen == state) { - return; - } - clearTimeout(this.lidTask_); - this.isOpen = state; - this.animateLid_(); -}; - -/** - * Rotate the lid open or closed by one step. Then wait and recurse. - * @private - */ -Blockly.Trashcan.prototype.animateLid_ = function() { - this.lidOpen_ += this.isOpen ? 0.2 : -0.2; - this.lidOpen_ = Math.min(Math.max(this.lidOpen_, 0), 1); - var lidAngle = this.lidOpen_ * 45; - this.svgLid_.setAttribute('transform', 'rotate(' + - (this.workspace_.RTL ? -lidAngle : lidAngle) + ',' + - (this.workspace_.RTL ? 4 : this.WIDTH_ - 4) + ',' + - (this.LID_HEIGHT_ - 2) + ')'); - // Linear interpolation between 0.4 and 0.8. - var opacity = 0.4 + this.lidOpen_ * (0.8 - 0.4); - this.svgGroup_.style.opacity = opacity; - if (this.lidOpen_ > 0 && this.lidOpen_ < 1) { - this.lidTask_ = setTimeout(this.animateLid_.bind(this), 20); - } -}; - -/** - * Flip the lid shut. - * Called externally after a drag. - */ -Blockly.Trashcan.prototype.close = function() { - this.setOpen_(false); -}; - -/** - * Inspect the contents of the trash. - */ -Blockly.Trashcan.prototype.click = function() { - var dx = this.workspace_.startScrollX - this.workspace_.scrollX; - var dy = this.workspace_.startScrollY - this.workspace_.scrollY; - if (Math.sqrt(dx * dx + dy * dy) > Blockly.DRAG_RADIUS) { - return; - } - console.log('TODO: Inspect trash.'); -}; diff --git a/core/ui_events.js b/core/ui_events.js deleted file mode 100644 index 2430ac928b..0000000000 --- a/core/ui_events.js +++ /dev/null @@ -1,91 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2018 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Events fired as a result of UI actions in Blockly's editor. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.Events.Ui'); - -goog.require('Blockly.Events'); -goog.require('Blockly.Events.Abstract'); - -goog.require('goog.array'); -goog.require('goog.math.Coordinate'); - -/** - * Class for a UI event. - * UI events are events that don't need to be sent over the wire for multi-user - * editing to work (e.g. scrolling the workspace, zooming, opening toolbox - * categories). - * UI events do not undo or redo. - * @param {Blockly.Block} block The affected block. - * @param {string} element One of 'selected', 'comment', 'mutator', etc. - * @param {*} oldValue Previous value of element. - * @param {*} newValue New value of element. - * @extends {Blockly.Events.Abstract} - * @constructor - */ -Blockly.Events.Ui = function(block, element, oldValue, newValue) { - Blockly.Events.Ui.superClass_.constructor.call(this); - this.blockId = block ? block.id : null; - this.workspaceId = block ? block.workspace.id : null; - this.element = element; - this.oldValue = oldValue; - this.newValue = newValue; - // UI events do not undo or redo. - this.recordUndo = false; -}; -goog.inherits(Blockly.Events.Ui, Blockly.Events.Abstract); - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.Ui.prototype.type = Blockly.Events.UI; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.Ui.prototype.toJson = function() { - var json = Blockly.Events.Ui.superClass_.toJson.call(this); - json['element'] = this.element; - if (this.newValue !== undefined) { - json['newValue'] = this.newValue; - } - if (this.blockId) { - json['blockId'] = this.blockId; - } - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.Ui.prototype.fromJson = function(json) { - Blockly.Events.Ui.superClass_.fromJson.call(this, json); - this.element = json['element']; - this.newValue = json['newValue']; - this.blockId = json['blockId']; -}; diff --git a/core/ui_menu_utils.js b/core/ui_menu_utils.js deleted file mode 100644 index 8fda6021ac..0000000000 --- a/core/ui_menu_utils.js +++ /dev/null @@ -1,68 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Utility methods for working with the closure menu (goog.ui.menu). - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -/** - * @name Blockly.utils.uiMenu - * @namespace - **/ -goog.provide('Blockly.utils.uiMenu'); - - -/** - * Get the size of a rendered goog.ui.Menu. - * @param {!goog.ui.Menu} menu The menu to measure. - * @return {!goog.math.Size} Object with width and height properties. - * @package - */ -Blockly.utils.uiMenu.getSize = function(menu) { - var menuDom = menu.getElement(); - var menuSize = goog.style.getSize(menuDom); - // Recalculate height for the total content, not only box height. - menuSize.height = menuDom.scrollHeight; - return menuSize; -}; - -/** - * Adjust the bounding boxes used to position the widget div to deal with RTL - * goog.ui.Menu positioning. In RTL mode the menu renders down and to the left - * of its start point, instead of down and to the right. Adjusting all of the - * bounding boxes accordingly allows us to use the same code for all widgets. - * This function in-place modifies the provided bounding boxes. - * @param {!Object} viewportBBox The bounding rectangle of the current viewport, - * in window coordinates. - * @param {!Object} anchorBBox The bounding rectangle of the anchor, in window - * coordinates. - * @param {!goog.math.Size} menuSize The size of the menu that is inside the - * widget div, in window coordinates. - * @package - */ -Blockly.utils.uiMenu.adjustBBoxesForRTL = function(viewportBBox, anchorBBox, - menuSize) { - anchorBBox.left += menuSize.width; - anchorBBox.right += menuSize.width; - viewportBBox.left += menuSize.width; - viewportBBox.right += menuSize.width; -}; diff --git a/core/utils.js b/core/utils.js deleted file mode 100644 index a29c705ebf..0000000000 --- a/core/utils.js +++ /dev/null @@ -1,948 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Utility methods. - * These methods are not specific to Blockly, and could be factored out into - * a JavaScript framework such as Closure. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -/** - * @name Blockly.utils - * @namespace - **/ -goog.provide('Blockly.utils'); - -goog.require('Blockly.Touch'); -goog.require('goog.dom'); -goog.require('goog.events.BrowserFeature'); -goog.require('goog.math.Coordinate'); -goog.require('goog.userAgent'); - - -/** - * To allow ADVANCED_OPTIMIZATIONS, combining variable.name and variable['name'] - * is not possible. To access the exported Blockly.Msg.Something it needs to be - * accessed through the exact name that was exported. Note, that all the exports - * are happening as the last thing in the generated js files, so they won't be - * accessible before JavaScript loads! - * @return {!Object.} The message array. - * @private - */ -Blockly.utils.getMessageArray_ = function() { - return goog.global['Blockly']['Msg']; -}; - -/** - * Remove an attribute from a element even if it's in IE 10. - * Similar to Element.removeAttribute() but it works on SVG elements in IE 10. - * Sets the attribute to null in IE 10, which treats removeAttribute as a no-op - * if it's called on an SVG element. - * @param {!Element} element DOM element to remove attribute from. - * @param {string} attributeName Name of attribute to remove. - */ -Blockly.utils.removeAttribute = function(element, attributeName) { - // goog.userAgent.isVersion is deprecated, but the replacement is - // goog.userAgent.isVersionOrHigher. - if (goog.userAgent.IE && goog.userAgent.isVersion('10.0')) { - element.setAttribute(attributeName, null); - } else { - element.removeAttribute(attributeName); - } -}; - -/** - * 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. - */ -Blockly.utils.addClass = function(element, className) { - var classes = element.getAttribute('class') || ''; - if ((' ' + classes + ' ').indexOf(' ' + className + ' ') != -1) { - return false; - } - if (classes) { - classes += ' '; - } - element.setAttribute('class', classes + className); - return true; -}; - -/** - * 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. - */ -Blockly.utils.removeClass = function(element, className) { - var classes = element.getAttribute('class'); - if ((' ' + classes + ' ').indexOf(' ' + className + ' ') == -1) { - return false; - } - var classList = classes.split(/\s+/); - for (var i = 0; i < classList.length; i++) { - if (!classList[i] || classList[i] == className) { - classList.splice(i, 1); - i--; - } - } - if (classList.length) { - element.setAttribute('class', classList.join(' ')); - } else { - Blockly.utils.removeAttribute(element, 'class'); - } - return true; -}; - -/** - * 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. - * @package - */ -Blockly.utils.hasClass = function(element, className) { - var classes = element.getAttribute('class'); - return (' ' + classes + ' ').indexOf(' ' + className + ' ') != -1; -}; - -/** - * Don't do anything for this event, just halt propagation. - * @param {!Event} e An event. - */ -Blockly.utils.noEvent = function(e) { - // This event has been handled. No need to bubble up to the document. - e.preventDefault(); - e.stopPropagation(); -}; - -/** - * Is this event targeting a text input widget? - * @param {!Event} e An event. - * @return {boolean} True if text input. - */ -Blockly.utils.isTargetInput = function(e) { - return e.target.type == 'textarea' || e.target.type == 'text' || - e.target.type == 'number' || e.target.type == 'email' || - e.target.type == 'password' || e.target.type == 'search' || - e.target.type == 'tel' || e.target.type == 'url' || - e.target.isContentEditable; -}; - -/** - * 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 {!goog.math.Coordinate} Object with .x and .y properties. - */ -Blockly.utils.getRelativeXY = function(element) { - var xy = new goog.math.Coordinate(0, 0); - // First, check for x and y attributes. - var x = element.getAttribute('x'); - if (x) { - xy.x = parseInt(x, 10); - } - var y = element.getAttribute('y'); - if (y) { - xy.y = parseInt(y, 10); - } - // Second, check for transform="translate(...)" attribute. - var transform = element.getAttribute('transform'); - var r = transform && transform.match(Blockly.utils.getRelativeXY.XY_REGEX_); - if (r) { - xy.x += parseFloat(r[1]); - if (r[3]) { - xy.y += parseFloat(r[3]); - } - } - - // Then check for style = transform: translate(...) or translate3d(...) - var style = element.getAttribute('style'); - if (style && style.indexOf('translate') > -1) { - var styleComponents = style.match(Blockly.utils.getRelativeXY.XY_STYLE_REGEX_); - if (styleComponents) { - xy.x += parseFloat(styleComponents[1]); - if (styleComponents[3]) { - xy.y += parseFloat(styleComponents[3]); - } - } - } - return xy; -}; - -/** - * 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 {!goog.math.Coordinate} Object with .x and .y properties. - */ -Blockly.utils.getInjectionDivXY_ = function(element) { - var x = 0; - var y = 0; - while (element) { - var xy = Blockly.utils.getRelativeXY(element); - var scale = Blockly.utils.getScale_(element); - x = (x * scale) + xy.x; - y = (y * scale) + xy.y; - var classes = element.getAttribute('class') || ''; - if ((' ' + classes + ' ').indexOf(' injectionDiv ') != -1) { - break; - } - element = element.parentNode; - } - return new goog.math.Coordinate(x, y); -}; - -/** - * Return the scale of this element. - * @param {!Element} element The element to find the coordinates of. - * @return {!number} number represending the scale applied to the element. - * @private - */ -Blockly.utils.getScale_ = function(element) { - var scale = 1; - var transform = element.getAttribute('transform'); - if (transform) { - var transformComponents = - transform.match(Blockly.utils.getScale_.REGEXP_); - if (transformComponents && transformComponents[0]) { - scale = parseFloat(transformComponents[0]); - } - } - return scale; -}; - -/** - * 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} - * @private - */ -Blockly.utils.getRelativeXY.XY_REGEX_ = - /translate\(\s*([-+\d.e]+)([ ,]\s*([-+\d.e]+)\s*)?/; - - -/** - * Static regex to pull the scale values out of a transform style property. - * Accounts for same exceptions as XY_REGEXP_. - * @type {!RegExp} - * @private - */ -Blockly.utils.getScale_REGEXP_ = /scale\(\s*([-+\d.e]+)\s*\)/; - -/** - * Static regex to pull the x,y values out of a translate3d() or translate3d() - * style property. - * Accounts for same exceptions as XY_REGEXP_. - * @type {!RegExp} - * @private - */ -Blockly.utils.getRelativeXY.XY_STYLE_REGEX_ = - /transform:\s*translate(?:3d)?\(\s*([-+\d.e]+)\s*px([ ,]\s*([-+\d.e]+)\s*px)?/; - -/** - * Helper method for creating SVG elements. - * @param {string} name Element's tag name. - * @param {!Object} attrs Dictionary of attribute names and values. - * @param {Element} parent Optional parent on which to append the element. - * @return {!SVGElement} Newly created SVG element. - */ -Blockly.utils.createSvgElement = function(name, attrs, parent /*, opt_workspace */) { - var e = /** @type {!SVGElement} */ - (document.createElementNS(Blockly.SVG_NS, name)); - for (var key in attrs) { - e.setAttribute(key, attrs[key]); - } - // IE defines a unique attribute "runtimeStyle", it is NOT applied to - // elements created with createElementNS. However, Closure checks for IE - // and assumes the presence of the attribute and crashes. - if (document.body.runtimeStyle) { // Indicates presence of IE-only attr. - e.runtimeStyle = e.currentStyle = e.style; - } - if (parent) { - parent.appendChild(e); - } - return e; -}; - -/** - * Is this event a right-click? - * @param {!Event} e Mouse event. - * @return {boolean} True if right-click. - */ -Blockly.utils.isRightButton = function(e) { - if (e.ctrlKey && goog.userAgent.MAC) { - // Control-clicking on Mac OS X is treated as a right-click. - // WebKit on Mac OS X fails to change button to 2 (but Gecko does). - return true; - } - return e.button == 2; -}; - -/** - * Return the converted coordinates of the given mouse event. - * The origin (0,0) is the top-left corner of the Blockly SVG. - * @param {!Event} e Mouse event. - * @param {!Element} svg SVG element. - * @param {SVGMatrix} matrix Inverted screen CTM to use. - * @return {!SVGPoint} Object with .x and .y properties. - */ -Blockly.utils.mouseToSvg = function(e, svg, matrix) { - var svgPoint = svg.createSVGPoint(); - svgPoint.x = e.clientX; - svgPoint.y = e.clientY; - - if (!matrix) { - matrix = svg.getScreenCTM().inverse(); - } - return svgPoint.matrixTransform(matrix); -}; - -/** - * Given an array of strings, return the length of the shortest one. - * @param {!Array.} array Array of strings. - * @return {number} Length of shortest string. - */ -Blockly.utils.shortestStringLength = function(array) { - if (!array.length) { - return 0; - } - return array.reduce(function(a, b) { - return a.length < b.length ? a : b; - }).length; -}; - -/** - * 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. - */ -Blockly.utils.commonWordPrefix = function(array, opt_shortest) { - if (!array.length) { - return 0; - } else if (array.length == 1) { - return array[0].length; - } - var wordPrefix = 0; - var max = opt_shortest || Blockly.utils.shortestStringLength(array); - for (var len = 0; len < max; len++) { - var letter = array[0][len]; - for (var i = 1; i < array.length; i++) { - if (letter != array[i][len]) { - return wordPrefix; - } - } - if (letter == ' ') { - wordPrefix = len + 1; - } - } - for (var i = 1; i < array.length; i++) { - var letter = array[i][len]; - if (letter && letter != ' ') { - return wordPrefix; - } - } - return max; -}; - -/** - * 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. - */ -Blockly.utils.commonWordSuffix = function(array, opt_shortest) { - if (!array.length) { - return 0; - } else if (array.length == 1) { - return array[0].length; - } - var wordPrefix = 0; - var max = opt_shortest || Blockly.utils.shortestStringLength(array); - for (var len = 0; len < max; len++) { - var letter = array[0].substr(-len - 1, 1); - for (var i = 1; i < array.length; i++) { - if (letter != array[i].substr(-len - 1, 1)) { - return wordPrefix; - } - } - if (letter == ' ') { - wordPrefix = len + 1; - } - } - for (var i = 1; i < array.length; i++) { - var letter = array[i].charAt(array[i].length - len - 1); - if (letter && letter != ' ') { - return wordPrefix; - } - } - return max; -}; - -/** - * 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 - * Blockly.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. - */ -Blockly.utils.tokenizeInterpolation = function(message) { - return Blockly.utils.tokenizeInterpolation_(message, true); -}; - -/** - * 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 Blockly.Msg['MY_MSG']. - * @param {string|?} message Message, which may be a string that contains - * string table references. - * @return {!string} String with message references replaced. - */ -Blockly.utils.replaceMessageReferences = function(message) { - if (!goog.isString(message)) { - return message; - } - var interpolatedResult = Blockly.utils.tokenizeInterpolation_(message, false); - // When parseInterpolationTokens == false, interpolatedResult should be at - // most length 1. - return interpolatedResult.length ? interpolatedResult[0] : ''; -}; - -/** - * Validates that any %{BKY_...} references in the message refer to keys of - * the Blockly.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. - */ -Blockly.utils.checkMessageReferences = function(message) { - var isValid = true; // True until a bad reference is found. - - var regex = /%{BKY_([a-zA-Z][a-zA-Z0-9_]*)}/g; - var match = regex.exec(message); - while (match) { - var msgKey = match[1]; - if (Blockly.utils.getMessageArray_()[msgKey] == undefined) { - console.log('WARNING: No message string for %{BKY_' + msgKey + '}.'); - isValid = false; - } - - // Re-run on remainder of string. - message = message.substring(match.index + msgKey.length + 1); - match = regex.exec(message); - } - - return isValid; -}; - -/** - * Internal implementation of the message reference and interpolation token - * parsing used by tokenizeInterpolation() and replaceMessageReferences(). - * @param {string} message Text which might contain string table references and - * interpolation tokens. - * @param {boolean} parseInterpolationTokens Option to parse numeric - * interpolation tokens (%1, %2, ...) when true. - * @return {!Array.} Array of strings and numbers. - * @private - */ -Blockly.utils.tokenizeInterpolation_ = function(message, - parseInterpolationTokens) { - var tokens = []; - var chars = message.split(''); - chars.push(''); // End marker. - // Parse the message with a finite state machine. - // 0 - Base case. - // 1 - % found. - // 2 - Digit found. - // 3 - Message ref found. - var state = 0; - var buffer = []; - var number = null; - for (var i = 0; i < chars.length; i++) { - var c = chars[i]; - if (state == 0) { - if (c == '%') { - var text = buffer.join(''); - if (text) { - tokens.push(text); - } - buffer.length = 0; - state = 1; // Start escape. - } else { - buffer.push(c); // Regular char. - } - } else if (state == 1) { - if (c == '%') { - buffer.push(c); // Escaped %: %% - state = 0; - } else if (parseInterpolationTokens && '0' <= c && c <= '9') { - state = 2; - number = c; - var text = buffer.join(''); - if (text) { - tokens.push(text); - } - buffer.length = 0; - } else if (c == '{') { - state = 3; - } else { - buffer.push('%', c); // Not recognized. Return as literal. - state = 0; - } - } else if (state == 2) { - if ('0' <= c && c <= '9') { - number += c; // Multi-digit number. - } else { - tokens.push(parseInt(number, 10)); - i--; // Parse this char again. - state = 0; - } - } else if (state == 3) { // String table reference - if (c == '') { - // Premature end before closing '}' - buffer.splice(0, 0, '%{'); // Re-insert leading delimiter - i--; // Parse this char again. - state = 0; // and parse as string literal. - } else if (c != '}') { - buffer.push(c); - } else { - var rawKey = buffer.join(''); - if (/[a-zA-Z][a-zA-Z0-9_]*/.test(rawKey)) { // Strict matching - // Found a valid string key. Attempt case insensitive match. - var keyUpper = rawKey.toUpperCase(); - - // BKY_ is the prefix used to namespace the strings used in Blockly - // core files and the predefined blocks in ../blocks/. These strings - // are defined in ../msgs/ files. - var bklyKey = goog.string.startsWith(keyUpper, 'BKY_') ? - keyUpper.substring(4) : null; - if (bklyKey && bklyKey in Blockly.Msg) { - var rawValue = Blockly.Msg[bklyKey]; - if (goog.isString(rawValue)) { - // Attempt to dereference substrings, too, appending to the end. - Array.prototype.push.apply(tokens, - Blockly.utils.tokenizeInterpolation(rawValue)); - } else if (parseInterpolationTokens) { - // When parsing interpolation tokens, numbers are special - // placeholders (%1, %2, etc). Make sure all other values are - // strings. - tokens.push(String(rawValue)); - } else { - tokens.push(rawValue); - } - } else { - // No entry found in the string table. Pass reference as string. - tokens.push('%{' + rawKey + '}'); - } - buffer.length = 0; // Clear the array - state = 0; - } else { - tokens.push('%{' + rawKey + '}'); - buffer.length = 0; - state = 0; // and parse as string literal. - } - } - } - } - var text = buffer.join(''); - if (text) { - tokens.push(text); - } - - // Merge adjacent text tokens into a single string. - var mergedTokens = []; - buffer.length = 0; - for (var i = 0; i < tokens.length; ++i) { - if (typeof tokens[i] == 'string') { - buffer.push(tokens[i]); - } else { - text = buffer.join(''); - if (text) { - mergedTokens.push(text); - } - buffer.length = 0; - mergedTokens.push(tokens[i]); - } - } - text = buffer.join(''); - if (text) { - mergedTokens.push(text); - } - buffer.length = 0; - - return mergedTokens; -}; - -/** - * Generate a unique ID. This should be globally unique. - * 87 characters ^ 20 length > 128 bits (better than a UUID). - * @return {string} A globally unique ID string. - */ -Blockly.utils.genUid = function() { - var length = 20; - var soupLength = Blockly.utils.genUid.soup_.length; - var id = []; - for (var i = 0; i < length; i++) { - id[i] = Blockly.utils.genUid.soup_.charAt(Math.random() * soupLength); - } - return id.join(''); -}; - -/** - * Legal characters for the unique ID. Should be all on a US keyboard. - * No characters that conflict with XML or JSON. Requests to remove additional - * 'problematic' characters from this soup will be denied. That's your failure - * to properly escape in your own environment. Issues #251, #625, #682, #1304. - * @private - */ -Blockly.utils.genUid.soup_ = '!#$%()*+,-./:;=?@[]^_`{|}~' + - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - -/** - * Wrap text to the specified width. - * @param {string} text Text to wrap. - * @param {number} limit Width to wrap each line. - * @return {string} Wrapped text. - */ -Blockly.utils.wrap = function(text, limit) { - var lines = text.split('\n'); - for (var i = 0; i < lines.length; i++) { - lines[i] = Blockly.utils.wrapLine_(lines[i], limit); - } - return lines.join('\n'); -}; - -/** - * Wrap single line of text to the specified width. - * @param {string} text Text to wrap. - * @param {number} limit Width to wrap each line. - * @return {string} Wrapped text. - * @private - */ -Blockly.utils.wrapLine_ = function(text, limit) { - if (text.length <= limit) { - // Short text, no need to wrap. - return text; - } - // Split the text into words. - var words = text.trim().split(/\s+/); - // Set limit to be the length of the largest word. - for (var i = 0; i < words.length; i++) { - if (words[i].length > limit) { - limit = words[i].length; - } - } - - var lastScore; - var score = -Infinity; - var lastText; - var lineCount = 1; - do { - lastScore = score; - lastText = text; - // Create a list of booleans representing if a space (false) or - // a break (true) appears after each word. - var wordBreaks = []; - // Seed the list with evenly spaced linebreaks. - var steps = words.length / lineCount; - var insertedBreaks = 1; - for (var i = 0; i < words.length - 1; i++) { - if (insertedBreaks < (i + 1.5) / steps) { - insertedBreaks++; - wordBreaks[i] = true; - } else { - wordBreaks[i] = false; - } - } - wordBreaks = Blockly.utils.wrapMutate_(words, wordBreaks, limit); - score = Blockly.utils.wrapScore_(words, wordBreaks, limit); - text = Blockly.utils.wrapToText_(words, wordBreaks); - lineCount++; - } while (score > lastScore); - return lastText; -}; - -/** - * Compute a score for how good the wrapping is. - * @param {!Array.} words Array of each word. - * @param {!Array.} wordBreaks Array of line breaks. - * @param {number} limit Width to wrap each line. - * @return {number} Larger the better. - * @private - */ -Blockly.utils.wrapScore_ = function(words, wordBreaks, limit) { - // If this function becomes a performance liability, add caching. - // Compute the length of each line. - var lineLengths = [0]; - var linePunctuation = []; - for (var i = 0; i < words.length; i++) { - lineLengths[lineLengths.length - 1] += words[i].length; - if (wordBreaks[i] === true) { - lineLengths.push(0); - linePunctuation.push(words[i].charAt(words[i].length - 1)); - } else if (wordBreaks[i] === false) { - lineLengths[lineLengths.length - 1]++; - } - } - var maxLength = Math.max.apply(Math, lineLengths); - - var score = 0; - for (var i = 0; i < lineLengths.length; i++) { - // Optimize for width. - // -2 points per char over limit (scaled to the power of 1.5). - score -= Math.pow(Math.abs(limit - lineLengths[i]), 1.5) * 2; - // Optimize for even lines. - // -1 point per char smaller than max (scaled to the power of 1.5). - score -= Math.pow(maxLength - lineLengths[i], 1.5); - // Optimize for structure. - // Add score to line endings after punctuation. - if ('.?!'.indexOf(linePunctuation[i]) != -1) { - score += limit / 3; - } else if (',;)]}'.indexOf(linePunctuation[i]) != -1) { - score += limit / 4; - } - } - // All else being equal, the last line should not be longer than the - // previous line. For example, this looks wrong: - // aaa bbb - // ccc ddd eee - if (lineLengths.length > 1 && lineLengths[lineLengths.length - 1] <= - lineLengths[lineLengths.length - 2]) { - score += 0.5; - } - return score; -}; - -/** - * Mutate the array of line break locations until an optimal solution is found. - * No line breaks are added or deleted, they are simply moved around. - * @param {!Array.} words Array of each word. - * @param {!Array.} wordBreaks Array of line breaks. - * @param {number} limit Width to wrap each line. - * @return {!Array.} New array of optimal line breaks. - * @private - */ -Blockly.utils.wrapMutate_ = function(words, wordBreaks, limit) { - var bestScore = Blockly.utils.wrapScore_(words, wordBreaks, limit); - var bestBreaks; - // Try shifting every line break forward or backward. - for (var i = 0; i < wordBreaks.length - 1; i++) { - if (wordBreaks[i] == wordBreaks[i + 1]) { - continue; - } - var mutatedWordBreaks = [].concat(wordBreaks); - mutatedWordBreaks[i] = !mutatedWordBreaks[i]; - mutatedWordBreaks[i + 1] = !mutatedWordBreaks[i + 1]; - var mutatedScore = - Blockly.utils.wrapScore_(words, mutatedWordBreaks, limit); - if (mutatedScore > bestScore) { - bestScore = mutatedScore; - bestBreaks = mutatedWordBreaks; - } - } - if (bestBreaks) { - // Found an improvement. See if it may be improved further. - return Blockly.utils.wrapMutate_(words, bestBreaks, limit); - } - // No improvements found. Done. - return wordBreaks; -}; - -/** - * Reassemble the array of words into text, with the specified line breaks. - * @param {!Array.} words Array of each word. - * @param {!Array.} wordBreaks Array of line breaks. - * @return {string} Plain text. - * @private - */ -Blockly.utils.wrapToText_ = function(words, wordBreaks) { - var text = []; - for (var i = 0; i < words.length; i++) { - text.push(words[i]); - if (wordBreaks[i] !== undefined) { - text.push(wordBreaks[i] ? '\n' : ' '); - } - } - return text.join(''); -}; - -/** - * Check if 3D transforms are supported by adding an element - * and attempting to set the property. - * @return {boolean} true if 3D transforms are supported. - */ -Blockly.utils.is3dSupported = function() { - if (Blockly.utils.is3dSupported.cached_ !== undefined) { - return Blockly.utils.is3dSupported.cached_; - } - // CC-BY-SA Lorenzo Polidori - // stackoverflow.com/questions/5661671/detecting-transform-translate3d-support - if (!goog.global.getComputedStyle) { - return false; - } - - var el = document.createElement('p'); - var has3d = 'none'; - var transforms = { - 'webkitTransform': '-webkit-transform', - 'OTransform': '-o-transform', - 'msTransform': '-ms-transform', - 'MozTransform': '-moz-transform', - 'transform': 'transform' - }; - - // Add it to the body to get the computed style. - document.body.insertBefore(el, null); - - for (var t in transforms) { - if (el.style[t] !== undefined) { - el.style[t] = 'translate3d(1px,1px,1px)'; - var computedStyle = goog.global.getComputedStyle(el); - if (!computedStyle) { - // getComputedStyle in Firefox returns null when blockly is loaded - // inside an iframe with display: none. Returning false and not - // caching is3dSupported means we try again later. This is most likely - // when users are interacting with blocks which should mean blockly is - // visible again. - // See https://bugzilla.mozilla.org/show_bug.cgi?id=548397 - document.body.removeChild(el); - return false; - } - has3d = computedStyle.getPropertyValue(transforms[t]); - } - } - document.body.removeChild(el); - Blockly.utils.is3dSupported.cached_ = has3d !== 'none'; - return Blockly.utils.is3dSupported.cached_; -}; - -/** - * 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. - * @package - */ -Blockly.utils.insertAfter = function(newNode, refNode) { - var siblingNode = refNode.nextSibling; - var parentNode = refNode.parentNode; - if (!parentNode) { - throw 'Reference node has no parent.'; - } - if (siblingNode) { - parentNode.insertBefore(newNode, siblingNode); - } else { - parentNode.appendChild(newNode); - } -}; - -/** - * Calls a function after the page has loaded, possibly immediately. - * @param {function()} fn Function to run. - * @throws Error Will throw if no global document can be found (e.g., Node.js). - */ -Blockly.utils.runAfterPageLoad = function(fn) { - if (!document) { - throw new Error('Blockly.utils.runAfterPageLoad() requires browser document.'); - } - if (document.readyState === 'complete') { - fn(); // Page has already loaded. Call immediately. - } else { - // Poll readyState. - var readyStateCheckInterval = setInterval(function() { - if (document.readyState === 'complete') { - clearInterval(readyStateCheckInterval); - fn(); - } - }, 10); - } -}; - -/** - * 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 http://caniuse.com/#feat=transforms2d - * @param {!Element} node The node which the CSS transform should be applied. - * @param {string} transform The value of the CSS `transform` property. - */ -Blockly.utils.setCssTransform = function(node, transform) { - node.style['transform'] = transform; - node.style['-webkit-transform'] = transform; -}; - -/** - * Get the position of the current viewport in window coordinates. This takes - * scroll into account. - * @return {!Object} an object containing window width, height, and scroll - * position in window coordinates. - * @package - */ -Blockly.utils.getViewportBBox = function() { - // Pixels. - var windowSize = goog.dom.getViewportSize(); - // Pixels, in window coordinates. - var scrollOffset = goog.style.getViewportPageOffset(document); - return { - right: windowSize.width + scrollOffset.x, - bottom: windowSize.height + scrollOffset.y, - top: scrollOffset.y, - left: scrollOffset.x - }; -}; - -/** - * 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`. - * @package - */ -Blockly.utils.startsWith = function(str, prefix) { - return str.lastIndexOf(prefix, 0) == 0; -}; - -/** - * Converts degrees to radians. - * Copied from Closure's goog.math.toRadians. - * @param {number} angleDegrees Angle in degrees. - * @return {number} Angle in radians. - * @package - */ -Blockly.utils.toRadians = function(angleDegrees) { - return angleDegrees * Math.PI / 180; -}; diff --git a/core/variable_events.js b/core/variable_events.js deleted file mode 100644 index 0dcabce7e1..0000000000 --- a/core/variable_events.js +++ /dev/null @@ -1,259 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2018 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Classes for all types of variable events. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.Events.VarBase'); -goog.provide('Blockly.Events.VarCreate'); -goog.provide('Blockly.Events.VarDelete'); -goog.provide('Blockly.Events.VarRename'); - -goog.require('Blockly.Events'); -goog.require('Blockly.Events.Abstract'); - -goog.require('goog.array'); -goog.require('goog.math.Coordinate'); - - -/** - * Abstract class for a variable event. - * @param {Blockly.VariableModel} variable The variable this event corresponds - * to. - * @extends {Blockly.Events.Abstract} - * @constructor - */ -Blockly.Events.VarBase = function(variable) { - Blockly.Events.VarBase.superClass_.constructor.call(this); - - /** - * The variable id for the variable this event pertains to. - * @type {string} - */ - this.varId = variable.getId(); - this.workspaceId = variable.workspace.id; -}; -goog.inherits(Blockly.Events.VarBase, Blockly.Events.Abstract); - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.VarBase.prototype.toJson = function() { - var json = Blockly.Events.VarBase.superClass_.toJson.call(this); - json['varId'] = this.varId; - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.VarBase.prototype.fromJson = function(json) { - Blockly.Events.VarBase.superClass_.toJson.call(this); - this.varId = json['varId']; -}; - -/** - * Class for a variable creation event. - * @param {Blockly.VariableModel} variable The created variable. - * Null for a blank event. - * @extends {Blockly.Events.VarBase} - * @constructor - */ -Blockly.Events.VarCreate = function(variable) { - if (!variable) { - return; // Blank event to be populated by fromJson. - } - Blockly.Events.VarCreate.superClass_.constructor.call(this, variable); - this.varType = variable.type; - this.varName = variable.name; - this.isLocal = variable.isLocal; - this.isCloud = variable.isCloud; -}; -goog.inherits(Blockly.Events.VarCreate, Blockly.Events.VarBase); - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.VarCreate.prototype.type = Blockly.Events.VAR_CREATE; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.VarCreate.prototype.toJson = function() { - var json = Blockly.Events.VarCreate.superClass_.toJson.call(this); - json['varType'] = this.varType; - json['varName'] = this.varName; - json['isLocal'] = this.isLocal; - json['isCloud'] = this.isCloud; - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.VarCreate.prototype.fromJson = function(json) { - Blockly.Events.VarCreate.superClass_.fromJson.call(this, json); - this.varType = json['varType']; - this.varName = json['varName']; - this.isLocal = json['isLocal']; - this.isCloud = json['isCloud']; -}; - -/** - * Run a variable creation event. - * @param {boolean} forward True if run forward, false if run backward (undo). - */ -Blockly.Events.VarCreate.prototype.run = function(forward) { - var workspace = this.getEventWorkspace_(); - if (forward) { - workspace.createVariable(this.varName, this.varType, this.varId, this.isLocal, this.isCloud); - } else { - workspace.deleteVariableById(this.varId); - } -}; - -/** - * Class for a variable deletion event. - * @param {Blockly.VariableModel} variable The deleted variable. - * Null for a blank event. - * @extends {Blockly.Events.VarBase} - * @constructor - */ -Blockly.Events.VarDelete = function(variable) { - if (!variable) { - return; // Blank event to be populated by fromJson. - } - Blockly.Events.VarDelete.superClass_.constructor.call(this, variable); - this.varType = variable.type; - this.varName = variable.name; - this.isLocal = variable.isLocal; - this.isCloud = variable.isCloud; -}; -goog.inherits(Blockly.Events.VarDelete, Blockly.Events.VarBase); - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.VarDelete.prototype.type = Blockly.Events.VAR_DELETE; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.VarDelete.prototype.toJson = function() { - var json = Blockly.Events.VarDelete.superClass_.toJson.call(this); - json['varType'] = this.varType; - json['varName'] = this.varName; - json['isLocal'] = this.isLocal; - json['isCloud'] = this.isCloud; - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.VarDelete.prototype.fromJson = function(json) { - Blockly.Events.VarDelete.superClass_.fromJson.call(this, json); - this.varType = json['varType']; - this.varName = json['varName']; - this.isLocal = json['isLocal']; - this.isCloud = json['isCloud']; -}; - -/** - * Run a variable deletion event. - * @param {boolean} forward True if run forward, false if run backward (undo). - */ -Blockly.Events.VarDelete.prototype.run = function(forward) { - var workspace = this.getEventWorkspace_(); - if (forward) { - workspace.deleteVariableById(this.varId); - } else { - workspace.createVariable(this.varName, this.varType, this.varId, this.isLocal, this.isCloud); - } -}; - -/** - * Class for a variable rename event. - * @param {Blockly.VariableModel} variable The renamed variable. - * Null for a blank event. - * @param {string} newName The new name the variable will be changed to. - * @extends {Blockly.Events.VarBase} - * @constructor - */ -Blockly.Events.VarRename = function(variable, newName) { - if (!variable) { - return; // Blank event to be populated by fromJson. - } - Blockly.Events.VarRename.superClass_.constructor.call(this, variable); - this.oldName = variable.name; - this.newName = newName; -}; -goog.inherits(Blockly.Events.VarRename, Blockly.Events.VarBase); - -/** - * Type of this event. - * @type {string} - */ -Blockly.Events.VarRename.prototype.type = Blockly.Events.VAR_RENAME; - -/** - * Encode the event as JSON. - * @return {!Object} JSON representation. - */ -Blockly.Events.VarRename.prototype.toJson = function() { - var json = Blockly.Events.VarRename.superClass_.toJson.call(this); - json['oldName'] = this.oldName; - json['newName'] = this.newName; - return json; -}; - -/** - * Decode the JSON event. - * @param {!Object} json JSON representation. - */ -Blockly.Events.VarRename.prototype.fromJson = function(json) { - Blockly.Events.VarRename.superClass_.fromJson.call(this, json); - this.oldName = json['oldName']; - this.newName = json['newName']; -}; - -/** - * Run a variable rename event. - * @param {boolean} forward True if run forward, false if run backward (undo). - */ -Blockly.Events.VarRename.prototype.run = function(forward) { - var workspace = this.getEventWorkspace_(); - if (forward) { - workspace.renameVariableById(this.varId, this.newName); - } else { - workspace.renameVariableById(this.varId, this.oldName); - } -}; diff --git a/core/variable_map.js b/core/variable_map.js deleted file mode 100644 index f391c0938b..0000000000 --- a/core/variable_map.js +++ /dev/null @@ -1,415 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Object representing a map of variables and their types. - * @author marisaleung@google.com (Marisa Leung) - */ -'use strict'; - -goog.provide('Blockly.VariableMap'); - -goog.require('Blockly.Events.VarDelete'); -goog.require('Blockly.Events.VarRename'); -goog.require('Blockly.VariableModel'); - - -/** - * 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 {!Blockly.Workspace} workspace The workspace this map belongs to. - * @constructor - */ -Blockly.VariableMap = function(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 - */ - this.variableMap_ = {}; - - /** - * The workspace this map belongs to. - * @type {!Blockly.Workspace} - */ - this.workspace = workspace; -}; - -/** - * Clear the variable map. - */ -Blockly.VariableMap.prototype.clear = function() { - this.variableMap_ = new Object(null); -}; - -/* Begin functions for renaming variables. */ - -/** - * Rename the given variable by updating its name in the variable map. - * @param {!Blockly.VariableModel} variable Variable to rename. - * @param {string} newName New variable name. - * @package - */ -Blockly.VariableMap.prototype.renameVariable = function(variable, newName) { - var type = variable.type; - var conflictVar = this.getVariable(newName, type); - var blocks = this.workspace.getAllBlocks(); - Blockly.Events.setGroup(true); - try { - if (!conflictVar) { - this.renameVariableAndUses_(variable, newName, blocks); - } else { - // We don't want to rename the variable if one with the exact new name - // already exists. - console.warn('Unexpected conflict when attempting to rename ' + - 'variable with name: ' + variable.name + ' and id: ' + variable.getId() + - ' to new name: ' + newName + '. A variable with the new name already exists' + - ' and has id: ' + conflictVar.getId()); - - } - } finally { - Blockly.Events.setGroup(false); - } -}; - -/** - * 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. - */ -Blockly.VariableMap.prototype.renameVariableById = function(id, newName) { - var variable = this.getVariableById(id); - if (!variable) { - throw new Error('Tried to rename a variable that didn\'t exist. ID: ' + id); - } - - this.renameVariable(variable, newName); -}; - -/** - * 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 {!Blockly.VariableModel} variable Variable to rename. - * @param {string} newName New variable name. - * @param {!Array.} blocks The list of all blocks in the - * workspace. - * @private - */ -Blockly.VariableMap.prototype.renameVariableAndUses_ = function(variable, - newName, blocks) { - Blockly.Events.fire(new Blockly.Events.VarRename(variable, newName)); - variable.name = newName; - for (var i = 0; i < blocks.length; i++) { - blocks[i].updateVarName(variable); - } -}; - -/** - * 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 {!Blockly.VariableModel} variable Variable to rename. - * @param {string} newName New variable name. - * @param {!Blockly.VariableModel} conflictVar The variable that was already - * using newName. - * @param {!Array.} blocks The list of all blocks in the - * workspace. - * @private - */ -Blockly.VariableMap.prototype.renameVariableWithConflict_ = function(variable, - newName, conflictVar, blocks) { - var type = variable.type; - var oldCase = conflictVar.name; - - if (newName != oldCase) { - // Simple rename to change the case and update references. - this.renameVariableAndUses_(conflictVar, newName, blocks); - } - - // These blocks now refer to a different variable. - // These will fire change events. - for (var i = 0; i < blocks.length; i++) { - blocks[i].renameVarById(variable.getId(), conflictVar.getId()); - } - - // Finally delete the original variable, which is now unreferenced. - Blockly.Events.fire(new Blockly.Events.VarDelete(variable)); - // And remove it from the list. - var variableList = this.getVariablesOfType(type); - var variableIndex = variableList.indexOf(variable); - this.variableMap_[type].splice(variableIndex, 1); - -}; - -/* End functions for renaming variabless. */ - -/** - * 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 - * each variable type. - * @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. - * @param {boolean=} opt_isLocal Whether the variable is locally scoped. - * @param {boolean=} opt_isCloud Whether the variable is a cloud variable. - * @return {?Blockly.VariableModel} The newly created variable. - */ -Blockly.VariableMap.prototype.createVariable = function(name, - opt_type, opt_id, opt_isLocal, opt_isCloud) { - var variable = this.getVariable(name, opt_type); - if (variable) { - if (opt_id && variable.getId() != opt_id) { - // There is a variable conflict. Variable conflicts should be eliminated - // in the scratch-vm, or before we get to this point, - // so log a warning, because throwing an error crashes projects. - console.warn('Variable "' + name + '" is already in use and its id is "' - + variable.getId() + '" which conflicts with the passed in ' + - 'id, "' + opt_id + '".'); - } - // The variable already exists and has the same ID. - return variable; - } - if (opt_id) { - variable = this.getVariableById(opt_id); - if (variable) { - console.warn('Variable id, "' + opt_id + '", is already in use.'); - return variable; - } - } - opt_id = opt_id || Blockly.utils.genUid(); - opt_type = opt_type || ''; - - variable = new Blockly.VariableModel(this.workspace, name, opt_type, opt_id, - opt_isLocal, opt_isCloud); - // If opt_type is not a key, create a new list. - if (!this.variableMap_[opt_type]) { - this.variableMap_[opt_type] = [variable]; - } else { - // Else append the variable to the preexisting list. - this.variableMap_[opt_type].push(variable); - } - return variable; -}; - -/* Begin functions for variable deletion. */ - -/** - * Delete a variable. - * @param {Blockly.VariableModel} variable Variable to delete. - */ -Blockly.VariableMap.prototype.deleteVariable = function(variable) { - var variableList = this.variableMap_[variable.type]; - for (var i = 0, tempVar; tempVar = variableList[i]; i++) { - if (tempVar.getId() == variable.getId()) { - variableList.splice(i, 1); - Blockly.Events.fire(new Blockly.Events.VarDelete(variable)); - return; - } - } -}; - -/** - * Delete a variable and all of its uses from this workspace by the passed - * in ID. May prompt the user for confirmation. - * @param {string} id ID of variable to delete. - */ -Blockly.VariableMap.prototype.deleteVariableById = function(id) { - var variable = this.getVariableById(id); - if (variable) { - // Check whether this variable is a function parameter before deleting. - var variableName = variable.name; - var uses = this.getVariableUsesById(id); - for (var i = 0, block; block = uses[i]; i++) { - if (block.type == Blockly.PROCEDURES_DEFINITION_BLOCK_TYPE || - block.type == 'procedures_defreturn') { - var procedureName = block.getFieldValue('NAME'); - var deleteText = Blockly.Msg.CANNOT_DELETE_VARIABLE_PROCEDURE. - replace('%1', variableName). - replace('%2', procedureName); - Blockly.alert(deleteText); - return; - } - } - - var map = this; - if (uses.length > 1) { - // Confirm before deleting multiple blocks. - var confirmText = Blockly.Msg.DELETE_VARIABLE_CONFIRMATION. - replace('%1', String(uses.length)). - replace('%2', variableName); - Blockly.confirm(confirmText, - function(ok) { - if (ok) { - map.deleteVariableInternal_(variable, uses); - } - }); - } else { - // No confirmation necessary for a single block. - map.deleteVariableInternal_(variable, uses); - } - } else { - console.warn("Can't delete non-existent variable: " + id); - } -}; - -/** - * Deletes a variable and all of its uses from this workspace without asking the - * user for confirmation. - * @param {!Blockly.VariableModel} variable Variable to delete. - * @param {!Array.} uses An array of uses of the variable. - * @private - */ -Blockly.VariableMap.prototype.deleteVariableInternal_ = function(variable, - uses) { - var existingGroup = Blockly.Events.getGroup(); - if (!existingGroup) { - Blockly.Events.setGroup(true); - } - try { - for (var i = 0; i < uses.length; i++) { - uses[i].dispose(true, false); - } - this.deleteVariable(variable); - } finally { - if (!existingGroup) { - Blockly.Events.setGroup(false); - } - } -}; - -/* End functions for variable deletion. */ - -/** - * 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 {Blockly.VariableModel} The variable with the given name, or null if - * it was not found. - */ -Blockly.VariableMap.prototype.getVariable = function(name, opt_type) { - var type = opt_type || ''; - var list = this.variableMap_[type]; - if (list) { - for (var j = 0, variable; variable = list[j]; j++) { - if (variable.name == name) { - return variable; - } - } - } - return null; -}; - -/** - * Find the variable by the given ID and return it. Return null if it is not - * found. - * @param {!string} id The id to check for. - * @return {?Blockly.VariableModel} The variable with the given id. - */ -Blockly.VariableMap.prototype.getVariableById = function(id) { - var keys = Object.keys(this.variableMap_); - for (var i = 0; i < keys.length; i++ ) { - var key = keys[i]; - for (var j = 0, variable; variable = this.variableMap_[key][j]; j++) { - if (variable.getId() == id) { - return variable; - } - } - } - return 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. - */ -Blockly.VariableMap.prototype.getVariablesOfType = function(type) { - type = type || ''; - var variable_list = this.variableMap_[type]; - if (variable_list) { - return variable_list.slice(); - } - return []; -}; - -/** - * Return all variable types. This list always contains the empty string. - * @return {!Array.} List of variable types. - * @package - */ -Blockly.VariableMap.prototype.getVariableTypes = function() { - var types = Object.keys(this.variableMap_); - var hasEmpty = false; - for (var i = 0; i < types.length; i++) { - if (types[i] == '') { - hasEmpty = true; - } - } - if (!hasEmpty) { - types.push(''); - } - return types; -}; - -/** - * Return all variables of all types. - * @return {!Array.} List of variable models. - */ -Blockly.VariableMap.prototype.getAllVariables = function() { - var all_variables = []; - var keys = Object.keys(this.variableMap_); - for (var i = 0; i < keys.length; i++ ) { - all_variables = all_variables.concat(this.variableMap_[keys[i]]); - } - return all_variables; -}; - -/** - * Find all the uses of a named variable. - * @param {string} id ID of the variable to find. - * @return {!Array.} Array of block usages. - */ -Blockly.VariableMap.prototype.getVariableUsesById = function(id) { - var uses = []; - var blocks = this.workspace.getAllBlocks(); - // Iterate through every block and check the name. - for (var i = 0; i < blocks.length; i++) { - var blockVariables = blocks[i].getVarModels(); - if (blockVariables) { - for (var j = 0; j < blockVariables.length; j++) { - if (blockVariables[j].getId() == id) { - uses.push(blocks[i]); - } - } - } - } - return uses; -}; diff --git a/core/variable_model.js b/core/variable_model.js deleted file mode 100644 index 4c62d8c624..0000000000 --- a/core/variable_model.js +++ /dev/null @@ -1,116 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Components for the variable model. - * @author marisaleung@google.com (Marisa Leung) - */ -'use strict'; - -goog.provide('Blockly.VariableModel'); - -goog.require('Blockly.Events.VarCreate'); - -goog.require('goog.string'); - - -/** - * Class for a variable model. - * Holds information for the variable including name, ID, and type. - * @param {!Blockly.Workspace} workspace The variable's workspace. - * @param {!string} name The name of the variable. This must be unique across - * each variable type. - * @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. - * @param {boolean=} opt_isLocal Whether the variable is locally scoped. - * @param {boolean=} opt_isCloud Whether the variable is a cloud variable. - * @see {Blockly.FieldVariable} - * @constructor - */ -Blockly.VariableModel = function(workspace, name, opt_type, opt_id, - opt_isLocal, opt_isCloud) { - /** - * The workspace the variable is in. - * @type {!Blockly.Workspace} - */ - this.workspace = workspace; - - /** - * The name of the variable, typically defined by the user. It must be - * unique across all names used for procedures and variables. It may be - * changed by the user. - * @type {string} - */ - this.name = name; - - /** - * 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} - */ - this.type = opt_type || ''; - - /** - * 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 - */ - this.id_ = opt_id || Blockly.utils.genUid(); - - /** - * Whether this variable is locally scoped. - * @package - */ - this.isLocal = opt_isLocal || false; - - /** - * Whether the variable is a cloud variable. - * @package - */ - this.isCloud = opt_isCloud || false; - - Blockly.Events.fire(new Blockly.Events.VarCreate(this)); -}; - -/** - * @return {!string} The ID for the variable. - */ -Blockly.VariableModel.prototype.getId = function() { - return this.id_; -}; - -/** - * A custom compare function for the VariableModel objects. - * @param {Blockly.VariableModel} var1 First variable to compare. - * @param {Blockly.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 - */ -Blockly.VariableModel.compareByName = function(var1, var2) { - return Blockly.scratchBlocksUtils.compareStrings(var1.name, var2.name); -}; diff --git a/core/variables.js b/core/variables.js deleted file mode 100644 index 9c1155cded..0000000000 --- a/core/variables.js +++ /dev/null @@ -1,674 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Utility functions for handling variables. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -/** - * @name Blockly.Variables - * @namespace - **/ -goog.provide('Blockly.Variables'); - -goog.require('Blockly.Blocks'); -goog.require('Blockly.constants'); -goog.require('Blockly.VariableModel'); -goog.require('Blockly.Workspace'); -goog.require('goog.string'); - - -/** - * Constant to separate variable names from procedures and generated functions - * when running generators. - * @deprecated Use Blockly.VARIABLE_CATEGORY_NAME - */ -Blockly.Variables.NAME_TYPE = Blockly.VARIABLE_CATEGORY_NAME; - -/** - * Constant prefix to differentiate cloud variable names from other types - * of variables. - * This is the \u2601 cloud unicode character followed by a space. - * @type {string} - * @package - */ -Blockly.Variables.CLOUD_PREFIX = '☁ '; - -/** - * Find all user-created variables that are in use in the workspace. - * For use by generators. - * @param {!Blockly.Block|!Blockly.Workspace} root Root block or workspace. - * @return {!Array.} Array of variable names. - */ -Blockly.Variables.allUsedVariables = function(root) { - var blocks; - if (root instanceof Blockly.Block) { - // Root is Block. - blocks = root.getDescendants(false); - } else if (root instanceof Blockly.Workspace || - root instanceof Blockly.WorkspaceSvg) { - // Root is Workspace. - blocks = root.getAllBlocks(); - } else { - throw 'Not Block or Workspace: ' + root; - } - - var ignorableName = Blockly.Variables.noVariableText(); - - var variableHash = Object.create(null); - // Iterate through every block and add each variable to the hash. - for (var x = 0; x < blocks.length; x++) { - var blockVariables = blocks[x].getVarModels(); - if (blockVariables) { - for (var y = 0; y < blockVariables.length; y++) { - var variable = blockVariables[y]; - // Variable ID may be null if the block is only half-built. - if (variable.getId() && variable.name.toLowerCase() != ignorableName) { - variableHash[variable.name.toLowerCase()] = variable.name; - } - } - } - } - // Flatten the hash into a list. - var variableList = []; - for (var name in variableHash) { - variableList.push(variableHash[name]); - } - return variableList; -}; - -/** - * Find all variables that the user has created through the workspace or - * toolbox. For use by generators. - * @param {!Blockly.Workspace} root The workspace to inspect. - * @return {!Array.} Array of variable models. - */ -Blockly.Variables.allVariables = function(root) { - if (root instanceof Blockly.Block) { - // Root is Block. - console.warn('Deprecated call to Blockly.Variables.allVariables ' + - 'with a block instead of a workspace. You may want ' + - 'Blockly.Variables.allUsedVariables'); - return {}; - } - return root.getAllVariables(); -}; - -/** - * 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 {!Blockly.Workspace} workspace The workspace to search. - * @return {!Array.} A list of non-duplicated variable names. - * @package - */ -Blockly.Variables.allDeveloperVariables = function(workspace) { - var blocks = workspace.getAllBlocks(); - var hash = {}; - for (var i = 0; i < blocks.length; i++) { - var block = blocks[i]; - if (block.getDeveloperVars) { - var devVars = block.getDeveloperVars(); - for (var j = 0; j < devVars.length; j++) { - hash[devVars[j]] = devVars[j]; - } - } - } - - // Flatten the hash into a list. - var list = []; - for (var name in hash) { - list.push(hash[name]); - } - return list; -}; - -/** -* Return the text that should be used in a field_variable or -* field_variable_getter when no variable exists. -* TODO: #572 -* @return {string} The text to display. - */ -Blockly.Variables.noVariableText = function() { - return "No variable selected"; -}; - -/** -* 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 {!Blockly.Workspace} workspace The workspace to be unique in. -* @return {string} New variable name. -*/ -Blockly.Variables.generateUniqueName = function(workspace) { - var variableList = workspace.getAllVariables(); - var newName = ''; - if (variableList.length) { - var nameSuffix = 1; - var letters = 'ijkmnopqrstuvwxyzabcdefgh'; // No 'l'. - var letterIndex = 0; - var potName = letters.charAt(letterIndex); - while (!newName) { - var inUse = false; - for (var i = 0; i < variableList.length; i++) { - if (variableList[i].name.toLowerCase() == potName) { - // This potential name is already used. - inUse = true; - break; - } - } - if (inUse) { - // Try the next potential name. - letterIndex++; - if (letterIndex == letters.length) { - // Reached the end of the character sequence so back to 'i'. - // a new suffix. - letterIndex = 0; - nameSuffix++; - } - potName = letters.charAt(letterIndex); - if (nameSuffix > 1) { - potName += nameSuffix; - } - } else { - // We can use the current potential name. - newName = potName; - } - } - } else { - newName = 'i'; - } - return newName; -}; - -/** - * Remove any possiblity of conflict/duplication between a real and potential variable. - * When creating a new variable, checks whether the desired name and type already exists - * as a real or potential variable. - * If 'checkReal' is true, checks whether a real variable with the given - * name and type already exists. - * Checks whether a potential variable (using the given 'potentialVarWs') exists. - * If a potential var exists and a real var also exists, discards the potential var - * and returns the real var. - * If a potential var exists and a real var does not exist (or 'checkReal' - * was false), creates the potential var as a real var, - * discards the potential var, and returns the newly created real var. - * If a potential var does not exist, returns null. - * - * @param {string} varName The name of the variable to check for. - * @param {string} varType The type of the variable to check for. - * @param {!Blockly.Workspace} potentialVarWs The workspace containing the - * potential variable map we want to check against. - * @param {boolean} checkReal Whether or not to check if a variable of the given - * name and type exists as a real variable. - * @return {?Blockly.VariableModel} The matching variable, if one already existed - * in the real workspace; the newly transformed variable, if one already - * existed as a potential variable. Null, if no matching variable, real or - * potential, was found. - */ -Blockly.Variables.realizePotentialVar = function(varName, varType, potentialVarWs, - checkReal) { - var potentialVarMap = potentialVarWs.getPotentialVariableMap(); - var realWs = potentialVarWs.targetWorkspace; - if (!potentialVarMap) { - console.warn('Called Blockly.Variables.realizePotentialVar with incorrect ' + - 'workspace. The provided workspace does not have a potential variable map.'); - return; - } - // First check if a variable with the same name and type already exists as a - // real variable. - var realVar; - if (checkReal) { - realVar = Blockly.Variables.getVariable(realWs, null, varName, varType); - } - - // Check if variable with same name and type exists as a potential var - var potentialVar = potentialVarMap.getVariable(varName, varType); - if (!potentialVar) { - return null; - } - - // The potential var exists, so save its id and delete it from the potential - // variable map. - var id = potentialVar.getId(); - potentialVarMap.deleteVariable(potentialVar); - - // Depending on whether a real var already exists or not, either return the - // existing real var or turn the potential var into a new one using its id. - if (realVar) { - return realVar; - } - return realWs.createVariable(varName, varType, id); -}; - -/** - * Create a new variable on the given workspace. - * @param {!Blockly.Workspace} workspace The workspace on which to create the - * variable. - * @param {function(?string=)=} opt_callback An optional callback function to act - * on the id of the variable that is created from the user's input, or null - * if the change is to be aborted (cancel button or an invalid name was provided). - * @param {string} opt_type Optional type of the variable to be created, - * like 'string' or 'list'. - */ -Blockly.Variables.createVariable = function(workspace, opt_callback, opt_type) { - // Decide on a modal message based on the opt_type. If opt_type was not - // provided, default to the original message for scalar variables. - var newMsg, modalTitle; - if (opt_type == Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE) { - newMsg = Blockly.Msg.NEW_BROADCAST_MESSAGE_TITLE; - modalTitle = Blockly.Msg.BROADCAST_MODAL_TITLE; - } else if (opt_type == Blockly.LIST_VARIABLE_TYPE) { - newMsg = Blockly.Msg.NEW_LIST_TITLE; - modalTitle = Blockly.Msg.LIST_MODAL_TITLE; - } else { - // Note: this case covers 1) scalar variables, 2) any new type of - // variable not explicitly checked for above, and 3) a null or undefined - // opt_type -- turns a falsey opt_type into '' - // TODO (#1251) Warn developers that they didn't provide an opt_type/provided - // a falsey opt_type - opt_type = opt_type ? opt_type : ''; - newMsg = Blockly.Msg.NEW_VARIABLE_TITLE; - modalTitle = Blockly.Msg.VARIABLE_MODAL_TITLE; - } - var validate = Blockly.Variables.nameValidator_.bind(null, opt_type); - - // Prompt the user to enter a name for the variable - Blockly.prompt(newMsg, '', - function(text, additionalVars, variableOptions) { - variableOptions = variableOptions || {}; - var scope = variableOptions.scope; - var isLocal = (scope === 'local') || false; - var isCloud = variableOptions.isCloud || false; - // Default to [] if additionalVars is not provided - additionalVars = additionalVars || []; - // Only use additionalVars for global variable creation. - var additionalVarNames = isLocal ? [] : additionalVars; - - var validatedText = validate(text, workspace, additionalVarNames, isCloud, opt_callback); - if (validatedText) { - // The name is valid according to the type, create the variable - var potentialVarMap = workspace.getPotentialVariableMap(); - var variable; - // This check ensures that if a new variable is being created from a - // workspace that already has a variable of the same name and type as - // a potential variable, that potential variable gets turned into a - // real variable and thus there aren't duplicate options in the field_variable - // dropdown. - if (potentialVarMap && opt_type) { - variable = Blockly.Variables.realizePotentialVar(validatedText, - opt_type, workspace, false); - } - if (!variable) { - variable = workspace.createVariable(validatedText, opt_type, null, isLocal, isCloud); - } - - var flyout = workspace.isFlyout ? workspace : workspace.getFlyout(); - var variableBlockId = variable.getId(); - if (flyout.setCheckboxState) { - flyout.setCheckboxState(variableBlockId, true); - } - - if (opt_callback) { - opt_callback(variableBlockId); - } - } else { - // User canceled prompt without a value. - if (opt_callback) { - opt_callback(null); - } - } - }, modalTitle, opt_type); -}; - -/** - * This function provides a common interface for variable name validation agnostic - * of type. This is so that functions like Blockly.Variables.createVariable and - * Blockly.Variables.renameVariable can call a single function (with a single - * type signature) to validate the user-provided name for a variable. - * @param {string} type The type of the variable for which the provided name - * should be validated. - * @param {string} text The user-provided text that should be validated as a - * variable name. - * @param {!Blockly.Workspace} workspace The workspace on which to validate the - * variable name. This is the workspace used to check whether the variable - * already exists. - * @param {Array} additionalVars A list of additional var names to check - * for conflicts against. - * @param {boolean} isCloud Whether the variable is a cloud variable. - * @param {function(?string=)=} opt_callback An optional function to be called on - * a pre-existing variable of the user-provided name. This function is currently - * only used for broadcast messages. - * @return {string} The validated name according to the parameters given, if - * the name is determined to be valid, or null if the name - * is determined to be invalid/in-use, and the calling function should not - * proceed with creating or renaming the variable. - * @private - */ -Blockly.Variables.nameValidator_ = function(type, text, workspace, additionalVars, - isCloud, opt_callback) { - // The validators for the different variable types require slightly different arguments. - // For broadcast messages, if a broadcast message of the provided name already exists, - // the validator needs to call a function that updates the selected - // field option of the dropdown menu of the block that was used to create the new message. - // For scalar variables and lists, the validator has the same validation behavior, but needs - // to know which type of variable to check for and needs a type-specific error message - // that is displayed when a variable of the given name and type already exists. - - if (type == Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE) { - return Blockly.Variables.validateBroadcastMessageName_(text, workspace, opt_callback); - } else if (type == Blockly.LIST_VARIABLE_TYPE) { - return Blockly.Variables.validateScalarVarOrListName_(text, workspace, additionalVars, false, type, - Blockly.Msg.LIST_ALREADY_EXISTS); - } else { - return Blockly.Variables.validateScalarVarOrListName_(text, workspace, additionalVars, isCloud, type, - Blockly.Msg.VARIABLE_ALREADY_EXISTS); - } -}; - -/** - * Validate the given name as a broadcast message type. - * @param {string} name The name to validate - * @param {!Blockly.Workspace} workspace The workspace the name should be validated - * against. - * @param {function(?string=)=} opt_callback An optional function to call if a broadcast - * message already exists with the given name. This function will be called on the id - * of the existing variable. - * @return {string} The validated name, or null if invalid. - * @private - */ -Blockly.Variables.validateBroadcastMessageName_ = function(name, workspace, opt_callback) { - if (!name) { // no name was provided or the user cancelled the prompt - return null; - } - var variable = workspace.getVariable(name, Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE); - if (variable) { - // If the user provided a name for a broadcast message that already exists, - // use the provided callback function to update the selected option in - // the field of the block that was used to create - // this message. - if (opt_callback) { - opt_callback(variable.getId()); - } - // Return null to signal to the calling function that we do not want to create - // a new variable since one already exists. - return null; - } else { - // The name provided is actually a new name, so the calling - // function should go ahead and create it as a new variable. - return name; - } -}; - -/** - * Validate the given name as a scalar variable or list type. - * This function is also responsible for any user facing error-handling. - * @param {string} name The name to validate - * @param {!Blockly.Workspace} workspace The workspace the name should be validated - * against. - * @param {Array} additionalVars A list of additional variable names to check - * for conflicts against. - * @param {boolean} isCloud Whether the variable is a cloud variable. - * @param {string} type The type to validate the variable as. This should be one of - * Blockly.SCALAR_VARIABLE_TYPE or Blockly.LIST_VARIABLE_TYPE. - * @param {string} errorMsg The type-specific error message the user should see - * if a variable of the validated, given name and type already exists. - * @return {string} The validated name, or null if invalid. - * @private - */ -Blockly.Variables.validateScalarVarOrListName_ = function(name, workspace, additionalVars, - isCloud, type, errorMsg) { - // For scalar variables, we don't want leading or trailing white space - name = Blockly.Variables.trimName_(name); - if (!name) { - return null; - } - if (isCloud) { - name = Blockly.Variables.CLOUD_PREFIX + name; - } - if (workspace.getVariable(name, type) || additionalVars.indexOf(name) >= 0) { - // error - Blockly.alert(errorMsg.replace('%1', name)); - return null; - } else { // trimmed name is valid - return name; - } -}; - -/** - * Rename a variable with the given workspace, variableType, and oldName. - * @param {!Blockly.Workspace} workspace The workspace on which to rename the - * variable. - * @param {Blockly.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. - */ -Blockly.Variables.renameVariable = function(workspace, variable, - opt_callback) { - // Validation and modal message/title depends on the variable type - var promptMsg, modalTitle; - var varType = variable.type; - if (varType == Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE) { - console.warn('Unexpected attempt to rename a broadcast message with ' + - 'id: ' + variable.getId() + ' and name: ' + variable.name); - return; - } - if (varType == Blockly.LIST_VARIABLE_TYPE) { - promptMsg = Blockly.Msg.RENAME_LIST_TITLE; - modalTitle = Blockly.Msg.RENAME_LIST_MODAL_TITLE; - } else { - // Default for all other types of variables - promptMsg = Blockly.Msg.RENAME_VARIABLE_TITLE; - modalTitle = Blockly.Msg.RENAME_VARIABLE_MODAL_TITLE; - } - var validate = Blockly.Variables.nameValidator_.bind(null, varType); - - var promptText = promptMsg.replace('%1', variable.name); - var promptDefaultText = variable.name; - if (variable.isCloud && variable.name.indexOf(Blockly.Variables.CLOUD_PREFIX) == 0) { - promptDefaultText = promptDefaultText.substring(Blockly.Variables.CLOUD_PREFIX.length); - } - - Blockly.prompt(promptText, promptDefaultText, - function(newName, additionalVars) { - if (variable.isCloud && - newName.length > 0 && newName.indexOf(Blockly.Variables.CLOUD_PREFIX) == 0) { - newName = newName.substring(Blockly.Variables.CLOUD_PREFIX.length); - // The name validator will add the prefix back - } - additionalVars = additionalVars || []; - var additionalVarNames = variable.isLocal ? [] : additionalVars; - var validatedText = validate(newName, workspace, additionalVarNames, variable.isCloud); - if (validatedText) { - workspace.renameVariableById(variable.getId(), validatedText); - if (opt_callback) { - opt_callback(newName); - } - } else { - // User canceled prompt without a value. - if (opt_callback) { - opt_callback(null); - } - } - }, modalTitle, varType); -}; - -/** - * Strip leading and trailing whitespace from the given name, for use with - * user provided name for scalar variables and lists. - * @param {string} name The user-provided name of the variable. - * @return {string} The trimmed name, or whatever falsey value was originally provided. - */ -Blockly.Variables.trimName_ = function(name) { - if (name) { - return goog.string.trim(name); - } else { - // Return whatever was provided - return name; - } -}; - -/** - * Generate XML string for variable field. - * @param {!Blockly.VariableModel} variableModel The variable model to generate - * an XML string from. - * @param {?string} opt_name The optional name of the field, such as "VARIABLE" - * or "LIST". Defaults to "VARIABLE". - * @return {string} The generated XML. - * @private - */ -Blockly.Variables.generateVariableFieldXml_ = function(variableModel, opt_name) { - // The variable name may be user input, so it may contain characters that need - // to be escaped to create valid XML. - var typeString = variableModel.type; - if (typeString == '') { - typeString = '\'\''; - } - var fieldName = opt_name || 'VARIABLE'; - var text = '' + goog.string.htmlEscape(variableModel.name) + ''; - return text; -}; - -/** - * Helper function to look up or create a variable on the given workspace. - * If no variable exists, creates and returns it. - * @param {!Blockly.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 {!Blockly.VariableModel} The variable corresponding to the given ID - * or name + type combination. - * @package - */ -Blockly.Variables.getOrCreateVariablePackage = function(workspace, id, opt_name, - opt_type) { - var variable = Blockly.Variables.getVariable(workspace, id, opt_name, - opt_type); - if (!variable) { - variable = Blockly.Variables.createVariable_(workspace, id, opt_name, - opt_type); - } - return variable; -}; - -/** - * 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 {!Blockly.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 {?Blockly.VariableModel} The variable corresponding to the given ID - * or name + type combination, or null if not found. - * @package - */ -Blockly.Variables.getVariable = function(workspace, id, opt_name, opt_type) { - var potentialVariableMap = workspace.getPotentialVariableMap(); - // Try to just get the variable, by ID if possible. - if (id) { - // Look in the real variable map before checking the potential variable map. - var variable = workspace.getVariableById(id); - if (!variable && potentialVariableMap) { - variable = potentialVariableMap.getVariableById(id); - } - } else if (opt_name) { - if (opt_type == undefined) { - throw new Error('Tried to look up a variable by name without a type'); - } - // Otherwise look up by name and type. - var variable = workspace.getVariable(opt_name, opt_type); - if (!variable && potentialVariableMap) { - variable = potentialVariableMap.getVariable(opt_name, opt_type); - } - } - return variable; -}; - -/** - * Helper function to create a variable on the given workspace. - * @param {!Blockly.Workspace} workspace The workspace in which to create the - * variable. It may be a flyout workspace or main workspace. - * @param {string} id The ID to use to create the variable, or null. - * @param {string=} opt_name The string to use to create the variable. - * @param {string=} opt_type The type to use to create the variable. - * @return {!Blockly.VariableModel} The variable corresponding to the given ID - * or name + type combination. - * @private - */ -Blockly.Variables.createVariable_ = function(workspace, id, opt_name, - opt_type) { - var potentialVariableMap = workspace.getPotentialVariableMap(); - // Variables without names get uniquely named for this workspace. - if (!opt_name) { - var ws = workspace.isFlyout ? workspace.targetWorkspace : workspace; - opt_name = Blockly.Variables.generateUniqueName(ws); - } - - // Create a potential variable if in the flyout. - if (potentialVariableMap) { - var variable = potentialVariableMap.createVariable(opt_name, opt_type, id); - } else { // In the main workspace, create a real variable. - var variable = workspace.createVariable(opt_name, opt_type, id); - } - return variable; -}; - -/** - * 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 {!Blockly.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. - * @package - */ -Blockly.Variables.getAddedVariables = function(workspace, originalVariables) { - var allCurrentVariables = workspace.getAllVariables(); - var addedVariables = []; - if (originalVariables.length != allCurrentVariables.length) { - for (var i = 0; i < allCurrentVariables.length; i++) { - var variable = allCurrentVariables[i]; - // For any variable that is present in allCurrentVariables but not - // present in originalVariables, add the variable to addedVariables. - if (!originalVariables.includes(variable)) { - addedVariables.push(variable); - } - } - } - return addedVariables; -}; diff --git a/core/warning.js b/core/warning.js deleted file mode 100644 index 8ea805bd31..0000000000 --- a/core/warning.js +++ /dev/null @@ -1,199 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Object representing a warning. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.Warning'); - -goog.require('Blockly.Bubble'); -goog.require('Blockly.Events.Ui'); -goog.require('Blockly.Icon'); - - -/** - * Class for a warning. - * @param {!Blockly.Block} block The block associated with this warning. - * @extends {Blockly.Icon} - * @constructor - */ -Blockly.Warning = function(block) { - Blockly.Warning.superClass_.constructor.call(this, block); - this.createIcon(); - // The text_ object can contain multiple warnings. - this.text_ = {}; -}; -goog.inherits(Blockly.Warning, Blockly.Icon); - -/** - * Does this icon get hidden when the block is collapsed. - */ -Blockly.Warning.prototype.collapseHidden = false; - -/** - * Draw the warning icon. - * @param {!Element} group The icon group. - * @private - */ -Blockly.Warning.prototype.drawIcon_ = function(group) { - // Triangle with rounded corners. - Blockly.utils.createSvgElement('path', - { - 'class': 'blocklyIconShape', - 'd': 'M2,15Q-1,15 0.5,12L6.5,1.7Q8,-1 9.5,1.7L15.5,12Q17,15 14,15z' - }, - group); - // Can't use a real '!' text character since different browsers and operating - // systems render it differently. - // Body of exclamation point. - Blockly.utils.createSvgElement('path', - { - 'class': 'blocklyIconSymbol', - 'd': 'm7,4.8v3.16l0.27,2.27h1.46l0.27,-2.27v-3.16z' - }, - group); - // Dot of exclamation point. - Blockly.utils.createSvgElement('rect', - { - 'class': 'blocklyIconSymbol', - 'x': '7', - 'y': '11', - 'height': '2', - 'width': '2' - }, - group); -}; - -/** - * Create the text for the warning's bubble. - * @param {string} text The text to display. - * @return {!SVGTextElement} The top-level node of the text. - * @private - */ -Blockly.Warning.textToDom_ = function(text) { - var paragraph = /** @type {!SVGTextElement} */ - (Blockly.utils.createSvgElement( - 'text', - { - 'class': 'blocklyText blocklyBubbleText', - 'y': Blockly.Bubble.BORDER_WIDTH - }, - null) - ); - var lines = text.split('\n'); - for (var i = 0; i < lines.length; i++) { - var tspanElement = Blockly.utils.createSvgElement('tspan', - {'dy': '1em', 'x': Blockly.Bubble.BORDER_WIDTH}, paragraph); - var textNode = document.createTextNode(lines[i]); - tspanElement.appendChild(textNode); - } - return paragraph; -}; - -/** - * Show or hide the warning bubble. - * @param {boolean} visible True if the bubble should be visible. - */ -Blockly.Warning.prototype.setVisible = function(visible) { - if (visible == this.isVisible()) { - // No change. - return; - } - Blockly.Events.fire( - new Blockly.Events.Ui(this.block_, 'warningOpen', !visible, visible)); - if (visible) { - // Create the bubble to display all warnings. - var paragraph = Blockly.Warning.textToDom_(this.getText()); - this.bubble_ = new Blockly.Bubble( - /** @type {!Blockly.WorkspaceSvg} */ (this.block_.workspace), - paragraph, this.block_.svgPath_, this.iconXY_, null, null); - if (this.block_.RTL) { - // Right-align the paragraph. - // This cannot be done until the bubble is rendered on screen. - var maxWidth = paragraph.getBBox().width; - for (var i = 0, textElement; textElement = paragraph.childNodes[i]; i++) { - textElement.setAttribute('text-anchor', 'end'); - textElement.setAttribute('x', maxWidth + Blockly.Bubble.BORDER_WIDTH); - } - } - this.updateColour(); - // Bump the warning into the right location. - var size = this.bubble_.getBubbleSize(); - this.bubble_.setBubbleSize(size.width, size.height); - } else { - // Dispose of the bubble. - this.bubble_.dispose(); - this.bubble_ = null; - this.body_ = null; - } -}; - -/** - * Bring the warning to the top of the stack when clicked on. - * @param {!Event} _e Mouse up event. - * @private - */ -Blockly.Warning.prototype.bodyFocus_ = function(_e) { - this.bubble_.promote_(); -}; - -/** - * Set this warning's text. - * @param {string} text Warning text (or '' to delete). - * @param {string} id An ID for this text entry to be able to maintain - * multiple warnings. - */ -Blockly.Warning.prototype.setText = function(text, id) { - if (this.text_[id] == text) { - return; - } - if (text) { - this.text_[id] = text; - } else { - delete this.text_[id]; - } - if (this.isVisible()) { - this.setVisible(false); - this.setVisible(true); - } -}; - -/** - * Get this warning's texts. - * @return {string} All texts concatenated into one string. - */ -Blockly.Warning.prototype.getText = function() { - var allWarnings = []; - for (var id in this.text_) { - allWarnings.push(this.text_[id]); - } - return allWarnings.join('\n'); -}; - -/** - * Dispose of this warning. - */ -Blockly.Warning.prototype.dispose = function() { - this.block_.warning = null; - Blockly.Icon.prototype.dispose.call(this); -}; diff --git a/core/widgetdiv.js b/core/widgetdiv.js deleted file mode 100644 index 44c476a77f..0000000000 --- a/core/widgetdiv.js +++ /dev/null @@ -1,344 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2013 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview A div that floats on top of Blockly. This singleton contains - * temporary HTML UI widgets that the user is currently interacting with. - * E.g. text input areas, colour pickers, context menus. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -/** - * @name Blockly.WidgetDiv - * @namespace - **/ -goog.provide('Blockly.WidgetDiv'); - -goog.require('Blockly.Css'); -goog.require('goog.dom'); -goog.require('goog.dom.TagName'); -goog.require('goog.style'); - - -/** - * The HTML container. Set once by Blockly.WidgetDiv.createDom. - * @type {Element} - */ -Blockly.WidgetDiv.DIV = null; - -/** - * The object currently using this container. - * @type {Object} - * @private - */ -Blockly.WidgetDiv.owner_ = null; - -/** - * Optional cleanup function set by whichever object uses the widget. - * This is called as soon as a dispose is desired. If the dispose should - * be animated, the animation should start on the call of dispose_. - * @type {Function} - * @private - */ -Blockly.WidgetDiv.dispose_ = null; - -/** - * Optional function called at the end of a dispose animation. - * Set by whichever object is using the widget. - * @type {Function} - * @private - */ -Blockly.WidgetDiv.disposeAnimationFinished_ = null; - -/** - * Timer ID for the dispose animation. - * @type {number} - * @private - */ -Blockly.WidgetDiv.disposeAnimationTimer_ = null; - -/** - * Length of time in seconds for the dispose animation. - * @type {number} - * @private - */ -Blockly.WidgetDiv.disposeAnimationTimerLength_ = 0; - - -/** - * Create the widget div and inject it onto the page. - */ -Blockly.WidgetDiv.createDom = function() { - if (Blockly.WidgetDiv.DIV) { - return; // Already created. - } - // Create an HTML container for popup overlays (e.g. editor widgets). - Blockly.WidgetDiv.DIV = - goog.dom.createDom(goog.dom.TagName.DIV, 'blocklyWidgetDiv'); - document.body.appendChild(Blockly.WidgetDiv.DIV); -}; - -/** - * Initialize and display the widget div. Close the old one if needed. - * @param {!Object} newOwner The object that will be using this container. - * @param {boolean} rtl Right-to-left (true) or left-to-right (false). - * @param {Function=} opt_dispose Optional cleanup function to be run when the widget - * is closed. If the dispose is animated, this function must start the animation. - * @param {Function=} opt_disposeAnimationFinished Optional cleanup function to be run - * when the widget is done animating and must disappear. - * @param {number=} opt_disposeAnimationTimerLength Length of animation time in seconds - if a dispose animation is provided. - */ -Blockly.WidgetDiv.show = function(newOwner, rtl, opt_dispose, - opt_disposeAnimationFinished, opt_disposeAnimationTimerLength) { - Blockly.WidgetDiv.hide(); - Blockly.WidgetDiv.owner_ = newOwner; - Blockly.WidgetDiv.dispose_ = opt_dispose; - Blockly.WidgetDiv.disposeAnimationFinished_ = opt_disposeAnimationFinished; - Blockly.WidgetDiv.disposeAnimationTimerLength_ = opt_disposeAnimationTimerLength; - // Temporarily move the widget to the top of the screen so that it does not - // cause a scrollbar jump in Firefox when displayed. - var xy = goog.style.getViewportPageOffset(document); - Blockly.WidgetDiv.DIV.style.top = xy.y + 'px'; - Blockly.WidgetDiv.DIV.style.direction = rtl ? 'rtl' : 'ltr'; - Blockly.WidgetDiv.DIV.style.display = 'block'; -}; - -/** - * Repositions the widgetDiv on window resize. If it doesn't know how to - * calculate the new position, it wll just hide it instead. - */ -Blockly.WidgetDiv.repositionForWindowResize = function() { - // This condition mainly catches the widget div when it is being used as a - // text input. It is important not to close it in this case because on Android, - // when a field is focused, the soft keyboard opens triggering a window resize - // event and we want the widget div to stick around so users can type into it. - if (Blockly.WidgetDiv.owner_ - && Blockly.WidgetDiv.owner_.getScaledBBox_ - && Blockly.WidgetDiv.owner_.getSize) { - var widgetScaledBBox = Blockly.WidgetDiv.owner_.getScaledBBox_(); - var widgetSize = Blockly.WidgetDiv.owner_.getSize(); - Blockly.WidgetDiv.positionInternal_(widgetScaledBBox.left, widgetScaledBBox.top, - widgetSize.height); - } else { - Blockly.WidgetDiv.hide(); - } -}; - -/** - * Destroy the widget and hide the div. - * @param {boolean=} opt_noAnimate If set, animation will not be run for the hide. - */ -Blockly.WidgetDiv.hide = function(opt_noAnimate) { - if (Blockly.WidgetDiv.disposeAnimationTimer_) { - // An animation timer is set already. - // This happens when a previous widget was animating out, - // but Blockly is hiding the widget to create a new one. - // So, short-circuit the animation and clear the timer. - window.clearTimeout(Blockly.WidgetDiv.disposeAnimationTimer_); - Blockly.WidgetDiv.disposeAnimationFinished_ && Blockly.WidgetDiv.disposeAnimationFinished_(); - Blockly.WidgetDiv.disposeAnimationFinished_ = null; - Blockly.WidgetDiv.disposeAnimationTimer_ = null; - Blockly.WidgetDiv.owner_ = null; - Blockly.WidgetDiv.hideAndClearDom_(); - } else if (Blockly.WidgetDiv.isVisible()) { - // No animation timer set, but the widget is visible - // Start animation out (or immediately hide) - Blockly.WidgetDiv.dispose_ && Blockly.WidgetDiv.dispose_(); - Blockly.WidgetDiv.dispose_ = null; - // If we want to animate out, set the appropriate timer for final dispose. - if (Blockly.WidgetDiv.disposeAnimationFinished_ && !opt_noAnimate) { - Blockly.WidgetDiv.disposeAnimationTimer_ = window.setTimeout( - Blockly.WidgetDiv.hide, // Come back to hide and take the first branch. - Blockly.WidgetDiv.disposeAnimationTimerLength_ * 1000 - ); - } else { - // No timer provided (or no animation desired) - auto-hide the DOM now. - Blockly.WidgetDiv.disposeAnimationFinished_ && Blockly.WidgetDiv.disposeAnimationFinished_(); - Blockly.WidgetDiv.disposeAnimationFinished_ = null; - Blockly.WidgetDiv.owner_ = null; - Blockly.WidgetDiv.hideAndClearDom_(); - } - } -}; - -/** - * Hide all DOM for the WidgetDiv, and clear its children. - * @private - */ -Blockly.WidgetDiv.hideAndClearDom_ = function() { - Blockly.WidgetDiv.DIV.style.display = 'none'; - Blockly.WidgetDiv.DIV.style.left = ''; - Blockly.WidgetDiv.DIV.style.top = ''; - Blockly.WidgetDiv.DIV.style.height = ''; - goog.dom.removeChildren(Blockly.WidgetDiv.DIV); -}; - -/** - * Is the container visible? - * @return {boolean} True if visible. - */ -Blockly.WidgetDiv.isVisible = function() { - return !!Blockly.WidgetDiv.owner_; -}; - -/** - * Destroy the widget and hide the div if it is being used by the specified - * object. - * @param {!Object} oldOwner The object that was using this container. - */ -Blockly.WidgetDiv.hideIfOwner = function(oldOwner) { - if (Blockly.WidgetDiv.owner_ == oldOwner) { - Blockly.WidgetDiv.hide(); - } -}; - -/** - * Position the widget at a given location. Prevent the widget from going - * offscreen top or left (right in RTL). - * @param {number} anchorX Horizontal location (window coordinates, not body). - * @param {number} anchorY Vertical location (window coordinates, not body). - * @param {!goog.math.Size} windowSize Height/width of window. - * @param {!goog.math.Coordinate} scrollOffset X/y of window scrollbars. - * @param {boolean} rtl True if RTL, false if LTR. - */ -Blockly.WidgetDiv.position = function(anchorX, anchorY, windowSize, - scrollOffset, rtl) { - // Don't let the widget go above the top edge of the window. - if (anchorY < scrollOffset.y) { - anchorY = scrollOffset.y; - } - if (rtl) { - // Don't let the widget go right of the right edge of the window. - if (anchorX > windowSize.width + scrollOffset.x) { - anchorX = windowSize.width + scrollOffset.x; - } - } else { - // Don't let the widget go left of the left edge of the window. - if (anchorX < scrollOffset.x) { - anchorX = scrollOffset.x; - } - } - Blockly.WidgetDiv.positionInternal_(anchorX, anchorY, windowSize.height); -}; - -/** - * Set the widget div's position and height. This function does nothing clever: - * it will not ensure that your widget div ends up in the visible window. - * @param {number} x Horizontal location (window coordinates, not body). - * @param {number} y Vertical location (window coordinates, not body). - * @param {number} height The height of the widget div (pixels). - * @private - */ -Blockly.WidgetDiv.positionInternal_ = function(x, y, height) { - Blockly.WidgetDiv.DIV.style.left = x + 'px'; - Blockly.WidgetDiv.DIV.style.top = y + 'px'; - Blockly.WidgetDiv.DIV.style.height = height + 'px'; -}; - -/** - * Position the widget div based on an anchor rectangle. - * The widget should be placed adjacent to but not overlapping the anchor - * rectangle. The preferred position is directly below and aligned to the left - * (ltr) or right (rtl) side of the anchor. - * @param {!Object} viewportBBox The bounding rectangle of the current viewport, - * in window coordinates. - * @param {!Object} anchorBBox The bounding rectangle of the anchor, in window - * coordinates. - * @param {!goog.math.Size} widgetSize The size of the widget that is inside the - * widget div, in window coordinates. - * @param {boolean} rtl Whether the workspace is in RTL mode. This determines - * horizontal alignment. - * @package - */ -Blockly.WidgetDiv.positionWithAnchor = function(viewportBBox, anchorBBox, - widgetSize, rtl) { - var y = Blockly.WidgetDiv.calculateY_(viewportBBox, anchorBBox, widgetSize); - var x = Blockly.WidgetDiv.calculateX_(viewportBBox, anchorBBox, widgetSize, - rtl); - - if (y < 0) { - Blockly.WidgetDiv.positionInternal_(x, 0, widgetSize.height + y); - } - else { - Blockly.WidgetDiv.positionInternal_(x, y, widgetSize.height); - } -}; - -/** - * Calculate an x position (in window coordinates) such that the widget will not - * be offscreen on the right or left. - * @param {!Object} viewportBBox The bounding rectangle of the current viewport, - * in window coordinates. - * @param {!Object} anchorBBox The bounding rectangle of the anchor, in window - * coordinates. - * @param {goog.math.Size} widgetSize The dimensions of the widget inside the - * widget div. - * @param {boolean} rtl Whether the Blockly workspace is in RTL mode. - * @return {number} A valid x-coordinate for the top left corner of the widget - * div, in window coordinates. - * @private - */ -Blockly.WidgetDiv.calculateX_ = function(viewportBBox, anchorBBox, widgetSize, - rtl) { - if (rtl) { - // Try to align the right side of the field and the right side of the widget. - var widgetLeft = anchorBBox.right - widgetSize.width; - // Don't go offscreen left. - var x = Math.max(widgetLeft, viewportBBox.left); - // But really don't go offscreen right: - return Math.min(x, viewportBBox.right - widgetSize.width); - } else { - // Try to align the left side of the field and the left side of the widget. - // Don't go offscreen right. - var x = Math.min(anchorBBox.left, - viewportBBox.right - widgetSize.width); - // But left is more important, because that's where the text is. - return Math.max(x, viewportBBox.left); - } -}; - -/** - * Calculate a y position (in window coordinates) such that the widget will not - * be offscreen on the top or bottom. - * @param {!Object} viewportBBox The bounding rectangle of the current viewport, - * in window coordinates. - * @param {!Object} anchorBBox The bounding rectangle of the anchor, in window - * coordinates. - * @param {goog.math.Size} widgetSize The dimensions of the widget inside the - * widget div. - * @return {number} A valid y-coordinate for the top left corner of the widget - * div, in window coordinates. - * @private - */ -Blockly.WidgetDiv.calculateY_ = function(viewportBBox, anchorBBox, widgetSize) { - // Flip the widget vertically if off the bottom. - if (anchorBBox.bottom + widgetSize.height >= - viewportBBox.bottom) { - // The bottom of the widget is at the top of the field. - return anchorBBox.top - widgetSize.height; - // The widget could go off the top of the window, but it would also go off - // the bottom. The window is just too small. - } else { - // The top of the widget is at the bottom of the field. - return anchorBBox.bottom; - } -}; diff --git a/core/workspace.js b/core/workspace.js deleted file mode 100644 index 904315dc3f..0000000000 --- a/core/workspace.js +++ /dev/null @@ -1,673 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Object representing a workspace. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.Workspace'); - -goog.require('Blockly.VariableMap'); -goog.require('Blockly.WorkspaceComment'); -goog.require('goog.array'); -goog.require('goog.math'); - - -/** - * Class for a workspace. This is a data structure that contains blocks. - * There is no UI, and can be created headlessly. - * @param {!Blockly.Options=} opt_options Dictionary of options. - * @constructor - */ -Blockly.Workspace = function(opt_options) { - /** @type {string} */ - this.id = Blockly.utils.genUid(); - Blockly.Workspace.WorkspaceDB_[this.id] = this; - /** @type {!Blockly.Options} */ - this.options = opt_options || {}; - /** @type {boolean} */ - this.RTL = !!this.options.RTL; - /** @type {boolean} */ - this.horizontalLayout = !!this.options.horizontalLayout; - /** @type {number} */ - this.toolboxPosition = this.options.toolboxPosition; - - /** - * @type {!Array.} - * @private - */ - this.topBlocks_ = []; - /** - * @type {!Array.} - * @private - */ - this.topComments_ = []; - /** - * @type {!Object} - * @private - */ - this.commentDB_ = Object.create(null); - /** - * @type {!Array.} - * @private - */ - this.listeners_ = []; - - /** @type {!Array.} */ - this.tapListeners_ = []; - - /** - * @type {!Array.} - * @protected - */ - this.undoStack_ = []; - - /** - * @type {!Array.} - * @protected - */ - this.redoStack_ = []; - - /** - * @type {!Object} - * @private - */ - this.blockDB_ = Object.create(null); - - /** - * @type {!Blockly.VariableMap} - * 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. - * @private - */ - this.variableMap_ = new Blockly.VariableMap(this); - - /** - * 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 Blockly.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 {!Blockly.VariableMap} - * @private - */ - this.potentialVariableMap_ = null; -}; - -/** - * Returns `true` if the workspace is visible and `false` if it's headless. - * @type {boolean} - */ -Blockly.Workspace.prototype.rendered = false; - -/** - * Returns `true` if the workspace is currently in the process of a bulk clear. - * @type {boolean} - * @package - */ -Blockly.Workspace.prototype.isClearing = false; - -/** - * Maximum number of undo events in stack. `0` turns off undo, `Infinity` sets it to unlimited. - * @type {number} - */ -Blockly.Workspace.prototype.MAX_UNDO = 1024; - -// TODO (#1354) Update this function when it is fixed upstream -/** - * Refresh the toolbox. This is a no-op in a non-rendered workspace, - * but may be overriden by subclasses. - * @private - */ -Blockly.Workspace.prototype.refreshToolboxSelection_ = function() { - // No-op. Overriden by subclass. -}; - -/** - * Dispose of this workspace. - * Unlink from all DOM elements to prevent memory leaks. - */ -Blockly.Workspace.prototype.dispose = function() { - this.listeners_.length = 0; - this.clear(); - // Remove from workspace database. - delete Blockly.Workspace.WorkspaceDB_[this.id]; -}; - -/** - * Angle away from the horizontal to sweep for blocks. Order of execution is - * generally top to bottom, but a small angle changes the scan to give a bit of - * a left to right bias (reversed in RTL). Units are in degrees. - * See: http://tvtropes.org/pmwiki/pmwiki.php/Main/DiagonalBilling. - */ -Blockly.Workspace.SCAN_ANGLE = 3; - -/** - * Add a block to the list of top blocks. - * @param {!Blockly.Block} block Block to add. - */ -Blockly.Workspace.prototype.addTopBlock = function(block) { - this.topBlocks_.push(block); -}; - -/** - * Remove a block from the list of top blocks. - * @param {!Blockly.Block} block Block to remove. - */ -Blockly.Workspace.prototype.removeTopBlock = function(block) { - if (!goog.array.remove(this.topBlocks_, block)) { - throw 'Block not present in workspace\'s list of top-most blocks.'; - } -}; - -/** - * 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. - */ -Blockly.Workspace.prototype.getTopBlocks = function(ordered) { - // Copy the topBlocks_ list. - var blocks = [].concat(this.topBlocks_); - if (ordered && blocks.length > 1) { - var offset = Math.sin(goog.math.toRadians(Blockly.Workspace.SCAN_ANGLE)); - if (this.RTL) { - offset *= -1; - } - blocks.sort(function(a, b) { - var aXY = a.getRelativeToSurfaceXY(); - var bXY = b.getRelativeToSurfaceXY(); - return (aXY.y + offset * aXY.x) - (bXY.y + offset * bXY.x); - }); - } - return blocks; -}; - -/** - * Add a comment to the list of top comments. - * @param {!Blockly.WorkspaceComment} comment comment to add. - * @package - */ -Blockly.Workspace.prototype.addTopComment = function(comment) { - this.topComments_.push(comment); - - // Note: If the comment database starts to hold block comments, this may need - // to move to a separate function. - if (this.commentDB_[comment.id]) { - console.warn('Overriding an existing comment on this workspace, with id "' + - comment.id + '"'); - } - this.commentDB_[comment.id] = comment; -}; - -/** - * Remove a comment from the list of top comments. - * @param {!Blockly.WorkspaceComment} comment comment to remove. - * @package - */ -Blockly.Workspace.prototype.removeTopComment = function(comment) { - if (!goog.array.remove(this.topComments_, comment)) { - throw 'Comment not present in workspace\'s list of top-most comments.'; - } - // Note: If the comment database starts to hold block comments, this may need - // to move to a separate function. - delete this.commentDB_[comment.id]; -}; - -/** - * 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 - */ -Blockly.Workspace.prototype.getTopComments = function(ordered) { - // Copy the topComments_ list. - var comments = [].concat(this.topComments_); - if (ordered && comments.length > 1) { - var offset = Math.sin(goog.math.toRadians(Blockly.Workspace.SCAN_ANGLE)); - if (this.RTL) { - offset *= -1; - } - comments.sort(function(a, b) { - var aXY = a instanceof Blockly.ScratchBlockComment ? a.getXY() : a.getRelativeToSurfaceXY(); - var bXY = b instanceof Blockly.ScratchBlockComment ? b.getXY() : b.getRelativeToSurfaceXY(); - return (aXY.y + offset * aXY.x) - (bXY.y + offset * bXY.x); - }); - } - return comments; -}; - -/** - * 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. - */ -Blockly.Workspace.prototype.getAllBlocks = function(ordered) { - if (ordered) { - // Slow, but ordered. - // This gets all levels of descendants because getDescendants - // is called recuusively. They are added to a new list, not the - // list that it's iterating over. - var topBlocks = this.getTopBlocks(true); - var blocks = []; - for (var i = 0; i < topBlocks.length; i++) { - blocks.push.apply(blocks, topBlocks[i].getDescendants(true)); - } - } else { - // Fast, but in no particular order. - // This gets all of levels of descendants by always adding to the - // list that it's iterating over. - var blocks = this.getTopBlocks(false); - for (var i = 0; i < blocks.length; i++) { - blocks.push.apply(blocks, blocks[i].getChildren(false)); - } - } - return blocks; -}; - -/** - * Dispose of all blocks and comments in workspace. - */ -Blockly.Workspace.prototype.clear = function() { - this.isClearing = true; - var existingGroup = Blockly.Events.getGroup(); - if (!existingGroup) { - Blockly.Events.setGroup(true); - } - while (this.topBlocks_.length) { - this.topBlocks_[0].dispose(); - } - while (this.topComments_.length) { - this.topComments_[this.topComments_.length - 1].dispose(); - } - if (!existingGroup) { - Blockly.Events.setGroup(false); - } - this.variableMap_.clear(); - // Any block with a drop-down or WidgetDiv was disposed. - if (Blockly.DropDownDiv) { - Blockly.DropDownDiv.hideWithoutAnimation(); - } - if (Blockly.WidgetDiv) { - Blockly.WidgetDiv.hide(true); - } - if (this.potentialVariableMap_) { - this.potentialVariableMap_.clear(); - } - this.isClearing = false; -}; - -/* Begin functions that are just pass-throughs to the variable map. */ -/** - * 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. - */ -Blockly.Workspace.prototype.renameVariableById = function(id, newName) { - this.variableMap_.renameVariableById(id, newName); -}; - -/** - * 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 - * each variable type. - * @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. - * @param {boolean=} opt_isLocal Whether the variable to create is locally scoped. - * @param {boolean=} opt_isCloud Whether the variable to create is locally scoped. - * @return {?Blockly.VariableModel} The newly created variable. - */ -Blockly.Workspace.prototype.createVariable = function(name, opt_type, opt_id, - opt_isLocal, opt_isCloud) { - return this.variableMap_.createVariable(name, opt_type, opt_id, opt_isLocal, opt_isCloud); -}; - -/** - * 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. - */ -Blockly.Workspace.prototype.getVariableUsesById = function(id) { - return this.variableMap_.getVariableUsesById(id); -}; - -/** - * 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. - */ -Blockly.Workspace.prototype.deleteVariableById = function(id) { - this.variableMap_.deleteVariableById(id); -}; - -/** - * Deletes a variable and all of its uses from this workspace without asking the - * user for confirmation. - * @param {!Blockly.VariableModel} variable Variable to delete. - * @param {!Array.} uses An array of uses of the variable. - * @private - */ -Blockly.Workspace.prototype.deleteVariableInternal_ = function(variable, uses) { - this.variableMap_.deleteVariableInternal_(variable, uses); -}; - -/** - * Check whether a variable exists with the given name. The check is - * case-insensitive. - * @param {string} _name The name to check for. - * @return {number} The index of the name in the variable list, or -1 if it is - * not present. - * @deprecated April 2017 - */ - -Blockly.Workspace.prototype.variableIndexOf = function(_name) { - console.warn( - 'Deprecated call to Blockly.Workspace.prototype.variableIndexOf'); - return -1; -}; - -/** - * Find the variable by the given name and return it. Return null if it is not - * found. - * TODO (#1199): Possibly delete this function. - * @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 {?Blockly.VariableModel} the variable with the given name. - */ -Blockly.Workspace.prototype.getVariable = function(name, opt_type) { - return this.variableMap_.getVariable(name, opt_type); -}; - -/** - * Find the variable by the given ID and return it. Return null if it is not - * found. - * @param {!string} id The ID to check for. - * @return {?Blockly.VariableModel} The variable with the given ID. - */ -Blockly.Workspace.prototype.getVariableById = function(id) { - return this.variableMap_.getVariableById(id); -}; - -/** - * 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. - */ -Blockly.Workspace.prototype.getVariablesOfType = function(type) { - return this.variableMap_.getVariablesOfType(type); -}; - -/** - * Return all variable types. - * @return {!Array.} List of variable types. - * @package - */ -Blockly.Workspace.prototype.getVariableTypes = function() { - return this.variableMap_.getVariableTypes(); -}; - -/** - * Return all variables of all types. - * @return {!Array.} List of variable models. - */ -Blockly.Workspace.prototype.getAllVariables = function() { - return this.variableMap_.getAllVariables(); -}; - -/* End functions that are just pass-throughs to the variable map. */ - -/** - * Returns the horizontal offset of the workspace. - * Intended for LTR/RTL compatibility in XML. - * Not relevant for a headless workspace. - * @return {number} Width. - */ -Blockly.Workspace.prototype.getWidth = function() { - return 0; -}; - -/** - * 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 {!Blockly.Block} The created block. - */ -Blockly.Workspace.prototype.newBlock = function(prototypeName, opt_id) { - return new Blockly.Block(this, prototypeName, opt_id); -}; - -/** - * Undo or redo the previous action. - * @param {boolean} redo False if undo, true if redo. - */ -Blockly.Workspace.prototype.undo = function(redo) { - var inputStack = redo ? this.redoStack_ : this.undoStack_; - var outputStack = redo ? this.undoStack_ : this.redoStack_; - var inputEvent = inputStack.pop(); - if (!inputEvent) { - return; - } - var events = [inputEvent]; - // Do another undo/redo if the next one is of the same group. - while (inputStack.length && inputEvent.group && - inputEvent.group == inputStack[inputStack.length - 1].group) { - events.push(inputStack.pop()); - } - // Push these popped events on the opposite stack. - for (var i = 0, event; event = events[i]; i++) { - outputStack.push(event); - } - events = Blockly.Events.filter(events, redo); - Blockly.Events.recordUndo = false; - if (Blockly.selected) { - Blockly.Events.disable(); - try { - Blockly.selected.unselect(); - } finally { - Blockly.Events.enable(); - } - } - try { - for (var i = 0, event; event = events[i]; i++) { - event.run(redo); - } - } finally { - Blockly.Events.recordUndo = true; - } -}; - -/** - * Clear the undo/redo stacks. - */ -Blockly.Workspace.prototype.clearUndo = function() { - this.undoStack_.length = 0; - this.redoStack_.length = 0; - // Stop any events already in the firing queue from being undoable. - Blockly.Events.clearPendingUndo(); -}; - -/** - * @return {boolean} whether there are any events in the redo stack. - * @package - */ -Blockly.Workspace.prototype.hasRedoStack = function() { - return this.redoStack_.length != 0; -}; - -/** - * @return {boolean} whether there are any events in the undo stack. - * @package - */ -Blockly.Workspace.prototype.hasUndoStack = function() { - return this.undoStack_.length != 0; -}; -/** - * When something in this workspace changes, call a function. - * @param {!Function} func Function to call. - * @return {!Function} Function that can be passed to - * removeChangeListener. - */ -Blockly.Workspace.prototype.addChangeListener = function(func) { - this.listeners_.push(func); - return func; -}; - -/** - * Stop listening for this workspace's changes. - * @param {Function} func Function to stop calling. - */ -Blockly.Workspace.prototype.removeChangeListener = function(func) { - goog.array.remove(this.listeners_, func); -}; - -/** - * Fire a change event. - * @param {!Blockly.Events.Abstract} event Event to fire. - */ -Blockly.Workspace.prototype.fireChangeListener = function(event) { - if (event.recordUndo) { - this.undoStack_.push(event); - this.redoStack_.length = 0; - if (this.undoStack_.length > this.MAX_UNDO) { - this.undoStack_.unshift(); - } - } - // Copy listeners in case a listener attaches/detaches itself. - var currentListeners = this.listeners_.slice(); - for (var i = 0, func; func = currentListeners[i]; i++) { - func(event); - } -}; - -/** - * Find the block on this workspace with the specified ID. - * @param {string} id ID of block to find. - * @return {Blockly.Block} The sought after block or null if not found. - */ -Blockly.Workspace.prototype.getBlockById = function(id) { - var block = this.blockDB_[id]; - if (!block && this.getFlyout() && this.getFlyout().getWorkspace()) { - block = this.getFlyout().getWorkspace().blockDB_[id]; - } - return block || null; -}; - -/** - * Find the comment on this workspace with the specified ID. - * @param {string} id ID of comment to find. - * @return {Blockly.WorkspaceComment} The sought after comment or null if not - * found. - * @package - */ -Blockly.Workspace.prototype.getCommentById = function(id) { - return this.commentDB_[id] || null; -}; - -/** - * Getter for the flyout associated with this workspace. This is null in a - * non-rendered workspace, but may be overriden by subclasses. - * @return {Blockly.Flyout} The flyout on this workspace. - */ -Blockly.Workspace.prototype.getFlyout = function() { - return 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. - */ -Blockly.Workspace.prototype.allInputsFilled = function(opt_shadowBlocksAreFilled) { - var blocks = this.getTopBlocks(false); - for (var i = 0, block; block = blocks[i]; i++) { - if (!block.allInputsFilled(opt_shadowBlocksAreFilled)) { - return false; - } - } - return true; -}; - -/** - * Return the variable map that contains "potential" variables. These exist in - * the flyout but not in the workspace. - * @return {?Blockly.VariableMap} The potential variable map. - * @package - */ -Blockly.Workspace.prototype.getPotentialVariableMap = function() { - return this.potentialVariableMap_; -}; - -/** - * Create and store the potential variable map for this workspace. - * @package - */ -Blockly.Workspace.prototype.createPotentialVariableMap = function() { - this.potentialVariableMap_ = new Blockly.VariableMap(this); -}; - -/** - * Return the map of all variables on the workspace. - * @return {?Blockly.VariableMap} The variable map. - * @package - */ -Blockly.Workspace.prototype.getVariableMap = function() { - return this.variableMap_; -}; - -/** - * Database of all workspaces. - * @private - */ -Blockly.Workspace.WorkspaceDB_ = Object.create(null); - -/** - * Find the workspace with the specified ID. - * @param {string} id ID of workspace to find. - * @return {Blockly.Workspace} The sought after workspace or null if not found. - */ -Blockly.Workspace.getById = function(id) { - return Blockly.Workspace.WorkspaceDB_[id] || null; -}; - -// Export symbols that would otherwise be renamed by Closure compiler. -Blockly.Workspace.prototype['clear'] = Blockly.Workspace.prototype.clear; -Blockly.Workspace.prototype['clearUndo'] = - Blockly.Workspace.prototype.clearUndo; -Blockly.Workspace.prototype['addChangeListener'] = - Blockly.Workspace.prototype.addChangeListener; -Blockly.Workspace.prototype['removeChangeListener'] = - Blockly.Workspace.prototype.removeChangeListener; diff --git a/core/workspace_audio.js b/core/workspace_audio.js deleted file mode 100644 index f81758a03d..0000000000 --- a/core/workspace_audio.js +++ /dev/null @@ -1,170 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Object in charge of loading, storing, and playing audio for a - * workspace. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.WorkspaceAudio'); - -goog.require('goog.userAgent'); - - -/** - * Class for loading, storing, and playing audio for a workspace. - * @param {Blockly.WorkspaceSvg} parentWorkspace The parent of the workspace - * this audio object belongs to, or null. - * @constructor - */ -Blockly.WorkspaceAudio = function(parentWorkspace) { - - /** - * The parent of the workspace this object belongs to, or null. May be - * checked for sounds that this object can't find. - * @type {Blockly.WorkspaceSvg} - * @private - */ - this.parentWorkspace_ = parentWorkspace; - - /** - * Database of pre-loaded sounds. - * @private - * @const - */ - this.SOUNDS_ = Object.create(null); -}; - -/** - * Time that the last sound was played. - * @type {Date} - * @private - */ -Blockly.WorkspaceAudio.prototype.lastSound_ = null; - -/** - * Dispose of this audio manager. - * @package - */ -Blockly.WorkspaceAudio.prototype.dispose = function() { - this.parentWorkspace_ = null; - this.SOUNDS_ = null; -}; - -/** - * Load an audio file. Cache it, ready for instantaneous playing. - * @param {!Array.} filenames List of file types in decreasing order of - * preference (i.e. increasing size). E.g. ['media/go.mp3', 'media/go.wav'] - * Filenames include path from Blockly's root. File extensions matter. - * @param {string} name Name of sound. - * @package - */ -Blockly.WorkspaceAudio.prototype.load = function(filenames, name) { - if (!filenames.length) { - return; - } - try { - var audioTest = new window['Audio'](); - } catch (e) { - // No browser support for Audio. - // IE can throw an error even if the Audio object exists. - return; - } - var sound; - for (var i = 0; i < filenames.length; i++) { - var filename = filenames[i]; - var ext = filename.match(/\.(\w+)$/); - if (ext && audioTest.canPlayType('audio/' + ext[1])) { - // Found an audio format we can play. - sound = new window['Audio'](filename); - break; - } - } - if (sound && sound.play) { - this.SOUNDS_[name] = sound; - } -}; - -/** - * Preload all the audio files so that they play quickly when asked for. - * @package - */ -Blockly.WorkspaceAudio.prototype.preload = function() { - for (var name in this.SOUNDS_) { - var sound = this.SOUNDS_[name]; - sound.volume = 0.01; - var playPromise = sound.play(); - - // Edge does not return a promise, so we need to check. - if (playPromise) { - // If we don't wait for the play request to complete before calling pause() we will get an exception: - // Uncaught (in promise) DOMException: The play() request was interrupted by a call to pause(). - // See more: https://developers.google.com/web/updates/2017/06/play-request-was-interrupted - playPromise.then(sound.pause).catch(function() { - // Play without user interaction was prevented. - }); - } else { - sound.pause(); - } - - // iOS can only process one sound at a time. Trying to load more than one - // corrupts the earlier ones. Just load one and leave the others uncached. - if (goog.userAgent.IPAD || goog.userAgent.IPHONE) { - break; - } - } -}; - -/** - * Play a named sound at specified volume. If volume is not specified, - * use full volume (1). - * @param {string} name Name of sound. - * @param {number=} opt_volume Volume of sound (0-1). - */ -Blockly.WorkspaceAudio.prototype.play = function(name, opt_volume) { - var sound = this.SOUNDS_[name]; - if (sound) { - // Don't play one sound on top of another. - var now = new Date; - if (this.lastSound_ != null && - now - this.lastSound_ < Blockly.SOUND_LIMIT) { - return; - } - this.lastSound_ = now; - var mySound; - var ie9 = goog.userAgent.DOCUMENT_MODE && - goog.userAgent.DOCUMENT_MODE === 9; - if (ie9 || goog.userAgent.IPAD || goog.userAgent.ANDROID) { - // Creating a new audio node causes lag in IE9, Android and iPad. Android - // and IE9 refetch the file from the server, iPad uses a singleton audio - // node which must be deleted and recreated for each new audio tag. - mySound = sound; - } else { - mySound = sound.cloneNode(); - } - mySound.volume = (opt_volume === undefined ? 1 : opt_volume); - mySound.play(); - } else if (this.parentWorkspace_) { - // Maybe a workspace on a lower level knows about this sound. - this.parentWorkspace_.getAudioManager().play(name, opt_volume); - } -}; diff --git a/core/workspace_comment.js b/core/workspace_comment.js deleted file mode 100644 index 02c6e3fecd..0000000000 --- a/core/workspace_comment.js +++ /dev/null @@ -1,432 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Object representing a code comment on the workspace. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.WorkspaceComment'); - -goog.require('Blockly.Events.CommentChange'); -goog.require('Blockly.Events.CommentCreate'); -goog.require('Blockly.Events.CommentDelete'); -goog.require('Blockly.Events.CommentMove'); - -goog.require('goog.math.Coordinate'); - - -/** - * Class for a workspace comment. - * @param {!Blockly.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 {boolean} minimized Whether this comment is in the minimized state - * @param {string=} opt_id Optional ID. Use this ID if provided, otherwise - * create a new ID. If the ID conflicts with an in-use ID, a new one will - * be generated. - * @constructor - */ -Blockly.WorkspaceComment = function(workspace, content, height, width, minimized, opt_id) { - /** @type {string} */ - this.id = (opt_id && !workspace.getCommentById(opt_id)) ? - opt_id : Blockly.utils.genUid(); - - workspace.addTopComment(this); - - /** - * The comment's position in workspace units. (0, 0) is at the workspace's - * origin; scale does not change this value. - * @type {!goog.math.Coordinate} - * @protected - */ - this.xy_ = new goog.math.Coordinate(0, 0); - - /** - * The comment's height in workspace units. Scale does not change this value. - * @type {number} - * @private - */ - this.height_ = height; - - /** - * The comment's width in workspace units. Scale does not change this value. - * @type {number} - * @private - */ - this.width_ = width; - - /** - * The comment's minimized state. - * @type{boolean} - * @private - */ - this.isMinimized_ = minimized; - - /** - * @type {!Blockly.Workspace} - */ - this.workspace = workspace; - - /** - * @protected - * @type {boolean} - */ - this.RTL = workspace.RTL; - - /** - * @type {boolean} - * @private - */ - this.deletable_ = true; - - /** - * @type {boolean} - * @private - */ - this.movable_ = true; - - /** - * @protected - * @type {!string} - */ - this.content_ = content; - - /** - * @package - * @type {boolean} - */ - this.isComment = true; - - Blockly.WorkspaceComment.fireCreateEvent(this); -}; - -/** - * Maximum lable length (actual label length will include - * one additional character, the ellipsis). - * @private - */ -Blockly.WorkspaceComment.MAX_LABEL_LENGTH = 12; - -/** - * Maximum character length for comment text. - * @private - */ -Blockly.WorkspaceComment.COMMENT_TEXT_LIMIT = 8000; - -/** - * Dispose of this comment. - * @package - */ -Blockly.WorkspaceComment.prototype.dispose = function() { - if (!this.workspace) { - // The comment has already been deleted. - return; - } - - if (Blockly.Events.isEnabled()) { - Blockly.Events.fire(new Blockly.Events.CommentDelete(this)); - } - - // Remove from the list of top comments and the comment database. - this.workspace.removeTopComment(this); - this.workspace = null; -}; - -// Height, width, x, and y are all stored on even non-rendered comments, to -// preserve state if you pass the contents through a headless workspace. - -/** - * Get comment height. - * @return {number} comment height. - * @package - */ -Blockly.WorkspaceComment.prototype.getHeight = function() { - return this.height_; -}; - -/** - * Set comment height. - * @param {number} height comment height. - * @package - */ -Blockly.WorkspaceComment.prototype.setHeight = function(height) { - this.height_ = height; -}; - -/** - * Get comment width. - * @return {number} comment width. - * @package - */ -Blockly.WorkspaceComment.prototype.getWidth = function() { - return this.width_; -}; - -/** - * Set comment width. - * @param {number} width comment width. - * @package - */ -Blockly.WorkspaceComment.prototype.setWidth = function(width) { - this.width_ = width; -}; - -/** - * Get the height and width of this comment. - * @return {{height: number, width: number}} The height and width of this comment; - * these numbers do not change as the workspace scales. - */ -Blockly.WorkspaceComment.prototype.getHeightWidth = function() { - return {height: this.height_, width: this.width_}; -}; - -/** - * Get stored location. - * @return {!goog.math.Coordinate} The comment's stored location. This is not - * valid if the comment is currently being dragged. - * @package - */ -Blockly.WorkspaceComment.prototype.getXY = function() { - return this.xy_.clone(); -}; - -/** - * Move a comment by a relative offset. - * @param {number} dx Horizontal offset, in workspace units. - * @param {number} dy Vertical offset, in workspace units. - * @package - */ -Blockly.WorkspaceComment.prototype.moveBy = function(dx, dy) { - var event = new Blockly.Events.CommentMove(this); - this.xy_.translate(dx, dy); - event.recordNew(); - Blockly.Events.fire(event); -}; - -/** - * Get whether this comment is deletable or not. - * @return {boolean} True if deletable. - * @package - */ -Blockly.WorkspaceComment.prototype.isDeletable = function() { - return this.deletable_ && - !(this.workspace && this.workspace.options.readOnly); -}; - -/** - * Set whether this comment is deletable or not. - * @param {boolean} deletable True if deletable. - * @package - */ -Blockly.WorkspaceComment.prototype.setDeletable = function(deletable) { - this.deletable_ = deletable; -}; - -/** - * Get whether this comment is movable or not. - * @return {boolean} True if movable. - * @package - */ -Blockly.WorkspaceComment.prototype.isMovable = function() { - return this.movable_ && - !(this.workspace && this.workspace.options.readOnly); -}; - -/** - * Set whether this comment is movable or not. - * @param {boolean} movable True if movable. - * @package - */ -Blockly.WorkspaceComment.prototype.setMovable = function(movable) { - this.movable_ = movable; -}; - -/** - * Returns this comment's text. - * @return {string} Comment text. - * @package - */ -Blockly.WorkspaceComment.prototype.getText = function() { - return this.content_; -}; - -/** - * Set this comment's text content. - * @param {string} text Comment text. - * @package - */ -Blockly.WorkspaceComment.prototype.setText = function(text) { - if (this.content_ != text) { - Blockly.Events.fire(new Blockly.Events.CommentChange( - this, {text: this.content_}, {text: text})); - this.content_ = text; - } -}; - -/** - * Check whether this comment is currently minimized. - * @return {boolean} True if minimized - * @package - */ -Blockly.WorkspaceComment.prototype.isMinimized = function() { - return this.isMinimized_; -}; - -/** - * 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 - */ -Blockly.WorkspaceComment.prototype.toXmlWithXY = function(opt_noId) { - var element = this.toXml(opt_noId); - element.setAttribute('x', Math.round(this.xy_.x)); - element.setAttribute('y', Math.round(this.xy_.y)); - element.setAttribute('h', this.height_); - element.setAttribute('w', this.width_); - return element; -}; - -/** - * Get the truncated text for this comment to display in the minimized - * top bar. - * @return {string} The truncated comment text - * @package - */ -Blockly.WorkspaceComment.prototype.getLabelText = function() { - if (this.content_.length > Blockly.WorkspaceComment.MAX_LABEL_LENGTH) { - if (this.RTL) { - return '\u2026' + this.content_.slice(0, Blockly.WorkspaceComment.MAX_LABEL_LENGTH); - } - return this.content_.slice(0, Blockly.WorkspaceComment.MAX_LABEL_LENGTH) + '\u2026'; - } else { - return this.content_; - } -}; - -/** - * Encode a comment subtree as XML, but don't serialize the XY coordinates or - * width and height. If you need that additional information use toXmlWithXY. - * @param {boolean=} opt_noId True if the encoder should skip the comment id. - * @return {!Element} Tree of XML elements. - * @package - */ -Blockly.WorkspaceComment.prototype.toXml = function(opt_noId) { - var commentElement = goog.dom.createDom('comment'); - if (!opt_noId) { - commentElement.setAttribute('id', this.id); - } - if (this.isMinimized_) { - commentElement.setAttribute('minimized', true); - } - commentElement.textContent = this.getText(); - return commentElement; -}; - -/** - * Fire a create event for the given workspace comment, if comments are enabled. - * @param {!Blockly.WorkspaceComment} comment The comment that was just created. - * @package - */ -Blockly.WorkspaceComment.fireCreateEvent = function(comment) { - if (Blockly.Events.isEnabled()) { - var existingGroup = Blockly.Events.getGroup(); - if (!existingGroup) { - Blockly.Events.setGroup(true); - } - try { - Blockly.Events.fire(new Blockly.Events.CommentCreate(comment)); - } finally { - if (!existingGroup) { - Blockly.Events.setGroup(false); - } - } - } -}; - -/** - * Decode an XML comment tag and create a comment on the workspace. - * @param {!Element} xmlComment XML comment element. - * @param {!Blockly.Workspace} workspace The workspace. - * @return {!Blockly.WorkspaceComment} The created workspace comment. - * @package - */ -Blockly.WorkspaceComment.fromXml = function(xmlComment, workspace) { - var info = Blockly.WorkspaceComment.parseAttributes(xmlComment); - - var comment = new Blockly.WorkspaceComment( - workspace, info.content, info.h, info.w, info.minimized, info.id); - - if (!isNaN(info.x) && !isNaN(info.y)) { - comment.moveBy(info.x, info.y); - } - - Blockly.WorkspaceComment.fireCreateEvent(comment); - return comment; -}; - -/** - * Decode an XML comment tag and return the results in an object. - * @param {!Element} xml XML comment element. - * @return {!Object} An object containing the information about the comment. - * @package - */ -Blockly.WorkspaceComment.parseAttributes = function(xml) { - var xmlH = xml.getAttribute('h'); - var xmlW = xml.getAttribute('w'); - - return { - /* @type {string} */ - id: xml.getAttribute('id'), - /** - * The height of the comment in workspace units, or 100 if not specified. - * @type {number} - */ - h: xmlH ? parseInt(xmlH, 10) : 100, - /** - * The width of the comment in workspace units, or 100 if not specified. - * @type {number} - */ - w: xmlW ? parseInt(xmlW, 10) : 100, - /** - * The x position of the comment in workspace coordinates, or NaN if not - * specified in the XML. - * @type {number} - */ - x: parseInt(xml.getAttribute('x'), 10), - /** - * The y position of the comment in workspace coordinates, or NaN if not - * specified in the XML. - * @type {number} - */ - y: parseInt(xml.getAttribute('y'), 10), - /** - * Whether this comment is minimized. Defaults to false if not specified in - * the XML. - * @type {boolean} - */ - minimized: xml.getAttribute('minimized') == 'true' || false, - /* @type {string} */ - content: xml.textContent - }; -}; diff --git a/core/workspace_comment_render_svg.js b/core/workspace_comment_render_svg.js deleted file mode 100644 index 94cf850ba9..0000000000 --- a/core/workspace_comment_render_svg.js +++ /dev/null @@ -1,723 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Methods for rendering a workspace comment as SVG - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.WorkspaceCommentSvg.render'); - -goog.require('Blockly.WorkspaceCommentSvg'); - -/** - * Radius of the border around the comment. - * @type {number} - * @const - * @private - */ -Blockly.WorkspaceCommentSvg.BORDER_WIDTH = 1; - -/** - * Size of the resize icon. - * @type {number} - * @const - * @private - */ -Blockly.WorkspaceCommentSvg.RESIZE_SIZE = 16; - -/** - * Offset from the foreignobject edge to the textarea edge. - * @type {number} - * @const - * @private - */ -Blockly.WorkspaceCommentSvg.TEXTAREA_OFFSET = 12; - -/** - * The height of the comment top bar. - * @package - */ -Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT = 32; - -/** - * The size of the minimize arrow icon in the comment top bar. - * @private - */ -Blockly.WorkspaceCommentSvg.MINIMIZE_ICON_SIZE = 32; - -/** - * The size of the delete icon in the comment top bar. - * @private - */ -Blockly.WorkspaceCommentSvg.DELETE_ICON_SIZE = 32; - -/** - * The inset for the top bar icons. - * @private - */ -Blockly.WorkspaceCommentSvg.TOP_BAR_ICON_INSET = 0; - -/** - * The bottom corner padding of the resize handle touch target. - * Extends slightly outside the comment box. - * @private - */ -Blockly.WorkspaceCommentSvg.RESIZE_CORNER_PAD = 4; - -/** - * The top/side padding around resize handle touch target. - * Extends about one extra "diagonal" above resize handle. - * @private - */ -Blockly.WorkspaceCommentSvg.RESIZE_OUTER_PAD = 8; - -/** - * Width that a minimized comment should have. - * @private - */ -Blockly.WorkspaceCommentSvg.MINIMIZE_WIDTH = 200; - -/** - * Returns a bounding box describing the dimensions of this comment. - * @return {!{height: number, width: number}} Object with height and width - * properties in workspace units. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.getHeightWidth = function() { - return { width: this.getWidth(), height: this.getHeight() }; -}; - -/** - * Renders the workspace comment. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.render = function() { - if (this.rendered_) { - return; - } - - var size = this.getHeightWidth(); - - // Add text area - this.commentEditor_ = this.createEditor_(); - this.svgGroup_.appendChild(this.commentEditor_); - - this.createCommentTopBar_(); - - this.svgRectTarget_ = Blockly.utils.createSvgElement('rect', - { - 'class': 'blocklyDraggable scratchCommentTarget', - 'x': 0, - 'y': Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT, - 'rx': 4 * Blockly.WorkspaceCommentSvg.BORDER_WIDTH, - 'ry': 4 * Blockly.WorkspaceCommentSvg.BORDER_WIDTH - }); - this.svgGroup_.appendChild(this.svgRectTarget_); - - // Add the resize icon - this.addResizeDom_(); - - // Show / hide relevant things based on minimized state - if (this.isMinimized()) { - this.minimizeArrow_.setAttributeNS('http://www.w3.org/1999/xlink', - 'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'comment-arrow-up.svg'); - this.commentEditor_.setAttribute('display', 'none'); - this.resizeGroup_.setAttribute('display', 'none'); - } else { - this.minimizeArrow_.setAttributeNS('http://www.w3.org/1999/xlink', - 'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'comment-arrow-down.svg'); - this.topBarLabel_.setAttribute('display', 'none'); - } - - this.setSize(size.width, size.height); - - // Set the content - this.textarea_.value = this.content_; - - this.rendered_ = true; - - if (this.resizeGroup_) { - Blockly.bindEventWithChecks_( - this.resizeGroup_, 'mousedown', this, this.resizeMouseDown_); - Blockly.bindEventWithChecks_( - this.resizeGroup_, 'mouseup', this, this.resizeMouseUp_); - } - - Blockly.bindEventWithChecks_( - this.minimizeArrow_, 'mousedown', this, this.minimizeArrowMouseDown_, true); - Blockly.bindEventWithChecks_( - this.minimizeArrow_, 'mouseout', this, this.minimizeArrowMouseOut_, true); - Blockly.bindEventWithChecks_( - this.minimizeArrow_, 'mouseup', this, this.minimizeArrowMouseUp_, true); - Blockly.bindEventWithChecks_( - this.deleteIcon_, 'mousedown', this, this.deleteMouseDown_, true); - Blockly.bindEventWithChecks_( - this.deleteIcon_, 'mouseout', this, this.deleteMouseOut_, true); - Blockly.bindEventWithChecks_( - this.deleteIcon_, 'mouseup', this, this.deleteMouseUp_, true); -}; - -/** - * Create the text area for the comment. - * @return {!Element} The top-level node of the editor. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.createEditor_ = function() { - this.foreignObject_ = Blockly.utils.createSvgElement( - 'foreignObject', - { - 'x': Blockly.WorkspaceCommentSvg.BORDER_WIDTH, - 'y': Blockly.WorkspaceCommentSvg.BORDER_WIDTH + Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT, - 'class': 'scratchCommentForeignObject' - }, - null); - var body = document.createElementNS(Blockly.HTML_NS, 'body'); - body.setAttribute('xmlns', Blockly.HTML_NS); - body.className = 'blocklyMinimalBody scratchCommentBody'; - var textarea = document.createElementNS(Blockly.HTML_NS, 'textarea'); - textarea.className = 'scratchCommentTextarea scratchCommentText'; - textarea.setAttribute('dir', this.RTL ? 'RTL' : 'LTR'); - textarea.setAttribute('maxlength', Blockly.WorkspaceComment.COMMENT_TEXT_LIMIT); - textarea.setAttribute('placeholder', Blockly.Msg.WORKSPACE_COMMENT_DEFAULT_TEXT); - body.appendChild(textarea); - this.textarea_ = textarea; - this.textarea_.style.margin = (Blockly.WorkspaceCommentSvg.TEXTAREA_OFFSET) + 'px'; - this.foreignObject_.appendChild(body); - Blockly.bindEventWithChecks_(textarea, 'mousedown', this, function(e) { - e.stopPropagation(); // Propagation causes preventDefault from workspace handler - }, true, true); - // Don't zoom with mousewheel. - Blockly.bindEventWithChecks_(textarea, 'wheel', this, function(e) { - e.stopPropagation(); - }); - Blockly.bindEventWithChecks_(textarea, 'change', this, function(_e) { - if (this.text_ != textarea.value) { - this.setText(textarea.value); - } - }); - - this.labelText_ = this.getLabelText(); - - return this.foreignObject_; -}; - -/** - * Add the resize icon to the DOM - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.addResizeDom_ = function() { - this.resizeGroup_ = Blockly.utils.createSvgElement( - 'g', - { - 'class': this.RTL ? 'scratchCommentResizeSW' : 'scratchCommentResizeSE' - }, - this.svgGroup_); - var resizeSize = Blockly.WorkspaceCommentSvg.RESIZE_SIZE; - var outerPad = Blockly.ScratchBubble.RESIZE_OUTER_PAD; - var cornerPad = Blockly.ScratchBubble.RESIZE_CORNER_PAD; - // Build an (invisible) triangle that will catch resizes. It is padded on the - // top/left by outerPad, and padded down/right by cornerPad. - Blockly.utils.createSvgElement('polygon', - { - 'points': [ - -outerPad, resizeSize + cornerPad, - resizeSize + cornerPad, resizeSize + cornerPad, - resizeSize + cornerPad, -outerPad - ].join(' ') - }, - this.resizeGroup_); - Blockly.utils.createSvgElement( - 'line', - { - 'class': 'blocklyResizeLine', - 'x1': resizeSize / 3, 'y1': resizeSize - 1, - 'x2': resizeSize - 1, 'y2': resizeSize / 3 - }, this.resizeGroup_); - Blockly.utils.createSvgElement( - 'line', - { - 'class': 'blocklyResizeLine', - 'x1': resizeSize * 2 / 3, 'y1': resizeSize - 1, - 'x2': resizeSize - 1, 'y2': resizeSize * 2 / 3 - }, this.resizeGroup_); -}; - -/** - * Create the comment top bar and its contents. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.createCommentTopBar_ = function() { - this.svgHandleTarget_ = Blockly.utils.createSvgElement('rect', - { - 'class': 'blocklyDraggable scratchCommentTopBar', - 'rx': Blockly.WorkspaceCommentSvg.BORDER_WIDTH, - 'ry': Blockly.WorkspaceCommentSvg.BORDER_WIDTH, - 'height': Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT - }, this.svgGroup_); - - this.createTopBarIcons_(); - this.createTopBarLabel_(); -}; - -/** - * Create the comment top bar label. This is the truncated comment text - * that shows when comment is minimized. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.createTopBarLabel_ = function() { - this.topBarLabel_ = Blockly.utils.createSvgElement('text', - { - 'class': 'scratchCommentText', - 'x': this.width_ / 2, - 'y': (Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT / 2) + Blockly.WorkspaceCommentSvg.BORDER_WIDTH, - 'text-anchor': 'middle', - 'dominant-baseline': 'middle' - }, this.svgGroup_); - - var labelTextNode = document.createTextNode(this.labelText_); - this.topBarLabel_.appendChild(labelTextNode); -}; - -/** - * Create the minimize toggle and delete icons that in the comment top bar. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.createTopBarIcons_ = function() { - var topBarMiddleY = (Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT / 2) + - Blockly.WorkspaceCommentSvg.BORDER_WIDTH; - - // Minimize Toggle Icon in Comment Top Bar - var xInset = Blockly.WorkspaceCommentSvg.TOP_BAR_ICON_INSET; - this.minimizeArrow_ = Blockly.utils.createSvgElement('image', - { - 'x': xInset, - 'y': topBarMiddleY - Blockly.WorkspaceCommentSvg.MINIMIZE_ICON_SIZE / 2, - 'width': Blockly.WorkspaceCommentSvg.MINIMIZE_ICON_SIZE, - 'height': Blockly.WorkspaceCommentSvg.MINIMIZE_ICON_SIZE - }, this.svgGroup_); - - // Delete Icon in Comment Top Bar - this.deleteIcon_ = Blockly.utils.createSvgElement('image', - { - 'x': xInset, - 'y': topBarMiddleY - Blockly.WorkspaceCommentSvg.DELETE_ICON_SIZE / 2, - 'width': Blockly.WorkspaceCommentSvg.DELETE_ICON_SIZE, - 'height': Blockly.WorkspaceCommentSvg.DELETE_ICON_SIZE - }, this.svgGroup_); - this.deleteIcon_.setAttributeNS('http://www.w3.org/1999/xlink', - 'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'delete-x.svg'); -}; - -/** - * Handle a mouse-down on bubble's minimize icon. - * @param {!Event} e Mouse down event. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.minimizeArrowMouseDown_ = function(e) { - // Set a property to indicate that this minimize arrow icon had a mouse down - // event. This property will get reset if the mouse leaves the icon, or when - // a mouse up event occurs on this icon. - this.shouldToggleMinimize_ = true; - e.stopPropagation(); -}; - -/** - * Handle a mouse-out on bubble's minimize icon. - * @param {!Event} _e Mouse out event. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.minimizeArrowMouseOut_ = function(_e) { - // If the mouse leaves the minimize arrow icon, make sure the - // shouldToggleMinimize_ property gets reset. - this.shouldToggleMinimize_ = false; -}; - -/** - * Handle a mouse-up on bubble's minimize icon. - * @param {!Event} e Mouse up event. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.minimizeArrowMouseUp_ = function(e) { - // First check if this is the icon that had a mouse down event on it and that - // the mouse never left the icon. - if (this.shouldToggleMinimize_) { - this.shouldToggleMinimize = false; - this.toggleMinimize_(); - } - e.stopPropagation(); -}; - -/** - * Handle a mouse-down on bubble's minimize icon. - * @param {!Event} e Mouse down event. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.deleteMouseDown_ = function(e) { - // Set a property to indicate that this delete icon had a mouse down event. - // This property will get reset if the mouse leaves the icon, or when - // a mouse up event occurs on this icon. - this.shouldDelete_ = true; - e.stopPropagation(); -}; - -/** - * Handle a mouse-out on bubble's minimize icon. - * @param {!Event} _e Mouse out event. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.deleteMouseOut_ = function(_e) { - // If the mouse leaves the delete icon, reset the shouldDelete_ property. - this.shouldDelete_ = false; -}; - -/** - * Handle a mouse-up on bubble's delete icon. - * @param {!Event} e Mouse up event. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.deleteMouseUp_ = function(e) { - // First check that this same icon had a mouse down event on it and that the - // mouse never left the icon. - if (this.shouldDelete_) { - this.dispose(); - } - e.stopPropagation(); -}; - -/** - * Handle a mouse-down on comment's resize corner. - * @param {!Event} e Mouse down event. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.resizeMouseDown_ = function(e) { - this.resizeStartSize_ = {width: this.width_, height: this.height_}; - this.unbindDragEvents_(); - this.workspace.setResizesEnabled(false); - if (Blockly.utils.isRightButton(e)) { - // No right-click. - e.stopPropagation(); - return; - } - // Left-click (or middle click) - this.workspace.startDrag(e, new goog.math.Coordinate( - this.workspace.RTL ? -this.width_ : this.width_, this.height_)); - - this.onMouseUpWrapper_ = Blockly.bindEventWithChecks_( - document, 'mouseup', this, this.resizeMouseUp_); - this.onMouseMoveWrapper_ = Blockly.bindEventWithChecks_( - document, 'mousemove', this, this.resizeMouseMove_); - Blockly.hideChaff(); - // This event has been handled. No need to bubble up to the document. - e.stopPropagation(); -}; - - -/** - * Set the apperance of the workspace comment bubble to the minimized or full size - * appearance. In the minimized state, the comment should only have the top bar - * displayed, with the minimize icon swapped to the minimized state, and - * truncated comment text is shown in the middle of the top bar. There should be - * no resize handle when the workspace comment is in its minimized state. - * @param {boolean} minimize Whether the bubble should be minimized - * @param {?string} labelText Optional label text for the comment top bar - * when it is minimized. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.setRenderedMinimizeState_ = function(minimize, labelText) { - if (minimize) { - // Change minimize icon - this.minimizeArrow_.setAttributeNS('http://www.w3.org/1999/xlink', - 'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'comment-arrow-up.svg'); - // Hide text area - this.commentEditor_.setAttribute('display', 'none'); - // Hide resize handle if it exists - if (this.resizeGroup_) { - this.resizeGroup_.setAttribute('display', 'none'); - } - if (labelText && this.labelText_ != labelText) { - // Update label and display - // TODO is there a better way to do this? - this.topBarLabel_.textContent = labelText; - } - Blockly.utils.removeAttribute(this.topBarLabel_, 'display'); - } else { - // Change minimize icon - this.minimizeArrow_.setAttributeNS('http://www.w3.org/1999/xlink', - 'xlink:href', Blockly.mainWorkspace.options.pathToMedia + 'comment-arrow-down.svg'); - // Hide label - this.topBarLabel_.setAttribute('display', 'none'); - // Show text area - Blockly.utils.removeAttribute(this.commentEditor_, 'display'); - // Display resize handle if it exists - if (this.resizeGroup_) { - Blockly.utils.removeAttribute(this.resizeGroup_, 'display'); - } - } -}; - -/** - * Stop binding to the global mouseup and mousemove events. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.unbindDragEvents_ = function() { - if (this.onMouseUpWrapper_) { - Blockly.unbindEvent_(this.onMouseUpWrapper_); - this.onMouseUpWrapper_ = null; - } - if (this.onMouseMoveWrapper_) { - Blockly.unbindEvent_(this.onMouseMoveWrapper_); - this.onMouseMoveWrapper_ = null; - } -}; - -/* - * Handle a mouse-up event while dragging a comment's border or resize handle. - * @param {!Event} e Mouse up event. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.resizeMouseUp_ = function(/*e*/) { - Blockly.Touch.clearTouchIdentifier(); - this.unbindDragEvents_(); - var oldHW = this.resizeStartSize_; - this.resizeStartSize_ = null; - if (this.width_ == oldHW.width && this.height_ == oldHW.height) { - return; - } - // Fire a change event for the new width/height after - // resize mouse up - Blockly.Events.fire(new Blockly.Events.CommentChange( - this, {width: oldHW.width , height: oldHW.height}, - {width: this.width_, height: this.height_})); - - this.workspace.setResizesEnabled(true); -}; - -/** - * Resize this comment to follow the mouse. - * @param {!Event} e Mouse move event. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.resizeMouseMove_ = function(e) { - this.autoLayout_ = false; - var newXY = this.workspace.moveDrag(e); - // The call to setSize below emits a CommentChange event, - // but we don't want multiple CommentChange events to be - // emitted while the user is still in the process of resizing - // the comment, so disable events here. The event is emitted in - // resizeMouseUp_. - var disabled = false; - if (Blockly.Events.isEnabled()) { - Blockly.Events.disable(); - disabled = true; - } - this.setSize(this.RTL ? -newXY.x : newXY.x, newXY.y); - if (disabled) { - Blockly.Events.enable(); - } -}; - -/** - * Callback function triggered when the comment has resized. - * Resize the text area accordingly. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.resizeComment_ = function() { - var doubleBorderWidth = 2 * Blockly.WorkspaceCommentSvg.BORDER_WIDTH; - var topOffset = Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT; - var textOffset = Blockly.WorkspaceCommentSvg.TEXTAREA_OFFSET * 2; - - this.foreignObject_.setAttribute('width', - this.width_ - doubleBorderWidth); - this.foreignObject_.setAttribute('height', - this.height_ - doubleBorderWidth - topOffset); - if (this.RTL) { - this.foreignObject_.setAttribute('x', - -this.width_); - } - this.textarea_.style.width = - (this.width_ - textOffset) + 'px'; - this.textarea_.style.height = - (this.height_ - doubleBorderWidth - textOffset - topOffset) + 'px'; -}; - -/** - * Set size - * @param {number} width width of the container - * @param {number} height height of the container - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.setSize = function(width, height) { - var oldWidth = this.width_; - var oldHeight = this.height_; - - var doubleBorderWidth = 2 * Blockly.WorkspaceCommentSvg.BORDER_WIDTH; - - if (this.isMinimized_) { - width = Blockly.WorkspaceCommentSvg.MINIMIZE_WIDTH; - height = Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT; - } else { - // Minimum size of a 'full size' (not minimized) comment. - width = Math.max(width, doubleBorderWidth + 50); - height = Math.max(height, doubleBorderWidth + 20 + Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT); - - // Note we are only updating this.width_ or this.height_ here - // and not in the case above, because when we're minimizing a comment, - // we want to keep track of the width/height of the maximized comment - this.width_ = width; - this.height_ = height; - Blockly.Events.fire(new Blockly.Events.CommentChange(this, - {width: oldWidth, height: oldHeight}, - {width: this.width_, height: this.height_})); - } - this.svgRect_.setAttribute('width', width); - this.svgRect_.setAttribute('height', height); - this.svgRectTarget_.setAttribute('width', width); - this.svgRectTarget_.setAttribute('height', height - Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT); - this.svgHandleTarget_.setAttribute('width', width); - this.svgHandleTarget_.setAttribute('height', Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT); - if (this.RTL) { - this.minimizeArrow_.setAttribute('x', width - - (Blockly.WorkspaceCommentSvg.MINIMIZE_ICON_SIZE) - - Blockly.WorkspaceCommentSvg.TOP_BAR_ICON_INSET); - this.deleteIcon_.setAttribute('x', (-width + - Blockly.WorkspaceCommentSvg.TOP_BAR_ICON_INSET)); - this.svgRect_.setAttribute('transform', 'scale(-1 1)'); - this.svgHandleTarget_.setAttribute('transform', 'scale(-1 1)'); - this.svgHandleTarget_.setAttribute('transform', 'translate(' + -width + ', 1)'); - this.minimizeArrow_.setAttribute('transform', 'translate(' + -width + ', 1)'); - this.deleteIcon_.setAttribute('tranform', 'translate(' + -width + ', 1)'); - this.svgRectTarget_.setAttribute('transform', 'translate(' + -width + ', 1)'); - this.topBarLabel_.setAttribute('transform', 'translate(' + -width + ', 1)'); - } else { - this.deleteIcon_.setAttribute('x', width - - Blockly.WorkspaceCommentSvg.DELETE_ICON_SIZE - - Blockly.WorkspaceCommentSvg.TOP_BAR_ICON_INSET); - } - - var resizeSize = Blockly.WorkspaceCommentSvg.RESIZE_SIZE; - if (this.resizeGroup_) { - if (this.RTL) { - // Mirror the resize group. - this.resizeGroup_.setAttribute('transform', 'translate(' + - (-width + doubleBorderWidth + resizeSize) + ',' + - (height - doubleBorderWidth - resizeSize) + ') scale(-1 1)'); - } else { - this.resizeGroup_.setAttribute('transform', 'translate(' + - (width - doubleBorderWidth - resizeSize) + ',' + - (height - doubleBorderWidth - resizeSize) + ')'); - } - } - - if (this.isMinimized_) { - this.topBarLabel_.setAttribute('x', width / 2); - this.topBarLabel_.setAttribute('y', height / 2); - } - - // Allow the contents to resize. - this.resizeComment_(); -}; - -/** - * Toggle the minimization state of this comment. - * @private - */ -Blockly.WorkspaceComment.prototype.toggleMinimize_ = function() { - this.setMinimized(!this.isMinimized_); -}; - -/** - * Set the minimized state for this comment. If the comment is rendered, - * change the appearance of the comment accordingly. - * @param {boolean} minimize Whether the comment should be minimized - * @package - */ -Blockly.WorkspaceComment.prototype.setMinimized = function(minimize) { - if (this.isMinimized_ == minimize) { - return; - } - Blockly.Events.fire(new Blockly.Events.CommentChange(this, - {minimized: this.isMinimized_}, {minimized: minimize})); - this.isMinimized_ = minimize; - if (minimize) { - if (this.rendered_) { - this.setRenderedMinimizeState_(true, this.getLabelText()); - } - this.setSize(Blockly.WorkspaceCommentSvg.MINIMIZE_WIDTH, - Blockly.WorkspaceCommentSvg.TOP_BAR_HEIGHT); - } else { - if (this.rendered_) { - this.setRenderedMinimizeState_(false); - } - this.setText(this.content_); - this.setSize(this.width_, this.height_); - } -}; - -/** - * Dispose of any rendered comment components. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.disposeInternal_ = function() { - this.textarea_ = null; - this.foreignObject_ = null; - this.svgRect_ = null; - this.svgRectTarget_ = null; - this.svgHandleTarget_ = null; -}; - -/** - * Set the focus on the text area. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.setFocus = function() { - var comment = this; - this.focused_ = true; - comment.textarea_.focus(); - // Defer CSS changes. - setTimeout(function() { - comment.addFocus(); - Blockly.utils.addClass( - comment.svgRectTarget_, 'scratchCommentTargetFocused'); - Blockly.utils.addClass( - comment.svgHandleTarget_, 'scratchCommentHandleTargetFocused'); - }, 0); -}; - -/** - * Remove focus from the text area. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.blurFocus = function() { - var comment = this; - this.focused_ = false; - comment.textarea_.blur(); - // Defer CSS changes. - setTimeout(function() { - if (comment.svgGroup_) { // Could have been deleted in the meantime - comment.removeFocus(); - Blockly.utils.removeClass( - comment.svgRectTarget_, 'scratchCommentTargetFocused'); - Blockly.utils.removeClass( - comment.svgHandleTarget_, 'scratchCommentHandleTargetFocused'); - } - }, 0); -}; diff --git a/core/workspace_comment_svg.js b/core/workspace_comment_svg.js deleted file mode 100644 index 2c7aa2bdc7..0000000000 --- a/core/workspace_comment_svg.js +++ /dev/null @@ -1,611 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Object representing a code comment on a rendered workspace. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.WorkspaceCommentSvg'); - -goog.require('Blockly.Events.CommentCreate'); -goog.require('Blockly.Events.CommentDelete'); -goog.require('Blockly.Events.CommentMove'); -goog.require('Blockly.WorkspaceComment'); - - -/** - * Class for a workspace comment's SVG representation. - * @param {!Blockly.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 {boolean} minimized Whether this comment is minimized. - * @param {string=} opt_id Optional ID. Use this ID if provided, otherwise - * create a new ID. - * @extends {Blockly.WorkspaceComment} - * @constructor - */ -Blockly.WorkspaceCommentSvg = function(workspace, content, height, width, minimized, - opt_id) { - // Create core elements for the block. - /** - * @type {SVGElement} - * @private - */ - this.svgGroup_ = Blockly.utils.createSvgElement( - 'g', {}, null); - this.svgGroup_.translate_ = ''; - - this.svgRect_ = Blockly.utils.createSvgElement( - 'rect', - { - 'class': 'scratchCommentRect scratchWorkspaceCommentBorder', - 'x': 0, - 'y': 0, - 'rx': 4 * Blockly.WorkspaceCommentSvg.BORDER_WIDTH, - 'ry': 4 * Blockly.WorkspaceCommentSvg.BORDER_WIDTH - }); - this.svgGroup_.appendChild(this.svgRect_); - - - /** - * Whether the comment is rendered onscreen and is a part of the DOM. - * @type {boolean} - * @private - */ - this.rendered_ = false; - - /** - * Whether to move the comment to the drag surface when it is dragged. - * True if it should move, false if it should be translated directly. - * @type {boolean} - * @private - */ - this.useDragSurface_ = - Blockly.utils.is3dSupported() && !!workspace.blockDragSurface_; - - Blockly.WorkspaceCommentSvg.superClass_.constructor.call(this, - workspace, content, height, width, minimized, opt_id); - - this.render(); -}; goog.inherits(Blockly.WorkspaceCommentSvg, Blockly.WorkspaceComment); - -/** - * The width and height to use to size a workspace comment when it is first - * added, before it has been edited by the user. - * @type {number} - * @package - */ -Blockly.WorkspaceCommentSvg.DEFAULT_SIZE = 200; - -/** - * Dispose of this comment. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.dispose = function() { - if (!this.workspace) { - // The comment has already been deleted. - return; - } - // If this comment is being deleted, unlink the mouse events. - if (Blockly.selected == this) { - this.unselect(); - this.workspace.cancelCurrentGesture(); - } - - if (Blockly.Events.isEnabled()) { - Blockly.Events.fire(new Blockly.Events.CommentDelete(this)); - } - - goog.dom.removeNode(this.svgGroup_); - // Sever JavaScript to DOM connections. - this.svgGroup_ = null; - this.svgRect_ = null; - // Dispose of any rendered components - this.disposeInternal_(); - - Blockly.Events.disable(); - Blockly.WorkspaceCommentSvg.superClass_.dispose.call(this); - Blockly.Events.enable(); -}; - -/** - * Create and initialize the SVG representation of a workspace comment. - * May be called more than once. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.initSvg = function() { - goog.asserts.assert(this.workspace.rendered, 'Workspace is headless.'); - if (!this.workspace.options.readOnly && !this.eventsInit_) { - Blockly.bindEventWithChecks_( - this.svgRectTarget_, 'mousedown', this, this.pathMouseDown_); - Blockly.bindEventWithChecks_( - this.svgHandleTarget_, 'mousedown', this, this.pathMouseDown_); - } - this.eventsInit_ = true; - - this.updateMovable(); - if (!this.getSvgRoot().parentNode) { - this.workspace.getBubbleCanvas().appendChild(this.getSvgRoot()); - } -}; - -/** - * Handle a mouse-down on an SVG comment. - * @param {!Event} e Mouse down event or touch start event. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.pathMouseDown_ = function(e) { - var gesture = this.workspace.getGesture(e); - if (gesture) { - gesture.handleBubbleStart(e, this); - } -}; - -/** - * Show the context menu for this workspace comment. - * @param {!Event} e Mouse event. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.showContextMenu_ = function(e) { - if (this.workspace.options.readOnly) { - return; - } - // Save the current workspace comment in a variable for use in closures. - var comment = this; - var menuOptions = []; - - if (this.isDeletable() && this.isMovable()) { - menuOptions.push(Blockly.ContextMenu.commentDuplicateOption(comment)); - menuOptions.push(Blockly.ContextMenu.commentDeleteOption(comment)); - } - - Blockly.ContextMenu.show(e, menuOptions, this.RTL); -}; - -/** - * Select this comment. Highlight it visually. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.select = function() { - if (Blockly.selected == this) { - return; - } - var oldId = null; - if (Blockly.selected) { - oldId = Blockly.selected.id; - // Unselect any previously selected block or comment. - Blockly.Events.disable(); - try { - Blockly.selected.unselect(); - } finally { - Blockly.Events.enable(); - } - } - var event = new Blockly.Events.Ui(null, 'selected', oldId, this.id); - event.workspaceId = this.workspace.id; - Blockly.Events.fire(event); - Blockly.selected = this; - this.addSelect(); -}; - -/** - * Unselect this comment. Remove its highlighting. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.unselect = function() { - if (Blockly.selected != this) { - return; - } - var event = new Blockly.Events.Ui(null, 'selected', this.id, null); - event.workspaceId = this.workspace.id; - Blockly.Events.fire(event); - Blockly.selected = null; - this.removeSelect(); -}; - -/** - * Select this comment. Highlight it visually. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.addSelect = function() { - Blockly.utils.addClass( - /** @type {!Element} */ (this.svgGroup_), 'blocklySelected'); - this.setFocus(); -}; - -/** - * Unselect this comment. Remove its highlighting. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.removeSelect = function() { - Blockly.utils.removeClass( - /** @type {!Element} */ (this.svgGroup_), 'blocklySelected'); - this.blurFocus(); -}; - -/** - * Focus this comment. Highlight it visually. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.addFocus = function() { - Blockly.utils.addClass( - /** @type {!Element} */ (this.svgGroup_), 'blocklyFocused'); -}; - -/** - * Unfocus this comment. Remove its highlighting. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.removeFocus = function() { - Blockly.utils.removeClass( - /** @type {!Element} */ (this.svgGroup_), 'blocklyFocused'); -}; - -/** - * Return the coordinates of the top-left corner of this comment relative to the - * drawing surface's origin (0,0), in workspace units. - * If the comment is on the workspace, (0, 0) is the origin of the workspace - * coordinate system. - * This does not change with workspace scale. - * @return {!goog.math.Coordinate} Object with .x and .y properties in - * workspace coordinates. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.getRelativeToSurfaceXY = function() { - var x = 0; - var y = 0; - - var dragSurfaceGroup = this.useDragSurface_ ? - this.workspace.blockDragSurface_.getGroup() : null; - - var element = this.getSvgRoot(); - if (element) { - do { - // Loop through this comment and every parent. - var xy = Blockly.utils.getRelativeXY(element); - x += xy.x; - y += xy.y; - // If this element is the current element on the drag surface, include - // the translation of the drag surface itself. - if (this.useDragSurface_ && - this.workspace.blockDragSurface_.getCurrentBlock() == element) { - var surfaceTranslation = - this.workspace.blockDragSurface_.getSurfaceTranslation(); - x += surfaceTranslation.x; - y += surfaceTranslation.y; - } - element = element.parentNode; - } while (element && element != this.workspace.getBubbleCanvas() && - element != dragSurfaceGroup); - } - this.xy_ = new goog.math.Coordinate(x, y); - return this.xy_; -}; - -/** - * Move a comment by a relative offset. - * @param {number} dx Horizontal offset, in workspace units. - * @param {number} dy Vertical offset, in workspace units. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.moveBy = function(dx, dy) { - var event = new Blockly.Events.CommentMove(this); - // TODO: Do I need to look up the relative to surface XY position here? - var xy = this.getRelativeToSurfaceXY(); - this.translate(xy.x + dx, xy.y + dy); - event.recordNew(); - Blockly.Events.fire(event); - this.workspace.resizeContents(); -}; - -/** - * Transforms a comment by setting the translation on the transform attribute - * of the block's SVG. - * @param {number} x The x coordinate of the translation in workspace units. - * @param {number} y The y coordinate of the translation in workspace units. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.translate = function(x, y) { - this.xy_ = new goog.math.Coordinate(x, y); - this.getSvgRoot().setAttribute('transform', - 'translate(' + x + ',' + y + ')'); -}; - -/** - * Move this comment to its workspace's drag surface, accounting for positioning. - * Generally should be called at the same time as setDragging(true). - * Does nothing if useDragSurface_ is false. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.moveToDragSurface_ = function() { - if (!this.useDragSurface_) { - return; - } - // The translation for drag surface blocks, - // is equal to the current relative-to-surface position, - // to keep the position in sync as it move on/off the surface. - // This is in workspace coordinates. - var xy = this.getRelativeToSurfaceXY(); - this.clearTransformAttributes_(); - this.workspace.blockDragSurface_.translateSurface(xy.x, xy.y); - // Execute the move on the top-level SVG component - this.workspace.blockDragSurface_.setBlocksAndShow(this.getSvgRoot()); -}; - -/** - * Move this comment back to the workspace block canvas. - * Generally should be called at the same time as setDragging(false). - * Does nothing if useDragSurface_ is false. - * @param {!goog.math.Coordinate} newXY The position the comment should take on - * on the workspace canvas, in workspace coordinates. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.moveOffDragSurface_ = function(newXY) { - if (!this.useDragSurface_) { - return; - } - // Translate to current position, turning off 3d. - this.translate(newXY.x, newXY.y); - this.workspace.blockDragSurface_.clearAndHide(this.workspace.getCanvas()); -}; - -/** - * Move this comment during a drag, taking into account whether we are using a - * drag surface to translate blocks. - * @param {?Blockly.BlockDragSurfaceSvg} dragSurface The surface that carries - * rendered items during a drag, or null if no drag surface is in use. - * @param {!goog.math.Coordinate} newLoc The location to translate to, in - * workspace coordinates. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.moveDuringDrag = function(dragSurface, newLoc) { - if (dragSurface) { - dragSurface.translateSurface(newLoc.x, newLoc.y); - } else { - this.svgGroup_.translate_ = 'translate(' + newLoc.x + ',' + newLoc.y + ')'; - this.svgGroup_.setAttribute('transform', - this.svgGroup_.translate_ + this.svgGroup_.skew_); - } -}; - -/** - * Move the bubble group to the specified location in workspace coordinates. - * @param {number} x The x position to move to. - * @param {number} y The y position to move to. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.moveTo = function(x, y) { - this.translate(x, y); -}; - -/** - * Clear the comment of transform="..." attributes. - * Used when the comment is switching from 3d to 2d transform or vice versa. - * @private - */ -Blockly.WorkspaceCommentSvg.prototype.clearTransformAttributes_ = function() { - Blockly.utils.removeAttribute(this.getSvgRoot(), 'transform'); -}; - -/** - * Return the rendered size of the comment or the stored size if the comment is - * not rendered. This differs from getHeightWidth in the behavior of rendered - * minimized comments. This function reports the actual size of the minimized - * comment instead of the full sized comment height/width. - * @return {!{height: number, width: number}} Object with height and width - * properties in workspace units. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.getBubbleSize = function() { - if (this.rendered_) { - return { - width: parseInt(this.svgRect_.getAttribute('width')), - height: parseInt(this.svgRect_.getAttribute('height')) - }; - } else { - this.getHeightWidth(); - } -}; - -/** - * Returns the coordinates of a bounding box describing the dimensions of this - * comment. - * Coordinate system: workspace coordinates. - * @return {!{topLeft: goog.math.Coordinate, bottomRight: goog.math.Coordinate}} - * Object with top left and bottom right coordinates of the bounding box. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.getBoundingRectangle = function() { - var blockXY = this.getRelativeToSurfaceXY(); - var commentBounds = this.getHeightWidth(); - var topLeft; - var bottomRight; - if (this.RTL) { - topLeft = new goog.math.Coordinate(blockXY.x - (commentBounds.width), - blockXY.y); - // Add the width of the tab/puzzle piece knob to the x coordinate - // since X is the corner of the rectangle, not the whole puzzle piece. - bottomRight = new goog.math.Coordinate(blockXY.x, - blockXY.y + commentBounds.height); - } else { - // Subtract the width of the tab/puzzle piece knob to the x coordinate - // since X is the corner of the rectangle, not the whole puzzle piece. - topLeft = new goog.math.Coordinate(blockXY.x, blockXY.y); - bottomRight = new goog.math.Coordinate(blockXY.x + commentBounds.width, - blockXY.y + commentBounds.height); - } - return {topLeft: topLeft, bottomRight: bottomRight}; -}; - -/** - * Add or remove the UI indicating if this comment is movable or not. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.updateMovable = function() { - if (this.isMovable()) { - Blockly.utils.addClass( - /** @type {!Element} */ (this.svgGroup_), 'blocklyDraggable'); - } else { - Blockly.utils.removeClass( - /** @type {!Element} */ (this.svgGroup_), 'blocklyDraggable'); - } -}; - -/** - * Set whether this comment is movable or not. - * @param {boolean} movable True if movable. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.setMovable = function(movable) { - Blockly.WorkspaceCommentSvg.superClass_.setMovable.call(this, movable); - this.updateMovable(); -}; - -/** - * Recursively adds or removes the dragging class to this node and its children. - * @param {boolean} adding True if adding, false if removing. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.setDragging = function(adding) { - if (adding) { - var group = this.getSvgRoot(); - group.translate_ = ''; - group.skew_ = ''; - Blockly.utils.addClass( - /** @type {!Element} */ (this.svgGroup_), 'blocklyDragging'); - } else { - Blockly.utils.removeClass( - /** @type {!Element} */ (this.svgGroup_), 'blocklyDragging'); - } -}; - -/** - * Return the root node of the SVG or null if none exists. - * @return {Element} The root SVG node (probably a group). - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.getSvgRoot = function() { - return this.svgGroup_; -}; - -/** - * Returns this comment's text. - * @return {string} Comment text. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.getText = function() { - return this.textarea_ ? this.textarea_.value : this.content_; -}; - -/** - * Set this comment's text. - * @param {string} text Comment text. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.setText = function(text) { - Blockly.WorkspaceCommentSvg.superClass_.setText.call(this, text); - if (this.textarea_) { - this.textarea_.value = text; - } -}; - -/** - * Update the cursor over this comment by adding or removing a class. - * @param {boolean} enable True if the delete cursor should be shown, false - * otherwise. - * @package - */ -Blockly.WorkspaceCommentSvg.prototype.setDeleteStyle = function(enable) { - if (enable) { - Blockly.utils.addClass( - /** @type {!Element} */ (this.svgGroup_), 'blocklyDraggingDelete'); - } else { - Blockly.utils.removeClass( - /** @type {!Element} */ (this.svgGroup_), 'blocklyDraggingDelete'); - } -}; - -Blockly.WorkspaceCommentSvg.prototype.setAutoLayout = function() { - // NOP for compatibility with the bubble dragger. -}; - -/** - * Decode an XML comment tag and create a rendered comment on the workspace. - * @param {!Element} xmlComment XML comment element. - * @param {!Blockly.Workspace} workspace The workspace. - * @param {number=} opt_wsWidth The width of the workspace, which is used to - * position comments correctly in RTL. - * @return {!Blockly.WorkspaceCommentSvg} The created workspace comment. - * @package - */ -Blockly.WorkspaceCommentSvg.fromXml = function(xmlComment, workspace, - opt_wsWidth) { - Blockly.Events.disable(); - try { - var info = Blockly.WorkspaceComment.parseAttributes(xmlComment); - - var comment = new Blockly.WorkspaceCommentSvg(workspace, - info.content, info.h, info.w, info.minimized, info.id); - if (workspace.rendered) { - comment.initSvg(); - comment.render(false); - } - // Position the comment correctly, taking into account the width of a - // rendered RTL workspace. - if (!isNaN(info.x) && !isNaN(info.y)) { - if (workspace.RTL) { - var wsWidth = opt_wsWidth || workspace.getWidth(); - comment.moveBy(wsWidth - info.x, info.y); - } else { - comment.moveBy(info.x, info.y); - } - } - } finally { - Blockly.Events.enable(); - } - Blockly.WorkspaceComment.fireCreateEvent(comment); - - return comment; -}; - -/** - * 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 - */ -Blockly.WorkspaceCommentSvg.prototype.toXmlWithXY = function(opt_noId) { - var width; // Not used in LTR. - if (this.workspace.RTL) { - // Here be performance dragons: This calls getMetrics(). - width = this.workspace.getWidth(); - } - var element = this.toXml(opt_noId); - var xy = this.getRelativeToSurfaceXY(); - element.setAttribute('x', - Math.round(this.workspace.RTL ? width - xy.x : xy.x)); - element.setAttribute('y', Math.round(xy.y)); - element.setAttribute('h', this.getHeight()); - element.setAttribute('w', this.getWidth()); - return element; -}; diff --git a/core/workspace_drag_surface_svg.js b/core/workspace_drag_surface_svg.js deleted file mode 100644 index 12e5e7e009..0000000000 --- a/core/workspace_drag_surface_svg.js +++ /dev/null @@ -1,195 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2016 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview An SVG that floats on top of the workspace. - * Blocks are moved into this SVG during a drag, improving performance. - * The entire SVG is translated using css translation instead of SVG so the - * blocks are never repainted during drag improving performance. - * @author katelyn@google.com (Katelyn Mann) - */ - -'use strict'; - -goog.provide('Blockly.WorkspaceDragSurfaceSvg'); - -goog.require('Blockly.utils'); - -goog.require('goog.asserts'); -goog.require('goog.math.Coordinate'); - - -/** - * Blocks are moved into this SVG during a drag, improving performance. - * The entire SVG is translated using css transforms instead of SVG so the - * blocks are never repainted during drag improving performance. - * @param {!Element} container Containing element. - * @constructor - */ -Blockly.WorkspaceDragSurfaceSvg = function(container) { - this.container_ = container; - this.createDom(); -}; - -/** - * The SVG drag surface. Set once by Blockly.WorkspaceDragSurfaceSvg.createDom. - * @type {Element} - * @private - */ -Blockly.WorkspaceDragSurfaceSvg.prototype.SVG_ = null; - -/** - * SVG group inside the drag surface that holds blocks while a drag is in - * progress. Blocks are moved here by the workspace at start of a drag and moved - * back into the main SVG at the end of a drag. - * - * @type {Element} - * @private - */ -Blockly.WorkspaceDragSurfaceSvg.prototype.dragGroup_ = null; - -/** - * Containing HTML element; parent of the workspace and the drag surface. - * @type {Element} - * @private - */ -Blockly.WorkspaceDragSurfaceSvg.prototype.container_ = null; - -/** - * Create the drag surface and inject it into the container. - */ -Blockly.WorkspaceDragSurfaceSvg.prototype.createDom = function() { - if (this.SVG_) { - return; // Already created. - } - - /** - * Dom structure when the workspace is being dragged. If there is no drag in - * progress, the SVG is empty and display: none. - * - * - * /g> - * - */ - this.SVG_ = Blockly.utils.createSvgElement('svg', - { - 'xmlns': Blockly.SVG_NS, - 'xmlns:html': Blockly.HTML_NS, - 'xmlns:xlink': 'http://www.w3.org/1999/xlink', - 'version': '1.1', - 'class': 'blocklyWsDragSurface blocklyOverflowVisible' - }, null); - this.container_.appendChild(this.SVG_); -}; - -/** - * Translate the entire drag surface during a drag. - * We translate the drag surface instead of the blocks inside the surface - * so that the browser avoids repainting the SVG. - * Because of this, the drag coordinates must be adjusted by scale. - * @param {number} x X translation for the entire surface - * @param {number} y Y translation for the entire surface - * @package - */ -Blockly.WorkspaceDragSurfaceSvg.prototype.translateSurface = function(x, y) { - // This is a work-around to prevent a the blocks from rendering - // fuzzy while they are being moved on the drag surface. - var fixedX = x.toFixed(0); - var fixedY = y.toFixed(0); - - this.SVG_.style.display = 'block'; - Blockly.utils.setCssTransform( - this.SVG_, 'translate3d(' + fixedX + 'px, ' + fixedY + 'px, 0px)'); -}; - -/** - * Reports the surface translation in scaled workspace coordinates. - * Use this when finishing a drag to return blocks to the correct position. - * @return {!goog.math.Coordinate} Current translation of the surface - * @package - */ -Blockly.WorkspaceDragSurfaceSvg.prototype.getSurfaceTranslation = function() { - return Blockly.utils.getRelativeXY(this.SVG_); -}; - -/** - * Move the blockCanvas and bubbleCanvas out of the surface SVG and on to - * newSurface. - * @param {SVGElement} newSurface The element to put the drag surface contents - * into. - * @package - */ -Blockly.WorkspaceDragSurfaceSvg.prototype.clearAndHide = function(newSurface) { - if (!newSurface) { - throw 'Couldn\'t clear and hide the drag surface: missing new surface.'; - } - var blockCanvas = this.SVG_.childNodes[0]; - var bubbleCanvas = this.SVG_.childNodes[1]; - if (!blockCanvas || !bubbleCanvas || - !Blockly.utils.hasClass(blockCanvas, 'blocklyBlockCanvas') || - !Blockly.utils.hasClass(bubbleCanvas, 'blocklyBubbleCanvas')) { - throw 'Couldn\'t clear and hide the drag surface. A node was missing.'; - } - - // If there is a previous sibling, put the blockCanvas back right afterwards, - // otherwise insert it as the first child node in newSurface. - if (this.previousSibling_ != null) { - Blockly.utils.insertAfter(blockCanvas, this.previousSibling_); - } else { - newSurface.insertBefore(blockCanvas, newSurface.firstChild); - } - - // Reattach the bubble canvas after the blockCanvas. - Blockly.utils.insertAfter(bubbleCanvas, blockCanvas); - // Hide the drag surface. - this.SVG_.style.display = 'none'; - goog.asserts.assert( - this.SVG_.childNodes.length == 0, 'Drag surface was not cleared.'); - Blockly.utils.setCssTransform(this.SVG_, ''); - this.previousSibling_ = null; -}; - -/** - * Set the SVG to have the block canvas and bubble canvas in it and then - * show the surface. - * @param {!Element} blockCanvas The block canvas element from the workspace. - * @param {!Element} bubbleCanvas The element that contains the bubbles. - * @param {?Element} previousSibling The element to insert the block canvas & - bubble canvas after when it goes back in the DOM at the end of a drag. - * @param {number} width The width of the workspace SVG element. - * @param {number} height The height of the workspace SVG element. - * @param {number} scale The scale of the workspace being dragged. - * @package - */ -Blockly.WorkspaceDragSurfaceSvg.prototype.setContentsAndShow = function( - blockCanvas, bubbleCanvas, previousSibling, width, height, scale) { - goog.asserts.assert( - this.SVG_.childNodes.length == 0, 'Already dragging a block.'); - this.previousSibling_ = previousSibling; - // Make sure the blocks and bubble canvas are scaled appropriately. - blockCanvas.setAttribute('transform', 'translate(0, 0) scale(' + scale + ')'); - bubbleCanvas.setAttribute( - 'transform', 'translate(0, 0) scale(' + scale + ')'); - this.SVG_.setAttribute('width', width); - this.SVG_.setAttribute('height', height); - this.SVG_.appendChild(blockCanvas); - this.SVG_.appendChild(bubbleCanvas); - this.SVG_.style.display = 'block'; -}; diff --git a/core/workspace_dragger.js b/core/workspace_dragger.js deleted file mode 100644 index 09cb9b8186..0000000000 --- a/core/workspace_dragger.js +++ /dev/null @@ -1,132 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Methods for dragging a workspace visually. - * @author fenichel@google.com (Rachel Fenichel) - */ -'use strict'; - -goog.provide('Blockly.WorkspaceDragger'); - -goog.require('goog.math.Coordinate'); -goog.require('goog.asserts'); - - -/** - * Class for a workspace dragger. It moves the workspace around when it is - * being dragged by a mouse or touch. - * Note that the workspace itself manages whether or not it has a drag surface - * and how to do translations based on that. This simply passes the right - * commands based on events. - * @param {!Blockly.WorkspaceSvg} workspace The workspace to drag. - * @constructor - */ -Blockly.WorkspaceDragger = function(workspace) { - /** - * @type {!Blockly.WorkspaceSvg} - * @private - */ - this.workspace_ = workspace; - - /** - * The workspace's metrics object at the beginning of the drag. Contains size - * and position metrics of a workspace. - * Coordinate system: pixel coordinates. - * @type {!Object} - * @private - */ - this.startDragMetrics_ = workspace.getMetrics(); - - /** - * The scroll position of the workspace at the beginning of the drag. - * Coordinate system: pixel coordinates. - * @type {!goog.math.Coordinate} - * @private - */ - this.startScrollXY_ = new goog.math.Coordinate( - workspace.scrollX, workspace.scrollY); -}; - -/** - * Sever all links from this object. - * @package - */ -Blockly.WorkspaceDragger.prototype.dispose = function() { - this.workspace_ = null; -}; - -/** - * Start dragging the workspace. - * @package - */ -Blockly.WorkspaceDragger.prototype.startDrag = function() { - if (Blockly.selected) { - Blockly.selected.unselect(); - } - this.workspace_.setupDragSurface(); -}; - -/** - * Finish dragging the workspace and put everything back where it belongs. - * @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has - * moved from the position at the start of the drag, in pixel coordinates. - * @package - */ -Blockly.WorkspaceDragger.prototype.endDrag = function(currentDragDeltaXY) { - // Make sure everything is up to date. - this.drag(currentDragDeltaXY); - this.workspace_.resetDragSurface(); -}; - -/** - * Move the workspace based on the most recent mouse movements. - * @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has - * moved from the position at the start of the drag, in pixel coordinates. - * @package - */ -Blockly.WorkspaceDragger.prototype.drag = function(currentDragDeltaXY) { - var metrics = this.startDragMetrics_; - var newXY = goog.math.Coordinate.sum(this.startScrollXY_, currentDragDeltaXY); - - // Bound the new XY based on workspace bounds. - var x = Math.min(newXY.x, -metrics.contentLeft); - var y = Math.min(newXY.y, -metrics.contentTop); - x = Math.max(x, metrics.viewWidth - metrics.contentLeft - - metrics.contentWidth); - y = Math.max(y, metrics.viewHeight - metrics.contentTop - - metrics.contentHeight); - - x = -x - metrics.contentLeft; - y = -y - metrics.contentTop; - - this.updateScroll_(x, y); -}; - -/** - * Move the scrollbars to drag the workspace. - * x and y are in pixels. - * @param {number} x The new x position to move the scrollbar to. - * @param {number} y The new y position to move the scrollbar to. - * @private - */ -Blockly.WorkspaceDragger.prototype.updateScroll_ = function(x, y) { - this.workspace_.scrollbar.set(x, y); -}; diff --git a/core/workspace_svg.js b/core/workspace_svg.js deleted file mode 100644 index e358f6ec93..0000000000 --- a/core/workspace_svg.js +++ /dev/null @@ -1,2267 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2014 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Object representing a workspace rendered as SVG. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -goog.provide('Blockly.WorkspaceSvg'); - -// TODO(scr): Fix circular dependencies -//goog.require('Blockly.BlockSvg'); -goog.require('Blockly.Colours'); -goog.require('Blockly.ConnectionDB'); -goog.require('Blockly.constants'); -goog.require('Blockly.DataCategory'); -goog.require('Blockly.DropDownDiv'); -goog.require('Blockly.Events.BlockCreate'); -goog.require('Blockly.Gesture'); -goog.require('Blockly.Grid'); -goog.require('Blockly.Options'); -goog.require('Blockly.scratchBlocksUtils'); -goog.require('Blockly.ScrollbarPair'); -goog.require('Blockly.Touch'); -goog.require('Blockly.Trashcan'); -//goog.require('Blockly.VerticalFlyout'); -goog.require('Blockly.Workspace'); -goog.require('Blockly.WorkspaceAudio'); -goog.require('Blockly.WorkspaceComment'); -goog.require('Blockly.WorkspaceCommentSvg'); -goog.require('Blockly.WorkspaceCommentSvg.render'); -goog.require('Blockly.WorkspaceDragSurfaceSvg'); -goog.require('Blockly.Xml'); -goog.require('Blockly.ZoomControls'); - -goog.require('goog.array'); -goog.require('goog.dom'); -goog.require('goog.math.Coordinate'); -goog.require('goog.userAgent'); -goog.require('goog.math.Rect'); - -/** - * Class for a workspace. This is an onscreen area with optional trashcan, - * scrollbars, bubbles, and dragging. - * @param {!Blockly.Options} options Dictionary of options. - * @param {Blockly.BlockDragSurfaceSvg=} opt_blockDragSurface Drag surface for - * blocks. - * @param {Blockly.WorkspaceDragSurfaceSvg=} opt_wsDragSurface Drag surface for - * the workspace. - * @extends {Blockly.Workspace} - * @constructor - */ -Blockly.WorkspaceSvg = function(options, opt_blockDragSurface, opt_wsDragSurface) { - Blockly.WorkspaceSvg.superClass_.constructor.call(this, options); - this.getMetrics = - options.getMetrics || Blockly.WorkspaceSvg.getTopLevelWorkspaceMetrics_; - this.setMetrics = - options.setMetrics || Blockly.WorkspaceSvg.setTopLevelWorkspaceMetrics_; - - Blockly.ConnectionDB.init(this); - - if (opt_blockDragSurface) { - this.blockDragSurface_ = opt_blockDragSurface; - } - - if (opt_wsDragSurface) { - this.workspaceDragSurface_ = opt_wsDragSurface; - } - - this.useWorkspaceDragSurface_ = - this.workspaceDragSurface_ && Blockly.utils.is3dSupported(); - - /** - * List of currently highlighted blocks. Block highlighting is often used to - * visually mark blocks currently being executed. - * @type !Array. - * @private - */ - this.highlightedBlocks_ = []; - - /** - * Object in charge of loading, storing, and playing audio for a workspace. - * @type {Blockly.WorkspaceAudio} - * @private - */ - this.audioManager_ = new Blockly.WorkspaceAudio(options.parentWorkspace); - - /** - * This workspace's grid object or null. - * @type {Blockly.Grid} - * @private - */ - this.grid_ = this.options.gridPattern ? - new Blockly.Grid(options.gridPattern, options.gridOptions) : null; - - this.registerToolboxCategoryCallback(Blockly.VARIABLE_CATEGORY_NAME, - Blockly.DataCategory); - this.registerToolboxCategoryCallback(Blockly.PROCEDURE_CATEGORY_NAME, - Blockly.Procedures.flyoutCategory); -}; -goog.inherits(Blockly.WorkspaceSvg, Blockly.Workspace); - -/** - * A wrapper function called when a resize event occurs. - * You can pass the result to `unbindEvent_`. - * @type {Array.} - */ -Blockly.WorkspaceSvg.prototype.resizeHandlerWrapper_ = null; - -/** - * The render status of an SVG workspace. - * Returns `false` for headless workspaces and true for instances of - * `Blockly.WorkspaceSvg`. - * @type {boolean} - */ -Blockly.WorkspaceSvg.prototype.rendered = true; - -/** - * Whether the workspace is visible. False if the workspace has been hidden - * by calling `setVisible(false)`. - * @type {boolean} - * @private - */ -Blockly.WorkspaceSvg.prototype.isVisible_ = true; - -/** - * Is this workspace the surface for a flyout? - * @type {boolean} - */ -Blockly.WorkspaceSvg.prototype.isFlyout = false; - -/** - * Is this workspace the surface for a mutator? - * @type {boolean} - * @package - */ -Blockly.WorkspaceSvg.prototype.isMutator = false; - -/** - * Whether this workspace has resizes enabled. - * Disable during batch operations for a performance improvement. - * @type {boolean} - * @private - */ -Blockly.WorkspaceSvg.prototype.resizesEnabled_ = true; - -/** - * Whether this workspace has toolbox/flyout refreshes enabled. - * Disable during batch operations for a performance improvement. - * @type {boolean} - * @private - */ -Blockly.WorkspaceSvg.prototype.toolboxRefreshEnabled_ = true; - -/** - * Current horizontal scrolling offset in pixel units. - * @type {number} - */ -Blockly.WorkspaceSvg.prototype.scrollX = 0; - -/** - * Current vertical scrolling offset in pixel units. - * @type {number} - */ -Blockly.WorkspaceSvg.prototype.scrollY = 0; - -/** - * Horizontal scroll value when scrolling started in pixel units. - * @type {number} - */ -Blockly.WorkspaceSvg.prototype.startScrollX = 0; - -/** - * Vertical scroll value when scrolling started in pixel units. - * @type {number} - */ -Blockly.WorkspaceSvg.prototype.startScrollY = 0; - -/** - * Distance from mouse to object being dragged. - * @type {goog.math.Coordinate} - * @private - */ -Blockly.WorkspaceSvg.prototype.dragDeltaXY_ = null; - -/** - * Current scale. - * @type {number} - */ -Blockly.WorkspaceSvg.prototype.scale = 1; - -/** - * The workspace's trashcan (if any). - * @type {Blockly.Trashcan} - */ -Blockly.WorkspaceSvg.prototype.trashcan = null; - -/** - * This workspace's scrollbars, if they exist. - * @type {Blockly.ScrollbarPair} - */ -Blockly.WorkspaceSvg.prototype.scrollbar = null; - -/** - * The current gesture in progress on this workspace, if any. - * @type {Blockly.Gesture} - * @private - */ -Blockly.WorkspaceSvg.prototype.currentGesture_ = null; - -/** - * This workspace's surface for dragging blocks, if it exists. - * @type {Blockly.BlockDragSurfaceSvg} - * @private - */ -Blockly.WorkspaceSvg.prototype.blockDragSurface_ = null; - -/** - * This workspace's drag surface, if it exists. - * @type {Blockly.WorkspaceDragSurfaceSvg} - * @private - */ -Blockly.WorkspaceSvg.prototype.workspaceDragSurface_ = null; - -/** - * Whether to move workspace to the drag surface when it is dragged. - * True if it should move, false if it should be translated directly. - * @type {boolean} - * @private - */ -Blockly.WorkspaceSvg.prototype.useWorkspaceDragSurface_ = false; - -/** - * Whether the drag surface is actively in use. When true, calls to - * translate will translate the drag surface instead of the translating the - * workspace directly. - * This is set to true in setupDragSurface and to false in resetDragSurface. - * @type {boolean} - * @private - */ -Blockly.WorkspaceSvg.prototype.isDragSurfaceActive_ = false; - -/** - * The first parent div with 'injectionDiv' in the name, or null if not set. - * Access this with getInjectionDiv. - * @type {!Element} - * @private - */ -Blockly.WorkspaceSvg.prototype.injectionDiv_ = null; - -/** - * Last known position of the page scroll. - * This is used to determine whether we have recalculated screen coordinate - * stuff since the page scrolled. - * @type {!goog.math.Coordinate} - * @private - */ -Blockly.WorkspaceSvg.prototype.lastRecordedPageScroll_ = null; - -/** - * Map from function names to callbacks, for deciding what to do when a button - * is clicked. - * @type {!Object.} - * @private - */ -Blockly.WorkspaceSvg.prototype.flyoutButtonCallbacks_ = {}; - -/** - * Map from function names to callbacks, for deciding what to do when a custom - * toolbox category is opened. - * @type {!Object.>} - * @private - */ -Blockly.WorkspaceSvg.prototype.toolboxCategoryCallbacks_ = {}; - -/** - * Inverted screen CTM, for use in mouseToSvg. - * @type {SVGMatrix} - * @private - */ -Blockly.WorkspaceSvg.prototype.inverseScreenCTM_ = null; - -/** - * Inverted screen CTM is dirty. - * @type {Boolean} - * @private - */ -Blockly.WorkspaceSvg.prototype.inverseScreenCTMDirty_ = true; - -/** - * Getter for the inverted screen CTM. - * @return {SVGMatrix} The matrix to use in mouseToSvg - */ -Blockly.WorkspaceSvg.prototype.getInverseScreenCTM = function() { - - // Defer getting the screen CTM until we actually need it, this should - // avoid forced reflows from any calls to updateInverseScreenCTM. - if (this.inverseScreenCTMDirty_) { - var ctm = this.getParentSvg().getScreenCTM(); - if (ctm) { - this.inverseScreenCTM_ = ctm.inverse(); - this.inverseScreenCTMDirty_ = false; - } - } - - return this.inverseScreenCTM_; -}; - -/** - * Getter for isVisible - * @return {boolean} Whether the workspace is visible. False if the workspace has been hidden - * by calling `setVisible(false)`. - */ -Blockly.WorkspaceSvg.prototype.isVisible = function() { - return this.isVisible_; -}; - -/** - * Mark the inverse screen CTM as dirty. - */ -Blockly.WorkspaceSvg.prototype.updateInverseScreenCTM = function() { - this.inverseScreenCTMDirty_ = true; -}; - -/** - * Return the absolute coordinates of the top-left corner of this element, - * scales that after canvas SVG element, if it's a descendant. - * The origin (0,0) is the top-left corner of the Blockly SVG. - * @param {!Element} element Element to find the coordinates of. - * @return {!goog.math.Coordinate} Object with .x and .y properties. - * @private - */ -Blockly.WorkspaceSvg.prototype.getSvgXY = function(element) { - var x = 0; - var y = 0; - var scale = 1; - if (goog.dom.contains(this.getCanvas(), element) || - goog.dom.contains(this.getBubbleCanvas(), element)) { - // Before the SVG canvas, scale the coordinates. - scale = this.scale; - } - do { - // Loop through this block and every parent. - var xy = Blockly.utils.getRelativeXY(element); - if (element == this.getCanvas() || - element == this.getBubbleCanvas()) { - // After the SVG canvas, don't scale the coordinates. - scale = 1; - } - x += xy.x * scale; - y += xy.y * scale; - element = element.parentNode; - } while (element && element != this.getParentSvg()); - return new goog.math.Coordinate(x, y); -}; - -/** - * Return the position of the workspace origin relative to the injection div - * origin in pixels. - * The workspace origin is where a block would render at position (0, 0). - * It is not the upper left corner of the workspace SVG. - * @return {!goog.math.Coordinate} Offset in pixels. - * @package - */ -Blockly.WorkspaceSvg.prototype.getOriginOffsetInPixels = function() { - return Blockly.utils.getInjectionDivXY_(this.svgBlockCanvas_); -}; - -/** - * Return the injection div that is a parent of this workspace. - * Walks the DOM the first time it's called, then returns a cached value. - * @return {!Element} The first parent div with 'injectionDiv' in the name. - * @package - */ -Blockly.WorkspaceSvg.prototype.getInjectionDiv = function() { - // NB: it would be better to pass this in at createDom, but is more likely to - // break existing uses of Blockly. - if (!this.injectionDiv_) { - var element = this.svgGroup_; - while (element) { - var classes = element.getAttribute('class') || ''; - if ((' ' + classes + ' ').indexOf(' injectionDiv ') != -1) { - this.injectionDiv_ = element; - break; - } - element = element.parentNode; - } - } - return this.injectionDiv_; -}; - -/** - * Save resize handler data so we can delete it later in dispose. - * @param {!Array.} handler Data that can be passed to unbindEvent_. - */ -Blockly.WorkspaceSvg.prototype.setResizeHandlerWrapper = function(handler) { - this.resizeHandlerWrapper_ = handler; -}; - -/** - * Create the workspace DOM elements. - * @param {string=} opt_backgroundClass Either 'blocklyMainBackground' or - * 'blocklyMutatorBackground'. - * @return {!Element} The workspace's SVG group. - */ -Blockly.WorkspaceSvg.prototype.createDom = function(opt_backgroundClass) { - /** - * - * - * [Trashcan and/or flyout may go here] - * - * - * - * @type {SVGElement} - */ - this.svgGroup_ = Blockly.utils.createSvgElement('g', - {'class': 'blocklyWorkspace'}, null); - - // Note that a alone does not receive mouse events--it must have a - // valid target inside it. If no background class is specified, as in the - // flyout, the workspace will not receive mouse events. - if (opt_backgroundClass) { - /** @type {SVGElement} */ - this.svgBackground_ = Blockly.utils.createSvgElement('rect', - {'height': '100%', 'width': '100%', 'class': opt_backgroundClass}, - this.svgGroup_); - - if (opt_backgroundClass == 'blocklyMainBackground' && this.grid_) { - this.svgBackground_.style.fill = - 'url(#' + this.grid_.getPatternId() + ')'; - } - } - /** @type {SVGElement} */ - this.svgBlockCanvas_ = Blockly.utils.createSvgElement('g', - {'class': 'blocklyBlockCanvas'}, this.svgGroup_, this); - /** @type {SVGElement} */ - this.svgBubbleCanvas_ = Blockly.utils.createSvgElement('g', - {'class': 'blocklyBubbleCanvas'}, this.svgGroup_, this); - var bottom = Blockly.Scrollbar.scrollbarThickness; - if (this.options.hasTrashcan) { - bottom = this.addTrashcan_(bottom); - } - if (this.options.zoomOptions && this.options.zoomOptions.controls) { - this.addZoomControls_(bottom); - } - - if (!this.isFlyout) { - Blockly.bindEventWithChecks_(this.svgGroup_, 'mousedown', this, - this.onMouseDown_); - if (this.options.zoomOptions && this.options.zoomOptions.wheel) { - // Mouse-wheel. - Blockly.bindEventWithChecks_(this.svgGroup_, 'wheel', this, - this.onMouseWheel_); - } - } - - // Determine if there needs to be a category tree, or a simple list of - // blocks. This cannot be changed later, since the UI is very different. - if (this.options.hasCategories) { - /** - * @type {Blockly.Toolbox} - * @private - */ - this.toolbox_ = new Blockly.Toolbox(this); - } - if (this.grid_) { - this.grid_.update(this.scale); - } - this.recordCachedAreas(); - return this.svgGroup_; -}; - -/** - * Dispose of this workspace. - * Unlink from all DOM elements to prevent memory leaks. - */ -Blockly.WorkspaceSvg.prototype.dispose = function() { - // Stop rerendering. - this.rendered = false; - if (this.currentGesture_) { - this.currentGesture_.cancel(); - } - Blockly.WorkspaceSvg.superClass_.dispose.call(this); - if (this.svgGroup_) { - goog.dom.removeNode(this.svgGroup_); - this.svgGroup_ = null; - } - this.svgBlockCanvas_ = null; - this.svgBubbleCanvas_ = null; - if (this.toolbox_) { - this.toolbox_.dispose(); - this.toolbox_ = null; - } - if (this.flyout_) { - this.flyout_.dispose(); - this.flyout_ = null; - } - if (this.trashcan) { - this.trashcan.dispose(); - this.trashcan = null; - } - if (this.scrollbar) { - this.scrollbar.dispose(); - this.scrollbar = null; - } - if (this.zoomControls_) { - this.zoomControls_.dispose(); - this.zoomControls_ = null; - } - - if (this.audioManager_) { - this.audioManager_.dispose(); - this.audioManager_ = null; - } - - if (this.grid_) { - this.grid_.dispose(); - this.grid_ = null; - } - - if (this.toolboxCategoryCallbacks_) { - this.toolboxCategoryCallbacks_ = null; - } - if (this.flyoutButtonCallbacks_) { - this.flyoutButtonCallbacks_ = null; - } - if (!this.options.parentWorkspace) { - // Top-most workspace. Dispose of the div that the - // SVG is injected into (i.e. injectionDiv). - goog.dom.removeNode(this.getParentSvg().parentNode); - } - if (this.resizeHandlerWrapper_) { - Blockly.unbindEvent_(this.resizeHandlerWrapper_); - this.resizeHandlerWrapper_ = null; - } -}; - -/** - * 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 {!Blockly.BlockSvg} The created block. - */ -Blockly.WorkspaceSvg.prototype.newBlock = function(prototypeName, opt_id) { - return new Blockly.BlockSvg(this, prototypeName, opt_id); -}; - -/** - * Add a trashcan. - * @param {number} bottom Distance from workspace bottom to bottom of trashcan. - * @return {number} Distance from workspace bottom to the top of trashcan. - * @private - */ -Blockly.WorkspaceSvg.prototype.addTrashcan_ = function(bottom) { - /** @type {Blockly.Trashcan} */ - this.trashcan = new Blockly.Trashcan(this); - var svgTrashcan = this.trashcan.createDom(); - this.svgGroup_.insertBefore(svgTrashcan, this.svgBlockCanvas_); - return this.trashcan.init(bottom); -}; - -/** - * Add zoom controls. - * @param {number} bottom Distance from workspace bottom to bottom of controls. - * @return {number} Distance from workspace bottom to the top of controls. - * @private - */ -Blockly.WorkspaceSvg.prototype.addZoomControls_ = function(bottom) { - /** @type {Blockly.ZoomControls} */ - this.zoomControls_ = new Blockly.ZoomControls(this); - var svgZoomControls = this.zoomControls_.createDom(); - this.svgGroup_.appendChild(svgZoomControls); - return this.zoomControls_.init(bottom); -}; - -/** - * Add a flyout element in an element with the given tag name. - * @param {string} tagName What type of tag the flyout belongs in. - * @return {!Element} The element containing the flyout DOM. - * @private - */ -Blockly.WorkspaceSvg.prototype.addFlyout_ = function(tagName) { - var workspaceOptions = { - disabledPatternId: this.options.disabledPatternId, - parentWorkspace: this, - RTL: this.RTL, - oneBasedIndex: this.options.oneBasedIndex, - horizontalLayout: this.horizontalLayout, - toolboxPosition: this.options.toolboxPosition, - stackGlowFilterId: this.options.stackGlowFilterId - }; - if (this.horizontalLayout) { - this.flyout_ = new Blockly.HorizontalFlyout(workspaceOptions); - } else { - this.flyout_ = new Blockly.VerticalFlyout(workspaceOptions); - } - this.flyout_.autoClose = false; - - // Return the element so that callers can place it in their desired - // spot in the DOM. For example, mutator flyouts do not go in the same place - // as main workspace flyouts. - return this.flyout_.createDom(tagName); -}; - -/** - * Getter for the flyout associated with this workspace. This flyout may be - * owned by either the toolbox or the workspace, depending on toolbox - * configuration. It will be null if there is no flyout. - * @return {Blockly.Flyout} The flyout on this workspace. - * @package - */ -Blockly.WorkspaceSvg.prototype.getFlyout = function() { - if (this.flyout_) { - return this.flyout_; - } - if (this.toolbox_) { - return this.toolbox_.flyout_; - } - return null; -}; - -/** - * Getter for the toolbox associated with this workspace, if one exists. - * @return {Blockly.Toolbox} The toolbox on this workspace. - * @package - */ -Blockly.WorkspaceSvg.prototype.getToolbox = function() { - return this.toolbox_; -}; - -/** - * Update items that use screen coordinate calculations - * because something has changed (e.g. scroll position, window size). - * @private - */ -Blockly.WorkspaceSvg.prototype.updateScreenCalculations_ = function() { - this.updateInverseScreenCTM(); - this.recordCachedAreas(); -}; - -/** - * If enabled, resize the parts of the workspace that change when the workspace - * contents (e.g. block positions) change. This will also scroll the - * workspace contents if needed. - * @package - */ -Blockly.WorkspaceSvg.prototype.resizeContents = function() { - if (!this.resizesEnabled_ || !this.rendered) { - return; - } - if (this.scrollbar) { - // TODO(picklesrus): Once rachel-fenichel's scrollbar refactoring - // is complete, call the method that only resizes scrollbar - // based on contents. - this.scrollbar.resize(); - } - this.updateInverseScreenCTM(); -}; - -/** - * Resize and reposition all of the workspace chrome (toolbox, - * trash, scrollbars etc.) - * This should be called when something changes that - * requires recalculating dimensions and positions of the - * trash, zoom, toolbox, etc. (e.g. window resize). - */ -Blockly.WorkspaceSvg.prototype.resize = function() { - if (this.toolbox_) { - this.toolbox_.position(); - } - if (this.flyout_) { - this.flyout_.position(); - } - if (this.trashcan) { - this.trashcan.position(); - } - if (this.zoomControls_) { - this.zoomControls_.position(); - } - if (this.scrollbar) { - this.scrollbar.resize(); - } - this.updateScreenCalculations_(); -}; - -/** - * Resizes and repositions workspace chrome if the page has a new - * scroll position. - * @package - */ -Blockly.WorkspaceSvg.prototype.updateScreenCalculationsIfScrolled - = function() { - /* eslint-disable indent */ - var currScroll = goog.dom.getDocumentScroll(); - if (!goog.math.Coordinate.equals(this.lastRecordedPageScroll_, - currScroll)) { - this.lastRecordedPageScroll_ = currScroll; - this.updateScreenCalculations_(); - } -}; /* eslint-enable indent */ - -/** - * Get the SVG element that forms the drawing surface. - * @return {!Element} SVG element. - */ -Blockly.WorkspaceSvg.prototype.getCanvas = function() { - return this.svgBlockCanvas_; -}; - -/** - * Get the SVG element that forms the bubble surface. - * @return {!SVGGElement} SVG element. - */ -Blockly.WorkspaceSvg.prototype.getBubbleCanvas = function() { - return this.svgBubbleCanvas_; -}; - -/** - * Get the SVG element that contains this workspace. - * @return {!Element} SVG element. - */ -Blockly.WorkspaceSvg.prototype.getParentSvg = function() { - if (this.cachedParentSvg_) { - return this.cachedParentSvg_; - } - var element = this.svgGroup_; - while (element) { - if (element.tagName == 'svg') { - this.cachedParentSvg_ = element; - return element; - } - element = element.parentNode; - } - return null; -}; - -/** - * Translate this workspace to new coordinates. - * @param {number} x Horizontal translation. - * @param {number} y Vertical translation. - */ -Blockly.WorkspaceSvg.prototype.translate = function(x, y) { - if (this.useWorkspaceDragSurface_ && this.isDragSurfaceActive_) { - this.workspaceDragSurface_.translateSurface(x,y); - } else { - var translation = 'translate(' + x + ',' + y + ') ' + - 'scale(' + this.scale + ')'; - this.svgBlockCanvas_.setAttribute('transform', translation); - this.svgBubbleCanvas_.setAttribute('transform', translation); - } - // Now update the block drag surface if we're using one. - if (this.blockDragSurface_) { - this.blockDragSurface_.translateAndScaleGroup(x, y, this.scale); - } -}; - -/** - * Called at the end of a workspace drag to take the contents - * out of the drag surface and put them back into the workspace SVG. - * Does nothing if the workspace drag surface is not enabled. - * @package - */ -Blockly.WorkspaceSvg.prototype.resetDragSurface = function() { - // Don't do anything if we aren't using a drag surface. - if (!this.useWorkspaceDragSurface_) { - return; - } - - this.isDragSurfaceActive_ = false; - - var trans = this.workspaceDragSurface_.getSurfaceTranslation(); - this.workspaceDragSurface_.clearAndHide(this.svgGroup_); - var translation = 'translate(' + trans.x + ',' + trans.y + ') ' + - 'scale(' + this.scale + ')'; - this.svgBlockCanvas_.setAttribute('transform', translation); - this.svgBubbleCanvas_.setAttribute('transform', translation); -}; - -/** - * Called at the beginning of a workspace drag to move contents of - * the workspace to the drag surface. - * Does nothing if the drag surface is not enabled. - * @package - */ -Blockly.WorkspaceSvg.prototype.setupDragSurface = function() { - // Don't do anything if we aren't using a drag surface. - if (!this.useWorkspaceDragSurface_) { - return; - } - - // This can happen if the user starts a drag, mouses up outside of the - // document where the mouseup listener is registered (e.g. outside of an - // iframe) and then moves the mouse back in the workspace. On mobile and ff, - // we get the mouseup outside the frame. On chrome and safari desktop we do - // not. - if (this.isDragSurfaceActive_) { - return; - } - - this.isDragSurfaceActive_ = true; - - // Figure out where we want to put the canvas back. The order - // in the is important because things are layered. - var previousElement = this.svgBlockCanvas_.previousSibling; - var width = parseInt(this.getParentSvg().getAttribute('width'), 10); - var height = parseInt(this.getParentSvg().getAttribute('height'), 10); - var coord = Blockly.utils.getRelativeXY(this.svgBlockCanvas_); - this.workspaceDragSurface_.setContentsAndShow(this.svgBlockCanvas_, - this.svgBubbleCanvas_, previousElement, width, height, this.scale); - this.workspaceDragSurface_.translateSurface(coord.x, coord.y); -}; - -/** - * @return {?Blockly.BlockDragSurfaceSvg} This workspace's block drag surface, - * if one is in use. - * @package - */ -Blockly.WorkspaceSvg.prototype.getBlockDragSurface = function() { - return this.blockDragSurface_; -}; - -/** - * Returns the horizontal offset of the workspace. - * Intended for LTR/RTL compatibility in XML. - * @return {number} Width. - */ -Blockly.WorkspaceSvg.prototype.getWidth = function() { - var metrics = this.getMetrics(); - return metrics ? metrics.viewWidth / this.scale : 0; -}; - -/** - * Toggles the visibility of the workspace. - * Currently only intended for main workspace. - * @param {boolean} isVisible True if workspace should be visible. - */ -Blockly.WorkspaceSvg.prototype.setVisible = function(isVisible) { - - // Tell the scrollbar whether its container is visible so it can - // tell when to hide itself. - if (this.scrollbar) { - this.scrollbar.setContainerVisible(isVisible); - } - - // Tell the flyout whether its container is visible so it can - // tell when to hide itself. - if (this.getFlyout()) { - this.getFlyout().setContainerVisible(isVisible); - } - - this.getParentSvg().style.display = isVisible ? 'block' : 'none'; - if (this.toolbox_) { - // Currently does not support toolboxes in mutators. - this.toolbox_.HtmlDiv.style.display = isVisible ? 'block' : 'none'; - } - if (isVisible) { - this.render(); - // The window may have changed size while the workspace was hidden. - // Resize recalculates scrollbar position, delete areas, etc. - this.resize(); - } else { - Blockly.hideChaff(true); - Blockly.DropDownDiv.hideWithoutAnimation(); - } - this.isVisible_ = isVisible; -}; - -/** - * Render all blocks in workspace. - */ -Blockly.WorkspaceSvg.prototype.render = function() { - // Generate list of all blocks. - var blocks = this.getAllBlocks(); - // Render each block. - for (var i = blocks.length - 1; i >= 0; i--) { - blocks[i].render(false); - } -}; - -/** - * Was used back when block highlighting (for execution) and block selection - * (for editing) were the same thing. - * Any calls of this function can be deleted. - * @deprecated October 2016 - */ -Blockly.WorkspaceSvg.prototype.traceOn = function() { - console.warn('Deprecated call to traceOn, delete this.'); -}; - -/** - * Highlight or unhighlight a block in the workspace. Block highlighting is - * often used to visually mark blocks currently being executed. - * @param {?string} id ID of block to highlight/unhighlight, - * or null for no block (used to unhighlight all blocks). - * @param {boolean=} opt_state If undefined, highlight specified block and - * automatically unhighlight all others. If true or false, manually - * highlight/unhighlight the specified block. - */ -Blockly.WorkspaceSvg.prototype.highlightBlock = function(id, opt_state) { - if (opt_state === undefined) { - // Unhighlight all blocks. - for (var i = 0, block; block = this.highlightedBlocks_[i]; i++) { - block.setHighlighted(false); - } - this.highlightedBlocks_.length = 0; - } - // Highlight/unhighlight the specified block. - var block = id ? this.getBlockById(id) : null; - if (block) { - var state = (opt_state === undefined) || opt_state; - // Using Set here would be great, but at the cost of IE10 support. - if (!state) { - goog.array.remove(this.highlightedBlocks_, block); - } else if (this.highlightedBlocks_.indexOf(block) == -1) { - this.highlightedBlocks_.push(block); - } - block.setHighlighted(state); - } -}; - -/** - * Glow/unglow a block in the workspace. - * @param {?string} id ID of block to find. - * @param {boolean} isGlowingBlock Whether to glow the block. - */ -Blockly.WorkspaceSvg.prototype.glowBlock = function(id, isGlowingBlock) { - var block = null; - if (id) { - block = this.getBlockById(id); - if (!block) { - throw 'Tried to glow block that does not exist.'; - } - } - block.setGlowBlock(isGlowingBlock); -}; - -/** - * Glow/unglow a stack in the workspace. - * @param {?string} id ID of block which starts the stack. - * @param {boolean} isGlowingStack Whether to glow the stack. - */ -Blockly.WorkspaceSvg.prototype.glowStack = function(id, isGlowingStack) { - var block = null; - if (id) { - block = this.getBlockById(id); - if (!block) { - throw 'Tried to glow stack on block that does not exist.'; - } - } - block.setGlowStack(isGlowingStack); -}; - -/** - * Visually report a value associated with a block. - * In Scratch, appears as a pop-up next to the block when a reporter block is clicked. - * @param {?string} id ID of block to report associated value. - * @param {?string} value String value to visually report. - */ -Blockly.WorkspaceSvg.prototype.reportValue = function(id, value) { - var block = this.getBlockById(id); - if (!block) { - throw 'Tried to report value on block that does not exist.'; - } - Blockly.DropDownDiv.hideWithoutAnimation(); - Blockly.DropDownDiv.clearContent(); - var contentDiv = Blockly.DropDownDiv.getContentDiv(); - var valueReportBox = goog.dom.createElement('div'); - valueReportBox.setAttribute('class', 'valueReportBox'); - valueReportBox.innerHTML = Blockly.scratchBlocksUtils.encodeEntities(value); - contentDiv.appendChild(valueReportBox); - Blockly.DropDownDiv.setColour( - Blockly.Colours.valueReportBackground, - Blockly.Colours.valueReportBorder - ); - Blockly.DropDownDiv.showPositionedByBlock(this, block); -}; - -/** - * Paste the provided block onto the workspace. - * @param {!Element} xmlBlock XML block element. - */ -Blockly.WorkspaceSvg.prototype.paste = function(xmlBlock) { - if (!this.rendered) { - return; - } - if (this.currentGesture_) { - this.currentGesture_.cancel(); // Dragging while pasting? No. - } - if (xmlBlock.tagName.toLowerCase() == 'comment') { - this.pasteWorkspaceComment_(xmlBlock); - } else { - this.pasteBlock_(xmlBlock); - } -}; - -/** - * Paste the provided block onto the workspace. - * @param {!Element} xmlBlock XML block element. - */ -Blockly.WorkspaceSvg.prototype.pasteBlock_ = function(xmlBlock) { - Blockly.Events.disable(); - try { - var block = Blockly.Xml.domToBlock(xmlBlock, this); - // Scratch-specific: Give shadow dom new IDs to prevent duplicating on paste - Blockly.scratchBlocksUtils.changeObscuredShadowIds(block); - // Move the duplicate to original position. - var blockX = parseInt(xmlBlock.getAttribute('x'), 10); - var blockY = parseInt(xmlBlock.getAttribute('y'), 10); - if (!isNaN(blockX) && !isNaN(blockY)) { - if (this.RTL) { - blockX = -blockX; - } - // Offset block until not clobbering another block and not in connection - // distance with neighbouring blocks. - do { - var collide = false; - var allBlocks = this.getAllBlocks(); - for (var i = 0, otherBlock; otherBlock = allBlocks[i]; i++) { - var otherXY = otherBlock.getRelativeToSurfaceXY(); - if (Math.abs(blockX - otherXY.x) <= 1 && - Math.abs(blockY - otherXY.y) <= 1) { - collide = true; - break; - } - } - if (!collide) { - // Check for blocks in snap range to any of its connections. - var connections = block.getConnections_(false); - for (var i = 0, connection; connection = connections[i]; i++) { - var neighbour = connection.closest(Blockly.SNAP_RADIUS, - new goog.math.Coordinate(blockX, blockY)); - if (neighbour.connection) { - collide = true; - break; - } - } - } - if (collide) { - if (this.RTL) { - blockX -= Blockly.SNAP_RADIUS; - } else { - blockX += Blockly.SNAP_RADIUS; - } - blockY += Blockly.SNAP_RADIUS * 2; - } - } while (collide); - block.moveBy(blockX, blockY); - } - } finally { - Blockly.Events.enable(); - } - if (Blockly.Events.isEnabled() && !block.isShadow()) { - Blockly.Events.fire(new Blockly.Events.BlockCreate(block)); - } - block.select(); -}; - -/** - * Paste the provided comment onto the workspace. - * @param {!Element} xmlComment XML workspace comment element. - * @private - */ -Blockly.WorkspaceSvg.prototype.pasteWorkspaceComment_ = function(xmlComment) { - Blockly.Events.disable(); - try { - var comment = Blockly.WorkspaceCommentSvg.fromXml(xmlComment, this); - // Move the duplicate to original position. - var commentX = parseInt(xmlComment.getAttribute('x'), 10); - var commentY = parseInt(xmlComment.getAttribute('y'), 10); - if (!isNaN(commentX) && !isNaN(commentY)) { - if (this.RTL) { - commentX = -commentX; - } - // Offset workspace comment. - // TODO: (github.com/google/blockly/issues/1719) properly offset comment - // such that it's not interfereing with any blocks - commentX += 50; - commentY += 50; - comment.moveBy(commentX, commentY); - } - } finally { - Blockly.Events.enable(); - } - if (Blockly.Events.isEnabled()) { - Blockly.WorkspaceComment.fireCreateEvent(comment); - } - comment.select(); -}; - -/** - * Refresh the toolbox unless there's a drag in progress. - * @private - */ -Blockly.WorkspaceSvg.prototype.refreshToolboxSelection_ = function() { - // Updating the toolbox can be expensive. Don't do it when when it is - // disabled. - if (this.toolbox_) { - if (this.toolbox_.flyout_ && !this.currentGesture_ && - this.toolboxRefreshEnabled_) { - this.toolbox_.refreshSelection(); - } - } else { - var thisTarget = this.targetWorkspace; - if (thisTarget && thisTarget.toolbox_ && thisTarget.toolbox_.flyout_ && - !thisTarget.currentGesture_ && thisTarget.toolboxRefreshEnabled_) { - thisTarget.toolbox_.refreshSelection(); - } - } -}; - -/** - * Rename a variable by updating its name in the variable map. Update the - * flyout to show the renamed variable immediately. - * @param {string} id ID of the variable to rename. - * @param {string} newName New variable name. - * @package - */ -Blockly.WorkspaceSvg.prototype.renameVariableById = function(id, newName) { - Blockly.WorkspaceSvg.superClass_.renameVariableById.call(this, id, newName); - this.refreshToolboxSelection_(); -}; - -/** - * Delete a variable by the passed in ID. Update the flyout to show - * immediately that the variable is deleted. - * @param {string} id ID of variable to delete. - * @package - */ -Blockly.WorkspaceSvg.prototype.deleteVariableById = function(id) { - Blockly.WorkspaceSvg.superClass_.deleteVariableById.call(this, id); - this.refreshToolboxSelection_(); -}; - -/** - * Create a new variable with the given name. Update the flyout to show the new - * variable immediately. - * @param {string} name The new variable's 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. - * @param {boolean=} opt_isLocal Whether the variable is locally scoped. - * @param {boolean=} opt_isCloud Whether the variable is a cloud variable. - * @return {?Blockly.VariableModel} The newly created variable. - * @package - */ -Blockly.WorkspaceSvg.prototype.createVariable = function(name, opt_type, opt_id, - opt_isLocal, opt_isCloud) { - var variableInMap = (this.getVariable(name, opt_type) != null); - var newVar = Blockly.WorkspaceSvg.superClass_.createVariable.call( - this, name, opt_type, opt_id, opt_isLocal, opt_isCloud); - // For performance reasons, only refresh the the toolbox for new variables. - // Variables that already exist should already be there. - if (!variableInMap && (opt_type != Blockly.BROADCAST_MESSAGE_VARIABLE_TYPE)) { - this.refreshToolboxSelection_(); - } - return newVar; -}; - -/** - * Update cached areas for this workspace. - */ -Blockly.WorkspaceSvg.prototype.recordCachedAreas = function() { - this.recordBlocksArea_(); - this.recordDeleteAreas_(); -}; - -/** - * Make a list of all the delete areas for this workspace. - * @private - */ -Blockly.WorkspaceSvg.prototype.recordDeleteAreas_ = function() { - if (this.trashcan) { - this.deleteAreaTrash_ = this.trashcan.getClientRect(); - } else { - this.deleteAreaTrash_ = null; - } - if (this.flyout_) { - this.deleteAreaToolbox_ = this.flyout_.getClientRect(); - } else if (this.toolbox_) { - this.deleteAreaToolbox_ = this.toolbox_.getClientRect(); - } else { - this.deleteAreaToolbox_ = null; - } -}; - -/** - * Record where all of blocks GUI is on the screen - * @private - */ -Blockly.WorkspaceSvg.prototype.recordBlocksArea_ = function() { - var parentSvg = this.getParentSvg(); - if (parentSvg) { - var bounds = parentSvg.getBoundingClientRect(); - this.blocksArea_ = new goog.math.Rect(bounds.left, bounds.top, bounds.width, bounds.height); - } else { - this.blocksArea_ = null; - } -}; - -/** - * Is the mouse event over a delete area (toolbox or non-closing flyout)? - * @param {!Event} e Mouse move event. - * @return {?number} Null if not over a delete area, or an enum representing - * which delete area the event is over. - */ -Blockly.WorkspaceSvg.prototype.isDeleteArea = function(e) { - var xy = new goog.math.Coordinate(e.clientX, e.clientY); - if (this.deleteAreaTrash_ && this.deleteAreaTrash_.contains(xy)) { - return Blockly.DELETE_AREA_TRASH; - } - if (this.deleteAreaToolbox_ && this.deleteAreaToolbox_.contains(xy)) { - return Blockly.DELETE_AREA_TOOLBOX; - } - return Blockly.DELETE_AREA_NONE; -}; - -/** - * Is the mouse event inside the blocks UI? - * @param {!Event} e Mouse move event. - * @return {boolean} True if event is within the bounds of the blocks UI or delete area - */ -Blockly.WorkspaceSvg.prototype.isInsideBlocksArea = function(e) { - var xy = new goog.math.Coordinate(e.clientX, e.clientY); - if (this.isDeleteArea(e) || (this.blocksArea_ && this.blocksArea_.contains(xy))) { - return true; - } - return false; -}; - -/** - * Handle a mouse-down on SVG drawing surface. - * @param {!Event} e Mouse down event. - * @private - */ -Blockly.WorkspaceSvg.prototype.onMouseDown_ = function(e) { - var gesture = this.getGesture(e); - if (gesture) { - gesture.handleWsStart(e, this); - } -}; - -/** - * Start tracking a drag of an object on this workspace. - * @param {!Event} e Mouse down event. - * @param {!goog.math.Coordinate} xy Starting location of object. - */ -Blockly.WorkspaceSvg.prototype.startDrag = function(e, xy) { - // Record the starting offset between the bubble's location and the mouse. - var point = Blockly.utils.mouseToSvg(e, this.getParentSvg(), - this.getInverseScreenCTM()); - // Fix scale of mouse event. - point.x /= this.scale; - point.y /= this.scale; - this.dragDeltaXY_ = goog.math.Coordinate.difference(xy, point); -}; - -/** - * Track a drag of an object on this workspace. - * @param {!Event} e Mouse move event. - * @return {!goog.math.Coordinate} New location of object. - */ -Blockly.WorkspaceSvg.prototype.moveDrag = function(e) { - var point = Blockly.utils.mouseToSvg(e, this.getParentSvg(), - this.getInverseScreenCTM()); - // Fix scale of mouse event. - point.x /= this.scale; - point.y /= this.scale; - return goog.math.Coordinate.sum(this.dragDeltaXY_, point); -}; - -/** - * Is the user currently dragging a block or scrolling the flyout/workspace? - * @return {boolean} True if currently dragging or scrolling. - */ -Blockly.WorkspaceSvg.prototype.isDragging = function() { - return this.currentGesture_ && this.currentGesture_.isDragging(); -}; - -/** - * Is this workspace draggable and scrollable? - * @return {boolean} True if this workspace may be dragged. - */ -Blockly.WorkspaceSvg.prototype.isDraggable = function() { - return !!this.scrollbar; -}; - -/** - * Handle a mouse-wheel on SVG drawing surface. - * @param {!Event} e Mouse wheel event. - * @private - */ -Blockly.WorkspaceSvg.prototype.onMouseWheel_ = function(e) { - // TODO: Remove gesture cancellation and compensate for coordinate skew during - // zoom. - if (this.currentGesture_) { - this.currentGesture_.cancel(); - } - - // Multiplier variable, so that non-pixel-deltaModes are supported. - // See LLK/scratch-blocks#1190. - var multiplier = e.deltaMode === 0x1 ? Blockly.LINE_SCROLL_MULTIPLIER : 1; - - if (e.ctrlKey) { - // The vertical scroll distance that corresponds to a click of a zoom button. - var PIXELS_PER_ZOOM_STEP = 50; - var delta = -e.deltaY / PIXELS_PER_ZOOM_STEP * multiplier; - var position = Blockly.utils.mouseToSvg(e, this.getParentSvg(), - this.getInverseScreenCTM()); - this.zoom(position.x, position.y, delta); - } else { - // This is a regular mouse wheel event - scroll the workspace - // First hide the WidgetDiv without animation - // (mouse scroll makes field out of place with div) - Blockly.WidgetDiv.hide(true); - Blockly.DropDownDiv.hideWithoutAnimation(); - - var x = this.scrollX - e.deltaX * multiplier; - var y = this.scrollY - e.deltaY * multiplier; - - if (e.shiftKey && e.deltaX === 0) { - // Scroll horizontally (based on vertical scroll delta) - // This is needed as for some browser/system combinations which do not - // set deltaX. See #1662. - x = this.scrollX - e.deltaY * multiplier; - y = this.scrollY; // Don't scroll vertically - } - - this.startDragMetrics = this.getMetrics(); - this.scroll(x, y); - } - e.preventDefault(); -}; - -/** - * Calculate the bounding box for the blocks on the workspace. - * Coordinate system: workspace coordinates. - * - * @return {Object} Contains the position and size of the bounding box - * containing the blocks on the workspace. - */ -Blockly.WorkspaceSvg.prototype.getBlocksBoundingBox = function() { - var topBlocks = this.getTopBlocks(false); - var topComments = this.getTopComments(false); - var topElements = topBlocks.concat(topComments); - // There are no blocks, return empty rectangle. - if (!topElements.length) { - return {x: 0, y: 0, width: 0, height: 0}; - } - - // Initialize boundary using the first block. - var boundary = topElements[0].getBoundingRectangle(); - - // Start at 1 since the 0th block was used for initialization - for (var i = 1; i < topElements.length; i++) { - var blockBoundary = topElements[i].getBoundingRectangle(); - if (blockBoundary.topLeft.x < boundary.topLeft.x) { - boundary.topLeft.x = blockBoundary.topLeft.x; - } - if (blockBoundary.bottomRight.x > boundary.bottomRight.x) { - boundary.bottomRight.x = blockBoundary.bottomRight.x; - } - if (blockBoundary.topLeft.y < boundary.topLeft.y) { - boundary.topLeft.y = blockBoundary.topLeft.y; - } - if (blockBoundary.bottomRight.y > boundary.bottomRight.y) { - boundary.bottomRight.y = blockBoundary.bottomRight.y; - } - } - return { - x: boundary.topLeft.x, - y: boundary.topLeft.y, - width: boundary.bottomRight.x - boundary.topLeft.x, - height: boundary.bottomRight.y - boundary.topLeft.y - }; -}; - -/** - * Clean up the workspace by ordering all the blocks in a column. - */ -Blockly.WorkspaceSvg.prototype.cleanUp = function() { - this.setResizesEnabled(false); - Blockly.Events.setGroup(true); - var topBlocks = this.getTopBlocks(true); - var cursorY = 0; - for (var i = 0, block; block = topBlocks[i]; i++) { - var xy = block.getRelativeToSurfaceXY(); - block.moveBy(-xy.x, cursorY - xy.y); - block.snapToGrid(); - cursorY = block.getRelativeToSurfaceXY().y + - block.getHeightWidth().height + Blockly.BlockSvg.MIN_BLOCK_Y; - } - Blockly.Events.setGroup(false); - this.setResizesEnabled(true); -}; - -/** - * Show the context menu for the workspace. - * @param {!Event} e Mouse event. - * @private - */ -Blockly.WorkspaceSvg.prototype.showContextMenu_ = function(e) { - if (this.options.readOnly || this.isFlyout) { - return; - } - var menuOptions = []; - var topBlocks = this.getTopBlocks(true); - var eventGroup = Blockly.utils.genUid(); - var ws = this; - - // Options to undo/redo previous action. - menuOptions.push(Blockly.ContextMenu.wsUndoOption(this)); - menuOptions.push(Blockly.ContextMenu.wsRedoOption(this)); - - // Option to clean up blocks. - if (this.scrollbar) { - menuOptions.push( - Blockly.ContextMenu.wsCleanupOption(this,topBlocks.length)); - } - - if (this.options.collapse) { - var hasCollapsedBlocks = false; - var hasExpandedBlocks = false; - for (var i = 0; i < topBlocks.length; i++) { - var block = topBlocks[i]; - while (block) { - if (block.isCollapsed()) { - hasCollapsedBlocks = true; - } else { - hasExpandedBlocks = true; - } - block = block.getNextBlock(); - } - } - - menuOptions.push(Blockly.ContextMenu.wsCollapseOption(hasExpandedBlocks, - topBlocks)); - - menuOptions.push(Blockly.ContextMenu.wsExpandOption(hasCollapsedBlocks, - topBlocks)); - } - - // Option to add a workspace comment. - if (this.options.comments) { - menuOptions.push(Blockly.ContextMenu.workspaceCommentOption(ws, e)); - } - - // Option to delete all blocks. - // Count the number of blocks that are deletable. - var deleteList = Blockly.WorkspaceSvg.buildDeleteList_(topBlocks); - // Scratch-specific: don't count shadow blocks in delete count - var deleteCount = 0; - for (var i = 0; i < deleteList.length; i++) { - if (!deleteList[i].isShadow()) { - deleteCount++; - } - } - - var DELAY = 10; - function deleteNext() { - Blockly.Events.setGroup(eventGroup); - var block = deleteList.shift(); - if (block) { - if (block.workspace) { - block.dispose(false, true); - setTimeout(deleteNext, DELAY); - } else { - deleteNext(); - } - } - Blockly.Events.setGroup(false); - } - - var deleteOption = { - text: deleteCount == 1 ? Blockly.Msg.DELETE_BLOCK : - Blockly.Msg.DELETE_X_BLOCKS.replace('%1', String(deleteCount)), - enabled: deleteCount > 0, - callback: function() { - if (ws.currentGesture_) { - ws.currentGesture_.cancel(); - } - if (deleteCount < 2 ) { - deleteNext(); - } else { - Blockly.confirm( - Blockly.Msg.DELETE_ALL_BLOCKS.replace('%1', String(deleteCount)), - function(ok) { - if (ok) { - deleteNext(); - } - }); - } - } - }; - menuOptions.push(deleteOption); - - Blockly.ContextMenu.show(e, menuOptions, this.RTL); -}; - -/** - * Build a list of all deletable blocks that are reachable from the given - * list of top blocks. - * @param {!Array.} topBlocks The list of top blocks on the - * workspace. - * @return {!Array.} A list of deletable blocks on the - * workspace. - * @private - */ -Blockly.WorkspaceSvg.buildDeleteList_ = function(topBlocks) { - var deleteList = []; - function addDeletableBlocks(block) { - if (block.isDeletable()) { - deleteList = deleteList.concat(block.getDescendants(false)); - } else { - var children = block.getChildren(); - for (var i = 0; i < children.length; i++) { - addDeletableBlocks(children[i]); - } - } - } - for (var i = 0; i < topBlocks.length; i++) { - addDeletableBlocks(topBlocks[i]); - } - return deleteList; -}; - -/** - * Modify the block tree on the existing toolbox. - * @param {Node|string} tree DOM tree of blocks, or text representation of same. - */ -Blockly.WorkspaceSvg.prototype.updateToolbox = function(tree) { - tree = Blockly.Options.parseToolboxTree(tree); - if (!tree) { - if (this.options.languageTree) { - throw 'Can\'t nullify an existing toolbox.'; - } - return; // No change (null to null). - } - if (!this.options.languageTree) { - throw 'Existing toolbox is null. Can\'t create new toolbox.'; - } - if (tree.getElementsByTagName('category').length) { - if (!this.toolbox_) { - throw 'Existing toolbox has no categories. Can\'t change mode.'; - } - this.options.languageTree = tree; - this.toolbox_.populate_(tree); - this.toolbox_.position(); - } else { - if (!this.flyout_) { - throw 'Existing toolbox has categories. Can\'t change mode.'; - } - this.options.languageTree = tree; - this.flyout_.show(tree.childNodes); - } -}; - -/** - * Mark this workspace as the currently focused main workspace. - */ -Blockly.WorkspaceSvg.prototype.markFocused = function() { - if (this.options.parentWorkspace) { - this.options.parentWorkspace.markFocused(); - } else { - Blockly.mainWorkspace = this; - // We call e.preventDefault in many event handlers which means we - // need to explicitly grab focus (e.g from a textarea) because - // the browser will not do it for us. How to do this is browser dependant. - this.setBrowserFocus(); - } -}; - -/** - * Set the workspace to have focus in the browser. - * @private - */ -Blockly.WorkspaceSvg.prototype.setBrowserFocus = function() { - // Blur whatever was focused since explcitly grabbing focus below does not - // work in Edge. - if (document.activeElement) { - document.activeElement.blur(); - } - try { - // Focus the workspace SVG - this is for Chrome and Firefox. - this.getParentSvg().focus(); - } catch (e) { - // IE and Edge do not support focus on SVG elements. When that fails - // above, get the injectionDiv (the workspace's parent) and focus that - // instead. This doesn't work in Chrome. - try { - // In IE11, use setActive (which is IE only) so the page doesn't scroll - // to the workspace gaining focus. - this.getParentSvg().parentNode.setActive(); - } catch (e) { - // setActive support was discontinued in Edge so when that fails, call - // focus instead. - this.getParentSvg().parentNode.focus(); - } - } -}; - -/** - * Zooming the blocks centered in (x, y) coordinate with zooming in or out. - * @param {number} x X coordinate of center. - * @param {number} y Y coordinate of center. - * @param {number} amount Amount of zooming - * (negative zooms out and positive zooms in). - */ -Blockly.WorkspaceSvg.prototype.zoom = function(x, y, amount) { - var speed = this.options.zoomOptions.scaleSpeed; - var metrics = this.getMetrics(); - var center = this.getParentSvg().createSVGPoint(); - center.x = x; - center.y = y; - center = center.matrixTransform(this.getCanvas().getCTM().inverse()); - x = center.x; - y = center.y; - var canvas = this.getCanvas(); - // Scale factor. - var scaleChange = Math.pow(speed, amount); - // Clamp scale within valid range. - var newScale = this.scale * scaleChange; - if (newScale > this.options.zoomOptions.maxScale) { - scaleChange = this.options.zoomOptions.maxScale / this.scale; - } else if (newScale < this.options.zoomOptions.minScale) { - scaleChange = this.options.zoomOptions.minScale / this.scale; - } - if (this.scale == newScale) { - return; // No change in zoom. - } - if (this.scrollbar) { - var matrix = canvas.getCTM() - .translate(x * (1 - scaleChange), y * (1 - scaleChange)) - .scale(scaleChange); - // newScale and matrix.a should be identical (within a rounding error). - // ScrollX and scrollY are in pixels. - this.scrollX = matrix.e - metrics.absoluteLeft; - this.scrollY = matrix.f - metrics.absoluteTop; - } - this.setScale(newScale); - // Hide the WidgetDiv without animation (zoom makes field out of place with div) - Blockly.WidgetDiv.hide(true); - Blockly.DropDownDiv.hideWithoutAnimation(); -}; - -/** - * Zooming the blocks centered in the center of view with zooming in or out. - * @param {number} type Type of zooming (-1 zooming out and 1 zooming in). - */ -Blockly.WorkspaceSvg.prototype.zoomCenter = function(type) { - var metrics = this.getMetrics(); - var x = metrics.viewWidth / 2; - var y = metrics.viewHeight / 2; - this.zoom(x, y, type); -}; - -/** - * Zoom the blocks to fit in the workspace if possible. - */ -Blockly.WorkspaceSvg.prototype.zoomToFit = function() { - var metrics = this.getMetrics(); - var blocksBox = this.getBlocksBoundingBox(); - var blocksWidth = blocksBox.width; - var blocksHeight = blocksBox.height; - if (!blocksWidth) { - return; // Prevents zooming to infinity. - } - var workspaceWidth = metrics.viewWidth; - var workspaceHeight = metrics.viewHeight; - if (this.flyout_) { - workspaceWidth -= this.flyout_.width_; - } - if (!this.scrollbar) { - // Origin point of 0,0 is fixed, blocks will not scroll to center. - blocksWidth += metrics.contentLeft; - blocksHeight += metrics.contentTop; - } - var ratioX = workspaceWidth / blocksWidth; - var ratioY = workspaceHeight / blocksHeight; - this.setScale(Math.min(ratioX, ratioY)); - this.scrollCenter(); -}; - -/** - * Center the workspace. - */ -Blockly.WorkspaceSvg.prototype.scrollCenter = function() { - if (!this.scrollbar) { - // Can't center a non-scrolling workspace. - console.warn('Tried to scroll a non-scrollable workspace.'); - return; - } - // Hide the WidgetDiv without animation (zoom makes field out of place with div) - Blockly.WidgetDiv.hide(true); - Blockly.DropDownDiv.hideWithoutAnimation(); - Blockly.hideChaff(false); - var metrics = this.getMetrics(); - var x = (metrics.contentWidth - metrics.viewWidth) / 2; - if (this.flyout_) { - x -= this.flyout_.width_ / 2; - } - var y = (metrics.contentHeight - metrics.viewHeight) / 2; - this.scrollbar.set(x, y); -}; - -/** - * Scroll the workspace to center on the given block. - * @param {?string} id ID of block center on. - * @public - */ -Blockly.WorkspaceSvg.prototype.centerOnBlock = function(id) { - if (!this.scrollbar) { - console.warn('Tried to scroll a non-scrollable workspace.'); - return; - } - - var block = this.getBlockById(id); - if (!block) { - return; - } - - // XY is in workspace coordinates. - var xy = block.getRelativeToSurfaceXY(); - // Height/width is in workspace units. - var heightWidth = block.getHeightWidth(); - - // Find the enter of the block in workspace units. - var blockCenterY = xy.y + heightWidth.height / 2; - - // In RTL the block's position is the top right of the block, not top left. - var multiplier = this.RTL ? -1 : 1; - var blockCenterX = xy.x + (multiplier * heightWidth.width / 2); - - // Workspace scale, used to convert from workspace coordinates to pixels. - var scale = this.scale; - - // Center in pixels. 0, 0 is at the workspace origin. These numbers may - // be negative. - var pixelX = blockCenterX * scale; - var pixelY = blockCenterY * scale; - - var metrics = this.getMetrics(); - - // Scrolling to here would put the block in the top-left corner of the - // visible workspace. - var scrollToBlockX = pixelX - metrics.contentLeft; - var scrollToBlockY = pixelY - metrics.contentTop; - - // viewHeight and viewWidth are in pixels. - var halfViewWidth = metrics.viewWidth / 2; - var halfViewHeight = metrics.viewHeight / 2; - - // Put the block in the center of the visible workspace instead. - var scrollToCenterX = scrollToBlockX - halfViewWidth; - var scrollToCenterY = scrollToBlockY - halfViewHeight; - - Blockly.hideChaff(); - this.scrollbar.set(scrollToCenterX, scrollToCenterY); -}; - -/** - * Set the workspace's zoom factor. - * @param {number} newScale Zoom factor. - */ -Blockly.WorkspaceSvg.prototype.setScale = function(newScale) { - if (this.options.zoomOptions.maxScale && - newScale > this.options.zoomOptions.maxScale) { - newScale = this.options.zoomOptions.maxScale; - } else if (this.options.zoomOptions.minScale && - newScale < this.options.zoomOptions.minScale) { - newScale = this.options.zoomOptions.minScale; - } - this.scale = newScale; - if (this.grid_) { - this.grid_.update(this.scale); - } - if (this.scrollbar) { - this.scrollbar.resize(); - } else { - this.translate(this.scrollX, this.scrollY); - } - Blockly.hideChaff(false); - if (this.flyout_) { - // No toolbox, resize flyout. - this.flyout_.reflow(); - } -}; - -/** - * Scroll the workspace by a specified amount, keeping in the bounds. - * Be sure to set this.startDragMetrics with cached metrics before calling. - * @param {number} x Target X to scroll to - * @param {number} y Target Y to scroll to - */ -Blockly.WorkspaceSvg.prototype.scroll = function(x, y) { - var metrics = this.startDragMetrics; // Cached values - x = Math.min(x, -metrics.contentLeft); - y = Math.min(y, -metrics.contentTop); - x = Math.max(x, metrics.viewWidth - metrics.contentLeft - - metrics.contentWidth); - y = Math.max(y, metrics.viewHeight - metrics.contentTop - - metrics.contentHeight); - // When the workspace starts scrolling, hide the WidgetDiv without animation. - // This is to prevent a dispoal animation from happening in the wrong location. - Blockly.WidgetDiv.hide(true); - Blockly.DropDownDiv.hideWithoutAnimation(); - // Move the scrollbars and the page will scroll automatically. - this.scrollbar.set(-x - metrics.contentLeft, -y - metrics.contentTop); -}; - -/** - * Update the workspace's stack glow radius to be proportional to scale. - * Ensures that stack glows always appear to be a fixed size. - */ -Blockly.WorkspaceSvg.prototype.updateStackGlowScale_ = function() { - // No such def in the flyout workspace. - if (this.options.stackGlowBlur) { - this.options.stackGlowBlur.setAttribute('stdDeviation', - Blockly.Colours.stackGlowSize / this.scale); - } -}; - -/** - * Get the dimensions of the given workspace component, in pixels. - * @param {Blockly.Toolbox|Blockly.Flyout} elem The element to get the - * dimensions of, or null. It should be a toolbox or flyout, and should - * implement getWidth() and getHeight(). - * @return {!Object} An object containing width and height attributes, which - * will both be zero if elem did not exist. - * @private - */ -Blockly.WorkspaceSvg.getDimensionsPx_ = function(elem) { - var width = 0; - var height = 0; - if (elem) { - width = elem.getWidth(); - height = elem.getHeight(); - } - return { - width: width, - height: height - }; -}; - -/** - * Get the content dimensions of the given workspace, taking into account - * whether or not it is scrollable and what size the workspace div is on screen. - * @param {!Blockly.WorkspaceSvg} ws The workspace to measure. - * @param {!Object} svgSize An object containing height and width attributes in - * CSS pixels. Together they specify the size of the visible workspace, not - * including areas covered up by the toolbox. - * @return {!Object} The dimensions of the contents of the given workspace, as - * an object containing at least - * - height and width in pixels - * - left and top in pixels relative to the workspace origin. - * @private - */ -Blockly.WorkspaceSvg.getContentDimensions_ = function(ws, svgSize) { - if (ws.scrollbar) { - return Blockly.WorkspaceSvg.getContentDimensionsBounded_(ws, svgSize); - } else { - return Blockly.WorkspaceSvg.getContentDimensionsExact_(ws); - } -}; - -/** - * Get the bounding box for all workspace contents, in pixels. - * @param {!Blockly.WorkspaceSvg} ws The workspace to inspect. - * @return {!Object} The dimensions of the contents of the given workspace, as - * an object containing - * - height and width in pixels - * - left, right, top and bottom in pixels relative to the workspace origin. - * @private - */ -Blockly.WorkspaceSvg.getContentDimensionsExact_ = function(ws) { - // Block bounding box is in workspace coordinates. - var blockBox = ws.getBlocksBoundingBox(); - var scale = ws.scale; - - // Convert to pixels. - var width = blockBox.width * scale; - var height = blockBox.height * scale; - var left = blockBox.x * scale; - var top = blockBox.y * scale; - - return { - left: left, - top: top, - right: left + width, - bottom: top + height, - width: width, - height: height - }; -}; - -/** - * Calculate the size of a scrollable workspace, which should include room for a - * half screen border around the workspace contents. - * @param {!Blockly.WorkspaceSvg} ws The workspace to measure. - * @param {!Object} svgSize An object containing height and width attributes in - * CSS pixels. Together they specify the size of the visible workspace, not - * including areas covered up by the toolbox. - * @return {!Object} The dimensions of the contents of the given workspace, as - * an object containing - * - height and width in pixels - * - left and top in pixels relative to the workspace origin. - * @private - */ -Blockly.WorkspaceSvg.getContentDimensionsBounded_ = function(ws, svgSize) { - var content = Blockly.WorkspaceSvg.getContentDimensionsExact_(ws); - - // View height and width are both in pixels, and are the same as the SVG size. - var viewWidth = svgSize.width; - var viewHeight = svgSize.height; - var halfWidth = viewWidth / 2; - var halfHeight = viewHeight / 2; - - // Add a border around the content that is at least half a screenful wide. - // Ensure border is wide enough that blocks can scroll over entire screen. - var left = Math.min(content.left - halfWidth, content.right - viewWidth); - var right = Math.max(content.right + halfWidth, content.left + viewWidth); - - var top = Math.min(content.top - halfHeight, content.bottom - viewHeight); - var bottom = Math.max(content.bottom + halfHeight, content.top + viewHeight); - - var dimensions = { - left: left, - top: top, - height: bottom - top, - width: right - left - }; - return dimensions; -}; - -/** - * Return an object with all the metrics required to size scrollbars for a - * top level workspace. The following properties are computed: - * Coordinate system: pixel coordinates. - * .viewHeight: Height of the visible rectangle, - * .viewWidth: Width of the visible rectangle, - * .contentHeight: Height of the contents, - * .contentWidth: Width of the content, - * .viewTop: Offset of top edge of visible rectangle from parent, - * .viewLeft: Offset of left edge of visible rectangle from parent, - * .contentTop: Offset of the top-most content from the y=0 coordinate, - * .contentLeft: Offset of the left-most content from the x=0 coordinate. - * .absoluteTop: Top-edge of view. - * .absoluteLeft: Left-edge of view. - * .toolboxWidth: Width of toolbox, if it exists. Otherwise zero. - * .toolboxHeight: Height of toolbox, if it exists. Otherwise zero. - * .flyoutWidth: Width of the flyout if it is always open. Otherwise zero. - * .flyoutHeight: Height of flyout if it is always open. Otherwise zero. - * .toolboxPosition: Top, bottom, left or right. - * @return {!Object} Contains size and position metrics of a top level - * workspace. - * @private - * @this Blockly.WorkspaceSvg - */ -Blockly.WorkspaceSvg.getTopLevelWorkspaceMetrics_ = function() { - - var toolboxDimensions = - Blockly.WorkspaceSvg.getDimensionsPx_(this.toolbox_); - var flyoutDimensions = - Blockly.WorkspaceSvg.getDimensionsPx_(this.flyout_); - - // Contains height and width in CSS pixels. - // svgSize is equivalent to the size of the injectionDiv at this point. - var svgSize = Blockly.svgSize(this.getParentSvg()); - if (this.toolbox_) { - if (this.toolboxPosition == Blockly.TOOLBOX_AT_TOP || - this.toolboxPosition == Blockly.TOOLBOX_AT_BOTTOM) { - svgSize.height -= toolboxDimensions.height; - } else if (this.toolboxPosition == Blockly.TOOLBOX_AT_LEFT || - this.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { - svgSize.width -= toolboxDimensions.width; - } - } - - // svgSize is now the space taken up by the Blockly workspace, not including - // the toolbox. - var contentDimensions = - Blockly.WorkspaceSvg.getContentDimensions_(this, svgSize); - - var absoluteLeft = 0; - if (this.toolbox_ && this.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) { - absoluteLeft = toolboxDimensions.width; - } - var absoluteTop = 0; - if (this.toolbox_ && this.toolboxPosition == Blockly.TOOLBOX_AT_TOP) { - absoluteTop = toolboxDimensions.height; - } - - var metrics = { - contentHeight: contentDimensions.height, - contentWidth: contentDimensions.width, - contentTop: contentDimensions.top, - contentLeft: contentDimensions.left, - - viewHeight: svgSize.height, - viewWidth: svgSize.width, - viewTop: -this.scrollY, // Must be in pixels, somehow. - viewLeft: -this.scrollX, // Must be in pixels, somehow. - - absoluteTop: absoluteTop, - absoluteLeft: absoluteLeft, - - toolboxWidth: toolboxDimensions.width, - toolboxHeight: toolboxDimensions.height, - - flyoutWidth: flyoutDimensions.width, - flyoutHeight: flyoutDimensions.height, - - toolboxPosition: this.toolboxPosition - }; - return metrics; -}; - -/** - * Sets the X/Y translations of a top level workspace to match the scrollbars. - * @param {!Object} xyRatio Contains an x and/or y property which is a float - * between 0 and 1 specifying the degree of scrolling. - * @private - * @this Blockly.WorkspaceSvg - */ -Blockly.WorkspaceSvg.setTopLevelWorkspaceMetrics_ = function(xyRatio) { - if (!this.scrollbar) { - throw 'Attempt to set top level workspace scroll without scrollbars.'; - } - var metrics = this.getMetrics(); - if (goog.isNumber(xyRatio.x)) { - this.scrollX = -metrics.contentWidth * xyRatio.x - metrics.contentLeft; - } - if (goog.isNumber(xyRatio.y)) { - this.scrollY = -metrics.contentHeight * xyRatio.y - metrics.contentTop; - } - var x = this.scrollX + metrics.absoluteLeft; - var y = this.scrollY + metrics.absoluteTop; - this.translate(x, y); - if (this.grid_) { - this.grid_.moveTo(x, y); - } -}; - -/** - * Update whether this workspace has resizes enabled. - * If enabled, workspace will resize when appropriate. - * If disabled, workspace will not resize until re-enabled. - * Use to avoid resizing during a batch operation, for performance. - * @param {boolean} enabled Whether resizes should be enabled. - */ -Blockly.WorkspaceSvg.prototype.setResizesEnabled = function(enabled) { - var reenabled = (!this.resizesEnabled_ && enabled); - this.resizesEnabled_ = enabled; - if (reenabled) { - // Newly enabled. Trigger a resize. - this.resizeContents(); - } -}; - -/** - * Update whether this workspace has toolbox refreshes enabled. - * If enabled, the toolbox will refresh when appropriate. - * If disabled, workspace will not refresh until re-enabled. - * Use to avoid refreshing during a batch operation, for performance. - * @param {boolean} enabled Whether refreshes should be enabled. - */ -Blockly.WorkspaceSvg.prototype.setToolboxRefreshEnabled = function(enabled) { - var reenabled = (!this.toolboxRefreshEnabled_ && enabled); - this.toolboxRefreshEnabled_ = enabled; - if (reenabled) { - // Newly enabled. Trigger a refresh. - this.refreshToolboxSelection_(); - } -}; - - -/** - * Dispose of all blocks in workspace, with an optimization to prevent resizes. - */ -Blockly.WorkspaceSvg.prototype.clear = function() { - this.setResizesEnabled(false); - Blockly.WorkspaceSvg.superClass_.clear.call(this); - this.setResizesEnabled(true); -}; - -/** - * Register a callback function associated with a given key, for clicks on - * buttons and labels in the flyout. - * For instance, a button specified by the XML - * - * should be matched by a call to - * registerButtonCallback("CREATE_VARIABLE", yourCallbackFunction). - * @param {string} key The name to use to look up this function. - * @param {function(!Blockly.FlyoutButton)} func The function to call when the - * given button is clicked. - */ -Blockly.WorkspaceSvg.prototype.registerButtonCallback = function(key, func) { - goog.asserts.assert(goog.isFunction(func), - 'Button callbacks must be functions.'); - this.flyoutButtonCallbacks_[key] = func; -}; - -/** - * Get the callback function associated with a given key, for clicks on buttons - * and labels in the flyout. - * @param {string} key The name to use to look up the function. - * @return {?function(!Blockly.FlyoutButton)} The function corresponding to the - * given key for this workspace; null if no callback is registered. - */ -Blockly.WorkspaceSvg.prototype.getButtonCallback = function(key) { - var result = this.flyoutButtonCallbacks_[key]; - return result ? result : null; -}; - -/** - * Remove a callback for a click on a button in the flyout. - * @param {string} key The name associated with the callback function. - */ -Blockly.WorkspaceSvg.prototype.removeButtonCallback = function(key) { - this.flyoutButtonCallbacks_[key] = null; -}; - -/** - * Register a callback function associated with a given key, for populating - * custom toolbox categories in this workspace. See the variable and procedure - * categories as an example. - * @param {string} key The name to use to look up this function. - * @param {function(!Blockly.Workspace):!Array.} func The function to - * call when the given toolbox category is opened. - */ -Blockly.WorkspaceSvg.prototype.registerToolboxCategoryCallback = function(key, - func) { - goog.asserts.assert(goog.isFunction(func), - 'Toolbox category callbacks must be functions.'); - this.toolboxCategoryCallbacks_[key] = func; -}; - -/** - * Get the callback function associated with a given key, for populating - * custom toolbox categories in this workspace. - * @param {string} key The name to use to look up the function. - * @return {?function(!Blockly.Workspace):!Array.} The function - * corresponding to the given key for this workspace, or null if no function - * is registered. - */ -Blockly.WorkspaceSvg.prototype.getToolboxCategoryCallback = function(key) { - var result = this.toolboxCategoryCallbacks_[key]; - return result ? result : null; -}; - -/** - * Remove a callback for a click on a custom category's name in the toolbox. - * @param {string} key The name associated with the callback function. - */ -Blockly.WorkspaceSvg.prototype.removeToolboxCategoryCallback = function(key) { - this.toolboxCategoryCallbacks_[key] = null; -}; - -/** - * Look up the gesture that is tracking this touch stream on this workspace. - * May create a new gesture. - * @param {!Event} e Mouse event or touch event - * @return {Blockly.Gesture} The gesture that is tracking this touch stream, - * or null if no valid gesture exists. - * @package - */ -Blockly.WorkspaceSvg.prototype.getGesture = function(e) { - var isStart = (e.type == 'mousedown' || e.type == 'touchstart'); - - var gesture = this.currentGesture_; - if (gesture) { - if (isStart && gesture.hasStarted()) { - // That's funny. We must have missed a mouse up. - // Cancel it, rather than try to retrieve all of the state we need. - gesture.cancel(); - return null; - } - return gesture; - } - - // No gesture existed on this workspace, but this looks like the start of a - // new gesture. - if (isStart) { - this.currentGesture_ = new Blockly.Gesture(e, this); - return this.currentGesture_; - } - // No gesture existed and this event couldn't be the start of a new gesture. - return null; -}; - -/** - * Clear the reference to the current gesture. - * @package - */ -Blockly.WorkspaceSvg.prototype.clearGesture = function() { - this.currentGesture_ = null; -}; - -/** - * Cancel the current gesture, if one exists. - * @package - */ -Blockly.WorkspaceSvg.prototype.cancelCurrentGesture = function() { - if (this.currentGesture_) { - this.currentGesture_.cancel(); - } -}; - -/** - * Don't even think about using this function before talking to rachel-fenichel. - * - * Force a drag to start without clicking and dragging the block itself. Used - * to attach duplicated blocks to the mouse pointer. - * @param {!Object} fakeEvent An object with the properties needed to start a - * drag, including clientX and clientY. - * @param {!Blockly.BlockSvg} block The block to start dragging. - * @package - */ -Blockly.WorkspaceSvg.prototype.startDragWithFakeEvent = function(fakeEvent, - block) { - Blockly.Touch.clearTouchIdentifier(); - Blockly.Touch.checkTouchIdentifier(fakeEvent); - var gesture = block.workspace.getGesture(fakeEvent); - gesture.forceStartBlockDrag(fakeEvent, block); -}; - -/** - * Get the audio manager for this workspace. - * @return {Blockly.WorkspaceAudio} The audio manager for this workspace. - */ -Blockly.WorkspaceSvg.prototype.getAudioManager = function() { - return this.audioManager_; -}; - -/** - * Get the grid object for this workspace, or null if there is none. - * @return {Blockly.Grid} The grid object for this workspace. - * @package - */ -Blockly.WorkspaceSvg.prototype.getGrid = function() { - return this.grid_; -}; - -// Export symbols that would otherwise be renamed by Closure compiler. -Blockly.WorkspaceSvg.prototype['setVisible'] = - Blockly.WorkspaceSvg.prototype.setVisible; diff --git a/core/xml.js b/core/xml.js deleted file mode 100644 index 1897a558d3..0000000000 --- a/core/xml.js +++ /dev/null @@ -1,919 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview XML reader and writer. - * @author fraser@google.com (Neil Fraser) - */ -'use strict'; - -/** - * @name Blockly.Xml - * @namespace - **/ -goog.provide('Blockly.Xml'); - -goog.require('Blockly.Events.BlockCreate'); -goog.require('Blockly.Events.VarCreate'); - -goog.require('goog.asserts'); -goog.require('goog.dom'); - - -/** - * Encode a block tree as XML. - * @param {!Blockly.Workspace} workspace The workspace containing blocks. - * @param {boolean=} opt_noId True if the encoder should skip the block IDs. - * @return {!Element} XML document. - */ -Blockly.Xml.workspaceToDom = function(workspace, opt_noId) { - var xml = goog.dom.createDom('xml'); - xml.appendChild(Blockly.Xml.variablesToDom(workspace.getAllVariables())); - var comments = workspace.getTopComments(true).filter(function(topComment) { - return topComment instanceof Blockly.WorkspaceComment; - }); - for (var i = 0, comment; comment = comments[i]; i++) { - xml.appendChild(comment.toXmlWithXY(opt_noId)); - } - var blocks = workspace.getTopBlocks(true); - for (var i = 0, block; block = blocks[i]; i++) { - xml.appendChild(Blockly.Xml.blockToDomWithXY(block, opt_noId)); - } - return xml; -}; - -/** - * Encode a list of variables as XML. - * @param {!Array.} variableList List of all variable - * models. - * @return {!Element} List of XML elements. - */ -Blockly.Xml.variablesToDom = function(variableList) { - var variables = goog.dom.createDom('variables'); - for (var i = 0, variable; variable = variableList[i]; i++) { - var element = goog.dom.createDom('variable', null, variable.name); - element.setAttribute('type', variable.type); - element.setAttribute('id', variable.getId()); - element.setAttribute('islocal', variable.isLocal); - element.setAttribute('isCloud', variable.isCloud); - variables.appendChild(element); - } - return variables; -}; - -/** - * Encode a block subtree as XML with XY coordinates. - * @param {!Blockly.Block} block The root block to encode. - * @param {boolean=} opt_noId True if the encoder should skip the block ID. - * @return {!Element} Tree of XML elements. - */ -Blockly.Xml.blockToDomWithXY = function(block, opt_noId) { - var width; // Not used in LTR. - if (block.workspace.RTL) { - width = block.workspace.getWidth(); - } - var element = Blockly.Xml.blockToDom(block, opt_noId); - var xy = block.getRelativeToSurfaceXY(); - element.setAttribute('x', - Math.round(block.workspace.RTL ? width - xy.x : xy.x)); - element.setAttribute('y', Math.round(xy.y)); - return element; -}; - -/** - * Encode a variable field as XML. - * @param {!Blockly.FieldVariable} field The field to encode. - * @return {?Element} XML element, or null if the field did not need to be - * serialized. - * @private - */ -Blockly.Xml.fieldToDomVariable_ = function(field) { - var id = field.getValue(); - // The field had not been initialized fully before being serialized. - // This can happen if a block is created directly through a call to - // workspace.newBlock instead of from XML. - // The new block will be serialized for the first time when firing a block - // creation event. - if (id == null) { - field.initModel(); - id = field.getValue(); - } - // Get the variable directly from the field, instead of doing a lookup. This - // will work even if the variable has already been deleted. This can happen - // because the flyout defers deleting blocks until the next time the flyout is - // opened. - var variable = field.getVariable(); - - if (!variable) { - throw Error('Tried to serialize a variable field with no variable.'); - } - var container = goog.dom.createDom('field', null, variable.name); - container.setAttribute('name', field.name); - container.setAttribute('id', variable.getId()); - container.setAttribute('variabletype', variable.type); - return container; -}; - -/** - * Encode a field as XML. - * @param {!Blockly.Field} field The field to encode. - * @param {!Blockly.Workspace} workspace The workspace that the field is in. - * @return {?Element} XML element, or null if the field did not need to be - * serialized. - * @private - */ -Blockly.Xml.fieldToDom_ = function(field) { - if (field.name && field.SERIALIZABLE) { - if (field.referencesVariables()) { - return Blockly.Xml.fieldToDomVariable_(field); - } else { - var container = goog.dom.createDom('field', null, field.getValue()); - container.setAttribute('name', field.name); - return container; - } - } - return null; -}; - -/** - * Encode all of a block's fields as XML and attach them to the given tree of - * XML elements. - * @param {!Blockly.Block} block A block with fields to be encoded. - * @param {!Element} element The XML element to which the field DOM should be - * attached. - * @private - */ -Blockly.Xml.allFieldsToDom_ = function(block, element) { - for (var i = 0, input; input = block.inputList[i]; i++) { - for (var j = 0, field; field = input.fieldRow[j]; j++) { - var fieldDom = Blockly.Xml.fieldToDom_(field); - if (fieldDom) { - element.appendChild(fieldDom); - } - } - } -}; - -/** - * Encode a block subtree as XML. - * @param {!Blockly.Block} block The root block to encode. - * @param {boolean=} opt_noId True if the encoder should skip the block ID. - * @return {!Element} Tree of XML elements. - */ -Blockly.Xml.blockToDom = function(block, opt_noId) { - var element = goog.dom.createDom(block.isShadow() ? 'shadow' : 'block'); - element.setAttribute('type', block.type); - if (!opt_noId) { - element.setAttribute('id', block.id); - } - if (block.mutationToDom) { - // Custom data for an advanced block. - var mutation = block.mutationToDom(); - if (mutation && (mutation.hasChildNodes() || mutation.hasAttributes())) { - element.appendChild(mutation); - } - } - - Blockly.Xml.allFieldsToDom_(block, element); - - Blockly.Xml.scratchCommentToDom_(block, element); - - if (block.data) { - var dataElement = goog.dom.createDom('data', null, block.data); - element.appendChild(dataElement); - } - - for (var i = 0, input; input = block.inputList[i]; i++) { - var container; - var empty = true; - if (input.type == Blockly.DUMMY_INPUT) { - continue; - } else { - var childBlock = input.connection.targetBlock(); - if (input.type == Blockly.INPUT_VALUE) { - container = goog.dom.createDom('value'); - } else if (input.type == Blockly.NEXT_STATEMENT) { - container = goog.dom.createDom('statement'); - } - var shadow = input.connection.getShadowDom(); - if (shadow && (!childBlock || !childBlock.isShadow())) { - var shadowClone = Blockly.Xml.cloneShadow_(shadow); - // Remove the ID from the shadow dom clone if opt_noId - // is specified to true. - if (opt_noId && shadowClone.getAttribute('id')) { - shadowClone.removeAttribute('id'); - } - container.appendChild(shadowClone); - } - if (childBlock) { - container.appendChild(Blockly.Xml.blockToDom(childBlock, opt_noId)); - empty = false; - } - } - container.setAttribute('name', input.name); - if (!empty) { - element.appendChild(container); - } - } - if (block.inputsInlineDefault != block.inputsInline) { - element.setAttribute('inline', block.inputsInline); - } - if (block.isCollapsed()) { - element.setAttribute('collapsed', true); - } - if (block.disabled) { - element.setAttribute('disabled', true); - } - if (!block.isDeletable() && !block.isShadow()) { - element.setAttribute('deletable', false); - } - if (!block.isMovable() && !block.isShadow()) { - element.setAttribute('movable', false); - } - if (!block.isEditable()) { - element.setAttribute('editable', false); - } - - var nextBlock = block.getNextBlock(); - if (nextBlock) { - var container = goog.dom.createDom('next', null, - Blockly.Xml.blockToDom(nextBlock, opt_noId)); - element.appendChild(container); - } - var shadow = block.nextConnection && block.nextConnection.getShadowDom(); - if (shadow && (!nextBlock || !nextBlock.isShadow())) { - container.appendChild(Blockly.Xml.cloneShadow_(shadow)); - } - - return element; -}; - -/** - * Encode a ScratchBlockComment as XML. - * @param {!Blockly.ScratchBlockComment} block The block possibly containing - * a comment to encode. - * @param {!Element} element The XML element to which the comment should - * encoding should be attached. - * @private - */ -Blockly.Xml.scratchCommentToDom_ = function(block, element) { - var commentText = block.getCommentText(); - if (commentText) { - var commentElement = goog.dom.createDom('comment', null, commentText); - if (typeof block.comment == 'object') { - commentElement.setAttribute('id', block.comment.id); - commentElement.setAttribute('pinned', block.comment.isVisible()); - var hw; - if (block.comment instanceof Blockly.ScratchBlockComment) { - hw = block.comment.getHeightWidth(); - } else { - hw = block.comment.getBubbleSize(); - } - commentElement.setAttribute('h', hw.height); - commentElement.setAttribute('w', hw.width); - var xy = block.comment.getXY(); - commentElement.setAttribute('x', - Math.round(block.workspace.RTL ? block.workspace.getWidth() - xy.x - hw.width : - xy.x)); - commentElement.setAttribute('y', xy.y); - commentElement.setAttribute('minimized', block.comment.isMinimized()); - - } - element.appendChild(commentElement); - } -}; - -/** - * Deeply clone the shadow's DOM so that changes don't back-wash to the block. - * @param {!Element} shadow A tree of XML elements. - * @return {!Element} A tree of XML elements. - * @private - */ -Blockly.Xml.cloneShadow_ = function(shadow) { - shadow = shadow.cloneNode(true); - // Walk the tree looking for whitespace. Don't prune whitespace in a tag. - var node = shadow; - var textNode; - while (node) { - if (node.firstChild) { - node = node.firstChild; - } else { - while (node && !node.nextSibling) { - textNode = node; - node = node.parentNode; - if (textNode.nodeType == 3 && textNode.data.trim() == '' && - node.firstChild != textNode) { - // Prune whitespace after a tag. - goog.dom.removeNode(textNode); - } - } - if (node) { - textNode = node; - node = node.nextSibling; - if (textNode.nodeType == 3 && textNode.data.trim() == '') { - // Prune whitespace before a tag. - goog.dom.removeNode(textNode); - } - } - } - } - return shadow; -}; - -/** - * Converts a DOM structure into plain text. - * Currently the text format is fairly ugly: all one line with no whitespace. - * @param {!Element} dom A tree of XML elements. - * @return {string} Text representation. - */ -Blockly.Xml.domToText = function(dom) { - var oSerializer = new XMLSerializer(); - return oSerializer.serializeToString(dom); -}; - -/** - * Converts a DOM structure into properly indented text. - * @param {!Element} dom A tree of XML elements. - * @return {string} Text representation. - */ -Blockly.Xml.domToPrettyText = function(dom) { - // This function is not guaranteed to be correct for all XML. - // But it handles the XML that Blockly generates. - var blob = Blockly.Xml.domToText(dom); - // Place every open and close tag on its own line. - var lines = blob.split('<'); - // Indent every line. - var indent = ''; - for (var i = 1; i < lines.length; i++) { - var line = lines[i]; - if (line[0] == '/') { - indent = indent.substring(2); - } - lines[i] = indent + '<' + line; - if (line[0] != '/' && line.slice(-2) != '/>') { - indent += ' '; - } - } - // Pull simple tags back together. - // E.g. - var text = lines.join('\n'); - text = text.replace(/(<(\w+)\b[^>]*>[^\n]*)\n *<\/\2>/g, '$1'); - // Trim leading blank line. - return text.replace(/^\n/, ''); -}; - -/** - * Converts plain text into a DOM structure. - * Throws an error if XML doesn't parse. - * @param {string} text Text representation. - * @return {!Element} A tree of XML elements. - */ -Blockly.Xml.textToDom = function(text) { - var oParser = new DOMParser(); - var dom = oParser.parseFromString(text, 'text/xml'); - // The DOM should have one and only one top-level node, an XML tag. - if (!dom || !dom.firstChild || - dom.firstChild.nodeName.toLowerCase() != 'xml' || - dom.firstChild !== dom.lastChild) { - // Whatever we got back from the parser is not XML. - goog.asserts.fail('Blockly.Xml.textToDom did not obtain a valid XML tree.'); - } - return dom.firstChild; -}; - -/** - * Clear the given workspace then decode an XML DOM and - * create blocks on the workspace. - * @param {!Element} xml XML DOM. - * @param {!Blockly.Workspace} workspace The workspace. - * @return {Array.} An array containing new block ids. - */ -Blockly.Xml.clearWorkspaceAndLoadFromXml = function(xml, workspace) { - workspace.setResizesEnabled(false); - workspace.setToolboxRefreshEnabled(false); - workspace.clear(); - var blockIds = Blockly.Xml.domToWorkspace(xml, workspace); - workspace.setResizesEnabled(true); - workspace.setToolboxRefreshEnabled(true); - return blockIds; -}; - -/** - * Decode an XML DOM and create blocks on the workspace. - * @param {!Element} xml XML DOM. - * @param {!Blockly.Workspace} workspace The workspace. - * @return {Array.} An array containing new block IDs. - */ -Blockly.Xml.domToWorkspace = function(xml, workspace) { - if (xml instanceof Blockly.Workspace) { - var swap = xml; - xml = workspace; - workspace = swap; - console.warn('Deprecated call to Blockly.Xml.domToWorkspace, ' + - 'swap the arguments.'); - } - var width; // Not used in LTR. - if (workspace.RTL) { - width = workspace.getWidth(); - } - var newBlockIds = []; // A list of block IDs added by this call. - Blockly.Field.startCache(); - // Safari 7.1.3 is known to provide node lists with extra references to - // children beyond the lists' length. Trust the length, do not use the - // looping pattern of checking the index for an object. - var childCount = xml.childNodes.length; - var existingGroup = Blockly.Events.getGroup(); - if (!existingGroup) { - Blockly.Events.setGroup(true); - } - - // Disable workspace resizes as an optimization. - if (workspace.setResizesEnabled) { - workspace.setResizesEnabled(false); - } - var variablesFirst = true; - try { - for (var i = 0; i < childCount; i++) { - var xmlChild = xml.childNodes[i]; - var name = xmlChild.nodeName.toLowerCase(); - if (name == 'block' || - (name == 'shadow' && !Blockly.Events.recordUndo)) { - // Allow top-level shadow blocks if recordUndo is disabled since - // that means an undo is in progress. Such a block is expected - // to be moved to a nested destination in the next operation. - var block = Blockly.Xml.domToBlock(xmlChild, workspace); - newBlockIds.push(block.id); - var blockX = xmlChild.hasAttribute('x') ? - parseInt(xmlChild.getAttribute('x'), 10) : 10; - var blockY = xmlChild.hasAttribute('y') ? - parseInt(xmlChild.getAttribute('y'), 10) : 10; - if (!isNaN(blockX) && !isNaN(blockY)) { - block.moveBy(workspace.RTL ? width - blockX : blockX, blockY); - if (block.comment && typeof block.comment === 'object') { - var commentXY = block.comment.getXY(); - var commentWidth = block.comment.getBubbleSize().width; - block.comment.moveTo(block.workspace.RTL ? width - commentXY.x - commentWidth : commentXY.x, commentXY.y); - } - } - variablesFirst = false; - } else if (name == 'shadow') { - goog.asserts.fail('Shadow block cannot be a top-level block.'); - variablesFirst = false; - } else if (name == 'comment') { - if (workspace.rendered) { - Blockly.WorkspaceCommentSvg.fromXml(xmlChild, workspace, width); - } else { - Blockly.WorkspaceComment.fromXml(xmlChild, workspace); - } - } else if (name == 'variables') { - if (variablesFirst) { - Blockly.Xml.domToVariables(xmlChild, workspace); - } else { - throw Error('\'variables\' tag must exist once before block and ' + - 'shadow tag elements in the workspace XML, but it was found in ' + - 'another location.'); - } - variablesFirst = false; - } - } - } finally { - if (!existingGroup) { - Blockly.Events.setGroup(false); - } - Blockly.Field.stopCache(); - } - // Re-enable workspace resizing. - if (workspace.setResizesEnabled) { - workspace.setResizesEnabled(true); - } - return newBlockIds; -}; - -/** - * Decode an XML DOM and create blocks on the workspace. Position the new - * blocks immediately below prior blocks, aligned by their starting edge. - * @param {!Element} xml The XML DOM. - * @param {!Blockly.Workspace} workspace The workspace to add to. - * @return {Array.} An array containing new block IDs. - */ -Blockly.Xml.appendDomToWorkspace = function(xml, workspace) { - var bbox; // Bounding box of the current blocks. - // First check if we have a workspaceSvg, otherwise the blocks have no shape - // and the position does not matter. - if (workspace.hasOwnProperty('scale')) { - var savetab = Blockly.BlockSvg.TAB_WIDTH; - try { - Blockly.BlockSvg.TAB_WIDTH = 0; - bbox = workspace.getBlocksBoundingBox(); - } finally { - Blockly.BlockSvg.TAB_WIDTH = savetab; - } - } - // Load the new blocks into the workspace and get the IDs of the new blocks. - var newBlockIds = Blockly.Xml.domToWorkspace(xml,workspace); - if (bbox && bbox.height) { // check if any previous block - var offsetY = 0; // offset to add to y of the new block - var offsetX = 0; - var farY = bbox.y + bbox.height; //bottom position - var topX = bbox.x; // x of bounding box - // check position of the new blocks - var newX = Infinity; // x of top corner - var newY = Infinity; // y of top corner - for (var i = 0; i < newBlockIds.length; i++) { - var blockXY = workspace.getBlockById(newBlockIds[i]).getRelativeToSurfaceXY(); - if (blockXY.y < newY) { - newY = blockXY.y; - } - if (blockXY.x < newX) { //if we align also on x - newX = blockXY.x; - } - } - offsetY = farY - newY + Blockly.BlockSvg.SEP_SPACE_Y; - offsetX = topX - newX; - // move the new blocks to append them at the bottom - var width; // Not used in LTR. - if (workspace.RTL) { - width = workspace.getWidth(); - } - for (var i = 0; i < newBlockIds.length; i++) { - var block = workspace.getBlockById(newBlockIds[i]); - block.moveBy(workspace.RTL ? width - offsetX : offsetX, offsetY); - } - } - return newBlockIds; -}; - -/** - * Decode an XML block tag and create a block (and possibly sub blocks) on the - * workspace. - * @param {!Element} xmlBlock XML block element. - * @param {!Blockly.Workspace} workspace The workspace. - * @return {!Blockly.Block} The root block created. - */ -Blockly.Xml.domToBlock = function(xmlBlock, workspace) { - if (xmlBlock instanceof Blockly.Workspace) { - var swap = xmlBlock; - xmlBlock = workspace; - workspace = swap; - console.warn('Deprecated call to Blockly.Xml.domToBlock, ' + - 'swap the arguments.'); - } - // Create top-level block. - Blockly.Events.disable(); - var variablesBeforeCreation = workspace.getAllVariables(); - try { - var topBlock = Blockly.Xml.domToBlockHeadless_(xmlBlock, workspace); - // Generate list of all blocks. - var blocks = topBlock.getDescendants(false); - if (workspace.rendered) { - // Hide connections to speed up assembly. - topBlock.setConnectionsHidden(true); - // Render each block. - for (var i = blocks.length - 1; i >= 0; i--) { - blocks[i].initSvg(); - } - for (var i = blocks.length - 1; i >= 0; i--) { - blocks[i].render(false); - } - // Populating the connection database may be deferred until after the - // blocks have rendered. - if (!workspace.isFlyout) { - setTimeout(function() { - if (topBlock.workspace) { // Check that the block hasn't been deleted. - topBlock.setConnectionsHidden(false); - } - }, 1); - } - topBlock.updateDisabled(); - // Allow the scrollbars to resize and move based on the new contents. - // TODO(@picklesrus): #387. Remove when domToBlock avoids resizing. - workspace.resizeContents(); - } else { - for (var i = blocks.length - 1; i >= 0; i--) { - blocks[i].initModel(); - } - } - } finally { - Blockly.Events.enable(); - } - if (Blockly.Events.isEnabled()) { - var newVariables = Blockly.Variables.getAddedVariables(workspace, - variablesBeforeCreation); - // Fire a VarCreate event for each (if any) new variable created. - for (var i = 0; i < newVariables.length; i++) { - var thisVariable = newVariables[i]; - Blockly.Events.fire(new Blockly.Events.VarCreate(thisVariable)); - } - // Block events come after var events, in case they refer to newly created - // variables. - Blockly.Events.fire(new Blockly.Events.BlockCreate(topBlock)); - } - return topBlock; -}; - -/** - * Decode an XML list of variables and add the variables to the workspace. - * @param {!Element} xmlVariables List of XML variable elements. - * @param {!Blockly.Workspace} workspace The workspace to which the variable - * should be added. - */ -Blockly.Xml.domToVariables = function(xmlVariables, workspace) { - for (var i = 0, xmlChild; xmlChild = xmlVariables.children[i]; i++) { - var type = xmlChild.getAttribute('type'); - var id = xmlChild.getAttribute('id'); - var isLocal = xmlChild.getAttribute('islocal') == 'true'; - var isCloud = xmlChild.getAttribute('iscloud') == 'true'; - var name = xmlChild.textContent; - - if (typeof(type) === undefined || type === null) { - throw Error('Variable with id, ' + id + ' is without a type'); - } - workspace.createVariable(name, type, id, isLocal, isCloud); - } -}; - -/** - * Decode an XML block tag and create a block (and possibly sub blocks) on the - * workspace. - * @param {!Element} xmlBlock XML block element. - * @param {!Blockly.Workspace} workspace The workspace. - * @return {!Blockly.Block} The root block created. - * @private - */ -Blockly.Xml.domToBlockHeadless_ = function(xmlBlock, workspace) { - var block = null; - var prototypeName = xmlBlock.getAttribute('type'); - goog.asserts.assert( - prototypeName, 'Block type unspecified: %s', xmlBlock.outerHTML); - var id = xmlBlock.getAttribute('id'); - block = workspace.newBlock(prototypeName, id); - - var blockChild = null; - for (var i = 0, xmlChild; xmlChild = xmlBlock.childNodes[i]; i++) { - if (xmlChild.nodeType == 3) { - // Ignore any text at the level. It's all whitespace anyway. - continue; - } - var input; - - // Find any enclosed blocks or shadows in this tag. - var childBlockElement = null; - var childShadowElement = null; - for (var j = 0, grandchild; grandchild = xmlChild.childNodes[j]; j++) { - if (grandchild.nodeType == 1) { - if (grandchild.nodeName.toLowerCase() == 'block') { - childBlockElement = /** @type {!Element} */ (grandchild); - } else if (grandchild.nodeName.toLowerCase() == 'shadow') { - childShadowElement = /** @type {!Element} */ (grandchild); - } - } - } - // Use the shadow block if there is no child block. - if (!childBlockElement && childShadowElement) { - childBlockElement = childShadowElement; - } - - var name = xmlChild.getAttribute('name'); - switch (xmlChild.nodeName.toLowerCase()) { - case 'mutation': - // Custom data for an advanced block. - if (block.domToMutation) { - block.domToMutation(xmlChild); - if (block.initSvg) { - // Mutation may have added some elements that need initializing. - block.initSvg(); - } - } - break; - case 'comment': - var commentId = xmlChild.getAttribute('id'); - var bubbleX = parseInt(xmlChild.getAttribute('x'), 10); - var bubbleY = parseInt(xmlChild.getAttribute('y'), 10); - var minimized = xmlChild.getAttribute('minimized') || false; - - // Note bubbleX and bubbleY can be NaN, but the ScratchBlockComment - // constructor will handle that. - block.setCommentText(xmlChild.textContent, commentId, bubbleX, bubbleY, - minimized == 'true'); - - var visible = xmlChild.getAttribute('pinned'); - if (visible && !block.isInFlyout) { - // Give the renderer a millisecond to render and position the block - // before positioning the comment bubble. - setTimeout(function() { - if (block.comment && block.comment.setVisible) { - block.comment.setVisible(visible == 'true'); - } - }, 1); - } - var bubbleW = parseInt(xmlChild.getAttribute('w'), 10); - var bubbleH = parseInt(xmlChild.getAttribute('h'), 10); - if (!isNaN(bubbleW) && !isNaN(bubbleH) && - block.comment && block.comment.setVisible) { - if (block.comment instanceof Blockly.ScratchBlockComment) { - block.comment.setSize(bubbleW, bubbleH); - } else { - block.comment.setBubbleSize(bubbleW, bubbleH); - } - } - break; - case 'data': - block.data = xmlChild.textContent; - break; - case 'title': - // Titles were renamed to field in December 2013. - // Fall through. - case 'field': - Blockly.Xml.domToField_(block, name, xmlChild); - break; - case 'value': - case 'statement': - input = block.getInput(name); - if (!input) { - console.warn('Ignoring non-existent input ' + name + ' in block ' + - prototypeName); - break; - } - if (childShadowElement) { - input.connection.setShadowDom(childShadowElement); - } - if (childBlockElement) { - blockChild = Blockly.Xml.domToBlockHeadless_(childBlockElement, - workspace); - if (blockChild.outputConnection) { - input.connection.connect(blockChild.outputConnection); - } else if (blockChild.previousConnection) { - input.connection.connect(blockChild.previousConnection); - } else { - goog.asserts.fail( - 'Child block does not have output or previous statement.'); - } - } - break; - case 'next': - if (childShadowElement && block.nextConnection) { - block.nextConnection.setShadowDom(childShadowElement); - } - if (childBlockElement) { - goog.asserts.assert(block.nextConnection, - 'Next statement does not exist.'); - // If there is more than one XML 'next' tag. - goog.asserts.assert(!block.nextConnection.isConnected(), - 'Next statement is already connected.'); - blockChild = Blockly.Xml.domToBlockHeadless_(childBlockElement, - workspace); - goog.asserts.assert(blockChild.previousConnection, - 'Next block does not have previous statement.'); - block.nextConnection.connect(blockChild.previousConnection); - } - break; - default: - // Unknown tag; ignore. Same principle as HTML parsers. - console.warn('Ignoring unknown tag: ' + xmlChild.nodeName); - } - } - - var inline = xmlBlock.getAttribute('inline'); - if (inline) { - block.setInputsInline(inline == 'true'); - } - var disabled = xmlBlock.getAttribute('disabled'); - if (disabled) { - block.setDisabled(disabled == 'true' || disabled == 'disabled'); - } - var deletable = xmlBlock.getAttribute('deletable'); - if (deletable) { - block.setDeletable(deletable == 'true'); - } - var movable = xmlBlock.getAttribute('movable'); - if (movable) { - block.setMovable(movable == 'true'); - } - var editable = xmlBlock.getAttribute('editable'); - if (editable) { - block.setEditable(editable == 'true'); - } - var collapsed = xmlBlock.getAttribute('collapsed'); - if (collapsed) { - block.setCollapsed(collapsed == 'true'); - } - if (xmlBlock.nodeName.toLowerCase() == 'shadow') { - // Ensure all children are also shadows. - var children = block.getChildren(false); - for (var i = 0, child; child = children[i]; i++) { - goog.asserts.assert( - child.isShadow(), 'Shadow block not allowed non-shadow child.'); - } - block.setShadow(true); - } - return block; -}; - -/** - * Decode an XML variable field tag and set the value of that field. - * @param {!Blockly.Workspace} workspace The workspace that is currently being - * deserialized. - * @param {!Element} xml The field tag to decode. - * @param {string} text The text content of the XML tag. - * @param {!Blockly.FieldVariable} field The field on which the value will be - * set. - * @private - */ -Blockly.Xml.domToFieldVariable_ = function(workspace, xml, text, field) { - var type = xml.getAttribute('variabletype') || ''; - // TODO (fenichel): Does this need to be explicit or not? - if (type == '\'\'') { - type = ''; - } - - var variable; - // This check ensures that there is not both a potential variable and a real - // variable with the same name and type. - if (!workspace.getPotentialVariableMap() && !workspace.isFlyout && - workspace.getFlyout()) { - var flyoutWs = workspace.getFlyout().getWorkspace(); - variable = Blockly.Variables.realizePotentialVar(text, type, flyoutWs, true); - } - if (!variable) { - variable = Blockly.Variables.getOrCreateVariablePackage(workspace, xml.id, - text, type); - } - - // This should never happen :) - if (type != null && type !== variable.type) { - throw Error('Serialized variable type with id \'' + - variable.getId() + '\' had type ' + variable.type + ', and ' + - 'does not match variable field that references it: ' + - Blockly.Xml.domToText(xml) + '.'); - } - - field.setValue(variable.getId()); -}; - -/** - * Decode an XML field tag and set the value of that field on the given block. - * @param {!Blockly.Block} block The block that is currently being deserialized. - * @param {string} fieldName The name of the field on the block. - * @param {!Element} xml The field tag to decode. - * @private - */ -Blockly.Xml.domToField_ = function(block, fieldName, xml) { - var field = block.getField(fieldName); - if (!field) { - console.warn('Ignoring non-existent field ' + fieldName + ' in block ' + - block.type); - return; - } - - var workspace = block.workspace; - var text = xml.textContent; - if (field.referencesVariables()) { - Blockly.Xml.domToFieldVariable_(workspace, xml, text, field); - } else { - field.setValue(text); - } -}; - -/** - * Remove any 'next' block (statements in a stack). - * @param {!Element} xmlBlock XML block element. - */ -Blockly.Xml.deleteNext = function(xmlBlock) { - for (var i = 0, child; child = xmlBlock.childNodes[i]; i++) { - if (child.nodeName.toLowerCase() == 'next') { - xmlBlock.removeChild(child); - break; - } - } -}; - -// Export symbols that would otherwise be renamed by Closure compiler. -if (!goog.global['Blockly']) { - goog.global['Blockly'] = {}; -} -if (!goog.global['Blockly']['Xml']) { - goog.global['Blockly']['Xml'] = {}; -} -goog.global['Blockly']['Xml']['domToText'] = Blockly.Xml.domToText; -goog.global['Blockly']['Xml']['domToWorkspace'] = Blockly.Xml.domToWorkspace; -goog.global['Blockly']['Xml']['textToDom'] = Blockly.Xml.textToDom; -goog.global['Blockly']['Xml']['workspaceToDom'] = Blockly.Xml.workspaceToDom; -goog.global['Blockly']['Xml']['clearWorkspaceAndLoadFromXml'] = - Blockly.Xml.clearWorkspaceAndLoadFromXml; diff --git a/core/zoom_controls.js b/core/zoom_controls.js deleted file mode 100644 index 3f8f1ab760..0000000000 --- a/core/zoom_controls.js +++ /dev/null @@ -1,301 +0,0 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2015 Google Inc. - * https://developers.google.com/blockly/ - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Object representing a zoom icons. - * @author carloslfu@gmail.com (Carlos Galarza) - */ -'use strict'; - -goog.provide('Blockly.ZoomControls'); - -goog.require('Blockly.Touch'); -goog.require('goog.dom'); - - -/** - * Class for a zoom controls. - * @param {!Blockly.Workspace} workspace The workspace to sit in. - * @constructor - */ -Blockly.ZoomControls = function(workspace) { - this.workspace_ = workspace; -}; - -/** - * Zoom in icon path. - * @type {string} - * @private - */ -Blockly.ZoomControls.prototype.ZOOM_IN_PATH_ = 'zoom-in.svg'; - -/** - * Zoom out icon path. - * @type {string} - * @private - */ -Blockly.ZoomControls.prototype.ZOOM_OUT_PATH_ = 'zoom-out.svg'; - -/** - * Zoom reset icon path. - * @type {string} - * @private - */ -Blockly.ZoomControls.prototype.ZOOM_RESET_PATH_ = 'zoom-reset.svg'; - -/** - * Width of the zoom controls. - * @type {number} - * @private - */ -Blockly.ZoomControls.prototype.WIDTH_ = 36; - -/** - * Height of the zoom controls. - * @type {number} - * @private - */ -Blockly.ZoomControls.prototype.HEIGHT_ = 124; - -/** - * Distance between each zoom control. - * @type {number} - * @private - */ -Blockly.ZoomControls.prototype.MARGIN_BETWEEN_ = 8; - -/** - * Distance between zoom controls and bottom edge of workspace. - * @type {number} - * @private - */ -Blockly.ZoomControls.prototype.MARGIN_BOTTOM_ = 12; - -/** - * Distance between zoom controls and right edge of workspace. - * @type {number} - * @private - */ -Blockly.ZoomControls.prototype.MARGIN_SIDE_ = 12; - -/** - * The SVG group containing the zoom controls. - * @type {Element} - * @private - */ -Blockly.ZoomControls.prototype.svgGroup_ = null; - -/** - * Left coordinate of the zoom controls. - * @type {number} - * @private - */ -Blockly.ZoomControls.prototype.left_ = 0; - -/** - * Top coordinate of the zoom controls. - * @type {number} - * @private - */ -Blockly.ZoomControls.prototype.top_ = 0; - -/** - * Create the zoom controls. - * @return {!Element} The zoom controls SVG group. - */ -Blockly.ZoomControls.prototype.createDom = function() { - this.svgGroup_ = - Blockly.utils.createSvgElement('g', {'class': 'blocklyZoom'}, null); - this.createZoomOutSvg_(); - this.createZoomInSvg_(); - this.createZoomResetSvg_(); - return this.svgGroup_; -}; - -/** - * Initialize the zoom controls. - * @param {number} bottom Distance from workspace bottom to bottom of controls. - * @return {number} Distance from workspace bottom to the top of controls. - */ -Blockly.ZoomControls.prototype.init = function(bottom) { - this.bottom_ = this.MARGIN_BOTTOM_ + bottom; - return this.bottom_ + this.HEIGHT_; -}; - -/** - * Dispose of this zoom controls. - * Unlink from all DOM elements to prevent memory leaks. - */ -Blockly.ZoomControls.prototype.dispose = function() { - if (this.svgGroup_) { - goog.dom.removeNode(this.svgGroup_); - this.svgGroup_ = null; - } - this.workspace_ = null; -}; - -/** - * Move the zoom controls to the bottom-right corner. - */ -Blockly.ZoomControls.prototype.position = function() { - var metrics = this.workspace_.getMetrics(); - if (!metrics) { - // There are no metrics available (workspace is probably not visible). - return; - } - if (this.workspace_.RTL) { - this.left_ = this.MARGIN_SIDE_ + Blockly.Scrollbar.scrollbarThickness; - if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_LEFT) { - this.left_ += metrics.flyoutWidth; - if (this.workspace_.toolbox_) { - this.left_ += metrics.absoluteLeft; - } - } - } else { - this.left_ = metrics.viewWidth + metrics.absoluteLeft - - this.WIDTH_ - this.MARGIN_SIDE_ - Blockly.Scrollbar.scrollbarThickness; - - if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { - this.left_ -= metrics.flyoutWidth; - } - } - this.top_ = metrics.viewHeight + metrics.absoluteTop - - this.HEIGHT_ - this.bottom_; - if (metrics.toolboxPosition == Blockly.TOOLBOX_AT_BOTTOM) { - this.top_ -= metrics.flyoutHeight; - } - this.svgGroup_.setAttribute('transform', - 'translate(' + this.left_ + ',' + this.top_ + ')'); -}; - -/** - * Create the zoom in icon and its event handler. - * The Scratch Blocks implementation of this function is different from the - * Blockly implementation. - * @private - */ -Blockly.ZoomControls.prototype.createZoomOutSvg_ = function() { - /* This markup will be generated and added to the "blocklyZoom" group: - - - */ - var ws = this.workspace_; - /** - * Zoom out control. - * @type {SVGElement} - */ - var zoomoutSvg = Blockly.utils.createSvgElement( - 'image', - { - 'width': this.WIDTH_, - 'height': this.WIDTH_, - 'y': (this.WIDTH_ * 1) + (this.MARGIN_BETWEEN_ * 1) - }, - this.svgGroup_ - ); - zoomoutSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', - ws.options.pathToMedia + this.ZOOM_OUT_PATH_); - // Attach listener. - Blockly.bindEventWithChecks_(zoomoutSvg, 'mousedown', null, function(e) { - ws.markFocused(); - ws.zoomCenter(-1); - Blockly.Touch.clearTouchIdentifier(); // Don't block future drags. - e.stopPropagation(); // Don't start a workspace scroll. - e.preventDefault(); // Stop double-clicking from selecting text. - }); -}; - -/** - * Create the zoom out icon and its event handler. - * The Scratch Blocks implementation of this function is different from the - * Blockly implementation. - * @private - */ -Blockly.ZoomControls.prototype.createZoomInSvg_ = function() { - /* This markup will be generated and added to the "blocklyZoom" group: - - - */ - var ws = this.workspace_; - /** - * Zoom in control. - * @type {SVGElement} - */ - var zoominSvg = Blockly.utils.createSvgElement( - 'image', - { - 'width': this.WIDTH_, - 'height': this.WIDTH_, - 'y': 0 - }, - this.svgGroup_ - ); - zoominSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', - ws.options.pathToMedia + this.ZOOM_IN_PATH_); - - // Attach listener. - Blockly.bindEventWithChecks_(zoominSvg, 'mousedown', null, function(e) { - ws.markFocused(); - ws.zoomCenter(1); - Blockly.Touch.clearTouchIdentifier(); // Don't block future drags. - e.stopPropagation(); // Don't start a workspace scroll. - e.preventDefault(); // Stop double-clicking from selecting text. - }); -}; - -/** - * Create the zoom reset icon and its event handler. - * The Scratch Blocks implementation of this function is different from the - * Blockly implementation. - * @private - */ -Blockly.ZoomControls.prototype.createZoomResetSvg_ = function() { - /* This markup will be generated and added to the "blocklyZoom" group: - - - */ - var ws = this.workspace_; - - /** - * Zoom reset control. - * @type {SVGElement} - */ - var zoomresetSvg = Blockly.utils.createSvgElement( - 'image', - { - 'width': this.WIDTH_, - 'height': this.WIDTH_, - 'y': (this.WIDTH_ * 2) + (this.MARGIN_BETWEEN_ * 2) - }, - this.svgGroup_ - ); - zoomresetSvg.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', - ws.options.pathToMedia + this.ZOOM_RESET_PATH_); - - // Attach event listeners. - Blockly.bindEventWithChecks_(zoomresetSvg, 'mousedown', null, function(e) { - ws.markFocused(); - ws.setScale(ws.options.zoomOptions.startScale); - ws.scrollCenter(); - Blockly.Touch.clearTouchIdentifier(); // Don't block future drags. - e.stopPropagation(); // Don't start a workspace scroll. - e.preventDefault(); // Stop double-clicking from selecting text. - }); -}; diff --git a/local_build.sh b/local_build.sh deleted file mode 100755 index fc02445db2..0000000000 --- a/local_build.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/bin/bash - -# Locally build and compress the core Blockly files into a single JavaScript -# file. -# -# Copyright 2018 Google Inc. -# https://developers.google.com/blockly/ -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Usage: local_build.sh. -# -# This script generates only local_blockly_compressed_vertical.js. You may -# modify it as needed to build other files. -# -# The compressed file is a concatenation of all of Scratch-Blocks's core files, -# run through a local copy of Google's Closure Compiler with simple -# optimizations turned on. - -# Future work: -# - Trim down Google's Apache licenses, to match the output of build.py. -# - Generate other compressed files generated by build.py normally. -# - Add a good error message if multiple versions of the closure compiler were -# found. -# - Add a flag for generating horizontal (still default to vertical). - -# Find the Closure Compiler. -if [ -f "$(npm root)/google-closure-compiler/compiler.jar" ]; then - COMPILER="$(npm root)/google-closure-compiler/compiler.jar" -elif [ -f closure-compiler*.jar ]; then - COMPILER="closure-compiler*.jar" - # TODO: Check whether multiple files were found. -else - echo "ERROR: Closure Compiler not found." - echo "Download from this URL, and place jar file in current directory." - echo "https://dl.google.com/closure-compiler/compiler-latest.zip" - exit 1 -fi - -echo Using $COMPILER as the compiler. -rm local_blockly_compressed_vertical.js 2> /dev/null -echo Compiling Scratch-Blocks.. -java -jar $COMPILER \ - --js='core/**.js' \ - --js='!core/block_render_svg_horizontal.js' \ - --js='../closure-library/closure/goog/**.js' \ - --js='../closure-library/third_party/closure/goog/**.js' \ - --generate_exports \ - --warning_level='DEFAULT' \ - --compilation_level SIMPLE_OPTIMIZATIONS \ - --dependency_mode=STRICT \ - --entry_point=Blockly \ - --js_output_file local_blockly_compressed_vertical.js - -if [ -s local_blockly_compressed_vertical.js ]; then - echo Compilation OK. -else - echo Compilation FAIL. - exit 1 -fi diff --git a/media/delete-icon.svg b/media/delete-icon.svg new file mode 100644 index 0000000000..ed1f71edff --- /dev/null +++ b/media/delete-icon.svg @@ -0,0 +1,10 @@ + + + + delete-x + Created with Sketch. + + + + + diff --git a/media/comment-arrow-down.svg b/media/foldout-icon.svg similarity index 100% rename from media/comment-arrow-down.svg rename to media/foldout-icon.svg diff --git a/media/resize-handle.svg b/media/resize-handle.svg new file mode 100644 index 0000000000..b7002710e5 --- /dev/null +++ b/media/resize-handle.svg @@ -0,0 +1,3 @@ + + + diff --git a/msg/scratch_msgs.js b/msg/scratch_msgs.js index 0a520a4691..794f9ac537 100644 --- a/msg/scratch_msgs.js +++ b/msg/scratch_msgs.js @@ -1,13 +1,33 @@ -// This file was automatically generated. Do not modify. +import * as Blockly from 'blockly/core'; -'use strict'; +export class ScratchMsgs { + static currentLocale_ = 'en'; + static locales = {}; -goog.provide('Blockly.ScratchMsgs.allLocales'); + static setLocale(locale) { + if (Object.keys(this.locales).includes(locale)) { + this.currentLocale_ = locale; + Object.assign(Blockly.Msg, this.locales[locale]); + } else { + // keep current locale + console.warn('Ignoring unrecognized locale: ' + locale); + } + } -goog.require('Blockly.ScratchMsgs'); + static translate(msgId, defaultMsg, useLocale) { + var locale = useLocale || this.currentLocale_; + if (Object.keys(this.locales).includes(locale)) { + var messages = this.locales[locale]; + if (Object.keys(messages).includes(msgId)) { + return messages[msgId]; + } + } + return defaultMsg; + } +} -Blockly.ScratchMsgs.locales["ab"] = +ScratchMsgs.locales["ab"] = { "CONTROL_FOREVER": "инагӡалатәуп еснагь", "CONTROL_REPEAT": "инагӡалатәуп %1 - нтә", @@ -246,7 +266,7 @@ Blockly.ScratchMsgs.locales["ab"] = "CATEGORY_OPERATORS": "Аоператорқәа", "CATEGORY_VARIABLES": "Аҽеиҭакқәа", "CATEGORY_MYBLOCKS": "Сара сблокқәа", - "DUPLICATE": "Адубликат", + "DUPLICATE_BLOCK": "Адубликат", "DELETE": "Ианыхтәуп", "ADD_COMMENT": "Иацҵатәуп акомментари", "REMOVE_COMMENT": "Ианыхтәуп акомментари", @@ -294,7 +314,7 @@ Blockly.ScratchMsgs.locales["ab"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "ацҳамҭа1" }; -Blockly.ScratchMsgs.locales["af"] = +ScratchMsgs.locales["af"] = { "CONTROL_FOREVER": "vir ewig", "CONTROL_REPEAT": "herhaal %1 keer", @@ -533,7 +553,7 @@ Blockly.ScratchMsgs.locales["af"] = "CATEGORY_OPERATORS": "Operateurs", "CATEGORY_VARIABLES": "Veranderlikes", "CATEGORY_MYBLOCKS": "My Blokke", - "DUPLICATE": "Dupliseer", + "DUPLICATE_BLOCK": "Dupliseer", "DELETE": "Skrap", "ADD_COMMENT": "Voeg Kommentaar By", "REMOVE_COMMENT": "Verwyder Kommentaar", @@ -581,7 +601,7 @@ Blockly.ScratchMsgs.locales["af"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "boodskap1" }; -Blockly.ScratchMsgs.locales["ar"] = +ScratchMsgs.locales["ar"] = { "CONTROL_FOREVER": "كرِّر باستمرار", "CONTROL_REPEAT": "كرِّر %1 مرة", @@ -820,7 +840,7 @@ Blockly.ScratchMsgs.locales["ar"] = "CATEGORY_OPERATORS": "العمليات", "CATEGORY_VARIABLES": "المتغيرات", "CATEGORY_MYBLOCKS": "لبناتي", - "DUPLICATE": "مضاعفة", + "DUPLICATE_BLOCK": "مضاعفة", "DELETE": "حذف", "ADD_COMMENT": "إضافة تعليق", "REMOVE_COMMENT": "حذف التعليق", @@ -868,7 +888,7 @@ Blockly.ScratchMsgs.locales["ar"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "الرسالة 1" }; -Blockly.ScratchMsgs.locales["am"] = +ScratchMsgs.locales["am"] = { "CONTROL_FOREVER": "ለዘላለም", "CONTROL_REPEAT": "%1ን ድገም", @@ -1107,7 +1127,7 @@ Blockly.ScratchMsgs.locales["am"] = "CATEGORY_OPERATORS": "ስሌቶች", "CATEGORY_VARIABLES": "ተለዋዋጮች", "CATEGORY_MYBLOCKS": "የኔ ጥምሮች", - "DUPLICATE": "ቅጂ አድርገህ ገልብጥ", + "DUPLICATE_BLOCK": "ቅጂ አድርገህ ገልብጥ", "DELETE": "አጥፋ", "ADD_COMMENT": "አስተያየት ጨምር", "REMOVE_COMMENT": "አስተያየት አውጣ", @@ -1155,7 +1175,7 @@ Blockly.ScratchMsgs.locales["am"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "መልእክት1" }; -Blockly.ScratchMsgs.locales["an"] = +ScratchMsgs.locales["an"] = { "CONTROL_FOREVER": "pa cutio", "CONTROL_REPEAT": "repetir %1", @@ -1394,7 +1414,7 @@ Blockly.ScratchMsgs.locales["an"] = "CATEGORY_OPERATORS": "Operadors", "CATEGORY_VARIABLES": "Variables", "CATEGORY_MYBLOCKS": "Los míos bloques", - "DUPLICATE": "Duplicar", + "DUPLICATE_BLOCK": "Duplicar", "DELETE": "Borrar", "ADD_COMMENT": "Anyadir comentario", "REMOVE_COMMENT": "Eliminar comentario", @@ -1442,7 +1462,7 @@ Blockly.ScratchMsgs.locales["an"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "mensache1" }; -Blockly.ScratchMsgs.locales["ast"] = +ScratchMsgs.locales["ast"] = { "CONTROL_FOREVER": "pa siempres", "CONTROL_REPEAT": "repetir %1", @@ -1681,7 +1701,7 @@ Blockly.ScratchMsgs.locales["ast"] = "CATEGORY_OPERATORS": "Operadores", "CATEGORY_VARIABLES": "Variables", "CATEGORY_MYBLOCKS": "Los Mios Bloques", - "DUPLICATE": "Duplicar", + "DUPLICATE_BLOCK": "Duplicar", "DELETE": "Esborrar", "ADD_COMMENT": "Amestar comentariu", "REMOVE_COMMENT": "Esborrar Comentariu", @@ -1729,7 +1749,7 @@ Blockly.ScratchMsgs.locales["ast"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "mensaxe1" }; -Blockly.ScratchMsgs.locales["az"] = +ScratchMsgs.locales["az"] = { "CONTROL_FOREVER": "həmişə", "CONTROL_REPEAT": "təkrarla %1 dəfə", @@ -1968,7 +1988,7 @@ Blockly.ScratchMsgs.locales["az"] = "CATEGORY_OPERATORS": "Operatorlar", "CATEGORY_VARIABLES": "Dəyişənlər", "CATEGORY_MYBLOCKS": "Mənim Bloklarım", - "DUPLICATE": "Dublikatın yarat", + "DUPLICATE_BLOCK": "Dublikatın yarat", "DELETE": "Sil", "ADD_COMMENT": "Şərh əlavə et", "REMOVE_COMMENT": "Şərhi sil", @@ -2016,7 +2036,7 @@ Blockly.ScratchMsgs.locales["az"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "ismarıc 1" }; -Blockly.ScratchMsgs.locales["id"] = +ScratchMsgs.locales["id"] = { "CONTROL_FOREVER": "selamanya", "CONTROL_REPEAT": "ulangi %1 kali", @@ -2255,7 +2275,7 @@ Blockly.ScratchMsgs.locales["id"] = "CATEGORY_OPERATORS": "Operator", "CATEGORY_VARIABLES": "Variabel", "CATEGORY_MYBLOCKS": "Balok Saya", - "DUPLICATE": "Gandakan", + "DUPLICATE_BLOCK": "Gandakan", "DELETE": "Hapus", "ADD_COMMENT": "Tambahkan Komentar", "REMOVE_COMMENT": "Hapus Komentar", @@ -2303,7 +2323,7 @@ Blockly.ScratchMsgs.locales["id"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "pesan1" }; -Blockly.ScratchMsgs.locales["bn"] = +ScratchMsgs.locales["bn"] = { "CONTROL_FOREVER": "চিরকালের জন্য", "CONTROL_REPEAT": "পুনরাবৃত্তি %1 বার", @@ -2542,7 +2562,7 @@ Blockly.ScratchMsgs.locales["bn"] = "CATEGORY_OPERATORS": "অপারেটর", "CATEGORY_VARIABLES": "ভ্যারিয়েবল", "CATEGORY_MYBLOCKS": "আমার ব্লকগুলো", - "DUPLICATE": "অনুরূপ", + "DUPLICATE_BLOCK": "অনুরূপ", "DELETE": "অপসারণ", "ADD_COMMENT": "মন্তব্য যোগ কর", "REMOVE_COMMENT": "মন্তব্য অপসারণ কর", @@ -2590,7 +2610,7 @@ Blockly.ScratchMsgs.locales["bn"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "বার্তা1" }; -Blockly.ScratchMsgs.locales["be"] = +ScratchMsgs.locales["be"] = { "CONTROL_FOREVER": "заўжды", "CONTROL_REPEAT": "паўтарыць %1", @@ -2829,7 +2849,7 @@ Blockly.ScratchMsgs.locales["be"] = "CATEGORY_OPERATORS": "Аператары", "CATEGORY_VARIABLES": "Зменныя", "CATEGORY_MYBLOCKS": "Уласныя блокі", - "DUPLICATE": "Падвоіць", + "DUPLICATE_BLOCK": "Падвоіць", "DELETE": "Выдаліць", "ADD_COMMENT": "Дадаць каментарый", "REMOVE_COMMENT": "Выдаліць каментарый", @@ -2877,7 +2897,7 @@ Blockly.ScratchMsgs.locales["be"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "паведамленне1" }; -Blockly.ScratchMsgs.locales["bg"] = +ScratchMsgs.locales["bg"] = { "CONTROL_FOREVER": "винаги", "CONTROL_REPEAT": "повтори %1", @@ -3116,7 +3136,7 @@ Blockly.ScratchMsgs.locales["bg"] = "CATEGORY_OPERATORS": "Оператори", "CATEGORY_VARIABLES": "Променливи", "CATEGORY_MYBLOCKS": "Моите Блокове", - "DUPLICATE": "Дублиране", + "DUPLICATE_BLOCK": "Дублиране", "DELETE": "Изтриване", "ADD_COMMENT": "Добави Коментар", "REMOVE_COMMENT": "Премахни Коментар", @@ -3164,7 +3184,7 @@ Blockly.ScratchMsgs.locales["bg"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "съобщение1" }; -Blockly.ScratchMsgs.locales["ca"] = +ScratchMsgs.locales["ca"] = { "CONTROL_FOREVER": "per sempre", "CONTROL_REPEAT": "repeteix %1", @@ -3403,7 +3423,7 @@ Blockly.ScratchMsgs.locales["ca"] = "CATEGORY_OPERATORS": "Operadors", "CATEGORY_VARIABLES": "Variables", "CATEGORY_MYBLOCKS": "Els meus blocs", - "DUPLICATE": "Duplica", + "DUPLICATE_BLOCK": "Duplica", "DELETE": "Elimina", "ADD_COMMENT": "Afegeix un comentari", "REMOVE_COMMENT": "Elimina el comentari", @@ -3451,7 +3471,7 @@ Blockly.ScratchMsgs.locales["ca"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "missatge1" }; -Blockly.ScratchMsgs.locales["cs"] = +ScratchMsgs.locales["cs"] = { "CONTROL_FOREVER": "opakuj stále", "CONTROL_REPEAT": "opakuj %1 krát", @@ -3690,7 +3710,7 @@ Blockly.ScratchMsgs.locales["cs"] = "CATEGORY_OPERATORS": "Operátory", "CATEGORY_VARIABLES": "Proměnné", "CATEGORY_MYBLOCKS": "Moje bloky", - "DUPLICATE": "Kopírovat", + "DUPLICATE_BLOCK": "Kopírovat", "DELETE": "Odstranit", "ADD_COMMENT": "Přidat poznámku", "REMOVE_COMMENT": "Odstranit poznámku", @@ -3738,7 +3758,7 @@ Blockly.ScratchMsgs.locales["cs"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "zpráva1" }; -Blockly.ScratchMsgs.locales["cy"] = +ScratchMsgs.locales["cy"] = { "CONTROL_FOREVER": "am byth", "CONTROL_REPEAT": "ailadrodd %1", @@ -3977,7 +3997,7 @@ Blockly.ScratchMsgs.locales["cy"] = "CATEGORY_OPERATORS": "Gweithredwyr", "CATEGORY_VARIABLES": "Newidynnau", "CATEGORY_MYBLOCKS": "Fy Mlociau", - "DUPLICATE": "Dyblygu", + "DUPLICATE_BLOCK": "Dyblygu", "DELETE": "Dileu", "ADD_COMMENT": "Ychwanegu Sylw", "REMOVE_COMMENT": "Tynnu Sylw", @@ -4025,7 +4045,7 @@ Blockly.ScratchMsgs.locales["cy"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "neges1" }; -Blockly.ScratchMsgs.locales["da"] = +ScratchMsgs.locales["da"] = { "CONTROL_FOREVER": "for evigt", "CONTROL_REPEAT": "gentag %1 gange", @@ -4264,7 +4284,7 @@ Blockly.ScratchMsgs.locales["da"] = "CATEGORY_OPERATORS": "Operatorer", "CATEGORY_VARIABLES": "Variabler", "CATEGORY_MYBLOCKS": "Mine brikker", - "DUPLICATE": "Kopiér", + "DUPLICATE_BLOCK": "Kopiér", "DELETE": "Slet", "ADD_COMMENT": "Tilføj kommentar", "REMOVE_COMMENT": "Slet kommentar", @@ -4312,7 +4332,7 @@ Blockly.ScratchMsgs.locales["da"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "besked1" }; -Blockly.ScratchMsgs.locales["de"] = +ScratchMsgs.locales["de"] = { "CONTROL_FOREVER": "wiederhole fortlaufend", "CONTROL_REPEAT": "wiederhole %1 mal", @@ -4551,7 +4571,7 @@ Blockly.ScratchMsgs.locales["de"] = "CATEGORY_OPERATORS": "Operatoren", "CATEGORY_VARIABLES": "Variablen", "CATEGORY_MYBLOCKS": "Meine Blöcke", - "DUPLICATE": "Duplizieren", + "DUPLICATE_BLOCK": "Duplizieren", "DELETE": "Löschen", "ADD_COMMENT": "Kommentar hinzufügen", "REMOVE_COMMENT": "Kommentar entfernen", @@ -4599,7 +4619,7 @@ Blockly.ScratchMsgs.locales["de"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "Nachricht1" }; -Blockly.ScratchMsgs.locales["et"] = +ScratchMsgs.locales["et"] = { "CONTROL_FOREVER": "korda lõputult", "CONTROL_REPEAT": "korda %1 korda", @@ -4838,7 +4858,7 @@ Blockly.ScratchMsgs.locales["et"] = "CATEGORY_OPERATORS": "Tehted", "CATEGORY_VARIABLES": "Muutujad", "CATEGORY_MYBLOCKS": "Minu Plokid", - "DUPLICATE": "Paljunda", + "DUPLICATE_BLOCK": "Paljunda", "DELETE": "Kustuta", "ADD_COMMENT": "Lisa kommentaar", "REMOVE_COMMENT": "Eemalda kommentaar", @@ -4886,7 +4906,7 @@ Blockly.ScratchMsgs.locales["et"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "teade1" }; -Blockly.ScratchMsgs.locales["el"] = +ScratchMsgs.locales["el"] = { "CONTROL_FOREVER": "για πάντα", "CONTROL_REPEAT": "επανάλαβε %1", @@ -5125,7 +5145,7 @@ Blockly.ScratchMsgs.locales["el"] = "CATEGORY_OPERATORS": "Τελεστές", "CATEGORY_VARIABLES": "Μεταβλητές", "CATEGORY_MYBLOCKS": "Οι Εντολές μου", - "DUPLICATE": "Διπλασιασμός", + "DUPLICATE_BLOCK": "Διπλασιασμός", "DELETE": "Διαγραφή", "ADD_COMMENT": "Προσθήκη σχολίου", "REMOVE_COMMENT": "Αφαίρεση σχολίου", @@ -5173,7 +5193,7 @@ Blockly.ScratchMsgs.locales["el"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "μήνυμα1" }; -Blockly.ScratchMsgs.locales["en"] = +ScratchMsgs.locales["en"] = { "CONTROL_FOREVER": "forever", "CONTROL_REPEAT": "repeat %1", @@ -5412,7 +5432,7 @@ Blockly.ScratchMsgs.locales["en"] = "CATEGORY_OPERATORS": "Operators", "CATEGORY_VARIABLES": "Variables", "CATEGORY_MYBLOCKS": "My Blocks", - "DUPLICATE": "Duplicate", + "DUPLICATE_BLOCK": "Duplicate", "DELETE": "Delete", "ADD_COMMENT": "Add Comment", "REMOVE_COMMENT": "Remove Comment", @@ -5460,7 +5480,7 @@ Blockly.ScratchMsgs.locales["en"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "message1" }; -Blockly.ScratchMsgs.locales["es"] = +ScratchMsgs.locales["es"] = { "CONTROL_FOREVER": "por siempre", "CONTROL_REPEAT": "repetir %1", @@ -5699,7 +5719,7 @@ Blockly.ScratchMsgs.locales["es"] = "CATEGORY_OPERATORS": "Operadores", "CATEGORY_VARIABLES": "Variables", "CATEGORY_MYBLOCKS": "Mis bloques", - "DUPLICATE": "Duplicar", + "DUPLICATE_BLOCK": "Duplicar", "DELETE": "Eliminar", "ADD_COMMENT": "Añadir comentario", "REMOVE_COMMENT": "Eliminar comentario", @@ -5747,7 +5767,7 @@ Blockly.ScratchMsgs.locales["es"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "mensaje1" }; -Blockly.ScratchMsgs.locales["es-419"] = +ScratchMsgs.locales["es-419"] = { "CONTROL_FOREVER": "por siempre", "CONTROL_REPEAT": "repetir %1", @@ -5986,7 +6006,7 @@ Blockly.ScratchMsgs.locales["es-419"] = "CATEGORY_OPERATORS": "Operadores", "CATEGORY_VARIABLES": "Variables", "CATEGORY_MYBLOCKS": "Mis Bloques", - "DUPLICATE": "Duplicar", + "DUPLICATE_BLOCK": "Duplicar", "DELETE": "Eliminar", "ADD_COMMENT": "Agregar comentario", "REMOVE_COMMENT": "Eliminar comentario", @@ -6034,7 +6054,7 @@ Blockly.ScratchMsgs.locales["es-419"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "mensaje1" }; -Blockly.ScratchMsgs.locales["eo"] = +ScratchMsgs.locales["eo"] = { "CONTROL_FOREVER": "ripeti senfine", "CONTROL_REPEAT": "ripeti %1-foje", @@ -6273,7 +6293,7 @@ Blockly.ScratchMsgs.locales["eo"] = "CATEGORY_OPERATORS": "Operatoroj", "CATEGORY_VARIABLES": "Variabloj", "CATEGORY_MYBLOCKS": "Miaj Blokoj", - "DUPLICATE": "Krei kopion", + "DUPLICATE_BLOCK": "Krei kopion", "DELETE": "Forigi", "ADD_COMMENT": "Aldoni komenton", "REMOVE_COMMENT": "Forigi komenton", @@ -6321,7 +6341,7 @@ Blockly.ScratchMsgs.locales["eo"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "mesaĝo1" }; -Blockly.ScratchMsgs.locales["eu"] = +ScratchMsgs.locales["eu"] = { "CONTROL_FOREVER": "etengabe", "CONTROL_REPEAT": "errepikatu %1 aldiz", @@ -6560,7 +6580,7 @@ Blockly.ScratchMsgs.locales["eu"] = "CATEGORY_OPERATORS": "Eragileak", "CATEGORY_VARIABLES": "Aldagaiak", "CATEGORY_MYBLOCKS": "Nire blokeak", - "DUPLICATE": "Bikoiztu", + "DUPLICATE_BLOCK": "Bikoiztu", "DELETE": "Ezabatu", "ADD_COMMENT": "Gehitu iruzkina", "REMOVE_COMMENT": "Kendu iruzkina", @@ -6608,7 +6628,7 @@ Blockly.ScratchMsgs.locales["eu"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "mezua1" }; -Blockly.ScratchMsgs.locales["fa"] = +ScratchMsgs.locales["fa"] = { "CONTROL_FOREVER": "برای همیشه", "CONTROL_REPEAT": "تکرار کن %1", @@ -6847,7 +6867,7 @@ Blockly.ScratchMsgs.locales["fa"] = "CATEGORY_OPERATORS": "عملگرها", "CATEGORY_VARIABLES": "متغیرها", "CATEGORY_MYBLOCKS": "قطعه‌های من", - "DUPLICATE": "تکثیر", + "DUPLICATE_BLOCK": "تکثیر", "DELETE": "حذف", "ADD_COMMENT": "افزودن یادداشت", "REMOVE_COMMENT": "حذف یادداشت", @@ -6895,7 +6915,7 @@ Blockly.ScratchMsgs.locales["fa"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "پیام 1" }; -Blockly.ScratchMsgs.locales["fil"] = +ScratchMsgs.locales["fil"] = { "CONTROL_FOREVER": "kailanman", "CONTROL_REPEAT": "ulitin nang %1", @@ -7134,7 +7154,7 @@ Blockly.ScratchMsgs.locales["fil"] = "CATEGORY_OPERATORS": "Mga Operator", "CATEGORY_VARIABLES": "Mga Variable", "CATEGORY_MYBLOCKS": "Mga Block Ko", - "DUPLICATE": "Doblehin", + "DUPLICATE_BLOCK": "Doblehin", "DELETE": "Burahin", "ADD_COMMENT": "Magkomento", "REMOVE_COMMENT": "Tanggalin ang Komento", @@ -7182,7 +7202,7 @@ Blockly.ScratchMsgs.locales["fil"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "mensahe1" }; -Blockly.ScratchMsgs.locales["fr"] = +ScratchMsgs.locales["fr"] = { "CONTROL_FOREVER": "répéter indéfiniment", "CONTROL_REPEAT": "répéter %1 fois", @@ -7421,7 +7441,7 @@ Blockly.ScratchMsgs.locales["fr"] = "CATEGORY_OPERATORS": "Opérateurs", "CATEGORY_VARIABLES": "Variables", "CATEGORY_MYBLOCKS": "Mes Blocs", - "DUPLICATE": "Dupliquer", + "DUPLICATE_BLOCK": "Dupliquer", "DELETE": "Supprimer", "ADD_COMMENT": "Ajouter un commentaire", "REMOVE_COMMENT": "Retirer le commentaire", @@ -7469,7 +7489,7 @@ Blockly.ScratchMsgs.locales["fr"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "message1" }; -Blockly.ScratchMsgs.locales["fy"] = +ScratchMsgs.locales["fy"] = { "CONTROL_FOREVER": "foar altyd", "CONTROL_REPEAT": "werhelje %1", @@ -7708,7 +7728,7 @@ Blockly.ScratchMsgs.locales["fy"] = "CATEGORY_OPERATORS": "Bestjoerders", "CATEGORY_VARIABLES": "Fariabelen", "CATEGORY_MYBLOCKS": "Myn Blokken", - "DUPLICATE": "Duplisearje", + "DUPLICATE_BLOCK": "Duplisearje", "DELETE": "Wiskje", "ADD_COMMENT": "Kommentaar tafoegje", "REMOVE_COMMENT": "Kommentaar fuortsmite", @@ -7756,7 +7776,7 @@ Blockly.ScratchMsgs.locales["fy"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "berjocht1" }; -Blockly.ScratchMsgs.locales["ga"] = +ScratchMsgs.locales["ga"] = { "CONTROL_FOREVER": "go deo", "CONTROL_REPEAT": "déan %1 uair", @@ -7995,7 +8015,7 @@ Blockly.ScratchMsgs.locales["ga"] = "CATEGORY_OPERATORS": "Oibreoirí", "CATEGORY_VARIABLES": "Athróga", "CATEGORY_MYBLOCKS": "Mo Chuid Blocanna", - "DUPLICATE": "Cóipeáil", + "DUPLICATE_BLOCK": "Cóipeáil", "DELETE": "Scrios", "ADD_COMMENT": "Cuir Nóta Tráchta Leis", "REMOVE_COMMENT": "Bain an Nóta Tráchta", @@ -8043,7 +8063,7 @@ Blockly.ScratchMsgs.locales["ga"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "teachtaireacht1" }; -Blockly.ScratchMsgs.locales["gd"] = +ScratchMsgs.locales["gd"] = { "CONTROL_FOREVER": "gu buan", "CONTROL_REPEAT": "dèan seo %1 turas", @@ -8282,7 +8302,7 @@ Blockly.ScratchMsgs.locales["gd"] = "CATEGORY_OPERATORS": "Gnìomharaiche", "CATEGORY_VARIABLES": "Caochladairean", "CATEGORY_MYBLOCKS": "Bloca agamsa", - "DUPLICATE": "Dùblaich", + "DUPLICATE_BLOCK": "Dùblaich", "DELETE": "Sguab às", "ADD_COMMENT": "Cuir beachd ris", "REMOVE_COMMENT": "Thoir am beachd air falbh", @@ -8330,7 +8350,7 @@ Blockly.ScratchMsgs.locales["gd"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "teachdaireachd1" }; -Blockly.ScratchMsgs.locales["gl"] = +ScratchMsgs.locales["gl"] = { "CONTROL_FOREVER": "para sempre", "CONTROL_REPEAT": "repetir %1", @@ -8569,7 +8589,7 @@ Blockly.ScratchMsgs.locales["gl"] = "CATEGORY_OPERATORS": "Operadores", "CATEGORY_VARIABLES": "Variábeis", "CATEGORY_MYBLOCKS": "Os meus bloques", - "DUPLICATE": "Duplicar", + "DUPLICATE_BLOCK": "Duplicar", "DELETE": "Eliminar", "ADD_COMMENT": "Engadir comentario", "REMOVE_COMMENT": "Retirar comentario", @@ -8617,7 +8637,7 @@ Blockly.ScratchMsgs.locales["gl"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "mensaxe1" }; -Blockly.ScratchMsgs.locales["ko"] = +ScratchMsgs.locales["ko"] = { "CONTROL_FOREVER": "무한 반복하기", "CONTROL_REPEAT": "%1 번 반복하기", @@ -8856,7 +8876,7 @@ Blockly.ScratchMsgs.locales["ko"] = "CATEGORY_OPERATORS": "연산", "CATEGORY_VARIABLES": "변수", "CATEGORY_MYBLOCKS": "내 블록", - "DUPLICATE": "복사하기", + "DUPLICATE_BLOCK": "복사하기", "DELETE": "삭제하기", "ADD_COMMENT": "주석 넣기", "REMOVE_COMMENT": "주석 지우기", @@ -8904,7 +8924,7 @@ Blockly.ScratchMsgs.locales["ko"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "메시지1" }; -Blockly.ScratchMsgs.locales["ha"] = +ScratchMsgs.locales["ha"] = { "CONTROL_FOREVER": "har abada ", "CONTROL_REPEAT": "maimaita %1", @@ -9143,7 +9163,7 @@ Blockly.ScratchMsgs.locales["ha"] = "CATEGORY_OPERATORS": "ma'alaƙanta", "CATEGORY_VARIABLES": "abubuwa masu canzawa", "CATEGORY_MYBLOCKS": "tubalai na", - "DUPLICATE": "kwafa", + "DUPLICATE_BLOCK": "kwafa", "DELETE": "goge", "ADD_COMMENT": "ƙara tsokaci ", "REMOVE_COMMENT": "cire tsokaci", @@ -9191,7 +9211,7 @@ Blockly.ScratchMsgs.locales["ha"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "saƙon1" }; -Blockly.ScratchMsgs.locales["hy"] = +ScratchMsgs.locales["hy"] = { "CONTROL_FOREVER": "անվերջ", "CONTROL_REPEAT": "կրկնել %1", @@ -9430,7 +9450,7 @@ Blockly.ScratchMsgs.locales["hy"] = "CATEGORY_OPERATORS": "Հաշվարկ", "CATEGORY_VARIABLES": "Փոփոխա­­կան", "CATEGORY_MYBLOCKS": "Մասնիկ", - "DUPLICATE": "Կրկնօրինակել", + "DUPLICATE_BLOCK": "Կրկնօրինակել", "DELETE": "Ջնջել", "ADD_COMMENT": "Ավելացնել մեկնաբանություն", "REMOVE_COMMENT": "Ջնջել մեկնաբանությունը", @@ -9478,7 +9498,7 @@ Blockly.ScratchMsgs.locales["hy"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "հաղորդագրություն1" }; -Blockly.ScratchMsgs.locales["he"] = +ScratchMsgs.locales["he"] = { "CONTROL_FOREVER": "לעולמים", "CONTROL_REPEAT": "חזור %1 פעמים", @@ -9717,7 +9737,7 @@ Blockly.ScratchMsgs.locales["he"] = "CATEGORY_OPERATORS": "מפעילים", "CATEGORY_VARIABLES": "משתנים", "CATEGORY_MYBLOCKS": "הלבנים שלי", - "DUPLICATE": "שכפל", + "DUPLICATE_BLOCK": "שכפל", "DELETE": "מחק", "ADD_COMMENT": "הוספת תגובה", "REMOVE_COMMENT": "מחק תגובה", @@ -9765,7 +9785,7 @@ Blockly.ScratchMsgs.locales["he"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "מסר 1" }; -Blockly.ScratchMsgs.locales["hi"] = +ScratchMsgs.locales["hi"] = { "CONTROL_FOREVER": "सदैव", "CONTROL_REPEAT": "%1 बार दोहराएं", @@ -10004,7 +10024,7 @@ Blockly.ScratchMsgs.locales["hi"] = "CATEGORY_OPERATORS": "ऑपरेटर्स", "CATEGORY_VARIABLES": "चर वस्तुएँ", "CATEGORY_MYBLOCKS": "मेरे खण्ड", - "DUPLICATE": "प्रतिरुप", + "DUPLICATE_BLOCK": "प्रतिरुप", "DELETE": "मिटाये", "ADD_COMMENT": "टिप्पणी दे", "REMOVE_COMMENT": "टिप्पणी मिटाये", @@ -10052,7 +10072,7 @@ Blockly.ScratchMsgs.locales["hi"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "संदेश 1 " }; -Blockly.ScratchMsgs.locales["hr"] = +ScratchMsgs.locales["hr"] = { "CONTROL_FOREVER": "ponavljaj", "CONTROL_REPEAT": "ponovi %1", @@ -10291,7 +10311,7 @@ Blockly.ScratchMsgs.locales["hr"] = "CATEGORY_OPERATORS": "Operacije", "CATEGORY_VARIABLES": "Varijable", "CATEGORY_MYBLOCKS": "Moji Blokovi", - "DUPLICATE": "Dupliciraj", + "DUPLICATE_BLOCK": "Dupliciraj", "DELETE": "Izbriši", "ADD_COMMENT": "Dodaj komentar", "REMOVE_COMMENT": "Ukloni komentar", @@ -10339,7 +10359,7 @@ Blockly.ScratchMsgs.locales["hr"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "poruka1" }; -Blockly.ScratchMsgs.locales["xh"] = +ScratchMsgs.locales["xh"] = { "CONTROL_FOREVER": "naphakade", "CONTROL_REPEAT": "phinda %1", @@ -10578,7 +10598,7 @@ Blockly.ScratchMsgs.locales["xh"] = "CATEGORY_OPERATORS": "ababhexeshi", "CATEGORY_VARIABLES": "iiveriyebhl", "CATEGORY_MYBLOCKS": "Ibhloko zam", - "DUPLICATE": "ukukhuphela", + "DUPLICATE_BLOCK": "ukukhuphela", "DELETE": "cima", "ADD_COMMENT": "faka uluvo", "REMOVE_COMMENT": "Susa uluvo", @@ -10626,7 +10646,7 @@ Blockly.ScratchMsgs.locales["xh"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "umyalezo1" }; -Blockly.ScratchMsgs.locales["zu"] = +ScratchMsgs.locales["zu"] = { "CONTROL_FOREVER": "phakade ", "CONTROL_REPEAT": "phinda %1 ", @@ -10865,7 +10885,7 @@ Blockly.ScratchMsgs.locales["zu"] = "CATEGORY_OPERATORS": "Abahambisayo", "CATEGORY_VARIABLES": "okuguqukayo", "CATEGORY_MYBLOCKS": "Amabhulokisi ami", - "DUPLICATE": "Fanisa", + "DUPLICATE_BLOCK": "Fanisa", "DELETE": "Cima", "ADD_COMMENT": "engeza ukuphawula", "REMOVE_COMMENT": "Susa ukuphawula", @@ -10913,7 +10933,7 @@ Blockly.ScratchMsgs.locales["zu"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "umyalezo wokuqala" }; -Blockly.ScratchMsgs.locales["is"] = +ScratchMsgs.locales["is"] = { "CONTROL_FOREVER": "endalaust", "CONTROL_REPEAT": "endurtaka %1 sinnum", @@ -11152,7 +11172,7 @@ Blockly.ScratchMsgs.locales["is"] = "CATEGORY_OPERATORS": "Virkjar", "CATEGORY_VARIABLES": "Breytur", "CATEGORY_MYBLOCKS": "Mínir kubbar", - "DUPLICATE": "Tvöfalda", + "DUPLICATE_BLOCK": "Tvöfalda", "DELETE": "Eyða", "ADD_COMMENT": "Bæta við athugasemd", "REMOVE_COMMENT": "Fjarlægja athugasemd", @@ -11200,7 +11220,7 @@ Blockly.ScratchMsgs.locales["is"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "dæmiUmNafnÁSkilaboðum" }; -Blockly.ScratchMsgs.locales["it"] = +ScratchMsgs.locales["it"] = { "CONTROL_FOREVER": "per sempre", "CONTROL_REPEAT": "ripeti %1 volte", @@ -11439,7 +11459,7 @@ Blockly.ScratchMsgs.locales["it"] = "CATEGORY_OPERATORS": "Operatori", "CATEGORY_VARIABLES": "Variabili", "CATEGORY_MYBLOCKS": "I Miei Blocchi", - "DUPLICATE": "Duplica", + "DUPLICATE_BLOCK": "Duplica", "DELETE": "Cancella", "ADD_COMMENT": "Aggiungi commento", "REMOVE_COMMENT": "Rimuovi commento", @@ -11487,7 +11507,7 @@ Blockly.ScratchMsgs.locales["it"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "messaggio1" }; -Blockly.ScratchMsgs.locales["ka"] = +ScratchMsgs.locales["ka"] = { "CONTROL_FOREVER": "მუდმივად", "CONTROL_REPEAT": "გაიმეორე %1ჯერ", @@ -11726,7 +11746,7 @@ Blockly.ScratchMsgs.locales["ka"] = "CATEGORY_OPERATORS": "ოპერატორები", "CATEGORY_VARIABLES": "ცვლადები", "CATEGORY_MYBLOCKS": "ჩემი ბლოკები", - "DUPLICATE": "დუბლირება", + "DUPLICATE_BLOCK": "დუბლირება", "DELETE": "წაშლა", "ADD_COMMENT": "დაამატე კომენტარი", "REMOVE_COMMENT": "წაშალე კომენტარი", @@ -11774,7 +11794,7 @@ Blockly.ScratchMsgs.locales["ka"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "შეტყობინება1" }; -Blockly.ScratchMsgs.locales["kk"] = +ScratchMsgs.locales["kk"] = { "CONTROL_FOREVER": "әрқашан", "CONTROL_REPEAT": "%1 рет қайталау", @@ -12013,7 +12033,7 @@ Blockly.ScratchMsgs.locales["kk"] = "CATEGORY_OPERATORS": "Операторлар", "CATEGORY_VARIABLES": "айнымалылар", "CATEGORY_MYBLOCKS": "Менің блоктарым", - "DUPLICATE": "Көшірмесін жасау", + "DUPLICATE_BLOCK": "Көшірмесін жасау", "DELETE": "Жою", "ADD_COMMENT": "Комментарий жазу", "REMOVE_COMMENT": "Комментарийді өшіру", @@ -12061,7 +12081,7 @@ Blockly.ScratchMsgs.locales["kk"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "хабарлама1" }; -Blockly.ScratchMsgs.locales["qu"] = +ScratchMsgs.locales["qu"] = { "CONTROL_FOREVER": "wiñaypaq", "CONTROL_REPEAT": "musuqmanta %1", @@ -12300,7 +12320,7 @@ Blockly.ScratchMsgs.locales["qu"] = "CATEGORY_OPERATORS": "Llamkaq", "CATEGORY_VARIABLES": "hukniraq", "CATEGORY_MYBLOCKS": "champaykuna", - "DUPLICATE": "iskachay", + "DUPLICATE_BLOCK": "iskachay", "DELETE": "Pichay", "ADD_COMMENT": "yapay parlarisqaykita", "REMOVE_COMMENT": "parlasqaykita kitay", @@ -12348,7 +12368,7 @@ Blockly.ScratchMsgs.locales["qu"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "qillqa1" }; -Blockly.ScratchMsgs.locales["sw"] = +ScratchMsgs.locales["sw"] = { "CONTROL_FOREVER": "milele", "CONTROL_REPEAT": "rudia %1", @@ -12587,7 +12607,7 @@ Blockly.ScratchMsgs.locales["sw"] = "CATEGORY_OPERATORS": "Opereta", "CATEGORY_VARIABLES": "Vibadilika", "CATEGORY_MYBLOCKS": "Bloku Zangu", - "DUPLICATE": "Toa Nakala Nyingine", + "DUPLICATE_BLOCK": "Toa Nakala Nyingine", "DELETE": "Futa", "ADD_COMMENT": "Ongeza Maoni", "REMOVE_COMMENT": "Futa Maoni", @@ -12635,7 +12655,7 @@ Blockly.ScratchMsgs.locales["sw"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "ujumbe1" }; -Blockly.ScratchMsgs.locales["ht"] = +ScratchMsgs.locales["ht"] = { "CONTROL_FOREVER": "pou toujou", "CONTROL_REPEAT": "repete %1", @@ -12874,7 +12894,7 @@ Blockly.ScratchMsgs.locales["ht"] = "CATEGORY_OPERATORS": "Operatè", "CATEGORY_VARIABLES": "Varyab ", "CATEGORY_MYBLOCKS": "Blòk mwen yo", - "DUPLICATE": "Fè marasa", + "DUPLICATE_BLOCK": "Fè marasa", "DELETE": "Efase", "ADD_COMMENT": "Ajoute remak", "REMOVE_COMMENT": "Retire remak", @@ -12922,7 +12942,7 @@ Blockly.ScratchMsgs.locales["ht"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "mesaj1" }; -Blockly.ScratchMsgs.locales["ku"] = +ScratchMsgs.locales["ku"] = { "CONTROL_FOREVER": "berdewamî", "CONTROL_REPEAT": "%1 caran dubare bike", @@ -13161,7 +13181,7 @@ Blockly.ScratchMsgs.locales["ku"] = "CATEGORY_OPERATORS": "Operator", "CATEGORY_VARIABLES": "Guherok", "CATEGORY_MYBLOCKS": "Blokên Min", - "DUPLICATE": "Dubare", + "DUPLICATE_BLOCK": "Dubare", "DELETE": "Jê bibe", "ADD_COMMENT": "Şîrove tevlî bike", "REMOVE_COMMENT": "Şîroveyê Rake", @@ -13209,7 +13229,7 @@ Blockly.ScratchMsgs.locales["ku"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "peyam1" }; -Blockly.ScratchMsgs.locales["ckb"] = +ScratchMsgs.locales["ckb"] = { "CONTROL_FOREVER": "بۆهەتایە", "CONTROL_REPEAT": "دوبارەکردنەوە %1", @@ -13448,7 +13468,7 @@ Blockly.ScratchMsgs.locales["ckb"] = "CATEGORY_OPERATORS": "كرده‌هێماکان", "CATEGORY_VARIABLES": "گۆڕاوەکان", "CATEGORY_MYBLOCKS": "بلۆکەکانم", - "DUPLICATE": "هاوشێوەکردنەوە", + "DUPLICATE_BLOCK": "هاوشێوەکردنەوە", "DELETE": "سڕینەوە", "ADD_COMMENT": "زیادکردنی لێدوان", "REMOVE_COMMENT": "لابردنی لێدوان", @@ -13496,7 +13516,7 @@ Blockly.ScratchMsgs.locales["ckb"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "نامەی1" }; -Blockly.ScratchMsgs.locales["lv"] = +ScratchMsgs.locales["lv"] = { "CONTROL_FOREVER": "nepārtraukti", "CONTROL_REPEAT": "atkārtot %1", @@ -13735,7 +13755,7 @@ Blockly.ScratchMsgs.locales["lv"] = "CATEGORY_OPERATORS": "Operatori", "CATEGORY_VARIABLES": "Mainīgie", "CATEGORY_MYBLOCKS": "Mani bloki", - "DUPLICATE": "Dublēt", + "DUPLICATE_BLOCK": "Dublēt", "DELETE": "Dzēst", "ADD_COMMENT": "Pievienot komentāru", "REMOVE_COMMENT": "Noņemt komentāru", @@ -13783,7 +13803,7 @@ Blockly.ScratchMsgs.locales["lv"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "ziņa1" }; -Blockly.ScratchMsgs.locales["lt"] = +ScratchMsgs.locales["lt"] = { "CONTROL_FOREVER": "kartok be galo", "CONTROL_REPEAT": "kartok %1", @@ -14022,7 +14042,7 @@ Blockly.ScratchMsgs.locales["lt"] = "CATEGORY_OPERATORS": "Matematika", "CATEGORY_VARIABLES": "Kintamieji", "CATEGORY_MYBLOCKS": "Mano Komandos", - "DUPLICATE": "Kurti kopiją", + "DUPLICATE_BLOCK": "Kurti kopiją", "DELETE": "Ištrinti", "ADD_COMMENT": "Pridėti komentarą", "REMOVE_COMMENT": "Pašalinti komentarą", @@ -14070,7 +14090,7 @@ Blockly.ScratchMsgs.locales["lt"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "žinutė1" }; -Blockly.ScratchMsgs.locales["hu"] = +ScratchMsgs.locales["hu"] = { "CONTROL_FOREVER": "mindig", "CONTROL_REPEAT": "ismételd %1", @@ -14309,7 +14329,7 @@ Blockly.ScratchMsgs.locales["hu"] = "CATEGORY_OPERATORS": "Műveletek", "CATEGORY_VARIABLES": "Változók", "CATEGORY_MYBLOCKS": "Blokkjaim", - "DUPLICATE": "Duplikálás", + "DUPLICATE_BLOCK": "Duplikálás", "DELETE": "Törlés", "ADD_COMMENT": "Megjegyzés", "REMOVE_COMMENT": "Megjegyzés eltávolítása", @@ -14357,7 +14377,7 @@ Blockly.ScratchMsgs.locales["hu"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "üzenet1" }; -Blockly.ScratchMsgs.locales["mi"] = +ScratchMsgs.locales["mi"] = { "CONTROL_FOREVER": "mō ake, ake mahia", "CONTROL_REPEAT": "tōaitia %1", @@ -14596,7 +14616,7 @@ Blockly.ScratchMsgs.locales["mi"] = "CATEGORY_OPERATORS": "Tohutūmahi", "CATEGORY_VARIABLES": "Ngā Taurangi", "CATEGORY_MYBLOCKS": "Aku Paraka", - "DUPLICATE": "Tāruatia", + "DUPLICATE_BLOCK": "Tāruatia", "DELETE": "Mukua", "ADD_COMMENT": "Tāpiri Tākupu", "REMOVE_COMMENT": "Mukua te Tākupu", @@ -14644,7 +14664,7 @@ Blockly.ScratchMsgs.locales["mi"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "karere1" }; -Blockly.ScratchMsgs.locales["mn"] = +ScratchMsgs.locales["mn"] = { "CONTROL_FOREVER": "Үргэлж", "CONTROL_REPEAT": "%1 удаа давтах", @@ -14883,7 +14903,7 @@ Blockly.ScratchMsgs.locales["mn"] = "CATEGORY_OPERATORS": "Тоолохуй", "CATEGORY_VARIABLES": "Хувьсагч", "CATEGORY_MYBLOCKS": "Миний блокууд", - "DUPLICATE": "Хувилах", + "DUPLICATE_BLOCK": "Хувилах", "DELETE": "Устгах", "ADD_COMMENT": "Тайлбар нэмэх", "REMOVE_COMMENT": "Тайлбар устгах", @@ -14931,7 +14951,7 @@ Blockly.ScratchMsgs.locales["mn"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "мэссэж1" }; -Blockly.ScratchMsgs.locales["nl"] = +ScratchMsgs.locales["nl"] = { "CONTROL_FOREVER": "herhaal", "CONTROL_REPEAT": "herhaal %1", @@ -15170,7 +15190,7 @@ Blockly.ScratchMsgs.locales["nl"] = "CATEGORY_OPERATORS": "Functies", "CATEGORY_VARIABLES": "Variabelen", "CATEGORY_MYBLOCKS": "Mijn blokken", - "DUPLICATE": "Kopie maken", + "DUPLICATE_BLOCK": "Kopie maken", "DELETE": "Verwijderen", "ADD_COMMENT": "Commentaar toevoegen", "REMOVE_COMMENT": "Commentaar verwijderen", @@ -15218,7 +15238,7 @@ Blockly.ScratchMsgs.locales["nl"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "bericht1" }; -Blockly.ScratchMsgs.locales["ja"] = +ScratchMsgs.locales["ja"] = { "CONTROL_FOREVER": "ずっと", "CONTROL_REPEAT": "%1 回繰り返す", @@ -15457,7 +15477,7 @@ Blockly.ScratchMsgs.locales["ja"] = "CATEGORY_OPERATORS": "演算", "CATEGORY_VARIABLES": "変数", "CATEGORY_MYBLOCKS": "ブロック定義", - "DUPLICATE": "複製", + "DUPLICATE_BLOCK": "複製", "DELETE": "削除", "ADD_COMMENT": "コメントを追加", "REMOVE_COMMENT": "コメントを削除", @@ -15505,7 +15525,7 @@ Blockly.ScratchMsgs.locales["ja"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "メッセージ1" }; -Blockly.ScratchMsgs.locales["ja-Hira"] = +ScratchMsgs.locales["ja-Hira"] = { "CONTROL_FOREVER": "ずっと", "CONTROL_REPEAT": "%1 かいくりかえす", @@ -15744,7 +15764,7 @@ Blockly.ScratchMsgs.locales["ja-Hira"] = "CATEGORY_OPERATORS": "えんざん", "CATEGORY_VARIABLES": "へんすう", "CATEGORY_MYBLOCKS": "ブロックていぎ", - "DUPLICATE": "ふくせい", + "DUPLICATE_BLOCK": "ふくせい", "DELETE": "さくじょ", "ADD_COMMENT": "コメントをついか", "REMOVE_COMMENT": "コメントをさくじょ", @@ -15792,7 +15812,7 @@ Blockly.ScratchMsgs.locales["ja-Hira"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "メッセージ1" }; -Blockly.ScratchMsgs.locales["nb"] = +ScratchMsgs.locales["nb"] = { "CONTROL_FOREVER": "gjenta for alltid", "CONTROL_REPEAT": "gjenta %1 ganger", @@ -16031,7 +16051,7 @@ Blockly.ScratchMsgs.locales["nb"] = "CATEGORY_OPERATORS": "Operatorer", "CATEGORY_VARIABLES": "Variabler", "CATEGORY_MYBLOCKS": "Mine klosser", - "DUPLICATE": "Lag en kopi", + "DUPLICATE_BLOCK": "Lag en kopi", "DELETE": "Slett", "ADD_COMMENT": "Skriv en kommentar", "REMOVE_COMMENT": "Fjern kommentar", @@ -16079,7 +16099,7 @@ Blockly.ScratchMsgs.locales["nb"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "melding1" }; -Blockly.ScratchMsgs.locales["nn"] = +ScratchMsgs.locales["nn"] = { "CONTROL_FOREVER": "for alltid", "CONTROL_REPEAT": "gjenta %1 gongar", @@ -16318,7 +16338,7 @@ Blockly.ScratchMsgs.locales["nn"] = "CATEGORY_OPERATORS": "Operatorar", "CATEGORY_VARIABLES": "Variablar", "CATEGORY_MYBLOCKS": "Mine klossar", - "DUPLICATE": "Lag ein kopi", + "DUPLICATE_BLOCK": "Lag ein kopi", "DELETE": "Slett", "ADD_COMMENT": "Skriv kommentar", "REMOVE_COMMENT": "Fjern kommentar", @@ -16366,7 +16386,7 @@ Blockly.ScratchMsgs.locales["nn"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "melding1" }; -Blockly.ScratchMsgs.locales["oc"] = +ScratchMsgs.locales["oc"] = { "CONTROL_FOREVER": "per totjorn", "CONTROL_REPEAT": "repetir %1", @@ -16605,7 +16625,7 @@ Blockly.ScratchMsgs.locales["oc"] = "CATEGORY_OPERATORS": "Operators", "CATEGORY_VARIABLES": "Variablas", "CATEGORY_MYBLOCKS": "Mos Blòcs", - "DUPLICATE": "Desdoblar", + "DUPLICATE_BLOCK": "Desdoblar", "DELETE": "Suprimir", "ADD_COMMENT": "Apondre Comentari", "REMOVE_COMMENT": "Suprimir Comentari", @@ -16653,7 +16673,7 @@ Blockly.ScratchMsgs.locales["oc"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "messatge1" }; -Blockly.ScratchMsgs.locales["or"] = +ScratchMsgs.locales["or"] = { "CONTROL_FOREVER": "ସବୁ ଦିନ ପାଇଁ ", "CONTROL_REPEAT": "%1 ପୁନରାବୃତ୍ତି କର", @@ -16892,7 +16912,7 @@ Blockly.ScratchMsgs.locales["or"] = "CATEGORY_OPERATORS": "ଅପରେଟର ଗୁଡିକ", "CATEGORY_VARIABLES": "ଭେରିଏବଲ୍ ଗୁଡିକ", "CATEGORY_MYBLOCKS": "ମୋ ବ୍ଲକ ଗୁଡି଼କ", - "DUPLICATE": "ପ୍ରତିରୂପ", + "DUPLICATE_BLOCK": "ପ୍ରତିରୂପ", "DELETE": "ଲିଭାଅ", "ADD_COMMENT": "ଟିପ୍ପଣୀ ଦିଅ", "REMOVE_COMMENT": "ଟିପ୍ପଣୀ ଲିଭାଅ", @@ -16940,7 +16960,7 @@ Blockly.ScratchMsgs.locales["or"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "ସନ୍ଦେଶ 1 " }; -Blockly.ScratchMsgs.locales["uz"] = +ScratchMsgs.locales["uz"] = { "CONTROL_FOREVER": "har doim", "CONTROL_REPEAT": "%1 marta takrorlash", @@ -17179,7 +17199,7 @@ Blockly.ScratchMsgs.locales["uz"] = "CATEGORY_OPERATORS": "Amallar", "CATEGORY_VARIABLES": "O'zgaruvchi", "CATEGORY_MYBLOCKS": "Mening bloklarim", - "DUPLICATE": "Nusxalash", + "DUPLICATE_BLOCK": "Nusxalash", "DELETE": "O'chirish", "ADD_COMMENT": "Izoh qo'shish", "REMOVE_COMMENT": "Izohni o'chirish", @@ -17227,7 +17247,7 @@ Blockly.ScratchMsgs.locales["uz"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "xabar1" }; -Blockly.ScratchMsgs.locales["th"] = +ScratchMsgs.locales["th"] = { "CONTROL_FOREVER": "วนซ้ำตลอด", "CONTROL_REPEAT": "ทำซ้ำ %1", @@ -17466,7 +17486,7 @@ Blockly.ScratchMsgs.locales["th"] = "CATEGORY_OPERATORS": "ตัวดำเนินการ", "CATEGORY_VARIABLES": "ตัวแปร", "CATEGORY_MYBLOCKS": "บล็อกของฉัน", - "DUPLICATE": "ทำซ้ำ", + "DUPLICATE_BLOCK": "ทำซ้ำ", "DELETE": "ลบ", "ADD_COMMENT": "เพิ่มคำอธิบาย", "REMOVE_COMMENT": "ลบคำอธิบาย", @@ -17514,7 +17534,7 @@ Blockly.ScratchMsgs.locales["th"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "ข้อความ1" }; -Blockly.ScratchMsgs.locales["km"] = +ScratchMsgs.locales["km"] = { "CONTROL_FOREVER": "រហូត", "CONTROL_REPEAT": "ធ្វើដដែលៗ %1 ដង", @@ -17753,7 +17773,7 @@ Blockly.ScratchMsgs.locales["km"] = "CATEGORY_OPERATORS": "ប្រមាណវិធី", "CATEGORY_VARIABLES": "អថេរ", "CATEGORY_MYBLOCKS": "ប្លុកខ្ញុំ", - "DUPLICATE": "ចម្លង", + "DUPLICATE_BLOCK": "ចម្លង", "DELETE": "លុប", "ADD_COMMENT": "ដាក់មតិ", "REMOVE_COMMENT": "លុបមតិ", @@ -17801,7 +17821,7 @@ Blockly.ScratchMsgs.locales["km"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "សារ 1" }; -Blockly.ScratchMsgs.locales["pl"] = +ScratchMsgs.locales["pl"] = { "CONTROL_FOREVER": "zawsze", "CONTROL_REPEAT": "powtórz %1 razy", @@ -18040,7 +18060,7 @@ Blockly.ScratchMsgs.locales["pl"] = "CATEGORY_OPERATORS": "Wyrażenia", "CATEGORY_VARIABLES": "Zmienne", "CATEGORY_MYBLOCKS": "Moje bloki", - "DUPLICATE": "Duplikuj", + "DUPLICATE_BLOCK": "Duplikuj", "DELETE": "Usuń", "ADD_COMMENT": "Dodaj komentarz", "REMOVE_COMMENT": "Usuń komentarz", @@ -18088,7 +18108,7 @@ Blockly.ScratchMsgs.locales["pl"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "wiadomość1" }; -Blockly.ScratchMsgs.locales["pt"] = +ScratchMsgs.locales["pt"] = { "CONTROL_FOREVER": "repete para sempre", "CONTROL_REPEAT": "repete %1 vezes", @@ -18327,7 +18347,7 @@ Blockly.ScratchMsgs.locales["pt"] = "CATEGORY_OPERATORS": "Operadores", "CATEGORY_VARIABLES": "Variáveis", "CATEGORY_MYBLOCKS": "Os Meus Blocos", - "DUPLICATE": "Duplicar", + "DUPLICATE_BLOCK": "Duplicar", "DELETE": "Remover", "ADD_COMMENT": "Adicionar Comentário", "REMOVE_COMMENT": "Remover Comentário", @@ -18375,7 +18395,7 @@ Blockly.ScratchMsgs.locales["pt"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "Mensagem 1" }; -Blockly.ScratchMsgs.locales["pt-br"] = +ScratchMsgs.locales["pt-br"] = { "CONTROL_FOREVER": "sempre", "CONTROL_REPEAT": "repita %1 vezes", @@ -18614,7 +18634,7 @@ Blockly.ScratchMsgs.locales["pt-br"] = "CATEGORY_OPERATORS": "Operadores", "CATEGORY_VARIABLES": "Variáveis", "CATEGORY_MYBLOCKS": "Meus Blocos", - "DUPLICATE": "Duplicar", + "DUPLICATE_BLOCK": "Duplicar", "DELETE": "Apagar", "ADD_COMMENT": "Comentar", "REMOVE_COMMENT": "Remover Comentário", @@ -18662,7 +18682,7 @@ Blockly.ScratchMsgs.locales["pt-br"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "mensagem 1" }; -Blockly.ScratchMsgs.locales["rap"] = +ScratchMsgs.locales["rap"] = { "CONTROL_FOREVER": "mo āŋa paurō te hora", "CONTROL_REPEAT": "haka ʾou %1", @@ -18901,7 +18921,7 @@ Blockly.ScratchMsgs.locales["rap"] = "CATEGORY_OPERATORS": "operadores", "CATEGORY_VARIABLES": "variables", "CATEGORY_MYBLOCKS": "taʾaku avhata poto roa mekera", - "DUPLICATE": "haka rahi", + "DUPLICATE_BLOCK": "haka rahi", "DELETE": "haka kore", "ADD_COMMENT": "hahaʾo te vanāŋa", "REMOVE_COMMENT": "haka kore te vanaŋa", @@ -18949,7 +18969,7 @@ Blockly.ScratchMsgs.locales["rap"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "ki hāŋa1" }; -Blockly.ScratchMsgs.locales["ro"] = +ScratchMsgs.locales["ro"] = { "CONTROL_FOREVER": "la infinit", "CONTROL_REPEAT": "repetă %1", @@ -19188,7 +19208,7 @@ Blockly.ScratchMsgs.locales["ro"] = "CATEGORY_OPERATORS": "Operatori", "CATEGORY_VARIABLES": "Variabile", "CATEGORY_MYBLOCKS": "Blocurile mele", - "DUPLICATE": "Duplică", + "DUPLICATE_BLOCK": "Duplică", "DELETE": "Șterge", "ADD_COMMENT": "Adaugă comentariu", "REMOVE_COMMENT": "Șterge comentariul", @@ -19236,7 +19256,7 @@ Blockly.ScratchMsgs.locales["ro"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "mesaj1" }; -Blockly.ScratchMsgs.locales["ru"] = +ScratchMsgs.locales["ru"] = { "CONTROL_FOREVER": "повторять всегда", "CONTROL_REPEAT": "повторить %1 раз", @@ -19475,7 +19495,7 @@ Blockly.ScratchMsgs.locales["ru"] = "CATEGORY_OPERATORS": "Операторы", "CATEGORY_VARIABLES": "Переменные", "CATEGORY_MYBLOCKS": "Другие блоки", - "DUPLICATE": "Дублировать", + "DUPLICATE_BLOCK": "Дублировать", "DELETE": "Удалить", "ADD_COMMENT": "Добавить комментарий", "REMOVE_COMMENT": "Удалить комментарий", @@ -19523,7 +19543,7 @@ Blockly.ScratchMsgs.locales["ru"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "сообщение1" }; -Blockly.ScratchMsgs.locales["nso"] = +ScratchMsgs.locales["nso"] = { "CONTROL_FOREVER": "ka go sa felego", "CONTROL_REPEAT": "bušeletša %1", @@ -19762,7 +19782,7 @@ Blockly.ScratchMsgs.locales["nso"] = "CATEGORY_OPERATORS": "Bašomiši", "CATEGORY_VARIABLES": "Diphetošo", "CATEGORY_MYBLOCKS": "Dipoloko tša Ka", - "DUPLICATE": "Pedifatša", + "DUPLICATE_BLOCK": "Pedifatša", "DELETE": "Phumula", "ADD_COMMENT": "Tlatša Tshwayotshwayo", "REMOVE_COMMENT": "Tloša Tshwayotshwayo", @@ -19810,7 +19830,7 @@ Blockly.ScratchMsgs.locales["nso"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "molaetša1" }; -Blockly.ScratchMsgs.locales["tn"] = +ScratchMsgs.locales["tn"] = { "CONTROL_FOREVER": "gosafeleng", "CONTROL_REPEAT": "boeletsa %1", @@ -20049,7 +20069,7 @@ Blockly.ScratchMsgs.locales["tn"] = "CATEGORY_OPERATORS": "Badiri", "CATEGORY_VARIABLES": "Dipharologano", "CATEGORY_MYBLOCKS": "Diboloko tsa me", - "DUPLICATE": "Gatisa", + "DUPLICATE_BLOCK": "Gatisa", "DELETE": "Phimola", "ADD_COMMENT": "Tsenya kakgelo", "REMOVE_COMMENT": "Tlosa kakgelo", @@ -20097,7 +20117,7 @@ Blockly.ScratchMsgs.locales["tn"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "molaetsa 1" }; -Blockly.ScratchMsgs.locales["sk"] = +ScratchMsgs.locales["sk"] = { "CONTROL_FOREVER": "opakuj stále", "CONTROL_REPEAT": "opakuj %1", @@ -20336,7 +20356,7 @@ Blockly.ScratchMsgs.locales["sk"] = "CATEGORY_OPERATORS": "Operácie", "CATEGORY_VARIABLES": "Premenné", "CATEGORY_MYBLOCKS": "Nové bloky", - "DUPLICATE": "duplikuj", + "DUPLICATE_BLOCK": "duplikuj", "DELETE": "zruš", "ADD_COMMENT": "pridaj komentár", "REMOVE_COMMENT": "zruš komentár", @@ -20384,7 +20404,7 @@ Blockly.ScratchMsgs.locales["sk"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "správa1" }; -Blockly.ScratchMsgs.locales["sl"] = +ScratchMsgs.locales["sl"] = { "CONTROL_FOREVER": "ponavljaj", "CONTROL_REPEAT": "ponovi %1 krat", @@ -20623,7 +20643,7 @@ Blockly.ScratchMsgs.locales["sl"] = "CATEGORY_OPERATORS": "Operatorji", "CATEGORY_VARIABLES": "Spremenljivke", "CATEGORY_MYBLOCKS": "Moji bloki", - "DUPLICATE": "Podvoji", + "DUPLICATE_BLOCK": "Podvoji", "DELETE": "Izbriši", "ADD_COMMENT": "Dodaj komentar", "REMOVE_COMMENT": "Odstrani komentar", @@ -20671,7 +20691,7 @@ Blockly.ScratchMsgs.locales["sl"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "sporočilo1" }; -Blockly.ScratchMsgs.locales["sr"] = +ScratchMsgs.locales["sr"] = { "CONTROL_FOREVER": "понављај заувек", "CONTROL_REPEAT": "понови %1", @@ -20910,7 +20930,7 @@ Blockly.ScratchMsgs.locales["sr"] = "CATEGORY_OPERATORS": "Оператори", "CATEGORY_VARIABLES": "Променљиве", "CATEGORY_MYBLOCKS": "Моји блокови", - "DUPLICATE": "Умножи", + "DUPLICATE_BLOCK": "Умножи", "DELETE": "Обриши", "ADD_COMMENT": "Додај коментар", "REMOVE_COMMENT": "Уклони коментар", @@ -20958,7 +20978,7 @@ Blockly.ScratchMsgs.locales["sr"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "порука1" }; -Blockly.ScratchMsgs.locales["fi"] = +ScratchMsgs.locales["fi"] = { "CONTROL_FOREVER": "ikuisesti", "CONTROL_REPEAT": "toista %1 kertaa", @@ -21197,7 +21217,7 @@ Blockly.ScratchMsgs.locales["fi"] = "CATEGORY_OPERATORS": "Toiminnot", "CATEGORY_VARIABLES": "Muuttujat", "CATEGORY_MYBLOCKS": "Lohkoni", - "DUPLICATE": "Kopioi", + "DUPLICATE_BLOCK": "Kopioi", "DELETE": "Poista", "ADD_COMMENT": "Lisää kommentti", "REMOVE_COMMENT": "Poista kommentti", @@ -21245,7 +21265,7 @@ Blockly.ScratchMsgs.locales["fi"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "viesti1" }; -Blockly.ScratchMsgs.locales["sv"] = +ScratchMsgs.locales["sv"] = { "CONTROL_FOREVER": "för alltid", "CONTROL_REPEAT": "repetera %1", @@ -21484,7 +21504,7 @@ Blockly.ScratchMsgs.locales["sv"] = "CATEGORY_OPERATORS": "Operatorer", "CATEGORY_VARIABLES": "Variabler", "CATEGORY_MYBLOCKS": "Mina block", - "DUPLICATE": "Kopiera", + "DUPLICATE_BLOCK": "Kopiera", "DELETE": "Radera", "ADD_COMMENT": "Lägg till kommentar", "REMOVE_COMMENT": "Ta bort kommentar", @@ -21532,7 +21552,7 @@ Blockly.ScratchMsgs.locales["sv"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "meddelande1" }; -Blockly.ScratchMsgs.locales["vi"] = +ScratchMsgs.locales["vi"] = { "CONTROL_FOREVER": "liên tục", "CONTROL_REPEAT": "lặp lại %1", @@ -21771,7 +21791,7 @@ Blockly.ScratchMsgs.locales["vi"] = "CATEGORY_OPERATORS": "Các phép toán", "CATEGORY_VARIABLES": "Các biến số", "CATEGORY_MYBLOCKS": "Khối của tôi", - "DUPLICATE": "Nhân bản", + "DUPLICATE_BLOCK": "Nhân bản", "DELETE": "Xóa", "ADD_COMMENT": "Thêm chú thích", "REMOVE_COMMENT": "Xóa chú thích", @@ -21819,7 +21839,7 @@ Blockly.ScratchMsgs.locales["vi"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "tin nhắn 1" }; -Blockly.ScratchMsgs.locales["tr"] = +ScratchMsgs.locales["tr"] = { "CONTROL_FOREVER": "sürekli tekrarla", "CONTROL_REPEAT": "%1 defa tekrarla", @@ -22058,7 +22078,7 @@ Blockly.ScratchMsgs.locales["tr"] = "CATEGORY_OPERATORS": "Operatörler", "CATEGORY_VARIABLES": "Değişkenler", "CATEGORY_MYBLOCKS": "Bloklarım", - "DUPLICATE": "Çoğalt", + "DUPLICATE_BLOCK": "Çoğalt", "DELETE": "Sil", "ADD_COMMENT": "Yorum Ekle", "REMOVE_COMMENT": "Yorumu Sil", @@ -22106,7 +22126,7 @@ Blockly.ScratchMsgs.locales["tr"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "haber1" }; -Blockly.ScratchMsgs.locales["uk"] = +ScratchMsgs.locales["uk"] = { "CONTROL_FOREVER": "завжди", "CONTROL_REPEAT": "повторити %1", @@ -22345,7 +22365,7 @@ Blockly.ScratchMsgs.locales["uk"] = "CATEGORY_OPERATORS": "Оператори", "CATEGORY_VARIABLES": "Змінні", "CATEGORY_MYBLOCKS": "Мої блоки", - "DUPLICATE": "Дублювати", + "DUPLICATE_BLOCK": "Дублювати", "DELETE": "Вилучити", "ADD_COMMENT": "Додати коментар", "REMOVE_COMMENT": "Вилучити коментар", @@ -22393,7 +22413,7 @@ Blockly.ScratchMsgs.locales["uk"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "повідомлення1" }; -Blockly.ScratchMsgs.locales["zh-cn"] = +ScratchMsgs.locales["zh-cn"] = { "CONTROL_FOREVER": "重复执行", "CONTROL_REPEAT": "重复执行 %1 次", @@ -22632,7 +22652,7 @@ Blockly.ScratchMsgs.locales["zh-cn"] = "CATEGORY_OPERATORS": "运算", "CATEGORY_VARIABLES": "变量", "CATEGORY_MYBLOCKS": "自制积木", - "DUPLICATE": "复制", + "DUPLICATE_BLOCK": "复制", "DELETE": "删除", "ADD_COMMENT": "添加注释", "REMOVE_COMMENT": "删除注释", @@ -22680,7 +22700,7 @@ Blockly.ScratchMsgs.locales["zh-cn"] = "DEFAULT_BROADCAST_MESSAGE_NAME": "消息1" }; -Blockly.ScratchMsgs.locales["zh-tw"] = +ScratchMsgs.locales["zh-tw"] = { "CONTROL_FOREVER": "重複無限次", "CONTROL_REPEAT": "重複 %1 次", @@ -22919,7 +22939,7 @@ Blockly.ScratchMsgs.locales["zh-tw"] = "CATEGORY_OPERATORS": "運算", "CATEGORY_VARIABLES": "變數", "CATEGORY_MYBLOCKS": "函式積木", - "DUPLICATE": "複製", + "DUPLICATE_BLOCK": "複製", "DELETE": "刪除", "ADD_COMMENT": "添加註解", "REMOVE_COMMENT": "移除註解", diff --git a/package-lock.json b/package-lock.json index b2c2694953..6593c1668c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,47 +1,56 @@ { "name": "scratch-blocks", - "version": "1.1.86", + "version": "2.0.0-spork.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "scratch-blocks", - "version": "1.1.86", + "version": "2.0.0-spork.5", "license": "Apache-2.0", "dependencies": { - "exports-loader": "^0.7.0", - "google-closure-library": "^20190301.0.0", - "imports-loader": "^0.8.0", - "scratch-l10n": "^3.18.3" + "@blockly/continuous-toolbox": "^7.0.1", + "@blockly/field-colour": "^6.0.3", + "blockly": "12.3.0-beta.0" }, "devDependencies": { - "@commitlint/cli": "17.8.1", - "@commitlint/config-conventional": "17.8.1", - "async": "2.6.4", - "copy-webpack-plugin": "4.6.0", - "eslint": "4.19.1", - "event-stream": "3.3.5", - "gh-pages": "0.12.0", - "glob": "7.2.3", - "google-closure-compiler": "20180402.0.0", - "graceful-fs": "4.2.11", - "husky": "8.0.3", - "json": "9.0.6", - "rimraf": "2.7.1", - "scratch-semantic-release-config": "1.0.14", - "selenium-webdriver": "4.16.0", - "semantic-release": "19.0.5", - "transifex": "1.6.6", - "uglifyjs-webpack-plugin": "1.3.0", - "webpack": "4.47.0", - "webpack-cli": "3.3.12" + "@commitlint/cli": "^17.8.1", + "@commitlint/config-conventional": "^17.8.1", + "eslint": "^4.19.1", + "husky": "9.1.6", + "scratch-semantic-release-config": "1.0.16", + "semantic-release": "22.0.12", + "source-map-loader": "^4.0.1", + "ts-loader": "^9.5.1", + "typescript": "^5.6.3", + "webpack": "^5.76.0", + "webpack-cli": "^4.10.0", + "webpack-dev-server": "^4.11.1" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" } }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, "node_modules/@babel/code-frame": { "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", - "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/highlight": "^7.24.2", "picocolors": "^1.0.0" @@ -52,18 +61,16 @@ }, "node_modules/@babel/helper-validator-identifier": { "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { "version": "7.24.2", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", - "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", @@ -76,9 +83,8 @@ }, "node_modules/@babel/highlight/node_modules/ansi-styles": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -88,9 +94,8 @@ }, "node_modules/@babel/highlight/node_modules/chalk": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -102,39 +107,29 @@ }, "node_modules/@babel/highlight/node_modules/color-convert": { "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "1.1.3" } }, "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/@babel/highlight/node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, "node_modules/@babel/highlight/node_modules/supports-color": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -142,11 +137,49 @@ "node": ">=4" } }, + "node_modules/@blockly/continuous-toolbox": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@blockly/continuous-toolbox/-/continuous-toolbox-7.0.2.tgz", + "integrity": "sha512-xx65PX+hqynNxqHonFfd1taRkCEGDJYS66oUDbBkwL9lmHI/sXcPCy7E8NhKnYweYOISc0NO2p8G732U8uI0kw==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.17.0" + }, + "peerDependencies": { + "blockly": "^12.0.0" + } + }, + "node_modules/@blockly/field-colour": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@blockly/field-colour/-/field-colour-6.0.4.tgz", + "integrity": "sha512-/sNTrP/wQHwpLN8sEbBDvRBFkY9+7U+WAfnA66wmgRJc4N02F41kX89C29CLUS4SUVkElUuh9spYH6Lj7G4KRQ==", + "license": "Apache-2.0", + "dependencies": { + "@blockly/field-grid-dropdown": "^6.0.3" + }, + "engines": { + "node": ">=8.0.0" + }, + "peerDependencies": { + "blockly": "^12.0.0" + } + }, + "node_modules/@blockly/field-colour/node_modules/@blockly/field-grid-dropdown": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@blockly/field-grid-dropdown/-/field-grid-dropdown-6.0.3.tgz", + "integrity": "sha512-BeCmxNz4FuvZ9GRTw+RM8jWADK2DhQupacKRdhFlEnhR9akOSTwIgeaWoqnIvBkLvXLtSwAJTuQG5wyXoGGd0A==", + "license": "Apache 2.0", + "engines": { + "node": ">=8.17.0" + }, + "peerDependencies": { + "blockly": "^12.0.0" + } + }, "node_modules/@colors/colors": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=0.1.90" @@ -154,9 +187,8 @@ }, "node_modules/@commitlint/cli": { "version": "17.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.8.1.tgz", - "integrity": "sha512-ay+WbzQesE0Rv4EQKfNbSMiJJ12KdKTDzIt0tcK4k11FdsWmtwP0Kp1NWMOUswfIWo6Eb7p7Ln721Nx9FLNBjg==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/format": "^17.8.1", "@commitlint/lint": "^17.8.1", @@ -178,9 +210,8 @@ }, "node_modules/@commitlint/config-conventional": { "version": "17.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.8.1.tgz", - "integrity": "sha512-NxCOHx1kgneig3VLauWJcDWS40DVjg7nKOpBEEK9E5fjJpQqLCilcnKkIIjdBH98kEO1q3NpE5NSrZ2kl/QGJg==", "dev": true, + "license": "MIT", "dependencies": { "conventional-changelog-conventionalcommits": "^6.1.0" }, @@ -190,9 +221,8 @@ }, "node_modules/@commitlint/config-validator": { "version": "17.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.8.1.tgz", - "integrity": "sha512-UUgUC+sNiiMwkyiuIFR7JG2cfd9t/7MV8VB4TZ+q02ZFkHoduUS4tJGsCBWvBOGD9Btev6IecPMvlWUfJorkEA==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/types": "^17.8.1", "ajv": "^8.11.0" @@ -201,11 +231,30 @@ "node": ">=v14" } }, + "node_modules/@commitlint/config-validator/node_modules/ajv": { + "version": "8.12.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@commitlint/config-validator/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, "node_modules/@commitlint/ensure": { "version": "17.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-17.8.1.tgz", - "integrity": "sha512-xjafwKxid8s1K23NFpL8JNo6JnY/ysetKo8kegVM7c8vs+kWLP8VrQq+NbhgVlmCojhEDbzQKp4eRXSjVOGsow==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/types": "^17.8.1", "lodash.camelcase": "^4.3.0", @@ -220,18 +269,16 @@ }, "node_modules/@commitlint/execute-rule": { "version": "17.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.8.1.tgz", - "integrity": "sha512-JHVupQeSdNI6xzA9SqMF+p/JjrHTcrJdI02PwesQIDCIGUrv04hicJgCcws5nzaoZbROapPs0s6zeVHoxpMwFQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=v14" } }, "node_modules/@commitlint/format": { "version": "17.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-17.8.1.tgz", - "integrity": "sha512-f3oMTyZ84M9ht7fb93wbCKmWxO5/kKSbwuYvS867duVomoOsgrgljkGGIztmT/srZnaiGbaK8+Wf8Ik2tSr5eg==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/types": "^17.8.1", "chalk": "^4.1.0" @@ -242,9 +289,8 @@ }, "node_modules/@commitlint/is-ignored": { "version": "17.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.8.1.tgz", - "integrity": "sha512-UshMi4Ltb4ZlNn4F7WtSEugFDZmctzFpmbqvpyxD3la510J+PLcnyhf9chs7EryaRFJMdAKwsEKfNK0jL/QM4g==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/types": "^17.8.1", "semver": "7.5.4" @@ -255,9 +301,8 @@ }, "node_modules/@commitlint/lint": { "version": "17.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.8.1.tgz", - "integrity": "sha512-aQUlwIR1/VMv2D4GXSk7PfL5hIaFSfy6hSHV94O8Y27T5q+DlDEgd/cZ4KmVI+MWKzFfCTiTuWqjfRSfdRllCA==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/is-ignored": "^17.8.1", "@commitlint/parse": "^17.8.1", @@ -270,9 +315,8 @@ }, "node_modules/@commitlint/load": { "version": "17.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.8.1.tgz", - "integrity": "sha512-iF4CL7KDFstP1kpVUkT8K2Wl17h2yx9VaR1ztTc8vzByWWcbO/WaKwxsnCOqow9tVAlzPfo1ywk9m2oJ9ucMqA==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/config-validator": "^17.8.1", "@commitlint/execute-rule": "^17.8.1", @@ -293,20 +337,23 @@ "node": ">=v14" } }, + "node_modules/@commitlint/load/node_modules/@types/node": { + "version": "20.5.1", + "dev": true, + "license": "MIT" + }, "node_modules/@commitlint/message": { "version": "17.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-17.8.1.tgz", - "integrity": "sha512-6bYL1GUQsD6bLhTH3QQty8pVFoETfFQlMn2Nzmz3AOLqRVfNNtXBaSY0dhZ0dM6A2MEq4+2d7L/2LP8TjqGRkA==", "dev": true, + "license": "MIT", "engines": { "node": ">=v14" } }, "node_modules/@commitlint/parse": { "version": "17.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.8.1.tgz", - "integrity": "sha512-/wLUickTo0rNpQgWwLPavTm7WbwkZoBy3X8PpkUmlSmQJyWQTj0m6bDjiykMaDt41qcUbfeFfaCvXfiR4EGnfw==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/types": "^17.8.1", "conventional-changelog-angular": "^6.0.0", @@ -318,9 +365,8 @@ }, "node_modules/@commitlint/read": { "version": "17.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-17.8.1.tgz", - "integrity": "sha512-Fd55Oaz9irzBESPCdMd8vWWgxsW3OWR99wOntBDHgf9h7Y6OOHjWEdS9Xzen1GFndqgyoaFplQS5y7KZe0kO2w==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/top-level": "^17.8.1", "@commitlint/types": "^17.8.1", @@ -334,9 +380,8 @@ }, "node_modules/@commitlint/resolve-extends": { "version": "17.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.8.1.tgz", - "integrity": "sha512-W/ryRoQ0TSVXqJrx5SGkaYuAaE/BUontL1j1HsKckvM6e5ZaG0M9126zcwL6peKSuIetJi7E87PRQF8O86EW0Q==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/config-validator": "^17.8.1", "@commitlint/types": "^17.8.1", @@ -351,9 +396,8 @@ }, "node_modules/@commitlint/rules": { "version": "17.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.8.1.tgz", - "integrity": "sha512-2b7OdVbN7MTAt9U0vKOYKCDsOvESVXxQmrvuVUZ0rGFMCrCPJWWP1GJ7f0lAypbDAhaGb8zqtdOr47192LBrIA==", "dev": true, + "license": "MIT", "dependencies": { "@commitlint/ensure": "^17.8.1", "@commitlint/message": "^17.8.1", @@ -367,18 +411,16 @@ }, "node_modules/@commitlint/to-lines": { "version": "17.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-17.8.1.tgz", - "integrity": "sha512-LE0jb8CuR/mj6xJyrIk8VLz03OEzXFgLdivBytoooKO5xLt5yalc8Ma5guTWobw998sbR3ogDd+2jed03CFmJA==", "dev": true, + "license": "MIT", "engines": { "node": ">=v14" } }, "node_modules/@commitlint/top-level": { "version": "17.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-17.8.1.tgz", - "integrity": "sha512-l6+Z6rrNf5p333SHfEte6r+WkOxGlWK4bLuZKbtf/2TXRN+qhrvn1XE63VhD8Oe9oIHQ7F7W1nG2k/TJFhx2yA==", "dev": true, + "license": "MIT", "dependencies": { "find-up": "^5.0.0" }, @@ -386,11 +428,67 @@ "node": ">=v14" } }, + "node_modules/@commitlint/top-level/node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/p-locate": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@commitlint/types": { "version": "17.8.1", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-17.8.1.tgz", - "integrity": "sha512-PXDQXkAmiMEG162Bqdh9ChML/GJZo6vU+7F03ALKDK8zYc6SuAr47LjG7hGYRqUOz+WK0dU7bQ0xzuqFMdxzeQ==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.1.0" }, @@ -400,9 +498,8 @@ }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -410,36 +507,194 @@ "node": ">=12" } }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", + "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz", + "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.0.2", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "version": "0.3.20", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.4", + "dev": true, + "license": "MIT" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -450,18 +705,16 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -471,174 +724,196 @@ } }, "node_modules/@octokit/auth-token": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.4.tgz", - "integrity": "sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ==", + "version": "4.0.0", "dev": true, + "license": "MIT", "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/core": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.4.tgz", - "integrity": "sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==", + "version": "5.2.0", "dev": true, + "license": "MIT", "dependencies": { - "@octokit/auth-token": "^3.0.0", - "@octokit/graphql": "^5.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.1.0", + "@octokit/request": "^8.3.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.0.0", "before-after-hook": "^2.2.0", "universal-user-agent": "^6.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/endpoint": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.6.tgz", - "integrity": "sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==", + "version": "9.0.5", "dev": true, + "license": "MIT", "dependencies": { - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", + "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/graphql": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.6.tgz", - "integrity": "sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw==", + "version": "7.1.0", "dev": true, + "license": "MIT", "dependencies": { - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", + "@octokit/request": "^8.3.0", + "@octokit/types": "^13.0.0", "universal-user-agent": "^6.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/openapi-types": { - "version": "18.1.1", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.1.1.tgz", - "integrity": "sha512-VRaeH8nCDtF5aXWnjPuEMIYf1itK/s3JYyJcWFJT8X9pSNnBtriDf7wlEWsGuhPLl4QIH4xM8fqTXDwJ3Mu6sw==", - "dev": true + "version": "22.2.0", + "dev": true, + "license": "MIT" }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.1.2.tgz", - "integrity": "sha512-qhrmtQeHU/IivxucOV1bbI/xZyC/iOBhclokv7Sut5vnejAIAEXVcGQeRpQlU39E0WwK9lNvJHphHri/DB6lbQ==", + "version": "9.2.1", "dev": true, + "license": "MIT", "dependencies": { - "@octokit/tsconfig": "^1.0.2", - "@octokit/types": "^9.2.3" + "@octokit/types": "^12.6.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" }, "peerDependencies": { - "@octokit/core": ">=4" + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { + "version": "12.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" } }, "node_modules/@octokit/plugin-retry": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-4.1.6.tgz", - "integrity": "sha512-obkYzIgEC75r8+9Pnfiiqy3y/x1bc3QLE5B7qvv9wi9Kj0R5tGQFC6QMBg1154WQ9lAVypuQDGyp3hNpp15gQQ==", + "version": "6.0.1", "dev": true, + "license": "MIT", "dependencies": { - "@octokit/types": "^9.0.0", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0", "bottleneck": "^2.15.3" }, "engines": { - "node": ">= 14" + "node": ">= 18" }, "peerDependencies": { - "@octokit/core": ">=3" + "@octokit/core": ">=5" + } + }, + "node_modules/@octokit/plugin-retry/node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-retry/node_modules/@octokit/types": { + "version": "12.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" } }, "node_modules/@octokit/plugin-throttling": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-5.2.3.tgz", - "integrity": "sha512-C9CFg9mrf6cugneKiaI841iG8DOv6P5XXkjmiNNut+swePxQ7RWEdAZRp5rJoE1hjsIqiYcKa/ZkOQ+ujPI39Q==", + "version": "8.2.0", "dev": true, + "license": "MIT", "dependencies": { - "@octokit/types": "^9.0.0", + "@octokit/types": "^12.2.0", "bottleneck": "^2.15.3" }, "engines": { - "node": ">= 14" + "node": ">= 18" }, "peerDependencies": { - "@octokit/core": "^4.0.0" + "@octokit/core": "^5.0.0" + } + }, + "node_modules/@octokit/plugin-throttling/node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-throttling/node_modules/@octokit/types": { + "version": "12.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" } }, "node_modules/@octokit/request": { - "version": "6.2.8", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.8.tgz", - "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==", + "version": "8.4.0", "dev": true, + "license": "MIT", "dependencies": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", + "@octokit/endpoint": "^9.0.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/request-error": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz", - "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", + "version": "5.1.0", "dev": true, + "license": "MIT", "dependencies": { - "@octokit/types": "^9.0.0", + "@octokit/types": "^13.1.0", "deprecation": "^2.0.0", "once": "^1.4.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/tsconfig": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@octokit/tsconfig/-/tsconfig-1.0.2.tgz", - "integrity": "sha512-I0vDR0rdtP8p2lGMzvsJzbhdOWy405HcGovrspJ8RRibHnyRgggUSNO5AIox5LmqiwmatHKYsvj6VGFHkqS7lA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@octokit/types": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz", - "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==", + "version": "13.6.1", "dev": true, + "license": "MIT", "dependencies": { - "@octokit/openapi-types": "^18.0.0" + "@octokit/openapi-types": "^22.2.0" } }, "node_modules/@pnpm/config.env-replace": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", - "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.22.0" } }, "node_modules/@pnpm/network.ca-file": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", - "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "4.2.10" }, @@ -648,15 +923,13 @@ }, "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/@pnpm/npm-conf": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz", - "integrity": "sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==", + "version": "2.3.1", "dev": true, + "license": "MIT", "dependencies": { "@pnpm/config.env-replace": "^1.1.0", "@pnpm/network.ca-file": "^1.0.1", @@ -668,9 +941,8 @@ }, "node_modules/@semantic-release/changelog": { "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-6.0.3.tgz", - "integrity": "sha512-dZuR5qByyfe3Y03TpmCvAxCyTnp7r5XwtHRf/8vD9EAn4ZWbavUX8adMtXYzE86EVh0gyLA7lm5yW4IV30XUag==", "dev": true, + "license": "MIT", "dependencies": { "@semantic-release/error": "^3.0.0", "aggregate-error": "^3.0.0", @@ -684,107 +956,135 @@ "semantic-release": ">=18.0.0" } }, + "node_modules/@semantic-release/changelog/node_modules/@semantic-release/error": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@semantic-release/changelog/node_modules/aggregate-error": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/changelog/node_modules/clean-stack": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/@semantic-release/commit-analyzer": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-9.0.2.tgz", - "integrity": "sha512-E+dr6L+xIHZkX4zNMe6Rnwg4YQrWNXK+rNsvwOPpdFppvZO1olE2fIgWhv89TkQErygevbjsZFSIxp+u6w2e5g==", + "version": "11.1.0", "dev": true, + "license": "MIT", "dependencies": { - "conventional-changelog-angular": "^5.0.0", - "conventional-commits-filter": "^2.0.0", - "conventional-commits-parser": "^3.2.3", + "conventional-changelog-angular": "^7.0.0", + "conventional-commits-filter": "^4.0.0", + "conventional-commits-parser": "^5.0.0", "debug": "^4.0.0", - "import-from": "^4.0.0", - "lodash": "^4.17.4", + "import-from-esm": "^1.0.3", + "lodash-es": "^4.17.21", "micromatch": "^4.0.2" }, "engines": { - "node": ">=14.17" + "node": "^18.17 || >=20.6.1" }, "peerDependencies": { - "semantic-release": ">=18.0.0-beta.1" + "semantic-release": ">=20.1.0" } }, "node_modules/@semantic-release/commit-analyzer/node_modules/conventional-changelog-angular": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", - "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", + "version": "7.0.0", "dev": true, + "license": "ISC", "dependencies": { - "compare-func": "^2.0.0", - "q": "^1.5.1" + "compare-func": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=16" } }, "node_modules/@semantic-release/commit-analyzer/node_modules/conventional-commits-parser": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", - "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", + "version": "5.0.0", "dev": true, + "license": "MIT", "dependencies": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.0.4", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" + "is-text-path": "^2.0.0", + "JSONStream": "^1.3.5", + "meow": "^12.0.1", + "split2": "^4.0.0" }, "bin": { - "conventional-commits-parser": "cli.js" + "conventional-commits-parser": "cli.mjs" }, "engines": { - "node": ">=10" + "node": ">=16" } }, - "node_modules/@semantic-release/commit-analyzer/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@semantic-release/commit-analyzer/node_modules/is-text-path": { + "version": "2.0.0", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "text-extensions": "^2.0.0" }, "engines": { - "node": ">=6.0" + "node": ">=8" + } + }, + "node_modules/@semantic-release/commit-analyzer/node_modules/meow": { + "version": "12.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.10" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@semantic-release/commit-analyzer/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "node_modules/@semantic-release/commit-analyzer/node_modules/split2": { + "version": "4.2.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } }, - "node_modules/@semantic-release/commit-analyzer/node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "node_modules/@semantic-release/commit-analyzer/node_modules/text-extensions": { + "version": "2.4.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@semantic-release/error": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", - "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==", + "version": "4.0.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=14.17" + "node": ">=18" } }, "node_modules/@semantic-release/git": { "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@semantic-release/git/-/git-10.0.1.tgz", - "integrity": "sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==", "dev": true, + "license": "MIT", "dependencies": { "@semantic-release/error": "^3.0.0", "aggregate-error": "^3.0.0", @@ -802,4615 +1102,3688 @@ "semantic-release": ">=18.0.0" } }, - "node_modules/@semantic-release/git/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@semantic-release/git/node_modules/@semantic-release/error": { + "version": "3.0.0", "dev": true, - "dependencies": { - "ms": "2.1.2" - }, + "license": "MIT", "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=14.17" } }, - "node_modules/@semantic-release/git/node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "node_modules/@semantic-release/git/node_modules/aggregate-error": { + "version": "3.1.0", "dev": true, + "license": "MIT", "dependencies": { - "path-type": "^4.0.0" + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/@semantic-release/git/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "node_modules/@semantic-release/git/node_modules/clean-stack": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@semantic-release/git/node_modules/p-reduce": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, "node_modules/@semantic-release/github": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-8.1.0.tgz", - "integrity": "sha512-erR9E5rpdsz0dW1I7785JtndQuMWN/iDcemcptf67tBNOmBUN0b2YNOgcjYUnBpgRpZ5ozfBHrK7Bz+2ets/Dg==", + "version": "9.2.6", "dev": true, + "license": "MIT", "dependencies": { - "@octokit/core": "^4.2.1", - "@octokit/plugin-paginate-rest": "^6.1.2", - "@octokit/plugin-retry": "^4.1.3", - "@octokit/plugin-throttling": "^5.2.3", - "@semantic-release/error": "^3.0.0", - "aggregate-error": "^3.0.0", - "debug": "^4.0.0", - "dir-glob": "^3.0.0", - "fs-extra": "^11.0.0", - "globby": "^11.0.0", + "@octokit/core": "^5.0.0", + "@octokit/plugin-paginate-rest": "^9.0.0", + "@octokit/plugin-retry": "^6.0.0", + "@octokit/plugin-throttling": "^8.0.0", + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "debug": "^4.3.4", + "dir-glob": "^3.0.1", + "globby": "^14.0.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "issue-parser": "^6.0.0", - "lodash": "^4.17.4", - "mime": "^3.0.0", - "p-filter": "^2.0.0", - "url-join": "^4.0.0" + "lodash-es": "^4.17.21", + "mime": "^4.0.0", + "p-filter": "^4.0.0", + "url-join": "^5.0.0" }, "engines": { - "node": ">=14.17" + "node": ">=18" }, "peerDependencies": { - "semantic-release": ">=18.0.0-beta.1" + "semantic-release": ">=20.1.0" } }, - "node_modules/@semantic-release/github/node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "node_modules/@semantic-release/github/node_modules/mime": { + "version": "4.0.4", "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, "engines": { - "node": ">=8" + "node": ">=16" } }, - "node_modules/@semantic-release/github/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@semantic-release/npm": { + "version": "11.0.3", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "execa": "^8.0.0", + "fs-extra": "^11.0.0", + "lodash-es": "^4.17.21", + "nerf-dart": "^1.0.0", + "normalize-url": "^8.0.0", + "npm": "^10.5.0", + "rc": "^1.2.8", + "read-pkg": "^9.0.0", + "registry-auth-token": "^5.0.0", + "semver": "^7.1.2", + "tempy": "^3.0.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "engines": { + "node": "^18.17 || >=20" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" } }, - "node_modules/@semantic-release/github/node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "node_modules/@semantic-release/npm/node_modules/execa": { + "version": "8.0.1", "dev": true, + "license": "MIT", "dependencies": { - "path-type": "^4.0.0" + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/@semantic-release/github/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/@semantic-release/npm/node_modules/get-stream": { + "version": "8.0.1", "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@semantic-release/github/node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "node_modules/@semantic-release/npm/node_modules/hosted-git-info": { + "version": "7.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/human-signals": { + "version": "5.0.0", "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">= 4" + "node": ">=16.17.0" } }, - "node_modules/@semantic-release/github/node_modules/mime": { + "node_modules/@semantic-release/npm/node_modules/is-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", "dev": true, - "bin": { - "mime": "cli.js" - }, + "license": "MIT", "engines": { - "node": ">=10.0.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@semantic-release/github/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "node_modules/@semantic-release/npm/node_modules/lru-cache": { + "version": "10.4.3", + "dev": true, + "license": "ISC" }, - "node_modules/@semantic-release/github/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/@semantic-release/npm/node_modules/mimic-fn": { + "version": "4.0.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@semantic-release/npm": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-9.0.2.tgz", - "integrity": "sha512-zgsynF6McdzxPnFet+a4iO9HpAlARXOM5adz7VGVCvj0ne8wtL2ZOQoDV2wZPDmdEotDIbVeJjafhelZjs9j6g==", + "node_modules/@semantic-release/npm/node_modules/normalize-package-data": { + "version": "6.0.2", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@semantic-release/error": "^3.0.0", - "aggregate-error": "^3.0.0", - "execa": "^5.0.0", - "fs-extra": "^11.0.0", - "lodash": "^4.17.15", - "nerf-dart": "^1.0.0", - "normalize-url": "^6.0.0", - "npm": "^8.3.0", - "rc": "^1.2.8", - "read-pkg": "^5.0.0", - "registry-auth-token": "^5.0.0", - "semver": "^7.1.2", - "tempy": "^1.0.0" + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": ">=16 || ^14.17" - }, - "peerDependencies": { - "semantic-release": ">=19.0.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@semantic-release/release-notes-generator": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-10.0.3.tgz", - "integrity": "sha512-k4x4VhIKneOWoBGHkx0qZogNjCldLPRiAjnIpMnlUh6PtaWXp/T+C9U7/TaNDDtgDa5HMbHl4WlREdxHio6/3w==", + "node_modules/@semantic-release/npm/node_modules/npm-run-path": { + "version": "5.3.0", "dev": true, + "license": "MIT", "dependencies": { - "conventional-changelog-angular": "^5.0.0", - "conventional-changelog-writer": "^5.0.0", - "conventional-commits-filter": "^2.0.0", - "conventional-commits-parser": "^3.2.3", - "debug": "^4.0.0", - "get-stream": "^6.0.0", - "import-from": "^4.0.0", - "into-stream": "^6.0.0", - "lodash": "^4.17.4", - "read-pkg-up": "^7.0.0" + "path-key": "^4.0.0" }, "engines": { - "node": ">=14.17" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, - "peerDependencies": { - "semantic-release": ">=18.0.0-beta.1" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@semantic-release/release-notes-generator/node_modules/conventional-changelog-angular": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", - "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", + "node_modules/@semantic-release/npm/node_modules/onetime": { + "version": "6.0.0", "dev": true, + "license": "MIT", "dependencies": { - "compare-func": "^2.0.0", - "q": "^1.5.1" + "mimic-fn": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@semantic-release/release-notes-generator/node_modules/conventional-commits-parser": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", - "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", + "node_modules/@semantic-release/npm/node_modules/parse-json": { + "version": "8.1.0", "dev": true, + "license": "MIT", "dependencies": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.0.4", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" + "@babel/code-frame": "^7.22.13", + "index-to-position": "^0.1.2", + "type-fest": "^4.7.1" }, - "bin": { - "conventional-commits-parser": "cli.js" + "engines": { + "node": ">=18" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm/node_modules/path-key": { + "version": "4.0.0", + "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@semantic-release/release-notes-generator/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@semantic-release/npm/node_modules/read-pkg": { + "version": "9.0.1", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" }, "engines": { - "node": ">=6.0" + "node": ">=18" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@semantic-release/release-notes-generator/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "node_modules/@semantic-release/npm/node_modules/signal-exit": { + "version": "4.1.0", "dev": true, + "license": "ISC", "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "node_modules/@types/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.5.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz", - "integrity": "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==", - "dev": true - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", - "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", - "dev": true - }, - "node_modules/@types/parse-json": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", - "dev": true - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", - "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "node_modules/@semantic-release/npm/node_modules/strip-final-newline": { + "version": "3.0.0", "dev": true, - "dependencies": { - "@webassemblyjs/helper-module-context": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", - "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", - "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", - "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-code-frame": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", - "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", + "node_modules/@semantic-release/npm/node_modules/type-fest": { + "version": "4.26.1", "dev": true, - "dependencies": { - "@webassemblyjs/wast-printer": "1.9.0" + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@webassemblyjs/helper-fsm": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", - "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-module-context": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", - "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "node_modules/@semantic-release/release-notes-generator": { + "version": "12.1.0", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.9.0" + "conventional-changelog-angular": "^7.0.0", + "conventional-changelog-writer": "^7.0.0", + "conventional-commits-filter": "^4.0.0", + "conventional-commits-parser": "^5.0.0", + "debug": "^4.0.0", + "get-stream": "^7.0.0", + "import-from-esm": "^1.0.3", + "into-stream": "^7.0.0", + "lodash-es": "^4.17.21", + "read-pkg-up": "^11.0.0" + }, + "engines": { + "node": "^18.17 || >=20.6.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" } }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", - "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", - "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "node_modules/@semantic-release/release-notes-generator/node_modules/conventional-changelog-angular": { + "version": "7.0.0", "dev": true, + "license": "ISC", "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0" + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" } }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", - "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "node_modules/@semantic-release/release-notes-generator/node_modules/conventional-commits-parser": { + "version": "5.0.0", "dev": true, + "license": "MIT", "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", - "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", - "dev": true, - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", - "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", - "dev": true - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", - "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/helper-wasm-section": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0", - "@webassemblyjs/wasm-opt": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0", - "@webassemblyjs/wast-printer": "1.9.0" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", - "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/ieee754": "1.9.0", - "@webassemblyjs/leb128": "1.9.0", - "@webassemblyjs/utf8": "1.9.0" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", - "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-buffer": "1.9.0", - "@webassemblyjs/wasm-gen": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", - "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-api-error": "1.9.0", - "@webassemblyjs/helper-wasm-bytecode": "1.9.0", - "@webassemblyjs/ieee754": "1.9.0", - "@webassemblyjs/leb128": "1.9.0", - "@webassemblyjs/utf8": "1.9.0" + "is-text-path": "^2.0.0", + "JSONStream": "^1.3.5", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.mjs" + }, + "engines": { + "node": ">=16" } }, - "node_modules/@webassemblyjs/wast-parser": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", - "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "node_modules/@semantic-release/release-notes-generator/node_modules/get-stream": { + "version": "7.0.1", "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/floating-point-hex-parser": "1.9.0", - "@webassemblyjs/helper-api-error": "1.9.0", - "@webassemblyjs/helper-code-frame": "1.9.0", - "@webassemblyjs/helper-fsm": "1.9.0", - "@xtuc/long": "4.2.2" + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", - "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "node_modules/@semantic-release/release-notes-generator/node_modules/hosted-git-info": { + "version": "7.0.2", "dev": true, + "license": "ISC", "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/wast-parser": "1.9.0", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "node_modules/acorn": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", - "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" + "lru-cache": "^10.0.1" }, "engines": { - "node": ">=0.4.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/acorn-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "integrity": "sha512-AU7pnZkguthwBjKgCg6998ByQNIMjbuDQZ8bb78QAFZwPfmKia8AIzgY/gWgqCjnht8JLdXmB4YxA0KaV60ncQ==", + "node_modules/@semantic-release/release-notes-generator/node_modules/is-text-path": { + "version": "2.0.0", "dev": true, + "license": "MIT", "dependencies": { - "acorn": "^3.0.4" - } - }, - "node_modules/acorn-jsx/node_modules/acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha512-OLUyIIZ7mF5oaAUT1w0TFqQS81q3saT46x8t7ukpPjMNk+nbs4ZHhs7ToV8EWnLYLepjETXd4XaCE4uxkMeqUw==", - "dev": true, - "bin": { - "acorn": "bin/acorn" + "text-extensions": "^2.0.0" }, "engines": { - "node": ">=0.4.0" + "node": ">=8" } }, - "node_modules/acorn-walk": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", - "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "node_modules/@semantic-release/release-notes-generator/node_modules/lru-cache": { + "version": "10.4.3", + "dev": true, + "license": "ISC" + }, + "node_modules/@semantic-release/release-notes-generator/node_modules/meow": { + "version": "12.1.1", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.4.0" + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "node_modules/@semantic-release/release-notes-generator/node_modules/normalize-package-data": { + "version": "6.0.2", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "debug": "^4.3.4" + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": ">= 14" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/agent-base/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@semantic-release/release-notes-generator/node_modules/parse-json": { + "version": "8.1.0", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "@babel/code-frame": "^7.22.13", + "index-to-position": "^0.1.2", + "type-fest": "^4.7.1" }, "engines": { - "node": ">=6.0" + "node": ">=18" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/agent-base/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "node_modules/@semantic-release/release-notes-generator/node_modules/read-pkg": { + "version": "9.0.1", "dev": true, + "license": "MIT", "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "node_modules/@semantic-release/release-notes-generator/node_modules/read-pkg-up": { + "version": "11.0.0", "dev": true, + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "find-up-simple": "^1.0.0", + "read-pkg": "^9.0.0", + "type-fest": "^4.6.0" + }, + "engines": { + "node": ">=18" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ajv-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "node_modules/@semantic-release/release-notes-generator/node_modules/split2": { + "version": "4.2.0", "dev": true, - "peerDependencies": { - "ajv": ">=5.0.0" + "license": "ISC", + "engines": { + "node": ">= 10.x" } }, - "node_modules/ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "node_modules/@semantic-release/release-notes-generator/node_modules/text-extensions": { + "version": "2.4.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-regex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "node_modules/@semantic-release/release-notes-generator/node_modules/type-fest": { + "version": "4.26.1", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=4" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@sindresorhus/is": { + "version": "4.6.0", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/ansicolors": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", - "integrity": "sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==", - "dev": true - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", "dev": true, - "optional": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, + "license": "MIT", "engines": { - "node": ">= 8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "dev": true, + "license": "MIT" }, - "node_modules/argv-formatter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", - "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==", - "dev": true + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "dev": true, + "license": "MIT" }, - "node_modules/arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "node_modules/@tsconfig/node14": { + "version": "1.0.3", "dev": true, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "node_modules/@tsconfig/node16": { + "version": "1.0.4", "dev": true, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "node_modules/@types/body-parser": { + "version": "1.19.5", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" } }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "node_modules/@types/bonjour": { + "version": "3.5.13", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@types/node": "*" } }, - "node_modules/array-ify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", - "dev": true - }, - "node_modules/array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "node_modules/@types/connect": { + "version": "3.4.38", "dev": true, + "license": "MIT", "dependencies": { - "array-uniq": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" + "@types/node": "*" } }, - "node_modules/array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.4", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" } }, - "node_modules/array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", + "node_modules/@types/eslint": { + "version": "8.56.0", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" } }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "node_modules/@types/eslint-scope": { + "version": "3.7.7", "dev": true, + "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@types/eslint": "*", + "@types/estree": "*" } }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "node_modules/@types/estree": { + "version": "1.0.5", "dev": true, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "node_modules/@types/express": { + "version": "4.17.21", "dev": true, + "license": "MIT", "dependencies": { - "safer-buffer": "~2.1.0" + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" } }, - "node_modules/asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "node_modules/@types/express-serve-static-core": { + "version": "4.17.41", "dev": true, + "license": "MIT", "dependencies": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" } }, - "node_modules/asn1.js/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true + "node_modules/@types/http-errors": { + "version": "2.0.4", + "dev": true, + "license": "MIT" }, - "node_modules/assert": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.1.tgz", - "integrity": "sha512-zzw1uCAgLbsKwBfFc8CX78DDg+xZeBksSO3vwVIDDN5i94eOrPsSSyiVhmsSABFDM/OcpE2aagCat9dnWQLG1A==", + "node_modules/@types/http-proxy": { + "version": "1.17.14", "dev": true, + "license": "MIT", "dependencies": { - "object.assign": "^4.1.4", - "util": "^0.10.4" + "@types/node": "*" } }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "node_modules/@types/json-schema": { + "version": "7.0.15", "dev": true, - "engines": { - "node": ">=0.8" - } + "license": "MIT" }, - "node_modules/assert/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "dev": true + "node_modules/@types/mime": { + "version": "1.3.5", + "dev": true, + "license": "MIT" }, - "node_modules/assert/node_modules/util": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", - "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "node_modules/@types/minimist": { + "version": "1.2.5", "dev": true, - "dependencies": { - "inherits": "2.0.3" - } + "license": "MIT" }, - "node_modules/assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "node_modules/@types/node": { + "version": "20.10.5", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" } }, - "node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "node_modules/@types/node-forge": { + "version": "1.3.10", "dev": true, + "license": "MIT", "dependencies": { - "lodash": "^4.17.14" + "@types/node": "*" } }, - "node_modules/async-each": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.6.tgz", - "integrity": "sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==", + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "optional": true + "license": "MIT" }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "node_modules/@types/qs": { + "version": "6.9.10", + "dev": true, + "license": "MIT" }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "node_modules/@types/range-parser": { + "version": "1.2.7", "dev": true, - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } + "license": "MIT" }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "node_modules/@types/retry": { + "version": "0.12.0", "dev": true, - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "MIT" }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "node_modules/@types/send": { + "version": "0.17.4", "dev": true, - "engines": { - "node": "*" + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" } }, - "node_modules/aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", - "dev": true - }, - "node_modules/babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==", + "node_modules/@types/serve-index": { + "version": "1.9.4", "dev": true, + "license": "MIT", "dependencies": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" + "@types/express": "*" } }, - "node_modules/babel-code-frame/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "node_modules/@types/serve-static": { + "version": "1.15.5", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/mime": "*", + "@types/node": "*" } }, - "node_modules/babel-code-frame/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "node_modules/@types/sockjs": { + "version": "0.3.36", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, - "node_modules/babel-code-frame/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "node_modules/@types/ws": { + "version": "8.5.10", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" + "@types/node": "*" } }, - "node_modules/babel-code-frame/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "node_modules/@webassemblyjs/ast": { + "version": "1.11.6", "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, - "node_modules/babel-code-frame/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", "dev": true, - "engines": { - "node": ">=0.8.0" - } + "license": "MIT" }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "dev": true, + "license": "MIT" }, - "node_modules/base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.6", "dev": true, - "dependencies": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/base/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", "dev": true, + "license": "MIT", "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" } }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "license": "MIT" }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.6", "dev": true, + "license": "MIT", "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/before-after-hook": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", - "dev": true - }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "engines": { - "node": "*" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", "dev": true, - "optional": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" } }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", "dev": true, - "optional": true, + "license": "Apache-2.0", "dependencies": { - "file-uri-to-path": "1.0.0" + "@xtuc/long": "4.2.2" } }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "node_modules/bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", - "dev": true - }, - "node_modules/bottleneck": { - "version": "2.19.5", - "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", - "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", - "dev": true + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "dev": true, + "license": "MIT" }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.6", "dev": true, + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" } }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.6", "dev": true, + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, - "node_modules/brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", - "dev": true - }, - "node_modules/browser-stdout": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", - "integrity": "sha512-7Rfk377tpSM9TWBEeHs0FlDZGoAIei2V/4MdZJoFMBFAK6BqLpxAIUepGRHGdPFgGsLb02PXovC4qddyHvQqTg==", - "dev": true - }, - "node_modules/browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.6", "dev": true, + "license": "MIT", "dependencies": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" } }, - "node_modules/browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.6", "dev": true, + "license": "MIT", "dependencies": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, - "node_modules/browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.6", "dev": true, + "license": "MIT", "dependencies": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "@webassemblyjs/ast": "1.11.6", + "@xtuc/long": "4.2.2" } }, - "node_modules/browserify-rsa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", - "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "node_modules/@webpack-cli/configtest": { + "version": "1.2.0", "dev": true, - "dependencies": { - "bn.js": "^5.0.0", - "randombytes": "^2.0.1" + "license": "MIT", + "peerDependencies": { + "webpack": "4.x.x || 5.x.x", + "webpack-cli": "4.x.x" } }, - "node_modules/browserify-sign": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", - "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", + "node_modules/@webpack-cli/info": { + "version": "1.5.0", "dev": true, + "license": "MIT", "dependencies": { - "bn.js": "^5.2.1", - "browserify-rsa": "^4.1.0", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "elliptic": "^6.5.5", - "hash-base": "~3.0", - "inherits": "^2.0.4", - "parse-asn1": "^5.1.7", - "readable-stream": "^2.3.8", - "safe-buffer": "^5.2.1" + "envinfo": "^7.7.3" }, - "engines": { - "node": ">= 0.12" + "peerDependencies": { + "webpack-cli": "4.x.x" } }, - "node_modules/browserify-sign/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "node_modules/@webpack-cli/serve": { + "version": "1.7.0", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" + "license": "MIT", + "peerDependencies": { + "webpack-cli": "4.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true } - ] - }, - "node_modules/browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "dependencies": { - "pako": "~1.0.5" } }, - "node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", "dev": true, - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } + "license": "BSD-3-Clause" }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", - "dev": true - }, - "node_modules/builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", - "dev": true + "node_modules/@xtuc/long": { + "version": "4.2.2", + "dev": true, + "license": "Apache-2.0" }, - "node_modules/cacache": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", - "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", + "node_modules/abab": { + "version": "2.0.6", "dev": true, - "dependencies": { - "bluebird": "^3.5.1", - "chownr": "^1.0.1", - "glob": "^7.1.2", - "graceful-fs": "^4.1.11", - "lru-cache": "^4.1.1", - "mississippi": "^2.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^5.2.4", - "unique-filename": "^1.1.0", - "y18n": "^4.0.0" - } + "license": "BSD-3-Clause" }, - "node_modules/cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "node_modules/accepts": { + "version": "1.3.8", "dev": true, + "license": "MIT", "dependencies": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "node_modules/acorn": { + "version": "8.11.2", "dev": true, - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "license": "MIT", + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.4.0" } }, - "node_modules/caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha512-UJiE1otjXPF5/x+T3zTnSFiTOEmJoGTD9HmBoxnCUwho61a2eSNn/VwtwuIBDAo2SEOv1AJ7ARI5gCmohFLu/g==", + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-jsx": { + "version": "3.0.1", "dev": true, + "license": "MIT", "dependencies": { - "callsites": "^0.2.0" - }, - "engines": { - "node": ">=0.10.0" + "acorn": "^3.0.4" } }, - "node_modules/caller-path/node_modules/callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha512-Zv4Dns9IbXXmPkgRRUjAaJQgfN4xX5p6+RQFhWUqscdvvK2xK/ZL8b3IXIJsj+4sD+f24NwnWy2BY8AJ82JB0A==", + "node_modules/acorn-jsx/node_modules/acorn": { + "version": "3.3.0", "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, "engines": { - "node": ">=0.10.0" + "node": ">=0.4.0" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/acorn-walk": { + "version": "8.3.2", "dev": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=0.4.0" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, + "node_modules/agent-base": { + "version": "7.1.3", + "license": "MIT", "engines": { - "node": ">=6" + "node": ">= 14" } }, - "node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "node_modules/aggregate-error": { + "version": "5.0.0", "dev": true, + "license": "MIT", "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" + "clean-stack": "^5.2.0", + "indent-string": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cardinal": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", - "integrity": "sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==", - "dev": true, - "dependencies": { - "ansicolors": "~0.3.2", - "redeyed": "~2.1.0" - }, - "bin": { - "cdl": "bin/cdl.js" - } - }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/aggregate-error/node_modules/indent-string": { + "version": "5.0.0", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha512-j/Toj7f1z98Hh2cYo2BVr85EpIRWqUi7rtRSGxh/cqUjqrnJe9l9UE7IUGd2vQ2p+kSHLkSzObQPZPLUC6TQwg==", - "dev": true - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "node_modules/ajv": { + "version": "6.12.6", "dev": true, - "optional": true, + "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "deprecated": "CircularJSON is in maintenance only, flatted is its successor.", - "dev": true - }, - "node_modules/class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "node_modules/ajv-formats": { + "version": "2.1.1", "dev": true, + "license": "MIT", "dependencies": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" + "ajv": "^8.0.0" }, - "engines": { - "node": ">=0.10.0" + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/class-utils/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.12.0", "dev": true, + "license": "MIT", "dependencies": { - "is-descriptor": "^0.1.0" + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/class-utils/node_modules/is-descriptor": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", - "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } + "license": "MIT" }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "node_modules/ajv-keywords": { + "version": "3.5.2", "dev": true, - "engines": { - "node": ">=6" + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" } }, - "node_modules/cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "node_modules/ansi-escapes": { + "version": "3.2.0", "dev": true, - "dependencies": { - "restore-cursor": "^2.0.0" - }, + "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/cli-table3": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.4.tgz", - "integrity": "sha512-Lm3L0p+/npIQWNIiyF/nAn7T5dnOwR3xNTHXYEBFBFVPXzCVNZ5lqEC/1eo/EVfpDsQ1I+TX4ORPQgp+UI0CRw==", + "node_modules/ansi-html-community": { + "version": "0.0.8", "dev": true, - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" + "engines": [ + "node >= 0.8.0" + ], + "license": "Apache-2.0", + "bin": { + "ansi-html": "bin/ansi-html" } }, - "node_modules/cli-table3/node_modules/ansi-regex": { + "node_modules/ansi-regex": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-table3/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/cli-table3/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/ansi-styles": { + "version": "4.3.0", "dev": true, + "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "color-convert": "^2.0.1" }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/cli-table3/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/ansicolors": { + "version": "0.3.2", "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "dev": true, + "license": "ISC", "dependencies": { - "ansi-regex": "^5.0.1" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" }, "engines": { - "node": ">=8" + "node": ">= 8" } }, - "node_modules/cli-width": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", - "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", - "dev": true + "node_modules/arg": { + "version": "4.1.3", + "dev": true, + "license": "MIT" }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "node_modules/argparse": { + "version": "2.0.1", "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } + "license": "Python-2.0" }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/argv-formatter": { + "version": "1.0.0", "dev": true, - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/cliui/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/array-flatten": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/array-ify": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/array-union": { + "version": "2.1.0", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/arrify": { + "version": "1.0.1", "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/babel-code-frame": { + "version": "6.26.0", "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" } }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "node_modules/babel-code-frame/node_modules/ansi-regex": { + "version": "2.1.1", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.8" + "node": ">=0.10.0" } }, - "node_modules/clone-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", - "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", + "node_modules/babel-code-frame/node_modules/ansi-styles": { + "version": "2.2.1", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">=0.10.0" } }, - "node_modules/clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==", - "dev": true - }, - "node_modules/cloneable-readable": { + "node_modules/babel-code-frame/node_modules/chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", - "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", "dev": true, + "license": "MIT", "dependencies": { - "inherits": "^2.0.1", - "process-nextick-args": "^2.0.0", - "readable-stream": "^2.3.5" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "node_modules/babel-code-frame/node_modules/js-tokens": { + "version": "3.0.2", "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } + "license": "MIT" }, - "node_modules/collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", + "node_modules/babel-code-frame/node_modules/strip-ansi": { + "version": "3.0.1", "dev": true, + "license": "MIT", "dependencies": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "ansi-regex": "^2.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/collections": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/collections/-/collections-0.2.2.tgz", - "integrity": "sha512-XMGG5GPXUnjERaZzrBIfJo3iY3ck2ChSlL73iRk0UrT39Ei0HaKxhWL4NdrFjF72SCI/QGGa3U5CnN0BgbSgnw==", + "node_modules/babel-code-frame/node_modules/supports-color": { + "version": "2.0.0", "dev": true, - "dependencies": { - "weak-map": "1.0.0" + "license": "MIT", + "engines": { + "node": ">=0.8.0" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/balanced-match": { + "version": "1.0.2", "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } + "license": "MIT" }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "node_modules/batch": { + "version": "0.6.1", + "dev": true, + "license": "MIT" + }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "dev": true, + "license": "Apache-2.0" }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/binary-extensions": { + "version": "2.2.0", "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/blockly": { + "version": "12.3.0-beta.0", + "resolved": "https://registry.npmjs.org/blockly/-/blockly-12.3.0-beta.0.tgz", + "integrity": "sha512-QXD6dXbsYFLzBs9umZRDhpllkM3Mdxtc9JnDMKksLzEFdFbl2CDl2JaKaw/rP5B0InpD1UrZPF7xT1ZRvJLS9w==", + "license": "Apache-2.0", "dependencies": { - "delayed-stream": "~1.0.0" + "jsdom": "26.1.0" }, "engines": { - "node": ">= 0.8" + "node": ">=18" } }, - "node_modules/commander": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==", + "node_modules/body-parser": { + "version": "1.20.1", "dev": true, + "license": "MIT", "dependencies": { - "graceful-readlink": ">= 1.0.0" + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.6.x" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, - "node_modules/compare-func": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", "dev": true, - "dependencies": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" + "license": "MIT", + "engines": { + "node": ">= 0.8" } }, - "node_modules/component-emitter": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", - "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", "dev": true, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "license": "MIT", + "dependencies": { + "ms": "2.0.0" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.4.24", "dev": true, - "engines": [ - "node >= 0.8" - ], + "license": "MIT", "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/config-chain": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/bonjour-service": { + "version": "1.1.1", "dev": true, + "license": "MIT", "dependencies": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" } }, - "node_modules/console-browserify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", - "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", - "dev": true - }, - "node_modules/constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", - "dev": true + "node_modules/bottleneck": { + "version": "2.19.5", + "dev": true, + "license": "MIT" }, - "node_modules/conventional-changelog-angular": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", - "integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==", + "node_modules/brace-expansion": { + "version": "1.1.11", "dev": true, + "license": "MIT", "dependencies": { - "compare-func": "^2.0.0" - }, - "engines": { - "node": ">=14" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/conventional-changelog-conventionalcommits": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-6.1.0.tgz", - "integrity": "sha512-3cS3GEtR78zTfMzk0AizXKKIdN4OvSh7ibNz6/DPbhWWQu7LqE/8+/GqSodV+sywUR2gpJAdP/1JFf4XtN7Zpw==", + "node_modules/braces": { + "version": "3.0.2", "dev": true, + "license": "MIT", "dependencies": { - "compare-func": "^2.0.0" + "fill-range": "^7.0.1" }, "engines": { - "node": ">=14" + "node": ">=8" } }, - "node_modules/conventional-changelog-writer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz", - "integrity": "sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==", + "node_modules/browserslist": { + "version": "4.22.2", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "dependencies": { - "conventional-commits-filter": "^2.0.7", - "dateformat": "^3.0.0", - "handlebars": "^4.7.7", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "semver": "^6.0.0", - "split": "^1.0.0", - "through2": "^4.0.0" + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" }, "bin": { - "conventional-changelog-writer": "cli.js" + "browserslist": "cli.js" }, "engines": { - "node": ">=10" + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/conventional-changelog-writer/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/buffer-from": { + "version": "1.1.2", "dev": true, - "bin": { - "semver": "bin/semver.js" - } + "license": "MIT" }, - "node_modules/conventional-commits-filter": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", - "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", + "node_modules/bytes": { + "version": "3.0.0", "dev": true, - "dependencies": { - "lodash.ismatch": "^4.4.0", - "modify-values": "^1.0.0" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 0.8" } }, - "node_modules/conventional-commits-parser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", - "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", + "node_modules/call-bind": { + "version": "1.0.5", "dev": true, + "license": "MIT", "dependencies": { - "is-text-path": "^1.0.1", - "JSONStream": "^1.3.5", - "meow": "^8.1.2", - "split2": "^3.2.2" - }, - "bin": { - "conventional-commits-parser": "cli.js" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" }, - "engines": { - "node": ">=14" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "node_modules/caller-path": { + "version": "0.1.0", "dev": true, + "license": "MIT", "dependencies": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" + "callsites": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", + "node_modules/caller-path/node_modules/callsites": { + "version": "0.2.0", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/copy-webpack-plugin": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz", - "integrity": "sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==", + "node_modules/callsites": { + "version": "3.1.0", "dev": true, - "dependencies": { - "cacache": "^10.0.4", - "find-cache-dir": "^1.0.0", - "globby": "^7.1.1", - "is-glob": "^4.0.0", - "loader-utils": "^1.1.0", - "minimatch": "^3.0.4", - "p-limit": "^1.0.0", - "serialize-javascript": "^1.4.0" - }, + "license": "MIT", "engines": { - "node": ">= 4" + "node": ">=6" } }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true + "node_modules/camelcase": { + "version": "5.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "node_modules/cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "node_modules/camelcase-keys": { + "version": "6.2.2", "dev": true, + "license": "MIT", "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" }, "engines": { - "node": ">=14" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cosmiconfig-typescript-loader": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.4.0.tgz", - "integrity": "sha512-BabizFdC3wBHhbI4kJh0VkQP9GkBfoHPydD0COMce1nJ1kJAB3F2TmJ/I7diULBKtmEWSwEbuN/KDtgnmUUVmw==", + "node_modules/caniuse-lite": { + "version": "1.0.30001570", "dev": true, - "engines": { - "node": ">=v14.21.3" - }, - "peerDependencies": { - "@types/node": "*", - "cosmiconfig": ">=7", - "ts-node": ">=10", - "typescript": ">=4" - } + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" }, - "node_modules/create-ecdh": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", - "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "node_modules/cardinal": { + "version": "2.1.1", "dev": true, + "license": "MIT", "dependencies": { - "bn.js": "^4.1.0", - "elliptic": "^6.5.3" + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" + }, + "bin": { + "cdl": "bin/cdl.js" } }, - "node_modules/create-ecdh/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "node_modules/create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "node_modules/chalk": { + "version": "4.1.2", "dev": true, + "license": "MIT", "dependencies": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", "dev": true, + "license": "MIT", "dependencies": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", + "node_modules/char-regex": { + "version": "1.0.2", "dev": true, - "dependencies": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "license": "MIT", + "engines": { + "node": ">=10" } }, - "node_modules/crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "node_modules/chardet": { + "version": "0.4.2", + "dev": true, + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "3.5.3", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", "dependencies": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "engines": { - "node": "*" + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "node_modules/chrome-trace-event": { + "version": "1.0.3", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=6.0" } }, - "node_modules/cyclist": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.2.tgz", - "integrity": "sha512-0sVXIohTfLqVIW3kb/0n6IiWF3Ifj5nm2XaSrLq2DI6fKIGa2fYAZdk917rUneaeLVpYfFcyXE2ft0fe3remsA==", - "dev": true - }, - "node_modules/dargs": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", - "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", + "node_modules/circular-json": { + "version": "0.3.3", "dev": true, - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "node_modules/clean-stack": { + "version": "5.2.0", "dev": true, + "license": "MIT", "dependencies": { - "assert-plus": "^1.0.0" + "escape-string-regexp": "5.0.0" }, "engines": { - "node": ">=0.10" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "node_modules/clean-stack/node_modules/escape-string-regexp": { + "version": "5.0.0", "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "node_modules/cli-cursor": { + "version": "2.1.0", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "restore-cursor": "^2.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "node_modules/cli-table3": { + "version": "0.6.5", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "string-width": "^4.2.0" }, "engines": { - "node": ">= 0.4" + "node": "10.* || >= 12.*" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optionalDependencies": { + "@colors/colors": "1.5.0" } }, - "node_modules/dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "node_modules/cli-width": { + "version": "2.2.1", "dev": true, - "engines": { - "node": "*" - } + "license": "ISC" }, - "node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/cliui": { + "version": "8.0.1", "dev": true, + "license": "ISC", "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "node_modules/decamelize-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", - "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "node_modules/clone-deep": { + "version": "4.0.1", "dev": true, + "license": "MIT", "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" }, "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "node_modules/co": { + "version": "4.6.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" } }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "node_modules/color-convert": { + "version": "2.0.1", "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, "engines": { - "node": ">=0.10" + "node": ">=7.0.0" } }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "node_modules/color-name": { + "version": "1.1.4", "dev": true, - "engines": { - "node": ">=4.0.0" - } + "license": "MIT" }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "node_modules/colorette": { + "version": "2.0.20", + "dev": true, + "license": "MIT" }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "node_modules/commander": { + "version": "2.20.3", "dev": true, - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "MIT" }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "node_modules/compare-func": { + "version": "2.0.0", "dev": true, + "license": "MIT", "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" } }, - "node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "node_modules/compressible": { + "version": "2.0.18", "dev": true, + "license": "MIT", "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" + "mime-db": ">= 1.43.0 < 2" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/del": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", - "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", + "node_modules/compression": { + "version": "1.7.4", "dev": true, + "license": "MIT", "dependencies": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.8.0" } }, - "node_modules/del/node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", "dev": true, - "engines": { - "node": ">=8" + "license": "MIT", + "dependencies": { + "ms": "2.0.0" } }, - "node_modules/del/node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/del/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "license": "MIT" }, - "node_modules/del/node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "node_modules/concat-map": { + "version": "0.0.1", "dev": true, - "engines": { - "node": ">= 4" - } + "license": "MIT" }, - "node_modules/del/node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "node_modules/concat-stream": { + "version": "1.6.2", "dev": true, + "engines": [ + "node >= 0.8" + ], + "license": "MIT", "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" } }, - "node_modules/del/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", "dev": true, + "license": "MIT", "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "node_modules/del/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", "dev": true, - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", "dev": true, - "engines": { - "node": ">=0.4.0" + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" } }, - "node_modules/deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", - "dev": true - }, - "node_modules/des.js": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", - "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "node_modules/config-chain": { + "version": "1.1.13", "dev": true, + "license": "MIT", "dependencies": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "ini": "^1.3.4", + "proto-list": "~1.2.1" } }, - "node_modules/detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=0.8" } }, - "node_modules/diff": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", - "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", + "node_modules/content-disposition": { + "version": "0.5.4", "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, "engines": { - "node": ">=0.3.1" + "node": ">= 0.6" } }, - "node_modules/diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "node_modules/content-type": { + "version": "1.0.5", "dev": true, - "dependencies": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" + "license": "MIT", + "engines": { + "node": ">= 0.6" } }, - "node_modules/diffie-hellman/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "node_modules/dir-glob": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", - "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "node_modules/conventional-changelog-angular": { + "version": "6.0.0", "dev": true, + "license": "ISC", "dependencies": { - "path-type": "^3.0.0" + "compare-func": "^2.0.0" }, "engines": { - "node": ">=4" + "node": ">=14" } }, - "node_modules/dir-glob/node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "node_modules/conventional-changelog-conventionalcommits": { + "version": "6.1.0", "dev": true, + "license": "ISC", "dependencies": { - "pify": "^3.0.0" + "compare-func": "^2.0.0" }, "engines": { - "node": ">=4" + "node": ">=14" } }, - "node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "node_modules/conventional-changelog-writer": { + "version": "7.0.1", "dev": true, + "license": "MIT", "dependencies": { - "esutils": "^2.0.2" + "conventional-commits-filter": "^4.0.0", + "handlebars": "^4.7.7", + "json-stringify-safe": "^5.0.1", + "meow": "^12.0.1", + "semver": "^7.5.2", + "split2": "^4.0.0" + }, + "bin": { + "conventional-changelog-writer": "cli.mjs" }, "engines": { - "node": ">=0.10.0" + "node": ">=16" } }, - "node_modules/domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "node_modules/conventional-changelog-writer/node_modules/meow": { + "version": "12.1.1", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.4", - "npm": ">=1.2" + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "node_modules/conventional-changelog-writer/node_modules/split2": { + "version": "4.2.0", "dev": true, - "dependencies": { - "is-obj": "^2.0.0" - }, + "license": "ISC", "engines": { - "node": ">=8" + "node": ">= 10.x" } }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, - "node_modules/duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "node_modules/conventional-commits-filter": { + "version": "4.0.0", "dev": true, - "dependencies": { - "readable-stream": "^2.0.2" + "license": "MIT", + "engines": { + "node": ">=16" } }, - "node_modules/duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "node_modules/conventional-commits-parser": { + "version": "4.0.0", "dev": true, + "license": "MIT", "dependencies": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" + "is-text-path": "^1.0.1", + "JSONStream": "^1.3.5", + "meow": "^8.1.2", + "split2": "^3.2.2" + }, + "bin": { + "conventional-commits-parser": "cli.js" + }, + "engines": { + "node": ">=14" } }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "node_modules/cookie": { + "version": "0.5.0", "dev": true, - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" + "license": "MIT", + "engines": { + "node": ">= 0.6" } }, - "node_modules/elliptic": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.5.tgz", - "integrity": "sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw==", + "node_modules/cookie-signature": { + "version": "1.0.6", "dev": true, - "dependencies": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/elliptic/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "engines": { - "node": ">= 4" - } + "license": "MIT" }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "node_modules/core-util-is": { + "version": "1.0.3", "dev": true, - "dependencies": { - "once": "^1.4.0" - } + "license": "MIT" }, - "node_modules/enhanced-resolve": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", - "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "node_modules/cosmiconfig": { + "version": "8.3.6", "dev": true, + "license": "MIT", "dependencies": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.5.0", - "tapable": "^1.0.0" + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" }, "engines": { - "node": ">=6.9.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/enhanced-resolve/node_modules/memory-fs": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", - "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "node_modules/cosmiconfig-typescript-loader": { + "version": "4.4.0", "dev": true, - "dependencies": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - }, + "license": "MIT", "engines": { - "node": ">=4.3.0 <5.0.0 || >=5.10" + "node": ">=v14.21.3" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=7", + "ts-node": ">=10", + "typescript": ">=4" } }, - "node_modules/env-ci": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-5.5.0.tgz", - "integrity": "sha512-o0JdWIbOLP+WJKIUt36hz1ImQQFuN92nhsfTkHHap+J8CiI8WgGpH/a9jEGHh4/TU5BUUGjlnKXNoDb57+ne+A==", + "node_modules/create-require": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.3", "dev": true, + "license": "MIT", "dependencies": { - "execa": "^5.0.0", - "fromentries": "^1.3.2", - "java-properties": "^1.0.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">=10.17" + "node": ">= 8" } }, - "node_modules/errno": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", - "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "node_modules/crypto-random-string": { + "version": "4.0.0", "dev": true, + "license": "MIT", "dependencies": { - "prr": "~1.0.1" + "type-fest": "^1.0.1" }, - "bin": { - "errno": "cli.js" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" - }, + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dev": true, + "node_modules/cssstyle": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4" + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" }, "engines": { - "node": ">= 0.4" + "node": ">=18" } }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "node_modules/dargs": { + "version": "7.0.0", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "license": "MIT", "dependencies": { - "es-errors": "^1.3.0" + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=18" } }, - "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "node_modules/dateformat": { + "version": "3.0.3", "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "ms": "2.1.2" }, "engines": { - "node": ">= 0.4" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "node_modules/decamelize": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", "dev": true, + "license": "MIT", "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", "dev": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "license": "MIT" + }, + "node_modules/deep-extend": { + "version": "0.6.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.8.0" + "node": ">=4.0.0" } }, - "node_modules/eslint": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", - "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", + "node_modules/deep-is": { + "version": "0.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/default-gateway": { + "version": "6.0.3", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "ajv": "^5.3.0", - "babel-code-frame": "^6.22.0", - "chalk": "^2.1.0", - "concat-stream": "^1.6.0", - "cross-spawn": "^5.1.0", - "debug": "^3.1.0", - "doctrine": "^2.1.0", - "eslint-scope": "^3.7.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^3.5.4", - "esquery": "^1.0.0", - "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.0.1", - "ignore": "^3.3.3", - "imurmurhash": "^0.1.4", - "inquirer": "^3.0.6", - "is-resolvable": "^1.0.0", - "js-yaml": "^3.9.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.4", - "minimatch": "^3.0.2", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", - "progress": "^2.0.0", - "regexpp": "^1.0.1", - "require-uncached": "^1.0.3", - "semver": "^5.3.0", - "strip-ansi": "^4.0.0", - "strip-json-comments": "~2.0.1", - "table": "4.0.2", - "text-table": "~0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" + "execa": "^5.0.0" }, "engines": { - "node": ">=4" + "node": ">= 10" } }, - "node_modules/eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", + "node_modules/define-data-property": { + "version": "1.1.1", "dev": true, + "license": "MIT", "dependencies": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" }, "engines": { - "node": ">=4.0.0" + "node": ">= 0.4" } }, - "node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "node_modules/define-lazy-prop": { + "version": "2.0.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/eslint/node_modules/ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha512-Ajr4IcMXq/2QmMkEmSvxqfLN5zGmJ92gHXAeOXq1OekoH2rfDNsgdDoL2f7QaRCy7G/E6TpxBVdRuNraMztGHw==", - "dev": true, + "node_modules/del": { + "version": "6.1.1", + "dev": true, + "license": "MIT", "dependencies": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/del/node_modules/aggregate-error": { + "version": "3.1.0", "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/eslint/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/del/node_modules/clean-stack": { + "version": "2.2.0", "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" + "license": "MIT", + "engines": { + "node": ">=6" } }, - "node_modules/eslint/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/del/node_modules/globby": { + "version": "11.1.0", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/del/node_modules/ignore": { + "version": "5.3.2", "dev": true, - "dependencies": { - "color-name": "1.1.3" + "license": "MIT", + "engines": { + "node": ">= 4" } }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/eslint/node_modules/fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha512-fueX787WZKCV0Is4/T2cyAdM4+x1S3MXXOAhavE1ys/W42SHAPacLTQhucja22QBYrfGw50M2sRiXPtTGv9Ymw==", - "dev": true + "node_modules/del/node_modules/p-map": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/eslint/node_modules/has-flag": { + "node_modules/del/node_modules/slash": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "node_modules/depd": { + "version": "2.0.0", "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "license": "MIT", + "engines": { + "node": ">= 0.8" } }, - "node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha512-4JD/Ivzg7PoW8NzdrBSr3UFwC9mHgvI7Z6z3QGBsSHgKaRTUDmyZAAKJo2UbG1kUVfS9WS8bi36N49U1xw43DA==", - "dev": true + "node_modules/deprecation": { + "version": "2.3.1", + "dev": true, + "license": "ISC" }, - "node_modules/eslint/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/destroy": { + "version": "1.2.0", "dev": true, - "bin": { - "semver": "bin/semver" + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/eslint/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/detect-node": { + "version": "2.1.0", "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, + "license": "MIT" + }, + "node_modules/diff": { + "version": "4.0.2", + "dev": true, + "license": "BSD-3-Clause", "engines": { - "node": ">=4" + "node": ">=0.3.1" } }, - "node_modules/espree": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", - "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", + "node_modules/dir-glob": { + "version": "3.0.1", "dev": true, + "license": "MIT", "dependencies": { - "acorn": "^5.5.0", - "acorn-jsx": "^3.0.0" + "path-type": "^4.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "node_modules/dns-equal": { + "version": "1.0.0", "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" + "license": "MIT" + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=6" } }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "node_modules/doctrine": { + "version": "2.1.0", "dev": true, + "license": "Apache-2.0", "dependencies": { - "estraverse": "^5.1.0" + "esutils": "^2.0.2" }, "engines": { - "node": ">=0.10" + "node": ">=0.10.0" } }, - "node_modules/esquery/node_modules/estraverse": { + "node_modules/dot-prop": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, "engines": { - "node": ">=4.0" + "node": ">=8" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/duplexer2": { + "version": "0.1.4", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" + "readable-stream": "^2.0.2" } }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.8", "dev": true, - "engines": { - "node": ">=4.0" + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "node_modules/duplexer2/node_modules/safe-buffer": { + "version": "5.1.2", "dev": true, - "engines": { - "node": ">=4.0" + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "node_modules/ee-first": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.4.615", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/emojilib": { + "version": "2.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "1.0.2", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.8" } }, - "node_modules/event-stream": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.5.tgz", - "integrity": "sha512-vyibDcu5JL20Me1fP734QBH/kenBGLZap2n0+XXM7mvuUPzJ20Ydqj1aKcIeMdri1p+PU+4yAKugjN8KCVst+g==", + "node_modules/enhanced-resolve": { + "version": "5.15.0", "dev": true, + "license": "MIT", "dependencies": { - "duplexer": "^0.1.1", - "from": "^0.1.7", - "map-stream": "0.0.7", - "pause-stream": "^0.0.11", - "split": "^1.0.1", - "stream-combiner": "^0.2.2", - "through": "^2.3.8" + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" } }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", "engines": { - "node": ">=0.8.x" + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "node_modules/env-ci": { + "version": "10.0.0", "dev": true, + "license": "MIT", "dependencies": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" + "execa": "^8.0.0", + "java-properties": "^1.0.2" + }, + "engines": { + "node": "^18.17 || >=20.6.1" } }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "node_modules/env-ci/node_modules/execa": { + "version": "8.0.1", "dev": true, + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" }, "engines": { - "node": ">=10" + "node": ">=16.17" }, "funding": { "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/execa/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "node_modules/env-ci/node_modules/get-stream": { + "version": "8.0.1", "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, + "license": "MIT", "engines": { - "node": ">= 8" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/execa/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/env-ci/node_modules/human-signals": { + "version": "5.0.0", "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, + "license": "Apache-2.0", "engines": { - "node": ">=8" + "node": ">=16.17.0" } }, - "node_modules/execa/node_modules/shebang-regex": { + "node_modules/env-ci/node_modules/is-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/execa/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/env-ci/node_modules/mimic-fn": { + "version": "4.0.0", "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, + "license": "MIT", "engines": { - "node": ">= 8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", + "node_modules/env-ci/node_modules/npm-run-path": { + "version": "5.3.0", "dev": true, + "license": "MIT", "dependencies": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "path-key": "^4.0.0" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/expand-brackets/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "node_modules/env-ci/node_modules/onetime": { + "version": "6.0.0", "dev": true, + "license": "MIT", "dependencies": { - "is-descriptor": "^0.1.0" + "mimic-fn": "^4.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/expand-brackets/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/env-ci/node_modules/path-key": { + "version": "4.0.0", "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/expand-brackets/node_modules/is-descriptor": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", - "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "node_modules/env-ci/node_modules/signal-exit": { + "version": "4.1.0", "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" - }, + "license": "ISC", "engines": { - "node": ">= 0.4" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/expand-brackets/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/env-ci/node_modules/strip-final-newline": { + "version": "3.0.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/expand-brackets/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "node_modules/envinfo": { + "version": "7.11.0", "dev": true, - "dependencies": { - "homedir-polyfill": "^1.0.1" + "license": "MIT", + "bin": { + "envinfo": "dist/cli.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/exports-loader": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/exports-loader/-/exports-loader-0.7.0.tgz", - "integrity": "sha512-RKwCrO4A6IiKm0pG3c9V46JxIHcDplwwGJn6+JJ1RcVnh/WSGJa0xkmk5cRVtgOPzCAtTMGj2F7nluh9L0vpSA==", + "node_modules/error-ex": { + "version": "1.3.2", + "dev": true, + "license": "MIT", "dependencies": { - "loader-utils": "^1.1.0", - "source-map": "0.5.0" - }, - "engines": { - "node": ">= 4" + "is-arrayish": "^0.2.1" } }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true + "node_modules/es-module-lexer": { + "version": "1.4.1", + "dev": true, + "license": "MIT" }, - "node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "node_modules/escalade": { + "version": "3.1.1", "dev": true, - "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "node_modules/escape-html": { + "version": "1.0.3", "dev": true, - "dependencies": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=0.12" - } + "license": "MIT" }, - "node_modules/extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "node_modules/escape-string-regexp": { + "version": "1.0.5", "dev": true, - "dependencies": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=0.8.0" } }, - "node_modules/extglob/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "node_modules/eslint": { + "version": "4.19.1", "dev": true, + "license": "MIT", "dependencies": { - "is-descriptor": "^1.0.0" + "ajv": "^5.3.0", + "babel-code-frame": "^6.22.0", + "chalk": "^2.1.0", + "concat-stream": "^1.6.0", + "cross-spawn": "^5.1.0", + "debug": "^3.1.0", + "doctrine": "^2.1.0", + "eslint-scope": "^3.7.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^3.5.4", + "esquery": "^1.0.0", + "esutils": "^2.0.2", + "file-entry-cache": "^2.0.0", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.0.1", + "ignore": "^3.3.3", + "imurmurhash": "^0.1.4", + "inquirer": "^3.0.6", + "is-resolvable": "^1.0.0", + "js-yaml": "^3.9.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.4", + "minimatch": "^3.0.2", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "pluralize": "^7.0.0", + "progress": "^2.0.0", + "regexpp": "^1.0.1", + "require-uncached": "^1.0.3", + "semver": "^5.3.0", + "strip-ansi": "^4.0.0", + "strip-json-comments": "~2.0.1", + "table": "4.0.2", + "text-table": "~0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/extglob/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/eslint-scope": { + "version": "5.1.1", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "is-extendable": "^0.1.0" + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8.0.0" } }, - "node_modules/extglob/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/eslint-visitor-keys": { + "version": "1.3.0", "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "dev": true, - "engines": [ - "node >=0.6.0" - ] - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "node_modules/eslint/node_modules/ajv": { + "version": "5.5.2", "dev": true, + "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "node_modules/eslint/node_modules/ansi-regex": { + "version": "3.0.1", "dev": true, - "dependencies": { - "reusify": "^1.0.4" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "node_modules/figgy-pudding": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", - "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", - "deprecated": "This module is no longer supported.", - "dev": true - }, - "node_modules/figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "node_modules/eslint/node_modules/ansi-styles": { + "version": "3.2.1", "dev": true, + "license": "MIT", "dependencies": { - "escape-string-regexp": "^1.0.5" + "color-convert": "^1.9.0" }, "engines": { "node": ">=4" } }, - "node_modules/file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha512-uXP/zGzxxFvFfcZGgBIwotm+Tdc55ddPAzF7iHshP4YGaXMww7rSF9peD9D1sui5ebONg5UobsZv+FfgEpGv/w==", + "node_modules/eslint/node_modules/argparse": { + "version": "1.0.10", "dev": true, + "license": "MIT", "dependencies": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" + "sprintf-js": "~1.0.2" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "node_modules/eslint/node_modules/color-convert": { + "version": "1.9.3", "dev": true, - "optional": true + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/cross-spawn": { + "version": "5.1.0", "dev": true, + "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" } }, - "node_modules/find-cache-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", - "integrity": "sha512-46TFiBOzX7xq/PcSWfFwkyjpemdRnMe31UQF+os0y+1W3k95f6R4SEt02Hj4p3X0Mir9gfrkmOtshFidS0VPUg==", + "node_modules/eslint/node_modules/debug": { + "version": "3.2.7", "dev": true, + "license": "MIT", "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^1.0.0", - "pkg-dir": "^2.0.0" - }, - "engines": { - "node": ">=4" + "ms": "^2.1.1" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/eslint/node_modules/eslint-scope": { + "version": "3.7.3", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4.0.0" } }, - "node_modules/find-versions": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-4.0.0.tgz", - "integrity": "sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ==", + "node_modules/eslint/node_modules/fast-deep-equal": { + "version": "1.1.0", "dev": true, - "dependencies": { - "semver-regex": "^3.1.2" - }, + "license": "MIT" + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/findup-sync": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", - "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "node_modules/eslint/node_modules/js-yaml": { + "version": "3.14.1", "dev": true, + "license": "MIT", "dependencies": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, - "engines": { - "node": ">= 0.10" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/findup-sync/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/lru-cache": { + "version": "4.1.5", + "dev": true, + "license": "ISC", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/eslint/node_modules/semver": { + "version": "5.7.2", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/eslint/node_modules/shebang-command": { + "version": "1.2.0", "dev": true, + "license": "MIT", "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" + "shebang-regex": "^1.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/findup-sync/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/eslint/node_modules/shebang-regex": { + "version": "1.0.0", "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/findup-sync/node_modules/fill-range": { + "node_modules/eslint/node_modules/strip-ansi": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", "dev": true, + "license": "MIT", "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "ansi-regex": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/findup-sync/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/eslint/node_modules/supports-color": { + "version": "5.5.0", "dev": true, + "license": "MIT", "dependencies": { - "is-extendable": "^0.1.0" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/findup-sync/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/eslint/node_modules/which": { + "version": "1.3.1", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" } }, - "node_modules/findup-sync/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "node_modules/eslint/node_modules/yallist": { + "version": "2.1.2", + "dev": true, + "license": "ISC" + }, + "node_modules/espree": { + "version": "3.5.4", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "kind-of": "^3.0.2" + "acorn": "^5.5.0", + "acorn-jsx": "^3.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/findup-sync/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/espree/node_modules/acorn": { + "version": "5.7.4", "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" + "license": "MIT", + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.4.0" } }, - "node_modules/findup-sync/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "node_modules/esprima": { + "version": "4.0.1", "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/findup-sync/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "node_modules/esquery": { + "version": "1.5.0", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "estraverse": "^5.1.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.10" } }, - "node_modules/flat-cache": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz", - "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", "dev": true, - "dependencies": { - "circular-json": "^0.3.1", - "graceful-fs": "^4.1.2", - "rimraf": "~2.6.2", - "write": "^0.2.1" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">=0.10.0" + "node": ">=4.0" } }, - "node_modules/flat-cache/node_modules/rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "node_modules/esrecurse": { + "version": "4.3.0", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "glob": "^7.1.3" + "estraverse": "^5.2.0" }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">=4.0" } }, - "node_modules/flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" } }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "node_modules/estraverse": { + "version": "4.3.0", "dev": true, - "dependencies": { - "is-callable": "^1.1.3" + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" } }, - "node_modules/for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "node_modules/esutils": { + "version": "2.0.3", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "node_modules/etag": { + "version": "1.8.1", "dev": true, + "license": "MIT", "engines": { - "node": "*" + "node": ">= 0.6" } }, - "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "node_modules/eventemitter3": { + "version": "4.0.7", "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.12" + "node": ">=0.8.x" } }, - "node_modules/fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", + "node_modules/execa": { + "version": "5.1.1", "dev": true, + "license": "MIT", "dependencies": { - "map-cache": "^0.2.2" + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", - "dev": true - }, - "node_modules/from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "node_modules/express": { + "version": "4.18.2", "dev": true, + "license": "MIT", "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "node_modules/fromentries": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", - "integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==", + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "license": "MIT" }, - "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "node_modules/express/node_modules/debug": { + "version": "2.6.9", "dev": true, + "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" + "ms": "2.0.0" } }, - "node_modules/fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==", + "node_modules/express/node_modules/ms": { + "version": "2.0.0", "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "license": "MIT" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "node_modules/external-editor": { + "version": "2.2.0", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], + "license": "MIT", + "dependencies": { + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" + }, "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.12" } }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "node_modules/external-editor/node_modules/iconv-lite": { + "version": "0.4.24", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "node_modules/fast-deep-equal": { + "version": "3.1.3", "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } + "license": "MIT" }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "node_modules/fast-glob": { + "version": "3.3.2", "dev": true, + "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8.6.0" } }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "license": "MIT" }, - "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "node_modules/fast-levenshtein": { + "version": "2.0.6", "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "MIT" }, - "node_modules/get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", + "node_modules/fastest-levenshtein": { + "version": "1.0.16", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 4.9.1" } }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "node_modules/fastq": { + "version": "1.17.1", "dev": true, + "license": "ISC", "dependencies": { - "assert-plus": "^1.0.0" + "reusify": "^1.0.4" } }, - "node_modules/gh-pages": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-0.12.0.tgz", - "integrity": "sha512-dsSujljaK8VOdFDssfL8dNFC1uATjXqOVpberiE6pr3XT+kyIHDjpBpMteFw+dD59jFdg64d3vloQl/jbYUKDA==", + "node_modules/faye-websocket": { + "version": "0.11.4", "dev": true, + "license": "Apache-2.0", "dependencies": { - "async": "2.1.2", - "commander": "2.9.0", - "globby": "^6.1.0", - "graceful-fs": "4.1.10", - "q": "1.4.1", - "q-io": "1.13.2", - "rimraf": "^2.5.4" + "websocket-driver": ">=0.5.1" }, - "bin": { - "gh-pages": "bin/gh-pages" + "engines": { + "node": ">=0.8.0" } }, - "node_modules/gh-pages/node_modules/async": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/async/-/async-2.1.2.tgz", - "integrity": "sha512-i0Jx7SEZNG5i+F9hrUILpfDkuVJxf+UqmsS6LVn3UdUegQryKplU5t5opYYkDPW0eKBeJUSiiuphgkUZagx5ZQ==", + "node_modules/figures": { + "version": "2.0.0", "dev": true, + "license": "MIT", "dependencies": { - "lodash": "^4.14.0" + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" } }, - "node_modules/gh-pages/node_modules/globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "node_modules/file-entry-cache": { + "version": "2.0.0", "dev": true, + "license": "MIT", "dependencies": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "flat-cache": "^1.2.1", + "object-assign": "^4.0.1" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/gh-pages/node_modules/graceful-fs": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.10.tgz", - "integrity": "sha512-fUSlmTortW+/Fr7OuwVfhHAK3/8Q3J2BxjdHKD2pw9b7fSTEtUmf1Dxc+yByw7r/BDVJT1iWKoLXdAN+qpAKFw==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/gh-pages/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "node_modules/fill-range": { + "version": "7.0.1", "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/git-log-parser": { + "node_modules/finalhandler": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.0.tgz", - "integrity": "sha512-rnCVNfkTL8tdNryFuaY0fYiBWEBcgF748O6ZI61rslBvr2o7U65c2/6npCRqH40vuAhtgtDiqLTJjBVdrejCzA==", "dev": true, + "license": "MIT", "dependencies": { - "argv-formatter": "~1.0.0", - "spawn-error-forwarder": "~1.0.0", - "split2": "~1.0.0", - "stream-combiner2": "~1.1.1", - "through2": "~2.0.0", - "traverse": "~0.6.6" + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "node_modules/git-log-parser/node_modules/split2": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", - "integrity": "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==", + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", "dev": true, + "license": "MIT", "dependencies": { - "through2": "~2.0.0" + "ms": "2.0.0" } }, - "node_modules/git-log-parser/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } + "license": "MIT" }, - "node_modules/git-raw-commits": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", - "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==", + "node_modules/find-up": { + "version": "4.1.0", "dev": true, + "license": "MIT", "dependencies": { - "dargs": "^7.0.0", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" - }, - "bin": { - "git-raw-commits": "cli.js" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "node_modules/find-up-simple": { + "version": "1.0.0", "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, + "license": "MIT", "engines": { - "node": "*" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/find-versions": { + "version": "5.1.0", "dev": true, + "license": "MIT", "dependencies": { - "is-glob": "^4.0.1" + "semver-regex": "^4.0.5" }, "engines": { - "node": ">= 6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", + "node_modules/flat": { + "version": "5.0.2", "dev": true, - "dependencies": { - "ini": "^1.3.4" - }, - "engines": { - "node": ">=4" + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" } }, - "node_modules/global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "node_modules/flat-cache": { + "version": "1.3.4", "dev": true, + "license": "MIT", "dependencies": { - "global-prefix": "^3.0.0" + "circular-json": "^0.3.1", + "graceful-fs": "^4.1.2", + "rimraf": "~2.6.2", + "write": "^0.2.1" }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "node_modules/flat-cache/node_modules/rimraf": { + "version": "2.6.3", "dev": true, + "license": "ISC", "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" + "glob": "^7.1.3" }, - "engines": { - "node": ">=6" + "bin": { + "rimraf": "bin.js" } }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "node_modules/follow-redirects": { + "version": "1.15.3", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "node_modules/forwarded": { + "version": "0.2.0", "dev": true, - "dependencies": { - "define-properties": "^1.1.3" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.6" } }, - "node_modules/globby": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", - "integrity": "sha512-yANWAN2DUcBtuus5Cpd+SKROzXHs2iVXFZt/Ykrfz6SAXqacLX25NZpltE+39ceMexYF4TtEadjuSTw8+3wX4g==", + "node_modules/fresh": { + "version": "0.5.2", "dev": true, - "dependencies": { - "array-union": "^1.0.1", - "dir-glob": "^2.0.0", - "glob": "^7.1.2", - "ignore": "^3.3.5", - "pify": "^3.0.0", - "slash": "^1.0.0" - }, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">= 0.6" } }, - "node_modules/google-closure-compiler": { - "version": "20180402.0.0", - "resolved": "https://registry.npmjs.org/google-closure-compiler/-/google-closure-compiler-20180402.0.0.tgz", - "integrity": "sha512-ZsbRpSBn8SdQ2yQndon6emVfZSr6MTH2lwdZDm6sz5JYX8gSEa3FuuMs3tOtTfsTZElPdYePf2B0I1+EejcdHw==", + "node_modules/from2": { + "version": "2.3.0", "dev": true, + "license": "MIT", "dependencies": { - "chalk": "^1.0.0", - "vinyl": "^2.0.1", - "vinyl-sourcemaps-apply": "^0.2.0" - }, - "bin": { - "google-closure-compiler": "cli.js" - }, - "engines": { - "node": ">=4" + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" } }, - "node_modules/google-closure-compiler/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "node_modules/from2/node_modules/readable-stream": { + "version": "2.3.8", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "node_modules/google-closure-compiler/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "node_modules/from2/node_modules/safe-buffer": { + "version": "5.1.2", "dev": true, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/google-closure-compiler/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "node_modules/from2/node_modules/string_decoder": { + "version": "1.1.1", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" + "safe-buffer": "~5.1.0" } }, - "node_modules/google-closure-compiler/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "node_modules/fs-extra": { + "version": "11.2.0", "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^2.0.0" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=14.14" } }, - "node_modules/google-closure-compiler/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "node_modules/fs-extra/node_modules/universalify": { + "version": "2.0.1", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.8.0" + "node": ">= 10.0.0" } }, - "node_modules/google-closure-library": { - "version": "20190301.0.0", - "resolved": "https://registry.npmjs.org/google-closure-library/-/google-closure-library-20190301.0.0.tgz", - "integrity": "sha512-mpeszbnXpRhXZ0sPqUxBgUmk0RtmzrJRy3KFygp0Ih9JuRUjQTCLhwYQeIlK2vB2lShhY/KUo9E1Z1gvxDFxOQ==" + "node_modules/fs-monkey": { + "version": "1.0.5", + "dev": true, + "license": "Unlicense" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "node_modules/function-bind": { + "version": "1.1.2", "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/graceful-readlink": { + "node_modules/functional-red-black-tree": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==", - "dev": true + "dev": true, + "license": "MIT" }, - "node_modules/growl": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", - "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", + "node_modules/get-caller-file": { + "version": "2.0.5", "dev": true, + "license": "ISC", "engines": { - "node": ">=4.x" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "node_modules/get-intrinsic": { + "version": "1.2.2", "dev": true, + "license": "MIT", "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "dev": true, + "license": "MIT", "engines": { - "node": ">=0.4.7" + "node": ">=10" }, - "optionalDependencies": { - "uglify-js": "^3.1.4" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/handlebars/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/git-log-parser": { + "version": "1.2.1", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "dependencies": { + "argv-formatter": "~1.0.0", + "spawn-error-forwarder": "~1.0.0", + "split2": "~1.0.0", + "stream-combiner2": "~1.1.1", + "through2": "~2.0.0", + "traverse": "0.6.8" } }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "node_modules/git-log-parser/node_modules/readable-stream": { + "version": "2.3.8", "dev": true, - "engines": { - "node": ">=4" + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", + "node_modules/git-log-parser/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/git-log-parser/node_modules/split2": { + "version": "1.0.0", "dev": true, + "license": "ISC", "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" + "through2": "~2.0.0" } }, - "node_modules/har-validator/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/git-log-parser/node_modules/string_decoder": { + "version": "1.1.1", "dev": true, + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "safe-buffer": "~5.1.0" } }, - "node_modules/har-validator/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "node_modules/git-log-parser/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } }, - "node_modules/hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "node_modules/git-raw-commits": { + "version": "2.0.11", "dev": true, + "license": "MIT", + "dependencies": { + "dargs": "^7.0.0", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.js" + }, "engines": { - "node": ">=6" + "node": ">=10" } }, - "node_modules/has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "node_modules/glob": { + "version": "7.2.3", "dev": true, + "license": "ISC", "dependencies": { - "ansi-regex": "^2.0.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/has-ansi/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "node_modules/glob-parent": { + "version": "5.1.2", "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "node_modules/glob-to-regexp": { + "version": "0.4.1", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "license": "BSD-2-Clause" + }, + "node_modules/global-dirs": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4" + }, + "engines": { + "node": ">=4" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/globals": { + "version": "11.12.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "node_modules/globby": { + "version": "14.0.2", "dev": true, + "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0" + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.2", + "ignore": "^5.2.4", + "path-type": "^5.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "node_modules/globby/node_modules/ignore": { + "version": "5.3.2", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 4" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "node_modules/globby/node_modules/path-type": { + "version": "5.0.0", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "node_modules/gopd": { + "version": "1.0.1", "dev": true, + "license": "MIT", "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" + "get-intrinsic": "^1.1.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", + "node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "license": "ISC" + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/handlebars": { + "version": "4.7.8", "dev": true, + "license": "MIT", "dependencies": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" } }, - "node_modules/has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", + "node_modules/hard-rejection": { + "version": "2.1.0", "dev": true, - "dependencies": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/has-values/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "node_modules/has-ansi": { + "version": "2.0.0", "dev": true, + "license": "MIT", "dependencies": { - "kind-of": "^3.0.2" + "ansi-regex": "^2.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/has-values/node_modules/kind-of": { + "node_modules/has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", + "node_modules/has-property-descriptors": { + "version": "1.0.1", "dev": true, + "license": "MIT", "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "get-intrinsic": "^1.2.2" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "node_modules/has-symbols": { + "version": "1.0.3", "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.0", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -5418,82 +4791,134 @@ "node": ">= 0.4" } }, - "node_modules/he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha512-z/GDPjlRMNOa2XJiB4em8wJpuuBfrFOlYKTZxtpkdr1uPdibHI8rYA3MY0KDObpVyaes0e/aunid/t88ZI2EKA==", + "node_modules/hook-std": { + "version": "3.0.0", "dev": true, - "bin": { - "he": "bin/he" + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "node_modules/hosted-git-info": { + "version": "4.1.0", "dev": true, + "license": "ISC", "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "node_modules/hpack.js": { + "version": "2.1.6", "dev": true, + "license": "MIT", "dependencies": { - "parse-passwd": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" } }, - "node_modules/hook-std": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-2.0.0.tgz", - "integrity": "sha512-zZ6T5WcuBMIUVh49iPQS9t977t7C0l7OtHrpeMb5uk48JdflRX0NSFvCekfYNmGQETnLq9W/isMyHl69kxGi8g==", + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", "dev": true, - "engines": { - "node": ">=8" + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", "dev": true, + "license": "MIT", "dependencies": { - "lru-cache": "^6.0.0" + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" }, "engines": { - "node": ">=10" + "node": ">=18" } }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/html-entities": { + "version": "2.4.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, + "node_modules/http-deceiver": { + "version": "1.2.7", "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "dev": true, + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">=10" + "node": ">= 0.8" } }, - "node_modules/hosted-git-info/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "node_modules/http-parser-js": { + "version": "0.5.8", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } }, "node_modules/http-proxy-agent": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, + "license": "MIT", "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" @@ -5502,165 +4927,81 @@ "node": ">= 14" } }, - "node_modules/http-proxy-agent/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/http-proxy-middleware": { + "version": "2.0.6", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" }, "engines": { - "node": ">=6.0" + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" }, "peerDependenciesMeta": { - "supports-color": { + "@types/express": { "optional": true } } }, - "node_modules/http-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", - "dev": true - }, "node_modules/https-proxy-agent": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", - "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", - "dev": true, + "version": "7.0.6", + "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { "node": ">= 14" } }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "node_modules/human-signals": { + "version": "2.1.0", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=10.17.0" } }, "node_modules/husky": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", - "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", + "version": "9.1.6", "dev": true, + "license": "MIT", "bin": { - "husky": "lib/bin.js" + "husky": "bin.js" }, "engines": { - "node": ">=14" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/typicode" } }, "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, + "version": "0.6.3", + "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==", - "dev": true - }, "node_modules/ignore": { "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", - "dev": true - }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/import-fresh": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -5674,18 +5015,16 @@ }, "node_modules/import-fresh/node_modules/resolve-from": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/import-from": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-4.0.0.tgz", - "integrity": "sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.2" }, @@ -5693,153 +5032,76 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-local": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "dev": true, - "dependencies": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/import-local/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "node_modules/import-from-esm": { + "version": "1.3.4", "dev": true, + "license": "MIT", "dependencies": { - "locate-path": "^3.0.0" + "debug": "^4.3.4", + "import-meta-resolve": "^4.0.0" }, "engines": { - "node": ">=6" + "node": ">=16.20" } }, - "node_modules/import-local/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "node_modules/import-local": { + "version": "3.1.0", "dev": true, + "license": "MIT", "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" }, - "engines": { - "node": ">=6" - } - }, - "node_modules/import-local/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" + "bin": { + "import-local-fixture": "fixtures/cli.js" }, "engines": { - "node": ">=6" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-local/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/import-local/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/import-local/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/import-local/node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "node_modules/import-meta-resolve": { + "version": "4.1.0", "dev": true, - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/imports-loader": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/imports-loader/-/imports-loader-0.8.0.tgz", - "integrity": "sha512-kXWL7Scp8KQ4552ZcdVTeaQCZSLW+e6nJfp3cwUMB673T7Hr98Xjx5JK+ql7ADlJUvj1JS5O01RLbKoutN5QDQ==", - "dependencies": { - "loader-utils": "^1.0.2", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/imports-loader/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, "node_modules/imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.19" } }, "node_modules/indent-string": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true + "node_modules/index-to-position": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, + "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -5847,21 +5109,18 @@ }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/ini": { "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/inquirer": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", - "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-escapes": "^3.0.0", "chalk": "^2.0.0", @@ -5879,11 +5138,18 @@ "through": "^2.3.6" } }, + "node_modules/inquirer/node_modules/ansi-regex": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/inquirer/node_modules/ansi-styles": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -5893,9 +5159,8 @@ }, "node_modules/inquirer/node_modules/chalk": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -5907,131 +5172,107 @@ }, "node_modules/inquirer/node_modules/color-convert": { "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "1.1.3" } }, "node_modules/inquirer/node_modules/color-name": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/inquirer/node_modules/has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/inquirer/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/inquirer/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/string-width": { + "version": "2.1.1", "dev": true, + "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" }, "engines": { "node": ">=4" } }, - "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "node_modules/inquirer/node_modules/strip-ansi": { + "version": "4.0.0", "dev": true, + "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "ansi-regex": "^3.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "version": "2.2.0", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.10" } }, "node_modules/into-stream": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", - "integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==", + "version": "7.0.0", "dev": true, + "license": "MIT", "dependencies": { "from2": "^2.3.0", "p-is-promise": "^3.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-accessor-descriptor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz", - "integrity": "sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "node_modules/ipaddr.js": { + "version": "2.1.0", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 10" } }, "node_modules/is-arrayish": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "MIT" }, "node_modules/is-binary-path": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, - "optional": true, + "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" }, @@ -6039,154 +5280,51 @@ "node": ">=8" } }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "node_modules/is-core-module": { + "version": "2.13.1", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "node_modules/is-docker": { + "version": "2.2.1", "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-descriptor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz", - "integrity": "sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", - "dev": true, - "dependencies": { - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-descriptor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz", - "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extendable/node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/is-extglob": { + "version": "2.1.1", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "version": "3.0.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -6194,129 +5332,75 @@ "node": ">=0.10.0" } }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-obj": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-path-cwd": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/is-path-inside": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "version": "3.0.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "version": "2.0.4", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "isobject": "^3.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "license": "MIT" + }, "node_modules/is-resolvable": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dev": true, - "dependencies": { - "call-bind": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "ISC" }, "node_modules/is-stream": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -6324,41 +5408,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-text-path": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", - "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", "dev": true, + "license": "MIT", "dependencies": { "text-extensions": "^1.0.0" }, @@ -6366,89 +5419,50 @@ "node": ">=0.10.0" } }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "node_modules/is-unicode-supported": { + "version": "2.1.0", "dev": true, - "dependencies": { - "which-typed-array": "^1.1.14" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "node_modules/is-wsl": { + "version": "2.2.0", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "is-docker": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", - "dev": true, "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/isarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/isobject": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true - }, "node_modules/issue-parser": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz", - "integrity": "sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==", "dev": true, + "license": "MIT", "dependencies": { "lodash.capitalize": "^4.2.1", "lodash.escaperegexp": "^4.1.2", @@ -6462,24 +5476,34 @@ }, "node_modules/java-properties": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", - "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6.0" } }, + "node_modules/jest-worker": { + "version": "27.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, "node_modules/js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", - "dev": true + "version": "4.0.0", + "dev": true, + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -6487,76 +5511,74 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true - }, - "node_modules/json": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/json/-/json-9.0.6.tgz", - "integrity": "sha512-Nx+4WwMM1xadgqjjteOVEyjoIVq7fGH1hAlRDoxoq2tFzYsBYZDIKwYbyxolkTYwxsSOgAZD2ACLkeGjhFW2Jw==", - "dev": true, - "bin": { - "json": "lib/json.js" + "node_modules/jsdom": { + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", + "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", + "license": "MIT", + "dependencies": { + "cssstyle": "^4.2.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.5.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.1.1", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.1.1", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" }, - "engines": { - "node": ">=0.10.0" + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } } }, "node_modules/json-parse-better-errors": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "version": "0.4.1", + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stringify-safe": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true - }, - "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } + "dev": true, + "license": "ISC" }, "node_modules/jsonfile": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -6564,20 +5586,26 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "node_modules/jsonfile/node_modules/universalify": { + "version": "2.0.1", "dev": true, - "engines": [ - "node >= 0.2.0" - ] + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" }, "node_modules/JSONStream": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", "dev": true, + "license": "(MIT OR Apache-2.0)", "dependencies": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" @@ -6589,47 +5617,27 @@ "node": "*" } }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "node_modules/kind-of": { + "version": "6.0.3", "dev": true, - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, + "license": "MIT", "engines": { - "node": ">=0.6.0" + "node": ">=0.10.0" } }, - "node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "node_modules/launch-editor": { + "version": "2.6.1", "dev": true, + "license": "MIT", "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" } }, "node_modules/levn": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "~1.1.2", "type-check": "~0.3.2" @@ -6638,26 +5646,15 @@ "node": ">= 0.8.0" } }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "dev": true, - "dependencies": { - "immediate": "~3.0.5" - } - }, "node_modules/lines-and-columns": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/load-json-file": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.1.2", "parse-json": "^4.0.0", @@ -6670,9 +5667,8 @@ }, "node_modules/load-json-file/node_modules/parse-json": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", "dev": true, + "license": "MIT", "dependencies": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" @@ -6682,180 +5678,129 @@ } }, "node_modules/loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "version": "4.3.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=4.3.0 <5.0.0 || >=5.10" - } - }, - "node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" + "node": ">=6.11.5" } }, "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "version": "5.0.0", "dev": true, + "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "p-locate": "^4.1.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/lodash": { "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "dev": true, + "license": "MIT" }, "node_modules/lodash.camelcase": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.capitalize": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", - "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.escaperegexp": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.isfunction": { "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", - "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.ismatch": { "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", - "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.isplainobject": { "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.isstring": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.kebabcase": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", - "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.mergewith": { "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.snakecase": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", - "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.startcase": { "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", - "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.uniq": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.uniqby": { "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", - "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.upperfirst": { "version": "4.3.1", - "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", - "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", - "dev": true - }, - "node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "dev": true, - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } + "license": "MIT" }, - "node_modules/make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "node_modules/lru-cache": { + "version": "6.0.0", "dev": true, + "license": "ISC", "dependencies": { - "pify": "^3.0.0" + "yallist": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" } }, "node_modules/make-error": { "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", "dev": true, - "engines": { - "node": ">=0.10.0" - } + "license": "ISC" }, "node_modules/map-obj": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -6863,61 +5808,40 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==", - "dev": true - }, - "node_modules/map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", - "dev": true, - "dependencies": { - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/marked": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", - "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "version": "9.1.6", "dev": true, + "license": "MIT", "bin": { "marked": "bin/marked.js" }, "engines": { - "node": ">= 12" + "node": ">= 16" } }, "node_modules/marked-terminal": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-5.2.0.tgz", - "integrity": "sha512-Piv6yNwAQXGFjZSaiNljyNFw7jKDdGrw70FSbtxEyldLsyeuV5ZHm/1wW++kWbrOF1VPnUgYOhB2oLL0ZpnekA==", + "version": "6.2.0", "dev": true, + "license": "MIT", "dependencies": { "ansi-escapes": "^6.2.0", "cardinal": "^2.1.1", - "chalk": "^5.2.0", + "chalk": "^5.3.0", "cli-table3": "^0.6.3", - "node-emoji": "^1.11.0", - "supports-hyperlinks": "^2.3.0" + "node-emoji": "^2.1.3", + "supports-hyperlinks": "^3.0.0" }, "engines": { - "node": ">=14.13.1 || >=16.0.0" + "node": ">=16.0.0" }, "peerDependencies": { - "marked": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0" + "marked": ">=1 <12" } }, "node_modules/marked-terminal/node_modules/ansi-escapes": { "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz", - "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -6927,9 +5851,8 @@ }, "node_modules/marked-terminal/node_modules/chalk": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, + "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -6937,32 +5860,29 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "node_modules/media-typer": { + "version": "0.3.0", "dev": true, - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "license": "MIT", + "engines": { + "node": ">= 0.6" } }, - "node_modules/memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==", + "node_modules/memfs": { + "version": "3.5.3", "dev": true, + "license": "Unlicense", "dependencies": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" } }, "node_modules/meow": { "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", "dev": true, + "license": "MIT", "dependencies": { "@types/minimist": "^1.2.0", "camelcase-keys": "^6.2.2", @@ -6983,26 +5903,36 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, "node_modules/merge-stream": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/merge2": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } }, + "node_modules/methods": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/micromatch": { "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, + "license": "MIT", "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -7011,30 +5941,10 @@ "node": ">=8.6" } }, - "node_modules/miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "dependencies": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - }, - "bin": { - "miller-rabin": "bin/miller-rabin" - } - }, - "node_modules/miller-rabin/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, "node_modules/mime": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -7044,18 +5954,16 @@ }, "node_modules/mime-db": { "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -7063,50 +5971,31 @@ "node": ">= 0.6" } }, - "node_modules/mimeparse": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/mimeparse/-/mimeparse-0.1.4.tgz", - "integrity": "sha512-jiuAsJJY4c0oF97oHKic9nva2y1QF2yhYJG3LXLys//f8SNQ89eFuGZ29z62Z29CAY4endJS6zFiKUtURFErog==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/mimic-fn": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/min-indent": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/minimalistic-assert": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -7116,17 +6005,16 @@ }, "node_modules/minimist": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/minimist-options": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", "dev": true, + "license": "MIT", "dependencies": { "arrify": "^1.0.1", "is-plain-obj": "^1.1.0", @@ -7136,55 +6024,18 @@ "node": ">= 6" } }, - "node_modules/mississippi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", - "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", - "dev": true, - "dependencies": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^2.0.1", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mississippi/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "node_modules/minimist-options/node_modules/is-plain-obj": { + "version": "1.1.0", "dev": true, - "dependencies": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/mkdirp": { "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, + "license": "MIT", "dependencies": { "minimist": "^1.2.6" }, @@ -7192,217 +6043,76 @@ "mkdirp": "bin/cmd.js" } }, - "node_modules/mkpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mkpath/-/mkpath-1.0.0.tgz", - "integrity": "sha512-PbNHr7Y/9Y/2P5pKFv5XOGBfNQqZ+fdiHWcuf7swLACN5ZW5LU7J5tMU8LSBjpluAxAxKYGD9nnaIbdRy9+m1w==", - "dev": true - }, - "node_modules/mocha": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", - "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==", + "node_modules/modify-values": { + "version": "1.0.1", "dev": true, - "dependencies": { - "browser-stdout": "1.3.0", - "commander": "2.11.0", - "debug": "3.1.0", - "diff": "3.3.1", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.3", - "he": "1.1.1", - "mkdirp": "0.5.1", - "supports-color": "4.4.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, + "license": "MIT", "engines": { - "node": ">= 4.0.0" + "node": ">=0.10.0" } }, - "node_modules/mocha/node_modules/commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", - "dev": true + "node_modules/ms": { + "version": "2.1.2", + "license": "MIT" }, - "node_modules/mocha/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "node_modules/multicast-dns": { + "version": "7.2.5", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.0.0" + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" } }, - "node_modules/mocha/node_modules/glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "node_modules/mute-stream": { + "version": "0.0.7", "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/mocha/node_modules/has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha512-P+1n3MnwjR/Epg9BBo1KT8qbye2g2Ou4sFumihwt6I4tsUX7jnLcX4BTOSKg/B1ZrIYMN9FcEnG4x5a7NB8Eng==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mocha/node_modules/minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==", - "dev": true - }, - "node_modules/mocha/node_modules/mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==", - "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", - "dev": true, - "dependencies": { - "minimist": "0.0.8" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mocha/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", - "dev": true, - "dependencies": { - "has-flag": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/modify-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", - "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==", - "dev": true, - "dependencies": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==", - "dev": true + "license": "ISC" }, - "node_modules/nan": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", - "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==", + "node_modules/natural-compare": { + "version": "1.4.0", "dev": true, - "optional": true + "license": "MIT" }, - "node_modules/nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "node_modules/negotiator": { + "version": "0.6.3", "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, "node_modules/neo-async": { "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/nerf-dart": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", - "integrity": "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==", - "dev": true - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/node-emoji": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", - "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "version": "2.1.3", "dev": true, + "license": "MIT", "dependencies": { - "lodash": "^4.17.21" + "@sindresorhus/is": "^4.6.0", + "char-regex": "^1.0.2", + "emojilib": "^2.4.0", + "skin-tone": "^2.0.0" + }, + "engines": { + "node": ">=18" } }, "node_modules/node-fetch": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dev": true, + "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -7418,48 +6128,42 @@ } } }, - "node_modules/node-libs-browser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", - "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", - "dev": true, - "dependencies": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.1", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "^1.0.1" - } - }, - "node_modules/node-libs-browser/node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", - "dev": true + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "dev": true, + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.14", + "dev": true, + "license": "MIT" }, "node_modules/normalize-package-data": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "hosted-git-info": "^4.0.1", "is-core-module": "^2.5.0", @@ -7472,47 +6176,42 @@ }, "node_modules/normalize-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, - "optional": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "version": "8.0.1", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/npm": { - "version": "8.19.4", - "resolved": "https://registry.npmjs.org/npm/-/npm-8.19.4.tgz", - "integrity": "sha512-3HANl8i9DKnUA89P4KEgVNN28EjSeDCmvEqbzOAuxCFDzdBZzjUl99zgnGpOUumvW5lvJo2HKcjrsc+tfyv1Hw==", + "version": "10.9.0", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", - "@npmcli/ci-detect", "@npmcli/config", "@npmcli/fs", "@npmcli/map-workspaces", "@npmcli/package-json", + "@npmcli/promise-spawn", + "@npmcli/redact", "@npmcli/run-script", + "@sigstore/tuf", "abbrev", "archy", "cacache", "chalk", - "chownr", + "ci-info", "cli-columns", - "cli-table3", - "columnify", "fastest-levenshtein", "fs-minipass", "glob", @@ -7537,11 +6236,10 @@ "minimatch", "minipass", "minipass-pipeline", - "mkdirp", - "mkdirp-infer-owner", "ms", "node-gyp", "nopt", + "normalize-package-data", "npm-audit-report", "npm-install-checks", "npm-package-arg", @@ -7549,20 +6247,16 @@ "npm-profile", "npm-registry-fetch", "npm-user-validate", - "npmlog", - "opener", "p-map", "pacote", "parse-conflict-json", "proc-log", "qrcode-terminal", "read", - "read-package-json", - "read-package-json-fast", - "readdir-scoped-modules", - "rimraf", "semver", + "spdx-expression-parse", "ssri", + "supports-color", "tar", "text-table", "tiny-relative-date", @@ -7572,99 +6266,96 @@ "write-file-atomic" ], "dev": true, + "license": "Artistic-2.0", "workspaces": [ "docs", "smoke-tests", + "mock-globals", + "mock-registry", "workspaces/*" ], "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^5.6.3", - "@npmcli/ci-detect": "^2.0.0", - "@npmcli/config": "^4.2.1", - "@npmcli/fs": "^2.1.0", - "@npmcli/map-workspaces": "^2.0.3", - "@npmcli/package-json": "^2.0.0", - "@npmcli/run-script": "^4.2.1", - "abbrev": "~1.1.1", + "@npmcli/arborist": "^8.0.0", + "@npmcli/config": "^9.0.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/package-json": "^6.0.1", + "@npmcli/promise-spawn": "^8.0.1", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "@sigstore/tuf": "^2.3.4", + "abbrev": "^3.0.0", "archy": "~1.0.0", - "cacache": "^16.1.3", - "chalk": "^4.1.2", - "chownr": "^2.0.0", + "cacache": "^19.0.1", + "chalk": "^5.3.0", + "ci-info": "^4.0.0", "cli-columns": "^4.0.0", - "cli-table3": "^0.6.2", - "columnify": "^1.6.0", - "fastest-levenshtein": "^1.0.12", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "graceful-fs": "^4.2.10", - "hosted-git-info": "^5.2.1", - "ini": "^3.0.1", - "init-package-json": "^3.0.2", - "is-cidr": "^4.0.2", - "json-parse-even-better-errors": "^2.3.1", - "libnpmaccess": "^6.0.4", - "libnpmdiff": "^4.0.5", - "libnpmexec": "^4.0.14", - "libnpmfund": "^3.0.5", - "libnpmhook": "^8.0.4", - "libnpmorg": "^4.0.4", - "libnpmpack": "^4.1.3", - "libnpmpublish": "^6.0.5", - "libnpmsearch": "^5.0.4", - "libnpmteam": "^4.0.4", - "libnpmversion": "^3.0.7", - "make-fetch-happen": "^10.2.0", - "minimatch": "^5.1.0", - "minipass": "^3.1.6", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^10.4.5", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^8.0.0", + "ini": "^5.0.0", + "init-package-json": "^7.0.1", + "is-cidr": "^5.1.0", + "json-parse-even-better-errors": "^4.0.0", + "libnpmaccess": "^9.0.0", + "libnpmdiff": "^7.0.0", + "libnpmexec": "^9.0.0", + "libnpmfund": "^6.0.0", + "libnpmhook": "^11.0.0", + "libnpmorg": "^7.0.0", + "libnpmpack": "^8.0.0", + "libnpmpublish": "^10.0.0", + "libnpmsearch": "^8.0.0", + "libnpmteam": "^7.0.0", + "libnpmversion": "^7.0.0", + "make-fetch-happen": "^14.0.1", + "minimatch": "^9.0.5", + "minipass": "^7.1.1", "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", "ms": "^2.1.2", - "node-gyp": "^9.1.0", - "nopt": "^6.0.0", - "npm-audit-report": "^3.0.0", - "npm-install-checks": "^5.0.0", - "npm-package-arg": "^9.1.0", - "npm-pick-manifest": "^7.0.2", - "npm-profile": "^6.2.0", - "npm-registry-fetch": "^13.3.1", - "npm-user-validate": "^1.0.1", - "npmlog": "^6.0.2", - "opener": "^1.5.2", + "node-gyp": "^10.2.0", + "nopt": "^8.0.0", + "normalize-package-data": "^7.0.0", + "npm-audit-report": "^6.0.0", + "npm-install-checks": "^7.1.0", + "npm-package-arg": "^12.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-profile": "^11.0.1", + "npm-registry-fetch": "^18.0.1", + "npm-user-validate": "^3.0.0", "p-map": "^4.0.0", - "pacote": "^13.6.2", - "parse-conflict-json": "^2.0.2", - "proc-log": "^2.0.1", + "pacote": "^19.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", "qrcode-terminal": "^0.12.0", - "read": "~1.0.7", - "read-package-json": "^5.0.2", - "read-package-json-fast": "^2.0.3", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", - "semver": "^7.3.7", - "ssri": "^9.0.1", - "tar": "^6.1.11", + "read": "^4.0.0", + "semver": "^7.6.3", + "spdx-expression-parse": "^4.0.0", + "ssri": "^12.0.0", + "supports-color": "^9.4.0", + "tar": "^6.2.1", "text-table": "~0.2.0", "tiny-relative-date": "^1.3.0", - "treeverse": "^2.0.0", - "validate-npm-package-name": "^4.0.0", - "which": "^2.0.2", - "write-file-atomic": "^4.0.1" + "treeverse": "^3.0.0", + "validate-npm-package-name": "^6.0.0", + "which": "^5.0.0", + "write-file-atomic": "^6.0.0" }, "bin": { "npm": "bin/npm-cli.js", "npx": "bin/npx-cli.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm-run-path": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -7672,744 +6363,840 @@ "node": ">=8" } }, - "node_modules/npm/node_modules/@colors/colors": { - "version": "1.5.0", + "node_modules/npm/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", "dev": true, "inBundle": true, "license": "MIT", - "optional": true, "engines": { - "node": ">=0.1.90" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/npm/node_modules/@gar/promisify": { - "version": "1.1.3", + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", "dev": true, "inBundle": true, "license": "MIT" }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/npm/node_modules/@isaacs/string-locale-compare": { "version": "1.1.0", "dev": true, "inBundle": true, "license": "ISC" }, - "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "5.6.3", + "node_modules/npm/node_modules/@npmcli/agent": { + "version": "3.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/map-workspaces": "^2.0.3", - "@npmcli/metavuln-calculator": "^3.0.1", - "@npmcli/move-file": "^2.0.0", - "@npmcli/name-from-folder": "^1.0.1", - "@npmcli/node-gyp": "^2.0.0", - "@npmcli/package-json": "^2.0.0", - "@npmcli/query": "^1.2.0", - "@npmcli/run-script": "^4.1.3", - "bin-links": "^3.0.3", - "cacache": "^16.1.3", + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/metavuln-calculator": "^8.0.0", + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.1", + "@npmcli/query": "^4.0.0", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "bin-links": "^5.0.0", + "cacache": "^19.0.1", "common-ancestor-path": "^1.0.1", - "hosted-git-info": "^5.2.1", - "json-parse-even-better-errors": "^2.3.1", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", "json-stringify-nice": "^1.1.4", - "minimatch": "^5.1.0", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", - "nopt": "^6.0.0", - "npm-install-checks": "^5.0.0", - "npm-package-arg": "^9.0.0", - "npm-pick-manifest": "^7.0.2", - "npm-registry-fetch": "^13.0.0", - "npmlog": "^6.0.2", - "pacote": "^13.6.1", - "parse-conflict-json": "^2.0.1", - "proc-log": "^2.0.0", + "lru-cache": "^10.2.2", + "minimatch": "^9.0.4", + "nopt": "^8.0.0", + "npm-install-checks": "^7.1.0", + "npm-package-arg": "^12.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.1", + "pacote": "^19.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "proggy": "^3.0.0", "promise-all-reject-late": "^1.0.0", - "promise-call-limit": "^1.0.1", - "read-package-json-fast": "^2.0.2", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", + "promise-call-limit": "^3.0.1", + "read-package-json-fast": "^4.0.0", "semver": "^7.3.7", - "ssri": "^9.0.0", - "treeverse": "^2.0.0", - "walk-up-path": "^1.0.0" + "ssri": "^12.0.0", + "treeverse": "^3.0.0", + "walk-up-path": "^3.0.1" }, "bin": { "arborist": "bin/index.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/ci-detect": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/@npmcli/config": { - "version": "4.2.2", + "version": "9.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/map-workspaces": "^2.0.2", - "ini": "^3.0.0", - "mkdirp-infer-owner": "^2.0.0", - "nopt": "^6.0.0", - "proc-log": "^2.0.0", - "read-package-json-fast": "^2.0.3", + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/package-json": "^6.0.1", + "ci-info": "^4.0.0", + "ini": "^5.0.0", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", "semver": "^7.3.5", - "walk-up-path": "^1.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/@npmcli/disparity-colors": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "ansi-styles": "^4.3.0" + "walk-up-path": "^3.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/@npmcli/fs": { - "version": "2.1.2", + "version": "4.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@gar/promisify": "^1.1.3", "semver": "^7.3.5" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/@npmcli/git": { - "version": "3.0.2", + "version": "6.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/promise-spawn": "^3.0.0", - "lru-cache": "^7.4.4", - "mkdirp": "^1.0.4", - "npm-pick-manifest": "^7.0.0", - "proc-log": "^2.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "ini": "^5.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^10.0.0", + "proc-log": "^5.0.0", "promise-inflight": "^1.0.1", "promise-retry": "^2.0.1", "semver": "^7.3.5", - "which": "^2.0.2" + "which": "^5.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/@npmcli/installed-package-contents": { - "version": "1.0.7", + "version": "3.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" + "npm-bundled": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" }, "bin": { - "installed-package-contents": "index.js" + "installed-package-contents": "bin/index.js" }, "engines": { - "node": ">= 10" - } - }, - "node_modules/npm/node_modules/@npmcli/installed-package-contents/node_modules/npm-bundled": { - "version": "1.1.2", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "npm-normalize-package-bin": "^1.0.1" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/@npmcli/map-workspaces": { - "version": "2.0.4", + "version": "4.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/name-from-folder": "^1.0.1", - "glob": "^8.0.1", - "minimatch": "^5.0.1", - "read-package-json-fast": "^2.0.3" + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { - "version": "3.1.1", + "version": "8.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "cacache": "^16.0.0", - "json-parse-even-better-errors": "^2.3.1", - "pacote": "^13.0.3", + "cacache": "^19.0.0", + "json-parse-even-better-errors": "^4.0.0", + "pacote": "^19.0.0", + "proc-log": "^5.0.0", "semver": "^7.3.5" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/@npmcli/move-file": { - "version": "2.0.1", + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "3.0.0", "dev": true, "inBundle": true, - "license": "MIT", - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, + "license": "ISC", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/@npmcli/name-from-folder": { - "version": "1.0.1", - "dev": true, - "inBundle": true, - "license": "ISC" - }, "node_modules/npm/node_modules/@npmcli/node-gyp": { - "version": "2.0.0", + "version": "4.0.0", "dev": true, "inBundle": true, "license": "ISC", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/@npmcli/package-json": { - "version": "2.0.0", + "version": "6.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "json-parse-even-better-errors": "^2.3.1" + "@npmcli/git": "^6.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "normalize-package-data": "^7.0.0", + "proc-log": "^5.0.0", + "semver": "^7.5.3" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/@npmcli/promise-spawn": { - "version": "3.0.0", + "version": "8.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "infer-owner": "^1.0.4" + "which": "^5.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/@npmcli/query": { - "version": "1.2.0", + "version": "4.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "npm-package-arg": "^9.1.0", - "postcss-selector-parser": "^6.0.10", - "semver": "^7.3.7" + "postcss-selector-parser": "^6.1.2" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/@npmcli/run-script": { - "version": "4.2.1", + "node_modules/npm/node_modules/@npmcli/redact": { + "version": "3.0.0", "dev": true, "inBundle": true, "license": "ISC", - "dependencies": { - "@npmcli/node-gyp": "^2.0.0", - "@npmcli/promise-spawn": "^3.0.0", - "node-gyp": "^9.0.0", - "read-package-json-fast": "^2.0.3", - "which": "^2.0.2" - }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/@tootallnate/once": { - "version": "2.0.0", + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "9.0.1", "dev": true, "inBundle": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "node-gyp": "^10.0.0", + "proc-log": "^5.0.0", + "which": "^5.0.0" + }, "engines": { - "node": ">= 10" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/abbrev": { - "version": "1.1.1", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/agent-base": { - "version": "6.0.2", + "node_modules/npm/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", "dev": true, "inBundle": true, "license": "MIT", - "dependencies": { - "debug": "4" - }, + "optional": true, "engines": { - "node": ">= 6.0.0" + "node": ">=14" } }, - "node_modules/npm/node_modules/agentkeepalive": { - "version": "4.2.1", + "node_modules/npm/node_modules/@sigstore/bundle": { + "version": "2.3.2", "dev": true, "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "debug": "^4.1.0", - "depd": "^1.1.2", - "humanize-ms": "^1.2.1" + "@sigstore/protobuf-specs": "^0.3.2" }, "engines": { - "node": ">= 8.0.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/aggregate-error": { - "version": "3.1.0", + "node_modules/npm/node_modules/@sigstore/core": { + "version": "1.1.0", "dev": true, "inBundle": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, + "license": "Apache-2.0", "engines": { - "node": ">=8" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/ansi-regex": { - "version": "5.0.1", + "node_modules/npm/node_modules/@sigstore/protobuf-specs": { + "version": "0.3.2", "dev": true, "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", "engines": { - "node": ">=8" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/ansi-styles": { - "version": "4.3.0", + "node_modules/npm/node_modules/@sigstore/sign": { + "version": "2.3.2", "dev": true, "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "color-convert": "^2.0.1" + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "make-fetch-happen": "^13.0.1", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/aproba": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/archy": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/are-we-there-yet": { - "version": "3.0.1", + "node_modules/npm/node_modules/@sigstore/sign/node_modules/@npmcli/agent": { + "version": "2.2.2", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/asap": { - "version": "2.0.6", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/balanced-match": { - "version": "1.0.2", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/bin-links": { - "version": "3.0.3", + "node_modules/npm/node_modules/@sigstore/sign/node_modules/@npmcli/fs": { + "version": "3.1.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "cmd-shim": "^5.0.0", - "mkdirp-infer-owner": "^2.0.0", - "npm-normalize-package-bin": "^2.0.0", - "read-cmd-shim": "^3.0.0", - "rimraf": "^3.0.0", - "write-file-atomic": "^4.0.0" + "semver": "^7.3.5" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/bin-links/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", + "node_modules/npm/node_modules/@sigstore/sign/node_modules/cacache": { + "version": "18.0.4", "dev": true, "inBundle": true, "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/binary-extensions": { - "version": "2.2.0", + "node_modules/npm/node_modules/@sigstore/sign/node_modules/make-fetch-happen": { + "version": "13.0.1", "dev": true, "inBundle": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" + }, "engines": { - "node": ">=8" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/brace-expansion": { - "version": "2.0.1", + "node_modules/npm/node_modules/@sigstore/sign/node_modules/minipass-fetch": { + "version": "3.0.5", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" } }, - "node_modules/npm/node_modules/builtins": { - "version": "5.0.1", + "node_modules/npm/node_modules/@sigstore/sign/node_modules/proc-log": { + "version": "4.2.0", "dev": true, "inBundle": true, - "license": "MIT", - "dependencies": { - "semver": "^7.0.0" + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/cacache": { - "version": "16.1.3", + "node_modules/npm/node_modules/@sigstore/sign/node_modules/ssri": { + "version": "10.0.6", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/fs": "^2.1.0", - "@npmcli/move-file": "^2.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "infer-owner": "^1.0.4", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11", - "unique-filename": "^2.0.0" + "minipass": "^7.0.3" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/chalk": { - "version": "4.1.2", + "node_modules/npm/node_modules/@sigstore/sign/node_modules/unique-filename": { + "version": "3.0.0", "dev": true, "inBundle": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "unique-slug": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/chownr": { - "version": "2.0.0", + "node_modules/npm/node_modules/@sigstore/sign/node_modules/unique-slug": { + "version": "4.0.0", "dev": true, "inBundle": true, "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, "engines": { - "node": ">=10" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/cidr-regex": { - "version": "3.1.1", + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "2.3.4", "dev": true, "inBundle": true, - "license": "BSD-2-Clause", + "license": "Apache-2.0", "dependencies": { - "ip-regex": "^4.1.0" + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^2.2.1" }, "engines": { - "node": ">=10" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/clean-stack": { - "version": "2.2.0", + "node_modules/npm/node_modules/@sigstore/verify": { + "version": "1.2.1", "dev": true, "inBundle": true, - "license": "MIT", + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.1.0", + "@sigstore/protobuf-specs": "^0.3.2" + }, "engines": { - "node": ">=6" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/cli-columns": { - "version": "4.0.0", + "node_modules/npm/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", "dev": true, "inBundle": true, "license": "MIT", - "dependencies": { - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, "engines": { - "node": ">= 10" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/cli-table3": { - "version": "0.6.2", + "node_modules/npm/node_modules/@tufjs/models": { + "version": "2.0.1", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "string-width": "^4.2.0" + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.4" }, "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/clone": { - "version": "1.0.4", + "node_modules/npm/node_modules/abbrev": { + "version": "3.0.0", "dev": true, "inBundle": true, - "license": "MIT", + "license": "ISC", "engines": { - "node": ">=0.8" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/cmd-shim": { - "version": "5.0.0", + "node_modules/npm/node_modules/agent-base": { + "version": "7.1.1", "dev": true, "inBundle": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "mkdirp-infer-owner": "^2.0.0" + "debug": "^4.3.4" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 14" } }, - "node_modules/npm/node_modules/color-convert": { - "version": "2.0.1", + "node_modules/npm/node_modules/aggregate-error": { + "version": "3.1.0", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=8" } }, - "node_modules/npm/node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/color-support": { - "version": "1.1.3", + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", "dev": true, "inBundle": true, - "license": "ISC", - "bin": { - "color-support": "bin.js" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "node_modules/npm/node_modules/columnify": { - "version": "1.6.0", + "node_modules/npm/node_modules/ansi-styles": { + "version": "6.2.1", "dev": true, "inBundle": true, "license": "MIT", - "dependencies": { - "strip-ansi": "^6.0.1", - "wcwidth": "^1.0.0" - }, "engines": { - "node": ">=8.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/npm/node_modules/common-ancestor-path": { - "version": "1.0.1", + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", "dev": true, "inBundle": true, "license": "ISC" }, - "node_modules/npm/node_modules/concat-map": { - "version": "0.0.1", + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", "dev": true, "inBundle": true, "license": "MIT" }, - "node_modules/npm/node_modules/console-control-strings": { - "version": "1.1.0", + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", "dev": true, "inBundle": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/npm/node_modules/cssesc": { - "version": "3.0.0", + "node_modules/npm/node_modules/bin-links": { + "version": "5.0.0", "dev": true, "inBundle": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" + "license": "ISC", + "dependencies": { + "cmd-shim": "^7.0.0", + "npm-normalize-package-bin": "^4.0.0", + "proc-log": "^5.0.0", + "read-cmd-shim": "^5.0.0", + "write-file-atomic": "^6.0.0" }, "engines": { - "node": ">=4" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/debug": { - "version": "4.3.4", + "node_modules/npm/node_modules/binary-extensions": { + "version": "2.3.0", "dev": true, "inBundle": true, "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, "engines": { - "node": ">=6.0" + "node": ">=8" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm/node_modules/debug/node_modules/ms": { - "version": "2.1.2", + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.1", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } }, - "node_modules/npm/node_modules/debuglog": { - "version": "1.0.1", + "node_modules/npm/node_modules/cacache": { + "version": "19.0.1", "dev": true, "inBundle": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" + }, "engines": { - "node": "*" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/defaults": { - "version": "1.0.3", + "node_modules/npm/node_modules/cacache/node_modules/chownr": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/minizlib": { + "version": "3.0.1", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "clone": "^1.0.2" + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" } }, - "node_modules/npm/node_modules/delegates": { - "version": "1.0.0", + "node_modules/npm/node_modules/cacache/node_modules/mkdirp": { + "version": "3.0.1", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "node_modules/npm/node_modules/depd": { - "version": "1.1.2", + "node_modules/npm/node_modules/cacache/node_modules/p-map": { + "version": "7.0.2", "dev": true, "inBundle": true, "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm/node_modules/dezalgo": { - "version": "1.0.4", + "node_modules/npm/node_modules/cacache/node_modules/tar": { + "version": "7.4.3", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" } }, - "node_modules/npm/node_modules/diff": { - "version": "5.1.0", + "node_modules/npm/node_modules/cacache/node_modules/yallist": { + "version": "5.0.0", "dev": true, "inBundle": true, - "license": "BSD-3-Clause", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=0.3.1" + "node": ">=18" } }, - "node_modules/npm/node_modules/emoji-regex": { - "version": "8.0.0", + "node_modules/npm/node_modules/chalk": { + "version": "5.3.0", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } }, - "node_modules/npm/node_modules/encoding": { - "version": "0.1.13", + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ci-info": { + "version": "4.0.0", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], "inBundle": true, "license": "MIT", - "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "4.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", "dependencies": { - "iconv-lite": "^0.6.2" + "ip-regex": "^5.0.0" + }, + "engines": { + "node": ">=14" } }, - "node_modules/npm/node_modules/env-paths": { - "version": "2.2.1", + "node_modules/npm/node_modules/clean-stack": { + "version": "2.2.0", "dev": true, "inBundle": true, "license": "MIT", @@ -8417,267 +7204,311 @@ "node": ">=6" } }, - "node_modules/npm/node_modules/err-code": { - "version": "2.0.3", + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } }, - "node_modules/npm/node_modules/fastest-levenshtein": { - "version": "1.0.12", + "node_modules/npm/node_modules/cmd-shim": { + "version": "7.0.0", "dev": true, "inBundle": true, - "license": "MIT" + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } }, - "node_modules/npm/node_modules/fs-minipass": { - "version": "2.1.0", + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", "dev": true, "inBundle": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "minipass": "^3.0.0" + "color-name": "~1.1.4" }, "engines": { - "node": ">= 8" + "node": ">=7.0.0" } }, - "node_modules/npm/node_modules/fs.realpath": { - "version": "1.0.0", + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", "dev": true, "inBundle": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/npm/node_modules/function-bind": { - "version": "1.1.1", + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", "dev": true, "inBundle": true, - "license": "MIT" + "license": "ISC" }, - "node_modules/npm/node_modules/gauge": { - "version": "4.0.4", + "node_modules/npm/node_modules/cross-spawn": { + "version": "7.0.3", "dev": true, "inBundle": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 8" } }, - "node_modules/npm/node_modules/glob": { - "version": "8.0.3", + "node_modules/npm/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "isexe": "^2.0.0" }, - "engines": { - "node": ">=12" + "bin": { + "node-which": "bin/node-which" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">= 8" } }, - "node_modules/npm/node_modules/graceful-fs": { - "version": "4.2.10", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/has": { - "version": "1.0.3", + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", "dev": true, "inBundle": true, "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1" + "bin": { + "cssesc": "bin/cssesc" }, "engines": { - "node": ">= 0.4.0" + "node": ">=4" } }, - "node_modules/npm/node_modules/has-flag": { - "version": "4.0.0", + "node_modules/npm/node_modules/debug": { + "version": "4.3.6", "dev": true, "inBundle": true, "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, "engines": { - "node": ">=8" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/npm/node_modules/has-unicode": { - "version": "2.0.1", + "node_modules/npm/node_modules/debug/node_modules/ms": { + "version": "2.1.2", "dev": true, "inBundle": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/npm/node_modules/hosted-git-info": { - "version": "5.2.1", + "node_modules/npm/node_modules/diff": { + "version": "5.2.0", "dev": true, "inBundle": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^7.5.1" - }, + "license": "BSD-3-Clause", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=0.3.1" } }, - "node_modules/npm/node_modules/http-cache-semantics": { - "version": "4.1.1", + "node_modules/npm/node_modules/eastasianwidth": { + "version": "0.2.0", "dev": true, "inBundle": true, - "license": "BSD-2-Clause" + "license": "MIT" }, - "node_modules/npm/node_modules/http-proxy-agent": { - "version": "5.0.0", + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", "dev": true, "inBundle": true, "license": "MIT", + "optional": true, "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" + "iconv-lite": "^0.6.2" } }, - "node_modules/npm/node_modules/https-proxy-agent": { - "version": "5.0.1", + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", "dev": true, "inBundle": true, "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, "engines": { - "node": ">= 6" + "node": ">=6" } }, - "node_modules/npm/node_modules/humanize-ms": { - "version": "1.2.1", + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", "dev": true, "inBundle": true, - "license": "MIT", - "dependencies": { - "ms": "^2.0.0" - } + "license": "MIT" }, - "node_modules/npm/node_modules/iconv-lite": { - "version": "0.6.3", + "node_modules/npm/node_modules/exponential-backoff": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.16", "dev": true, "inBundle": true, "license": "MIT", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, "engines": { - "node": ">=0.10.0" + "node": ">= 4.9.1" } }, - "node_modules/npm/node_modules/ignore-walk": { - "version": "5.0.1", + "node_modules/npm/node_modules/foreground-child": { + "version": "3.3.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "minimatch": "^5.0.1" + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/npm/node_modules/imurmurhash": { - "version": "0.1.4", + "node_modules/npm/node_modules/fs-minipass": { + "version": "3.0.3", "dev": true, "inBundle": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, "engines": { - "node": ">=0.8.19" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/indent-string": { - "version": "4.0.0", + "node_modules/npm/node_modules/glob": { + "version": "10.4.5", "dev": true, "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=8" + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/npm/node_modules/infer-owner": { - "version": "1.0.4", + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.11", "dev": true, "inBundle": true, "license": "ISC" }, - "node_modules/npm/node_modules/inflight": { - "version": "1.0.6", + "node_modules/npm/node_modules/hosted-git-info": { + "version": "8.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/inherits": { - "version": "2.0.4", - "dev": true, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.1.1", + "dev": true, "inBundle": true, - "license": "ISC" + "license": "BSD-2-Clause" }, - "node_modules/npm/node_modules/ini": { - "version": "3.0.1", + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "7.0.2", "dev": true, "inBundle": true, - "license": "ISC", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 14" } }, - "node_modules/npm/node_modules/init-package-json": { - "version": "3.0.2", + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "7.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "7.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "npm-package-arg": "^9.0.1", - "promzard": "^0.3.0", - "read": "^1.0.7", - "read-package-json": "^5.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^4.0.0" + "minimatch": "^9.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/ip": { - "version": "2.0.0", + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", "dev": true, "inBundle": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } }, - "node_modules/npm/node_modules/ip-regex": { - "version": "4.3.0", + "node_modules/npm/node_modules/indent-string": { + "version": "4.0.0", "dev": true, "inBundle": true, "license": "MIT", @@ -8685,28 +7516,68 @@ "node": ">=8" } }, - "node_modules/npm/node_modules/is-cidr": { - "version": "4.0.2", + "node_modules/npm/node_modules/ini": { + "version": "5.0.0", "dev": true, "inBundle": true, - "license": "BSD-2-Clause", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "7.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "cidr-regex": "^3.1.1" + "@npmcli/package-json": "^6.0.0", + "npm-package-arg": "^12.0.0", + "promzard": "^2.0.0", + "read": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^6.0.0" }, "engines": { - "node": ">=10" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/is-core-module": { - "version": "2.10.0", + "node_modules/npm/node_modules/ip-address": { + "version": "9.0.5", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "has": "^1.0.3" + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "5.1.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^4.1.1" + }, + "engines": { + "node": ">=14" } }, "node_modules/npm/node_modules/is-fullwidth-code-point": { @@ -8730,12 +7601,36 @@ "inBundle": true, "license": "ISC" }, - "node_modules/npm/node_modules/json-parse-even-better-errors": { - "version": "2.3.1", + "node_modules/npm/node_modules/jackspeak": { + "version": "3.4.3", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/jsbn": { + "version": "1.1.0", "dev": true, "inBundle": true, "license": "MIT" }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/npm/node_modules/json-stringify-nice": { "version": "1.1.4", "dev": true, @@ -8755,223 +7650,213 @@ "license": "MIT" }, "node_modules/npm/node_modules/just-diff": { - "version": "5.1.1", + "version": "6.0.2", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/just-diff-apply": { - "version": "5.4.1", + "version": "5.5.0", "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/libnpmaccess": { - "version": "6.0.4", + "version": "9.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "aproba": "^2.0.0", - "minipass": "^3.1.1", - "npm-package-arg": "^9.0.1", - "npm-registry-fetch": "^13.0.0" + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/libnpmdiff": { - "version": "4.0.5", + "version": "7.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/disparity-colors": "^2.0.0", - "@npmcli/installed-package-contents": "^1.0.7", - "binary-extensions": "^2.2.0", + "@npmcli/arborist": "^8.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "binary-extensions": "^2.3.0", "diff": "^5.1.0", - "minimatch": "^5.0.1", - "npm-package-arg": "^9.0.1", - "pacote": "^13.6.1", - "tar": "^6.1.0" + "minimatch": "^9.0.4", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "tar": "^6.2.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/libnpmexec": { - "version": "4.0.14", + "version": "9.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^5.6.3", - "@npmcli/ci-detect": "^2.0.0", - "@npmcli/fs": "^2.1.1", - "@npmcli/run-script": "^4.2.0", - "chalk": "^4.1.0", - "mkdirp-infer-owner": "^2.0.0", - "npm-package-arg": "^9.0.1", - "npmlog": "^6.0.2", - "pacote": "^13.6.1", - "proc-log": "^2.0.0", - "read": "^1.0.7", - "read-package-json-fast": "^2.0.2", + "@npmcli/arborist": "^8.0.0", + "@npmcli/run-script": "^9.0.1", + "ci-info": "^4.0.0", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "proc-log": "^5.0.0", + "read": "^4.0.0", + "read-package-json-fast": "^4.0.0", "semver": "^7.3.7", - "walk-up-path": "^1.0.0" + "walk-up-path": "^3.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/libnpmfund": { - "version": "3.0.5", + "version": "6.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^5.6.3" + "@npmcli/arborist": "^8.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/libnpmhook": { - "version": "8.0.4", + "version": "11.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" + "npm-registry-fetch": "^18.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/libnpmorg": { - "version": "4.0.4", + "version": "7.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" + "npm-registry-fetch": "^18.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/libnpmpack": { - "version": "4.1.3", + "version": "8.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/run-script": "^4.1.3", - "npm-package-arg": "^9.0.1", - "pacote": "^13.6.1" + "@npmcli/arborist": "^8.0.0", + "@npmcli/run-script": "^9.0.1", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/libnpmpublish": { - "version": "6.0.5", + "version": "10.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "normalize-package-data": "^4.0.0", - "npm-package-arg": "^9.0.1", - "npm-registry-fetch": "^13.0.0", + "ci-info": "^4.0.0", + "normalize-package-data": "^7.0.0", + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1", + "proc-log": "^5.0.0", "semver": "^7.3.7", - "ssri": "^9.0.0" + "sigstore": "^2.2.0", + "ssri": "^12.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/libnpmsearch": { - "version": "5.0.4", + "version": "8.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "npm-registry-fetch": "^13.0.0" + "npm-registry-fetch": "^18.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/libnpmteam": { - "version": "4.0.4", + "version": "7.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "aproba": "^2.0.0", - "npm-registry-fetch": "^13.0.0" + "npm-registry-fetch": "^18.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/libnpmversion": { - "version": "3.0.7", + "version": "7.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/git": "^3.0.0", - "@npmcli/run-script": "^4.1.3", - "json-parse-even-better-errors": "^2.3.1", - "proc-log": "^2.0.0", + "@npmcli/git": "^6.0.1", + "@npmcli/run-script": "^9.0.1", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", "semver": "^7.3.7" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/lru-cache": { - "version": "7.13.2", + "version": "10.4.3", "dev": true, "inBundle": true, - "license": "ISC", - "engines": { - "node": ">=12" - } + "license": "ISC" }, "node_modules/npm/node_modules/make-fetch-happen": { - "version": "10.2.1", + "version": "14.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^16.1.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^2.0.3", + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", + "proc-log": "^5.0.0", "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^9.0.0" + "ssri": "^12.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/minimatch": { - "version": "5.1.0", + "version": "9.0.5", "dev": true, "inBundle": true, "license": "ISC", @@ -8979,50 +7864,63 @@ "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/npm/node_modules/minipass": { - "version": "3.3.4", + "version": "7.1.2", "dev": true, "inBundle": true, "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/npm/node_modules/minipass-collect": { - "version": "1.0.2", + "version": "2.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "minipass": "^3.0.0" + "minipass": "^7.0.3" }, "engines": { - "node": ">= 8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/npm/node_modules/minipass-fetch": { - "version": "2.1.1", + "version": "4.0.0", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "minipass": "^3.1.6", + "minipass": "^7.0.3", "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" + "minizlib": "^3.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" }, "optionalDependencies": { "encoding": "^0.1.13" } }, + "node_modules/npm/node_modules/minipass-fetch/node_modules/minizlib": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/npm/node_modules/minipass-flush": { "version": "1.0.5", "dev": true, @@ -9035,14 +7933,16 @@ "node": ">= 8" } }, - "node_modules/npm/node_modules/minipass-json-stream": { - "version": "1.0.1", + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", "dev": true, "inBundle": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/npm/node_modules/minipass-pipeline": { @@ -9057,6 +7957,18 @@ "node": ">=8" } }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/npm/node_modules/minipass-sized": { "version": "1.0.3", "dev": true, @@ -9069,6 +7981,18 @@ "node": ">=8" } }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/npm/node_modules/minizlib": { "version": "2.1.2", "dev": true, @@ -9082,27 +8006,25 @@ "node": ">= 8" } }, - "node_modules/npm/node_modules/mkdirp": { - "version": "1.0.4", + "node_modules/npm/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", "dev": true, "inBundle": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/npm/node_modules/mkdirp-infer-owner": { - "version": "2.0.0", + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", "dev": true, "inBundle": true, - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "infer-owner": "^1.0.4", - "mkdirp": "^1.0.3" + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" }, "engines": { "node": ">=10" @@ -9115,10 +8037,13 @@ "license": "MIT" }, "node_modules/npm/node_modules/mute-stream": { - "version": "0.0.8", + "version": "2.0.0", "dev": true, "inBundle": true, - "license": "ISC" + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } }, "node_modules/npm/node_modules/negotiator": { "version": "0.6.3", @@ -9130,301 +8055,387 @@ } }, "node_modules/npm/node_modules/node-gyp": { - "version": "9.1.0", + "version": "10.2.0", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { "env-paths": "^2.2.0", - "glob": "^7.1.4", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^10.0.3", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^4.1.0", "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" + "tar": "^6.2.1", + "which": "^4.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": "^12.22 || ^14.13 || >=16" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/brace-expansion": { - "version": "1.1.11", + "node_modules/npm/node_modules/node-gyp/node_modules/@npmcli/agent": { + "version": "2.2.2", "dev": true, "inBundle": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/glob": { - "version": "7.2.3", + "node_modules/npm/node_modules/node-gyp/node_modules/@npmcli/fs": { + "version": "3.1.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "semver": "^7.3.5" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/minimatch": { - "version": "3.1.2", + "node_modules/npm/node_modules/node-gyp/node_modules/abbrev": { + "version": "2.0.0", "dev": true, "inBundle": true, "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, "engines": { - "node": "*" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/node-gyp/node_modules/nopt": { - "version": "5.0.0", + "node_modules/npm/node_modules/node-gyp/node_modules/cacache": { + "version": "18.0.4", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" }, "engines": { - "node": ">=6" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/nopt": { - "version": "6.0.0", + "node_modules/npm/node_modules/node-gyp/node_modules/isexe": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/make-fetch-happen": { + "version": "13.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "abbrev": "^1.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/normalize-package-data": { - "version": "4.0.1", + "node_modules/npm/node_modules/node-gyp/node_modules/minipass-fetch": { + "version": "3.0.5", "dev": true, "inBundle": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "hosted-git-info": "^5.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" } }, - "node_modules/npm/node_modules/npm-audit-report": { - "version": "3.0.0", + "node_modules/npm/node_modules/node-gyp/node_modules/nopt": { + "version": "7.2.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "chalk": "^4.0.0" + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/npm-bundled": { - "version": "2.0.1", + "node_modules/npm/node_modules/node-gyp/node_modules/proc-log": { + "version": "4.2.0", "dev": true, "inBundle": true, "license": "ISC", - "dependencies": { - "npm-normalize-package-bin": "^2.0.0" - }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/npm-bundled/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", + "node_modules/npm/node_modules/node-gyp/node_modules/ssri": { + "version": "10.0.6", "dev": true, "inBundle": true, "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/npm-install-checks": { - "version": "5.0.0", + "node_modules/npm/node_modules/node-gyp/node_modules/unique-filename": { + "version": "3.0.0", "dev": true, "inBundle": true, - "license": "BSD-2-Clause", + "license": "ISC", "dependencies": { - "semver": "^7.1.1" + "unique-slug": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/npm-normalize-package-bin": { - "version": "1.0.1", + "node_modules/npm/node_modules/node-gyp/node_modules/unique-slug": { + "version": "4.0.0", "dev": true, "inBundle": true, - "license": "ISC" + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, - "node_modules/npm/node_modules/npm-package-arg": { - "version": "9.1.0", + "node_modules/npm/node_modules/node-gyp/node_modules/which": { + "version": "4.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "hosted-git-info": "^5.0.0", - "proc-log": "^2.0.1", - "semver": "^7.3.5", - "validate-npm-package-name": "^4.0.0" + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/npm-packlist": { - "version": "5.1.3", + "node_modules/npm/node_modules/nopt": { + "version": "8.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "glob": "^8.0.1", - "ignore-walk": "^5.0.1", - "npm-bundled": "^2.0.0", - "npm-normalize-package-bin": "^2.0.0" + "abbrev": "^2.0.0" }, "bin": { - "npm-packlist": "bin/index.js" + "nopt": "bin/nopt.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/npm-packlist/node_modules/npm-normalize-package-bin": { + "node_modules/npm/node_modules/nopt/node_modules/abbrev": { "version": "2.0.0", "dev": true, "inBundle": true, "license": "ISC", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/npm-pick-manifest": { - "version": "7.0.2", + "node_modules/npm/node_modules/normalize-package-data": { + "version": "7.0.0", "dev": true, "inBundle": true, - "license": "ISC", + "license": "BSD-2-Clause", "dependencies": { - "npm-install-checks": "^5.0.0", - "npm-normalize-package-bin": "^2.0.0", - "npm-package-arg": "^9.0.0", - "semver": "^7.3.5" + "hosted-git-info": "^8.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/npm-pick-manifest/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", + "node_modules/npm/node_modules/npm-audit-report": { + "version": "6.0.0", "dev": true, "inBundle": true, "license": "ISC", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/npm-profile": { - "version": "6.2.1", + "node_modules/npm/node_modules/npm-bundled": { + "version": "4.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "npm-registry-fetch": "^13.0.1", - "proc-log": "^2.0.0" + "npm-normalize-package-bin": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/npm-registry-fetch": { - "version": "13.3.1", + "node_modules/npm/node_modules/npm-install-checks": { + "version": "7.1.0", "dev": true, "inBundle": true, - "license": "ISC", + "license": "BSD-2-Clause", "dependencies": { - "make-fetch-happen": "^10.0.6", - "minipass": "^3.1.6", - "minipass-fetch": "^2.0.3", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.1.2", - "npm-package-arg": "^9.0.1", - "proc-log": "^2.0.0" + "semver": "^7.1.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/npm-user-validate": { - "version": "1.0.1", + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "4.0.0", "dev": true, "inBundle": true, - "license": "BSD-2-Clause" + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } }, - "node_modules/npm/node_modules/npmlog": { - "version": "6.0.2", + "node_modules/npm/node_modules/npm-package-arg": { + "version": "12.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/once": { - "version": "1.4.0", + "node_modules/npm/node_modules/npm-packlist": { + "version": "9.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "wrappy": "1" + "ignore-walk": "^7.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/opener": { - "version": "1.5.2", + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "10.0.0", "dev": true, "inBundle": true, - "license": "(WTFPL OR MIT)", - "bin": { - "opener": "bin/opener-bin.js" + "license": "ISC", + "dependencies": { + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^12.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "11.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "18.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^3.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^14.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^12.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch/node_modules/minizlib": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/p-map": { @@ -9442,66 +8453,84 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/npm/node_modules/package-json-from-dist": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0" + }, "node_modules/npm/node_modules/pacote": { - "version": "13.6.2", + "version": "19.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/git": "^3.0.0", - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/promise-spawn": "^3.0.0", - "@npmcli/run-script": "^4.1.0", - "cacache": "^16.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "infer-owner": "^1.0.4", - "minipass": "^3.1.6", - "mkdirp": "^1.0.4", - "npm-package-arg": "^9.0.0", - "npm-packlist": "^5.1.0", - "npm-pick-manifest": "^7.0.0", - "npm-registry-fetch": "^13.0.1", - "proc-log": "^2.0.0", + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^9.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", "promise-retry": "^2.0.1", - "read-package-json": "^5.0.0", - "read-package-json-fast": "^2.0.3", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", + "sigstore": "^2.2.0", + "ssri": "^12.0.0", "tar": "^6.1.11" }, "bin": { - "pacote": "lib/bin.js" + "pacote": "bin/index.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/parse-conflict-json": { - "version": "2.0.2", + "version": "4.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "json-parse-even-better-errors": "^2.3.1", - "just-diff": "^5.0.1", + "json-parse-even-better-errors": "^4.0.0", + "just-diff": "^6.0.0", "just-diff-apply": "^5.2.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/path-is-absolute": { - "version": "1.0.1", + "node_modules/npm/node_modules/path-key": { + "version": "3.1.1", "dev": true, "inBundle": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" + } + }, + "node_modules/npm/node_modules/path-scurry": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/npm/node_modules/postcss-selector-parser": { - "version": "6.0.10", + "version": "6.1.2", "dev": true, "inBundle": true, "license": "MIT", @@ -9514,12 +8543,21 @@ } }, "node_modules/npm/node_modules/proc-log": { - "version": "2.0.1", + "version": "5.0.0", "dev": true, "inBundle": true, "license": "ISC", "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/proggy": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/promise-all-reject-late": { @@ -9532,7 +8570,7 @@ } }, "node_modules/npm/node_modules/promise-call-limit": { - "version": "1.0.1", + "version": "3.0.1", "dev": true, "inBundle": true, "license": "ISC", @@ -9560,12 +8598,15 @@ } }, "node_modules/npm/node_modules/promzard": { - "version": "0.3.0", + "version": "2.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "read": "1" + "read": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/qrcode-terminal": { @@ -9577,87 +8618,37 @@ } }, "node_modules/npm/node_modules/read": { - "version": "1.0.7", + "version": "4.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "mute-stream": "~0.0.4" + "mute-stream": "^2.0.0" }, "engines": { - "node": ">=0.8" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/read-cmd-shim": { - "version": "3.0.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/read-package-json": { - "version": "5.0.2", + "version": "5.0.0", "dev": true, "inBundle": true, "license": "ISC", - "dependencies": { - "glob": "^8.0.1", - "json-parse-even-better-errors": "^2.3.1", - "normalize-package-data": "^4.0.0", - "npm-normalize-package-bin": "^2.0.0" - }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/read-package-json-fast": { - "version": "2.0.3", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "json-parse-even-better-errors": "^2.3.0", - "npm-normalize-package-bin": "^1.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/npm/node_modules/read-package-json/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", + "version": "4.0.0", "dev": true, "inBundle": true, "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npm/node_modules/readable-stream": { - "version": "3.6.0", - "dev": true, - "inBundle": true, - "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/npm/node_modules/readdir-scoped-modules": { - "version": "1.1.0", - "dev": true, - "inBundle": true, - "license": "ISC", - "dependencies": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm/node_modules/retry": { @@ -9670,130 +8661,91 @@ } }, "node_modules/npm/node_modules/rimraf": { - "version": "3.0.2", + "version": "5.0.10", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "glob": "^7.1.3" + "glob": "^10.3.7" }, "bin": { - "rimraf": "bin.js" + "rimraf": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/npm/node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", "dev": true, "inBundle": true, "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } + "optional": true }, - "node_modules/npm/node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", + "node_modules/npm/node_modules/semver": { + "version": "7.6.3", "dev": true, "inBundle": true, "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=10" } }, - "node_modules/npm/node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", + "node_modules/npm/node_modules/shebang-command": { + "version": "2.0.0", "dev": true, "inBundle": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "shebang-regex": "^3.0.0" }, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/npm/node_modules/safe-buffer": { - "version": "5.2.1", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/safer-buffer": { - "version": "2.1.2", + "node_modules/npm/node_modules/shebang-regex": { + "version": "3.0.0", "dev": true, "inBundle": true, "license": "MIT", - "optional": true + "engines": { + "node": ">=8" + } }, - "node_modules/npm/node_modules/semver": { - "version": "7.3.7", + "node_modules/npm/node_modules/signal-exit": { + "version": "4.1.0", "dev": true, "inBundle": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/npm/node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", + "node_modules/npm/node_modules/sigstore": { + "version": "2.3.1", "dev": true, "inBundle": true, - "license": "ISC", + "license": "Apache-2.0", "dependencies": { - "yallist": "^4.0.0" + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "@sigstore/sign": "^2.3.2", + "@sigstore/tuf": "^2.3.4", + "@sigstore/verify": "^1.2.1" }, "engines": { - "node": ">=10" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/set-blocking": { - "version": "2.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/signal-exit": { - "version": "3.0.7", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/smart-buffer": { - "version": "4.2.0", + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", "dev": true, "inBundle": true, "license": "MIT", @@ -9803,35 +8755,35 @@ } }, "node_modules/npm/node_modules/socks": { - "version": "2.7.0", + "version": "2.8.3", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "ip": "^2.0.0", + "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" }, "engines": { - "node": ">= 10.13.0", + "node": ">= 10.0.0", "npm": ">= 3.0.0" } }, "node_modules/npm/node_modules/socks-proxy-agent": { - "version": "7.0.0", + "version": "8.0.4", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" }, "engines": { - "node": ">= 10" + "node": ">= 14" } }, "node_modules/npm/node_modules/spdx-correct": { - "version": "3.1.1", + "version": "3.2.0", "dev": true, "inBundle": true, "license": "Apache-2.0", @@ -9840,14 +8792,24 @@ "spdx-license-ids": "^3.0.0" } }, + "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, "node_modules/npm/node_modules/spdx-exceptions": { - "version": "2.3.0", + "version": "2.5.0", "dev": true, "inBundle": true, "license": "CC-BY-3.0" }, "node_modules/npm/node_modules/spdx-expression-parse": { - "version": "3.0.1", + "version": "4.0.0", "dev": true, "inBundle": true, "license": "MIT", @@ -9857,33 +8819,45 @@ } }, "node_modules/npm/node_modules/spdx-license-ids": { - "version": "3.0.11", + "version": "3.0.18", "dev": true, "inBundle": true, "license": "CC0-1.0" }, + "node_modules/npm/node_modules/sprintf-js": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause" + }, "node_modules/npm/node_modules/ssri": { - "version": "9.0.1", + "version": "12.0.0", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "minipass": "^3.1.1" + "minipass": "^7.0.3" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm/node_modules/string_decoder": { - "version": "1.3.0", + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "safe-buffer": "~5.2.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/npm/node_modules/string-width": { + "node_modules/npm/node_modules/string-width-cjs": { + "name": "string-width", "version": "4.2.3", "dev": true, "inBundle": true, @@ -9909,315 +8883,518 @@ "node": ">=8" } }, - "node_modules/npm/node_modules/supports-color": { - "version": "7.2.0", + "node_modules/npm/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, + "node_modules/npm/node_modules/supports-color": { + "version": "9.4.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/npm/node_modules/tar": { - "version": "6.1.11", + "version": "6.2.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", + "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" }, "engines": { - "node": ">= 10" + "node": ">=10" } }, - "node_modules/npm/node_modules/text-table": { - "version": "0.2.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/tiny-relative-date": { - "version": "1.3.0", - "dev": true, - "inBundle": true, - "license": "MIT" - }, - "node_modules/npm/node_modules/treeverse": { - "version": "2.0.0", + "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", "dev": true, "inBundle": true, "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 8" } }, - "node_modules/npm/node_modules/unique-filename": { - "version": "2.0.1", + "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "unique-slug": "^3.0.0" + "yallist": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=8" } }, - "node_modules/npm/node_modules/unique-slug": { - "version": "3.0.0", + "node_modules/npm/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", "dev": true, "inBundle": true, "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4" - }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=8" } }, - "node_modules/npm/node_modules/util-deprecate": { - "version": "1.0.2", + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", "dev": true, "inBundle": true, "license": "MIT" }, - "node_modules/npm/node_modules/validate-npm-package-license": { - "version": "3.0.4", + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", "dev": true, "inBundle": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } + "license": "MIT" }, - "node_modules/npm/node_modules/validate-npm-package-name": { - "version": "4.0.0", + "node_modules/npm/node_modules/treeverse": { + "version": "3.0.0", "dev": true, "inBundle": true, "license": "ISC", - "dependencies": { - "builtins": "^5.0.0" - }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/walk-up-path": { - "version": "1.0.0", - "dev": true, - "inBundle": true, - "license": "ISC" - }, - "node_modules/npm/node_modules/wcwidth": { - "version": "1.0.1", + "node_modules/npm/node_modules/tuf-js": { + "version": "2.2.1", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "defaults": "^1.0.3" + "@tufjs/models": "2.0.1", + "debug": "^4.3.4", + "make-fetch-happen": "^13.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/which": { - "version": "2.0.2", + "node_modules/npm/node_modules/tuf-js/node_modules/@npmcli/agent": { + "version": "2.2.2", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" }, "engines": { - "node": ">= 8" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/wide-align": { - "version": "1.1.5", + "node_modules/npm/node_modules/tuf-js/node_modules/@npmcli/fs": { + "version": "3.1.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/wrappy": { - "version": "1.0.2", + "node_modules/npm/node_modules/tuf-js/node_modules/cacache": { + "version": "18.0.4", "dev": true, "inBundle": true, - "license": "ISC" + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } }, - "node_modules/npm/node_modules/write-file-atomic": { - "version": "4.0.2", + "node_modules/npm/node_modules/tuf-js/node_modules/make-fetch-happen": { + "version": "13.0.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/yallist": { - "version": "4.0.0", + "node_modules/npm/node_modules/tuf-js/node_modules/minipass-fetch": { + "version": "3.0.5", "dev": true, "inBundle": true, - "license": "ISC" + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "node_modules/npm/node_modules/tuf-js/node_modules/proc-log": { + "version": "4.2.0", "dev": true, + "inBundle": true, + "license": "ISC", "engines": { - "node": "*" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "node_modules/npm/node_modules/tuf-js/node_modules/ssri": { + "version": "10.0.6", "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, "engines": { - "node": ">=0.10.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "node_modules/npm/node_modules/tuf-js/node_modules/unique-filename": { + "version": "3.0.0", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" + "unique-slug": "^4.0.0" }, "engines": { - "node": ">=0.10.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/object-copy/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "node_modules/npm/node_modules/tuf-js/node_modules/unique-slug": { + "version": "4.0.0", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "is-descriptor": "^0.1.0" + "imurmurhash": "^0.1.4" }, "engines": { - "node": ">=0.10.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/object-copy/node_modules/is-descriptor": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", - "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "node_modules/npm/node_modules/unique-filename": { + "version": "4.0.0", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" + "unique-slug": "^5.0.0" }, "engines": { - "node": ">= 0.4" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/object-copy/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/npm/node_modules/unique-slug": { + "version": "5.0.0", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "is-buffer": "^1.1.5" + "imurmurhash": "^0.1.4" }, "engines": { - "node": ">=0.10.0" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "inBundle": true, + "license": "MIT" }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", "dev": true, - "engines": { - "node": ">= 0.4" + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, - "node_modules/object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "6.0.0", "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/which": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" }, "engines": { - "node": ">= 0.4" + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/which/node_modules/isexe": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/wrap-ansi": { + "version": "8.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "node_modules/npm/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "isobject": "^3.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/write-file-atomic": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/nwsapi": { + "version": "2.2.21", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.21.tgz", + "integrity": "sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==", + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.13.1", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, + "license": "ISC", "dependencies": { "wrappy": "1" } }, "node_modules/onetime": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -10228,11 +9405,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open": { + "version": "8.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", "dev": true, + "license": "MIT", "dependencies": { "deep-is": "~0.1.3", "fast-levenshtein": "~2.0.6", @@ -10245,145 +9437,118 @@ "node": ">= 0.8.0" } }, - "node_modules/os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", - "dev": true - }, "node_modules/os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/p-each-series": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", - "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", + "version": "3.0.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-filter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", - "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", + "version": "4.1.0", "dev": true, + "license": "MIT", "dependencies": { - "p-map": "^2.0.0" + "p-map": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-is-promise": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", - "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "version": "2.3.0", "dev": true, + "license": "MIT", "dependencies": { - "p-try": "^1.0.0" + "p-try": "^2.0.0" }, "engines": { - "node": ">=4" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "version": "4.1.0", "dev": true, + "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "p-limit": "^2.2.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/p-map": { + "version": "7.0.2", "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "node_modules/p-reduce": { + "version": "3.0.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-reduce": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", - "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", + "node_modules/p-retry": { + "version": "4.6.2", "dev": true, + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, "engines": { "node": ">=8" } }, "node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "version": "2.2.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" - } - }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "node_modules/parallel-transform": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", - "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", - "dev": true, - "dependencies": { - "cyclist": "^1.0.1", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" + "node": ">=6" } }, "node_modules/parent-module": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -10391,48 +9556,10 @@ "node": ">=6" } }, - "node_modules/parse-asn1": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", - "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", - "dev": true, - "dependencies": { - "asn1.js": "^4.10.1", - "browserify-aes": "^1.2.0", - "evp_bytestokey": "^1.0.3", - "hash-base": "~3.0", - "pbkdf2": "^3.1.2", - "safe-buffer": "^5.2.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/parse-asn1/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "node_modules/parse-json": { + "version": "5.2.0", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -10446,127 +9573,82 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", + "node_modules/parseurl": { + "version": "1.3.3", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.8" } }, - "node_modules/path-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", - "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", - "dev": true - }, - "node_modules/path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", - "dev": true, - "optional": true - }, "node_modules/path-exists": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/path-is-inside": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", - "dev": true + "dev": true, + "license": "(WTFPL OR MIT)" }, "node_modules/path-key": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-parse": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==", + "node_modules/path-to-regexp": { + "version": "0.1.7", "dev": true, - "dependencies": { - "through": "~2.3" - } + "license": "MIT" }, - "node_modules/pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "node_modules/path-type": { + "version": "4.0.0", "dev": true, - "dependencies": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - }, + "license": "MIT", "engines": { - "node": ">=0.12" + "node": ">=8" } }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "dev": true - }, "node_modules/picocolors": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -10576,39 +9658,16 @@ }, "node_modules/pify": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", - "dev": true, - "dependencies": { - "pinkie": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/pkg-conf": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", - "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", "dev": true, + "license": "MIT", "dependencies": { "find-up": "^2.0.0", "load-json-file": "^4.0.0" @@ -10619,9 +9678,8 @@ }, "node_modules/pkg-conf/node_modules/find-up": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^2.0.0" }, @@ -10631,9 +9689,8 @@ }, "node_modules/pkg-conf/node_modules/locate-path": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^2.0.0", "path-exists": "^3.0.0" @@ -10642,271 +9699,145 @@ "node": ">=4" } }, - "node_modules/pkg-conf/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "node_modules/pkg-conf/node_modules/p-limit": { + "version": "1.3.0", "dev": true, + "license": "MIT", "dependencies": { - "p-limit": "^1.1.0" + "p-try": "^1.0.0" }, "engines": { "node": ">=4" } }, - "node_modules/pkg-conf/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-dir": { + "node_modules/pkg-conf/node_modules/p-locate": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha512-ojakdnUgL5pzJYWw2AIDEupaQCX5OPbM688ZevubICjdIX01PRSYKqm33fJoCOJBRseYCTUlQRnBNX+Pchaejw==", "dev": true, + "license": "MIT", "dependencies": { - "find-up": "^2.1.0" + "p-limit": "^1.1.0" }, "engines": { "node": ">=4" } }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "node_modules/pkg-conf/node_modules/p-try": { + "version": "1.0.0", "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, + "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "node_modules/pkg-conf/node_modules/path-exists": { + "version": "3.0.0", "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, + "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "node_modules/pkg-dir": { + "version": "4.2.0", "dev": true, + "license": "MIT", "dependencies": { - "p-limit": "^1.1.0" + "find-up": "^4.0.0" }, "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-dir/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/pluralize": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/prelude-ls": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", "dev": true, "engines": { "node": ">= 0.8.0" } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true, - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/process-nextick-args": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/progress": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4.0" } }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "dev": true - }, "node_modules/proto-list": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", - "dev": true - }, - "node_modules/prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", - "dev": true - }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", - "dev": true - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true + "dev": true, + "license": "ISC" }, - "node_modules/public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "node_modules/proxy-addr": { + "version": "2.0.7", "dev": true, + "license": "MIT", "dependencies": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" } }, - "node_modules/public-encrypt/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "node_modules/pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "license": "MIT", + "engines": { + "node": ">= 0.10" } }, - "node_modules/pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "node_modules/pseudomap": { + "version": "1.0.2", "dev": true, - "dependencies": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - } + "license": "ISC" }, "node_modules/punycode": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/q": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha512-/CdEdaw49VZVmyIDGUQKDDT53c7qBkO6g5CefWz91Ae+l4+cRtcDYwMTXh6me4O8TMldeGHG3N2Bl84V78Ywbg==", + "version": "1.5.1", "dev": true, + "license": "MIT", "engines": { "node": ">=0.6.0", "teleport": ">=0.2.0" } }, - "node_modules/q-io": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/q-io/-/q-io-1.13.2.tgz", - "integrity": "sha512-5PzgT/jVGwfwgpbN2KxjcaYdaWZyoTPeyQjM1toS5mLwlnRb5BxpnGTCQLUgMIs7b31MuIn14ZJd5pvQlvv8Xw==", + "node_modules/qs": { + "version": "6.11.0", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "collections": "^0.2.0", - "mime": "^1.2.11", - "mimeparse": "^0.1.4", - "q": "^1.0.1", - "qs": "^1.2.1", - "url2": "^0.0.0" + "side-channel": "^1.0.4" }, "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/qs": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-1.2.2.tgz", - "integrity": "sha512-xEqT+49YIt+BdwQthXKTOkp7atENe6JqrGGerxBPiER6BArOIiVJtpZZYpWOpq2IOkTPVnDM8CgYvppFoJNwyQ==", - "dev": true - }, - "node_modules/querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", - "dev": true, - "engines": { - "node": ">=0.4.x" + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/queue-microtask": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ { @@ -10921,41 +9852,70 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/quick-lru": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/randombytes": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" } }, - "node_modules/randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "node_modules/range-parser": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", "dev": true, + "license": "MIT", "dependencies": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.4.24", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" } }, "node_modules/rc": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -10968,9 +9928,8 @@ }, "node_modules/read-pkg": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", "dev": true, + "license": "MIT", "dependencies": { "@types/normalize-package-data": "^2.4.0", "normalize-package-data": "^2.5.0", @@ -10983,9 +9942,8 @@ }, "node_modules/read-pkg-up": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", "dev": true, + "license": "MIT", "dependencies": { "find-up": "^4.1.0", "read-pkg": "^5.2.0", @@ -10998,87 +9956,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=8" } }, "node_modules/read-pkg/node_modules/hosted-git-info": { "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/read-pkg/node_modules/normalize-package-data": { "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -11088,43 +9982,37 @@ }, "node_modules/read-pkg/node_modules/semver": { "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver" } }, "node_modules/read-pkg/node_modules/type-fest": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=8" } }, "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "version": "3.6.2", "dev": true, + "license": "MIT", "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, "node_modules/readdirp": { "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, - "optional": true, + "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, @@ -11132,11 +10020,21 @@ "node": ">=8.10.0" } }, + "node_modules/rechoir": { + "version": "0.7.1", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "^1.9.0" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/redent": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", "dev": true, + "license": "MIT", "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" @@ -11147,58 +10045,24 @@ }, "node_modules/redeyed": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-2.1.1.tgz", - "integrity": "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==", "dev": true, + "license": "MIT", "dependencies": { "esprima": "~4.0.0" } }, - "node_modules/regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "dependencies": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/regexpp": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", - "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", "dev": true, + "license": "MIT", "engines": { "node": ">=4.0.0" } }, "node_modules/registry-auth-token": { "version": "5.0.2", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz", - "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", "dev": true, + "license": "MIT", "dependencies": { "@pnpm/npm-conf": "^2.1.0" }, @@ -11206,109 +10070,26 @@ "node": ">=14" } }, - "node_modules/remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", - "dev": true - }, - "node_modules/repeat-element": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", - "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/replace-ext": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", - "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dev": true, - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request/node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, "node_modules/require-directory": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/require-from-string": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, "node_modules/require-uncached": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha512-Xct+41K3twrbBHdxAgMoOS+cNcoqIjfM2/VxBF4LL2hVph7YsF8VSKyQ3BDFZwEVbok9yeDl2le/qo0S77WG2w==", "dev": true, + "license": "MIT", "dependencies": { "caller-path": "^0.1.0", "resolve-from": "^1.0.0" @@ -11319,18 +10100,21 @@ }, "node_modules/require-uncached/node_modules/resolve-from": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha512-kT10v4dhrlLNcnO084hEjvXCI1wUG9qZLoz2RogxqDQQYy7IxjI/iMUkOtQTNEh6rzHxvdQWHsJyel1pKOVCxg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, + "license": "MIT", "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -11344,83 +10128,28 @@ } }, "node_modules/resolve-cwd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==", - "dev": true, - "dependencies": { - "resolve-from": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", - "dev": true, - "dependencies": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-dir/node_modules/global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "dependencies": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-dir/node_modules/global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", "dev": true, + "license": "MIT", "dependencies": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" + "resolve-from": "^5.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/resolve-from": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/resolve-global": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", - "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", "dev": true, + "license": "MIT", "dependencies": { "global-dirs": "^0.1.1" }, @@ -11428,18 +10157,10 @@ "node": ">=8" } }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", - "deprecated": "https://github.com/lydell/resolve-url#deprecated", - "dev": true - }, "node_modules/restore-cursor": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", "dev": true, + "license": "MIT", "dependencies": { "onetime": "^2.0.0", "signal-exit": "^3.0.2" @@ -11450,18 +10171,16 @@ }, "node_modules/restore-cursor/node_modules/mimic-fn": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/restore-cursor/node_modules/onetime": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", "dev": true, + "license": "MIT", "dependencies": { "mimic-fn": "^1.0.0" }, @@ -11469,60 +10188,53 @@ "node": ">=4" } }, - "node_modules/ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "node_modules/retry": { + "version": "0.13.1", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.12" + "node": ">= 4" } }, "node_modules/reusify": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, + "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" } }, "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "version": "3.0.2", "dev": true, + "license": "ISC", "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } + "node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "license": "MIT" }, "node_modules/run-async": { "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, "node_modules/run-parallel": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { @@ -11538,1446 +10250,3117 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } }, - "node_modules/run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==", + "node_modules/rx-lite": { + "version": "4.0.8", + "dev": true + }, + "node_modules/rx-lite-aggregates": { + "version": "4.0.8", + "dev": true, + "dependencies": { + "rx-lite": "*" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/schema-utils": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/scratch-semantic-release-config": { + "version": "1.0.16", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/changelog": "^6.0.1", + "@semantic-release/commit-analyzer": "^9.0.2", + "@semantic-release/git": "^10.0.1", + "@semantic-release/github": "^8.0.4", + "@semantic-release/npm": "^9.0.1", + "@semantic-release/release-notes-generator": "^10.0.3" + }, + "peerDependencies": { + "semantic-release": ">=19.0.2" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/@octokit/auth-token": { + "version": "3.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/@octokit/core": { + "version": "4.2.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^3.0.0", + "@octokit/graphql": "^5.0.0", + "@octokit/request": "^6.0.0", + "@octokit/request-error": "^3.0.0", + "@octokit/types": "^9.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/@octokit/endpoint": { + "version": "7.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^9.0.0", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/@octokit/graphql": { + "version": "5.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request": "^6.0.0", + "@octokit/types": "^9.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/@octokit/openapi-types": { + "version": "18.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/scratch-semantic-release-config/node_modules/@octokit/plugin-paginate-rest": { + "version": "6.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/tsconfig": "^1.0.2", + "@octokit/types": "^9.2.3" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "@octokit/core": ">=4" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/@octokit/plugin-retry": { + "version": "4.1.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^9.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/@octokit/plugin-throttling": { + "version": "5.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^9.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "@octokit/core": "^4.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/@octokit/request": { + "version": "6.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^7.0.0", + "@octokit/request-error": "^3.0.0", + "@octokit/types": "^9.0.0", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/@octokit/request-error": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^9.0.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/@octokit/types": { + "version": "9.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^18.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/@semantic-release/commit-analyzer": { + "version": "9.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-changelog-angular": "^5.0.0", + "conventional-commits-filter": "^2.0.0", + "conventional-commits-parser": "^3.2.3", + "debug": "^4.0.0", + "import-from": "^4.0.0", + "lodash": "^4.17.4", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0-beta.1" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/@semantic-release/error": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.17" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/@semantic-release/github": { + "version": "8.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/core": "^4.2.1", + "@octokit/plugin-paginate-rest": "^6.1.2", + "@octokit/plugin-retry": "^4.1.3", + "@octokit/plugin-throttling": "^5.2.3", + "@semantic-release/error": "^3.0.0", + "aggregate-error": "^3.0.0", + "debug": "^4.0.0", + "dir-glob": "^3.0.0", + "fs-extra": "^11.0.0", + "globby": "^11.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "issue-parser": "^6.0.0", + "lodash": "^4.17.4", + "mime": "^3.0.0", + "p-filter": "^2.0.0", + "url-join": "^4.0.0" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0-beta.1" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/@semantic-release/npm": { + "version": "9.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/error": "^3.0.0", + "aggregate-error": "^3.0.0", + "execa": "^5.0.0", + "fs-extra": "^11.0.0", + "lodash": "^4.17.15", + "nerf-dart": "^1.0.0", + "normalize-url": "^6.0.0", + "npm": "^8.3.0", + "rc": "^1.2.8", + "read-pkg": "^5.0.0", + "registry-auth-token": "^5.0.0", + "semver": "^7.1.2", + "tempy": "^1.0.0" + }, + "engines": { + "node": ">=16 || ^14.17" + }, + "peerDependencies": { + "semantic-release": ">=19.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/@semantic-release/release-notes-generator": { + "version": "10.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-changelog-angular": "^5.0.0", + "conventional-changelog-writer": "^5.0.0", + "conventional-commits-filter": "^2.0.0", + "conventional-commits-parser": "^3.2.3", + "debug": "^4.0.0", + "get-stream": "^6.0.0", + "import-from": "^4.0.0", + "into-stream": "^6.0.0", + "lodash": "^4.17.4", + "read-pkg-up": "^7.0.0" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0-beta.1" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/aggregate-error": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/clean-stack": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/conventional-changelog-angular": { + "version": "5.0.13", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0", + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/conventional-changelog-writer": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-filter": "^2.0.7", + "dateformat": "^3.0.0", + "handlebars": "^4.7.7", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "semver": "^6.0.0", + "split": "^1.0.0", + "through2": "^4.0.0" + }, + "bin": { + "conventional-changelog-writer": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/conventional-changelog-writer/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/conventional-commits-filter": { + "version": "2.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/conventional-commits-parser": { + "version": "3.2.4", + "dev": true, + "license": "MIT", + "dependencies": { + "is-text-path": "^1.0.1", + "JSONStream": "^1.0.4", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/crypto-random-string": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/globby": { + "version": "11.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/ignore": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/into-stream": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "from2": "^2.3.0", + "p-is-promise": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/is-plain-object": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/mime": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/normalize-url": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm": { + "version": "8.19.4", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/ci-detect", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/run-script", + "abbrev", + "archy", + "cacache", + "chalk", + "chownr", + "cli-columns", + "cli-table3", + "columnify", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmhook", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "mkdirp", + "mkdirp-infer-owner", + "ms", + "node-gyp", + "nopt", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "npmlog", + "opener", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "read-package-json", + "read-package-json-fast", + "readdir-scoped-modules", + "rimraf", + "semver", + "ssri", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which", + "write-file-atomic" + ], + "dev": true, + "license": "Artistic-2.0", + "workspaces": [ + "docs", + "smoke-tests", + "workspaces/*" + ], + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^5.6.3", + "@npmcli/ci-detect": "^2.0.0", + "@npmcli/config": "^4.2.1", + "@npmcli/fs": "^2.1.0", + "@npmcli/map-workspaces": "^2.0.3", + "@npmcli/package-json": "^2.0.0", + "@npmcli/run-script": "^4.2.1", + "abbrev": "~1.1.1", + "archy": "~1.0.0", + "cacache": "^16.1.3", + "chalk": "^4.1.2", + "chownr": "^2.0.0", + "cli-columns": "^4.0.0", + "cli-table3": "^0.6.2", + "columnify": "^1.6.0", + "fastest-levenshtein": "^1.0.12", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "graceful-fs": "^4.2.10", + "hosted-git-info": "^5.2.1", + "ini": "^3.0.1", + "init-package-json": "^3.0.2", + "is-cidr": "^4.0.2", + "json-parse-even-better-errors": "^2.3.1", + "libnpmaccess": "^6.0.4", + "libnpmdiff": "^4.0.5", + "libnpmexec": "^4.0.14", + "libnpmfund": "^3.0.5", + "libnpmhook": "^8.0.4", + "libnpmorg": "^4.0.4", + "libnpmpack": "^4.1.3", + "libnpmpublish": "^6.0.5", + "libnpmsearch": "^5.0.4", + "libnpmteam": "^4.0.4", + "libnpmversion": "^3.0.7", + "make-fetch-happen": "^10.2.0", + "minimatch": "^5.1.0", + "minipass": "^3.1.6", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "ms": "^2.1.2", + "node-gyp": "^9.1.0", + "nopt": "^6.0.0", + "npm-audit-report": "^3.0.0", + "npm-install-checks": "^5.0.0", + "npm-package-arg": "^9.1.0", + "npm-pick-manifest": "^7.0.2", + "npm-profile": "^6.2.0", + "npm-registry-fetch": "^13.3.1", + "npm-user-validate": "^1.0.1", + "npmlog": "^6.0.2", + "opener": "^1.5.2", + "p-map": "^4.0.0", + "pacote": "^13.6.2", + "parse-conflict-json": "^2.0.2", + "proc-log": "^2.0.1", + "qrcode-terminal": "^0.12.0", + "read": "~1.0.7", + "read-package-json": "^5.0.2", + "read-package-json-fast": "^2.0.3", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.7", + "ssri": "^9.0.1", + "tar": "^6.1.11", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^2.0.0", + "validate-npm-package-name": "^4.0.0", + "which": "^2.0.2", + "write-file-atomic": "^4.0.1" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/@colors/colors": { + "version": "1.5.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/@gar/promisify": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/@npmcli/arborist": { + "version": "5.6.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/map-workspaces": "^2.0.3", + "@npmcli/metavuln-calculator": "^3.0.1", + "@npmcli/move-file": "^2.0.0", + "@npmcli/name-from-folder": "^1.0.1", + "@npmcli/node-gyp": "^2.0.0", + "@npmcli/package-json": "^2.0.0", + "@npmcli/query": "^1.2.0", + "@npmcli/run-script": "^4.1.3", + "bin-links": "^3.0.3", + "cacache": "^16.1.3", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^5.2.1", + "json-parse-even-better-errors": "^2.3.1", + "json-stringify-nice": "^1.1.4", + "minimatch": "^5.1.0", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "nopt": "^6.0.0", + "npm-install-checks": "^5.0.0", + "npm-package-arg": "^9.0.0", + "npm-pick-manifest": "^7.0.2", + "npm-registry-fetch": "^13.0.0", + "npmlog": "^6.0.2", + "pacote": "^13.6.1", + "parse-conflict-json": "^2.0.1", + "proc-log": "^2.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^1.0.1", + "read-package-json-fast": "^2.0.2", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.7", + "ssri": "^9.0.0", + "treeverse": "^2.0.0", + "walk-up-path": "^1.0.0" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/@npmcli/ci-detect": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/@npmcli/config": { + "version": "4.2.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^2.0.2", + "ini": "^3.0.0", + "mkdirp-infer-owner": "^2.0.0", + "nopt": "^6.0.0", + "proc-log": "^2.0.0", + "read-package-json-fast": "^2.0.3", + "semver": "^7.3.5", + "walk-up-path": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/@npmcli/disparity-colors": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ansi-styles": "^4.3.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/@npmcli/fs": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/@npmcli/git": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^3.0.0", + "lru-cache": "^7.4.4", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^7.0.0", + "proc-log": "^2.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "1.0.7", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "installed-package-contents": "index.js" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/@npmcli/installed-package-contents/node_modules/npm-bundled": { + "version": "1.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "2.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^1.0.1", + "glob": "^8.0.1", + "minimatch": "^5.0.1", + "read-package-json-fast": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^16.0.0", + "json-parse-even-better-errors": "^2.3.1", + "pacote": "^13.0.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/@npmcli/move-file": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/@npmcli/package-json": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "infer-owner": "^1.0.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/@npmcli/query": { + "version": "1.2.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^9.1.0", + "postcss-selector-parser": "^6.0.10", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/@npmcli/run-script": { + "version": "4.2.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^2.0.0", + "@npmcli/promise-spawn": "^3.0.0", + "node-gyp": "^9.0.0", + "read-package-json-fast": "^2.0.3", + "which": "^2.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/@tootallnate/once": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/abbrev": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/agent-base": { + "version": "6.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/agentkeepalive": { + "version": "4.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/aggregate-error": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/are-we-there-yet": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/asap": { + "version": "2.0.6", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/bin-links": { + "version": "3.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^5.0.0", + "mkdirp-infer-owner": "^2.0.0", + "npm-normalize-package-bin": "^2.0.0", + "read-cmd-shim": "^3.0.0", + "rimraf": "^3.0.0", + "write-file-atomic": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/bin-links/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/binary-extensions": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/builtins": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/cacache": { + "version": "16.1.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/cidr-regex": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^4.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/clean-stack": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/cli-table3": { + "version": "0.6.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/clone": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/cmd-shim": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "mkdirp-infer-owner": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/color-support": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/columnify": { + "version": "1.6.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "strip-ansi": "^6.0.1", + "wcwidth": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/console-control-strings": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/debug": { + "version": "4.3.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/debuglog": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/defaults": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/delegates": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/depd": { + "version": "1.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/dezalgo": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/diff": { + "version": "5.1.0", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.12", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/fs-minipass": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/function-bind": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/gauge": { + "version": "4.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/glob": { + "version": "8.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.10", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/has": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/has-unicode": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/hosted-git-info": { + "version": "5.2.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/http-proxy-agent": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/https-proxy-agent": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/humanize-ms": { + "version": "1.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/ignore-walk": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^5.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/indent-string": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/infer-owner": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/ini": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/init-package-json": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^9.0.1", + "promzard": "^0.3.0", + "read": "^1.0.7", + "read-package-json": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/ip": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/ip-regex": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/is-cidr": { + "version": "4.0.2", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^3.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/is-core-module": { + "version": "2.10.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/is-lambda": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/just-diff": { + "version": "5.1.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/just-diff-apply": { + "version": "5.4.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/libnpmaccess": { + "version": "6.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "minipass": "^3.1.1", + "npm-package-arg": "^9.0.1", + "npm-registry-fetch": "^13.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/libnpmdiff": { + "version": "4.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/disparity-colors": "^2.0.0", + "@npmcli/installed-package-contents": "^1.0.7", + "binary-extensions": "^2.2.0", + "diff": "^5.1.0", + "minimatch": "^5.0.1", + "npm-package-arg": "^9.0.1", + "pacote": "^13.6.1", + "tar": "^6.1.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/libnpmexec": { + "version": "4.0.14", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^5.6.3", + "@npmcli/ci-detect": "^2.0.0", + "@npmcli/fs": "^2.1.1", + "@npmcli/run-script": "^4.2.0", + "chalk": "^4.1.0", + "mkdirp-infer-owner": "^2.0.0", + "npm-package-arg": "^9.0.1", + "npmlog": "^6.0.2", + "pacote": "^13.6.1", + "proc-log": "^2.0.0", + "read": "^1.0.7", + "read-package-json-fast": "^2.0.2", + "semver": "^7.3.7", + "walk-up-path": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/libnpmfund": { + "version": "3.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^5.6.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/libnpmhook": { + "version": "8.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^13.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/libnpmorg": { + "version": "4.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^13.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/libnpmpack": { + "version": "4.1.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/run-script": "^4.1.3", + "npm-package-arg": "^9.0.1", + "pacote": "^13.6.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/libnpmpublish": { + "version": "6.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "normalize-package-data": "^4.0.0", + "npm-package-arg": "^9.0.1", + "npm-registry-fetch": "^13.0.0", + "semver": "^7.3.7", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/libnpmsearch": { + "version": "5.0.4", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "aproba": "^1.1.1" + "npm-registry-fetch": "^13.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/rx-lite": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", - "integrity": "sha512-Cun9QucwK6MIrp3mry/Y7hqD1oFqTYLQ4pGxaHTjIdaFDWRGGLikqp6u8LcWJnzpoALg9hap+JGk8sFIUuEGNA==", - "dev": true - }, - "node_modules/rx-lite-aggregates": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", - "integrity": "sha512-3xPNZGW93oCjiO7PtKxRK6iOVYBWBvtf9QHDfU23Oc+dLIQmAV//UnyXV/yihv81VS/UqoQPk4NegS8EFi55Hg==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/libnpmteam": { + "version": "4.0.4", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "rx-lite": "*" + "aproba": "^2.0.0", + "npm-registry-fetch": "^13.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/libnpmversion": { + "version": "3.0.7", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" + "@npmcli/git": "^3.0.0", + "@npmcli/run-script": "^4.1.3", + "json-parse-even-better-errors": "^2.3.1", + "proc-log": "^2.0.0", + "semver": "^7.3.7" }, "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/safe-array-concat/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/lru-cache": { + "version": "7.13.2", "dev": true, - "dependencies": { - "ret": "~0.1.10" + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=12" } }, - "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/make-fetch-happen": { + "version": "10.2.1", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-regex": "^1.1.4" + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "node_modules/schema-utils": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", - "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/minimatch": { + "version": "5.1.0", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 4" + "node": ">=10" } }, - "node_modules/schema-utils/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/minipass": { + "version": "3.3.4", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "yallist": "^4.0.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">=8" } }, - "node_modules/schema-utils/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/minipass-collect": { + "version": "1.0.2", "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/schema-utils/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/scratch-l10n": { - "version": "3.18.106", - "resolved": "https://registry.npmjs.org/scratch-l10n/-/scratch-l10n-3.18.106.tgz", - "integrity": "sha512-PFhA01EYenlHV0NqYscmb0EunzoBelwwTF+Wpa9nIkd1TycI9DnG5j8ZqmTZKl1ewFn45GY8SRIEvrWeWJJABA==", - "bin": { - "build-i18n-src": "scripts/build-i18n-src.js", - "tx-push-src": "scripts/tx-push-src.js" + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/scratch-semantic-release-config": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/scratch-semantic-release-config/-/scratch-semantic-release-config-1.0.14.tgz", - "integrity": "sha512-lEPnAsP614FBcxMrBSrCDxuAdvYlUAGthiiTpqm3rhNBCuPTvVbrNo22yXWVXY3+ZtlrSNfkVKBtBKagDlexJw==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/minipass-fetch": { + "version": "2.1.1", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "@semantic-release/changelog": "^6.0.1", - "@semantic-release/commit-analyzer": "^9.0.2", - "@semantic-release/git": "^10.0.1", - "@semantic-release/github": "^8.0.4", - "@semantic-release/npm": "^9.0.1", - "@semantic-release/release-notes-generator": "^10.0.3" + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" }, - "peerDependencies": { - "semantic-release": ">=19.0.2" + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" } }, - "node_modules/selenium-webdriver": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.16.0.tgz", - "integrity": "sha512-IbqpRpfGE7JDGgXHJeWuCqT/tUqnLvZ14csSwt+S8o4nJo3RtQoE9VR4jB47tP/A8ArkYsh/THuMY6kyRP6kuA==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "jszip": "^3.10.1", - "tmp": "^0.2.1", - "ws": ">=8.14.2" + "minipass": "^3.0.0" }, "engines": { - "node": ">= 14.20.0" + "node": ">= 8" } }, - "node_modules/selenium-webdriver/node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/minipass-json-stream": { + "version": "1.0.1", "dev": true, - "engines": { - "node": ">=14.14" + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" } }, - "node_modules/semantic-release": { - "version": "19.0.5", - "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-19.0.5.tgz", - "integrity": "sha512-NMPKdfpXTnPn49FDogMBi36SiBfXkSOJqCkk0E4iWOY1tusvvgBwqUmxTX1kmlT6kIYed9YwNKD1sfPpqa5yaA==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "@semantic-release/commit-analyzer": "^9.0.2", - "@semantic-release/error": "^3.0.0", - "@semantic-release/github": "^8.0.0", - "@semantic-release/npm": "^9.0.0", - "@semantic-release/release-notes-generator": "^10.0.0", - "aggregate-error": "^3.0.0", - "cosmiconfig": "^7.0.0", - "debug": "^4.0.0", - "env-ci": "^5.0.0", - "execa": "^5.0.0", - "figures": "^3.0.0", - "find-versions": "^4.0.0", - "get-stream": "^6.0.0", - "git-log-parser": "^1.2.0", - "hook-std": "^2.0.0", - "hosted-git-info": "^4.0.0", - "lodash": "^4.17.21", - "marked": "^4.0.10", - "marked-terminal": "^5.0.0", - "micromatch": "^4.0.2", - "p-each-series": "^2.1.0", - "p-reduce": "^2.0.0", - "read-pkg-up": "^7.0.0", - "resolve-from": "^5.0.0", - "semver": "^7.3.2", - "semver-diff": "^3.1.1", - "signale": "^1.2.1", - "yargs": "^16.2.0" - }, - "bin": { - "semantic-release": "bin/semantic-release.js" + "minipass": "^3.0.0" }, "engines": { - "node": ">=16 || ^14.17" + "node": ">=8" } }, - "node_modules/semantic-release/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/semantic-release/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/minizlib": { + "version": "2.1.2", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/semantic-release/node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", "dev": true, - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" }, "engines": { "node": ">=10" } }, - "node_modules/semantic-release/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/mkdirp-infer-owner": { + "version": "2.0.0", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "ms": "2.1.2" + "chownr": "^2.0.0", + "infer-owner": "^1.0.4", + "mkdirp": "^1.0.3" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=10" } }, - "node_modules/semantic-release/node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/ms": { + "version": "2.1.3", "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "inBundle": true, + "license": "MIT" }, - "node_modules/semantic-release/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/mute-stream": { + "version": "0.0.8", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/negotiator": { + "version": "0.6.3", "dev": true, + "inBundle": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/semantic-release/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/semantic-release/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/node-gyp": { + "version": "9.1.0", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": ">=8" + "node": "^12.22 || ^14.13 || >=16" } }, - "node_modules/semantic-release/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/node-gyp/node_modules/brace-expansion": { + "version": "1.1.11", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/semantic-release/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/node-gyp/node_modules/glob": { + "version": "7.2.3", "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, "engines": { - "node": ">=10" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/semantic-release/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/node-gyp/node_modules/minimatch": { + "version": "3.1.2", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=10" + "node": "*" } }, - "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/node-gyp/node_modules/nopt": { + "version": "5.0.0", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "lru-cache": "^6.0.0" + "abbrev": "1" }, "bin": { - "semver": "bin/semver.js" + "nopt": "bin/nopt.js" }, "engines": { - "node": ">=10" + "node": ">=6" } }, - "node_modules/semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/nopt": { + "version": "6.0.0", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "semver": "^6.3.0" + "abbrev": "^1.0.0" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/semver-diff/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/semver-regex": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.4.tgz", - "integrity": "sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA==", - "dev": true, - "engines": { - "node": ">=8" + "nopt": "bin/nopt.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/normalize-package-data": { + "version": "4.0.1", "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", "dependencies": { - "yallist": "^4.0.0" + "hosted-git-info": "^5.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": ">=10" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/serialize-javascript": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz", - "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==", - "dev": true - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/npm-audit-report": { + "version": "3.0.0", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" + "chalk": "^4.0.0" }, "engines": { - "node": ">= 0.4" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/npm-bundled": { + "version": "2.0.1", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" + "npm-normalize-package-bin": "^2.0.0" }, "engines": { - "node": ">= 0.4" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/npm-bundled/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, + "inBundle": true, + "license": "ISC", "engines": { - "node": ">=0.10.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/set-value/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/npm-install-checks": { + "version": "5.0.0", "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", "dependencies": { - "is-extendable": "^0.1.0" + "semver": "^7.1.1" }, "engines": { - "node": ">=0.10.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/set-value/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "1.0.1", "dev": true, - "engines": { - "node": ">=0.10.0" - } + "inBundle": true, + "license": "ISC" }, - "node_modules/set-value/node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/npm-package-arg": { + "version": "9.1.0", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "isobject": "^3.0.1" + "hosted-git-info": "^5.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "validate-npm-package-name": "^4.0.0" }, "engines": { - "node": ">=0.10.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true - }, - "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/npm-packlist": { + "version": "5.1.3", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "glob": "^8.0.1", + "ignore-walk": "^5.0.1", + "npm-bundled": "^2.0.0", + "npm-normalize-package-bin": "^2.0.0" }, "bin": { - "sha.js": "bin.js" - } - }, - "node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" + "npm-packlist": "bin/index.js" }, "engines": { - "node": ">=0.10.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/npm-packlist/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", "dev": true, + "inBundle": true, + "license": "ISC", "engines": { - "node": ">=0.10.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/should": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", - "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/npm-pick-manifest": { + "version": "7.0.2", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "should-equal": "^2.0.0", - "should-format": "^3.0.3", - "should-type": "^1.4.0", - "should-type-adaptors": "^1.0.1", - "should-util": "^1.0.0" + "npm-install-checks": "^5.0.0", + "npm-normalize-package-bin": "^2.0.0", + "npm-package-arg": "^9.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/should-equal": { + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/npm-pick-manifest/node_modules/npm-normalize-package-bin": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", - "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", - "dev": true, - "dependencies": { - "should-type": "^1.4.0" - } - }, - "node_modules/should-format": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", - "integrity": "sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==", - "dev": true, - "dependencies": { - "should-type": "^1.3.0", - "should-type-adaptors": "^1.0.1" - } - }, - "node_modules/should-type": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", - "integrity": "sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==", - "dev": true - }, - "node_modules/should-type-adaptors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", - "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", "dev": true, - "dependencies": { - "should-type": "^1.3.0", - "should-util": "^1.0.0" + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/should-util": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", - "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", - "dev": true - }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/npm-profile": { + "version": "6.2.1", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "npm-registry-fetch": "^13.0.1", + "proc-log": "^2.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/signale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", - "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/npm-registry-fetch": { + "version": "13.3.1", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "chalk": "^2.3.2", - "figures": "^2.0.0", - "pkg-conf": "^2.1.0" + "make-fetch-happen": "^10.0.6", + "minipass": "^3.1.6", + "minipass-fetch": "^2.0.3", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^9.0.1", + "proc-log": "^2.0.0" }, "engines": { - "node": ">=6" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/signale/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/npm-user-validate": { + "version": "1.0.1", "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } + "inBundle": true, + "license": "BSD-2-Clause" }, - "node_modules/signale/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/npmlog": { + "version": "6.0.2", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" }, "engines": { - "node": ">=4" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/signale/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/once": { + "version": "1.4.0", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "color-name": "1.1.3" + "wrappy": "1" } }, - "node_modules/signale/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/signale/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/opener": { + "version": "1.5.2", "dev": true, - "engines": { - "node": ">=4" + "inBundle": true, + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" } }, - "node_modules/signale/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/p-map": { + "version": "4.0.0", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "aggregate-error": "^3.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/pacote": { + "version": "13.6.2", "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^3.0.0", + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/promise-spawn": "^3.0.0", + "@npmcli/run-script": "^4.1.0", + "cacache": "^16.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.6", + "mkdirp": "^1.0.4", + "npm-package-arg": "^9.0.0", + "npm-packlist": "^5.1.0", + "npm-pick-manifest": "^7.0.0", + "npm-registry-fetch": "^13.0.1", + "proc-log": "^2.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^5.0.0", + "read-package-json-fast": "^2.0.3", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" + }, "engines": { - "node": ">=0.10.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/slice-ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/parse-conflict-json": { + "version": "2.0.2", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "is-fullwidth-code-point": "^2.0.0" + "json-parse-even-better-errors": "^2.3.1", + "just-diff": "^5.0.1", + "just-diff-apply": "^5.2.0" }, "engines": { - "node": ">=4" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/path-is-absolute": { + "version": "1.0.1", "dev": true, - "dependencies": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, + "inBundle": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/postcss-selector-parser": { + "version": "6.0.10", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/snapdragon-node/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/proc-log": { + "version": "2.0.1", "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, + "inBundle": true, + "license": "ISC", "engines": { - "node": ">=0.10.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", "dev": true, - "dependencies": { - "kind-of": "^3.2.0" - }, - "engines": { - "node": ">=0.10.0" + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/snapdragon-util/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/promise-call-limit": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/promise-inflight": { + "version": "1.0.1", "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "is-buffer": "^1.1.5" + "err-code": "^2.0.2", + "retry": "^0.12.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/snapdragon/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/promzard": { + "version": "0.3.0", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "ms": "2.0.0" + "read": "1" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" } }, - "node_modules/snapdragon/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/read": { + "version": "1.0.7", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "is-descriptor": "^0.1.0" + "mute-stream": "~0.0.4" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.8" } }, - "node_modules/snapdragon/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/read-cmd-shim": { + "version": "3.0.0", "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, + "inBundle": true, + "license": "ISC", "engines": { - "node": ">=0.10.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/snapdragon/node_modules/is-descriptor": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", - "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/read-package-json": { + "version": "5.0.2", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" + "glob": "^8.0.1", + "json-parse-even-better-errors": "^2.3.1", + "normalize-package-data": "^4.0.0", + "npm-normalize-package-bin": "^2.0.0" }, "engines": { - "node": ">= 0.4" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/snapdragon/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/read-package-json-fast": { + "version": "2.0.3", "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/snapdragon/node_modules/ms": { + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/read-package-json/node_modules/npm-normalize-package-bin": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/snapdragon/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "dev": true, + "inBundle": true, + "license": "ISC", "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "dev": true - }, - "node_modules/source-map": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.0.tgz", - "integrity": "sha512-gjGnxNN0K+/Pr4Mi4fs/pOtda10dKB6Wn9QvjOrH6v5TWsI7ghHuJUHoIgyM6DkUL5kr2GtPFGererzKpMBWfA==", - "engines": { - "node": ">=0.10.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/readable-stream": { + "version": "3.6.0", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/readdir-scoped-modules": { + "version": "1.1.0", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" } }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/retry": { + "version": "0.12.0", "dev": true, + "inBundle": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 4" } }, - "node_modules/source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", - "deprecated": "See https://github.com/lydell/source-map-url#deprecated", - "dev": true - }, - "node_modules/spawn-error-forwarder": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", - "integrity": "sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==", - "dev": true - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/rimraf": { + "version": "3.0.2", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } - }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/spdx-license-ids": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", - "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", - "dev": true - }, - "node_modules/split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "through": "2" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "extend-shallow": "^3.0.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=0.10.0" + "node": "*" } }, - "node_modules/split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/safe-buffer": { + "version": "5.2.1", "dev": true, - "dependencies": { - "readable-stream": "^3.0.0" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT" }, - "node_modules/split2/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true + "inBundle": true, + "license": "MIT", + "optional": true }, - "node_modules/sshpk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/semver": { + "version": "7.3.7", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" + "lru-cache": "^6.0.0" }, "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" + "semver": "bin/semver.js" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/ssri": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", - "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "safe-buffer": "^5.1.1" + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/set-blocking": { + "version": "2.0.0", "dev": true, - "dependencies": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, + "inBundle": true, + "license": "ISC" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 6.0.0", + "npm": ">= 3.0.0" } }, - "node_modules/static-extend/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/socks": { + "version": "2.7.0", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "is-descriptor": "^0.1.0" + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 10.13.0", + "npm": ">= 3.0.0" } }, - "node_modules/static-extend/node_modules/is-descriptor": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz", - "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/socks-proxy-agent": { + "version": "7.0.0", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "is-accessor-descriptor": "^1.0.1", - "is-data-descriptor": "^1.0.1" + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" }, "engines": { - "node": ">= 0.4" + "node": ">= 10" } }, - "node_modules/stream-browserify": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", - "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/spdx-correct": { + "version": "3.1.1", "dev": true, + "inBundle": true, + "license": "Apache-2.0", "dependencies": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/stream-combiner": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", - "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.3.0", "dev": true, - "dependencies": { - "duplexer": "~0.1.1", - "through": "~2.3.4" - } + "inBundle": true, + "license": "CC-BY-3.0" }, - "node_modules/stream-combiner2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", - "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/spdx-expression-parse": { + "version": "3.0.1", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "duplexer2": "~0.1.0", - "readable-stream": "^2.0.2" + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/stream-each": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", - "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.11", "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } + "inBundle": true, + "license": "CC0-1.0" }, - "node_modules/stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/ssri": { + "version": "9.0.1", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/stream-shift": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", - "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", - "dev": true - }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/string_decoder": { + "version": "1.3.0", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "safe-buffer": "~5.1.0" + "safe-buffer": "~5.2.0" } }, - "node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/string-width": { + "version": "4.2.3", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/strip-ansi": { + "version": "6.0.1", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/supports-color": { + "version": "7.2.0", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/tar": { + "version": "6.1.11", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "ansi-regex": "^3.0.0" + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" }, "engines": { - "node": ">=4" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" + "node": ">= 10" } }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/text-table": { + "version": "0.2.0", "dev": true, - "engines": { - "node": ">=6" - } + "inBundle": true, + "license": "MIT" }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", "dev": true, - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } + "inBundle": true, + "license": "MIT" }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/treeverse": { + "version": "2.0.0", "dev": true, + "inBundle": true, + "license": "ISC", "engines": { - "node": ">=0.10.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/unique-filename": { + "version": "2.0.1", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "has-flag": "^4.0.0" + "unique-slug": "^3.0.0" }, "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/unique-slug": { + "version": "3.0.0", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" + "imurmurhash": "^0.1.4" }, "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "inBundle": true, + "license": "MIT" }, - "node_modules/table": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", - "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", "dev": true, + "inBundle": true, + "license": "Apache-2.0", "dependencies": { - "ajv": "^5.2.3", - "ajv-keywords": "^2.1.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", - "slice-ansi": "1.0.0", - "string-width": "^2.1.1" + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, - "node_modules/table/node_modules/ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha512-Ajr4IcMXq/2QmMkEmSvxqfLN5zGmJ92gHXAeOXq1OekoH2rfDNsgdDoL2f7QaRCy7G/E6TpxBVdRuNraMztGHw==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/validate-npm-package-name": { + "version": "4.0.0", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" + "builtins": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/table/node_modules/ajv-keywords": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", - "integrity": "sha512-ZFztHzVRdGLAzJmpUT9LNFLe1YiVOEylcaNpEutM26PVTCtOD919IMfD01CgbRouB42Dd9atjx1HseC15DgOZA==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/walk-up-path": { + "version": "1.0.0", "dev": true, - "peerDependencies": { - "ajv": "^5.0.0" - } + "inBundle": true, + "license": "ISC" }, - "node_modules/table/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/wcwidth": { + "version": "1.0.1", "dev": true, + "inBundle": true, + "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" + "defaults": "^1.0.3" } }, - "node_modules/table/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/which": { + "version": "2.0.2", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" }, "engines": { - "node": ">=4" + "node": ">= 8" } }, - "node_modules/table/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/wide-align": { + "version": "1.1.5", "dev": true, + "inBundle": true, + "license": "ISC", "dependencies": { - "color-name": "1.1.3" + "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "node_modules/table/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/table/node_modules/fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha512-fueX787WZKCV0Is4/T2cyAdM4+x1S3MXXOAhavE1ys/W42SHAPacLTQhucja22QBYrfGw50M2sRiXPtTGv9Ymw==", - "dev": true + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "ISC" }, - "node_modules/table/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/write-file-atomic": { + "version": "4.0.2", "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, "engines": { - "node": ">=4" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha512-4JD/Ivzg7PoW8NzdrBSr3UFwC9mHgvI7Z6z3QGBsSHgKaRTUDmyZAAKJo2UbG1kUVfS9WS8bi36N49U1xw43DA==", - "dev": true + "node_modules/scratch-semantic-release-config/node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" }, - "node_modules/table/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/scratch-semantic-release-config/node_modules/p-filter": { + "version": "2.1.0", "dev": true, + "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "p-map": "^2.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "node_modules/scratch-semantic-release-config/node_modules/p-map": { + "version": "2.1.0", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/temp-dir": { + "node_modules/scratch-semantic-release-config/node_modules/slash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/temp-dir": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/tempy": { + "node_modules/scratch-semantic-release-config/node_modules/tempy": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-1.0.1.tgz", - "integrity": "sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==", "dev": true, + "license": "MIT", "dependencies": { "del": "^6.0.0", "is-stream": "^2.0.0", @@ -12992,11 +13375,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tempy/node_modules/type-fest": { + "node_modules/scratch-semantic-release-config/node_modules/type-fest": { "version": "0.16.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", - "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -13004,1847 +13386,1780 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/terser": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", - "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==", + "node_modules/scratch-semantic-release-config/node_modules/unique-string": { + "version": "2.0.0", "dev": true, + "license": "MIT", "dependencies": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/scratch-semantic-release-config/node_modules/url-join": { + "version": "4.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/select-hose": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/selfsigned": { + "version": "2.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node-forge": "^1.3.0", + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semantic-release": { + "version": "22.0.12", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/commit-analyzer": "^11.0.0", + "@semantic-release/error": "^4.0.0", + "@semantic-release/github": "^9.0.0", + "@semantic-release/npm": "^11.0.0", + "@semantic-release/release-notes-generator": "^12.0.0", + "aggregate-error": "^5.0.0", + "cosmiconfig": "^8.0.0", + "debug": "^4.0.0", + "env-ci": "^10.0.0", + "execa": "^8.0.0", + "figures": "^6.0.0", + "find-versions": "^5.1.0", + "get-stream": "^6.0.0", + "git-log-parser": "^1.2.0", + "hook-std": "^3.0.0", + "hosted-git-info": "^7.0.0", + "import-from-esm": "^1.3.1", + "lodash-es": "^4.17.21", + "marked": "^9.0.0", + "marked-terminal": "^6.0.0", + "micromatch": "^4.0.2", + "p-each-series": "^3.0.0", + "p-reduce": "^3.0.0", + "read-pkg-up": "^11.0.0", + "resolve-from": "^5.0.0", + "semver": "^7.3.2", + "semver-diff": "^4.0.0", + "signale": "^1.2.1", + "yargs": "^17.5.1" }, "bin": { - "terser": "bin/terser" + "semantic-release": "bin/semantic-release.js" }, "engines": { - "node": ">=6.0.0" + "node": "^18.17 || >=20.6.1" } }, - "node_modules/terser-webpack-plugin": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", - "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", + "node_modules/semantic-release/node_modules/execa": { + "version": "8.0.1", "dev": true, + "license": "MIT", "dependencies": { - "cacache": "^12.0.2", - "find-cache-dir": "^2.1.0", - "is-wsl": "^1.1.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^4.0.0", - "source-map": "^0.6.1", - "terser": "^4.1.2", - "webpack-sources": "^1.4.0", - "worker-farm": "^1.7.0" + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" }, "engines": { - "node": ">= 6.9.0" + "node": ">=16.17" }, - "peerDependencies": { - "webpack": "^4.0.0" + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/terser-webpack-plugin/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/semantic-release/node_modules/execa/node_modules/get-stream": { + "version": "8.0.1", "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "license": "MIT", + "engines": { + "node": ">=16" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "node_modules/semantic-release/node_modules/figures": { + "version": "6.1.0", "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/terser-webpack-plugin/node_modules/cacache": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", - "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "node_modules/semantic-release/node_modules/hosted-git-info": { + "version": "7.0.2", "dev": true, + "license": "ISC", "dependencies": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/semantic-release/node_modules/human-signals": { + "version": "5.0.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/semantic-release/node_modules/is-stream": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/lru-cache": { + "version": "10.4.3", + "dev": true, + "license": "ISC" + }, + "node_modules/semantic-release/node_modules/mimic-fn": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/terser-webpack-plugin/node_modules/find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "node_modules/semantic-release/node_modules/normalize-package-data": { + "version": "6.0.2", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": ">=6" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/terser-webpack-plugin/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "node_modules/semantic-release/node_modules/npm-run-path": { + "version": "5.3.0", "dev": true, + "license": "MIT", "dependencies": { - "locate-path": "^3.0.0" + "path-key": "^4.0.0" }, "engines": { - "node": ">=6" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/terser-webpack-plugin/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "node_modules/semantic-release/node_modules/onetime": { + "version": "6.0.0", "dev": true, + "license": "MIT", "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "mimic-fn": "^4.0.0" }, "engines": { - "node": ">=6" - } - }, - "node_modules/terser-webpack-plugin/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/terser-webpack-plugin/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "node_modules/semantic-release/node_modules/parse-json": { + "version": "8.1.0", "dev": true, + "license": "MIT", "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "@babel/code-frame": "^7.22.13", + "index-to-position": "^0.1.2", + "type-fest": "^4.7.1" }, "engines": { - "node": ">=6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/terser-webpack-plugin/node_modules/mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "node_modules/semantic-release/node_modules/path-key": { + "version": "4.0.0", "dev": true, - "dependencies": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - }, + "license": "MIT", "engines": { - "node": ">=4.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/terser-webpack-plugin/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/semantic-release/node_modules/read-pkg": { + "version": "9.0.1", "dev": true, + "license": "MIT", "dependencies": { - "p-try": "^2.0.0" + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" }, "engines": { - "node": ">=6" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/terser-webpack-plugin/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "node_modules/semantic-release/node_modules/read-pkg-up": { + "version": "11.0.0", "dev": true, + "license": "MIT", "dependencies": { - "p-limit": "^2.0.0" + "find-up-simple": "^1.0.0", + "read-pkg": "^9.0.0", + "type-fest": "^4.6.0" }, "engines": { - "node": ">=6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/terser-webpack-plugin/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "node_modules/semantic-release/node_modules/signal-exit": { + "version": "4.1.0", "dev": true, + "license": "ISC", "engines": { - "node": ">=6" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/terser-webpack-plugin/node_modules/path-exists": { + "node_modules/semantic-release/node_modules/strip-final-newline": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/terser-webpack-plugin/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "node_modules/semantic-release/node_modules/type-fest": { + "version": "4.26.1", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=6" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/terser-webpack-plugin/node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "node_modules/semver": { + "version": "7.5.4", "dev": true, + "license": "ISC", "dependencies": { - "find-up": "^3.0.0" + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=6" + "node": ">=10" } }, - "node_modules/terser-webpack-plugin/node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "node_modules/semver-diff": { + "version": "4.0.0", "dev": true, + "license": "MIT", "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "node_modules/semver-regex": { + "version": "4.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/send": { + "version": "0.18.0", "dev": true, + "license": "MIT", "dependencies": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" }, "engines": { - "node": ">= 4" + "node": ">= 0.8.0" } }, - "node_modules/terser-webpack-plugin/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/send/node_modules/debug": { + "version": "2.6.9", "dev": true, - "bin": { - "semver": "bin/semver" + "license": "MIT", + "dependencies": { + "ms": "2.0.0" } }, - "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/serialize-javascript": { + "version": "6.0.1", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } }, - "node_modules/terser-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/serve-index": { + "version": "1.9.1", "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8.0" } }, - "node_modules/terser-webpack-plugin/node_modules/ssri": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", - "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", "dev": true, + "license": "MIT", "dependencies": { - "figgy-pudding": "^3.5.1" + "ms": "2.0.0" } }, - "node_modules/terser-webpack-plugin/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "license": "MIT", + "engines": { + "node": ">= 0.6" } }, - "node_modules/terser-webpack-plugin/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/terser/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/text-extensions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", - "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", "dev": true, - "engines": { - "node": ">=0.10" - } + "license": "ISC" }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "dev": true, + "license": "ISC" }, - "node_modules/through2": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", - "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", "dev": true, - "dependencies": { - "readable-stream": "3" + "license": "MIT", + "engines": { + "node": ">= 0.6" } }, - "node_modules/through2/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/serve-static": { + "version": "1.15.0", "dev": true, + "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" }, "engines": { - "node": ">= 6" + "node": ">= 0.8.0" } }, - "node_modules/timers-browserify": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", - "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "node_modules/set-function-length": { + "version": "1.1.1", "dev": true, + "license": "MIT", "dependencies": { - "setimmediate": "^1.0.4" + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" }, "engines": { - "node": ">=0.6.0" + "node": ">= 0.4" } }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "node_modules/setprototypeof": { + "version": "1.2.0", + "dev": true, + "license": "ISC" + }, + "node_modules/shallow-clone": { + "version": "3.0.1", "dev": true, + "license": "MIT", "dependencies": { - "os-tmpdir": "~1.0.2" + "kind-of": "^6.0.2" }, "engines": { - "node": ">=0.6.0" + "node": ">=8" } }, - "node_modules/to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==", - "dev": true - }, - "node_modules/to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", + "node_modules/shebang-command": { + "version": "2.0.0", "dev": true, + "license": "MIT", "dependencies": { - "kind-of": "^3.0.2" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/to-object-path/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/shebang-regex": { + "version": "3.0.0", "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "node_modules/shell-quote": { + "version": "1.8.1", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", "dev": true, + "license": "MIT", "dependencies": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/signal-exit": { + "version": "3.0.7", + "dev": true, + "license": "ISC" + }, + "node_modules/signale": { + "version": "1.4.0", "dev": true, + "license": "MIT", "dependencies": { - "is-number": "^7.0.0" + "chalk": "^2.3.2", + "figures": "^2.0.0", + "pkg-conf": "^2.1.0" }, "engines": { - "node": ">=8.0" + "node": ">=6" } }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "node_modules/signale/node_modules/ansi-styles": { + "version": "3.2.1", "dev": true, + "license": "MIT", "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" + "color-convert": "^1.9.0" }, "engines": { - "node": ">=0.8" + "node": ">=4" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true - }, - "node_modules/transifex": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/transifex/-/transifex-1.6.6.tgz", - "integrity": "sha512-uHeRvhfLfZN+JdH+X0zR1jkQAbMGkgExZgcXm31CzaVVd4kq98YaPr4MCgCU0LwA7cgOuB97d2HZQ/WikOAxlg==", + "node_modules/signale/node_modules/chalk": { + "version": "2.4.2", "dev": true, + "license": "MIT", "dependencies": { - "commander": "^2.9.0", - "lodash": "^4.17.1", - "mkpath": "^1.0.0", - "mocha": "^4.0.0", - "request": "^2.34.0", - "should": "^13.0.0" - }, - "bin": { - "transifex": "bin/index.js" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": ">=8.9.0" + "node": ">=4" } }, - "node_modules/traverse": { - "version": "0.6.9", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.9.tgz", - "integrity": "sha512-7bBrcF+/LQzSgFmT0X5YclVqQxtv7TDJ1f8Wj7ibBu/U6BMLeOpUxuZjV7rMc44UtKxlnMFigdhFAIszSX1DMg==", + "node_modules/signale/node_modules/color-convert": { + "version": "1.9.3", "dev": true, + "license": "MIT", "dependencies": { - "gopd": "^1.0.1", - "typedarray.prototype.slice": "^1.0.3", - "which-typed-array": "^1.1.15" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "color-name": "1.1.3" } }, - "node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "node_modules/signale/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/signale/node_modules/has-flag": { + "version": "3.0.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "node_modules/signale/node_modules/supports-color": { + "version": "5.5.0", "dev": true, + "license": "MIT", "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" + "has-flag": "^3.0.0" }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } + "engines": { + "node": ">=4" } }, - "node_modules/ts-node/node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "node_modules/skin-tone": { + "version": "2.0.0", "dev": true, - "bin": { - "acorn": "bin/acorn" + "license": "MIT", + "dependencies": { + "unicode-emoji-modifier-base": "^1.0.0" }, "engines": { - "node": ">=0.4.0" + "node": ">=8" } }, - "node_modules/ts-node/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "node_modules/slash": { + "version": "5.1.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.3.1" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==", - "dev": true - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "node_modules/slice-ansi": { + "version": "1.0.0", "dev": true, + "license": "MIT", "dependencies": { - "safe-buffer": "^5.0.1" + "is-fullwidth-code-point": "^2.0.0" }, "engines": { - "node": "*" + "node": ">=4" } }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "node_modules/sockjs": { + "version": "0.3.24", "dev": true, + "license": "MIT", "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" } }, - "node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "node_modules/source-map": { + "version": "0.6.1", "dev": true, + "license": "BSD-3-Clause", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/typed-array-buffer": { + "node_modules/source-map-js": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" - }, + "license": "BSD-3-Clause", "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" } }, - "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "node_modules/source-map-loader": { + "version": "4.0.1", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "abab": "^2.0.6", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.2" }, "engines": { - "node": ">= 0.4" + "node": ">= 14.15.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.72.1" } }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "node_modules/source-map-support": { + "version": "0.5.21", "dev": true, + "license": "MIT", "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "node_modules/spawn-error-forwarder": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/spdx-correct": { + "version": "3.2.0", "dev": true, + "license": "Apache-2.0", "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "dev": true + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "dev": true, + "license": "CC-BY-3.0" }, - "node_modules/typedarray.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typedarray.prototype.slice/-/typedarray.prototype.slice-1.0.3.tgz", - "integrity": "sha512-8WbVAQAUlENo1q3c3zZYuy5k9VzBQvp8AX9WOtbvyWlLM1v5JaSRmjubLjzHF4JFtptjH/5c/i95yaElvcjC0A==", + "node_modules/spdx-expression-parse": { + "version": "3.0.1", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-errors": "^1.3.0", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-offset": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "node_modules/spdx-license-ids": { + "version": "3.0.17", "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } + "license": "CC0-1.0" }, - "node_modules/uglify-es": { - "version": "3.3.9", - "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", - "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", - "deprecated": "support for ECMAScript is superseded by `uglify-js` as of v3.13.0", + "node_modules/spdy": { + "version": "4.0.2", "dev": true, + "license": "MIT", "dependencies": { - "commander": "~2.13.0", - "source-map": "~0.6.1" - }, - "bin": { - "uglifyjs": "bin/uglifyjs" + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" }, "engines": { - "node": ">=0.8.0" + "node": ">=6.0.0" } }, - "node_modules/uglify-es/node_modules/commander": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", - "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", - "dev": true - }, - "node_modules/uglify-es/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/spdy-transport": { + "version": "3.0.0", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" } }, - "node_modules/uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "node_modules/split": { + "version": "1.0.1", "dev": true, - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" + "license": "MIT", + "dependencies": { + "through": "2" }, "engines": { - "node": ">=0.8.0" + "node": "*" } }, - "node_modules/uglifyjs-webpack-plugin": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz", - "integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==", + "node_modules/split2": { + "version": "3.2.2", "dev": true, - "dependencies": { - "cacache": "^10.0.4", - "find-cache-dir": "^1.0.0", - "schema-utils": "^0.4.5", - "serialize-javascript": "^1.4.0", - "source-map": "^0.6.1", - "uglify-es": "^3.3.4", - "webpack-sources": "^1.1.0", - "worker-farm": "^1.5.2" - }, - "engines": { - "node": ">= 4.8 < 5.0.0 || >= 5.10" - }, - "peerDependencies": { - "webpack": "^2.0.0 || ^3.0.0 || ^4.0.0" + "license": "ISC", + "dependencies": { + "readable-stream": "^3.0.0" } }, - "node_modules/uglifyjs-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/sprintf-js": { + "version": "1.0.3", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/statuses": { + "version": "2.0.1", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.8" } }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "node_modules/stream-combiner2": { + "version": "1.1.1", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" } }, - "node_modules/union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "node_modules/stream-combiner2/node_modules/readable-stream": { + "version": "2.3.8", "dev": true, + "license": "MIT", "dependencies": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - }, - "engines": { - "node": ">=0.10.0" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "node_modules/union-value/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/stream-combiner2/node_modules/safe-buffer": { + "version": "5.1.2", "dev": true, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/unique-filename": { + "node_modules/stream-combiner2/node_modules/string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", "dev": true, + "license": "MIT", "dependencies": { - "unique-slug": "^2.0.0" + "safe-buffer": "~5.1.0" } }, - "node_modules/unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "node_modules/string_decoder": { + "version": "1.3.0", "dev": true, + "license": "MIT", "dependencies": { - "imurmurhash": "^0.1.4" + "safe-buffer": "~5.2.0" } }, - "node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "node_modules/string-width": { + "version": "4.2.3", "dev": true, + "license": "MIT", "dependencies": { - "crypto-random-string": "^2.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, - "node_modules/universal-user-agent": { + "node_modules/strip-ansi": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", - "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", - "dev": true - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=8" } }, - "node_modules/unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", + "node_modules/strip-bom": { + "version": "3.0.0", "dev": true, - "dependencies": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/unset-value/node_modules/has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", + "node_modules/strip-final-newline": { + "version": "2.0.0", "dev": true, - "dependencies": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "node_modules/strip-indent": { + "version": "3.0.0", "dev": true, + "license": "MIT", "dependencies": { - "isarray": "1.0.0" + "min-indent": "^1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/unset-value/node_modules/has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", + "node_modules/strip-json-comments": { + "version": "2.0.1", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "node_modules/supports-color": { + "version": "8.1.1", "dev": true, - "optional": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { - "node": ">=4", - "yarn": "*" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "node_modules/supports-hyperlinks": { + "version": "3.1.0", "dev": true, + "license": "MIT", "dependencies": { - "punycode": "^2.1.0" + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", - "deprecated": "Please see https://github.com/lydell/urix#deprecated", - "dev": true - }, - "node_modules/url": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.3.tgz", - "integrity": "sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==", + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "7.2.0", "dev": true, + "license": "MIT", "dependencies": { - "punycode": "^1.4.1", - "qs": "^6.11.2" + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true - }, - "node_modules/url/node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", - "dev": true - }, - "node_modules/url/node_modules/qs": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.0.tgz", - "integrity": "sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", "dev": true, - "dependencies": { - "side-channel": "^1.0.6" - }, + "license": "MIT", "engines": { - "node": ">=0.6" + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/url2": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/url2/-/url2-0.0.0.tgz", - "integrity": "sha512-gb/XT1m2mnWOIbQwa5V9Dq2O07fkZbtu1K0WAAKuaNSX0c8psp2jovJTbbvPKCpimutdoK9jXOejDCtvQOoKOA==", - "dev": true + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "license": "MIT" }, - "node_modules/use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "node_modules/table": { + "version": "4.0.2", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^5.2.3", + "ajv-keywords": "^2.1.0", + "chalk": "^2.1.0", + "lodash": "^4.17.4", + "slice-ansi": "1.0.0", + "string-width": "^2.1.1" } }, - "node_modules/util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "node_modules/table/node_modules/ajv": { + "version": "5.5.2", "dev": true, + "license": "MIT", "dependencies": { - "inherits": "2.0.3" + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "node_modules/util/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "dev": true - }, - "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "node_modules/table/node_modules/ajv-keywords": { + "version": "2.1.1", "dev": true, - "bin": { - "uuid": "bin/uuid" + "license": "MIT", + "peerDependencies": { + "ajv": "^5.0.0" } }, - "node_modules/v8-compile-cache": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", - "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", - "dev": true - }, - "node_modules/v8-compile-cache-lib": { + "node_modules/table/node_modules/ansi-regex": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "node_modules/table/node_modules/ansi-styles": { + "version": "3.2.1", "dev": true, - "engines": [ - "node >=0.6.0" - ], + "license": "MIT", "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/verror/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true - }, - "node_modules/vinyl": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", - "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", + "node_modules/table/node_modules/chalk": { + "version": "2.4.2", "dev": true, + "license": "MIT", "dependencies": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": ">= 0.10" + "node": ">=4" } }, - "node_modules/vinyl-sourcemaps-apply": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", - "integrity": "sha512-+oDh3KYZBoZC8hfocrbrxbLUeaYtQK7J5WU5Br9VqWqmCll3tFJqKp97GC9GmMsVIL0qnx2DgEDVxdo5EZ5sSw==", + "node_modules/table/node_modules/color-convert": { + "version": "1.9.3", "dev": true, + "license": "MIT", "dependencies": { - "source-map": "^0.5.1" + "color-name": "1.1.3" } }, - "node_modules/vinyl-sourcemaps-apply/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "node_modules/table/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/table/node_modules/fast-deep-equal": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/table/node_modules/has-flag": { + "version": "3.0.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/vm-browserify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", - "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", - "dev": true + "node_modules/table/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "node_modules/watchpack": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", - "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", + "node_modules/table/node_modules/json-schema-traverse": { + "version": "0.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/table/node_modules/string-width": { + "version": "2.1.1", "dev": true, + "license": "MIT", "dependencies": { - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" }, - "optionalDependencies": { - "chokidar": "^3.4.1", - "watchpack-chokidar2": "^2.0.1" + "engines": { + "node": ">=4" } }, - "node_modules/watchpack-chokidar2": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", - "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "node_modules/table/node_modules/strip-ansi": { + "version": "4.0.0", "dev": true, - "optional": true, + "license": "MIT", "dependencies": { - "chokidar": "^2.1.8" + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/watchpack-chokidar2/node_modules/anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "node_modules/table/node_modules/supports-color": { + "version": "5.5.0", "dev": true, - "optional": true, + "license": "MIT", "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/watchpack-chokidar2/node_modules/anymatch/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "node_modules/tapable": { + "version": "2.2.1", "dev": true, - "optional": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/watchpack-chokidar2/node_modules/binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "node_modules/temp-dir": { + "version": "3.0.0", "dev": true, - "optional": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=14.16" } }, - "node_modules/watchpack-chokidar2/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "node_modules/tempy": { + "version": "3.1.0", "dev": true, - "optional": true, + "license": "MIT", "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" + "is-stream": "^3.0.0", + "temp-dir": "^3.0.0", + "type-fest": "^2.12.2", + "unique-string": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/watchpack-chokidar2/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/tempy/node_modules/is-stream": { + "version": "3.0.0", "dev": true, - "optional": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/watchpack-chokidar2/node_modules/chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", + "node_modules/tempy/node_modules/type-fest": { + "version": "2.19.0", "dev": true, - "optional": true, - "dependencies": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" }, - "optionalDependencies": { - "fsevents": "^1.2.7" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/watchpack-chokidar2/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "node_modules/terser": { + "version": "5.26.0", "dev": true, - "optional": true, + "license": "BSD-2-Clause", "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/watchpack-chokidar2/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/terser-webpack-plugin": { + "version": "5.3.9", "dev": true, - "optional": true, + "license": "MIT", "dependencies": { - "is-extendable": "^0.1.0" + "@jridgewell/trace-mapping": "^0.3.17", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" }, "engines": { - "node": ">=0.10.0" + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } } }, - "node_modules/watchpack-chokidar2/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2", + "node_modules/text-extensions": { + "version": "1.9.0", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, + "license": "MIT", "engines": { - "node": ">= 4.0" + "node": ">=0.10" } }, - "node_modules/watchpack-chokidar2/node_modules/glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "node_modules/text-table": { + "version": "0.2.0", "dev": true, - "optional": true, + "license": "MIT" + }, + "node_modules/through": { + "version": "2.3.8", + "dev": true, + "license": "MIT" + }, + "node_modules/through2": { + "version": "4.0.2", + "dev": true, + "license": "MIT", "dependencies": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "readable-stream": "3" } }, - "node_modules/watchpack-chokidar2/node_modules/glob-parent/node_modules/is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "node_modules/thunky": { + "version": "1.1.0", "dev": true, - "optional": true, + "license": "MIT" + }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "license": "MIT", "dependencies": { - "is-extglob": "^2.1.0" + "tldts-core": "^6.1.86" }, - "engines": { - "node": ">=0.10.0" + "bin": { + "tldts": "bin/cli.js" } }, - "node_modules/watchpack-chokidar2/node_modules/is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.0.33", "dev": true, - "optional": true, + "license": "MIT", "dependencies": { - "binary-extensions": "^1.0.0" + "os-tmpdir": "~1.0.2" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.6.0" } }, - "node_modules/watchpack-chokidar2/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/to-regex-range": { + "version": "5.0.1", "dev": true, - "optional": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8.0" } }, - "node_modules/watchpack-chokidar2/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "node_modules/toidentifier": { + "version": "1.0.1", "dev": true, - "optional": true, - "dependencies": { - "kind-of": "^3.0.2" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=0.6" } }, - "node_modules/watchpack-chokidar2/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "optional": true, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "license": "BSD-3-Clause", "dependencies": { - "is-buffer": "^1.1.5" + "tldts": "^6.1.32" }, "engines": { - "node": ">=0.10.0" + "node": ">=16" } }, - "node_modules/watchpack-chokidar2/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "optional": true, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "license": "MIT", "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "punycode": "^2.3.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=18" } }, - "node_modules/watchpack-chokidar2/node_modules/readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "node_modules/traverse": { + "version": "0.6.8", "dev": true, - "optional": true, - "dependencies": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "license": "MIT", + "engines": { + "node": ">= 0.4" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10" + "node": ">=8" } }, - "node_modules/watchpack-chokidar2/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "node_modules/ts-loader": { + "version": "9.5.1", "dev": true, - "optional": true, + "license": "MIT", "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" }, "engines": { - "node": ">=0.10.0" + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" } }, - "node_modules/weak-map": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/weak-map/-/weak-map-1.0.0.tgz", - "integrity": "sha512-Vb13TbgdvUEmzBA5mpsMqtPqcZGJPE2gj+b8wzxsevC7WkmL3c7YZg9H0pV1Jo8C1Sa1ykk3DU08hFRGLNWvLQ==", - "dev": true - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.4", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } }, - "node_modules/webpack": { - "version": "4.47.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.47.0.tgz", - "integrity": "sha512-td7fYwgLSrky3fI1EuU5cneU4+pbH6GgOfuKNS1tNPcfdGinGELAqsb/BP4nnvZyKSG2i/xFGU7+n2PvZA8HJQ==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.9.0", - "@webassemblyjs/helper-module-context": "1.9.0", - "@webassemblyjs/wasm-edit": "1.9.0", - "@webassemblyjs/wasm-parser": "1.9.0", - "acorn": "^6.4.1", - "ajv": "^6.10.2", - "ajv-keywords": "^3.4.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^4.5.0", - "eslint-scope": "^4.0.3", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.4.0", - "loader-utils": "^1.2.3", - "memory-fs": "^0.4.1", - "micromatch": "^3.1.10", - "mkdirp": "^0.5.3", - "neo-async": "^2.6.1", - "node-libs-browser": "^2.2.1", - "schema-utils": "^1.0.0", - "tapable": "^1.1.3", - "terser-webpack-plugin": "^1.4.3", - "watchpack": "^1.7.4", - "webpack-sources": "^1.4.1" + "node_modules/ts-node": { + "version": "10.9.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" }, "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=6.11.5" + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" }, "peerDependenciesMeta": { - "webpack-cli": { + "@swc/core": { "optional": true }, - "webpack-command": { + "@swc/wasm": { "optional": true } } }, - "node_modules/webpack-cli": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.12.tgz", - "integrity": "sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag==", + "node_modules/type-check": { + "version": "0.3.2", "dev": true, - "dependencies": { - "chalk": "^2.4.2", - "cross-spawn": "^6.0.5", - "enhanced-resolve": "^4.1.1", - "findup-sync": "^3.0.0", - "global-modules": "^2.0.0", - "import-local": "^2.0.0", - "interpret": "^1.4.0", - "loader-utils": "^1.4.0", - "supports-color": "^6.1.0", - "v8-compile-cache": "^2.1.1", - "yargs": "^13.3.2" - }, - "bin": { - "webpack-cli": "bin/cli.js" + "license": "MIT", + "dependencies": { + "prelude-ls": "~1.1.2" }, "engines": { - "node": ">=6.11.5" - }, - "peerDependencies": { - "webpack": "4.x.x" + "node": ">= 0.8.0" } }, - "node_modules/webpack-cli/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "node_modules/type-fest": { + "version": "0.18.1", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/webpack-cli/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/type-is": { + "version": "1.6.18", "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" + "media-typer": "0.3.0", + "mime-types": "~2.1.24" }, "engines": { - "node": ">=4" + "node": ">= 0.6" } }, - "node_modules/webpack-cli/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/typedarray": { + "version": "0.0.6", "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.6.3", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4" + "node": ">=14.17" } }, - "node_modules/webpack-cli/node_modules/chalk/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/uglify-js": { + "version": "3.19.3", "dev": true, - "dependencies": { - "has-flag": "^3.0.0" + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" }, "engines": { - "node": ">=4" + "node": ">=0.8.0" } }, - "node_modules/webpack-cli/node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "node_modules/undici-types": { + "version": "5.26.5", "dev": true, - "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } + "license": "MIT" }, - "node_modules/webpack-cli/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/unicode-emoji-modifier-base": { + "version": "1.0.0", "dev": true, - "dependencies": { - "color-name": "1.1.3" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "node_modules/webpack-cli/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/webpack-cli/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "node_modules/unicorn-magic": { + "version": "0.1.0", "dev": true, - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, + "license": "MIT", "engines": { - "node": ">=4.8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/webpack-cli/node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/webpack-cli/node_modules/find-up": { + "node_modules/unique-string": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, + "license": "MIT", "dependencies": { - "locate-path": "^3.0.0" + "crypto-random-string": "^4.0.0" }, "engines": { - "node": ">=6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/webpack-cli/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/universal-user-agent": { + "version": "6.0.1", "dev": true, - "engines": { - "node": ">=4" - } + "license": "ISC" }, - "node_modules/webpack-cli/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "node_modules/unpipe": { + "version": "1.0.0", "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">= 0.8" } }, - "node_modules/webpack-cli/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/update-browserslist-db": { + "version": "1.0.13", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", "dependencies": { - "p-try": "^2.0.0" + "escalade": "^3.1.1", + "picocolors": "^1.0.0" }, - "engines": { - "node": ">=6" + "bin": { + "update-browserslist-db": "cli.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "node_modules/webpack-cli/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "node_modules/uri-js": { + "version": "4.4.1", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" + "punycode": "^2.1.0" } }, - "node_modules/webpack-cli/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "node_modules/url-join": { + "version": "5.0.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/webpack-cli/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "node_modules/util-deprecate": { + "version": "1.0.2", "dev": true, - "engines": { - "node": ">=4" - } + "license": "MIT" }, - "node_modules/webpack-cli/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "node_modules/utils-merge": { + "version": "1.0.1", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">= 0.4.0" } }, - "node_modules/webpack-cli/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/uuid": { + "version": "8.3.2", "dev": true, + "license": "MIT", "bin": { - "semver": "bin/semver" + "uuid": "dist/bin/uuid" } }, - "node_modules/webpack-cli/node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", "dev": true, + "license": "Apache-2.0", "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "engines": { - "node": ">=6" + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, - "node_modules/webpack-cli/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "node_modules/vary": { + "version": "1.1.2", "dev": true, - "dependencies": { - "ansi-regex": "^4.1.0" - }, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">= 0.8" } }, - "node_modules/webpack-cli/node_modules/supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "xml-name-validator": "^5.0.0" }, "engines": { - "node": ">=6" + "node": ">=18" } }, - "node_modules/webpack-cli/node_modules/wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "node_modules/watchpack": { + "version": "2.4.0", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" }, "engines": { - "node": ">=6" + "node": ">=10.13.0" } }, - "node_modules/webpack-cli/node_modules/yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "node_modules/wbuf": { + "version": "1.7.3", "dev": true, + "license": "MIT", "dependencies": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" } }, - "node_modules/webpack-cli/node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "node_modules/webpack": { + "version": "5.89.0", "dev": true, + "license": "MIT", "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.7", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } } }, - "node_modules/webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "node_modules/webpack-cli": { + "version": "4.10.0", "dev": true, + "license": "MIT", "dependencies": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.2.0", + "@webpack-cli/info": "^1.5.0", + "@webpack-cli/serve": "^1.7.0", + "colorette": "^2.0.14", + "commander": "^7.0.0", + "cross-spawn": "^7.0.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "4.x.x || 5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "@webpack-cli/migrate": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } } }, - "node_modules/webpack-sources/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/webpack-cli/node_modules/commander": { + "version": "7.2.0", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 10" } }, - "node_modules/webpack/node_modules/acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "node_modules/webpack-dev-middleware": { + "version": "5.3.3", "dev": true, - "bin": { - "acorn": "bin/acorn" + "license": "MIT", + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" }, "engines": { - "node": ">=0.4.0" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" } }, - "node_modules/webpack/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/webpack-dev-middleware/node_modules/ajv": { + "version": "8.12.0", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", "uri-js": "^4.2.2" }, "funding": { @@ -14852,270 +15167,259 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/webpack/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": { + "version": "5.1.0", "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, "peerDependencies": { - "ajv": "^6.9.1" + "ajv": "^8.8.2" } }, - "node_modules/webpack/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": { + "version": "1.0.0", "dev": true, - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/webpack/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.2.0", "dev": true, + "license": "MIT", "dependencies": { - "is-extendable": "^0.1.0" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/webpack/node_modules/eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "node_modules/webpack-dev-server": { + "version": "4.15.1", "dev": true, + "license": "MIT", "dependencies": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.5", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.13.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" }, "engines": { - "node": ">=4.0.0" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } } }, - "node_modules/webpack/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "node_modules/webpack-dev-server/node_modules/ajv": { + "version": "8.12.0", "dev": true, + "license": "MIT", "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/webpack/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/webpack-dev-server/node_modules/ajv-keywords": { + "version": "5.1.0", "dev": true, + "license": "MIT", "dependencies": { - "is-extendable": "^0.1.0" + "fast-deep-equal": "^3.1.3" }, - "engines": { - "node": ">=0.10.0" + "peerDependencies": { + "ajv": "^8.8.2" } }, - "node_modules/webpack/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/webpack-dev-server/node_modules/json-schema-traverse": { + "version": "1.0.0", "dev": true, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/webpack/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.2.0", "dev": true, + "license": "MIT", "dependencies": { - "kind-of": "^3.0.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/webpack/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/webpack-merge": { + "version": "5.10.0", "dev": true, + "license": "MIT", "dependencies": { - "is-buffer": "^1.1.5" + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10.0.0" } }, - "node_modules/webpack/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/webpack/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "node_modules/webpack-sources": { + "version": "3.2.3", "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=10.13.0" } }, - "node_modules/webpack/node_modules/schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "node_modules/websocket-driver": { + "version": "0.7.4", "dev": true, + "license": "Apache-2.0", "dependencies": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" }, "engines": { - "node": ">= 4" + "node": ">=0.8.0" } }, - "node_modules/webpack/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "node_modules/websocket-extensions": { + "version": "0.1.4", "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "license": "MIT", "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "iconv-lite": "0.6.3" }, "engines": { - "node": ">=0.10.0" + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "license": "MIT", + "engines": { + "node": ">=18" } }, "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "license": "MIT", "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" } }, "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "2.0.2", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, "bin": { - "which": "bin/which" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "node-which": "bin/node-which" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 8" } }, - "node_modules/which-module": { + "node_modules/wildcard": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true - }, - "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "MIT" }, "node_modules/word-wrap": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/wordwrap": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true - }, - "node_modules/worker-farm": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", - "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", "dev": true, - "dependencies": { - "errno": "~0.1.7" - } + "license": "MIT" }, "node_modules/wrap-ansi": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -15128,61 +15432,15 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/write": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha512-CJ17OoULEKXpA5pef3qLj5AxTJ6mSt7g84he2WIskKwqFO4T97d5V7Tadl0DYDk7qyUOQD5WlUlOMChaYrhxeA==", "dev": true, + "license": "MIT", "dependencies": { "mkdirp": "^0.5.1" }, @@ -15191,10 +15449,8 @@ } }, "node_modules/ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", - "dev": true, + "version": "8.18.0", + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -15211,41 +15467,46 @@ } } }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "license": "MIT" + }, "node_modules/xtend": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4" } }, "node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "version": "5.0.8", "dev": true, + "license": "ISC", "engines": { - "node": ">= 6" + "node": ">=10" } }, + "node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, "node_modules/yargs": { "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, + "license": "MIT", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -15261,89 +15522,32 @@ }, "node_modules/yargs-parser": { "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/yargs/node_modules/yargs-parser": { "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, + "license": "ISC", "engines": { "node": ">=12" } }, "node_modules/yn": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/yocto-queue": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, diff --git a/package.json b/package.json index a821775746..18cbf5eea6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "scratch-blocks", - "version": "1.1.86", + "version": "2.0.0-spork.5", "description": "Scratch Blocks is a library for building creative computing interfaces.", "author": "Massachusetts Institute of Technology", "license": "Apache-2.0", @@ -9,48 +9,32 @@ "type": "git", "url": "https://github.com/scratchfoundation/scratch-blocks.git" }, - "main": "./dist/vertical.js", - "browser": "./shim/vertical.js", + "main": "./dist/main.js", "scripts": { - "deploy": "rimraf gh-pages/closure-library/scripts/ci/CloseAdobeDialog.exe && gh-pages -t -d gh-pages -m \"Build for $(git log --pretty=format:%H -n1) [skip ci]\"", - "prepare": "husky install", - "prepublish": "python build.py && webpack", - "test": "npm run test:messages && npm run test:unit", - "test:lint": "eslint .", - "test:messages": "npm run translate && node i18n/test_scratch_msgs.js", - "test:unit": "node tests/jsunit/test_runner.js", - "translate": "node i18n/js_to_json.js && node i18n/json_to_js.js", - "translate:sync:src": "tx-push-src scratch-editor blocks msg/json/en.json", - "translate:sync:translations": "node i18n/sync_tx_translations.js", - "translate:update": "npm run translate:sync:src && npm run translate:sync:translations" - }, - "dependencies": { - "exports-loader": "^0.7.0", - "google-closure-library": "^20190301.0.0", - "imports-loader": "^0.8.0", - "scratch-l10n": "^3.18.3" + "build": "webpack --mode production", + "prepare": "husky || true", + "start": "webpack serve --open --mode development", + "test": "echo \"Error: no test specified\" && exit 1", + "test:lint": "eslint ." }, "devDependencies": { - "@commitlint/cli": "17.8.1", - "@commitlint/config-conventional": "17.8.1", - "async": "2.6.4", - "copy-webpack-plugin": "4.6.0", - "eslint": "4.19.1", - "event-stream": "3.3.5", - "gh-pages": "0.12.0", - "glob": "7.2.3", - "google-closure-compiler": "20180402.0.0", - "graceful-fs": "4.2.11", - "husky": "8.0.3", - "json": "9.0.6", - "rimraf": "2.7.1", - "scratch-semantic-release-config": "1.0.14", - "selenium-webdriver": "4.16.0", - "semantic-release": "19.0.5", - "transifex": "1.6.6", - "uglifyjs-webpack-plugin": "1.3.0", - "webpack": "4.47.0", - "webpack-cli": "3.3.12" + "@commitlint/cli": "^17.8.1", + "@commitlint/config-conventional": "^17.8.1", + "eslint": "^4.19.1", + "husky": "9.1.6", + "scratch-semantic-release-config": "1.0.16", + "semantic-release": "22.0.12", + "source-map-loader": "^4.0.1", + "ts-loader": "^9.5.1", + "typescript": "^5.6.3", + "webpack": "^5.76.0", + "webpack-cli": "^4.10.0", + "webpack-dev-server": "^4.11.1" + }, + "dependencies": { + "@blockly/continuous-toolbox": "^7.0.1", + "@blockly/field-colour": "^6.0.3", + "blockly": "12.3.0-beta.0" }, "config": { "commitizen": { diff --git a/pull_from_blockly.sh b/pull_from_blockly.sh deleted file mode 100755 index fe3dcb8674..0000000000 --- a/pull_from_blockly.sh +++ /dev/null @@ -1,151 +0,0 @@ -#!/bin/bash - -# Pull from Blockly into Scratch Blocks and do basic cleanup. -# Rachel Fenichel (fenichel@google.com) - -BOLD='\e[1m' -NOBOLD='\e[21m' - -# Formatting helper. -empty_lines() { printf '\n\n'; } -bold_echo() { - echo -e "${BOLD}$1${NOBOLD}" -} - -stop_on_fail() { - # Fail if any command fails. - set -e - # Even if you're piping the output. - set -o pipefail -} - -# Undo the effects of start_failing. -continue_on_fail() { - set +e - set +o pipefail -} - -# Prompt for y/n and put the result in $prompt_result -# The first argument specifies the text to use in the prompt. -# The second argument specifies which value to use if we're skipping prompts. -prompt() { - if [ $with_prompts ] - then - if [ $2 = true ] - then - paren_text="(Y/n)" - else - paren_text="(y/N)" - fi - # Prompt the user and retry if they try any funny business. - while true; do - read -p "$1 $paren_text > " yn - case $yn in - [Yy]* ) prompt_result=true; break;; - [Nn]* ) prompt_result=false; break;; - * ) echo "Please answer yes or no.";; - esac - done - else - # Running without prompts. Use the default value. - prompt_result=$2; - fi -} - - -# Ask the user for confirmation, then pull from Blockly's develop branch. -# The default is to do the pull. -pull_from_develop_fn() { - empty_lines - prompt "Do you want to pull from develop?" true - if [ $prompt_result = false ] - then - bold_echo "You don't want to pull from develop. Why are you running this script?" - exit - fi - - bold_echo "Pulling from Blockly's develop branch" - sleep .5 - # This pull will likely fail with merge conflicts, but that's okay. - # However, this means that we won't fail on errors other than merge conflicts. - continue_on_fail - git pull https://github.com/google/blockly.git develop - stop_on_fail -} - -# Ask the user for confirmation, then run cleanup. -# The default is to run cleanup. -run_cleanup_fn() { - empty_lines - prompt "Ready to run cleanup.sh. Continue?" true - if [ $prompt_result = false ] - then - bold_echo "Skipping cleanup.sh" - prompt_for_merge_abort - empty_lines - bold_echo "Done" - exit - fi - - bold_echo "Running cleanup.sh" - sleep .5 - # Cleanup.sh resolves common conflicts. - ./cleanup.sh -} - -# Ask the user for confirmation, then possibly abort the merge. -# The default is to *not* abort the merge. -# Used to clean up the repo instead of leaving it in a bad state. -prompt_for_merge_abort() { - empty_lines - prompt "Do you want to abort this merge?" false - if [ $prompt_result = false ] - then - bold_echo "Continuing with merge..." - else - bold_echo "Running git merge --abort" - git merge --abort - display_status_fn - bold_echo "Done" - exit - fi -} - -# Ask the user for confirmation, then show the current repo status. -# The default to to show status. -display_status_fn() { - empty_lines - prompt "Do you want to display the current status?" true - if [ $prompt_result = true ] - then - # Tell the user the current state. - bold_echo "Current status" - sleep .5 - git status - else - bold_echo "Skipping status display." - fi -} - -# Give the user one more chance to abort the merge, then tell them what their -# next steps should be. -finish_fn() { - prompt_for_merge_abort - bold_echo "Done. You may need to manually resolve conflicts." - # Helpful tips about what to do next. - empty_lines - sleep .5 - echo "Fix conflicts and run 'git commit'." - echo "Use 'git add ' to mark resolution." - echo "Use 'git merge --abort' to abort this merge." -} - -# Check whether we're running with prompts. If unset, we'll skip all prompts. -with_prompts=$1 - -# Here we go! -stop_on_fail -pull_from_develop_fn -run_cleanup_fn -display_status_fn -finish_fn diff --git a/release.config.js b/release.config.js index 53cf4a450a..a669fae098 100644 --- a/release.config.js +++ b/release.config.js @@ -5,6 +5,18 @@ module.exports = { { name: 'develop' // default channel + }, + { + name: 'alpha', + prerelease: true + }, + { + name: 'beta', + prerelease: true + }, + { + name: 'spork', + prerelease: true } ] }; diff --git a/shim/blockly_compressed_horizontal-blocks_compressed.js b/shim/blockly_compressed_horizontal-blocks_compressed.js deleted file mode 100644 index 32675cc413..0000000000 --- a/shim/blockly_compressed_horizontal-blocks_compressed.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('imports-loader?Blockly=./shim/blockly_compressed_horizontal.Blockly!exports-loader?Blockly!../blocks_compressed'); diff --git a/shim/blockly_compressed_horizontal.Blockly.js b/shim/blockly_compressed_horizontal.Blockly.js deleted file mode 100644 index f481649d88..0000000000 --- a/shim/blockly_compressed_horizontal.Blockly.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./blockly_compressed_horizontal').Blockly; diff --git a/shim/blockly_compressed_horizontal.goog.js b/shim/blockly_compressed_horizontal.goog.js deleted file mode 100644 index 1bc78ca5ae..0000000000 --- a/shim/blockly_compressed_horizontal.goog.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./blockly_compressed_horizontal').goog; diff --git a/shim/blockly_compressed_horizontal.js b/shim/blockly_compressed_horizontal.js deleted file mode 100644 index ed83a67472..0000000000 --- a/shim/blockly_compressed_horizontal.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('imports-loader?this=>window!exports-loader?Blockly&goog!../blockly_compressed_horizontal'); diff --git a/shim/blockly_compressed_vertical-blocks_compressed.js b/shim/blockly_compressed_vertical-blocks_compressed.js deleted file mode 100644 index 8eec1f68a2..0000000000 --- a/shim/blockly_compressed_vertical-blocks_compressed.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('imports-loader?Blockly=./shim/blockly_compressed_vertical.Blockly!exports-loader?Blockly!../blocks_compressed'); diff --git a/shim/blockly_compressed_vertical.Blockly.js b/shim/blockly_compressed_vertical.Blockly.js deleted file mode 100644 index 041f4e1313..0000000000 --- a/shim/blockly_compressed_vertical.Blockly.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./blockly_compressed_vertical').Blockly; diff --git a/shim/blockly_compressed_vertical.goog.js b/shim/blockly_compressed_vertical.goog.js deleted file mode 100644 index 22ccbfdd73..0000000000 --- a/shim/blockly_compressed_vertical.goog.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./blockly_compressed_vertical').goog; diff --git a/shim/blockly_compressed_vertical.js b/shim/blockly_compressed_vertical.js deleted file mode 100644 index dce46a9de5..0000000000 --- a/shim/blockly_compressed_vertical.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('imports-loader?this=>window!exports-loader?Blockly&goog!../blockly_compressed_vertical'); diff --git a/shim/blocks_compressed_horizontal-blockly_compressed_horizontal-messages.js b/shim/blocks_compressed_horizontal-blockly_compressed_horizontal-messages.js deleted file mode 100644 index 609080e29f..0000000000 --- a/shim/blocks_compressed_horizontal-blockly_compressed_horizontal-messages.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('imports-loader?Blockly=../shim/blocks_compressed_horizontal,goog=../shim/blockly_compressed_horizontal.goog!exports-loader?Blockly!../msg/messages'); diff --git a/shim/blocks_compressed_horizontal.js b/shim/blocks_compressed_horizontal.js deleted file mode 100644 index 0c4272ae08..0000000000 --- a/shim/blocks_compressed_horizontal.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('imports-loader?Blockly=./shim/blockly_compressed_horizontal-blocks_compressed!exports-loader?Blockly!../blocks_compressed_horizontal'); diff --git a/shim/blocks_compressed_vertical-blockly_compressed_vertical-messages.js b/shim/blocks_compressed_vertical-blockly_compressed_vertical-messages.js deleted file mode 100644 index 65449c83b4..0000000000 --- a/shim/blocks_compressed_vertical-blockly_compressed_vertical-messages.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('imports-loader?Blockly=../shim/blocks_compressed_vertical,goog=../shim/blockly_compressed_vertical.goog!exports-loader?Blockly!../msg/messages'); diff --git a/shim/blocks_compressed_vertical.js b/shim/blocks_compressed_vertical.js deleted file mode 100644 index 3368c09d03..0000000000 --- a/shim/blocks_compressed_vertical.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('imports-loader?goog=./shim/blockly_compressed_vertical.goog,Blockly=./shim/blockly_compressed_vertical-blocks_compressed!exports-loader?Blockly!../blocks_compressed_vertical'); diff --git a/shim/gh-pages.js b/shim/gh-pages.js deleted file mode 100644 index 36db4933d9..0000000000 --- a/shim/gh-pages.js +++ /dev/null @@ -1 +0,0 @@ -// intentionally left empty diff --git a/shim/horizontal.js b/shim/horizontal.js deleted file mode 100644 index 9416cada6d..0000000000 --- a/shim/horizontal.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('imports-loader?Blockly=../shim/blocks_compressed_horizontal-blockly_compressed_horizontal-messages,goog=../shim/blockly_compressed_horizontal.goog!exports-loader?Blockly!../msg/scratch_msgs'); diff --git a/shim/index.js b/shim/index.js deleted file mode 100644 index cd27975deb..0000000000 --- a/shim/index.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Webpack shim module - * - * Uses webpack imports-loader and exports-loader to provide the horizontal and - * vertical flavors of Blockly. All of the other files in this directory shim - * Blockly and goog between blockly_compressed_* and blocks_compressed*. - * - * Horizontal and Vertical export Blockly out of - * blockly_compressed_[horizontal, vertical] + - * blocks_compressed + - * blocks_compressed_[horizontal, vertical] + - * msg/messages -**/ -module.exports = { - Horizontal: require('./horizontal'), - Vertical: require('./vertical') -}; diff --git a/shim/vertical.js b/shim/vertical.js deleted file mode 100644 index 7990fe5b65..0000000000 --- a/shim/vertical.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('imports-loader?Blockly=../shim/blocks_compressed_vertical-blockly_compressed_vertical-messages,goog=../shim/blockly_compressed_vertical.goog!exports-loader?Blockly!../msg/scratch_msgs'); diff --git a/src/block_reporting.ts b/src/block_reporting.ts new file mode 100644 index 0000000000..9df9bc87ad --- /dev/null +++ b/src/block_reporting.ts @@ -0,0 +1,33 @@ +import * as Blockly from "blockly/core"; +import { Colours } from "./colours"; + +export function reportValue(id: string, value: string) { + const block = (Blockly.getMainWorkspace().getBlockById(id) || + (Blockly.getMainWorkspace() as Blockly.WorkspaceSvg) + .getFlyout() + .getWorkspace() + .getBlockById(id)) as Blockly.BlockSvg; + if (!block) { + throw "Tried to report value on block that does not exist."; + } + + let field; + for (const input of block.inputList) { + for (const f of input.fieldRow) { + field = f; + break; + } + } + if (!field) return; + + const contentDiv = Blockly.DropDownDiv.getContentDiv(); + const valueReportBox = document.createElement("div"); + valueReportBox.setAttribute("class", "valueReportBox"); + valueReportBox.innerText = value; + contentDiv.appendChild(valueReportBox); + Blockly.DropDownDiv.setColour( + Colours.valueReportBackground, + Colours.valueReportBorder + ); + Blockly.DropDownDiv.showPositionedByBlock(field, block); +} diff --git a/blocks_common/colour.js b/src/blocks/colour.ts similarity index 59% rename from blocks_common/colour.js rename to src/blocks/colour.ts index 30be7523ac..f1462786b9 100644 --- a/blocks_common/colour.js +++ b/src/blocks/colour.ts @@ -22,40 +22,35 @@ * @fileoverview Colour blocks for Blockly. * @author fraser@google.com (Neil Fraser) */ -'use strict'; - -goog.provide('Blockly.Blocks.colour'); - -goog.require('Blockly.Blocks'); - -goog.require('Blockly.constants'); +import * as Blockly from "blockly/core"; +import * as Constants from "../constants"; /** * Pick a random colour. - * @return {string} #RRGGBB for random colour. + * + * @returns #RRGGBB for random colour. */ -function randomColour() { - var num = Math.floor(Math.random() * Math.pow(2, 24)); - return '#' + ('00000' + num.toString(16)).substr(-6); +function randomColour(): string { + const num = Math.floor(Math.random() * Math.pow(2, 24)); + return "#" + ("00000" + num.toString(16)).substr(-6); } -Blockly.Blocks['colour_picker'] = { +Blockly.Blocks["colour_picker"] = { /** * Block for colour picker. - * @this Blockly.Block */ - init: function() { + init: function (this: Blockly.Block) { this.jsonInit({ - "message0": "%1", - "args0": [ + message0: "%1", + args0: [ { - "type": "field_colour_slider", - "name": "COLOUR", - "colour": randomColour() - } + type: "field_colour_slider", + name: "COLOUR", + colour: randomColour(), + }, ], - "outputShape": Blockly.OUTPUT_SHAPE_ROUND, - "output": "Colour" + outputShape: Constants.OUTPUT_SHAPE_ROUND, + output: "Colour", }); - } + }, }; diff --git a/src/blocks/control.ts b/src/blocks/control.ts new file mode 100644 index 0000000000..6c625ed78a --- /dev/null +++ b/src/blocks/control.ts @@ -0,0 +1,459 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Massachusetts Institute of Technology + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as Blockly from "blockly/core"; + +Blockly.Blocks["control_forever"] = { + /** + * Block for repeat n times (external number). + * https://blockly-demo.appspot.com/static/demos/blockfactory/index.html#5eke39 + */ + init: function (this: Blockly.Block) { + const ws = this.workspace.options.parentWorkspace || this.workspace; + this.jsonInit({ + id: "control_forever", + message0: Blockly.Msg.CONTROL_FOREVER, + message1: "%1", // Statement + message2: "%1", // Icon + lastDummyAlign2: "RIGHT", + args1: [ + { + type: "input_statement", + name: "SUBSTACK", + }, + ], + args2: [ + { + type: "field_image", + src: ws.options.pathToMedia + "repeat.svg", + width: 24, + height: 24, + alt: "*", + flip_rtl: true, + }, + ], + extensions: ["colours_control", "shape_end"], + }); + }, +}; + +Blockly.Blocks["control_repeat"] = { + /** + * Block for repeat n times (external number). + * https://blockly-demo.appspot.com/static/demos/blockfactory/index.html#so57n9 + */ + init: function (this: Blockly.Block) { + const ws = this.workspace.options.parentWorkspace || this.workspace; + this.jsonInit({ + id: "control_repeat", + message0: Blockly.Msg.CONTROL_REPEAT, + message1: "%1", // Statement + message2: "%1", // Icon + lastDummyAlign2: "RIGHT", + args0: [ + { + type: "input_value", + name: "TIMES", + }, + ], + args1: [ + { + type: "input_statement", + name: "SUBSTACK", + }, + ], + args2: [ + { + type: "field_image", + src: ws.options.pathToMedia + "repeat.svg", + width: 24, + height: 24, + alt: "*", + flip_rtl: true, + }, + ], + extensions: ["colours_control", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["control_if"] = { + /** + * Block for if-then. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + type: "control_if", + message0: Blockly.Msg.CONTROL_IF, + message1: "%1", // Statement + args0: [ + { + type: "input_value", + name: "CONDITION", + check: "Boolean", + }, + ], + args1: [ + { + type: "input_statement", + name: "SUBSTACK", + }, + ], + extensions: ["colours_control", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["control_if_else"] = { + /** + * Block for if-else. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + type: "control_if_else", + message0: Blockly.Msg.CONTROL_IF, + message1: "%1", + message2: Blockly.Msg.CONTROL_ELSE, + message3: "%1", + args0: [ + { + type: "input_value", + name: "CONDITION", + check: "Boolean", + }, + ], + args1: [ + { + type: "input_statement", + name: "SUBSTACK", + }, + ], + args3: [ + { + type: "input_statement", + name: "SUBSTACK2", + }, + ], + extensions: ["colours_control", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["control_stop"] = { + /** + * Block for stop all scripts. + */ + init: function (this: Blockly.Block) { + const ALL_SCRIPTS = "all"; + const THIS_SCRIPT = "this script"; + const OTHER_SCRIPTS = "other scripts in sprite"; + const stopDropdown = new Blockly.FieldDropdown( + function () { + if ( + this.sourceBlock_ && + this.sourceBlock_.nextConnection && + this.sourceBlock_.nextConnection.isConnected() + ) { + return [[Blockly.Msg.CONTROL_STOP_OTHER, OTHER_SCRIPTS]]; + } + return [ + [Blockly.Msg.CONTROL_STOP_ALL, ALL_SCRIPTS], + [Blockly.Msg.CONTROL_STOP_THIS, THIS_SCRIPT], + [Blockly.Msg.CONTROL_STOP_OTHER, OTHER_SCRIPTS], + ]; + }, + function (option) { + this.getSourceBlock().setNextStatement(option === OTHER_SCRIPTS); + return option; + } + ); + this.appendDummyInput() + .appendField(Blockly.Msg.CONTROL_STOP) + .appendField(stopDropdown, "STOP_OPTION"); + this.setStyle("control"); + this.setPreviousStatement(true); + }, +}; + +Blockly.Blocks["control_wait"] = { + /** + * Block to wait (pause) stack. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + id: "control_wait", + message0: Blockly.Msg.CONTROL_WAIT, + args0: [ + { + type: "input_value", + name: "DURATION", + }, + ], + extensions: ["colours_control", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["control_wait_until"] = { + /** + * Block to wait until a condition becomes true. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.CONTROL_WAITUNTIL, + args0: [ + { + type: "input_value", + name: "CONDITION", + check: "Boolean", + }, + ], + extensions: ["colours_control", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["control_repeat_until"] = { + /** + * Block to repeat until a condition becomes true. + */ + init: function (this: Blockly.Block) { + const ws = this.workspace.options.parentWorkspace || this.workspace; + this.jsonInit({ + message0: Blockly.Msg.CONTROL_REPEATUNTIL, + message1: "%1", + message2: "%1", + lastDummyAlign2: "RIGHT", + args0: [ + { + type: "input_value", + name: "CONDITION", + check: "Boolean", + }, + ], + args1: [ + { + type: "input_statement", + name: "SUBSTACK", + }, + ], + args2: [ + { + type: "field_image", + src: ws.options.pathToMedia + "repeat.svg", + width: 24, + height: 24, + alt: "*", + flip_rtl: true, + }, + ], + extensions: ["colours_control", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["control_while"] = { + /** + * Block to repeat until a condition becomes false. + * (This is an obsolete "hacked" block, for compatibility with 2.0.) + */ + init: function (this: Blockly.Block) { + const ws = this.workspace.options.parentWorkspace || this.workspace; + this.jsonInit({ + message0: Blockly.Msg.CONTROL_WHILE, + message1: "%1", + message2: "%1", + lastDummyAlign2: "RIGHT", + args0: [ + { + type: "input_value", + name: "CONDITION", + check: "Boolean", + }, + ], + args1: [ + { + type: "input_statement", + name: "SUBSTACK", + }, + ], + args2: [ + { + type: "field_image", + src: ws.options.pathToMedia + "repeat.svg", + width: 24, + height: 24, + alt: "*", + flip_rtl: true, + }, + ], + extensions: ["colours_control", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["control_for_each"] = { + /** + * Block for for-each. This is an obsolete block that is implemented for + * compatibility with Scratch 2.0 projects. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + type: "control_for_each", + message0: Blockly.Msg.CONTROL_FOREACH, + message1: "%1", + args0: [ + { + type: "field_variable", + name: "VARIABLE", + }, + { + type: "input_value", + name: "VALUE", + }, + ], + args1: [ + { + type: "input_statement", + name: "SUBSTACK", + }, + ], + extensions: ["colours_control", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["control_start_as_clone"] = { + /** + * Block for "when I start as a clone" hat. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + id: "control_start_as_clone", + message0: Blockly.Msg.CONTROL_STARTASCLONE, + args0: [], + extensions: ["colours_control", "shape_hat"], + }); + }, +}; + +/** + * Create-clone drop-down menu. Populated dynamically by scratch-gui. + */ +Blockly.Blocks["control_create_clone_of_menu"] = {}; + +Blockly.Blocks["control_create_clone_of"] = { + /** + * Block for "create clone of..." + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + id: "control_start_as_clone", + message0: Blockly.Msg.CONTROL_CREATECLONEOF, + args0: [ + { + type: "input_value", + name: "CLONE_OPTION", + }, + ], + extensions: ["colours_control", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["control_delete_this_clone"] = { + /** + * Block for "delete this clone." + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.CONTROL_DELETETHISCLONE, + args0: [], + extensions: ["colours_control", "shape_end"], + }); + }, +}; + +Blockly.Blocks["control_get_counter"] = { + /** + * Block to get the counter value. This is an obsolete block that is + * implemented for compatibility with Scratch 2.0 projects. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.CONTROL_COUNTER, + extensions: ["colours_control", "output_number"], + }); + }, +}; + +Blockly.Blocks["control_incr_counter"] = { + /** + * Block to add one to the counter value. This is an obsolete block that is + * implemented for compatibility with Scratch 2.0 projects. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.CONTROL_INCRCOUNTER, + extensions: ["colours_control", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["control_clear_counter"] = { + /** + * Block to clear the counter value. This is an obsolete block that is + * implemented for compatibility with Scratch 2.0 projects. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.CONTROL_CLEARCOUNTER, + extensions: ["colours_control", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["control_all_at_once"] = { + /** + * Block to run the contained script. This is an obsolete block that is + * implemented for compatibility with Scratch 2.0 projects. Note that + * this was originally designed to run all of the contained blocks + * (sequentially, like normal) within a single frame, but this feature + * was removed in place of custom blocks marked "run without screen + * refresh". The "all at once" block was changed to run the contained + * blocks ordinarily, functioning the same way as an "if" block with a + * reporter that is always true (e.g. "if 1 = 1"). Also note that the + * Scratch 2.0 spec for this block is "warpSpeed", but the label shows + * "all at once". + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.CONTROL_ALLATONCE, + message1: "%1", // Statement + args1: [ + { + type: "input_statement", + name: "SUBSTACK", + }, + ], + extensions: ["colours_control", "shape_statement"], + }); + }, +}; diff --git a/src/blocks/data.ts b/src/blocks/data.ts new file mode 100644 index 0000000000..bbc0588cdf --- /dev/null +++ b/src/blocks/data.ts @@ -0,0 +1,682 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Massachusetts Institute of Technology + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as Blockly from "blockly/core"; +import * as Constants from "../constants"; +import * as scratchBlocksUtils from "../scratch_blocks_utils"; +import { renameVariable } from "../variables"; +import type { ScratchFieldVariable } from "../fields/scratch_field_variable"; +import type { ScratchVariableModel } from "../scratch_variable_model"; + +Blockly.Blocks["data_variable"] = { + /** + * Block of Variables + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: "%1", + lastDummyAlign0: "CENTRE", + args0: [ + { + type: "field_variable_getter", + name: "VARIABLE", + allowedVariableType: Constants.SCALAR_VARIABLE_TYPE, + }, + ], + extensions: [ + "contextMenu_getVariableBlock", + "colours_data", + "output_string", + "monitor_block", + ], + }); + }, +}; + +Blockly.Blocks["data_setvariableto"] = { + /** + * Block to set variable to a certain value + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.DATA_SETVARIABLETO, + args0: [ + { + type: "field_variable", + name: "VARIABLE", + variableTypes: [Constants.SCALAR_VARIABLE_TYPE], + defaultType: Constants.SCALAR_VARIABLE_TYPE, + }, + { + type: "input_value", + name: "VALUE", + }, + ], + extensions: ["colours_data", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["data_changevariableby"] = { + /** + * Block to change variable by a certain value + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.DATA_CHANGEVARIABLEBY, + args0: [ + { + type: "field_variable", + name: "VARIABLE", + variableTypes: [Constants.SCALAR_VARIABLE_TYPE], + defaultType: Constants.SCALAR_VARIABLE_TYPE, + }, + { + type: "input_value", + name: "VALUE", + }, + ], + extensions: ["colours_data", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["data_showvariable"] = { + /** + * Block to show a variable + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.DATA_SHOWVARIABLE, + args0: [ + { + type: "field_variable", + name: "VARIABLE", + variableTypes: [Constants.SCALAR_VARIABLE_TYPE], + defaultType: Constants.SCALAR_VARIABLE_TYPE, + }, + ], + previousStatement: null, + nextStatement: null, + extensions: ["colours_data"], + }); + }, +}; + +Blockly.Blocks["data_hidevariable"] = { + /** + * Block to hide a variable + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.DATA_HIDEVARIABLE, + args0: [ + { + type: "field_variable", + name: "VARIABLE", + variableTypes: [Constants.SCALAR_VARIABLE_TYPE], + defaultType: Constants.SCALAR_VARIABLE_TYPE, + }, + ], + previousStatement: null, + nextStatement: null, + extensions: ["colours_data"], + }); + }, +}; + +Blockly.Blocks["data_listcontents"] = { + /** + * List reporter. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: "%1", + args0: [ + { + type: "field_variable_getter", + name: "LIST", + allowedVariableType: Constants.LIST_VARIABLE_TYPE, + }, + ], + extensions: [ + "contextMenu_getListBlock", + "colours_data_lists", + "output_string", + "monitor_block", + ], + }); + }, +}; + +Blockly.Blocks["data_listindexall"] = { + /** + * List index menu, with all option. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: "%1", + args0: [ + { + type: "field_numberdropdown", + name: "INDEX", + value: "1", + min: 1, + precision: 1, + options: [ + ["1", "1"], + [Blockly.Msg.DATA_INDEX_LAST, "last"], + [Blockly.Msg.DATA_INDEX_ALL, "all"], + ], + }, + ], + extensions: ["colours_textfield", "output_string"], + }); + }, +}; + +Blockly.Blocks["data_listindexrandom"] = { + /** + * List index menu, with random option. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: "%1", + args0: [ + { + type: "field_numberdropdown", + name: "INDEX", + value: "1", + min: 1, + precision: 1, + options: [ + ["1", "1"], + [Blockly.Msg.DATA_INDEX_LAST, "last"], + [Blockly.Msg.DATA_INDEX_RANDOM, "random"], + ], + }, + ], + extensions: ["colours_textfield", "output_string"], + }); + }, +}; + +Blockly.Blocks["data_addtolist"] = { + /** + * Block to add item to list. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.DATA_ADDTOLIST, + args0: [ + { + type: "input_value", + name: "ITEM", + }, + { + type: "field_variable", + name: "LIST", + variableTypes: [Constants.LIST_VARIABLE_TYPE], + defaultType: Constants.LIST_VARIABLE_TYPE, + }, + ], + extensions: ["colours_data_lists", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["data_deleteoflist"] = { + /** + * Block to delete item from list. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.DATA_DELETEOFLIST, + args0: [ + { + type: "input_value", + name: "INDEX", + }, + { + type: "field_variable", + name: "LIST", + variableTypes: [Constants.LIST_VARIABLE_TYPE], + defaultType: Constants.LIST_VARIABLE_TYPE, + }, + ], + extensions: ["colours_data_lists", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["data_deletealloflist"] = { + /** + * Block to delete all items from list. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.DATA_DELETEALLOFLIST, + args0: [ + { + type: "field_variable", + name: "LIST", + variableTypes: [Constants.LIST_VARIABLE_TYPE], + defaultType: Constants.LIST_VARIABLE_TYPE, + }, + ], + extensions: ["colours_data_lists", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["data_insertatlist"] = { + /** + * Block to insert item to list. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.DATA_INSERTATLIST, + args0: [ + { + type: "input_value", + name: "ITEM", + }, + { + type: "input_value", + name: "INDEX", + }, + { + type: "field_variable", + name: "LIST", + variableTypes: [Constants.LIST_VARIABLE_TYPE], + defaultType: Constants.LIST_VARIABLE_TYPE, + }, + ], + extensions: ["colours_data_lists", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["data_replaceitemoflist"] = { + /** + * Block to insert item to list. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.DATA_REPLACEITEMOFLIST, + args0: [ + { + type: "input_value", + name: "INDEX", + }, + { + type: "field_variable", + name: "LIST", + variableTypes: [Constants.LIST_VARIABLE_TYPE], + defaultType: Constants.LIST_VARIABLE_TYPE, + }, + { + type: "input_value", + name: "ITEM", + }, + ], + extensions: ["colours_data_lists", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["data_itemoflist"] = { + /** + * Block for reporting item of list. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.DATA_ITEMOFLIST, + args0: [ + { + type: "input_value", + name: "INDEX", + }, + { + type: "field_variable", + name: "LIST", + variableTypes: [Constants.LIST_VARIABLE_TYPE], + defaultType: Constants.LIST_VARIABLE_TYPE, + }, + ], + output: null, + extensions: ["colours_data_lists"], + outputShape: Constants.OUTPUT_SHAPE_ROUND, + }); + }, +}; + +Blockly.Blocks["data_itemnumoflist"] = { + /** + * Block for reporting the item # of a string in a list. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.DATA_ITEMNUMOFLIST, + args0: [ + { + type: "input_value", + name: "ITEM", + }, + { + type: "field_variable", + name: "LIST", + variableTypes: [Constants.LIST_VARIABLE_TYPE], + defaultType: Constants.LIST_VARIABLE_TYPE, + }, + ], + output: null, + extensions: ["colours_data_lists"], + outputShape: Constants.OUTPUT_SHAPE_ROUND, + }); + }, +}; + +Blockly.Blocks["data_lengthoflist"] = { + /** + * Block for reporting length of list. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.DATA_LENGTHOFLIST, + args0: [ + { + type: "field_variable", + name: "LIST", + variableTypes: [Constants.LIST_VARIABLE_TYPE], + defaultType: Constants.LIST_VARIABLE_TYPE, + }, + ], + extensions: ["colours_data_lists", "output_number"], + }); + }, +}; + +Blockly.Blocks["data_listcontainsitem"] = { + /** + * Block to report whether list contains item. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.DATA_LISTCONTAINSITEM, + args0: [ + { + type: "field_variable", + name: "LIST", + variableTypes: [Constants.LIST_VARIABLE_TYPE], + defaultType: Constants.LIST_VARIABLE_TYPE, + }, + { + type: "input_value", + name: "ITEM", + }, + ], + extensions: ["colours_data_lists", "output_boolean"], + }); + }, +}; + +Blockly.Blocks["data_showlist"] = { + /** + * Block to show a list. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.DATA_SHOWLIST, + args0: [ + { + type: "field_variable", + name: "LIST", + variableTypes: [Constants.LIST_VARIABLE_TYPE], + defaultType: Constants.LIST_VARIABLE_TYPE, + }, + ], + extensions: ["colours_data_lists", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["data_hidelist"] = { + /** + * Block to hide a list. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.DATA_HIDELIST, + args0: [ + { + type: "field_variable", + name: "LIST", + variableTypes: [Constants.LIST_VARIABLE_TYPE], + defaultType: Constants.LIST_VARIABLE_TYPE, + }, + ], + extensions: ["colours_data_lists", "shape_statement"], + }); + }, +}; + +/** + * Mixin to add a context menu for a data_variable block. It adds one item for + * each variable defined on the workspace. + */ +const CUSTOM_CONTEXT_MENU_GET_VARIABLE_MIXIN = { + /** + * Add context menu option to change the selected variable. + * + * @param options List of menu options to add to. + */ + customContextMenu: function ( + this: Blockly.Block, + options: Array< + | Blockly.ContextMenuRegistry.ContextMenuOption + | Blockly.ContextMenuRegistry.LegacyContextMenuOption + > + ) { + const fieldName = "VARIABLE"; + if (this.isCollapsed()) { + return; + } + const currentVarName = (this.getField(fieldName) as ScratchFieldVariable) + .getVariable() + .getName(); + if (!this.isInFlyout) { + this.workspace + .getVariablesOfType(Constants.SCALAR_VARIABLE_TYPE) + .sort(function ( + a: Blockly.IVariableModel, + b: Blockly.IVariableModel + ) { + return scratchBlocksUtils.compareStrings(a.getName(), b.getName()); + }) + .forEach((variable: Blockly.IVariableModel) => { + const varName = variable.getName(); + if (varName === currentVarName) return; + + options.push({ + enabled: true, + text: varName, + callback: VARIABLE_OPTION_CALLBACK_FACTORY( + this, + variable.getId(), + fieldName + ), + }); + }); + } else { + const renameOption = { + text: Blockly.Msg.RENAME_VARIABLE, + enabled: true, + callback: RENAME_OPTION_CALLBACK_FACTORY(this, fieldName), + }; + const deleteOption = { + text: Blockly.Msg.DELETE_VARIABLE.replace("%1", currentVarName), + enabled: true, + callback: DELETE_OPTION_CALLBACK_FACTORY(this, fieldName), + }; + options.push(renameOption); + options.push(deleteOption); + } + }, +}; + +Blockly.Extensions.registerMixin( + "contextMenu_getVariableBlock", + CUSTOM_CONTEXT_MENU_GET_VARIABLE_MIXIN +); + +/** + * Mixin to add a context menu for a data_listcontents block. It adds one item for + * each list defined on the workspace. + */ +const CUSTOM_CONTEXT_MENU_GET_LIST_MIXIN = { + /** + * Add context menu option to change the selected list. + * + * @param options List of menu options to add to. + */ + customContextMenu: function ( + this: Blockly.Block, + options: Array< + | Blockly.ContextMenuRegistry.ContextMenuOption + | Blockly.ContextMenuRegistry.LegacyContextMenuOption + > + ) { + const fieldName = "LIST"; + if (this.isCollapsed()) { + return; + } + const currentVarName = (this.getField(fieldName) as ScratchFieldVariable) + .getVariable() + .getName(); + if (!this.isInFlyout) { + this.workspace + .getVariablesOfType(Constants.LIST_VARIABLE_TYPE) + .sort(function ( + a: Blockly.IVariableModel, + b: Blockly.IVariableModel + ) { + return scratchBlocksUtils.compareStrings(a.getName(), b.getName()); + }) + .forEach((variable: Blockly.IVariableModel) => { + const varName = variable.getName(); + if (varName === currentVarName) return; + + options.push({ + enabled: true, + text: varName, + callback: VARIABLE_OPTION_CALLBACK_FACTORY( + this, + variable.getId(), + fieldName + ), + }); + }); + } else { + const renameOption = { + text: Blockly.Msg.RENAME_LIST, + enabled: true, + callback: RENAME_OPTION_CALLBACK_FACTORY(this, fieldName), + }; + const deleteOption = { + text: Blockly.Msg.DELETE_LIST.replace("%1", currentVarName), + enabled: true, + callback: DELETE_OPTION_CALLBACK_FACTORY(this, fieldName), + }; + options.push(renameOption); + options.push(deleteOption); + } + }, +}; +Blockly.Extensions.registerMixin( + "contextMenu_getListBlock", + CUSTOM_CONTEXT_MENU_GET_LIST_MIXIN +); + +/** + * Callback factory for dropdown menu options associated with a variable getter + * block. Each variable on the workspace gets its own item in the dropdown + * menu, and clicking on that item changes the text of the field on the source + * block. + * + * @param block The block to update. + * @param id The id of the variable to set on this block. + * @param fieldName The name of the field to update on the block. + * @returns A function that updates the block with the new name. + */ +const VARIABLE_OPTION_CALLBACK_FACTORY = function ( + block: Blockly.Block, + id: string, + fieldName: string +): () => void { + return () => { + const variableField = block.getField(fieldName); + if (!variableField) { + console.log("Tried to get a variable field on the wrong type of block."); + } + variableField.setValue(id); + }; +}; + +/** + * Callback for rename variable dropdown menu option associated with a + * variable getter block. + * + * @param block The block with the variable to rename. + * @param fieldName The name of the field to inspect on the block. + * @returns A function that renames the variable. + */ +const RENAME_OPTION_CALLBACK_FACTORY = function ( + block: Blockly.Block, + fieldName: string +): () => void { + return () => { + const workspace = block.workspace; + const variable = ( + block.getField(fieldName) as ScratchFieldVariable + ).getVariable() as ScratchVariableModel; + renameVariable(workspace as Blockly.WorkspaceSvg, variable); + }; +}; + +/** + * Callback for delete variable dropdown menu option associated with a + * variable getter block. + * + * @param block The block with the variable to delete. + * @param fieldName The name of the field to inspect on the block. + * @return A function that deletes the variable. + */ +const DELETE_OPTION_CALLBACK_FACTORY = function ( + block: Blockly.Block, + fieldName: string +): () => void { + return () => { + const variable = ( + block.getField(fieldName) as ScratchFieldVariable + ).getVariable(); + Blockly.Variables.deleteVariable(variable.getWorkspace(), variable, block); + }; +}; diff --git a/src/blocks/event.ts b/src/blocks/event.ts new file mode 100644 index 0000000000..609c260e96 --- /dev/null +++ b/src/blocks/event.ts @@ -0,0 +1,284 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Massachusetts Institute of Technology + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as Blockly from "blockly/core"; +import * as Constants from "../constants"; + +Blockly.Blocks["event_whentouchingobject"] = { + /** + * Block for when a sprite is touching an object. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.EVENT_WHENTOUCHINGOBJECT, + args0: [ + { + type: "input_value", + name: "TOUCHINGOBJECTMENU", + }, + ], + extensions: ["colours_event", "shape_hat"], + }); + }, +}; + +Blockly.Blocks["event_touchingobjectmenu"] = { + /** + * "Touching [Object]" Block Menu. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: "%1", + args0: [ + { + type: "field_dropdown", + name: "TOUCHINGOBJECTMENU", + options: [ + [Blockly.Msg.SENSING_TOUCHINGOBJECT_POINTER, "_mouse_"], + [Blockly.Msg.SENSING_TOUCHINGOBJECT_EDGE, "_edge_"], + ], + }, + ], + extensions: ["colours_event", "output_string"], + }); + }, +}; + +Blockly.Blocks["event_whenflagclicked"] = { + /** + * Block for when flag clicked. + */ + init: function (this: Blockly.Block) { + const ws = this.workspace.options.parentWorkspace || this.workspace; + this.jsonInit({ + id: "event_whenflagclicked", + message0: Blockly.Msg.EVENT_WHENFLAGCLICKED, + args0: [ + { + type: "field_image", + src: ws.options.pathToMedia + "green-flag.svg", + width: 24, + height: 24, + alt: "flag", + }, + ], + extensions: ["colours_event", "shape_hat"], + }); + }, +}; + +Blockly.Blocks["event_whenthisspriteclicked"] = { + /** + * Block for when this sprite clicked. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.EVENT_WHENTHISSPRITECLICKED, + extensions: ["colours_event", "shape_hat"], + }); + }, +}; + +Blockly.Blocks["event_whenstageclicked"] = { + /** + * Block for when the stage is clicked. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.EVENT_WHENSTAGECLICKED, + extensions: ["colours_event", "shape_hat"], + }); + }, +}; + +Blockly.Blocks["event_whenbroadcastreceived"] = { + /** + * Block for when broadcast received. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + id: "event_whenbroadcastreceived", + message0: Blockly.Msg.EVENT_WHENBROADCASTRECEIVED, + args0: [ + { + type: "field_variable", + name: "BROADCAST_OPTION", + variableTypes: [Constants.BROADCAST_MESSAGE_VARIABLE_TYPE], + defaultType: Constants.BROADCAST_MESSAGE_VARIABLE_TYPE, + variable: Blockly.Msg.DEFAULT_BROADCAST_MESSAGE_NAME, + }, + ], + extensions: ["colours_event", "shape_hat"], + }); + }, +}; + +/** + * Block for when the current backdrop switched to a selected backdrop. + * Populated dynamically by scratch-gui. + */ +Blockly.Blocks["event_whenbackdropswitchesto"] = {}; + +Blockly.Blocks["event_whengreaterthan"] = { + /** + * Block for when loudness/timer/video motion is greater than the value. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.EVENT_WHENGREATERTHAN, + args0: [ + { + type: "field_dropdown", + name: "WHENGREATERTHANMENU", + options: [ + [Blockly.Msg.EVENT_WHENGREATERTHAN_LOUDNESS, "LOUDNESS"], + [Blockly.Msg.EVENT_WHENGREATERTHAN_TIMER, "TIMER"], + ], + }, + { + type: "input_value", + name: "VALUE", + }, + ], + extensions: ["colours_event", "shape_hat"], + }); + }, +}; + +Blockly.Blocks["event_broadcast_menu"] = { + /** + * Broadcast drop-down menu. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: "%1", + args0: [ + { + type: "field_variable", + name: "BROADCAST_OPTION", + variableTypes: [Constants.BROADCAST_MESSAGE_VARIABLE_TYPE], + defaultType: Constants.BROADCAST_MESSAGE_VARIABLE_TYPE, + variable: Blockly.Msg.DEFAULT_BROADCAST_MESSAGE_NAME, + }, + ], + extensions: ["colours_event", "output_string"], + }); + }, +}; + +Blockly.Blocks["event_broadcast"] = { + /** + * Block to send a broadcast. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + id: "event_broadcast", + message0: Blockly.Msg.EVENT_BROADCAST, + args0: [ + { + type: "input_value", + name: "BROADCAST_INPUT", + }, + ], + extensions: ["colours_event", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["event_broadcastandwait"] = { + /** + * Block to send a broadcast. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.EVENT_BROADCASTANDWAIT, + args0: [ + { + type: "input_value", + name: "BROADCAST_INPUT", + }, + ], + extensions: ["colours_event", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["event_whenkeypressed"] = { + /** + * Block to send a broadcast. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + id: "event_whenkeypressed", + message0: Blockly.Msg.EVENT_WHENKEYPRESSED, + args0: [ + { + type: "field_dropdown", + name: "KEY_OPTION", + options: [ + [Blockly.Msg.EVENT_WHENKEYPRESSED_SPACE, "space"], + [Blockly.Msg.EVENT_WHENKEYPRESSED_UP, "up arrow"], + [Blockly.Msg.EVENT_WHENKEYPRESSED_DOWN, "down arrow"], + [Blockly.Msg.EVENT_WHENKEYPRESSED_RIGHT, "right arrow"], + [Blockly.Msg.EVENT_WHENKEYPRESSED_LEFT, "left arrow"], + [Blockly.Msg.EVENT_WHENKEYPRESSED_ANY, "any"], + ["a", "a"], + ["b", "b"], + ["c", "c"], + ["d", "d"], + ["e", "e"], + ["f", "f"], + ["g", "g"], + ["h", "h"], + ["i", "i"], + ["j", "j"], + ["k", "k"], + ["l", "l"], + ["m", "m"], + ["n", "n"], + ["o", "o"], + ["p", "p"], + ["q", "q"], + ["r", "r"], + ["s", "s"], + ["t", "t"], + ["u", "u"], + ["v", "v"], + ["w", "w"], + ["x", "x"], + ["y", "y"], + ["z", "z"], + ["0", "0"], + ["1", "1"], + ["2", "2"], + ["3", "3"], + ["4", "4"], + ["5", "5"], + ["6", "6"], + ["7", "7"], + ["8", "8"], + ["9", "9"], + ], + }, + ], + extensions: ["colours_event", "shape_hat"], + }); + }, +}; diff --git a/src/blocks/looks.ts b/src/blocks/looks.ts new file mode 100644 index 0000000000..5d3b907e3d --- /dev/null +++ b/src/blocks/looks.ts @@ -0,0 +1,488 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Massachusetts Institute of Technology + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as Blockly from "blockly/core"; + +Blockly.Blocks["looks_sayforsecs"] = { + /** + * Block to say for some time. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_SAYFORSECS, + args0: [ + { + type: "input_value", + name: "MESSAGE", + }, + { + type: "input_value", + name: "SECS", + }, + ], + extensions: ["colours_looks", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["looks_say"] = { + /** + * Block to say. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_SAY, + args0: [ + { + type: "input_value", + name: "MESSAGE", + }, + ], + extensions: ["colours_looks", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["looks_thinkforsecs"] = { + /** + * Block to think for some time. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_THINKFORSECS, + args0: [ + { + type: "input_value", + name: "MESSAGE", + }, + { + type: "input_value", + name: "SECS", + }, + ], + extensions: ["colours_looks", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["looks_think"] = { + /** + * Block to think. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_THINK, + args0: [ + { + type: "input_value", + name: "MESSAGE", + }, + ], + extensions: ["colours_looks", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["looks_show"] = { + /** + * Show block. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_SHOW, + extensions: ["colours_looks", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["looks_hide"] = { + /** + * Hide block. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_HIDE, + extensions: ["colours_looks", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["looks_hideallsprites"] = { + /** + * Hide-all-sprites block. Does not actually do anything. This is an + * obsolete block that is implemented for compatibility with Scratch 2.0 + * projects. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_HIDEALLSPRITES, + extensions: ["colours_looks", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["looks_changeeffectby"] = { + /** + * Block to change graphic effect. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_CHANGEEFFECTBY, + args0: [ + { + type: "field_dropdown", + name: "EFFECT", + options: [ + [Blockly.Msg.LOOKS_EFFECT_COLOR, "COLOR"], + [Blockly.Msg.LOOKS_EFFECT_FISHEYE, "FISHEYE"], + [Blockly.Msg.LOOKS_EFFECT_WHIRL, "WHIRL"], + [Blockly.Msg.LOOKS_EFFECT_PIXELATE, "PIXELATE"], + [Blockly.Msg.LOOKS_EFFECT_MOSAIC, "MOSAIC"], + [Blockly.Msg.LOOKS_EFFECT_BRIGHTNESS, "BRIGHTNESS"], + [Blockly.Msg.LOOKS_EFFECT_GHOST, "GHOST"], + ], + }, + { + type: "input_value", + name: "CHANGE", + }, + ], + extensions: ["colours_looks", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["looks_seteffectto"] = { + /** + * Block to set graphic effect. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_SETEFFECTTO, + args0: [ + { + type: "field_dropdown", + name: "EFFECT", + options: [ + [Blockly.Msg.LOOKS_EFFECT_COLOR, "COLOR"], + [Blockly.Msg.LOOKS_EFFECT_FISHEYE, "FISHEYE"], + [Blockly.Msg.LOOKS_EFFECT_WHIRL, "WHIRL"], + [Blockly.Msg.LOOKS_EFFECT_PIXELATE, "PIXELATE"], + [Blockly.Msg.LOOKS_EFFECT_MOSAIC, "MOSAIC"], + [Blockly.Msg.LOOKS_EFFECT_BRIGHTNESS, "BRIGHTNESS"], + [Blockly.Msg.LOOKS_EFFECT_GHOST, "GHOST"], + ], + }, + { + type: "input_value", + name: "VALUE", + }, + ], + extensions: ["colours_looks", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["looks_cleargraphiceffects"] = { + /** + * Block to clear graphic effects. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_CLEARGRAPHICEFFECTS, + extensions: ["colours_looks", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["looks_changesizeby"] = { + /** + * Block to change size + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_CHANGESIZEBY, + args0: [ + { + type: "input_value", + name: "CHANGE", + }, + ], + extensions: ["colours_looks", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["looks_setsizeto"] = { + /** + * Block to set size + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_SETSIZETO, + args0: [ + { + type: "input_value", + name: "SIZE", + }, + ], + extensions: ["colours_looks", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["looks_size"] = { + /** + * Block to report size + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_SIZE, + extensions: ["colours_looks", "output_number", "monitor_block"], + }); + }, +}; + +Blockly.Blocks["looks_changestretchby"] = { + /** + * Block to change stretch. Does not actually do anything. This is an + * obsolete block that is implemented for compatibility with Scratch 1.4 + * projects as well as 2.0 projects that still have the block. + * The "stretch" blocks were introduced in very early versions of Scratch, + * but their functionality was removed shortly later. They still appeared + * correctly up until (and including) Scratch 1.4 - as "change stretch by" + * and "set stretch to" - but were removed altogether in Scratch 2.0, and + * displayed as red "undefined" blocks. Some Scratch projects still contain + * these blocks, however, and they don't open in 3.0 unless the blocks + * actually exist (though they still don't funcitonally do anything). + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_CHANGESTRETCHBY, + args0: [ + { + type: "input_value", + name: "CHANGE", + }, + ], + extensions: ["colours_looks", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["looks_setstretchto"] = { + /** + * Block to set stretch. Does not actually do anything. This is an obsolete + * block that is implemented for compatibility with Scratch 1.4 projects + * (see looks_changestretchby). + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_SETSTRETCHTO, + args0: [ + { + type: "input_value", + name: "STRETCH", + }, + ], + extensions: ["colours_looks", "shape_statement"], + }); + }, +}; + +/** + * Costumes drop-down menu. Populated dynamically by scratch-gui. + */ +Blockly.Blocks["looks_costume"] = {}; + +Blockly.Blocks["looks_switchcostumeto"] = { + /** + * Block to switch the sprite's costume to the selected one. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_SWITCHCOSTUMETO, + args0: [ + { + type: "input_value", + name: "COSTUME", + }, + ], + extensions: ["colours_looks", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["looks_nextcostume"] = { + /** + * Block to switch the sprite's costume to the next one. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_NEXTCOSTUME, + extensions: ["colours_looks", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["looks_switchbackdropto"] = { + /** + * Block to switch the backdrop to the selected one. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_SWITCHBACKDROPTO, + args0: [ + { + type: "input_value", + name: "BACKDROP", + }, + ], + extensions: ["colours_looks", "shape_statement"], + }); + }, +}; + +/** + * Backdrop list. Populated dynamically by scratch-gui. + */ +Blockly.Blocks["looks_backdrops"] = {}; + +Blockly.Blocks["looks_gotofrontback"] = { + /** + * "Go to front/back" Block. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_GOTOFRONTBACK, + args0: [ + { + type: "field_dropdown", + name: "FRONT_BACK", + options: [ + [Blockly.Msg.LOOKS_GOTOFRONTBACK_FRONT, "front"], + [Blockly.Msg.LOOKS_GOTOFRONTBACK_BACK, "back"], + ], + }, + ], + extensions: ["colours_looks", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["looks_goforwardbackwardlayers"] = { + /** + * "Go forward/backward [Number] Layers" Block. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_GOFORWARDBACKWARDLAYERS, + args0: [ + { + type: "field_dropdown", + name: "FORWARD_BACKWARD", + options: [ + [Blockly.Msg.LOOKS_GOFORWARDBACKWARDLAYERS_FORWARD, "forward"], + [Blockly.Msg.LOOKS_GOFORWARDBACKWARDLAYERS_BACKWARD, "backward"], + ], + }, + { + type: "input_value", + name: "NUM", + }, + ], + extensions: ["colours_looks", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["looks_backdropnumbername"] = { + /** + * Block to report backdrop's number or name + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_BACKDROPNUMBERNAME, + args0: [ + { + type: "field_dropdown", + name: "NUMBER_NAME", + options: [ + [Blockly.Msg.LOOKS_NUMBERNAME_NUMBER, "number"], + [Blockly.Msg.LOOKS_NUMBERNAME_NAME, "name"], + ], + }, + ], + extensions: ["colours_looks", "output_number", "monitor_block"], + }); + }, +}; + +Blockly.Blocks["looks_costumenumbername"] = { + /** + * Block to report costume's number or name + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_COSTUMENUMBERNAME, + args0: [ + { + type: "field_dropdown", + name: "NUMBER_NAME", + options: [ + [Blockly.Msg.LOOKS_NUMBERNAME_NUMBER, "number"], + [Blockly.Msg.LOOKS_NUMBERNAME_NAME, "name"], + ], + }, + ], + extensions: ["colours_looks", "output_number", "monitor_block"], + }); + }, +}; + +Blockly.Blocks["looks_switchbackdroptoandwait"] = { + /** + * Block to switch the backdrop to the selected one and wait. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_SWITCHBACKDROPTOANDWAIT, + args0: [ + { + type: "input_value", + name: "BACKDROP", + }, + ], + extensions: ["colours_looks", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["looks_nextbackdrop"] = { + /** + * Block to switch the backdrop to the next one. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.LOOKS_NEXTBACKDROP_BLOCK, + extensions: ["colours_looks", "shape_statement"], + }); + }, +}; diff --git a/src/blocks/math.ts b/src/blocks/math.ts new file mode 100644 index 0000000000..1e6e8ce85f --- /dev/null +++ b/src/blocks/math.ts @@ -0,0 +1,132 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2012 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Math blocks for Blockly. + * @author q.neutron@gmail.com (Quynh Neutron) + */ +import * as Blockly from "blockly/core"; +import * as Constants from "../constants"; + +Blockly.Blocks["math_number"] = { + /** + * Block for generic numeric value. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: "%1", + args0: [ + { + type: "field_number", + name: "NUM", + value: "0", + }, + ], + output: "Number", + outputShape: Constants.OUTPUT_SHAPE_ROUND, + extensions: ["colours_textfield"], + }); + }, +}; + +Blockly.Blocks["math_integer"] = { + /** + * Block for integer value (no decimal, + or -). + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: "%1", + args0: [ + { + type: "field_number", + name: "NUM", + precision: 1, + }, + ], + output: "Number", + outputShape: Constants.OUTPUT_SHAPE_ROUND, + extensions: ["colours_textfield"], + }); + }, +}; + +Blockly.Blocks["math_whole_number"] = { + /** + * Block for whole number value, no negatives or decimals. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: "%1", + args0: [ + { + type: "field_number", + name: "NUM", + min: 0, + precision: 1, + }, + ], + output: "Number", + outputShape: Constants.OUTPUT_SHAPE_ROUND, + extensions: ["colours_textfield"], + }); + }, +}; + +Blockly.Blocks["math_positive_number"] = { + /** + * Block for positive number value, with decimal. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: "%1", + args0: [ + { + type: "field_number", + name: "NUM", + min: 0, + }, + ], + output: "Number", + outputShape: Constants.OUTPUT_SHAPE_ROUND, + extensions: ["colours_textfield"], + }); + }, +}; + +Blockly.Blocks["math_angle"] = { + /** + * Block for angle picker. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: "%1", + args0: [ + { + type: "field_angle", + name: "NUM", + value: 90, + }, + ], + output: "Number", + outputShape: Constants.OUTPUT_SHAPE_ROUND, + extensions: ["colours_textfield"], + }); + }, +}; diff --git a/blocks_common/matrix.js b/src/blocks/matrix.ts similarity index 65% rename from blocks_common/matrix.js rename to src/blocks/matrix.ts index 8945f8b09a..68940c580b 100644 --- a/blocks_common/matrix.js +++ b/src/blocks/matrix.ts @@ -22,33 +22,25 @@ * @fileoverview Matrix blocks for Blockly. * @author khanning@gmail.com (Kreg Hanning) */ -'use strict'; +import * as Blockly from "blockly/core"; +import * as Constants from "../constants"; -goog.provide('Blockly.Blocks.matrix'); - -goog.require('Blockly.Blocks'); - -goog.require('Blockly.Colours'); - -goog.require('Blockly.constants'); - -Blockly.Blocks['matrix'] = { +Blockly.Blocks["matrix"] = { /** * Block for matrix value. - * @this Blockly.Block */ - init: function() { + init: function (this: Blockly.Block) { this.jsonInit({ - "message0": "%1", - "args0": [ + message0: "%1", + args0: [ { - "type": "field_matrix", - "name": "MATRIX" - } + type: "field_matrix", + name: "MATRIX", + }, ], - "outputShape": Blockly.OUTPUT_SHAPE_ROUND, - "output": "Number", - "extensions": ["colours_pen"] + outputShape: Constants.OUTPUT_SHAPE_ROUND, + output: "Number", + extensions: ["colours_pen"], }); - } + }, }; diff --git a/src/blocks/motion.ts b/src/blocks/motion.ts new file mode 100644 index 0000000000..f6d1805a42 --- /dev/null +++ b/src/blocks/motion.ts @@ -0,0 +1,466 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Massachusetts Institute of Technology + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as Blockly from "blockly/core"; + +Blockly.Blocks["motion_movesteps"] = { + /** + * Block to move steps. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.MOTION_MOVESTEPS, + args0: [ + { + type: "input_value", + name: "STEPS", + }, + ], + extensions: ["colours_motion", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["motion_turnright"] = { + /** + * Block to turn right. + */ + init: function (this: Blockly.Block) { + const ws = this.workspace.options.parentWorkspace || this.workspace; + this.jsonInit({ + message0: Blockly.Msg.MOTION_TURNRIGHT, + args0: [ + { + type: "field_image", + src: ws.options.pathToMedia + "rotate-right.svg", + width: 24, + height: 24, + }, + { + type: "input_value", + name: "DEGREES", + }, + ], + extensions: ["colours_motion", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["motion_turnleft"] = { + /** + * Block to turn left. + */ + init: function (this: Blockly.Block) { + const ws = this.workspace.options.parentWorkspace || this.workspace; + this.jsonInit({ + message0: Blockly.Msg.MOTION_TURNLEFT, + args0: [ + { + type: "field_image", + src: ws.options.pathToMedia + "rotate-left.svg", + width: 24, + height: 24, + }, + { + type: "input_value", + name: "DEGREES", + }, + ], + extensions: ["colours_motion", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["motion_pointindirection"] = { + /** + * Block to point in direction. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.MOTION_POINTINDIRECTION, + args0: [ + { + type: "input_value", + name: "DIRECTION", + }, + ], + extensions: ["colours_motion", "shape_statement"], + }); + }, +}; + +/** + * Point towards drop-down menu. Populated dynamically by scratch-gui. + */ +Blockly.Blocks["motion_pointtowards_menu"] = {}; + +Blockly.Blocks["motion_pointtowards"] = { + /** + * Block to point in direction. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.MOTION_POINTTOWARDS, + args0: [ + { + type: "input_value", + name: "TOWARDS", + }, + ], + extensions: ["colours_motion", "shape_statement"], + }); + }, +}; + +/** + * Go to drop-down menu. Populated dynamically by scratch-gui. + */ +Blockly.Blocks["motion_goto_menu"] = {}; + +Blockly.Blocks["motion_gotoxy"] = { + /** + * Block to go to X, Y. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.MOTION_GOTOXY, + args0: [ + { + type: "input_value", + name: "X", + }, + { + type: "input_value", + name: "Y", + }, + ], + extensions: ["colours_motion", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["motion_goto"] = { + /** + * Block to go to a menu item. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.MOTION_GOTO, + args0: [ + { + type: "input_value", + name: "TO", + }, + ], + extensions: ["colours_motion", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["motion_glidesecstoxy"] = { + /** + * Block to glide for a specified time. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.MOTION_GLIDESECSTOXY, + args0: [ + { + type: "input_value", + name: "SECS", + }, + { + type: "input_value", + name: "X", + }, + { + type: "input_value", + name: "Y", + }, + ], + extensions: ["colours_motion", "shape_statement"], + }); + }, +}; + +/** + * Glide to drop-down menu. Populated dynamically by scratch-gui. + */ +Blockly.Blocks["motion_glideto_menu"] = {}; + +Blockly.Blocks["motion_glideto"] = { + /** + * Block to glide to a menu item + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.MOTION_GLIDETO, + args0: [ + { + type: "input_value", + name: "SECS", + }, + { + type: "input_value", + name: "TO", + }, + ], + extensions: ["colours_motion", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["motion_changexby"] = { + /** + * Block to change X. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.MOTION_CHANGEXBY, + args0: [ + { + type: "input_value", + name: "DX", + }, + ], + extensions: ["colours_motion", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["motion_setx"] = { + /** + * Block to set X. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.MOTION_SETX, + args0: [ + { + type: "input_value", + name: "X", + }, + ], + extensions: ["colours_motion", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["motion_changeyby"] = { + /** + * Block to change Y. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.MOTION_CHANGEYBY, + args0: [ + { + type: "input_value", + name: "DY", + }, + ], + extensions: ["colours_motion", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["motion_sety"] = { + /** + * Block to set Y. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.MOTION_SETY, + args0: [ + { + type: "input_value", + name: "Y", + }, + ], + extensions: ["colours_motion", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["motion_ifonedgebounce"] = { + /** + * Block to bounce on edge. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.MOTION_IFONEDGEBOUNCE, + extensions: ["colours_motion", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["motion_setrotationstyle"] = { + /** + * Block to set rotation style. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.MOTION_SETROTATIONSTYLE, + args0: [ + { + type: "field_dropdown", + name: "STYLE", + options: [ + [Blockly.Msg.MOTION_SETROTATIONSTYLE_LEFTRIGHT, "left-right"], + [Blockly.Msg.MOTION_SETROTATIONSTYLE_DONTROTATE, "don't rotate"], + [Blockly.Msg.MOTION_SETROTATIONSTYLE_ALLAROUND, "all around"], + ], + }, + ], + extensions: ["colours_motion", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["motion_xposition"] = { + /** + * Block to report X. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.MOTION_XPOSITION, + extensions: ["colours_motion", "output_number", "monitor_block"], + }); + }, +}; + +Blockly.Blocks["motion_yposition"] = { + /** + * Block to report Y. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.MOTION_YPOSITION, + extensions: ["colours_motion", "output_number", "monitor_block"], + }); + }, +}; + +Blockly.Blocks["motion_direction"] = { + /** + * Block to report direction. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.MOTION_DIRECTION, + extensions: ["colours_motion", "output_number", "monitor_block"], + }); + }, +}; + +Blockly.Blocks["motion_scroll_right"] = { + /** + * Block to scroll the stage right. Does not actually do anything. This is + * an obsolete block that is implemented for compatibility with Scratch 2.0 + * projects. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.MOTION_SCROLLRIGHT, + args0: [ + { + type: "input_value", + name: "DISTANCE", + }, + ], + extensions: ["colours_motion", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["motion_scroll_up"] = { + /** + * Block to scroll the stage up. Does not actually do anything. This is an + * obsolete block that is implemented for compatibility with Scratch 2.0 + * projects. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.MOTION_SCROLLUP, + args0: [ + { + type: "input_value", + name: "DISTANCE", + }, + ], + extensions: ["colours_motion", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["motion_align_scene"] = { + /** + * Block to change the stage's scrolling alignment. Does not actually do + * anything. This is an obsolete block that is implemented for compatibility + * with Scratch 2.0 projects. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.MOTION_ALIGNSCENE, + args0: [ + { + type: "field_dropdown", + name: "ALIGNMENT", + options: [ + [Blockly.Msg.MOTION_ALIGNSCENE_BOTTOMLEFT, "bottom-left"], + [Blockly.Msg.MOTION_ALIGNSCENE_BOTTOMRIGHT, "bottom-right"], + [Blockly.Msg.MOTION_ALIGNSCENE_MIDDLE, "middle"], + [Blockly.Msg.MOTION_ALIGNSCENE_TOPLEFT, "top-left"], + [Blockly.Msg.MOTION_ALIGNSCENE_TOPRIGHT, "top-right"], + ], + }, + ], + extensions: ["colours_motion", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["motion_xscroll"] = { + /** + * Block to report the stage's scroll position's X value. Does not actually + * do anything. This is an obsolete block that is implemented for + * compatibility with Scratch 2.0 projects. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.MOTION_XSCROLL, + extensions: ["colours_motion", "output_number"], + }); + }, +}; + +Blockly.Blocks["motion_yscroll"] = { + /** + * Block to report the stage's scroll position's Y value. Does not actually + * do anything. This is an obsolete block that is implemented for + * compatibility with Scratch 2.0 projects. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.MOTION_YSCROLL, + extensions: ["colours_motion", "output_number"], + }); + }, +}; diff --git a/blocks_common/note.js b/src/blocks/note.ts similarity index 57% rename from blocks_common/note.js rename to src/blocks/note.ts index f51ca40f14..540447a514 100644 --- a/blocks_common/note.js +++ b/src/blocks/note.ts @@ -22,37 +22,26 @@ * @fileoverview Note block. * @author ericr@media.mit.edu (Eric Rosenbaum) */ -'use strict'; +import * as Blockly from "blockly/core"; +import * as Constants from "../constants"; -goog.provide('Blockly.Blocks.note'); - -goog.require('Blockly.Blocks'); - -goog.require('Blockly.Colours'); - -goog.require('Blockly.constants'); - -Blockly.Blocks['note'] = { +Blockly.Blocks["note"] = { /** * Block for musical note value. - * @this Blockly.Block */ - init: function() { + init: function (this: Blockly.Block) { this.jsonInit({ - "message0": "%1", - "args0": [ + message0: "%1", + args0: [ { - "type": "field_note", - "name": "NOTE", - "value": 60 - } + type: "field_note", + name: "NOTE", + value: 60, + }, ], - "outputShape": Blockly.OUTPUT_SHAPE_ROUND, - "output": "Number", - "colour": Blockly.Colours.textField, - "colourSecondary": Blockly.Colours.textField, - "colourTertiary": Blockly.Colours.textField, - "colourQuaternary": Blockly.Colours.textField + outputShape: Constants.OUTPUT_SHAPE_ROUND, + output: "Number", + extensions: ["colours_textfield"], }); - } + }, }; diff --git a/src/blocks/operators.ts b/src/blocks/operators.ts new file mode 100644 index 0000000000..3852579912 --- /dev/null +++ b/src/blocks/operators.ts @@ -0,0 +1,426 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2012 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as Blockly from "blockly/core"; + +Blockly.Blocks["operator_add"] = { + /** + * Block for adding two numbers. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.OPERATORS_ADD, + args0: [ + { + type: "input_value", + name: "NUM1", + }, + { + type: "input_value", + name: "NUM2", + }, + ], + extensions: ["colours_operators", "output_number"], + }); + }, +}; + +Blockly.Blocks["operator_subtract"] = { + /** + * Block for subtracting two numbers. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.OPERATORS_SUBTRACT, + args0: [ + { + type: "input_value", + name: "NUM1", + }, + { + type: "input_value", + name: "NUM2", + }, + ], + extensions: ["colours_operators", "output_number"], + }); + }, +}; + +Blockly.Blocks["operator_multiply"] = { + /** + * Block for multiplying two numbers. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.OPERATORS_MULTIPLY, + args0: [ + { + type: "input_value", + name: "NUM1", + }, + { + type: "input_value", + name: "NUM2", + }, + ], + extensions: ["colours_operators", "output_number"], + }); + }, +}; + +Blockly.Blocks["operator_divide"] = { + /** + * Block for dividing two numbers. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.OPERATORS_DIVIDE, + args0: [ + { + type: "input_value", + name: "NUM1", + }, + { + type: "input_value", + name: "NUM2", + }, + ], + extensions: ["colours_operators", "output_number"], + }); + }, +}; + +Blockly.Blocks["operator_random"] = { + /** + * Block for picking a random number. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.OPERATORS_RANDOM, + args0: [ + { + type: "input_value", + name: "FROM", + }, + { + type: "input_value", + name: "TO", + }, + ], + extensions: ["colours_operators", "output_number"], + }); + }, +}; + +Blockly.Blocks["operator_lt"] = { + /** + * Block for less than comparator. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.OPERATORS_LT, + args0: [ + { + type: "input_value", + name: "OPERAND1", + }, + { + type: "input_value", + name: "OPERAND2", + }, + ], + extensions: ["colours_operators", "output_boolean"], + }); + }, +}; + +Blockly.Blocks["operator_equals"] = { + /** + * Block for equals comparator. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.OPERATORS_EQUALS, + args0: [ + { + type: "input_value", + name: "OPERAND1", + }, + { + type: "input_value", + name: "OPERAND2", + }, + ], + extensions: ["colours_operators", "output_boolean"], + }); + }, +}; + +Blockly.Blocks["operator_gt"] = { + /** + * Block for greater than comparator. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.OPERATORS_GT, + args0: [ + { + type: "input_value", + name: "OPERAND1", + }, + { + type: "input_value", + name: "OPERAND2", + }, + ], + extensions: ["colours_operators", "output_boolean"], + }); + }, +}; + +Blockly.Blocks["operator_and"] = { + /** + * Block for "and" boolean comparator. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.OPERATORS_AND, + args0: [ + { + type: "input_value", + name: "OPERAND1", + check: "Boolean", + }, + { + type: "input_value", + name: "OPERAND2", + check: "Boolean", + }, + ], + extensions: ["colours_operators", "output_boolean"], + }); + }, +}; + +Blockly.Blocks["operator_or"] = { + /** + * Block for "or" boolean comparator. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.OPERATORS_OR, + args0: [ + { + type: "input_value", + name: "OPERAND1", + check: "Boolean", + }, + { + type: "input_value", + name: "OPERAND2", + check: "Boolean", + }, + ], + extensions: ["colours_operators", "output_boolean"], + }); + }, +}; + +Blockly.Blocks["operator_not"] = { + /** + * Block for "not" unary boolean operator. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.OPERATORS_NOT, + args0: [ + { + type: "input_value", + name: "OPERAND", + check: "Boolean", + }, + ], + extensions: ["colours_operators", "output_boolean"], + }); + }, +}; + +Blockly.Blocks["operator_join"] = { + /** + * Block for string join operator. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.OPERATORS_JOIN, + args0: [ + { + type: "input_value", + name: "STRING1", + }, + { + type: "input_value", + name: "STRING2", + }, + ], + extensions: ["colours_operators", "output_string"], + }); + }, +}; + +Blockly.Blocks["operator_letter_of"] = { + /** + * Block for "letter _ of _" operator. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.OPERATORS_LETTEROF, + args0: [ + { + type: "input_value", + name: "LETTER", + }, + { + type: "input_value", + name: "STRING", + }, + ], + extensions: ["colours_operators", "output_string"], + }); + }, +}; + +Blockly.Blocks["operator_length"] = { + /** + * Block for string length operator. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.OPERATORS_LENGTH, + args0: [ + { + type: "input_value", + name: "STRING", + }, + ], + extensions: ["colours_operators", "output_string"], + }); + }, +}; + +Blockly.Blocks["operator_contains"] = { + /** + * Block for _ contains _ operator + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.OPERATORS_CONTAINS, + args0: [ + { + type: "input_value", + name: "STRING1", + }, + { + type: "input_value", + name: "STRING2", + }, + ], + extensions: ["colours_operators", "output_boolean"], + }); + }, +}; + +Blockly.Blocks["operator_mod"] = { + /** + * Block for mod two numbers. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.OPERATORS_MOD, + args0: [ + { + type: "input_value", + name: "NUM1", + }, + { + type: "input_value", + name: "NUM2", + }, + ], + extensions: ["colours_operators", "output_number"], + }); + }, +}; + +Blockly.Blocks["operator_round"] = { + /** + * Block for rounding a numbers. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.OPERATORS_ROUND, + args0: [ + { + type: "input_value", + name: "NUM", + }, + ], + extensions: ["colours_operators", "output_number"], + }); + }, +}; + +Blockly.Blocks["operator_mathop"] = { + /** + * Block for "advanced" math ops on a number. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.OPERATORS_MATHOP, + args0: [ + { + type: "field_dropdown", + name: "OPERATOR", + options: [ + [Blockly.Msg.OPERATORS_MATHOP_ABS, "abs"], + [Blockly.Msg.OPERATORS_MATHOP_FLOOR, "floor"], + [Blockly.Msg.OPERATORS_MATHOP_CEILING, "ceiling"], + [Blockly.Msg.OPERATORS_MATHOP_SQRT, "sqrt"], + [Blockly.Msg.OPERATORS_MATHOP_SIN, "sin"], + [Blockly.Msg.OPERATORS_MATHOP_COS, "cos"], + [Blockly.Msg.OPERATORS_MATHOP_TAN, "tan"], + [Blockly.Msg.OPERATORS_MATHOP_ASIN, "asin"], + [Blockly.Msg.OPERATORS_MATHOP_ACOS, "acos"], + [Blockly.Msg.OPERATORS_MATHOP_ATAN, "atan"], + [Blockly.Msg.OPERATORS_MATHOP_LN, "ln"], + [Blockly.Msg.OPERATORS_MATHOP_LOG, "log"], + [Blockly.Msg.OPERATORS_MATHOP_EEXP, "e ^"], + [Blockly.Msg.OPERATORS_MATHOP_10EXP, "10 ^"], + ], + }, + { + type: "input_value", + name: "NUM", + }, + ], + extensions: ["colours_operators", "output_number"], + }); + }, +}; diff --git a/src/blocks/procedures.ts b/src/blocks/procedures.ts new file mode 100644 index 0000000000..00e7acca03 --- /dev/null +++ b/src/blocks/procedures.ts @@ -0,0 +1,1158 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2012 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Procedure blocks for Scratch. + */ + +import * as Blockly from "blockly/core"; +import { FieldTextInputRemovable } from "../fields/field_textinput_removable"; +import type { ScratchDragger } from "../scratch_dragger"; + +/** + * An object mapping argument IDs to blocks and shadow DOMs. + */ +type ConnectionMap = { + [key: string]: { + shadow: Element; + block: Blockly.BlockSvg; + }; +}; + +/** + * Possible types for procedure arguments. + */ +enum ArgumentType { + STRING = "s", + NUMBER = "n", + BOOLEAN = "b", +} + +/** + * Class representing a draggable block that copies itself on drag. + */ +class DuplicateOnDragDraggable implements Blockly.IDraggable { + /** + * The newly-created duplicate block. + */ + private copy?: Blockly.BlockSvg; + constructor(private block: Blockly.BlockSvg) {} + + /** + * Returns whether or not this draggable is movable. */ + isMovable(): boolean { + return true; + } + + /** + * Handles the start of a drag. + * + * @param e The event that triggered the drag. + */ + startDrag(e: PointerEvent) { + const data = this.block.toCopyData(); + this.copy = Blockly.clipboard.paste( + data, + this.block.workspace + ) as Blockly.BlockSvg; + this.copy.startDrag(e); + } + + drag(newLoc: Blockly.utils.Coordinate, e?: PointerEvent) { + ( + this.block.workspace.getGesture(e).getCurrentDragger() as ScratchDragger + ).setDraggable(this.copy); + this.copy.drag(newLoc, e); + } + + endDrag(e: PointerEvent) { + this.copy?.endDrag(e); + } + + revertDrag() { + this.copy?.dispose(); + } + + getRelativeToSurfaceXY() { + return this.copy + ? this.copy.getRelativeToSurfaceXY() + : this.block.getRelativeToSurfaceXY(); + } +} + +// Serialization and deserialization. + +/** + * Create XML to represent the (non-editable) name and arguments of a procedure + * call block. + * + * @returns XML storage element. + */ +function callerMutationToDom(this: ProcedureCallBlock): Element { + const container = document.createElement("mutation"); + container.setAttribute("proccode", this.procCode_); + container.setAttribute("argumentids", JSON.stringify(this.argumentIds_)); + container.setAttribute("warp", JSON.stringify(this.warp_)); + return container; +} + +/** + * Parse XML to restore the (non-editable) name and arguments of a procedure + * call block. + * + * @param xmlElement XML storage element. + */ +function callerDomToMutation(this: ProcedureCallBlock, xmlElement: Element) { + this.procCode_ = xmlElement.getAttribute("proccode"); + this.generateShadows_ = JSON.parse( + xmlElement.getAttribute("generateshadows") + ); + this.argumentIds_ = JSON.parse(xmlElement.getAttribute("argumentids")); + this.warp_ = JSON.parse(xmlElement.getAttribute("warp")); + this.updateDisplay_(); +} + +/** + * Create XML to represent the (non-editable) name and arguments of a + * procedures_prototype block or a procedures_declaration block. + * + * @param opt_generateShadows Whether to include the generateshadows flag in the + * generated XML. False if not provided. + * @returns XML storage element. + */ +function definitionMutationToDom( + this: ProcedurePrototypeBlock | ProcedureDeclarationBlock, + opt_generateShadows?: boolean +): Element { + const container = document.createElement("mutation"); + + if (opt_generateShadows) { + container.setAttribute("generateshadows", "true"); + } + container.setAttribute("proccode", this.procCode_); + container.setAttribute("argumentids", JSON.stringify(this.argumentIds_)); + container.setAttribute("argumentnames", JSON.stringify(this.displayNames_)); + container.setAttribute( + "argumentdefaults", + JSON.stringify(this.argumentDefaults_) + ); + container.setAttribute("warp", JSON.stringify(this.warp_)); + return container; +} + +/** + * Parse XML to restore the (non-editable) name and arguments of a + * procedures_prototype block or a procedures_declaration block. + * + * @param xmlElement XML storage element. + */ +function definitionDomToMutation( + this: ProcedurePrototypeBlock | ProcedureDeclarationBlock, + xmlElement: Element +) { + this.procCode_ = xmlElement.getAttribute("proccode"); + this.warp_ = JSON.parse(xmlElement.getAttribute("warp")); + + const prevArgIds = this.argumentIds_; + const prevDisplayNames = this.displayNames_; + + this.argumentIds_ = JSON.parse(xmlElement.getAttribute("argumentids")); + this.displayNames_ = JSON.parse(xmlElement.getAttribute("argumentnames")); + this.argumentDefaults_ = JSON.parse( + xmlElement.getAttribute("argumentdefaults") + ); + this.updateDisplay_(); + if ("updateArgumentReporterNames_" in this) { + this.updateArgumentReporterNames_(prevArgIds, prevDisplayNames); + } +} + +// End of serialization and deserialization. + +// Shared by all three procedure blocks (procedures_declaration, +// procedures_call, and procedures_prototype). +/** + * Returns the name of the procedure this block calls, or the empty string if + * it has not yet been set. + * + * @returns Procedure name. + */ +function getProcCode(this: ProcedureBlock): string { + return this.procCode_; +} + +/** + * Update the block's structure and appearance to match the internally stored + * mutation. + */ +function updateDisplay_(this: ProcedureBlock) { + const connectionMap = this.disconnectOldBlocks_(); + this.removeAllInputs_(); + this.createAllInputs_(connectionMap); + this.deleteShadows_(connectionMap); +} + +/** + * Disconnect old blocks from all value inputs on this block, but hold onto them + * in case they can be reattached later. Also save the shadow DOM if it exists. + * The result is a map from argument ID to information that was associated with + * that argument at the beginning of the mutation. + * + * @returns An object mapping argument IDs to blocks and shadow DOMs. + */ +function disconnectOldBlocks_(this: ProcedureBlock): ConnectionMap { + // Remove old stuff + const connectionMap: ConnectionMap = {}; + for (const input of this.inputList) { + if (input.connection) { + const target = input.connection.targetBlock() as Blockly.BlockSvg; + const saveInfo = { + shadow: input.connection.getShadowDom(true), + block: target, + }; + connectionMap[input.name] = saveInfo; + + if (target) { + input.connection.disconnect(); + } + } + } + return connectionMap; +} + +/** + * Remove all inputs on the block, including dummy inputs. + * Assumes no input has shadow DOM set. + */ +function removeAllInputs_(this: ProcedureBlock) { + // Delete inputs directly instead of with block.removeInput to avoid splicing + // out of the input list at every index. + this.inputList.forEach((input: Blockly.Input) => input.dispose()); + this.inputList = []; +} + +/** + * Create all inputs specified by the new procCode, and populate them with + * shadow blocks or reconnected old blocks as appropriate. + * + * @param connectionMap An object mapping argument IDs to blocks and shadow DOMs. + */ +function createAllInputs_(this: ProcedureBlock, connectionMap: ConnectionMap) { + // Split the proc into components, by %n, %b, and %s (ignoring escaped). + const procComponents = this.procCode_ + .split(/(?=[^\\]%[nbs])/) + .map(function (c: string) { + return c.trim(); // Strip whitespace. + }); + // Create arguments and labels as appropriate. + let argumentCount = 0; + for (const component of procComponents) { + let labelText; + if (component.substring(0, 1) === "%") { + const argumentType = component.substring(1, 2); + if ( + !( + argumentType === ArgumentType.NUMBER || + argumentType === ArgumentType.BOOLEAN || + argumentType === ArgumentType.STRING + ) + ) { + throw new Error( + "Found an custom procedure with an invalid type: " + argumentType + ); + } + labelText = component.substring(2).trim(); + + const id = this.argumentIds_[argumentCount]; + + const input = this.appendValueInput(id); + if (argumentType === ArgumentType.BOOLEAN) { + input.setCheck("Boolean"); + } + this.populateArgument_( + argumentType, + argumentCount, + connectionMap, + id, + input + ); + argumentCount++; + } else { + labelText = component.trim(); + } + this.addProcedureLabel_(labelText.replace(/\\%/, "%")); + } +} + +/** + * Delete all shadow blocks in the given map. + * + * @param connectionMap An object mapping argument IDs to the blocks that were + * connected to those IDs at the beginning of the mutation. + */ +function deleteShadows_(this: ProcedureBlock, connectionMap: ConnectionMap) { + // Get rid of all of the old shadow blocks if they aren't connected. + if (connectionMap) { + for (const id in connectionMap) { + const saveInfo = connectionMap[id]; + if (saveInfo) { + const block = saveInfo["block"]; + if (block && block.isShadow()) { + block.dispose(); + connectionMap[id] = null; + // At this point we know which shadow DOMs are about to be orphaned in + // the VM. What do we do with that information? + } + } + } + } +} +// End of shared code. + +/** + * Add a label field with the given text to a procedures_call or + * procedures_prototype block. + * + * @param text The label text. + */ +function addLabelField_( + this: ProcedureCallBlock | ProcedurePrototypeBlock, + text: string +) { + this.appendDummyInput().appendField(text); +} + +/** + * Add a label editor with the given text to a procedures_declaration + * block. Editing the text in the label editor updates the text of the + * corresponding label fields on function calls. + * + * @param text The label text. + */ +function addLabelEditor_(this: ProcedureDeclarationBlock, text: string) { + if (text) { + this.appendDummyInput(Blockly.utils.idGenerator.genUid()).appendField( + new FieldTextInputRemovable(text) + ); + } +} + +/** + * Build a DOM node representing a shadow block of the given type. + * + * @param type One of 's' (string) or 'n' (number). + * @returns The DOM node representing the new shadow block. + */ +function buildShadowDom_(type: ArgumentType): Element { + const shadowDom = document.createElement("shadow"); + let shadowType, fieldName, fieldValue; + if (type === ArgumentType.NUMBER) { + shadowType = "math_number"; + fieldName = "NUM"; + fieldValue = "1"; + } else { + shadowType = "text"; + fieldName = "TEXT"; + fieldValue = ""; + } + shadowDom.setAttribute("type", shadowType); + const fieldDom = document.createElement("field"); + fieldDom.textContent = fieldValue; + fieldDom.setAttribute("name", fieldName); + shadowDom.appendChild(fieldDom); + return shadowDom; +} + +/** + * Create a new shadow block and attach it to the given input. + * + * @param input The value input to attach a block to. + * @param argumentType One of 'b' (boolean), 's' (string) or + * 'n' (number). + */ +function attachShadow_( + this: ProcedureCallBlock, + input: Blockly.Input, + argumentType: ArgumentType +) { + if ( + argumentType === ArgumentType.NUMBER || + argumentType === ArgumentType.STRING + ) { + const blockType = + argumentType === ArgumentType.NUMBER ? "math_number" : "text"; + Blockly.Events.disable(); + let newBlock; + try { + newBlock = this.workspace.newBlock(blockType); + if (argumentType === ArgumentType.NUMBER) { + newBlock.setFieldValue("1", "NUM"); + } else { + newBlock.setFieldValue("", "TEXT"); + } + newBlock.setShadow(true); + if (!this.isInsertionMarker()) { + newBlock.initSvg(); + newBlock.render(); + } + } finally { + Blockly.Events.enable(); + } + if (Blockly.Events.isEnabled()) { + Blockly.Events.fire( + new (Blockly.Events.get(Blockly.Events.BLOCK_CREATE))(newBlock) + ); + } + newBlock.outputConnection.connect(input.connection); + } +} + +/** + * Create a new argument reporter block. + * + * @param argumentType One of 'b' (boolean), 's' (string) or + * 'n' (number). + * @param displayName The name of the argument as provided by the + * user, which becomes the text of the label on the argument reporter block. + * @returns The newly created argument reporter block. + */ +function createArgumentReporter_( + this: ProcedurePrototypeBlock, + argumentType: ArgumentType, + displayName: string +): Blockly.BlockSvg { + let blockType; + if ( + argumentType === ArgumentType.NUMBER || + argumentType === ArgumentType.STRING + ) { + blockType = "argument_reporter_string_number"; + } else { + blockType = "argument_reporter_boolean"; + } + Blockly.Events.disable(); + let newBlock; + try { + newBlock = this.workspace.newBlock(blockType); + newBlock.setShadow(true); + newBlock.setFieldValue(displayName, "VALUE"); + if (!this.isInsertionMarker()) { + newBlock.initSvg(); + newBlock.render(); + } + } finally { + Blockly.Events.enable(); + } + if (Blockly.Events.isEnabled()) { + Blockly.Events.fire( + new (Blockly.Events.get(Blockly.Events.BLOCK_CREATE))(newBlock) + ); + } + return newBlock; +} + +/** + * Populate the argument by attaching the correct child block or shadow to the + * given input. + * + * @param type One of 'b' (boolean), 's' (string) or 'n' (number). + * @param index The index of this argument into the argument id array. + * @param connectionMap An object mapping argument IDs to blocks and shadow DOMs. + * @param id The ID of the input to populate. + * @param input The newly created input to populate. + */ +function populateArgumentOnCaller_( + this: ProcedureCallBlock, + type: ArgumentType, + index: number, + connectionMap: ConnectionMap, + id: string, + input: Blockly.Input +) { + let oldBlock: Blockly.BlockSvg; + let oldShadow: Element; + if (connectionMap && id in connectionMap) { + const saveInfo = connectionMap[id]; + oldBlock = saveInfo["block"]; + oldShadow = saveInfo["shadow"]; + } + + if (connectionMap && oldBlock) { + // Reattach the old block and shadow DOM. + connectionMap[input.name] = null; + oldBlock.outputConnection.connect(input.connection); + if (type !== ArgumentType.BOOLEAN && this.generateShadows_) { + const shadowDom = oldShadow || this.buildShadowDom_(type); + input.connection.setShadowDom(shadowDom); + } + } else if (this.generateShadows_) { + this.attachShadow_(input, type); + } +} + +/** + * Populate the argument by attaching the correct argument reporter to the given + * input. + * + * @param type One of 'b' (boolean), 's' (string) or 'n' (number). + * @param index The index of this argument into the argument ID and + * argument display name arrays. + * @param connectionMap An object mapping argument IDs to blocks and shadow DOMs. + * @param id The ID of the input to populate. + * @param input The newly created input to populate. + */ +function populateArgumentOnPrototype_( + this: ProcedurePrototypeBlock, + type: ArgumentType, + index: number, + connectionMap: ConnectionMap, + id: string, + input: Blockly.Input +) { + let oldBlock = null; + if (connectionMap && id in connectionMap) { + const saveInfo = connectionMap[id]; + oldBlock = saveInfo["block"]; + } + + const oldTypeMatches = checkOldTypeMatches_(oldBlock, type); + const displayName = this.displayNames_[index]; + + // Decide which block to attach. + let argumentReporter: Blockly.BlockSvg; + if (connectionMap && oldBlock && oldTypeMatches) { + // Update the text if needed. The old argument reporter is the same type, + // and on the same input, but the argument's display name may have changed. + argumentReporter = oldBlock; + argumentReporter.setFieldValue(displayName, "VALUE"); + connectionMap[input.name] = null; + } else { + argumentReporter = this.createArgumentReporter_(type, displayName); + } + + // Attach the block. + input.connection.connect(argumentReporter.outputConnection); +} + +/** + * Populate the argument by attaching the correct argument editor to the given + * input. + * + * @param type One of 'b' (boolean), 's' (string) or 'n' (number). + * @param index The index of this argument into the argument id and argument + * display name arrays. + * @param connectionMap An object mapping argument IDs to blocks and shadow DOMs. + * @param id The ID of the input to populate. + * @param input The newly created input to populate. + */ +function populateArgumentOnDeclaration_( + this: ProcedureDeclarationBlock, + type: ArgumentType, + index: number, + connectionMap: ConnectionMap, + id: string, + input: Blockly.Input +) { + let oldBlock = null; + if (connectionMap && id in connectionMap) { + const saveInfo = connectionMap[id]; + oldBlock = saveInfo["block"]; + } + + // TODO: This always returns false, because it checks for argument reporter + // blocks instead of argument editor blocks. Create a new version for argument + // editors. + const oldTypeMatches = checkOldTypeMatches_(oldBlock, type); + const displayName = this.displayNames_[index]; + + // Decide which block to attach. + let argumentEditor: Blockly.BlockSvg; + if (oldBlock && oldTypeMatches) { + argumentEditor = oldBlock; + oldBlock.setFieldValue(displayName, "TEXT"); + connectionMap[input.name] = null; + } else { + argumentEditor = this.createArgumentEditor_(type, displayName); + } + + // Attach the block. + input.connection.connect(argumentEditor.outputConnection); +} + +/** + * Check whether the type of the old block corresponds to the given argument + * type. + * + * @param oldBlock The old block to check. + * @param type The argument type. One of 'n', 'n', or 's'. + * @returns True if the type matches, false otherwise. + */ +function checkOldTypeMatches_( + oldBlock: Blockly.BlockSvg | null, + type: string +): boolean { + if (!oldBlock) { + return false; + } + if ( + (type === ArgumentType.NUMBER || type === ArgumentType.STRING) && + oldBlock.type === "argument_reporter_string_number" + ) { + return true; + } + if ( + type === ArgumentType.BOOLEAN && + oldBlock.type === "argument_reporter_boolean" + ) { + return true; + } + return false; +} + +/** + * Create an argument editor. + * An argument editor is a shadow block with a single text field, which is used + * to set the display name of the argument. + * + * @param argumentType One of 'b' (boolean), 's' (string) or 'n' (number). + * @param displayName The display name of this argument, which is the text of + * the field on the shadow block. + * @returns The newly created argument editor block. + */ +function createArgumentEditor_( + this: ProcedureDeclarationBlock, + argumentType: ArgumentType, + displayName: string +): Blockly.BlockSvg { + Blockly.Events.disable(); + let newBlock; + try { + if ( + argumentType === ArgumentType.NUMBER || + argumentType === ArgumentType.STRING + ) { + newBlock = this.workspace.newBlock("argument_editor_string_number"); + } else { + newBlock = this.workspace.newBlock("argument_editor_boolean"); + } + newBlock.setFieldValue(displayName, "TEXT"); + newBlock.setShadow(true); + if (!this.isInsertionMarker()) { + newBlock.initSvg(); + newBlock.queueRender(); + } + } finally { + Blockly.Events.enable(); + } + if (Blockly.Events.isEnabled()) { + Blockly.Events.fire( + new (Blockly.Events.get(Blockly.Events.BLOCK_CREATE))(newBlock) + ); + } + return newBlock; +} + +/** + * Update the serializable information on the block based on the existing inputs + * and their text. + */ +function updateDeclarationProcCode_(this: ProcedureDeclarationBlock) { + this.procCode_ = ""; + this.displayNames_ = []; + this.argumentIds_ = []; + for (let i = 0; i < this.inputList.length; i++) { + if (i !== 0) { + this.procCode_ += " "; + } + const input = this.inputList[i]; + if (input.type === Blockly.inputs.inputTypes.DUMMY) { + this.procCode_ += input.fieldRow[0].getValue(); + } else if (input.type === Blockly.inputs.inputTypes.VALUE) { + // Inspect the argument editor. + const target = input.connection.targetBlock(); + this.displayNames_.push(target.getFieldValue("TEXT")); + this.argumentIds_.push(input.name); + if (target.type === "argument_editor_boolean") { + this.procCode_ += "%b"; + } else { + this.procCode_ += "%s"; + } + } else { + throw new Error( + "Unexpected input type on a procedure mutator root: " + input.type + ); + } + } +} + +/** + * Focus on the last argument editor or label editor on the block. + */ +function focusLastEditor_(this: ProcedureDeclarationBlock) { + if (this.inputList.length > 0) { + const newInput = this.inputList[this.inputList.length - 1]; + if (newInput.type === Blockly.inputs.inputTypes.DUMMY) { + newInput.fieldRow[0].showEditor(); + } else if (newInput.type === Blockly.inputs.inputTypes.VALUE) { + // Inspect the argument editor. + const target = newInput.connection.targetBlock(); + target.getField("TEXT").showEditor(); + } + } +} + +/** + * Externally-visible function to add a label to the procedure declaration. + */ +function addLabelExternal(this: ProcedureDeclarationBlock) { + Blockly.WidgetDiv.hide(); + this.procCode_ = this.procCode_ + " label text"; + this.updateDisplay_(); + this.focusLastEditor_(); +} + +/** + * Externally-visible function to add a boolean argument to the procedure + * declaration. + */ +function addBooleanExternal(this: ProcedureDeclarationBlock) { + Blockly.WidgetDiv.hide(); + this.procCode_ = this.procCode_ + " %b"; + this.displayNames_.push("boolean"); + this.argumentIds_.push(Blockly.utils.idGenerator.genUid()); + this.argumentDefaults_.push("false"); + this.updateDisplay_(); + this.focusLastEditor_(); +} + +/** + * Externally-visible function to add a string/number argument to the procedure + * declaration. + */ +function addStringNumberExternal(this: ProcedureDeclarationBlock) { + Blockly.WidgetDiv.hide(); + this.procCode_ = this.procCode_ + " %s"; + this.displayNames_.push("number or text"); + this.argumentIds_.push(Blockly.utils.idGenerator.genUid()); + this.argumentDefaults_.push(""); + this.updateDisplay_(); + this.focusLastEditor_(); +} + +/** + * Externally-visible function to get the warp on procedure declaration. + * + * @returns The value of the warp_ property. + */ +function getWarp(this: ProcedureDeclarationBlock): boolean { + return this.warp_; +} + +/** + * Externally-visible function to set the warp on procedure declaration. + * + * @param warp The value of the warp_ property. + */ +function setWarp(this: ProcedureDeclarationBlock, warp: boolean) { + this.warp_ = warp; +} + +/** + * Callback to remove a field, only for the declaration block. + * + * @param field The field being removed. + */ +function removeFieldCallback( + this: ProcedureDeclarationBlock, + field: Blockly.Field +) { + // Do not delete if there is only one input + if (this.inputList.length === 1) { + return; + } + var inputNameToRemove = null; + for (var n = 0; n < this.inputList.length; n++) { + var input = this.inputList[n]; + if (input.connection) { + var target = input.connection.targetBlock(); + if (field.name && target.getField(field.name) === field) { + inputNameToRemove = input.name; + } + } else { + for (var j = 0; j < input.fieldRow.length; j++) { + if (input.fieldRow[j] === field) { + inputNameToRemove = input.name; + } + } + } + } + if (inputNameToRemove) { + Blockly.WidgetDiv.hide(); + this.removeInput(inputNameToRemove); + this.onChangeFn(); + this.updateDisplay_(); + } +} + +/** + * Callback to pass removeField up to the declaration block from arguments. + * + * @param field The field being removed. + */ +function removeArgumentCallback_( + this: ProcedureDeclarationBlock | ProcedureArgumentEditorBlock, + field: Blockly.Field +) { + const parent = this.getParent(); + if (parent && parent.removeFieldCallback) { + parent.removeFieldCallback(field); + } +} + +/** + * Update argument reporter field values after an edit to the prototype mutation + * using previous argument ids and names. + * Because the argument reporters only store names and not which argument ids they + * are linked to, it would not be safe to update all argument reporters on the workspace + * since they may be argument reporters with the same name from a different procedure. + * Until there is a more explicit way of identifying argument reporter blocks using ids, + * be conservative and only update argument reporters that are used in the + * stack below the prototype, ie the definition. + * + * @param prevArgIds The previous ordering of argument ids. + * @param prevDisplayNames The previous argument names. + */ +function updateArgumentReporterNames_( + this: ProcedurePrototypeBlock, + prevArgIds: string[], + prevDisplayNames: string[] +) { + const nameChanges: { newName: string; blocks: Blockly.BlockSvg[] }[] = []; + const argReporters: Blockly.BlockSvg[] = []; + const definitionBlock = this.getParent(); + if (!definitionBlock) return; + + // Create a list of argument reporters that are descendants of the definition stack (see above comment) + definitionBlock.getDescendants(false).forEach((block: Blockly.BlockSvg) => { + if ( + (block.type === "argument_reporter_string_number" || + block.type === "argument_reporter_boolean") && + !block.isShadow() + ) { + // Exclude arg reporters in the prototype block, which are shadows. + argReporters.push(block); + } + }); + + // Create a list of "name changes", including the new name and blocks matching the old name + // Only search over the current set of argument ids, ignore args that have been removed + for (let i = 0, id; (id = this.argumentIds_[i]); i++) { + // Find the previous index of this argument id. Could be -1 if it is newly added. + const prevIndex = prevArgIds.indexOf(id); + if (prevIndex === -1) continue; // Newly added argument, no corresponding previous argument to update. + const prevName = prevDisplayNames[prevIndex]; + if (prevName !== this.displayNames_[i]) { + nameChanges.push({ + newName: this.displayNames_[i], + blocks: argReporters.filter((block) => { + return block.getFieldValue("VALUE") === prevName; + }), + }); + } + } + + // Finally update the blocks for each name change. + // Do this after creating the lists to avoid cycles of renaming. + for (const nameChange of nameChanges) { + for (const block of nameChange.blocks) { + block.setFieldValue(nameChange.newName, "VALUE"); + } + } +} + +Blockly.Blocks["procedures_definition"] = { + /** + * Block for defining a procedure with no return value. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.PROCEDURES_DEFINITION, + args0: [ + { + type: "input_statement", + name: "custom_block", + }, + ], + extensions: [ + "colours_more", + "shape_bowler_hat", + "procedure_def_contextmenu", + ], + }); + }, +}; + +Blockly.Blocks["procedures_call"] = { + /** + * Block for calling a procedure with no return value. + */ + init: function (this: ProcedureCallBlock) { + this.jsonInit({ + extensions: [ + "colours_more", + "shape_statement", + "procedure_call_contextmenu", + ], + }); + this.procCode_ = ""; + this.argumentIds_ = []; + this.warp_ = false; + + // Shared. + this.getProcCode = getProcCode.bind(this); + this.removeAllInputs_ = removeAllInputs_.bind(this); + this.disconnectOldBlocks_ = disconnectOldBlocks_.bind(this); + this.deleteShadows_ = deleteShadows_.bind(this); + this.createAllInputs_ = createAllInputs_.bind(this); + this.updateDisplay_ = updateDisplay_.bind(this); + + // Exist on all three blocks, but have different implementations. + this.mutationToDom = callerMutationToDom.bind(this); + this.domToMutation = callerDomToMutation.bind(this); + this.populateArgument_ = populateArgumentOnCaller_.bind(this); + this.addProcedureLabel_ = addLabelField_.bind(this); + + // Only exists on the external caller. + this.attachShadow_ = attachShadow_.bind(this); + this.buildShadowDom_ = buildShadowDom_.bind(this); + }, +}; + +Blockly.Blocks["procedures_prototype"] = { + /** + * Block for calling a procedure with no return value, for rendering inside + * define block. + */ + init: function (this: ProcedurePrototypeBlock) { + this.jsonInit({ + extensions: ["colours_more", "shape_statement"], + }); + + /* Data known about the procedure. */ + this.procCode_ = ""; + this.displayNames_ = []; + this.argumentIds_ = []; + this.argumentDefaults_ = []; + this.warp_ = false; + + // Shared. + this.getProcCode = getProcCode.bind(this); + this.removeAllInputs_ = removeAllInputs_.bind(this); + this.disconnectOldBlocks_ = disconnectOldBlocks_.bind(this); + this.deleteShadows_ = deleteShadows_.bind(this); + this.createAllInputs_ = createAllInputs_.bind(this); + this.updateDisplay_ = updateDisplay_.bind(this); + // Exist on all three blocks, but have different implementations. + this.mutationToDom = definitionMutationToDom.bind(this); + this.domToMutation = definitionDomToMutation.bind(this); + this.populateArgument_ = populateArgumentOnPrototype_.bind(this); + this.addProcedureLabel_ = addLabelField_.bind(this); + + // Only exists on procedures_prototype. + this.createArgumentReporter_ = createArgumentReporter_.bind(this); + this.updateArgumentReporterNames_ = updateArgumentReporterNames_.bind(this); + }, +}; + +Blockly.Blocks["procedures_declaration"] = { + /** + * The root block in the procedure declaration editor. + */ + init: function (this: ProcedureDeclarationBlock) { + this.jsonInit({ + extensions: ["colours_more", "shape_statement"], + }); + /* Data known about the procedure. */ + this.procCode_ = ""; + this.displayNames_ = []; + this.argumentIds_ = []; + this.argumentDefaults_ = []; + this.warp_ = false; + + // Shared. + this.getProcCode = getProcCode.bind(this); + this.removeAllInputs_ = removeAllInputs_.bind(this); + this.disconnectOldBlocks_ = disconnectOldBlocks_.bind(this); + this.deleteShadows_ = deleteShadows_.bind(this); + this.createAllInputs_ = createAllInputs_.bind(this); + this.updateDisplay_ = updateDisplay_.bind(this); + + // Exist on all three blocks, but have different implementations. + this.mutationToDom = definitionMutationToDom.bind(this); + this.domToMutation = definitionDomToMutation.bind(this); + this.populateArgument_ = populateArgumentOnDeclaration_.bind(this); + this.addProcedureLabel_ = addLabelEditor_.bind(this); + + // Exist on declaration and arguments editors, with different implementations. + this.removeFieldCallback = removeFieldCallback.bind(this); + + // Only exist on procedures_declaration. + this.createArgumentEditor_ = createArgumentEditor_.bind(this); + this.focusLastEditor_ = focusLastEditor_.bind(this); + this.getWarp = getWarp.bind(this); + this.setWarp = setWarp.bind(this); + this.addLabelExternal = addLabelExternal.bind(this); + this.addBooleanExternal = addBooleanExternal.bind(this); + this.addStringNumberExternal = addStringNumberExternal.bind(this); + this.onChangeFn = updateDeclarationProcCode_.bind(this); + }, +}; + +Blockly.Blocks["argument_reporter_boolean"] = { + init: function (this: Blockly.BlockSvg) { + this.jsonInit({ + message0: " %1", + args0: [ + { + type: "field_label_serializable", + name: "VALUE", + text: "", + }, + ], + extensions: ["colours_more", "output_boolean"], + }); + this.setDragStrategy(new DuplicateOnDragDraggable(this)); + }, +}; + +Blockly.Blocks["argument_reporter_string_number"] = { + init: function (this: Blockly.BlockSvg) { + this.jsonInit({ + message0: " %1", + args0: [ + { + type: "field_label_serializable", + name: "VALUE", + text: "", + }, + ], + extensions: ["colours_more", "output_number", "output_string"], + }); + this.setDragStrategy(new DuplicateOnDragDraggable(this)); + }, +}; + +Blockly.Blocks["argument_editor_boolean"] = { + init: function (this: ProcedureArgumentEditorBlock) { + this.jsonInit({ + message0: " %1", + args0: [ + { + type: "field_input_removable", + name: "TEXT", + text: "foo", + }, + ], + extensions: ["colours_textfield", "output_boolean"], + }); + + // Exist on declaration and arguments editors, with different implementations. + this.removeFieldCallback = removeArgumentCallback_.bind(this); + }, +}; + +Blockly.Blocks["argument_editor_string_number"] = { + init: function (this: ProcedureArgumentEditorBlock) { + this.jsonInit({ + message0: " %1", + args0: [ + { + type: "field_input_removable", + name: "TEXT", + text: "foo", + }, + ], + extensions: ["colours_textfield", "output_number", "output_string"], + }); + + // Exist on declaration and arguments editors, with different implementations. + this.removeFieldCallback = removeArgumentCallback_.bind(this); + }, +}; + +interface ProcedureBlock extends Blockly.BlockSvg { + procCode_: string; + argumentIds_: string[]; + warp_: boolean; + getProcCode: () => string; + removeAllInputs_: () => void; + disconnectOldBlocks_: () => ConnectionMap; + deleteShadows_: (connectionMap: ConnectionMap) => void; + createAllInputs_: (connectionMap: ConnectionMap) => void; + updateDisplay_: () => void; + populateArgument_: ( + type: ArgumentType, + index: number, + connectionMap: ConnectionMap, + id: string, + input: Blockly.Input + ) => void; + addProcedureLabel_: (text: string) => void; +} + +export interface ProcedureDeclarationBlock extends ProcedureBlock { + displayNames_: string[]; + argumentDefaults_: string[]; + removeFieldCallback: (field: Blockly.Field) => void; + createArgumentEditor_: ( + argumentType: ArgumentType, + displayName: string + ) => Blockly.BlockSvg; + focusLastEditor_: () => void; + getWarp: () => boolean; + setWarp: (warp: boolean) => void; + addLabelExternal: () => void; + addBooleanExternal: () => void; + addStringNumberExternal: () => void; + onChangeFn: () => void; +} + +interface ProcedureCallBlock extends ProcedureBlock { + generateShadows_: boolean; + attachShadow_: (input: Blockly.Input, argumentType: ArgumentType) => void; + buildShadowDom_: (type: ArgumentType) => Element; +} + +interface ProcedurePrototypeBlock extends ProcedureBlock { + displayNames_: string[]; + argumentDefaults_: string[]; + createArgumentReporter_: ( + argumentType: ArgumentType, + displayName: string + ) => Blockly.BlockSvg; + updateArgumentReporterNames_: ( + prevArgIds: string[], + prevDisplayNames: string[] + ) => void; +} + +interface ProcedureArgumentEditorBlock extends Blockly.BlockSvg { + removeFieldCallback: (field: Blockly.Field) => void; +} diff --git a/src/blocks/sensing.ts b/src/blocks/sensing.ts new file mode 100644 index 0000000000..bf8aa2b842 --- /dev/null +++ b/src/blocks/sensing.ts @@ -0,0 +1,401 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Massachusetts Institute of Technology + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as Blockly from "blockly/core"; +import * as Constants from "../constants"; + +Blockly.Blocks["sensing_touchingobject"] = { + /** + * Block to Report if its touching a Object. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SENSING_TOUCHINGOBJECT, + args0: [ + { + type: "input_value", + name: "TOUCHINGOBJECTMENU", + }, + ], + extensions: ["colours_sensing", "output_boolean"], + }); + }, +}; + +/** + * "Touching [Object]" Block Menu. Populated dynamically by scratch-gui. + */ +Blockly.Blocks["sensing_touchingobjectmenu"] = {}; + +Blockly.Blocks["sensing_touchingcolor"] = { + /** + * Block to Report if its touching a certain Color. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SENSING_TOUCHINGCOLOR, + args0: [ + { + type: "input_value", + name: "COLOR", + }, + ], + extensions: ["colours_sensing", "output_boolean"], + }); + }, +}; + +Blockly.Blocks["sensing_coloristouchingcolor"] = { + /** + * Block to Report if a color is touching a certain Color. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SENSING_COLORISTOUCHINGCOLOR, + args0: [ + { + type: "input_value", + name: "COLOR", + }, + { + type: "input_value", + name: "COLOR2", + }, + ], + extensions: ["colours_sensing", "output_boolean"], + }); + }, +}; + +Blockly.Blocks["sensing_distanceto"] = { + /** + * Block to Report distance to another Object. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SENSING_DISTANCETO, + args0: [ + { + type: "input_value", + name: "DISTANCETOMENU", + }, + ], + extensions: ["colours_sensing", "output_number"], + }); + }, +}; + +/** + * "Distance to [Object]" Block Menu. Populated dynamically by scratch-gui. + */ +Blockly.Blocks["sensing_distancetomenu"] = {}; + +Blockly.Blocks["sensing_askandwait"] = { + /** + * Block to ask a question and wait + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SENSING_ASKANDWAIT, + args0: [ + { + type: "input_value", + name: "QUESTION", + }, + ], + extensions: ["colours_sensing", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["sensing_answer"] = { + /** + * Block to report answer + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SENSING_ANSWER, + extensions: ["colours_sensing", "output_number", "monitor_block"], + }); + }, +}; + +Blockly.Blocks["sensing_keypressed"] = { + /** + * Block to Report if a key is pressed. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SENSING_KEYPRESSED, + args0: [ + { + type: "input_value", + name: "KEY_OPTION", + }, + ], + extensions: ["colours_sensing", "output_boolean"], + }); + }, +}; + +Blockly.Blocks["sensing_keyoptions"] = { + /** + * Options for Keys + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: "%1", + args0: [ + { + type: "field_dropdown", + name: "KEY_OPTION", + options: [ + [Blockly.Msg.EVENT_WHENKEYPRESSED_SPACE, "space"], + [Blockly.Msg.EVENT_WHENKEYPRESSED_UP, "up arrow"], + [Blockly.Msg.EVENT_WHENKEYPRESSED_DOWN, "down arrow"], + [Blockly.Msg.EVENT_WHENKEYPRESSED_RIGHT, "right arrow"], + [Blockly.Msg.EVENT_WHENKEYPRESSED_LEFT, "left arrow"], + [Blockly.Msg.EVENT_WHENKEYPRESSED_ANY, "any"], + ["a", "a"], + ["b", "b"], + ["c", "c"], + ["d", "d"], + ["e", "e"], + ["f", "f"], + ["g", "g"], + ["h", "h"], + ["i", "i"], + ["j", "j"], + ["k", "k"], + ["l", "l"], + ["m", "m"], + ["n", "n"], + ["o", "o"], + ["p", "p"], + ["q", "q"], + ["r", "r"], + ["s", "s"], + ["t", "t"], + ["u", "u"], + ["v", "v"], + ["w", "w"], + ["x", "x"], + ["y", "y"], + ["z", "z"], + ["0", "0"], + ["1", "1"], + ["2", "2"], + ["3", "3"], + ["4", "4"], + ["5", "5"], + ["6", "6"], + ["7", "7"], + ["8", "8"], + ["9", "9"], + ], + }, + ], + extensions: ["colours_sensing", "output_string"], + }); + }, +}; + +Blockly.Blocks["sensing_mousedown"] = { + /** + * Block to Report if the mouse is down. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SENSING_MOUSEDOWN, + extensions: ["colours_sensing", "output_boolean"], + }); + }, +}; + +Blockly.Blocks["sensing_mousex"] = { + /** + * Block to report mouse's x position + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SENSING_MOUSEX, + extensions: ["colours_sensing", "output_number"], + }); + }, +}; + +Blockly.Blocks["sensing_mousey"] = { + /** + * Block to report mouse's y position + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SENSING_MOUSEY, + extensions: ["colours_sensing", "output_number"], + }); + }, +}; + +Blockly.Blocks["sensing_setdragmode"] = { + /** + * Block to set drag mode. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SENSING_SETDRAGMODE, + args0: [ + { + type: "field_dropdown", + name: "DRAG_MODE", + options: [ + [Blockly.Msg.SENSING_SETDRAGMODE_DRAGGABLE, "draggable"], + [Blockly.Msg.SENSING_SETDRAGMODE_NOTDRAGGABLE, "not draggable"], + ], + }, + ], + extensions: ["colours_sensing", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["sensing_loudness"] = { + /** + * Block to report loudness + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SENSING_LOUDNESS, + extensions: ["colours_sensing", "output_number", "monitor_block"], + }); + }, +}; + +Blockly.Blocks["sensing_loud"] = { + /** + * Block to report if the loudness is "loud" (greater than 10). This is an + * obsolete block that is implemented for compatibility with Scratch 2.0 and + * 1.4 projects. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SENSING_LOUD, + extensions: ["colours_sensing", "output_boolean"], + }); + }, +}; + +Blockly.Blocks["sensing_timer"] = { + /** + * Block to report timer + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SENSING_TIMER, + extensions: ["colours_sensing", "output_number", "monitor_block"], + }); + }, +}; + +Blockly.Blocks["sensing_resettimer"] = { + /** + * Block to reset timer + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SENSING_RESETTIMER, + extensions: ["colours_sensing", "shape_statement"], + }); + }, +}; + +/** + * "* of _" object menu. Populated dynamically by scratch-gui. + */ +Blockly.Blocks["sensing_of_object_menu"] = {}; + +/** + * Block to report properties of sprites. Populated dynamically by scratch-gui. + */ +Blockly.Blocks["sensing_of"] = {}; + +Blockly.Blocks["sensing_current"] = { + /** + * Block to Report the current option. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SENSING_CURRENT, + args0: [ + { + type: "field_dropdown", + name: "CURRENTMENU", + options: [ + [Blockly.Msg.SENSING_CURRENT_YEAR, "YEAR"], + [Blockly.Msg.SENSING_CURRENT_MONTH, "MONTH"], + [Blockly.Msg.SENSING_CURRENT_DATE, "DATE"], + [Blockly.Msg.SENSING_CURRENT_DAYOFWEEK, "DAYOFWEEK"], + [Blockly.Msg.SENSING_CURRENT_HOUR, "HOUR"], + [Blockly.Msg.SENSING_CURRENT_MINUTE, "MINUTE"], + [Blockly.Msg.SENSING_CURRENT_SECOND, "SECOND"], + ], + }, + ], + extensions: ["colours_sensing", "output_number", "monitor_block"], + }); + }, +}; + +Blockly.Blocks["sensing_dayssince2000"] = { + /** + * Block to report days since 2000 + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SENSING_DAYSSINCE2000, + extensions: ["colours_sensing", "output_number"], + }); + }, +}; + +Blockly.Blocks["sensing_username"] = { + /** + * Block to report user's username + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SENSING_USERNAME, + extensions: ["colours_sensing", "output_number", "monitor_block"], + }); + }, +}; + +Blockly.Blocks["sensing_userid"] = { + /** + * Block to report user's ID. Does not actually do anything. This is an + * obsolete block that is implemented for compatibility with Scratch 2.0 + * projects. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SENSING_USERID, + extensions: ["colours_sensing", "output_number"], + }); + }, +}; diff --git a/src/blocks/sound.ts b/src/blocks/sound.ts new file mode 100644 index 0000000000..eea82bff59 --- /dev/null +++ b/src/blocks/sound.ts @@ -0,0 +1,186 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Massachusetts Institute of Technology + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as Blockly from "blockly/core"; + +/** + * Sound effects drop-down menu. Populated dynamically by scratch-gui. + */ +Blockly.Blocks["sound_sounds_menu"] = {}; + +Blockly.Blocks["sound_play"] = { + /** + * Block to play sound. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SOUND_PLAY, + args0: [ + { + type: "input_value", + name: "SOUND_MENU", + }, + ], + extensions: ["colours_sounds", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["sound_playuntildone"] = { + /** + * Block to play sound until done. + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SOUND_PLAYUNTILDONE, + args0: [ + { + type: "input_value", + name: "SOUND_MENU", + }, + ], + extensions: ["colours_sounds", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["sound_stopallsounds"] = { + /** + * Block to stop all sounds + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SOUND_STOPALLSOUNDS, + extensions: ["colours_sounds", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["sound_seteffectto"] = { + /** + * Block to set the audio effect + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SOUND_SETEFFECTO, + args0: [ + { + type: "field_dropdown", + name: "EFFECT", + options: [ + [Blockly.Msg.SOUND_EFFECTS_PITCH, "PITCH"], + [Blockly.Msg.SOUND_EFFECTS_PAN, "PAN"], + ], + }, + { + type: "input_value", + name: "VALUE", + }, + ], + extensions: ["colours_sounds", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["sound_changeeffectby"] = { + /** + * Block to change the audio effect + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SOUND_CHANGEEFFECTBY, + args0: [ + { + type: "field_dropdown", + name: "EFFECT", + options: [ + [Blockly.Msg.SOUND_EFFECTS_PITCH, "PITCH"], + [Blockly.Msg.SOUND_EFFECTS_PAN, "PAN"], + ], + }, + { + type: "input_value", + name: "VALUE", + }, + ], + extensions: ["colours_sounds", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["sound_cleareffects"] = { + /** + * Block to clear audio effects + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SOUND_CLEAREFFECTS, + extensions: ["colours_sounds", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["sound_changevolumeby"] = { + /** + * Block to change the sprite's volume by a certain value + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SOUND_CHANGEVOLUMEBY, + args0: [ + { + type: "input_value", + name: "VOLUME", + }, + ], + extensions: ["colours_sounds", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["sound_setvolumeto"] = { + /** + * Block to set the sprite's volume to a certain percent + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SOUND_SETVOLUMETO, + args0: [ + { + type: "input_value", + name: "VOLUME", + }, + ], + extensions: ["colours_sounds", "shape_statement"], + }); + }, +}; + +Blockly.Blocks["sound_volume"] = { + /** + * Block to report volume + */ + init: function (this: Blockly.Block) { + this.jsonInit({ + message0: Blockly.Msg.SOUND_VOLUME, + extensions: ["colours_sounds", "output_number", "monitor_block"], + }); + }, +}; diff --git a/blocks_common/text.js b/src/blocks/text.ts similarity index 58% rename from blocks_common/text.js rename to src/blocks/text.ts index 484475d642..4e9335bb4c 100644 --- a/blocks_common/text.js +++ b/src/blocks/text.ts @@ -22,36 +22,23 @@ * @fileoverview Text blocks for Blockly. * @author fraser@google.com (Neil Fraser) */ -'use strict'; +import * as Blockly from "blockly/core"; -goog.provide('Blockly.Blocks.texts'); - -goog.require('Blockly.Blocks'); - -goog.require('Blockly.Colours'); - -goog.require('Blockly.constants'); - -Blockly.Blocks['text'] = { +Blockly.Blocks["text"] = { /** * Block for text value. - * @this Blockly.Block */ - init: function() { + init: function (this: Blockly.Block) { this.jsonInit({ - "message0": "%1", - "args0": [ + message0: "%1", + args0: [ { - "type": "field_input", - "name": "TEXT" - } + type: "field_input", + name: "TEXT", + }, ], - "output": "String", - "outputShape": Blockly.OUTPUT_SHAPE_ROUND, - "colour": Blockly.Colours.textField, - "colourSecondary": Blockly.Colours.textField, - "colourTertiary": Blockly.Colours.textField, - "colourQuaternary": Blockly.Colours.textField + output: "String", + extensions: ["colours_textfield"], }); - } + }, }; diff --git a/src/blocks/vertical_extensions.ts b/src/blocks/vertical_extensions.ts new file mode 100644 index 0000000000..cfda376d16 --- /dev/null +++ b/src/blocks/vertical_extensions.ts @@ -0,0 +1,272 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2017 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Extensions for vertical blocks in scratch-blocks. + * The following extensions can be used to describe a block in Scratch terms. + * For instance, a block in the operators colour scheme with a number output + * would have the "colours_operators" and "output_number" extensions. + * @author fenichel@google.com (Rachel Fenichel) + */ +import * as Blockly from "blockly/core"; +import { ScratchProcedures } from "../procedures"; +import * as Constants from "../constants"; +import { FlyoutCheckboxIcon } from "../flyout_checkbox_icon"; + +/** + * Helper function that generates an extension based on a category name. + * The generated function will set the block's style based on the category name. + * + * @param category The name of the category to set colours for. + * @return An extension function that sets colours based on the given category. + */ +const colourHelper = function (category: string): () => void { + /** + * Set the block style on this block for the given category. + */ + return function (this: Blockly.Block) { + this.setStyle(category); + }; +}; + +/** + * Extension to set the colours of a text field, which are all the same. + */ +const COLOUR_TEXTFIELD = function (this: Blockly.Block) { + colourHelper("textField").apply(this); +}; + +/** + * Extension to make a block fit into a stack of statements, regardless of its + * inputs. That means the block should have a previous connection and a next + * connection and have inline inputs. + */ +const SHAPE_STATEMENT = function (this: Blockly.Block) { + this.setInputsInline(true); + this.setPreviousStatement(true, null); + this.setNextStatement(true, null); +}; + +/** + * Extension to make a block be shaped as a hat block, regardless of its + * inputs. That means the block should have a next connection and have inline + * inputs, but have no previous connection. + */ +const SHAPE_HAT = function (this: Blockly.Block) { + this.setInputsInline(true); + this.setNextStatement(true, null); + this.hat = "cap"; +}; + +/** + * Extension to make a block be shaped as a bowler hat block, with rounded + * corners on both sides and no indentation for statement blocks. + */ +const SHAPE_BOWLER_HAT = function (this: Blockly.Block) { + this.setInputsInline(true); + this.setNextStatement(true, null); + this.hat = "bowler"; +}; + +/** + * Extension to make a block be shaped as an end block, regardless of its + * inputs. That means the block should have a previous connection and have + * inline inputs, but have no next connection. + */ +const SHAPE_END = function (this: Blockly.Block) { + this.setInputsInline(true); + this.setPreviousStatement(true, null); +}; + +/** + * Extension to make represent a number reporter in Scratch-Blocks. + * That means the block has inline inputs, a round output shape, and a 'Number' + * output type. + */ +const OUTPUT_NUMBER = function (this: Blockly.Block) { + this.setInputsInline(true); + this.setOutputShape(Constants.OUTPUT_SHAPE_ROUND); + this.setOutput(true, "Number"); +}; + +/** + * Extension to make represent a string reporter in Scratch-Blocks. + * That means the block has inline inputs, a round output shape, and a 'String' + * output type. + */ +const OUTPUT_STRING = function (this: Blockly.Block) { + this.setInputsInline(true); + this.setOutputShape(Constants.OUTPUT_SHAPE_ROUND); + this.setOutput(true, "String"); +}; + +/** + * Extension to make represent a boolean reporter in Scratch-Blocks. + * That means the block has inline inputs, a round output shape, and a 'Boolean' + * output type. + */ +const OUTPUT_BOOLEAN = function (this: Blockly.Block) { + this.setInputsInline(true); + this.setOutputShape(Constants.OUTPUT_SHAPE_HEXAGONAL); + this.setOutput(true, "Boolean"); +}; + +/** + * Extension to make a block a monitor block, capable of reporting its current + * value in a dropdown. These blocks also have an accompanying checkbox in the + * flyout to toggle display of their current value in a chip on the stage. + */ +const MONITOR_BLOCK = function (this: Blockly.BlockSvg) { + this.addIcon(new FlyoutCheckboxIcon(this)); + (this as any).checkboxInFlyout = true; +}; + +/** + * Mixin to add a context menu for a procedure definition block. + * It adds the "edit" option and removes the "duplicate" option. + */ +const PROCEDURE_DEF_CONTEXTMENU = function (this: Blockly.Block) { + /** + * Add the "edit" option and removes the "duplicate" option from the context + * menu. + * + * @param menuOptions List of menu options to edit. + */ + this.mixin( + { + customContextMenu: function ( + menuOptions: Array< + | Blockly.ContextMenuRegistry.ContextMenuOption + | Blockly.ContextMenuRegistry.LegacyContextMenuOption + > + ) { + // Add the edit option at the end. + menuOptions.push(ScratchProcedures.makeEditOption(this)); + + // Find and remove the duplicate option + for (let i = 0, option; (option = menuOptions[i]); i++) { + if (option.text == Blockly.Msg.DUPLICATE) { + menuOptions.splice(i, 1); + break; + } + } + }, + checkAndDelete: function () { + const input = this.getInput("custom_block"); + // this is the root block, not the shadow block. + if (input && input.connection && input.connection.targetBlock()) { + const procCode = input.connection.targetBlock().getProcCode(); + const didDelete = ScratchProcedures.deleteProcedureDefCallback( + procCode, + this + ); + if (!didDelete) { + alert(Blockly.Msg.PROCEDURE_USED); + } + } + }, + }, + true + ); +}; + +/** + * Mixin to add a context menu for a procedure call block. + * It adds the "edit" option and the "define" option. + * @mixin + * @augments Blockly.Block + * @package + */ +const PROCEDURE_CALL_CONTEXTMENU = { + /** + * Add the "edit" option to the context menu. + * + * @todo Add "go to definition" option once implemented. + * @param menuOptions List of menu options to edit. + */ + customContextMenu: function ( + this: Blockly.BlockSvg, + menuOptions: Array< + | Blockly.ContextMenuRegistry.ContextMenuOption + | Blockly.ContextMenuRegistry.LegacyContextMenuOption + > + ) { + menuOptions.push(ScratchProcedures.makeEditOption(this)); + }, +}; + +const SCRATCH_EXTENSION = function (this: Blockly.Block) { + (this as any).isScratchExtension = true; +}; + +/** + * Register all extensions for scratch-blocks. + * @package + */ +function registerAll() { + const categoryNames = [ + "control", + "data", + "data_lists", + "sounds", + "motion", + "looks", + "event", + "sensing", + "pen", + "operators", + "more", + ]; + // Register functions for all category colours. + for (const name of categoryNames) { + Blockly.Extensions.register("colours_" + name, colourHelper(name)); + } + + // Text fields transcend categories. + Blockly.Extensions.register("colours_textfield", COLOUR_TEXTFIELD); + + // Register extensions for common block shapes. + Blockly.Extensions.register("shape_statement", SHAPE_STATEMENT); + Blockly.Extensions.register("shape_hat", SHAPE_HAT); + Blockly.Extensions.register("shape_bowler_hat", SHAPE_BOWLER_HAT); + Blockly.Extensions.register("shape_end", SHAPE_END); + + // Output shapes and types are related. + Blockly.Extensions.register("output_number", OUTPUT_NUMBER); + Blockly.Extensions.register("output_string", OUTPUT_STRING); + Blockly.Extensions.register("output_boolean", OUTPUT_BOOLEAN); + + // Custom procedures have interesting context menus. + Blockly.Extensions.register( + "procedure_def_contextmenu", + PROCEDURE_DEF_CONTEXTMENU + ); + Blockly.Extensions.registerMixin( + "procedure_call_contextmenu", + PROCEDURE_CALL_CONTEXTMENU + ); + + // Extension blocks have slightly different block rendering. + Blockly.Extensions.register("scratch_extension", SCRATCH_EXTENSION); + + Blockly.Extensions.register("monitor_block", MONITOR_BLOCK); +} + +registerAll(); diff --git a/src/checkable_continuous_flyout.ts b/src/checkable_continuous_flyout.ts new file mode 100644 index 0000000000..55b195de4c --- /dev/null +++ b/src/checkable_continuous_flyout.ts @@ -0,0 +1,116 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import { ContinuousFlyout } from "@blockly/continuous-toolbox"; +import { CheckboxBubble } from "./checkbox_bubble"; +import { StatusIndicatorLabel } from "./status_indicator_label"; +import { STATUS_INDICATOR_LABEL_TYPE } from "./status_indicator_label_flyout_inflater"; + +export class CheckableContinuousFlyout extends ContinuousFlyout { + /** + * Creates a new CheckableContinuousFlyout. + * + * @param workspaceOptions Configuration options for the flyout workspace. + */ + constructor(workspaceOptions: Blockly.Options) { + workspaceOptions.modalInputs = false; + super(workspaceOptions); + this.tabWidth_ = 0; + this.MARGIN = 12; + this.GAP_Y = 12; + } + + /** + * Serializes a block to JSON in order to copy it to the main workspace. + * + * @param block The block to serialize. + * @returns A JSON representation of the block. + */ + protected serializeBlock(block: Blockly.BlockSvg) { + const json = super.serializeBlock(block); + // Delete the serialized block's ID so that a new one is generated when it is + // placed on the workspace. Otherwise, the block on the workspace may be + // indistinguishable from the one in the flyout, which can cause reporter blocks + // to have their value dropdown shown in the wrong place. + delete json.id; + return json; + } + + /** + * Set the state of a checkbox by block ID. + * + * @param blockId ID of the block whose checkbox should be set + * @param value Value to set the checkbox to. + */ + setCheckboxState(blockId: string, value: boolean) { + this.getWorkspace() + .getBlockById(blockId) + ?.getIcon("checkbox") + ?.setChecked(value); + } + + getFlyoutScale() { + return 0.675; + } + + getWidth() { + return 250; + } + + protected reflowInternal_() { + super.reflowInternal_(); + + if (this.RTL) { + // The parent implementation assumes that the flyout grows to fit its + // contents, and adjusts blocks in RTL mode accordingly. In Scratch, the + // flyout width is fixed (and blocks may exceed it), so re-adjust blocks + // accordingly based on the actual fixed width. + for (const item of this.getContents()) { + const oldX = item.getElement().getBoundingRectangle().left; + let newX = + this.getWidth() / this.workspace_.scale - + item.getElement().getBoundingRectangle().getWidth() - + this.MARGIN; + if ( + "checkboxInFlyout" in item.getElement() && + item.getElement().checkboxInFlyout + ) { + newX -= CheckboxBubble.CHECKBOX_SIZE + CheckboxBubble.CHECKBOX_MARGIN; + } + item.getElement().moveBy(newX - oldX, 0); + } + } + } + + /** + * Validates that the given toolbox item represents a label. + * + * @param item The toolbox item to check. + * @returns True if the item represents a label in the flyout. + */ + protected toolboxItemIsLabel(item: Blockly.FlyoutItem) { + return ( + item.getType() === STATUS_INDICATOR_LABEL_TYPE || + super.toolboxItemIsLabel(item) + ); + } + + /** + * Updates the state of status indicators for hardware-based extensions. + */ + refreshStatusButtons() { + for (const item of this.contents) { + if (item.element instanceof StatusIndicatorLabel) { + item.element.refreshStatus(); + } + } + } + + scrollTo(position: number) { + super.scrollTo(Math.ceil(position)); + } +} diff --git a/src/checkbox_bubble.ts b/src/checkbox_bubble.ts new file mode 100644 index 0000000000..5ee410011d --- /dev/null +++ b/src/checkbox_bubble.ts @@ -0,0 +1,285 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; + +/** + * A checkbox shown next to reporter blocks in the flyout. + */ +export class CheckboxBubble + implements Blockly.IBubble, Blockly.IRenderedElement +{ + /** + * Size of a checkbox next to a variable reporter. + */ + static readonly CHECKBOX_SIZE = 25; + + /** + * Amount of touchable padding around reporter checkboxes. + */ + static readonly CHECKBOX_TOUCH_PADDING = 12; + + /** + * SVG path data for checkmark in checkbox. + */ + static readonly CHECKMARK_PATH = + "M" + + CheckboxBubble.CHECKBOX_SIZE / 4 + + " " + + CheckboxBubble.CHECKBOX_SIZE / 2 + + "L" + + (5 * CheckboxBubble.CHECKBOX_SIZE) / 12 + + " " + + (2 * CheckboxBubble.CHECKBOX_SIZE) / 3 + + "L" + + (3 * CheckboxBubble.CHECKBOX_SIZE) / 4 + + " " + + CheckboxBubble.CHECKBOX_SIZE / 3; + + /** + * Size of the checkbox corner radius. + */ + static readonly CHECKBOX_CORNER_RADIUS = 5; + + /** + * The margin around a checkbox. + */ + static readonly CHECKBOX_MARGIN = 12; + + /** + * Total additional width of a row that contains a checkbox. + */ + static readonly CHECKBOX_SPACE_X = + CheckboxBubble.CHECKBOX_SIZE + 2 * CheckboxBubble.CHECKBOX_MARGIN; + + /** + * Root SVG element for this bubble. + */ + svgRoot: SVGGElement; + + /** + * Identifier for click handler, to allow unregistering during disposal. + */ + clickListener: Blockly.browserEvents.Data; + + /** + * Whether or not this bubble is displayed as checked. Note that the source of + * truth is the Scratch VM. + */ + checked = false; + + /** + * The location of this bubble in workspace coordinates. + */ + location = new Blockly.utils.Coordinate(0, 0); + + /** + * Creates a new flyout checkbox bubble. + * + * @param sourceBlock The block this bubble should be associated with. + */ + constructor(private sourceBlock: Blockly.BlockSvg) { + this.svgRoot = Blockly.utils.dom.createSvgElement( + Blockly.utils.Svg.G, + {}, + this.sourceBlock.workspace.getBubbleCanvas() + ); + + const touchMargin = CheckboxBubble.CHECKBOX_TOUCH_PADDING; + const checkboxGroup = Blockly.utils.dom.createSvgElement( + "g", + { + fill: "transparent", + }, + null + ); + Blockly.utils.dom.createSvgElement( + "rect", + { + class: "blocklyFlyoutCheckbox", + height: CheckboxBubble.CHECKBOX_SIZE, + width: CheckboxBubble.CHECKBOX_SIZE, + rx: CheckboxBubble.CHECKBOX_CORNER_RADIUS, + ry: CheckboxBubble.CHECKBOX_CORNER_RADIUS, + }, + checkboxGroup + ); + Blockly.utils.dom.createSvgElement( + "path", + { + class: "blocklyFlyoutCheckboxPath", + d: CheckboxBubble.CHECKMARK_PATH, + }, + checkboxGroup + ); + Blockly.utils.dom.createSvgElement( + "rect", + { + class: "blocklyTouchTargetBackground", + x: -touchMargin + "px", + y: -touchMargin + "px", + height: CheckboxBubble.CHECKBOX_SIZE + 2 * touchMargin, + width: CheckboxBubble.CHECKBOX_SIZE + 2 * touchMargin, + }, + checkboxGroup + ); + this.setChecked(this.isChecked(this.sourceBlock.id)); + + this.svgRoot.prepend(checkboxGroup); + + this.clickListener = Blockly.browserEvents.bind( + this.svgRoot, + "pointerdown", + null, + (event: PointerEvent) => { + this.setChecked(!this.checked); + event.stopPropagation(); + event.preventDefault(); + } + ); + this.updateLocation(); + } + + /** + * Sets whether or not this bubble should be displayed in the checked state. + * + * @param checked True if this bubble should be checked. + */ + setChecked(checked: boolean) { + if (checked === this.checked) return; + + this.checked = checked; + if (this.checked) { + Blockly.utils.dom.addClass(this.svgRoot, "checked"); + } else { + Blockly.utils.dom.removeClass(this.svgRoot, "checked"); + } + + Blockly.Events.fire( + new Blockly.Events.BlockChange( + this.sourceBlock, + "checkbox", + null, + !this.checked, + this.checked + ) + ); + } + + /** + * Returns whether or not the specified block has its checkbox checked. + * + * This method is patched by scratch-gui to query the VM state. + * + * @param blockId The ID of the block in question. + * @returns True if the block's checkbox should be checked. + */ + isChecked(blockId: string): boolean { + return false; + } + + /** + * Returns whether this bubble is movable by the user. + * + * @returns Always returns false. + */ + isMovable(): boolean { + return false; + } + + /** + * Returns the root SVG element for this bubble. + * + * @returns The root SVG element. + */ + getSvgRoot(): SVGGElement { + return this.svgRoot; + } + + /** + * Recalculates this bubble's location, keeping it adjacent to its block. + */ + updateLocation() { + const bounds = this.sourceBlock.getBoundingRectangle(); + const x = this.sourceBlock.workspace.RTL + ? bounds.right + CheckboxBubble.CHECKBOX_MARGIN + : bounds.left - + CheckboxBubble.CHECKBOX_MARGIN - + CheckboxBubble.CHECKBOX_SIZE; + const y = + bounds.top + (bounds.getHeight() - CheckboxBubble.CHECKBOX_SIZE) / 2; + this.moveTo(x, y); + } + + /** + * Moves this bubble to the specified location. + * + * @param x The location on the X axis to move to. + * @param y The location on the Y axis to move to. + */ + moveTo(x: number, y: number) { + this.location.x = x; + this.location.y = y; + this.svgRoot.setAttribute("transform", `translate(${x}, ${y})`); + } + + /** + * Returns this bubble's location in workspace coordinates. + * + * @returns The bubble's location. + */ + getRelativeToSurfaceXY(): Blockly.utils.Coordinate { + return this.location; + } + + /** + * Disposes of this checkbox bubble. + */ + dispose() { + Blockly.utils.dom.removeNode(this.svgRoot); + Blockly.browserEvents.unbind(this.clickListener); + } + + /** See IFocusableNode.getFocusableElement. */ + getFocusableElement(): HTMLElement | SVGElement { + return this.svgRoot; + } + + /** See IFocusableNode.getFocusableTree. */ + getFocusableTree(): Blockly.IFocusableTree { + return this.sourceBlock.workspace; + } + + /** See IFocusableNode.onNodeFocus. */ + onNodeFocus(): void {} + + /** See IFocusableNode.onNodeBlur. */ + onNodeBlur(): void {} + + /** See IFocusableNode.canBeFocused. */ + canBeFocused(): boolean { + return true; + } + + // These methods are required by the interfaces, but intentionally have no + // implementation, largely because this bubble's location is fixed relative + // to its block and is not draggable by the user. + showContextMenu() {} + + setDragging(dragging: boolean) {} + + startDrag(event: PointerEvent) {} + + drag(newLocation: Blockly.utils.Coordinate, event: PointerEvent) {} + + moveDuringDrag(newLocation: Blockly.utils.Coordinate) {} + + endDrag() {} + + revertDrag() {} + + setDeleteStyle(enable: boolean) {} +} diff --git a/src/colours.ts b/src/colours.ts new file mode 100644 index 0000000000..cb3cfe2843 --- /dev/null +++ b/src/colours.ts @@ -0,0 +1,85 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Massachusetts Institute of Technology + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import * as Blockly from "blockly/core"; + +const Colours = { + // SVG colours: these must be specified in #RRGGBB style + // To add an opacity, this must be specified as a separate property (for SVG fill-opacity) + text: "#FFFFFF", + workspace: "#F9F9F9", + toolboxHover: "#4C97FF", + toolboxSelected: "#e9eef2", + toolboxText: "#575E75", + toolbox: "#FFFFFF", + flyout: "#F9F9F9", + scrollbar: "#CECDCE", + scrollbarHover: "#CECDCE", + textField: "#FFFFFF", + textFieldText: "#575E75", + insertionMarker: "#000000", + insertionMarkerOpacity: 0.2, + dragShadowOpacity: 0.6, + stackGlow: "#FFF200", + stackGlowSize: 4, + stackGlowOpacity: 1, + replacementGlow: "#FFFFFF", + replacementGlowSize: 2, + replacementGlowOpacity: 1, + colourPickerStroke: "#FFFFFF", + // CSS colours: support RGBA + fieldShadow: "rgba(0,0,0,0.1)", + dropDownShadow: "rgba(0, 0, 0, .3)", + numPadBackground: "#547AB2", + numPadBorder: "#435F91", + numPadActiveBackground: "#435F91", + numPadText: "white", // Do not use hex here, it cannot be inlined with data-uri SVG + valueReportBackground: "#FFFFFF", + valueReportBorder: "#AAAAAA", + contextualMenuHover: "rgba(77, 151, 255, .25)", + menuHover: "rgba(0, 0, 0, .2)", +}; + +/** + * Converts the given colours to CSS variables. + * + * @param coloursObj A (potentially nested) object whose keys are colour names + * and values are CSS colours. + * @param prefix A prefix to prepend to the CSS variables. + * @returns A string containing CSS variable definitions for the colours. + */ +function varify(coloursObj: Object, prefix = "--colour"): string { + return Object.entries(coloursObj) + .map(([key, colour]) => { + if (typeof colour === "string") { + return `${prefix}-${key}: ${colour};`; + } else { + return varify(colour, `${prefix}-${key}`); + } + }) + .join("\n"); +} + +const cssVariables = `:root { + ${varify(Colours)} +}`; + +Blockly.Css.register(cssVariables); + +export { Colours }; diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000000..5aedaf941d --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,76 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * String representing the variable type of scalar variables. + * This string, for use in differentiating between types of variables, + * indicates that the current variable is a scalar variable. + * @const {string} + */ +const SCALAR_VARIABLE_TYPE = ""; +export { SCALAR_VARIABLE_TYPE }; + +/** + * String representing the variable type of broadcast message blocks. + * This string, for use in differentiating between types of variables, + * indicates that the current variable is a broadcast message. + * @const {string} + */ +const BROADCAST_MESSAGE_VARIABLE_TYPE = "broadcast_msg"; +export { BROADCAST_MESSAGE_VARIABLE_TYPE }; + +/** + * String representing the variable type of list blocks. + * This string, for use in differentiating between types of variables, + * indicates that the current variable is a list. + * @const {string} + */ +const LIST_VARIABLE_TYPE = "list"; +export { LIST_VARIABLE_TYPE }; + +/* + * The type of all procedure definition blocks. + * @const {string} + */ +const PROCEDURES_DEFINITION_BLOCK_TYPE = "procedures_definition"; +export { PROCEDURES_DEFINITION_BLOCK_TYPE }; + +/* + * The type of all procedure declaration blocks. + * @const {string} + */ +const PROCEDURES_DECLARATION_BLOCK_TYPE = "procedures_declaration"; +export { PROCEDURES_DECLARATION_BLOCK_TYPE }; + +/** + * The type of all procedure prototype blocks. + * @const {string} + */ +const PROCEDURES_PROTOTYPE_BLOCK_TYPE = "procedures_prototype"; +export { PROCEDURES_PROTOTYPE_BLOCK_TYPE }; + +/** + * The type of all procedure call blocks. + * @const {string} + */ +const PROCEDURES_CALL_BLOCK_TYPE = "procedures_call"; +export { PROCEDURES_CALL_BLOCK_TYPE }; + +const OUTPUT_SHAPE_HEXAGONAL = 1; +export { OUTPUT_SHAPE_HEXAGONAL }; + +const OUTPUT_SHAPE_ROUND = 2; +export { OUTPUT_SHAPE_ROUND }; + +/** + * String for use in the dropdown created in field_variable, + * specifically for broadcast messages. + * This string indicates that this option in the dropdown is 'New message...' + * and if selected, should trigger the prompt to create a new message. + * @const {string} + */ +const NEW_BROADCAST_MESSAGE_ID = "NEW_BROADCAST_MESSAGE_ID"; +export { NEW_BROADCAST_MESSAGE_ID }; diff --git a/src/context_menu_items.ts b/src/context_menu_items.ts new file mode 100644 index 0000000000..0ebae9f991 --- /dev/null +++ b/src/context_menu_items.ts @@ -0,0 +1,180 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; + +/** + * Registers a block delete option that ignores shadows in the block count. + */ +export function registerDeleteBlock() { + const deleteOption = { + displayText(scope: Blockly.ContextMenuRegistry.Scope) { + const descendantCount = getDeletableBlocksInStack(scope.block).length; + return descendantCount === 1 + ? Blockly.Msg["DELETE_BLOCK"] + : Blockly.Msg["DELETE_X_BLOCKS"].replace("%1", `${descendantCount}`); + }, + preconditionFn(scope: Blockly.ContextMenuRegistry.Scope) { + if (!scope.block.isInFlyout && scope.block.isDeletable()) { + return "enabled"; + } + return "hidden"; + }, + callback(scope: Blockly.ContextMenuRegistry.Scope) { + Blockly.Events.setGroup(true); + scope.block.dispose(true, true); + Blockly.Events.setGroup(false); + }, + scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK, + id: "blockDelete", + weight: 6, + }; + Blockly.ContextMenuRegistry.registry.register(deleteOption); +} + +function getDeletableBlocksInStack( + block: Blockly.BlockSvg +): Blockly.BlockSvg[] { + let descendants = block.getDescendants(false).filter(isDeletable); + if (block.getNextBlock()) { + // Next blocks are not deleted. + const nextDescendants = block + .getNextBlock() + .getDescendants(false) + .filter(isDeletable); + descendants = descendants.filter((b) => !nextDescendants.includes(b)); + } + return descendants; +} + +function isDeletable(block: Blockly.BlockSvg): boolean { + return block.isDeletable() && !block.isShadow(); +} + +/** + * Option to delete all blocks. + */ +export function registerDeleteAll() { + const deleteOption = { + displayText(scope: Blockly.ContextMenuRegistry.Scope) { + if (!scope.workspace) { + return ""; + } + const deletableBlocksLength = getDeletableBlocksInWorkspace( + scope.workspace + ).length; + if (deletableBlocksLength === 1) { + return Blockly.Msg["DELETE_BLOCK"]; + } + return Blockly.Msg["DELETE_X_BLOCKS"].replace( + "%1", + `${deletableBlocksLength}` + ); + }, + preconditionFn(scope: Blockly.ContextMenuRegistry.Scope) { + if (!scope.workspace) { + return "disabled"; + } + const deletableBlocksLength = getDeletableBlocksInWorkspace( + scope.workspace + ).length; + return deletableBlocksLength > 0 ? "enabled" : "disabled"; + }, + callback(scope: Blockly.ContextMenuRegistry.Scope) { + if (!scope.workspace) { + return; + } + scope.workspace.cancelCurrentGesture(); + const deletableBlocks = getDeletableBlocksInWorkspace(scope.workspace); + if (deletableBlocks.length < 2) { + deleteNext(deletableBlocks); + } else { + Blockly.dialog.confirm( + Blockly.Msg["DELETE_ALL_BLOCKS"].replace( + "%1", + String(deletableBlocks.length) + ), + function (ok: boolean) { + if (ok) { + deleteNext(deletableBlocks); + } + } + ); + } + }, + scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE, + id: "workspaceDelete", + weight: 6, + }; + Blockly.ContextMenuRegistry.registry.register(deleteOption); +} + +/* + * Constructs a list of blocks that can be deleted in the given workspace. + * + * @param workspace to delete all blocks from. + * @returns list of blocks to delete. + */ +function getDeletableBlocksInWorkspace( + workspace: Blockly.WorkspaceSvg +): Blockly.BlockSvg[] { + return workspace + .getTopBlocks(true) + .flatMap((b: Blockly.BlockSvg) => + b.getDescendants(false).filter(isDeletable) + ); +} + +/** + * Deletes the given blocks. Used to delete all blocks in the workspace. + * + * @param deleteList List of blocks to delete. + * @param eventGroup Event group ID with which all delete events should be + * associated. If not specified, create a new group. + */ +function deleteNext(deleteList: Blockly.BlockSvg[], eventGroup?: string) { + const DELAY = 10; + if (eventGroup) { + Blockly.Events.setGroup(eventGroup); + } else { + Blockly.Events.setGroup(true); + eventGroup = Blockly.Events.getGroup(); + } + const block = deleteList.shift(); + if (block) { + if (!block.isDeadOrDying()) { + block.dispose(false, true); + setTimeout(deleteNext, DELAY, deleteList, eventGroup); + } else { + deleteNext(deleteList, eventGroup); + } + } + Blockly.Events.setGroup(false); +} + +/** + * Registers a block duplicate option that duplicates the selected block and + * all subsequent blocks in the stack. + */ +export function registerDuplicateBlock() { + const original = + Blockly.ContextMenuRegistry.registry.getItem("blockDuplicate"); + const duplicateOption = { + displayText: original.displayText, + preconditionFn: original.preconditionFn, + callback(scope: Blockly.ContextMenuRegistry.Scope) { + if (!scope.block) return; + const data = scope.block.toCopyData(true); + if (!data) return; + Blockly.clipboard.paste(data, scope.block.workspace); + }, + scopeType: original.scopeType, + id: original.id, + weight: original.weight, + }; + Blockly.ContextMenuRegistry.registry.unregister(duplicateOption.id); + Blockly.ContextMenuRegistry.registry.register(duplicateOption); +} diff --git a/src/css.ts b/src/css.ts new file mode 100644 index 0000000000..656d700dbc --- /dev/null +++ b/src/css.ts @@ -0,0 +1,1206 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2013 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import * as Blockly from "blockly/core"; + +const styles = ` + .blocklySvg { + background-color: var(--colour-workspace); + outline: none; + overflow: hidden; /* IE overflows by default. */ + position: absolute; + display: block; + } + + /* Necessary to position the drag surface */ + .blocklyRelativeWrapper { + position: relative; + width: 100%; + height: 100%; + } + + .blocklyWidgetDiv { + display: none; + position: absolute; + z-index: 99999; /* big value for bootstrap3 compatibility */ + } + + .injectionDiv { + height: 100%; + position: relative; + overflow: hidden; /* So blocks in drag surface disappear at edges */ + touch-action: none; + } + + .injectionDiv.boundless { + overflow: visible; + } + + .blocklyNonSelectable { + user-select: none; + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + } + + .blocklyBlockCanvas.blocklyCanvasTransitioning, + .blocklyBubbleCanvas.blocklyCanvasTransitioning { + transition: none; + } + + .blocklyWidgetDiv.fieldTextInput { + overflow: hidden; + border: 1px solid; + box-sizing: border-box; + transform-origin: 0 0; + -ms-transform-origin: 0 0; + -moz-transform-origin: 0 0; + -webkit-transform-origin: 0 0; + } + + .blocklyWidgetDiv.fieldTextInput.removableTextInput { + overflow: visible; + } + + .blocklyTextDropDownArrow { + position: absolute; + } + + .blocklyTextRemoveIcon { + position: absolute; + width: 24px; + height: 24px; + top: -40px; + left: 50%; + margin-left: -12px; + cursor: pointer; + } + + .blocklyWsDragSurface { + display: none; + position: absolute; + top: 0; + left: 0; + } + /* Added as a separate rule with multiple classes to make it more specific + than a bootstrap rule that selects svg:root. See issue #1275 for context. + */ + .blocklyWsDragSurface.blocklyOverflowVisible { + overflow: visible; + } + + .blocklyTooltipDiv { + background-color: #ffffc7; + border: 1px solid #ddc; + box-shadow: 4px 4px 20px 1px rgba(0,0,0,.15); + color: #000; + display: none; + font-family: "Helvetica Neue", Helvetica, sans-serif; + font-size: 9pt; + opacity: 0.9; + padding: 2px; + position: absolute; + z-index: 100000; /* big value for bootstrap3 compatibility */ + } + + .blocklyDropDownDiv { + position: fixed; + left: 0; + top: 0; + z-index: 1000; + display: none; + border: 1px solid; + border-radius: 4px; + box-shadow: 0px 0px 8px 1px var(--colour-dropDownShadow); + padding: 4px; + -webkit-user-select: none; + min-height: 15px + } + + .blocklyDropDownContent { + max-height: 300px; // @todo: spec for maximum height. + overflow: auto; + } + + .blocklyDropDownArrow { + position: absolute; + left: 0; + top: 0; + width: 16px; + height: 16px; + z-index: -1; + background-color: inherit; + border-color: inherit; + } + + .blocklyDropDownButton { + display: inline-block; + float: left; + padding: 0; + margin: 4px; + border-radius: 4px; + outline: none; + border: 1px solid; + transition: box-shadow .1s; + cursor: pointer; + } + + .blocklyDropDownButtonHover { + box-shadow: 0px 0px 0px 4px var(--colour-fieldShadow); + } + + .blocklyDropDownButton:active { + box-shadow: 0px 0px 0px 6px var(--colour-fieldShadow); + } + + .blocklyDropDownButton > img { + width: 80%; + height: 80%; + margin-top: 5% + } + + .blocklyDropDownPlaceholder { + display: inline-block; + float: left; + padding: 0; + margin: 4px; + } + + .blocklyNumPadButton { + display: inline-block; + float: left; + padding: 0; + width: 48px; + height: 48px; + margin: 4px; + border-radius: 4px; + background: var(--colour-numPadBackground); + color: var(--colour-numPadText); + outline: none; + border: 1px solid var(--colour-numPadBorder); + cursor: pointer; + font-weight: 600; + font-family: "Helvetica Neue", Helvetica, sans-serif; + font-size: 12pt; + -webkit-tap-highlight-color: rgba(0,0,0,0); + } + + .blocklyNumPadButton > img { + margin-top: 10%; + width: 80%; + height: 80%; + } + + .blocklyNumPadButton:active { + background: var(--colour-numPadActiveBackground); + -webkit-tap-highlight-color: rgba(0,0,0,0); + } + + .arrowTop { + border-top: 1px solid; + border-left: 1px solid; + border-top-left-radius: 4px; + border-color: inherit; + } + + .arrowBottom { + border-bottom: 1px solid; + border-right: 1px solid; + border-bottom-right-radius: 4px; + border-color: inherit; + } + + .valueReportBox { + min-width: 50px; + max-width: 300px; + max-height: 200px; + overflow: auto; + word-wrap: break-word; + text-align: center; + font-family: "Helvetica Neue", Helvetica, sans-serif; + font-size: .8em; + color: var(--colour-textFieldText); + } + + .blocklyResizeSE { + cursor: se-resize; + fill: #aaa; + } + + .blocklyResizeSW { + cursor: sw-resize; + fill: #aaa; + } + + .blocklyResizeLine { + stroke: #888; + stroke-width: 1; + } + + .blocklyHighlightedConnectionPath { + fill: none; + stroke: #fc3; + stroke-width: 4px; + } + + .blocklyPath { + stroke-width: 1px; + } + + .blocklySelected>.blocklyPath { + // stroke: #fc3; + // stroke-width: 3px; + } + + .blocklySelected>.blocklyPathLight { + display: none; + } + + .blocklyDraggable { + /* backup for browsers (e.g. IE11) that don't support grab */ + cursor: url("<<>>/handopen.cur"), auto; + cursor: grab; + cursor: -webkit-grab; + cursor: -moz-grab; + } + + .blocklyDragging { + /* backup for browsers (e.g. IE11) that don't support grabbing */ + cursor: url("<<>>/handclosed.cur"), auto; + cursor: grabbing; + cursor: -webkit-grabbing; + cursor: -moz-grabbing; + } + + /* All the blocks being dragged get the blocklyDragging class, so match only the root one */ + :not(.blocklyDragging) > .blocklyDragging { + filter: url(#blocklyDragShadowFilter); + } + + /* Changes cursor on mouse down. Not effective in Firefox because of + https://bugzilla.mozilla.org/show_bug.cgi?id=771241 */ + .blocklyDraggable:active { + /* backup for browsers (e.g. IE11) that don't support grabbing */ + cursor: url("<<>>/handclosed.cur"), auto; + cursor: grabbing; + cursor: -webkit-grabbing; + cursor: -moz-grabbing; + } + /* Change the cursor on the whole drag surface in case the mouse gets + ahead of block during a drag. This way the cursor is still a closed hand. + */ + .blocklyBlockDragSurface .blocklyDraggable { + /* backup for browsers (e.g. IE11) that don't support grabbing */ + cursor: url("<<>>/handclosed.cur"), auto; + cursor: grabbing; + cursor: -webkit-grabbing; + cursor: -moz-grabbing; + } + + .blocklyDragging.blocklyDraggingDelete { + cursor: url("<<>>/handdelete.cur"), auto; + } + + .blocklyDragging.blocklyDraggingMouseThrough { + pointer-events: none; + } + + .blocklyToolboxDelete { + cursor: url("<<>>/handdelete.cur"), auto; + } + + .blocklyToolboxGrab { + cursor: url("<<>>/handclosed.cur"), auto; + cursor: grabbing; + cursor: -webkit-grabbing; + } + + .blocklyDragging>.blocklyPath, + .blocklyDragging>.blocklyPathLight { + fill-opacity: 1.0; + stroke-opacity: 1.0; + } + + .blocklyDragging>.blocklyPath { + } + + .blocklyDisabled>.blocklyPath { + fill-opacity: .5; + stroke-opacity: .5; + } + + .blocklyInsertionMarker>.blocklyPath { + stroke: none; + } + + .blocklyText { + fill: var(--colour-text); + font-family: "Helvetica Neue", Helvetica, sans-serif; + font-size: 12pt; + font-weight: 500; + } + + .blocklyTextTruncated { + font-size: 11pt; + } + + .blocklyNonEditableText>text { + pointer-events: none; + } + .blocklyNonEditableText>text, + .blocklyEditableText>text { + fill: var(--colour-textFieldText); + } + + .blocklyEditableText>.blocklyEditableLabel { + fill: #fff; + } + + .blocklyDropdownText { + fill: $colour_text !important; + } + + .blocklyBubbleText { + fill: var(--colour-textFieldText); + } + .blocklyFlyout { + position: absolute; + z-index: 20; + } + .blocklyFlyoutButton { + fill: none; + pointer-events: all; + } + + .blocklyFlyoutButtonBackground { + stroke: #c6c6c6; + } + + .blocklyFlyoutButtonShadow { + fill: transparent; + } + + .blocklyFlyoutButton:hover { + fill: white; + cursor: pointer; + } + + .blocklyFlyoutLabel { + cursor: default; + } + + .blocklyFlyoutLabelBackground { + opacity: 0; + } + + .blocklyTouchTargetBackground { + fill: transparent; + cursor: pointer; + } + + .scratch-renderer.default-theme .blocklyFlyoutLabelText, + .scratch-renderer.high-contrast-theme .blocklyFlyoutLabelText { + font-family: "Helvetica Neue", Helvetica, sans-serif; + font-size: 14pt; + fill: #575E75; + font-weight: bold; + } + + .scratch-renderer.default-theme .blocklyText, + .scratch-renderer.default-theme .blocklyHtmlInput, + .scratch-renderer.high-contrast-theme .blocklyText, + .scratch-renderer.high-contrast-theme .blocklyHtmlInput { + font-weight: 500; + } + + .scratch-renderer.high-contrast-theme .blocklyText, + .scratch-renderer.high-contrast-theme .blocklyEditableField .blocklyDropdownText { + fill: #000 !important; + } + + .scratch-renderer.high-contrast-theme .blocklyEditableField image:last-child { + filter: invert(1); + } + + .scratch-renderer.default-theme .blocklyFlyoutButton .blocklyText, + .scratch-renderer.high-contrast-theme .blocklyFlyoutButton .blocklyText { + fill: var(--colour-textFieldText); + } + + /* + Don't allow users to select text. It gets annoying when trying to + drag a block and selected text moves instead. + */ + .blocklySvg text, .blocklyBlockDragSurface text, .blocklyFlyout text, .blocklyToolboxDiv text { + user-select: none; + -moz-user-select: none; + -webkit-user-select: none; + cursor: inherit; + } + + .blocklyHidden { + display: none; + } + + .blocklyFieldDropdown:not(.blocklyHidden) { + display: block; + } + + .blocklyIconGroup { + cursor: default; + } + + .blocklyIconGroup:not(:hover), + .blocklyIconGroupReadonly { + opacity: .6; + } + + .blocklyIconShape { + fill: #00f; + stroke: #fff; + stroke-width: 1px; + } + + .blocklyIconSymbol { + fill: #fff; + } + + .blocklyMinimalBody { + margin: 0; + padding: 0; + } + + .blocklyCommentForeignObject { + position: relative; + z-index: 0; + } + + .blocklyCommentRect { + fill: #E7DE8E; + stroke: #bcA903; + stroke-width: 1px + } + + .blocklyCommentTarget { + fill: transparent; + stroke: #bcA903; + } + + .blocklyCommentTargetFocused { + fill: none; + } + + .blocklyCommentHandleTarget { + fill: none; + } + + .blocklyCommentHandleTargetFocused { + fill: transparent; + } + + .blocklyFocused>.blocklyCommentRect { + fill: #B9B272; + stroke: #B9B272; + } + + .blocklySelected>.blocklyCommentTarget { + stroke: #fc3; + stroke-width: 3px; + } + + .blocklyCommentText::placeholder { + font-style: italic; + } + + .blocklyCommentTextarea { + background-color: #fef49c; + border: 0; + outline: 0; + margin: 0; + padding: 3px; + resize: none; + display: block; + overflow: hidden; + } + + .blocklyCommentDeleteIcon { + cursor: pointer; + fill: #000; + display: none + } + + .blocklySelected > .blocklyCommentDeleteIcon { + display: block + } + + .blocklyDeleteIconShape { + fill: #000; + stroke: #000; + stroke-width: 1px; + } + + .blocklyDeleteIconShape.blocklyDeleteIconHighlighted { + stroke: #fc3; + } + + // Scratch Comments + + .scratchCommentForeignObject { + position: relative; + } + + .scratchCommentBody { + background-color: #fef49c; + border-radius: 4px; + } + + .scratchCommentRect { + fill: #fef49c; + } + + .scratchCommentTarget { + fill: transparent; + } + + .scratchWorkspaceCommentBorder { + stroke: #bcA903; + stroke-width: 1px; + } + + .scratchCommentTargetFocused { + fill: none; + } + + .scratchCommentTopBar { + fill: #000000; + fill-opacity: 0.1 + } + + .scratchCommentText { + font-family: "Helvetica Neue", Helvetica, sans-serif; + font-size: 12pt; + font-weight: 400; + } + + .scratchCommentTextarea { + background-color: #fef49c; + border: 0; + outline: 0; + padding: 0; + resize: none; + overflow: hidden; + } + + .scratchCommentTextarea::placeholder { + color: rgba(0,0,0,0.5); + font-style: italic; + } + + .scratchCommentResizeSE { + cursor: se-resize; + fill: transparent; + } + + .scratchCommentResizeSW { + cursor: sw-resize; + fill: transparent; + } + + .blocklyHtmlInput { + border: none; + font-family: "Helvetica Neue", Helvetica, sans-serif; + font-size: 12pt; + height: 100%; + margin: 0; + outline: none; + box-sizing: border-box; + width: 100%; + text-align: center; + color: var(--colour-textFieldText); + font-weight: 500; + } + + .blocklyMainBackground { + stroke-width: 1; + stroke: #c6c6c6; /* Equates to #ddd due to border being off-pixel. */ + } + + .blocklyMutatorBackground { + fill: #fff; + stroke: #ddd; + stroke-width: 1; + } + + .blocklyFlyoutBackground { + fill: var(--colour-flyout); + fill-opacity: .8; + } + + .blocklyMainWorkspaceScrollbar { + z-index: 20; + } + + .blocklyFlyoutScrollbar { + z-index: 30; + } + + .blocklyScrollbarHorizontal, .blocklyScrollbarVertical { + position: absolute; + outline: none; + } + + .blocklyScrollbarBackground { + opacity: 0; + } + + .blocklyScrollbarHandle { + fill: var(--colour-scrollbar); + } + + .blocklyScrollbarBackground:hover+.blocklyScrollbarHandle, + .blocklyScrollbarHandle:hover { + fill: var(--colour-scrollbarHover); + } + + .blocklyZoom>image { + opacity: 1; + } + + /* Darken flyout scrollbars due to being on a grey background. */ + /* By contrast, workspace scrollbars are on a white background. */ + .blocklyFlyout .blocklyScrollbarHandle { + fill: #bbb; + } + + .blocklyFlyout .blocklyScrollbarBackground:hover+.blocklyScrollbarHandle, + .blocklyFlyout .blocklyScrollbarHandle:hover { + fill: #aaa; + } + + .blocklyInvalidInput { + background: #faa; + } + + .blocklyAngleCircle { + stroke-width: 1; + } + + .blocklyAngleCenterPoint { + stroke: var(--colour-text); + stroke-width: 1; + fill: var(--colour-text); + } + + .blocklyAngleDragHandle { + stroke: var(--colour-text); + stroke-width: 5; + stroke-opacity: 0.25; + fill: var(--colour-text); + cursor: pointer; + } + + .blocklyAngleDragArrow { + pointer-events: none + } + + .blocklyAngleMarks { + stroke: var(--colour-text); + stroke-width: 1; + stroke-opacity: 0.5; + } + + .blocklyAngleGauge { + fill: var(--colour-text); + fill-opacity: 0.20; + } + + .blocklyAngleLine { + stroke: var(--colour-text); + stroke-width: 1; + stroke-linecap: round; + pointer-events: none; + } + + .blocklyContextMenu { + border-radius: 4px; + max-height: 100%; + } + + .blocklyDropdownMenu { + padding: 0 !important; + } + + .blocklyDropDownNumPad { + background-color: var(--colour-numPadBackground); + } + + /* Category tree in Toolbox. */ + .blocklyToolbox { + background-color: var(--colour-toolbox); + border-right: 1px solid #ddd; + color: var(--colour-toolboxText); + overflow-x: visible; + overflow-y: auto; + position: absolute; + font-family: "Helvetica Neue", Helvetica, sans-serif; + z-index: 40; /* so blocks go over toolbox when dragging */ + -webkit-tap-highlight-color: transparent; /* issue #1345 */ + padding: 0; + } + + .blocklyToolbox[dir="RTL"] { + border-right: none; + border-left: 1px solid #ddd; + } + + .blocklyTreeRoot { + padding: 4px 0; + } + + .blocklyTreeRoot:focus { + outline: none; + } + + .blocklyToolbox .blocklyToolboxCategory { + line-height: 22px; + margin: 0; + padding: 0.375rem 0px; + white-space: nowrap; + cursor: pointer; + } + + .blocklyHorizontalTree { + float: left; + margin: 1px 5px 8px 0; + } + + .blocklyHorizontalTreeRtl { + float: right; + margin: 1px 0 8px 5px; + } + + .blocklyToolbox[dir="RTL"] .blocklyToolboxCategory { + margin-left: 0px; + } + + .blocklyToolboxCategory:hover { + color: var(--colour-toolboxHover); + } + + .blocklyTreeSeparator { + display: none; + } + + .blocklyTreeSeparatorHorizontal { + border-right: solid #e5e5e5 1px; + width: 0; + padding: 5px 0; + margin: 0 5px; + } + + .blocklyTreeIcon { + background-image: url(<<>>/sprites.png); + height: 16px; + vertical-align: middle; + width: 16px; + } + + .blocklyTreeIconClosedLtr { + background-position: -32px -1px; + } + + .blocklyTreeIconClosedRtl { + background-position: 0px -1px; + } + + .blocklyTreeIconOpen { + background-position: -16px -1px; + } + + .blocklyTreeSelected>.blocklyTreeIconClosedLtr { + background-position: -32px -17px; + } + + .blocklyTreeSelected>.blocklyTreeIconClosedRtl { + background-position: 0px -17px; + } + + .blocklyTreeSelected>.blocklyTreeIconOpen { + background-position: -16px -17px; + } + + .blocklyTreeIconNone, + .blocklyTreeSelected>.blocklyTreeIconNone { + background-position: -48px -1px; + } + + .blocklyToolboxCategoryLabel { + cursor: default; + font-family: "Helvetica Neue", Helvetica, sans-serif; + font-size: .65rem; + padding: 0; + vertical-align: middle; + width: 60px; + text-align: center; + text-wrap: wrap; + } + + .blocklyToolboxSelected .blocklyToolboxCategoryLabel { + color: inherit; + } + + .blocklyToolboxDelete .blocklyTreeLabel { + cursor: url("<<>>/handdelete.cur"), auto; + } + + .blocklyToolboxSelected { + background-color: var(--colour-toolboxSelected); + } + + .blocklyDropDownDiv .goog-slider-horizontal { + margin: 8px; + height: 22px; + width: 150px; + position: relative; + outline: none; + border-radius: 11px; + margin-bottom: 20px; + } + + .blocklyDropDownDiv .goog-slider-horizontal .goog-slider-thumb { + width: 26px; + height: 26px; + top: -1px; + position: absolute; + background-color: white; + border-radius: 100%; + -webkit-box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.15); + -moz-box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.15); + box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.15); + } + + .scratchEyedropper { + background: none; + outline: none; + border: none; + width: 100%; + text-align: center; + border-top: 1px solid #ddd; + padding-top: 5px; + cursor: pointer; + } + + .scratchColourPicker { + width: min-content; + } + + .scratchColourPickerLabel { + font-family: "Helvetica Neue", Helvetica, sans-serif; + font-size: 0.65rem; + color: var(--colour-toolboxText); + margin: 8px; + } + + .scratchColourPickerLabelText { + font-weight: bold; + } + + .scratchColourPickerReadout { + margin-left: 10px; + } + + .scratchColourSlider { + appearance: none; + margin: 8px; + height: 22px; + width: 150px; + position: relative; + outline: none; + border-radius: 11px; + margin-bottom: 20px; + } + + /* Combining this and the -moz equivalent below with a comma break the webkit version */ + .scratchColourSlider::-webkit-slider-thumb { + appearance: none; + background-color: #fff; + height: 26px; + width: 26px; + border-radius: 100%; + box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.15); + } + + .scratchColourSlider::-moz-range-thumb { + appearance: none; + background-color: #fff; + height: 26px; + width: 26px; + border-radius: 100%; + box-shadow: 0 0 0 4px rgba(0, 0, 0, 0.15); + } + + .scratchMatrixButtonDiv { + width: 50%; + text-align: center; + float: left; + } + + .scratchNotePickerKeyLabel { + font-family: "Helvetica Neue", Helvetica, sans-serif; + font-size: 0.75rem; + fill: var(--colour-textFieldText); + pointer-events: none; + } + + /* Copied from: goog/css/menu.css */ + /* + * Copyright 2009 The Closure Library Authors. All Rights Reserved. + * + * Use of this source code is governed by the Apache License, Version 2.0. + * See the COPYING file for details. + */ + + /** + * Standard styling for menus created by goog.ui.MenuRenderer. + * + * @author attila@google.com (Attila Bodis) + */ + + .blocklyWidgetDiv .blocklyMenu { + background: #fff; + border-color: #ccc #666 #666 #ccc; + border-style: solid; + border-width: 1px; + cursor: default; + font: normal 13px "Helvetica Neue", Helvetica, sans-serif; + margin: 0; + outline: none; + padding: 4px 0; + position: absolute; + overflow-y: auto; + overflow-x: hidden; + z-index: 20000; /* Arbitrary, but some apps depend on it... */ + box-sizing: content-box; + box-shadow: none; + } + + .blocklyWidgetDiv .blocklyMenu:focus { + box-shadow: none; + } + + .blocklyDropDownDiv .blocklyMenu { + cursor: default; + font: normal 13px "Helvetica Neue", Helvetica, sans-serif; + outline: none; + z-index: 20000; /* Arbitrary, but some apps depend on it... */ + } + + .blocklyDropDownDiv .blocklyMenu .blocklyMenuItem.blocklyMenuItemHighlight { + background-color: var(--colour-menuHover); + } + + .blocklyWidgetDiv .blocklyMenu .blocklyMenuItem.blocklyMenuItemHighlight { + background-color: var(--colour-contextualMenuHover); + } + + .blocklyWidgetDiv .blocklyMenu .blocklyMenuItemDisabled.blocklyMenuItem:hover { + background: none; + } + + .blocklyFlyoutCheckbox { + fill: white; + stroke: #c8c8c8; + } + + .checked .blocklyFlyoutCheckbox { + fill: var(--colour-toolboxHover); + stroke: rgba(0,0,0,0.2); + } + + .blocklyFlyoutCheckboxPath { + fill: transparent; + stroke: white; + stroke-width: 3; + stroke-linecap: round; + stroke-linejoin: round; + } + + .scratchCategoryMenu { + width: 60px; + background: var(--colour-toolbox); + color: var(--colour-toolboxText); + font-size: .7rem; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + } + + .scratchCategoryMenuHorizontal { + width: 100%; + height: 50px; + background: var(--colour-toolbox); + color: var(--colour-toolboxText); + font-size: .7em; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + } + + .scratchCategoryMenuHorizontal .scratchCategoryMenuRow { + float: left; + margin: 3px; + } + + .scratchCategoryMenuRow { + } + + .scratchCategoryMenuItem { + padding: 0.375rem 0px; + cursor: pointer; + text-align: center; + } + + .scratchCategoryMenuHorizontal .scratchCategoryMenuItem { + padding: 6px 5px; + } + + .scratchCategoryMenuItem.categorySelected { + background: var(--colour-toolboxSelected); + } + + .scratchCategoryItemBubble { + width: 1.25rem; + height: 1.25rem; + border: 1px solid; + border-radius: 100%; + margin: 0 auto 0.125rem; + } + + .scratchCategoryItemIcon { + width: 1.25rem; + height: 1.25rem; + margin: 0 auto 0.125rem; + background-size: 100%; + } + + .scratchCategoryMenuItem:hover { + color: $colour_toolboxHover !important; + } + + .categoryIconBubble { + margin: 0 auto 0.125rem; + width: 1.25rem; + height: 1.25rem; + } + + .blocklyComment { + --colour-commentBorder: #bcA903; + } + + .blocklyCommentTopbar { + height: 32px; + --commentBorderColour: #e2db96; + } + + .blocklyCommentTopbarBackground { + height: 32px; + } + + .blocklyFoldoutIcon { + width: 32px; + height: 32px; + transform-origin: 16px 16px; + } + + .blocklyComment:not(.blocklyCollapsed) .blocklyCommentHighlight, + .blocklySelected .blocklyCommentHighlight, + .blocklyCollapsed .blocklyCommentTopbarBackground, + .blocklyCollapsed.blocklySelected .blocklyCommentTopbarBackground { + stroke: var(--colour-commentBorder); + stroke-width: 1px; + } + + .blocklyCollapsed.blocklyComment .blocklyFoldoutIcon { + transform: rotate(-180deg); + } + + .scratch-renderer.default-theme .blocklyComment .blocklyTextarea, + .scratch-renderer.high-contrast-theme .blocklyComment .blocklyTextarea { + border: none; + --commentFillColour: #fef49c; + font-size: 12pt; + font-weight: 400; + padding: 12px; + color: #575e75; + } + + .scratch-renderer.default-theme .blocklyCommentText.blocklyText, + .scratch-renderer.high-contrast-theme .blocklyCommentText.blocklyText { + font-weight: 400; + } + + .blocklyToolboxCategory { + height: auto; + line-height: auto; + margin-bottom: 0; + padding: 0.375rem 0px; + cursor: pointer; + } + .blocklyToolboxCategory:hover { + color: #4c97ff; + } + .blocklyDropDownDiv .blocklyMenuItem { + font-weight: bold; + min-height: 32px; + padding: 4px 7em 4px 28px; + } + .scratch-renderer.blocklyDropDownDiv .blocklyMenuItem .blocklyMenuItemContent { + color: var(--colour-text); + } + .blocklyToolboxSelected .blocklyTreeLabel { + color: var(--colour-toolboxText); + } + + .blocklyDeleteIcon { + display: block; + width: 32px; + height: 32px; + } + + .blocklyResizeHandle { + height: 20px; + width: 20px; + } + + .scratch-renderer.default-theme .blocklyDraggable:not(.blocklyDisabled) .blocklyEditableField:not(.blocklyEditing):hover>rect, + .scratch-renderer.default-theme .blocklyDraggable:not(.blocklyDisabled) .blocklyEditableField:not(.blocklyEditing):hover>.blocklyPath, + .scratch-renderer.high-contrast-theme .blocklyDraggable:not(.blocklyDisabled) .blocklyEditableField:not(.blocklyEditing):hover>rect, + .scratch-renderer.high-contrast-theme .blocklyDraggable:not(.blocklyDisabled) .blocklyEditableField:not(.blocklyEditing):hover>.blocklyPath { + stroke: revert-layer; + stroke-width: 1; + } + + .blocklyInsertionMarker > g:not(:last-child) { + visibility: hidden; + } +`; + +Blockly.Css.register(styles); diff --git a/src/data_category.ts b/src/data_category.ts new file mode 100644 index 0000000000..7b9a417fd4 --- /dev/null +++ b/src/data_category.ts @@ -0,0 +1,622 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2017 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Data Flyout components including variable and list blocks. + * @author marisaleung@google.com (Marisa Leung) + */ + +import * as Blockly from "blockly/core"; +import { createVariable } from "./variables"; +import { LIST_VARIABLE_TYPE, SCALAR_VARIABLE_TYPE } from "./constants"; + +/** + * Construct the blocks required by the flyout for the variable category. + * + * @param workspace The workspace containing variables. + * @returns Array of XML block elements. + */ +export function getVariablesCategory( + workspace: Blockly.WorkspaceSvg +): Element[] { + const scalarVariables = workspace.getVariablesOfType(SCALAR_VARIABLE_TYPE); + scalarVariables.sort(Blockly.Variables.compareByName); + const xmlList: Element[] = []; + + addCreateButton(xmlList, workspace, "VARIABLE"); + + scalarVariables.forEach((variable) => addDataVariable(xmlList, variable)); + + if (scalarVariables.length > 0) { + xmlList[xmlList.length - 1].setAttribute("gap", "24"); + const firstVariable = scalarVariables[0]; + + addSetVariableTo(xmlList, firstVariable); + addChangeVariableBy(xmlList, firstVariable); + addShowVariable(xmlList, firstVariable); + addHideVariable(xmlList, firstVariable); + } + + // Now add list variables to the flyout + addCreateButton(xmlList, workspace, "LIST"); + const listVariables = workspace.getVariablesOfType(LIST_VARIABLE_TYPE); + listVariables.sort(Blockly.Variables.compareByName); + listVariables.forEach((variable) => addDataList(xmlList, variable)); + + if (listVariables.length > 0) { + xmlList[xmlList.length - 1].setAttribute("gap", "24"); + const firstVariable = listVariables[0]; + + addAddToList(xmlList, firstVariable); + addSep(xmlList); + addDeleteOfList(xmlList, firstVariable); + addDeleteAllOfList(xmlList, firstVariable); + addInsertAtList(xmlList, firstVariable); + addReplaceItemOfList(xmlList, firstVariable); + addSep(xmlList); + addItemOfList(xmlList, firstVariable); + addItemNumberOfList(xmlList, firstVariable); + addLengthOfList(xmlList, firstVariable); + addListContainsItem(xmlList, firstVariable); + addSep(xmlList); + addShowList(xmlList, firstVariable); + addHideList(xmlList, firstVariable); + } + + return xmlList; +} + +/** + * Construct and add a data_variable block to xmlList. + * + * @param xmlList Array of XML block elements. + * @param variable Variable to select in the field. + */ +function addDataVariable( + xmlList: Element[], + variable: Blockly.IVariableModel +) { + // + // variablename + // + addBlock(xmlList, variable, "data_variable", "VARIABLE"); + // In the flyout, this ID must match variable ID for monitor syncing reasons + xmlList[xmlList.length - 1].setAttribute("id", variable.getId()); +} + +/** + * Construct and add a data_setvariableto block to xmlList. + * + * @param xmlList Array of XML block elements. + * @param variable Variable to select in the field. + */ +function addSetVariableTo( + xmlList: Element[], + variable: Blockly.IVariableModel +) { + // + // + // + // + // + // + // 0 + // + // + // + addBlock(xmlList, variable, "data_setvariableto", "VARIABLE", [ + "VALUE", + "text", + "0", + ]); +} + +/** + * Construct and add a data_changevariableby block to xmlList. + * + * @param xmlList Array of XML block elements. + * @param variable Variable to select in the field. + */ +function addChangeVariableBy( + xmlList: Element[], + variable: Blockly.IVariableModel +) { + // + // + // + // + // + // + // 1 + // + // + // + addBlock(xmlList, variable, "data_changevariableby", "VARIABLE", [ + "VALUE", + "math_number", + "1", + ]); +} + +/** + * Construct and add a data_showVariable block to xmlList. + * + * @param xmlList Array of XML block elements. + * @param variable Variable to select in the field. + */ +function addShowVariable( + xmlList: Element[], + variable: Blockly.IVariableModel +) { + // + // + // + // + // + addBlock(xmlList, variable, "data_showvariable", "VARIABLE"); +} + +/** + * Construct and add a data_hideVariable block to xmlList. + * + * @param xmlList Array of XML block elements. + * @param variable Variable to select in the field. + */ +function addHideVariable( + xmlList: Element[], + variable: Blockly.IVariableModel +) { + // + // + // + // + // + addBlock(xmlList, variable, "data_hidevariable", "VARIABLE"); +} + +/** + * Construct and add a data_listcontents block to xmlList. + * + * @param xmlList Array of XML block elements. + * @param variable Variable to select in the field. + */ +function addDataList( + xmlList: Element[], + variable: Blockly.IVariableModel +) { + // + // variablename + // + addBlock(xmlList, variable, "data_listcontents", "LIST"); + // In the flyout, this ID must match variable ID for monitor syncing reasons + xmlList[xmlList.length - 1].setAttribute("id", variable.getId()); +} + +/** + * Construct and add a data_addtolist block to xmlList. + * + * @param xmlList Array of XML block elements. + * @param variable Variable to select in the field. + */ +function addAddToList( + xmlList: Element[], + variable: Blockly.IVariableModel +) { + // + // variablename + // + // + // thing + // + // + // + addBlock(xmlList, variable, "data_addtolist", "LIST", [ + "ITEM", + "text", + Blockly.Msg.DEFAULT_LIST_ITEM, + ]); +} + +/** + * Construct and add a data_deleteoflist block to xmlList. + * + * @param xmlList Array of XML block elements. + * @param variable Variable to select in the field. + */ +function addDeleteOfList( + xmlList: Element[], + variable: Blockly.IVariableModel +) { + // + // variablename + // + // + // 1 + // + // + // + addBlock(xmlList, variable, "data_deleteoflist", "LIST", [ + "INDEX", + "math_integer", + "1", + ]); +} + +/** + * Construct and add a data_deleteoflist block to xmlList. + * + * @param xmlList Array of XML block elements. + * @param variable Variable to select in the field. + */ +function addDeleteAllOfList( + xmlList: Element[], + variable: Blockly.IVariableModel +) { + // + // variablename + // + addBlock(xmlList, variable, "data_deletealloflist", "LIST"); +} + +/** + * Construct and add a data_insertatlist block to xmlList. + * + * @param xmlList Array of XML block elements. + * @param variable Variable to select in the field. + */ +function addInsertAtList( + xmlList: Element[], + variable: Blockly.IVariableModel +) { + // + // variablename + // + // + // 1 + // + // + // + // + // thing + // + // + // + addBlock( + xmlList, + variable, + "data_insertatlist", + "LIST", + ["INDEX", "math_integer", "1"], + ["ITEM", "text", Blockly.Msg.DEFAULT_LIST_ITEM] + ); +} + +/** + * Construct and add a data_replaceitemoflist block to xmlList. + * + * @param xmlList Array of XML block elements. + * @param variable Variable to select in the field. + */ +function addReplaceItemOfList( + xmlList: Element[], + variable: Blockly.IVariableModel +) { + // + // variablename + // + // + // 1 + // + // + // + // + // thing + // + // + // + addBlock( + xmlList, + variable, + "data_replaceitemoflist", + "LIST", + ["INDEX", "math_integer", "1"], + ["ITEM", "text", Blockly.Msg.DEFAULT_LIST_ITEM] + ); +} + +/** + * Construct and add a data_itemoflist block to xmlList. + * + * @param xmlList Array of XML block elements. + * @param variable Variable to select in the field. + */ +function addItemOfList( + xmlList: Element[], + variable: Blockly.IVariableModel +) { + // + // variablename + // + // + // 1 + // + // + // + addBlock(xmlList, variable, "data_itemoflist", "LIST", [ + "INDEX", + "math_integer", + "1", + ]); +} + +/** Construct and add a data_itemnumoflist block to xmlList. + * + * @param xmlList Array of XML block elements. + * @param variable Variable to select in the field. + */ +function addItemNumberOfList( + xmlList: Element[], + variable: Blockly.IVariableModel +) { + // + // + // + // thing + // + // + // variablename + // + addBlock(xmlList, variable, "data_itemnumoflist", "LIST", [ + "ITEM", + "text", + Blockly.Msg.DEFAULT_LIST_ITEM, + ]); +} + +/** + * Construct and add a data_lengthoflist block to xmlList. + * + * @param xmlList Array of XML block elements. + * @param variable Variable to select in the field. + */ +function addLengthOfList( + xmlList: Element[], + variable: Blockly.IVariableModel +) { + // + // variablename + // + addBlock(xmlList, variable, "data_lengthoflist", "LIST"); +} + +/** + * Construct and add a data_listcontainsitem block to xmlList. + * + * @param xmlList Array of XML block elements. + * @param variable Variable to select in the field. + */ +function addListContainsItem( + xmlList: Element[], + variable: Blockly.IVariableModel +) { + // + // variablename + // + // + // thing + // + // + // + addBlock(xmlList, variable, "data_listcontainsitem", "LIST", [ + "ITEM", + "text", + Blockly.Msg.DEFAULT_LIST_ITEM, + ]); +} + +/** + * Construct and add a data_showlist block to xmlList. + * + * @param xmlList Array of XML block elements. + * @param variable Variable to select in the field. + */ +function addShowList( + xmlList: Element[], + variable: Blockly.IVariableModel +) { + // + // variablename + // + addBlock(xmlList, variable, "data_showlist", "LIST"); +} + +/** + * Construct and add a data_hidelist block to xmlList. + * + * @param xmlList Array of XML block elements. + * @param variable Variable to select in the field. + */ +function addHideList( + xmlList: Element[], + variable: Blockly.IVariableModel +) { + // + // variablename + // + addBlock(xmlList, variable, "data_hidelist", "LIST"); +} + +/** + * Construct a create variable button and push it to the xmlList. + * + * @param xmlList Array of XML block elements. + * @param workspace Workspace to register callback to. + * @param type Type of variable this is for. For example, 'LIST' or + * 'VARIABLE'. + */ +function addCreateButton( + xmlList: Element[], + workspace: Blockly.WorkspaceSvg, + type: string +) { + const button = document.createElement("button"); + // Set default msg, callbackKey, and callback values for type 'VARIABLE' + let msg = Blockly.Msg.NEW_VARIABLE; + let callbackKey = "CREATE_VARIABLE"; + let callback = function (button: Blockly.FlyoutButton) { + createVariable(button.getTargetWorkspace(), null, SCALAR_VARIABLE_TYPE); + }; + + if (type === "LIST") { + msg = Blockly.Msg.NEW_LIST; + callbackKey = "CREATE_LIST"; + callback = function (button: Blockly.FlyoutButton) { + createVariable(button.getTargetWorkspace(), null, LIST_VARIABLE_TYPE); + }; + } + button.setAttribute("text", msg); + button.setAttribute("callbackKey", callbackKey); + workspace.registerButtonCallback(callbackKey, (b) => { + // Run the callback after a delay to avoid it getting captured by the React + // modal in scratch-gui and being registered as a click on the scrim that + // dismisses the dialog. + requestAnimationFrame(() => { + setTimeout(() => { + callback(b); + }); + }); + }); + xmlList.push(button); +} + +/** + * Construct a variable block with the given variable, blockType, and optional + * value tags. Add the variable block to the given xmlList. + * + * @param xmlList Array of XML block elements. + * @param variable Variable to select in the field. + * @param blockType Type of block. For example, 'data_hidelist' or + * 'data_showlist'. + * @param fieldName Name of field in block. For example: 'VARIABLE' or 'LIST'. + * @param opt_value Optional array containing the value name and shadow type of + * value tags. + * @param opt_secondValue Optional array containing the value name and shadow + * type of a second pair of value tags. + */ +function addBlock( + xmlList: Element[], + variable: Blockly.IVariableModel, + blockType: string, + fieldName: string, + opt_value?: string[], + opt_secondValue?: string[] +) { + if (Blockly.Blocks[blockType]) { + let firstValueField; + let secondValueField; + if (opt_value) { + firstValueField = createValue(opt_value[0], opt_value[1], opt_value[2]); + } + if (opt_secondValue) { + secondValueField = createValue( + opt_secondValue[0], + opt_secondValue[1], + opt_secondValue[2] + ); + } + + var gap = 8; + var blockText = ` + + + ${generateVariableFieldXml(variable, fieldName)} + ${firstValueField} + ${secondValueField} + + `; + var block = Blockly.utils.xml.textToDom(blockText).firstElementChild; + xmlList.push(block); + } +} + +/** + * Creates XML representing a variable field. + * + * @param variableModel The variable to represent in the field. + * @param opt_name A custom name for the field, if desired. + * @returns XML representation of a variable field. + */ +function generateVariableFieldXml( + variableModel: Blockly.IVariableModel, + opt_name?: string +): string { + const field = document.createElement("field"); + field.setAttribute("name", opt_name || "VARIABLE"); + field.setAttribute("id", variableModel.getId()); + field.setAttribute("variabletype", variableModel.getType()); + field.textContent = variableModel.getName(); + return field.outerHTML; +} + +/** + * Create the text representation of a value dom element with a shadow of the + * indicated type inside. + * + * @param valueName Name of the value tags. + * @param type The type of the shadow tags. + * @param value The default shadow value. + * @returns The generated dom element in text. + */ +function createValue(valueName: string, type: string, value: string): string { + let fieldName; + switch (valueName) { + case "ITEM": + fieldName = "TEXT"; + break; + case "INDEX": + fieldName = "NUM"; + break; + case "VALUE": + if (type === "math_number") { + fieldName = "NUM"; + } else { + fieldName = "TEXT"; + } + break; + } + const valueField = ` + + + ${value} + + `; + return valueField; +} + +/** + * Construct a block separator. Add the separator to the given xmlList. + * + * @param xmlList Array of XML block elements. + */ +function addSep(xmlList: Element[]) { + const sepText = ``; + const sep = Blockly.utils.xml.textToDom(sepText).firstElementChild; + xmlList.push(sep); +} diff --git a/src/events/events_block_comment_base.ts b/src/events/events_block_comment_base.ts new file mode 100644 index 0000000000..7cbdf92158 --- /dev/null +++ b/src/events/events_block_comment_base.ts @@ -0,0 +1,54 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import type { ScratchCommentBubble } from "../scratch_comment_bubble"; + +export class BlockCommentBase extends Blockly.Events.Abstract { + isBlank = true; + commentId: string; + blockId: string; + workspaceId: string; + + constructor(opt_blockComment?: ScratchCommentBubble) { + super(); + this.isBlank = !opt_blockComment; + + if (!opt_blockComment) return; + + this.commentId = opt_blockComment.getId(); + this.blockId = opt_blockComment.getSourceBlock()?.id; + this.workspaceId = opt_blockComment.getSourceBlock()?.workspace.id; + } + + toJson(): BlockCommentBaseJson { + return { + ...super.toJson(), + commentId: this.commentId, + blockId: this.blockId, + }; + } + + static fromJson( + json: BlockCommentBaseJson, + workspace: Blockly.Workspace, + event?: any + ): BlockCommentBase { + const newEvent = super.fromJson( + json, + workspace, + event ?? new BlockCommentBase() + ) as BlockCommentBase; + newEvent.commentId = json["commentId"]; + newEvent.blockId = json["blockId"]; + return newEvent; + } +} + +export interface BlockCommentBaseJson extends Blockly.Events.AbstractEventJson { + commentId: string; + blockId: string; +} diff --git a/src/events/events_block_comment_change.ts b/src/events/events_block_comment_change.ts new file mode 100644 index 0000000000..d265415da0 --- /dev/null +++ b/src/events/events_block_comment_change.ts @@ -0,0 +1,67 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import { + BlockCommentBase, + BlockCommentBaseJson, +} from "./events_block_comment_base"; +import type { ScratchCommentBubble } from "../scratch_comment_bubble"; + +class BlockCommentChange extends BlockCommentBase { + oldContents_: string; + newContents_: string; + + constructor( + opt_blockComment?: ScratchCommentBubble, + oldContents?: string, + newContents?: string + ) { + super(opt_blockComment); + this.type = "block_comment_change"; + this.oldContents_ = oldContents; + this.newContents_ = newContents; + // Disable undo because Blockly already tracks changes to comment text for + // undo purposes; this event exists solely to keep the Scratch VM apprised + // of the state of things. + this.recordUndo = false; + } + + toJson(): BlockCommentChangeJson { + return { + ...super.toJson(), + newContents: this.newContents_, + oldContents: this.oldContents_, + }; + } + + static fromJson( + json: BlockCommentChangeJson, + workspace: Blockly.Workspace, + event?: any + ): BlockCommentChange { + const newEvent = super.fromJson( + json, + workspace, + event ?? new BlockCommentChange() + ) as BlockCommentChange; + newEvent.newContents_ = json["newContents"]; + newEvent.oldContents_ = json["oldContents"]; + + return newEvent; + } +} + +interface BlockCommentChangeJson extends BlockCommentBaseJson { + newContents: string; + oldContents: string; +} + +Blockly.registry.register( + Blockly.registry.Type.EVENT, + "block_comment_change", + BlockCommentChange +); diff --git a/src/events/events_block_comment_collapse.ts b/src/events/events_block_comment_collapse.ts new file mode 100644 index 0000000000..30aaa533ca --- /dev/null +++ b/src/events/events_block_comment_collapse.ts @@ -0,0 +1,61 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import { + BlockCommentBase, + BlockCommentBaseJson, +} from "./events_block_comment_base"; +import type { ScratchCommentBubble } from "../scratch_comment_bubble"; + +class BlockCommentCollapse extends BlockCommentBase { + newCollapsed: boolean; + + constructor(opt_blockComment?: ScratchCommentBubble, collapsed?: boolean) { + super(opt_blockComment); + this.type = "block_comment_collapse"; + this.newCollapsed = collapsed; + } + + toJson(): BlockCommentCollapseJson { + return { + ...super.toJson(), + collapsed: this.newCollapsed, + }; + } + + static fromJson( + json: BlockCommentCollapseJson, + workspace: Blockly.Workspace, + event?: any + ): BlockCommentCollapse { + const newEvent = super.fromJson( + json, + workspace, + event ?? new BlockCommentCollapse() + ) as BlockCommentCollapse; + newEvent.newCollapsed = json["collapsed"]; + + return newEvent; + } + + run(forward: boolean) { + const workspace = this.getEventWorkspace_(); + const block = workspace.getBlockById(this.blockId); + const comment = block.getIcon(Blockly.icons.IconType.COMMENT); + comment.setBubbleVisible(forward ? !this.newCollapsed : this.newCollapsed); + } +} + +interface BlockCommentCollapseJson extends BlockCommentBaseJson { + collapsed: boolean; +} + +Blockly.registry.register( + Blockly.registry.Type.EVENT, + "block_comment_collapse", + BlockCommentCollapse +); diff --git a/src/events/events_block_comment_create.ts b/src/events/events_block_comment_create.ts new file mode 100644 index 0000000000..2d8a1b7c42 --- /dev/null +++ b/src/events/events_block_comment_create.ts @@ -0,0 +1,78 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import { + BlockCommentBase, + BlockCommentBaseJson, +} from "./events_block_comment_base"; +import type { ScratchCommentBubble } from "../scratch_comment_bubble"; + +class BlockCommentCreate extends BlockCommentBase { + json: { + x: number; + y: number; + width: number; + height: number; + }; + + constructor(opt_blockComment?: ScratchCommentBubble) { + super(opt_blockComment); + this.type = "block_comment_create"; + const size = opt_blockComment.getSize(); + const location = opt_blockComment.getRelativeToSurfaceXY(); + this.json = { + x: location.x, + y: location.y, + width: size.width, + height: size.height, + }; + // Disable undo because Blockly already tracks comment creation for + // undo purposes; this event exists solely to keep the Scratch VM apprised + // of the state of things. + this.recordUndo = false; + } + + toJson(): BlockCommentCreateJson { + return { + ...super.toJson(), + ...this.json, + }; + } + + static fromJson( + json: BlockCommentCreateJson, + workspace: Blockly.Workspace, + event?: any + ): BlockCommentCreate { + const newEvent = super.fromJson( + json, + workspace, + event ?? new BlockCommentCreate() + ) as BlockCommentCreate; + newEvent.json = { + x: json["x"], + y: json["y"], + width: json["width"], + height: json["height"], + }; + + return newEvent; + } +} + +interface BlockCommentCreateJson extends BlockCommentBaseJson { + x: number; + y: number; + width: number; + height: number; +} + +Blockly.registry.register( + Blockly.registry.Type.EVENT, + "block_comment_create", + BlockCommentCreate +); diff --git a/src/events/events_block_comment_delete.ts b/src/events/events_block_comment_delete.ts new file mode 100644 index 0000000000..4e118b3e1e --- /dev/null +++ b/src/events/events_block_comment_delete.ts @@ -0,0 +1,31 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import { BlockCommentBase } from "./events_block_comment_base"; +import type { ScratchCommentBubble } from "../scratch_comment_bubble"; + +class BlockCommentDelete extends BlockCommentBase { + constructor( + opt_blockComment?: ScratchCommentBubble, + sourceBlock?: Blockly.Block + ) { + super(opt_blockComment); + this.type = "block_comment_delete"; + this.blockId = sourceBlock.id; + this.workspaceId = sourceBlock.workspace.id; + // Disable undo because Blockly already tracks comment deletion for + // undo purposes; this event exists solely to keep the Scratch VM apprised + // of the state of things. + this.recordUndo = false; + } +} + +Blockly.registry.register( + Blockly.registry.Type.EVENT, + "block_comment_delete", + BlockCommentDelete +); diff --git a/src/events/events_block_comment_move.ts b/src/events/events_block_comment_move.ts new file mode 100644 index 0000000000..7bb9d925e9 --- /dev/null +++ b/src/events/events_block_comment_move.ts @@ -0,0 +1,84 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import { + BlockCommentBase, + BlockCommentBaseJson, +} from "./events_block_comment_base"; +import type { ScratchCommentBubble } from "../scratch_comment_bubble"; + +class BlockCommentMove extends BlockCommentBase { + oldCoordinate_: Blockly.utils.Coordinate; + newCoordinate_: Blockly.utils.Coordinate; + + constructor( + opt_blockComment?: ScratchCommentBubble, + oldCoordinate?: Blockly.utils.Coordinate, + newCoordinate?: Blockly.utils.Coordinate + ) { + super(opt_blockComment); + this.type = "block_comment_move"; + this.oldCoordinate_ = oldCoordinate; + this.newCoordinate_ = newCoordinate; + } + + toJson(): BlockCommentMoveJson { + return { + ...super.toJson(), + newCoordinate: this.newCoordinate_, + oldCoordinate: this.oldCoordinate_, + }; + } + + static fromJson( + json: BlockCommentMoveJson, + workspace: Blockly.Workspace, + event?: any + ): BlockCommentMove { + const newEvent = super.fromJson( + json, + workspace, + event ?? new BlockCommentMove() + ) as BlockCommentMove; + newEvent.newCoordinate_ = new Blockly.utils.Coordinate( + json["newCoordinate"]["x"], + json["newCoordinate"]["y"] + ); + newEvent.oldCoordinate_ = new Blockly.utils.Coordinate( + json["oldCoordinate"]["x"], + json["oldCoordinate"]["y"] + ); + + return newEvent; + } + + run(forward: boolean) { + const workspace = this.getEventWorkspace_(); + const block = workspace?.getBlockById(this.blockId); + const comment = block?.getIcon(Blockly.icons.IconType.COMMENT); + comment?.setBubbleLocation( + forward ? this.newCoordinate_ : this.oldCoordinate_ + ); + } +} + +interface BlockCommentMoveJson extends BlockCommentBaseJson { + newCoordinate: { + x: number; + y: number; + }; + oldCoordinate: { + x: number; + y: number; + }; +} + +Blockly.registry.register( + Blockly.registry.Type.EVENT, + "block_comment_move", + BlockCommentMove +); diff --git a/src/events/events_block_comment_resize.ts b/src/events/events_block_comment_resize.ts new file mode 100644 index 0000000000..9dfd159231 --- /dev/null +++ b/src/events/events_block_comment_resize.ts @@ -0,0 +1,88 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import { + BlockCommentBase, + BlockCommentBaseJson, +} from "./events_block_comment_base"; +import type { ScratchCommentBubble } from "../scratch_comment_bubble"; + +class BlockCommentResize extends BlockCommentBase { + oldSize: Blockly.utils.Size; + newSize: Blockly.utils.Size; + + constructor( + opt_blockComment?: ScratchCommentBubble, + oldSize?: Blockly.utils.Size, + newSize?: Blockly.utils.Size + ) { + super(opt_blockComment); + this.type = "block_comment_resize"; + this.oldSize = oldSize; + this.newSize = newSize; + } + + toJson(): BlockCommentResizeJson { + return { + ...super.toJson(), + newSize: { + width: this.newSize.width, + height: this.newSize.height, + }, + oldSize: { + width: this.oldSize.width, + height: this.oldSize.height, + }, + }; + } + + static fromJson( + json: BlockCommentResizeJson, + workspace: Blockly.Workspace, + event?: any + ): BlockCommentResize { + const newEvent = super.fromJson( + json, + workspace, + event ?? new BlockCommentResize() + ) as BlockCommentResize; + newEvent.newSize = new Blockly.utils.Size( + json["newSize"]["width"], + json["newSize"]["height"] + ); + newEvent.oldSize = new Blockly.utils.Size( + json["oldSize"]["width"], + json["oldSize"]["height"] + ); + + return newEvent; + } + + run(forward: boolean) { + const workspace = this.getEventWorkspace_(); + const block = workspace?.getBlockById(this.blockId); + const comment = block?.getIcon(Blockly.icons.IconType.COMMENT); + comment?.setBubbleSize(forward ? this.newSize : this.oldSize); + } +} + +interface BlockCommentResizeJson extends BlockCommentBaseJson { + newSize: { + width: number; + height: number; + }; + oldSize: { + width: number; + height: number; + }; +} + +Blockly.registry.register( + Blockly.registry.Type.EVENT, + "block_comment_resize", + BlockCommentResize +); diff --git a/src/events/events_block_drag_end.ts b/src/events/events_block_drag_end.ts new file mode 100644 index 0000000000..34e2c410aa --- /dev/null +++ b/src/events/events_block_drag_end.ts @@ -0,0 +1,49 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; + +export class BlockDragEnd extends Blockly.Events.BlockBase { + isOutside: boolean; + xml: Element | DocumentFragment; + + constructor(block?: Blockly.Block, isOutside?: boolean) { + super(block); + this.type = "endDrag"; + this.isOutside = isOutside; + this.recordUndo = false; + this.xml = Blockly.Xml.blockToDom(block, true); + } + + toJson(): BlockDragEndJson { + return { + ...super.toJson(), + isOutside: this.isOutside, + xml: Blockly.utils.xml.domToText(this.xml), + }; + } + + static fromJson( + json: BlockDragEndJson, + workspace: Blockly.Workspace, + event?: any + ): BlockDragEnd { + const newEvent = super.fromJson( + json, + workspace, + event ?? new BlockDragEnd() + ) as BlockDragEnd; + newEvent.isOutside = json["isOutside"]; + newEvent.xml = Blockly.utils.xml.textToDom(json["xml"]); + + return newEvent; + } +} + +interface BlockDragEndJson extends Blockly.Events.BlockBaseJson { + isOutside: boolean; + xml: string; +} diff --git a/src/events/events_block_drag_outside.ts b/src/events/events_block_drag_outside.ts new file mode 100644 index 0000000000..7f64722d72 --- /dev/null +++ b/src/events/events_block_drag_outside.ts @@ -0,0 +1,44 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; + +export class BlockDragOutside extends Blockly.Events.BlockBase { + isOutside: boolean; + + constructor(block?: Blockly.Block, isOutside?: boolean) { + super(block); + this.type = "dragOutside"; + this.isOutside = isOutside; + this.recordUndo = false; + } + + toJson(): BlockDragOutsideJson { + return { + ...super.toJson(), + isOutside: this.isOutside, + }; + } + + static fromJson( + json: BlockDragOutsideJson, + workspace: Blockly.Workspace, + event?: any + ): BlockDragOutside { + const newEvent = super.fromJson( + json, + workspace, + event ?? new BlockDragOutside() + ) as BlockDragOutside; + newEvent.isOutside = json["isOutside"]; + + return newEvent; + } +} + +interface BlockDragOutsideJson extends Blockly.Events.BlockBaseJson { + isOutside: boolean; +} diff --git a/src/events/events_scratch_variable_create.ts b/src/events/events_scratch_variable_create.ts new file mode 100644 index 0000000000..1f67e79359 --- /dev/null +++ b/src/events/events_scratch_variable_create.ts @@ -0,0 +1,80 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import { ScratchVariableModel } from "../scratch_variable_model"; + +class ScratchVariableCreate extends Blockly.Events.VarCreate { + isLocal: boolean; + isCloud: boolean; + + constructor(variable?: ScratchVariableModel) { + super(variable); + if (!variable) return; + + this.isLocal = variable.isLocal; + this.isCloud = variable.isCloud; + } + + toJson(): ScratchVariableCreateJson { + return { + ...super.toJson(), + isLocal: this.isLocal, + isCloud: this.isCloud, + }; + } + + static fromJson( + json: ScratchVariableCreateJson, + workspace: Blockly.Workspace, + event?: any + ): ScratchVariableCreate { + const newEvent = super.fromJson( + json, + workspace, + event ?? new ScratchVariableCreate() + ) as ScratchVariableCreate; + newEvent.isLocal = json["isLocal"]; + newEvent.isCloud = json["isCloud"]; + return newEvent; + } + + run(forward: boolean) { + const workspace = this.getEventWorkspace_(); + const variableMap = workspace.getVariableMap(); + if (forward) { + const variable = new ScratchVariableModel( + workspace, + this.varName, + this.varType, + this.varId, + this.isLocal, + this.isCloud + ); + variableMap.addVariable(variable); + Blockly.Events.fire( + new (Blockly.Events.get(Blockly.Events.VAR_CREATE))(variable) + ); + } else { + const variable = variableMap.getVariableById(this.varId); + if (variable) { + variableMap.deleteVariable(variable); + } + } + } +} + +interface ScratchVariableCreateJson extends Blockly.Events.VarCreateJson { + isCloud: boolean; + isLocal: boolean; +} + +Blockly.registry.register( + Blockly.registry.Type.EVENT, + Blockly.Events.VAR_CREATE, + ScratchVariableCreate, + true +); diff --git a/src/fields/field_colour_slider.ts b/src/fields/field_colour_slider.ts new file mode 100644 index 0000000000..4b4966e5ec --- /dev/null +++ b/src/fields/field_colour_slider.ts @@ -0,0 +1,399 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2012 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Colour input field. + * @author fraser@google.com (Neil Fraser) + */ +import * as Blockly from "blockly/core"; +import { FieldColour, FieldColourFromJsonConfig } from "@blockly/field-colour"; + +enum ColourChannel { + HUE = "hue", + SATURATION = "saturation", + BRIGHTNESS = "brightness", +} + +/** + * Class for a slider-based colour input field. + */ +export class FieldColourSlider extends FieldColour { + /** + * Function to be called if eyedropper can be activated. + * If defined, an eyedropper button will be added to the color picker. + * The button calls this function with a callback to update the field value. + * BEWARE: This is not a stable API. It may change. + */ + static activateEyedropper_: ( + callback: (colour: string) => void + ) => void | null = null; + + /** + * Path to the eyedropper svg icon. + */ + EYEDROPPER_PATH = "eyedropper.svg"; + SERIALIZABLE = true; + EDITABLE = true; + + private hueChangeEventKey_?: Blockly.browserEvents.Data; + private saturationChangeEventKey_?: Blockly.browserEvents.Data; + private brightnessChangeEventKey_?: Blockly.browserEvents.Data; + private hueSlider_?: HTMLInputElement; + private saturationSlider_?: HTMLInputElement; + private brightnessSlider_?: HTMLInputElement; + private hueReadout_?: Element; + private saturationReadout_?: Element; + private brightnessReadout_?: Element; + private hue_?: number; + private saturation_?: number; + private brightness_?: number; + private eyedropperEventData_?: Blockly.browserEvents.Data; + + /** + * Construct a FieldColourSlider from a JSON arg object. + * + * @param options A JSON object with options (colour). + * @returns The new field instance. + */ + static fromJson(options: FieldColourFromJsonConfig): FieldColourSlider { + return new FieldColourSlider(options["colour"]); + } + + doValueUpdate_(newValue: string) { + super.doValueUpdate_(newValue); + this.updateSliderHandles_(); + this.updateDom_(); + } + + /** + * Create the hue, saturation or value CSS gradient for the slide backgrounds. + * + * @param channel – Either "hue", "saturation" or "value". + * @return Array colour hex colour stops for the given channel + */ + private createColourStops_(channel: ColourChannel): string[] { + const stops = []; + for (let n = 0; n <= 360; n += 20) { + switch (channel) { + case ColourChannel.HUE: + stops.push( + Blockly.utils.colour.hsvToHex(n, this.saturation_, this.brightness_) + ); + break; + case ColourChannel.SATURATION: + stops.push( + Blockly.utils.colour.hsvToHex(this.hue_, n / 360, this.brightness_) + ); + break; + case ColourChannel.BRIGHTNESS: + stops.push( + Blockly.utils.colour.hsvToHex( + this.hue_, + this.saturation_, + (255 * n) / 360 + ) + ); + break; + default: + throw new Error("Unknown channel for colour sliders: " + channel); + } + } + return stops; + } + + /** + * Set the gradient CSS properties for the given node and channel + * + * @param node The DOM node the gradient will be set on. + * @param channel Either "hue", "saturation" or "value". + */ + private setGradient_(node: HTMLElement, channel: ColourChannel) { + const gradient = this.createColourStops_(channel).join(","); + node.style["background"] = `linear-gradient(to right, ${gradient})`; + } + + /** + * Update the readouts and slider backgrounds after value has changed. + */ + private updateDom_() { + if (this.hueSlider_) { + // Update the slider backgrounds + this.setGradient_(this.hueSlider_, ColourChannel.HUE); + this.setGradient_(this.saturationSlider_, ColourChannel.SATURATION); + this.setGradient_(this.brightnessSlider_, ColourChannel.BRIGHTNESS); + + // Update the readouts + this.hueReadout_.textContent = Math.floor( + (100 * this.hue_) / 360 + ).toFixed(0); + this.saturationReadout_.textContent = Math.floor( + 100 * this.saturation_ + ).toFixed(0); + this.brightnessReadout_.textContent = Math.floor( + (100 * this.brightness_) / 255 + ).toFixed(0); + } + } + + /** + * Update the slider handle positions from the current field value. + */ + private updateSliderHandles_() { + if (this.hueSlider_) { + this.hueSlider_.value = `${this.hue_}`; + this.saturationSlider_.value = `${this.saturation_}`; + this.brightnessSlider_.value = `${this.brightness_}`; + } + } + + /** + * Create label and readout DOM elements, returning the readout. + * + * @param labelText Text for the label + * @return The container node and the readout node. + */ + private createLabelDom_(labelText: string): Element[] { + const labelContainer = document.createElement("div"); + labelContainer.setAttribute("class", "scratchColourPickerLabel"); + const readout = document.createElement("span"); + readout.setAttribute("class", "scratchColourPickerReadout"); + const label = document.createElement("span"); + label.setAttribute("class", "scratchColourPickerLabelText"); + label.textContent = labelText; + labelContainer.appendChild(label); + labelContainer.appendChild(readout); + return [labelContainer, readout]; + } + + /** + * Factory for creating the different slider callbacks + * + * @param channel One of "hue", "saturation" or "brightness" + * @returns The callback for slider update + */ + private sliderCallbackFactory_( + channel: ColourChannel + ): (event: PointerEvent) => void { + return (event: PointerEvent) => { + const channelValue = (event.target as HTMLInputElement).value; + switch (channel) { + case ColourChannel.HUE: + this.hue_ = Number(channelValue); + break; + case ColourChannel.SATURATION: + this.saturation_ = Number(channelValue); + break; + case ColourChannel.BRIGHTNESS: + this.brightness_ = Number(channelValue); + break; + } + const colour = Blockly.utils.colour.hsvToHex( + this.hue_, + this.saturation_, + this.brightness_ + ); + if (colour !== null) { + this.setValue(colour, true); + } + }; + } + + /** + * Activate the eyedropper, passing in a callback for setting the field value. + */ + private activateEyedropperInternal_() { + FieldColourSlider.activateEyedropper_((chosenColour: string) => { + // Update the internal hue/saturation/brightness values so sliders update. + const components = Blockly.utils.colour.hexToRgb(chosenColour); + const { hue, saturation, value } = this.rgbToHsv( + components[0], + components[1], + components[2] + ); + this.hue_ = hue; + this.saturation_ = saturation; + this.brightness_ = value; + this.setValue(chosenColour); + }); + } + + /** + * Create hue, saturation and brightness sliders under the colour field. + */ + showEditor_() { + Blockly.DropDownDiv.hideWithoutAnimation(); + Blockly.DropDownDiv.clearContent(); + const div = Blockly.DropDownDiv.getContentDiv(); + div.className = "scratchColourPicker"; + + // Init color component values that are used while the editor is open + // in order to keep the slider values stable. + const components = Blockly.utils.colour.hexToRgb(this.getValue()); + const { hue, saturation, value } = this.rgbToHsv( + components[0], + components[1], + components[2] + ); + this.hue_ = hue; + this.saturation_ = saturation; + this.brightness_ = value; + + const hueElements = this.createLabelDom_(Blockly.Msg.COLOUR_HUE_LABEL); + div.appendChild(hueElements[0]); + this.hueReadout_ = hueElements[1]; + this.hueSlider_ = document.createElement("input"); + this.hueSlider_.type = "range"; + this.hueSlider_.min = "0"; + this.hueSlider_.max = "360"; + this.hueSlider_.className = "scratchColourSlider"; + div.appendChild(this.hueSlider_); + + const saturationElements = this.createLabelDom_( + Blockly.Msg.COLOUR_SATURATION_LABEL + ); + div.appendChild(saturationElements[0]); + this.saturationReadout_ = saturationElements[1]; + this.saturationSlider_ = document.createElement("input"); + this.saturationSlider_.type = "range"; + this.saturationSlider_.step = "0.001"; + this.saturationSlider_.min = "0"; + this.saturationSlider_.max = "1.0"; + this.saturationSlider_.className = "scratchColourSlider"; + div.appendChild(this.saturationSlider_); + + const brightnessElements = this.createLabelDom_( + Blockly.Msg.COLOUR_BRIGHTNESS_LABEL + ); + div.appendChild(brightnessElements[0]); + this.brightnessReadout_ = brightnessElements[1]; + this.brightnessSlider_ = document.createElement("input"); + this.brightnessSlider_.type = "range"; + this.brightnessSlider_.min = "0"; + this.brightnessSlider_.max = "255"; + this.brightnessSlider_.className = "scratchColourSlider"; + div.appendChild(this.brightnessSlider_); + + if (FieldColourSlider.activateEyedropper_) { + const button = document.createElement("button"); + button.setAttribute("class", "scratchEyedropper"); + const image = document.createElement("img"); + image.src = + Blockly.getMainWorkspace().options.pathToMedia + this.EYEDROPPER_PATH; + button.appendChild(image); + div.appendChild(button); + this.eyedropperEventData_ = Blockly.browserEvents.conditionalBind( + button, + "click", + this, + this.activateEyedropperInternal_ + ); + } + + Blockly.DropDownDiv.setColour("#ffffff", "#dddddd"); + Blockly.DropDownDiv.showPositionedByBlock( + this, + this.getSourceBlock() as Blockly.BlockSvg + ); + + // Set value updates the slider positions + // Do this before attaching callbacks to avoid extra events from initial set + this.setValue(this.getValue()); + + this.hueChangeEventKey_ = Blockly.browserEvents.bind( + this.hueSlider_, + "input", + this, + this.sliderCallbackFactory_(ColourChannel.HUE) + ); + this.saturationChangeEventKey_ = Blockly.browserEvents.bind( + this.saturationSlider_, + "input", + this, + this.sliderCallbackFactory_(ColourChannel.SATURATION) + ); + this.brightnessChangeEventKey_ = Blockly.browserEvents.bind( + this.brightnessSlider_, + "input", + this, + this.sliderCallbackFactory_(ColourChannel.BRIGHTNESS) + ); + } + + dispose() { + if (this.hueChangeEventKey_) { + Blockly.browserEvents.unbind(this.hueChangeEventKey_); + } + if (this.saturationChangeEventKey_) { + Blockly.browserEvents.unbind(this.saturationChangeEventKey_); + } + if (this.brightnessChangeEventKey_) { + Blockly.browserEvents.unbind(this.brightnessChangeEventKey_); + } + if (this.eyedropperEventData_) { + Blockly.browserEvents.unbind(this.eyedropperEventData_); + } + Blockly.Events.setGroup(false); + super.dispose(); + } + + // From Closure + rgbToHsv( + red: number, + green: number, + blue: number + ): { hue: number; saturation: number; value: number } { + const max = Math.max(Math.max(red, green), blue); + const min = Math.min(Math.min(red, green), blue); + let hue; + let saturation; + const value = max; + if (min == max) { + hue = 0; + saturation = 0; + } else { + const delta = max - min; + saturation = delta / max; + + if (red == max) { + hue = (green - blue) / delta; + } else if (green == max) { + hue = 2 + (blue - red) / delta; + } else { + hue = 4 + (red - green) / delta; + } + hue *= 60; + if (hue < 0) { + hue += 360; + } + if (hue > 360) { + hue -= 360; + } + } + + return { hue, saturation, value }; + } +} + +/** + * Register the field and any dependencies. + */ +export function registerFieldColourSlider() { + Blockly.fieldRegistry.register("field_colour_slider", FieldColourSlider); +} diff --git a/src/fields/field_matrix.ts b/src/fields/field_matrix.ts new file mode 100644 index 0000000000..ad6afe5f37 --- /dev/null +++ b/src/fields/field_matrix.ts @@ -0,0 +1,619 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Massachusetts Institute of Technology + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview 5x5 matrix input field. + * Displays an editable 5x5 matrix for controlling LED arrays. + * @author khanning@gmail.com (Kreg Hanning) + */ +import * as Blockly from "blockly/core"; + +enum PaintStyle { + FILL = "fill", + CLEAR = "clear", +} + +enum LEDState { + ON = "1", + OFF = "0", +} + +/** + * Class for a matrix field. + */ +class FieldMatrix extends Blockly.Field { + private originalStyle?: string; + + /** + * Array of SVGElement for matrix thumbnail image on block field. + */ + private ledThumbNodes_: SVGElement[] = []; + /** + * Array of SVGElement for matrix editor in dropdown menu. + */ + private ledButtons_: SVGElement[] = []; + + /** + * SVGElement for LED matrix in editor. + */ + private matrixStage_: SVGElement | null = null; + + /** + * SVG image for dropdown arrow. + */ + private arrow_: SVGElement | null = null; + + /** + * String indicating matrix paint style. + * value can be [null, 'fill', 'clear']. + */ + private paintStyle_: PaintStyle | null = null; + + /** + * Touch event wrapper. + * Runs when the field is selected. + */ + private mouseDownWrapper_: Blockly.browserEvents.Data | null = null; + + /** + * Touch event wrapper. + * Runs when the clear button editor button is selected. + */ + private clearButtonWrapper_: Blockly.browserEvents.Data | null = null; + + /** + * Touch event wrapper. + * Runs when the fill button editor button is selected. + */ + private fillButtonWrapper_: Blockly.browserEvents.Data | null = null; + + /** + * Touch event wrapper. + * Runs when the matrix editor is touched. + */ + private matrixTouchWrapper_: Blockly.browserEvents.Data | null = null; + + /** + * Touch event wrapper. + * Runs when the matrix editor touch event moves. + */ + private matrixMoveWrapper_: Blockly.browserEvents.Data | null = null; + + /** + * Touch event wrapper. + * Runs when the matrix editor is released. + */ + private matrixReleaseWrapper_: Blockly.browserEvents.Data | null = null; + + SERIALIZABLE = true; + + /** + * Construct a FieldMatrix from a JSON arg object. + * @param options A JSON object with options (matrix). + * @returns The new field instance. + */ + static fromJson(options: FieldMatrixConfig): FieldMatrix { + return new FieldMatrix(options["matrix"]); + } + + /** + * Fixed size of the matrix thumbnail in the input field, in px. + */ + static readonly THUMBNAIL_SIZE = 26; + + /** + * Fixed size of each matrix thumbnail node, in px. + */ + static readonly THUMBNAIL_NODE_SIZE = 4; + + /** + * Fixed size of each matrix thumbnail node, in px. + */ + static readonly THUMBNAIL_NODE_PAD = 1; + + /** + * Fixed size of arrow icon in drop down menu, in px. + */ + static readonly ARROW_SIZE = 12; + + /** + * Fixed size of each button inside the 5x5 matrix, in px. + */ + static readonly MATRIX_NODE_SIZE = 18; + + /** + * Fixed corner radius for 5x5 matrix buttons, in px. + */ + static readonly MATRIX_NODE_RADIUS = 4; + + /** + * Fixed padding for 5x5 matrix buttons, in px. + */ + static readonly MATRIX_NODE_PAD = 5; + + /** + * String with 25 '0' chars. + * Used for clearing a matrix or filling an LED node array. + */ + static readonly ZEROS = "0000000000000000000000000"; + + /** + * String with 25 '1' chars. + * Used for filling a matrix. + */ + static readonly ONES = "1111111111111111111111111"; + + /** + * Called when the field is placed on a block. + */ + initView() { + // Build the DOM. + this.updateSize_(); + const dropdownArrowPadding = + (this.getConstants() as Blockly.zelos.ConstantProvider).GRID_UNIT * 2; + const thumbX = dropdownArrowPadding / 2; + const thumbY = (this.size_.height - FieldMatrix.THUMBNAIL_SIZE) / 2; + const thumbnail = Blockly.utils.dom.createSvgElement( + "g", + { + transform: "translate(" + thumbX + ", " + thumbY + ")", + "pointer-events": "bounding-box", + cursor: "pointer", + }, + this.fieldGroup_ + ); + this.ledThumbNodes_ = []; + const nodeSize = FieldMatrix.THUMBNAIL_NODE_SIZE; + const nodePad = FieldMatrix.THUMBNAIL_NODE_PAD; + for (let i = 0; i < 5; i++) { + for (let n = 0; n < 5; n++) { + const attr = { + x: (nodeSize + nodePad) * n + nodePad, + y: (nodeSize + nodePad) * i + nodePad, + width: nodeSize, + height: nodeSize, + rx: nodePad, + ry: nodePad, + }; + this.ledThumbNodes_.push( + Blockly.utils.dom.createSvgElement("rect", attr, thumbnail) + ); + } + thumbnail.style.cursor = "default"; + this.updateMatrix_(); + } + + if (!this.arrow_) { + const arrowX = FieldMatrix.THUMBNAIL_SIZE + dropdownArrowPadding * 1.5; + const arrowY = (this.size_.height - FieldMatrix.ARROW_SIZE) / 2; + this.arrow_ = Blockly.utils.dom.createSvgElement( + "image", + { + height: FieldMatrix.ARROW_SIZE + "px", + width: FieldMatrix.ARROW_SIZE + "px", + transform: "translate(" + arrowX + ", " + arrowY + ")", + }, + this.fieldGroup_ + ); + this.arrow_.setAttributeNS( + "http://www.w3.org/1999/xlink", + "xlink:href", + this.getConstants().FIELD_DROPDOWN_SVG_ARROW_DATAURI + ); + this.arrow_.style.cursor = "default"; + } + } + + doClassValidation_(matrix: string) { + return matrix + ? matrix + FieldMatrix.ZEROS.substr(0, 25 - matrix.length) + : matrix; + } + + doValueUpdate_(newValue: string) { + super.doValueUpdate_(newValue); + if (newValue) { + this.updateMatrix_(); + } + } + + /** + * Show the drop-down menu for editing this field. + */ + showEditor_() { + const sourceBlock = this.getSourceBlock() as Blockly.BlockSvg; + Blockly.DropDownDiv.setColour( + sourceBlock.getColour(), + sourceBlock.getColourTertiary() + ); + + const style = sourceBlock.style; + if (sourceBlock.isShadow()) { + this.originalStyle = sourceBlock.getStyleName(); + sourceBlock.setStyle(`${this.originalStyle}_selected`); + } else if (this.borderRect_) { + this.borderRect_.setAttribute( + "fill", + "colourQuaternary" in style + ? `${style.colourQuaternary}` + : style.colourTertiary + ); + } + + const div = Blockly.DropDownDiv.getContentDiv(); + // Build the SVG DOM. + const matrixSize = + FieldMatrix.MATRIX_NODE_SIZE * 5 + FieldMatrix.MATRIX_NODE_PAD * 6; + this.matrixStage_ = Blockly.utils.dom.createSvgElement( + "svg", + { + xmlns: "http://www.w3.org/2000/svg", + "xmlns:html": "http://www.w3.org/1999/xhtml", + "xmlns:xlink": "http://www.w3.org/1999/xlink", + version: "1.1", + height: matrixSize + "px", + width: matrixSize + "px", + }, + div + ); + // Create the 5x5 matrix + this.ledButtons_ = []; + for (let i = 0; i < 5; i++) { + for (let n = 0; n < 5; n++) { + const x = + FieldMatrix.MATRIX_NODE_SIZE * n + + FieldMatrix.MATRIX_NODE_PAD * (n + 1); + const y = + FieldMatrix.MATRIX_NODE_SIZE * i + + FieldMatrix.MATRIX_NODE_PAD * (i + 1); + const attr = { + x: x + "px", + y: y + "px", + width: FieldMatrix.MATRIX_NODE_SIZE, + height: FieldMatrix.MATRIX_NODE_SIZE, + rx: FieldMatrix.MATRIX_NODE_RADIUS, + ry: FieldMatrix.MATRIX_NODE_RADIUS, + }; + const led = Blockly.utils.dom.createSvgElement( + "rect", + attr, + this.matrixStage_ + ); + this.matrixStage_.appendChild(led); + this.ledButtons_.push(led); + } + } + // Div for lower button menu + const buttonDiv = document.createElement("div"); + // Button to clear matrix + const clearButtonDiv = document.createElement("div"); + clearButtonDiv.className = "scratchMatrixButtonDiv"; + + const clearButton = this.createButton_(sourceBlock.getColourSecondary()); + clearButtonDiv.appendChild(clearButton); + // Button to fill matrix + const fillButtonDiv = document.createElement("div"); + fillButtonDiv.className = "scratchMatrixButtonDiv"; + const fillButton = this.createButton_("var(--colour-text)"); + fillButtonDiv.appendChild(fillButton); + + buttonDiv.appendChild(clearButtonDiv); + buttonDiv.appendChild(fillButtonDiv); + div.appendChild(buttonDiv); + + Blockly.DropDownDiv.showPositionedByBlock( + this, + sourceBlock, + this.dropdownDispose_.bind(this) + ); + + this.matrixTouchWrapper_ = Blockly.browserEvents.bind( + this.matrixStage_, + "mousedown", + this, + this.onMouseDown + ); + this.clearButtonWrapper_ = Blockly.browserEvents.bind( + clearButton, + "click", + this, + this.clearMatrix_ + ); + this.fillButtonWrapper_ = Blockly.browserEvents.bind( + fillButton, + "click", + this, + this.fillMatrix_ + ); + + // Update the matrix for the current value + this.updateMatrix_(); + } + + dropdownDispose_() { + const sourceBlock = this.getSourceBlock(); + if (sourceBlock.isShadow()) { + sourceBlock.setStyle(this.originalStyle); + } + this.updateMatrix_(); + } + + /** + * Make an svg object that resembles a 3x3 matrix to be used as a button. + * + * @param fill The color to fill the matrix nodes. + * @returns The button svg element. + */ + createButton_(fill: string): SVGElement { + const button = Blockly.utils.dom.createSvgElement("svg", { + xmlns: "http://www.w3.org/2000/svg", + "xmlns:html": "http://www.w3.org/1999/xhtml", + "xmlns:xlink": "http://www.w3.org/1999/xlink", + version: "1.1", + height: FieldMatrix.MATRIX_NODE_SIZE + "px", + width: FieldMatrix.MATRIX_NODE_SIZE + "px", + }); + const nodeSize = FieldMatrix.MATRIX_NODE_SIZE / 4; + const nodePad = FieldMatrix.MATRIX_NODE_SIZE / 16; + for (let i = 0; i < 3; i++) { + for (let n = 0; n < 3; n++) { + Blockly.utils.dom.createSvgElement( + "rect", + { + x: (nodeSize + nodePad) * n + nodePad, + y: (nodeSize + nodePad) * i + nodePad, + width: nodeSize, + height: nodeSize, + rx: nodePad, + ry: nodePad, + fill: fill, + }, + button + ); + } + } + return button; + } + + /** + * Redraw the matrix with the current value. + */ + private updateMatrix_() { + const matrix = this.getValue(); + const sourceBlock = this.getSourceBlock() as Blockly.BlockSvg; + for (let i = 0; i < matrix.length; i++) { + if (matrix[i] === LEDState.OFF) { + this.fillMatrixNode_( + this.ledButtons_, + i, + sourceBlock.getColourTertiary() + ); + this.fillMatrixNode_( + this.ledThumbNodes_, + i, + sourceBlock.getColourSecondary() + ); + } else { + this.fillMatrixNode_(this.ledButtons_, i, "var(--colour-text)"); + this.fillMatrixNode_(this.ledThumbNodes_, i, "var(--colour-text)"); + } + } + } + + /** + * Clear the matrix. + * + * @param e Mouse event. + */ + clearMatrix_(e: PointerEvent) { + if (e.button != 0) return; + this.setValue(FieldMatrix.ZEROS); + } + + /** + * Fill the matrix. + * + * @param e Mouse event. + */ + fillMatrix_(e: PointerEvent) { + if (e.button != 0) return; + this.setValue(FieldMatrix.ONES); + } + + /** + * Fill matrix node with specified colour. + * + * @param node The array of matrix nodes. + * @param index The index of the matrix node. + * @param fill The fill colour in '#rrggbb' format. + */ + fillMatrixNode_(node: SVGElement[], index: number, fill: string) { + if (!node || !node[index] || !fill) return; + node[index].setAttribute("fill", fill); + } + + setLEDNode_(led: number, state: LEDState) { + if (led < 0 || led > 24) return; + const oldMatrix = this.getValue(); + const newMatrix = + oldMatrix.substr(0, led) + state + oldMatrix.substr(led + 1); + this.setValue(newMatrix); + } + + fillLEDNode_(led: number) { + if (led < 0 || led > 24) return; + this.setLEDNode_(led, LEDState.ON); + } + + clearLEDNode_(led: number) { + if (led < 0 || led > 24) return; + this.setLEDNode_(led, LEDState.OFF); + } + + toggleLEDNode_(led: number) { + if (led < 0 || led > 24) return; + if (this.getValue().charAt(led) === LEDState.OFF) { + this.setLEDNode_(led, LEDState.ON); + } else { + this.setLEDNode_(led, LEDState.OFF); + } + } + + /** + * Toggle matrix nodes on and off. + * + * @param e Mouse event. + */ + onMouseDown(e: PointerEvent) { + this.matrixMoveWrapper_ = Blockly.browserEvents.bind( + document.body, + "mousemove", + this, + this.onMouseMove + ); + this.matrixReleaseWrapper_ = Blockly.browserEvents.bind( + document.body, + "mouseup", + this, + this.onMouseUp + ); + const ledHit = this.checkForLED_(e); + if (ledHit > -1) { + if (this.getValue().charAt(ledHit) === LEDState.OFF) { + this.paintStyle_ = PaintStyle.FILL; + } else { + this.paintStyle_ = PaintStyle.CLEAR; + } + this.toggleLEDNode_(ledHit); + this.updateMatrix_(); + } else { + this.paintStyle_ = null; + } + } + + /** + * Unbind mouse move event and clear the paint style. + */ + onMouseUp() { + if (this.matrixMoveWrapper_) { + Blockly.browserEvents.unbind(this.matrixMoveWrapper_); + this.matrixMoveWrapper_ = null; + } + if (this.matrixReleaseWrapper_) { + Blockly.browserEvents.unbind(this.matrixReleaseWrapper_); + this.matrixReleaseWrapper_ = null; + } + this.paintStyle_ = null; + } + + /** + * Toggle matrix nodes on and off by dragging mouse. + * + * @param e Mouse move event. + */ + onMouseMove(e: PointerEvent) { + e.preventDefault(); + if (this.paintStyle_) { + const led = this.checkForLED_(e); + if (led < 0) return; + if (this.paintStyle_ === PaintStyle.CLEAR) { + this.clearLEDNode_(led); + } else if (this.paintStyle_ === PaintStyle.FILL) { + this.fillLEDNode_(led); + } + } + } + + /** + * Check if mouse coordinates collide with a matrix node. + * + * @param e Mouse move event. + * @returns The matching matrix node or -1 for none. + */ + checkForLED_(e: PointerEvent): number { + const bBox = this.matrixStage_.getBoundingClientRect(); + const nodeSize = FieldMatrix.MATRIX_NODE_SIZE; + const nodePad = FieldMatrix.MATRIX_NODE_PAD; + const dx = e.clientX - bBox.left; + const dy = e.clientY - bBox.top; + const min = nodePad / 2; + const max = bBox.width - nodePad / 2; + if (dx < min || dx > max || dy < min || dy > max) { + return -1; + } + const xDiv = Math.trunc((dx - nodePad / 2) / (nodeSize + nodePad)); + const yDiv = Math.trunc((dy - nodePad / 2) / (nodeSize + nodePad)); + return xDiv + yDiv * nodePad; + } + + /** + * Clean up this FieldMatrix, as well as the inherited Field. + */ + dispose() { + super.dispose(); + this.matrixStage_ = null; + if (this.mouseDownWrapper_) { + Blockly.browserEvents.unbind(this.mouseDownWrapper_); + } + if (this.matrixTouchWrapper_) { + Blockly.browserEvents.unbind(this.matrixTouchWrapper_); + } + if (this.matrixReleaseWrapper_) { + Blockly.browserEvents.unbind(this.matrixReleaseWrapper_); + } + if (this.matrixMoveWrapper_) { + Blockly.browserEvents.unbind(this.matrixMoveWrapper_); + } + if (this.clearButtonWrapper_) { + Blockly.browserEvents.unbind(this.clearButtonWrapper_); + } + if (this.fillButtonWrapper_) { + Blockly.browserEvents.unbind(this.fillButtonWrapper_); + } + } + + updateSize_() { + const constants = this.getConstants() as Blockly.zelos.ConstantProvider; + let totalHeight = constants.FIELD_TEXT_HEIGHT; + + this.size_.height = totalHeight; + this.size_.width = + FieldMatrix.THUMBNAIL_SIZE + + FieldMatrix.ARROW_SIZE + + constants.GRID_UNIT * 2 * 1.5; + + this.positionBorderRect_(); + } + + getClickTarget_() { + return (this.getSourceBlock() as Blockly.BlockSvg).getSvgRoot(); + } +} + +interface FieldMatrixConfig extends Blockly.FieldConfig { + matrix: string; +} + +/** + * Register the field and any dependencies. + */ +export function registerFieldMatrix() { + Blockly.fieldRegistry.register("field_matrix", FieldMatrix); +} diff --git a/src/fields/field_note.ts b/src/fields/field_note.ts new file mode 100644 index 0000000000..67fbfbcdd8 --- /dev/null +++ b/src/fields/field_note.ts @@ -0,0 +1,878 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2018 Massachusetts Institute of Technology + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Note input field, for selecting a musical note on a piano. + * @author ericr@media.mit.edu (Eric Rosenbaum) + */ +import * as Blockly from "blockly/core"; + +/** + * Class for a note input field, for selecting a musical note on a piano. + * @param {(string|number)=} opt_value The initial content of the field. The + * value should cast to a number, and if it does not, '0' will be used. + * @param {Function=} opt_validator An optional function that is called + * to validate any constraints on what the user entered. Takes the new + * text as an argument and returns the accepted text or null to abort + * the change. + * @extends {Blockly.FieldTextInput} + * @constructor + */ +export class FieldNote extends Blockly.FieldTextInput { + /** + * Width of the field. Computed when drawing it, and used for animation. + */ + private fieldEditorWidth_ = 0; + + /** + * Height of the field. Computed when drawing it. + */ + private fieldEditorHeight_ = 0; + + /** + * The piano SVG. + */ + private pianoSVG_: SVGElement | null = null; + + /** + * Array of SVG elements representing the clickable piano keys. + */ + private keySVGs_: SVGElement[] = []; + + /** + * Note name indicator at the top of the field. + */ + private noteNameText_: SVGElement | null = null; + + /** + * Note name indicator on the low C key. + */ + private lowCText_: SVGElement | null = null; + + /** + * Note name indicator on the low C key. + */ + private highCText_: SVGElement | null = null; + + /** + * Octave number of the currently displayed range of keys. + */ + private displayedOctave_: number | null = null; + + /** + * Current animation position of the piano SVG, as it shifts left or right to + * change octaves. + */ + private animationPos_ = 0; + + /** + * Target position for the animation as the piano SVG shifts left or right. + */ + private animationTarget_ = 0; + + /** + * A flag indicating that the mouse is currently down. Used in combination with + * mouse enter events to update the key selection while dragging. + */ + private mouseIsDown_ = false; + + /** + * An array of wrappers for mouse down events on piano keys. + */ + private mouseDownWrappers_: Blockly.browserEvents.Data[] = []; + + /** + * A wrapper for the mouse up event. + */ + private mouseUpWrapper_: Blockly.browserEvents.Data | null = null; + + /** + * An array of wrappers for mouse enter events on piano keys. + */ + private mouseEnterWrappers_: Blockly.browserEvents.Data[] = []; + + /** + * A wrapper for the mouse down event on the octave down button. + */ + private octaveDownMouseDownWrapper_: Blockly.browserEvents.Data | null = null; + + /** + * A wrapper for the mouse down event on the octave up button. + */ + private octaveUpMouseDownWrapper_: Blockly.browserEvents.Data | null = null; + + /** + * Inset in pixels of content displayed in the field, caused by parent properties. + * The inset is actually determined by the CSS property blocklyDropDownDiv- it is + * the sum of the padding and border thickness. + */ + static INSET = 5; + + /** + * Height of the top area of the field, in px. + */ + static readonly TOP_MENU_HEIGHT = 32 - FieldNote.INSET; + + /** + * Padding on the top and sides of the field, in px. + */ + static readonly EDGE_PADDING = 1; + + /** + * Height of the drop shadow on the piano, in px. + */ + static readonly SHADOW_HEIGHT = 4; + + /** + * Color for the shadow on the piano. + */ + static readonly SHADOW_COLOR = "#000"; + + /** + * Opacity for the shadow on the piano. + */ + static readonly SHADOW_OPACITY = 0.2; + + /** + * A color for the white piano keys. + */ + static readonly WHITE_KEY_COLOR = "#FFFFFF"; + + /** + * A color for the black piano keys. + */ + static readonly BLACK_KEY_COLOR = "#323133"; + + /** + * A color for stroke around black piano keys. + */ + static readonly BLACK_KEY_STROKE = "#555555"; + + /** + * A color for the selected state of a piano key. + */ + static readonly KEY_SELECTED_COLOR = "#b0d6ff"; + + /** + * The number of white keys in one octave on the piano. + */ + static readonly NUM_WHITE_KEYS = 8; + + /** + * Height of a white piano key, in px. + */ + static readonly WHITE_KEY_HEIGHT = 72; + + /** + * Width of a white piano key, in px. + */ + static readonly WHITE_KEY_WIDTH = 40; + + /** + * Height of a black piano key, in px. + */ + static readonly BLACK_KEY_HEIGHT = 40; + + /** + * Width of a black piano key, in px. + */ + static readonly BLACK_KEY_WIDTH = 32; + + /** + * Radius of the curved bottom corner of a piano key, in px. + */ + static readonly KEY_RADIUS = 6; + + /** + * Bottom padding for the labels on C keys. + */ + static readonly KEY_LABEL_PADDING = 8; + + /** + * An array of objects with data describing the keys on the piano. + */ + static readonly KEY_INFO = [ + { name: "C", pitch: 0 }, + { name: "C♯", pitch: 1, isBlack: true }, + { name: "D", pitch: 2 }, + { name: "E♭", pitch: 3, isBlack: true }, + { name: "E", pitch: 4 }, + { name: "F", pitch: 5 }, + { name: "F♯", pitch: 6, isBlack: true }, + { name: "G", pitch: 7 }, + { name: "G♯", pitch: 8, isBlack: true }, + { name: "A", pitch: 9 }, + { name: "B♭", pitch: 10, isBlack: true }, + { name: "B", pitch: 11 }, + { name: "C", pitch: 12 }, + ]; + + /** + * The MIDI note number of the highest note selectable on the piano. + */ + static readonly MAX_NOTE = 130; + + /** + * The fraction of the distance to the target location to move the piano at each + * step of the animation. + */ + static readonly ANIMATION_FRACTION = 0.2; + + /** + * Path to the arrow svg icon, used on the octave buttons. + */ + static readonly ARROW_SVG_PATH = "icons/arrow_button.svg"; + + /** + * The size of the square octave buttons. + */ + static readonly OCTAVE_BUTTON_SIZE = 32; + + /** + * Construct a FieldNote from a JSON arg object. + * + * @param options A JSON object with options. + * @returns The new field instance. + */ + static fromJson(options: FieldNoteJsonConfig): FieldNote { + return new FieldNote(options["note"]); + } + + /** + * Clean up this FieldNote, as well as the inherited FieldTextInput. + */ + dispose() { + super.dispose(); + this.mouseDownWrappers_.forEach(function (wrapper) { + Blockly.browserEvents.unbind(wrapper); + }); + this.mouseEnterWrappers_.forEach(function (wrapper) { + Blockly.browserEvents.unbind(wrapper); + }); + if (this.mouseUpWrapper_) { + Blockly.browserEvents.unbind(this.mouseUpWrapper_); + } + if (this.octaveDownMouseDownWrapper_) { + Blockly.browserEvents.unbind(this.octaveDownMouseDownWrapper_); + } + if (this.octaveUpMouseDownWrapper_) { + Blockly.browserEvents.unbind(this.octaveUpMouseDownWrapper_); + } + this.pianoSVG_ = null; + this.keySVGs_.length = 0; + this.noteNameText_ = null; + this.lowCText_ = null; + this.highCText_ = null; + } + + /** + * Show a field with piano keys. + */ + showEditor_(event: PointerEvent, quietInput = false) { + super.showEditor_(event, quietInput, false); + + // Build the SVG DOM. + const div = Blockly.DropDownDiv.getContentDiv(); + + this.fieldEditorWidth_ = + FieldNote.NUM_WHITE_KEYS * FieldNote.WHITE_KEY_WIDTH + + FieldNote.EDGE_PADDING; + this.fieldEditorHeight_ = + FieldNote.TOP_MENU_HEIGHT + + FieldNote.WHITE_KEY_HEIGHT + + FieldNote.EDGE_PADDING; + + const svg = Blockly.utils.dom.createSvgElement( + "svg", + { + xmlns: "http://www.w3.org/2000/svg", + "xmlns:html": "http://www.w3.org/1999/xhtml", + "xmlns:xlink": "http://www.w3.org/1999/xlink", + version: "1.1", + height: this.fieldEditorHeight_ + "px", + width: this.fieldEditorWidth_ + "px", + }, + div + ); + + // Add the white and black keys + // Since we are adding the keys from left to right in order, they need + // to be in two groups in order to layer correctly. + this.pianoSVG_ = Blockly.utils.dom.createSvgElement("g", {}, svg); + const whiteKeyGroup = Blockly.utils.dom.createSvgElement( + "g", + {}, + this.pianoSVG_ + ); + const blackKeyGroup = Blockly.utils.dom.createSvgElement( + "g", + {}, + this.pianoSVG_ + ); + + // Add three piano octaves, so we can animate moving up or down an octave. + // Only the middle octave gets bound to events. + this.keySVGs_ = []; + this.addPianoOctave_( + -this.fieldEditorWidth_ + FieldNote.EDGE_PADDING, + whiteKeyGroup, + blackKeyGroup, + null + ); + this.addPianoOctave_(0, whiteKeyGroup, blackKeyGroup, this.keySVGs_); + this.addPianoOctave_( + this.fieldEditorWidth_ - FieldNote.EDGE_PADDING, + whiteKeyGroup, + blackKeyGroup, + null + ); + + // Note name indicator at the top of the field + this.noteNameText_ = Blockly.utils.dom.createSvgElement( + "text", + { + x: this.fieldEditorWidth_ / 2, + y: FieldNote.TOP_MENU_HEIGHT / 2, + class: "blocklyText", + "text-anchor": "middle", + "dominant-baseline": "middle", + }, + svg + ); + + // Note names on the low and high C keys + const lowCX = FieldNote.WHITE_KEY_WIDTH / 2; + this.lowCText_ = this.addCKeyLabel_(lowCX, svg); + const highCX = + lowCX + FieldNote.WHITE_KEY_WIDTH * (FieldNote.NUM_WHITE_KEYS - 1); + this.highCText_ = this.addCKeyLabel_(highCX, svg); + + // Horizontal line at the top of the keys + Blockly.utils.dom.createSvgElement( + "line", + { + stroke: ( + this.sourceBlock_.getParent() as Blockly.BlockSvg + ).getColourTertiary(), + x1: 0, + y1: FieldNote.TOP_MENU_HEIGHT, + x2: this.fieldEditorWidth_, + y2: FieldNote.TOP_MENU_HEIGHT, + }, + svg + ); + + // Drop shadow at the top of the keys + Blockly.utils.dom.createSvgElement( + "rect", + { + x: 0, + y: FieldNote.TOP_MENU_HEIGHT, + width: this.fieldEditorWidth_, + height: FieldNote.SHADOW_HEIGHT, + fill: FieldNote.SHADOW_COLOR, + "fill-opacity": FieldNote.SHADOW_OPACITY, + }, + svg + ); + + // Octave buttons + const octaveDownButton = this.addOctaveButton_(0, true, svg); + const octaveUpButton = this.addOctaveButton_( + this.fieldEditorWidth_ + + FieldNote.INSET * 2 - + FieldNote.OCTAVE_BUTTON_SIZE, + false, + svg + ); + + this.octaveDownMouseDownWrapper_ = Blockly.browserEvents.bind( + octaveDownButton, + "mousedown", + this, + function () { + this.changeOctaveBy_(-1); + } + ); + this.octaveUpMouseDownWrapper_ = Blockly.browserEvents.bind( + octaveUpButton, + "mousedown", + this, + function () { + this.changeOctaveBy_(1); + } + ); + const sourceBlock = this.getSourceBlock() as Blockly.BlockSvg; + Blockly.DropDownDiv.setColour( + sourceBlock.getParent().getColour(), + sourceBlock.getParent().getColourTertiary() + ); + Blockly.DropDownDiv.showPositionedByBlock(this, sourceBlock); + + this.updateSelection_(); + } + + /** + * Add one octave of piano keys drawn using SVG. + * + * @param x The x position of the left edge of this octave of keys. + * @param whiteKeyGroup The group for all white piano keys. + * @param blackKeyGroup The group for all black piano keys. + * @param keySVGarray An array containing all the key SVGs. + */ + private addPianoOctave_( + x: number, + whiteKeyGroup: SVGElement, + blackKeyGroup: SVGElement, + keySVGarray: SVGElement[] + ) { + let xIncrement, width, height, fill, stroke, group; + x += FieldNote.EDGE_PADDING / 2; + const y = FieldNote.TOP_MENU_HEIGHT; + for (var i = 0; i < FieldNote.KEY_INFO.length; i++) { + // Draw a black or white key + if (FieldNote.KEY_INFO[i].isBlack) { + // Black keys are shifted back half a key + x -= FieldNote.BLACK_KEY_WIDTH / 2; + xIncrement = FieldNote.BLACK_KEY_WIDTH / 2; + width = FieldNote.BLACK_KEY_WIDTH; + height = FieldNote.BLACK_KEY_HEIGHT; + fill = FieldNote.BLACK_KEY_COLOR; + stroke = FieldNote.BLACK_KEY_STROKE; + group = blackKeyGroup; + } else { + xIncrement = FieldNote.WHITE_KEY_WIDTH; + width = FieldNote.WHITE_KEY_WIDTH; + height = FieldNote.WHITE_KEY_HEIGHT; + fill = FieldNote.WHITE_KEY_COLOR; + stroke = ( + this.sourceBlock_.getParent() as Blockly.BlockSvg + ).getColourTertiary(); + group = whiteKeyGroup; + } + const attr = { + d: this.getPianoKeyPath_(x, y, width, height), + fill: fill, + stroke: stroke, + }; + x += xIncrement; + + const keySVG = Blockly.utils.dom.createSvgElement("path", attr, group); + + if (keySVGarray) { + keySVGarray[i] = keySVG; + keySVG.setAttribute("data-pitch", `${FieldNote.KEY_INFO[i].pitch}`); + keySVG.setAttribute("data-name", `${FieldNote.KEY_INFO[i].name}`); + keySVG.setAttribute("data-isBlack", `${FieldNote.KEY_INFO[i].isBlack}`); + + this.mouseDownWrappers_[i] = Blockly.browserEvents.bind( + keySVG, + "mousedown", + this, + this.onMouseDownOnKey_ + ); + this.mouseEnterWrappers_[i] = Blockly.browserEvents.bind( + keySVG, + "mouseenter", + this, + this.onMouseEnter_ + ); + } + } + } + + /** + * Construct the SVG path string for a piano key shape: a rectangle with rounded + * corners at the bottom. + * + * @param x the x position for the key. + * @param y the y position for the key. + * @param width the width of the key. + * @param height the height of the key. + * @returns the SVG path as a string. + */ + private getPianoKeyPath_( + x: number, + y: number, + width: number, + height: number + ): string { + return ( + "M" + + x + + " " + + y + + " " + + "L" + + x + + " " + + (y + height - FieldNote.KEY_RADIUS) + + " " + + "Q" + + x + + " " + + (y + height) + + " " + + (x + FieldNote.KEY_RADIUS) + + " " + + (y + height) + + " " + + "L" + + (x + width - FieldNote.KEY_RADIUS) + + " " + + (y + height) + + " " + + "Q" + + (x + width) + + " " + + (y + height) + + " " + + (x + width) + + " " + + (y + height - FieldNote.KEY_RADIUS) + + " " + + "L" + + (x + width) + + " " + + y + + " " + + "L" + + x + + " " + + y + ); + } + + /** + * Add a button for switching the displayed octave of the piano up or down. + * + * @param x The x position of the button. + * @param flipped If true, the icon should be flipped. + * @param svg The svg element to add the buttons to. + * @returns A group containing the button SVG elements. + */ + private addOctaveButton_( + x: number, + flipped: boolean, + svg: SVGElement + ): SVGElement { + const group = Blockly.utils.dom.createSvgElement("g", {}, svg); + const imageSize = FieldNote.OCTAVE_BUTTON_SIZE; + const arrow = Blockly.utils.dom.createSvgElement( + "image", + { + width: imageSize, + height: imageSize, + x: x - FieldNote.INSET, + y: -1 * FieldNote.INSET, + }, + group + ); + arrow.setAttributeNS( + "http://www.w3.org/1999/xlink", + "xlink:href", + Blockly.getMainWorkspace().options.pathToMedia + FieldNote.ARROW_SVG_PATH + ); + Blockly.utils.dom.createSvgElement( + "line", + { + stroke: ( + this.sourceBlock_.getParent() as Blockly.BlockSvg + ).getColourTertiary(), + x1: x - FieldNote.INSET, + y1: 0, + x2: x - FieldNote.INSET, + y2: FieldNote.TOP_MENU_HEIGHT - FieldNote.INSET, + }, + group + ); + if (flipped) { + const translateX = + -1 * FieldNote.OCTAVE_BUTTON_SIZE + FieldNote.INSET * 2; + group.setAttribute( + "transform", + "scale(-1, 1) " + "translate(" + translateX + ", 0)" + ); + } + return group; + } + + /** + * Add an SVG text label for display on the C keys of the piano. + * + * @param x The x position for the label. + * @param svg The SVG element to add the label to. + * @returns The SVG element containing the label. + */ + private addCKeyLabel_(x: number, svg: SVGElement): SVGElement { + return Blockly.utils.dom.createSvgElement( + "text", + { + x: x, + y: + FieldNote.TOP_MENU_HEIGHT + + FieldNote.WHITE_KEY_HEIGHT - + FieldNote.KEY_LABEL_PADDING, + class: "scratchNotePickerKeyLabel", + "text-anchor": "middle", + }, + svg + ); + } + + /** + * Set the visibility of the C key labels. + * + * @param visible If true, set labels to be visible. + */ + private setCKeyLabelsVisible_(visible: boolean) { + if (visible) { + this.fadeSvgToOpacity_(this.lowCText_, 1); + this.fadeSvgToOpacity_(this.highCText_, 1); + } else { + this.fadeSvgToOpacity_(this.lowCText_, 0); + this.fadeSvgToOpacity_(this.highCText_, 0); + } + } + + /** + * Animate an SVG to fade it in or out to a target opacity. + * + * @param svg The SVG element to apply the fade to. + * @param opacity The target opacity. + */ + private fadeSvgToOpacity_(svg: SVGElement, opacity: number) { + svg.setAttribute( + "style", + "opacity: " + opacity + "; transition: opacity 0.1s;" + ); + } + + /** + * Handle the mouse down event on a piano key. + * + * @param e Mouse down event. + */ + private onMouseDownOnKey_(e: PointerEvent) { + this.mouseIsDown_ = true; + this.mouseUpWrapper_ = Blockly.browserEvents.bind( + document.body, + "mouseup", + this, + this.onMouseUp_ + ); + this.selectNoteWithMouseEvent_(e); + } + + /** + * Handle the mouse up event following a mouse down on a piano key. + */ + private onMouseUp_() { + this.mouseIsDown_ = false; + Blockly.browserEvents.unbind(this.mouseUpWrapper_); + this.mouseUpWrapper_ = null; + } + + /** + * Handle the event when the mouse enters a piano key. + * + * @param e Mouse enter event. + */ + private onMouseEnter_(e: PointerEvent) { + if (this.mouseIsDown_) { + this.selectNoteWithMouseEvent_(e); + } + } + + /** + * Use the data in a mouse event to select a new note, and play it. + * + * @param e Mouse event. + */ + private selectNoteWithMouseEvent_(e: PointerEvent) { + const newNoteNum = + Number((e.target as HTMLElement).getAttribute("data-pitch")) + + this.displayedOctave_ * 12; + this.setEditorValue_(newNoteNum); + this.playNoteInternal_(); + } + + /** + * Play a note, by calling the externally overriden play note function. + */ + private playNoteInternal_() { + if (FieldNote.playNote_) { + FieldNote.playNote_(Number(this.getValue()), "Music"); + } + } + + /** + * Function to play a musical note corresponding to the key selected. + * Overridden externally. + * + * @param noteNum the MIDI note number to play. + * @param id An id to select a scratch extension to play the note. + */ + static playNote_ = function (noteNum: number, id: string) { + return; + }; + + /** + * Change the selected note by a number of octaves, and start the animation. + * + * @param octaves The number of octaves to change by. + */ + private changeOctaveBy_(octaves: number) { + this.displayedOctave_ += octaves; + if (this.displayedOctave_ < 0) { + this.displayedOctave_ = 0; + return; + } + const maxOctave = Math.floor(FieldNote.MAX_NOTE / 12); + if (this.displayedOctave_ > maxOctave) { + this.displayedOctave_ = maxOctave; + return; + } + + const newNote = Number(this.getText()) + octaves * 12; + this.setEditorValue_(newNote); + + this.animationTarget_ = this.fieldEditorWidth_ * octaves * -1; + this.animationPos_ = 0; + this.stepOctaveAnimation_(); + this.setCKeyLabelsVisible_(false); + } + + /** + * Animate the piano up or down an octave by sliding it to the left or right. + */ + private stepOctaveAnimation_() { + const absDiff = Math.abs(this.animationPos_ - this.animationTarget_); + if (absDiff < 1) { + this.pianoSVG_.setAttribute("transform", "translate(0, 0)"); + this.setCKeyLabelsVisible_(true); + this.playNoteInternal_(); + return; + } + this.animationPos_ += + (this.animationTarget_ - this.animationPos_) * + FieldNote.ANIMATION_FRACTION; + this.pianoSVG_.setAttribute( + "transform", + "translate(" + this.animationPos_ + ",0)" + ); + requestAnimationFrame(this.stepOctaveAnimation_.bind(this)); + } + + doValueUpdate_(newValue: string) { + super.doValueUpdate_(newValue); + + if (!this.textElement_) { + // Not rendered yet. + return; + } + + this.updateSelection_(); + } + + /** + * For a MIDI note number, find the index of the corresponding piano key. + * + * @param noteNum The note number. + * @returns The index of the piano key. + */ + private noteNumToKeyIndex_(noteNum: number): number { + return Math.floor(noteNum) - this.displayedOctave_ * 12; + } + + /** + * Update the selected note and labels on the field. + */ + private updateSelection_() { + const noteNum = Number(this.getText()); + + // If the note is outside the currently displayed octave, update it + if ( + this.displayedOctave_ == null || + noteNum > this.displayedOctave_ * 12 + 12 || + noteNum < this.displayedOctave_ * 12 + ) { + this.displayedOctave_ = Math.floor(noteNum / 12); + } + + const index = this.noteNumToKeyIndex_(noteNum); + + // Clear the highlight on all keys + this.keySVGs_.forEach(function (svg) { + const isBlack = svg.getAttribute("data-isBlack"); + if (isBlack === "true") { + svg.setAttribute("fill", FieldNote.BLACK_KEY_COLOR); + } else { + svg.setAttribute("fill", FieldNote.WHITE_KEY_COLOR); + } + }); + // Set the highlight on the selected key + if (this.keySVGs_[index]) { + this.keySVGs_[index].setAttribute("fill", FieldNote.KEY_SELECTED_COLOR); + // Update the note name text + const noteName = FieldNote.KEY_INFO[index].name; + this.noteNameText_.textContent = + noteName + " (" + Math.floor(noteNum) + ")"; + // Update the low and high C note names + const lowCNum = this.displayedOctave_ * 12; + this.lowCText_.textContent = "C(" + lowCNum + ")"; + this.highCText_.textContent = "C(" + (lowCNum + 12) + ")"; + } + } + + /** + * Ensure that only a valid MIDI note number may be entered. + * + * @param text The user's text. + * @returns A string representing a valid note number, or null if invalid. + */ + doClassValidation_(text: string): string | null { + if (text === null) { + return null; + } + var n = parseFloat(text || "0"); + if (isNaN(n)) { + return null; + } + if (n < 0) { + n = 0; + } + if (n > FieldNote.MAX_NOTE) { + n = FieldNote.MAX_NOTE; + } + return String(n); + } +} + +interface FieldNoteJsonConfig extends Blockly.FieldTextInputFromJsonConfig { + note: string; +} + +/** + * Register the field and any dependencies. + */ +export function registerFieldNote() { + Blockly.fieldRegistry.register("field_note", FieldNote); +} diff --git a/src/fields/field_textinput_removable.ts b/src/fields/field_textinput_removable.ts new file mode 100644 index 0000000000..9cc0243daf --- /dev/null +++ b/src/fields/field_textinput_removable.ts @@ -0,0 +1,104 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Massachusetts Institute of Technology + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Text input field with floating "remove" button. + * @author pkaplan@media.mit.edu (Paul Kaplan) + */ +import * as Blockly from "blockly/core"; +import type { ProcedureDeclarationBlock } from "../blocks/procedures"; + +/** + * Class for an editable text field displaying a deletion icon when selected. + */ +export class FieldTextInputRemovable extends Blockly.FieldTextInput { + private removeButtonMouseWrapper_?: Blockly.browserEvents.Data; + + /** + * Show the inline free-text editor on top of the text with the remove button. + */ + showEditor_() { + // Wait for our parent block to render so we can examine its metrics to + // calculate rounded corners on the editor as needed. + Blockly.renderManagement.finishQueuedRenders().then(() => { + super.showEditor_(); + + const div = Blockly.WidgetDiv.getDiv(); + div.className += " removableTextInput"; + const removeButton = document.createElement("img"); + removeButton.className = "blocklyTextRemoveIcon"; + removeButton.setAttribute( + "src", + this.sourceBlock_.workspace.options.pathToMedia + "icons/remove.svg" + ); + this.removeButtonMouseWrapper_ = Blockly.browserEvents.bind( + removeButton, + "mousedown", + this, + this.removeCallback_ + ); + div.appendChild(removeButton); + }); + } + + /** + * Function to call when remove button is called. Checks for removeFieldCallback + * on sourceBlock and calls it if possible. + */ + private removeCallback_() { + if (this.sourceBlock_ && "removeFieldCallback" in this.sourceBlock_) { + (this.sourceBlock_ as ProcedureDeclarationBlock).removeFieldCallback( + this + ); + } else { + console.warn("Expected a source block with removeFieldCallback"); + } + } + + /** + * Helper function to construct a FieldTextInputRemovable from a JSON arg object, + * dereferencing any string table references. + * + * @param options A JSON object with options (text, class, and spellcheck). + * @returns The new text input. + */ + fromJson( + options: Blockly.FieldTextInputFromJsonConfig + ): FieldTextInputRemovable { + const text = Blockly.utils.parsing.replaceMessageReferences( + options["text"] + ); + const field = new FieldTextInputRemovable(text, null, options); + if (typeof options["spellcheck"] == "boolean") { + field.setSpellcheck(options["spellcheck"]); + } + return field; + } +} + +/** + * Register the field and any dependencies. + */ +export function registerFieldTextInputRemovable() { + Blockly.fieldRegistry.register( + "field_input_removable", + FieldTextInputRemovable + ); +} diff --git a/src/fields/field_variable_getter.ts b/src/fields/field_variable_getter.ts new file mode 100644 index 0000000000..b58b436560 --- /dev/null +++ b/src/fields/field_variable_getter.ts @@ -0,0 +1,120 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2017 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Variable getter field. Appears as a label but has a variable + * picker in the right-click menu. + * @author fenichel@google.com (Rachel Fenichel) + */ +import * as Blockly from "blockly/core"; + +/** + * Class for a variable getter field. + */ +class FieldVariableGetter extends Blockly.FieldLabel { + private variable: Blockly.IVariableModel | null = + null; + + /** + * Creates a new FieldVariableGetter. + * + * @param allowedVariableType The type of variables this field can display. + */ + constructor(private allowedVariableType = "") { + super(Blockly.Field.SKIP_SETUP); + this.SERIALIZABLE = true; + } + + /** + * Returns the ID of this field's variable. + * + * @returns The ID of this field's variable. + */ + getValue(): string { + return this.variable?.getId() ?? ""; + } + + /** + * Returns the name of this field's variable. + * + * @returns The name of this field's variable. + */ + getText(): string { + return this.variable?.getName() ?? ""; + } + + /** + * Get the variable model for the variable associated with this field. + * Not guaranteed to be in the variable map on the workspace (e.g. if accessed + * after the variable has been deleted). + * + * @returns the selected variable, or null if none was selected. + */ + getVariable(): Blockly.IVariableModel | null { + return this.variable; + } + + /** + * Updates this field's variable to one with the given ID. + * + * @param newVariableId ID of a variable this field should represent. + */ + doValueUpdate_(newVariableId: string) { + super.doValueUpdate_(newVariableId); + const workspace = this.getSourceBlock().workspace; + this.variable = Blockly.Variables.getVariable(workspace, newVariableId); + } + + /** Informs Blockly that this field depends on a variable. */ + referencesVariables() { + return true; + } + + /** Rerenders this field when the underlying variable's name changes. */ + refreshVariableName() { + this.forceRerender(); + } + + static fromJson(options: FieldVariableGetterConfig) { + return new FieldVariableGetter(options["allowedVariableType"]); + } + + fromXml(element: Element) { + this.setValue(element.getAttribute("id")); + } + + toXml(element: Element): Element { + element.setAttribute("id", this.variable.getId()); + element.setAttribute("variabletype", this.variable.getType()); + element.textContent = this.variable.getName(); + return element; + } +} + +interface FieldVariableGetterConfig extends Blockly.FieldLabelConfig { + allowedVariableType?: string; +} + +/** + * Register the field and any dependencies. + */ +export function registerFieldVariableGetter() { + Blockly.fieldRegistry.register("field_variable_getter", FieldVariableGetter); +} diff --git a/src/fields/field_vertical_separator.ts b/src/fields/field_vertical_separator.ts new file mode 100644 index 0000000000..f622a96df7 --- /dev/null +++ b/src/fields/field_vertical_separator.ts @@ -0,0 +1,123 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2017 Massachusetts Institute of Technology + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Vertical separator field. Draws a vertical line. + * @author ericr@media.mit.edu (Eric Rosenbaum) + */ +import * as Blockly from "blockly/core"; + +/** + * Class for a vertical separator line. + */ +class FieldVerticalSeparator extends Blockly.Field { + private lineElement?: SVGLineElement; + + constructor() { + super(Blockly.Field.SKIP_SETUP); + /** + * Editable fields are saved by the XML renderer, non-editable fields are not. + */ + this.EDITABLE = false; + } + + /** + * Construct a FieldVerticalSeparator. + * @returns The new field instance. + */ + static fromJson = function () { + return new FieldVerticalSeparator(); + }; + + /** + * Install this field on a block. + */ + initView() { + const height = + 10 * (this.getConstants() as Blockly.zelos.ConstantProvider).GRID_UNIT; + this.size_ = new Blockly.utils.Size(1, height); + + this.lineElement = Blockly.utils.dom.createSvgElement( + "line", + { + stroke: (this.sourceBlock_ as Blockly.BlockSvg).getColourSecondary(), + "stroke-linecap": "round", + x1: 0, + y1: 0, + x2: 0, + y2: height, + }, + this.fieldGroup_ + ); + } + + /** + * Set the height of the line element, without adjusting the field's height. + * This allows the line's height to be changed without causing it to be + * centered with the new height (needed for correct rendering of hat blocks). + * @param newHeight the new height for the line. + * @package + */ + setLineHeight(newHeight: number) { + this.lineElement.setAttribute("y2", `${newHeight}`); + } + + /** + * Get the value of this field. A no-op in this case. + */ + getValue(): string | null { + return null; + } + + getText() { + return ""; + } + + /** + * Set the value of this field. A no-op in this case. + */ + setValue() { + return; + } + + /** + * Separator lines are fixed width, no need to render. + */ + render_() { + // NOP + } + + /** + * Separator lines are fixed width, no need to update. + */ + updateWidth() { + // NOP + } +} + +/** + * Register the field and any dependencies. + */ +export function registerFieldVerticalSeparator() { + Blockly.fieldRegistry.register( + "field_vertical_separator", + FieldVerticalSeparator + ); +} diff --git a/src/fields/scratch_field_angle.ts b/src/fields/scratch_field_angle.ts new file mode 100644 index 0000000000..4b203e9692 --- /dev/null +++ b/src/fields/scratch_field_angle.ts @@ -0,0 +1,485 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2013 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Angle input field. + * @author fraser@google.com (Neil Fraser) + */ +"use strict"; + +import * as Blockly from "blockly/core"; + +class ScratchFieldAngle extends Blockly.FieldNumber { + /** + * The highlighted portion of the angle picker circle, between 0º and the + * selected angle. + */ + private gauge?: SVGPathElement; + + /** + * The line to the angle picker handle. + */ + private line?: SVGLineElement; + + /** + * The grabbable handle used to choose an angle. + */ + private handle?: SVGGElement; + + /** + * The arrow graphic shown on the grab handle. + */ + private arrow?: SVGImageElement; + + /** + * Opaque identifier used to unbind event listener in dispose(). + */ + private mouseDownWrapper_: Blockly.browserEvents.Data; + + /** + * Opaque identifier used to unbind event listener in dispose(). + */ + private mouseMoveWrapper: Blockly.browserEvents.Data; + + /** + * Opaque identifier used to unbind event listener in dispose(). + */ + private mouseUpWrapper: Blockly.browserEvents.Data; + + /** + * Round angles to the nearest 15 degrees when using mouse. + * Set to 0 to disable rounding. + */ + ROUND = 15; + + /** + * Half the width of protractor image. + */ + HALF = 120 / 2; + + /* The following two settings work together to set the behaviour of the angle + * picker. While many combinations are possible, two modes are typical: + * Math mode. + * 0 deg is right, 90 is up. This is the style used by protractors. + * CLOCKWISE = false; + * OFFSET = 0; + * Compass mode. + * 0 deg is up, 90 is right. This is the style used by maps. + * CLOCKWISE = true; + * OFFSET = 90; + */ + + /** + * Angle increases clockwise (true) or counterclockwise (false). + */ + CLOCKWISE = true; + + /** + * Offset the location of 0 degrees (and all angles) by a constant. + * Usually either 0 (0 = right) or 90 (0 = up). + */ + OFFSET = 90; + + /** + * Maximum allowed angle before wrapping. + * Usually either 360 (for 0 to 359.9) or 180 (for -179.9 to 180). + */ + WRAP = 180; + + /** + * Radius of drag handle + */ + HANDLE_RADIUS = 10; + + /** + * Width of drag handle arrow + */ + ARROW_WIDTH = this.HANDLE_RADIUS; + + /** + * Half the stroke-width used for the "glow" around the drag handle, rounded + * up to nearest whole pixel. + */ + + HANDLE_GLOW_WIDTH = 3; + + /** + * Radius of protractor circle. Slightly smaller than protractor size since + * otherwise SVG crops off half the border at the edges. + */ + RADIUS = this.HALF - this.HANDLE_RADIUS - this.HANDLE_GLOW_WIDTH; + + /** + * Radius of central dot circle. + */ + CENTER_RADIUS = 2; + + /** + * Path to the arrow svg icon. + */ + ARROW_SVG_PATH = "icons/arrow.svg"; + + /** + * Clean up this FieldAngle, as well as the inherited FieldNumber. + */ + dispose() { + super.dispose(); + this.gauge = null; + if (this.mouseDownWrapper_) { + Blockly.browserEvents.unbind(this.mouseDownWrapper_); + } + if (this.mouseUpWrapper) { + Blockly.browserEvents.unbind(this.mouseUpWrapper); + } + if (this.mouseMoveWrapper) { + Blockly.browserEvents.unbind(this.mouseMoveWrapper); + } + } + + /** + * Show the inline free-text editor on top of the text. + */ + showEditor_(event: PointerEvent) { + // Mobile browsers have issues with in-line textareas (focus & keyboards). + // Also, don't let the parent take ephemeral focus since the drop-down div + // below will handle it, instead. + const noFocus = + Blockly.utils.userAgent.MOBILE || + Blockly.utils.userAgent.ANDROID || + Blockly.utils.userAgent.IPAD; + super.showEditor_(event, noFocus, false); + + // If there is an existing drop-down someone else owns, hide it immediately and clear it. + Blockly.DropDownDiv.hideWithoutAnimation(); + Blockly.DropDownDiv.clearContent(); + const div = Blockly.DropDownDiv.getContentDiv(); + // Build the SVG DOM. + const svg = Blockly.utils.dom.createSvgElement( + "svg", + { + xmlns: "http://www.w3.org/2000/svg", + "xmlns:html": "http://www.w3.org/1999/xhtml", + "xmlns:xlink": "http://www.w3.org/1999/xlink", + version: "1.1", + height: this.HALF * 2 + "px", + width: this.HALF * 2 + "px", + }, + div + ); + Blockly.utils.dom.createSvgElement( + "circle", + { + cx: this.HALF, + cy: this.HALF, + r: this.RADIUS, + fill: ( + this.getSourceBlock().getParent() as Blockly.BlockSvg + ).getColourSecondary(), + stroke: ( + this.getSourceBlock().getParent() as Blockly.BlockSvg + ).getColourTertiary(), + class: "blocklyAngleCircle", + }, + svg + ); + this.gauge = Blockly.utils.dom.createSvgElement( + "path", + { class: "blocklyAngleGauge" }, + svg + ); + // The moving line, x2 and y2 are set in updateGraph + this.line = Blockly.utils.dom.createSvgElement( + "line", + { + x1: this.HALF, + y1: this.HALF, + class: "blocklyAngleLine", + }, + svg + ); + // The fixed vertical line at the offset + const offsetRadians = (Math.PI * this.OFFSET) / 180; + Blockly.utils.dom.createSvgElement( + "line", + { + x1: this.HALF, + y1: this.HALF, + x2: this.HALF + this.RADIUS * Math.cos(offsetRadians), + y2: this.HALF - this.RADIUS * Math.sin(offsetRadians), + class: "blocklyAngleLine", + }, + svg + ); + // Draw markers around the edge. + for (let angle = 0; angle < 360; angle += 15) { + Blockly.utils.dom.createSvgElement( + "line", + { + x1: this.HALF + this.RADIUS - 13, + y1: this.HALF, + x2: this.HALF + this.RADIUS - 7, + y2: this.HALF, + class: "blocklyAngleMarks", + transform: + "rotate(" + angle + "," + this.HALF + "," + this.HALF + ")", + }, + svg + ); + } + // Center point + Blockly.utils.dom.createSvgElement( + "circle", + { + cx: this.HALF, + cy: this.HALF, + r: this.CENTER_RADIUS, + class: "blocklyAngleCenterPoint", + }, + svg + ); + // Handle group: a circle and the arrow image + this.handle = Blockly.utils.dom.createSvgElement("g", {}, svg); + Blockly.utils.dom.createSvgElement( + "circle", + { + cx: 0, + cy: 0, + r: this.HANDLE_RADIUS, + class: "blocklyAngleDragHandle", + }, + this.handle + ); + this.arrow = Blockly.utils.dom.createSvgElement( + "image", + { + width: this.ARROW_WIDTH, + height: this.ARROW_WIDTH, + x: -this.ARROW_WIDTH / 2, + y: -this.ARROW_WIDTH / 2, + class: "blocklyAngleDragArrow", + }, + this.handle + ); + this.arrow.setAttributeNS( + "http://www.w3.org/1999/xlink", + "xlink:href", + Blockly.getMainWorkspace().options.pathToMedia + this.ARROW_SVG_PATH + ); + + Blockly.DropDownDiv.setColour( + this.getSourceBlock().getParent().getColour(), + ( + this.getSourceBlock().getParent() as Blockly.BlockSvg + ).getColourTertiary() + ); + Blockly.DropDownDiv.showPositionedByBlock( + this, + this.getSourceBlock() as Blockly.BlockSvg + ); + + this.mouseDownWrapper_ = Blockly.browserEvents.bind( + this.handle, + "mousedown", + this, + this.onMouseDown + ); + + this.updateGraph(); + } + + /** + * Set the angle to match the mouse's position. + */ + onMouseDown() { + this.mouseMoveWrapper = Blockly.browserEvents.bind( + document.body, + "mousemove", + this, + this.onMouseMove + ); + this.mouseUpWrapper = Blockly.browserEvents.bind( + document.body, + "mouseup", + this, + this.onMouseUp + ); + } + + /** + * Set the angle to match the mouse's position. + */ + onMouseUp() { + Blockly.browserEvents.unbind(this.mouseMoveWrapper); + Blockly.browserEvents.unbind(this.mouseUpWrapper); + } + + /** + * Set the angle to match the mouse's position. + * @param e Mouse move event. + */ + onMouseMove(e: PointerEvent) { + e.preventDefault(); + const bBox = this.gauge.ownerSVGElement.getBoundingClientRect(); + const dx = e.clientX - bBox.left - this.HALF; + const dy = e.clientY - bBox.top - this.HALF; + let angle = Math.atan(-dy / dx); + if (isNaN(angle)) { + // This shouldn't happen, but let's not let this error propagate further. + return; + } + angle = this.toDegrees(angle); + // 0: East, 90: North, 180: West, 270: South. + if (dx < 0) { + angle += 180; + } else if (dy > 0) { + angle += 360; + } + if (this.CLOCKWISE) { + angle = this.OFFSET + 360 - angle; + } else { + angle -= this.OFFSET; + } + if (this.ROUND) { + angle = Math.round(angle / this.ROUND) * this.ROUND; + } + this.setValue(angle); + this.setEditorValue_(this.getValue()); + this.resizeEditor_(); + } + + /** + * Redraw the graph with the current angle. + */ + private updateGraph() { + if (!this.gauge) { + return; + } + const angleDegrees = (Number(this.getValue()) % 360) + this.OFFSET; + let angleRadians = this.toRadians(angleDegrees); + const path = ["M ", this.HALF, ",", this.HALF]; + let x2 = this.HALF; + let y2 = this.HALF; + if (!isNaN(angleRadians)) { + const angle1 = this.toRadians(this.OFFSET); + const x1 = Math.cos(angle1) * this.RADIUS; + const y1 = Math.sin(angle1) * -this.RADIUS; + if (this.CLOCKWISE) { + angleRadians = 2 * angle1 - angleRadians; + } + x2 += Math.cos(angleRadians) * this.RADIUS; + y2 -= Math.sin(angleRadians) * this.RADIUS; + // Use large arc only if input value is greater than wrap + const largeFlag = Math.abs(angleDegrees - this.OFFSET) > 180 ? 1 : 0; + let sweepFlag = Number(this.CLOCKWISE); + if (angleDegrees < this.OFFSET) { + sweepFlag = 1 - sweepFlag; // Sweep opposite direction if less than the offset + } + path.push( + " l ", + x1, + ",", + y1, + " A ", + this.RADIUS, + ",", + this.RADIUS, + " 0 ", + largeFlag, + " ", + sweepFlag, + " ", + x2, + ",", + y2, + " z" + ); + + // Image rotation needs to be set in degrees + let imageRotation: number; + if (this.CLOCKWISE) { + imageRotation = angleDegrees + 2 * this.OFFSET; + } else { + imageRotation = -angleDegrees; + } + this.arrow.setAttribute("transform", "rotate(" + imageRotation + ")"); + } + this.gauge.setAttribute("d", path.join("")); + this.line.setAttribute("x2", `${x2}`); + this.line.setAttribute("y2", `${y2}`); + this.handle.setAttribute("transform", "translate(" + x2 + "," + y2 + ")"); + } + + /** + * Ensure that only an angle may be entered. + * @param text The user's text. + * @returns A string representing a valid angle, or null if invalid. + */ + doClassValidation_(text: string): number | null { + if (text === null) { + return null; + } + let n = parseFloat(text || "0"); + if (isNaN(n)) { + return null; + } + n = n % 360; + if (n < 0) { + n += 360; + } + if (n > this.WRAP) { + n -= 360; + } + return Number(n); + } + + doValueUpdate_(newValue: number) { + super.doValueUpdate_(newValue); + this.updateGraph(); + } + + toDegrees(radians: number) { + return (radians * 180) / Math.PI; + } + + toRadians(degrees: number) { + return (degrees * Math.PI) / 180; + } + + /** + * Construct a FieldAngle from a JSON arg object. + * + * @param options A JSON object with options (angle). + * @returns The new field instance. + */ + fromJson(options: ScratchFieldAngleJsonConfig): ScratchFieldAngle { + return new ScratchFieldAngle(options["angle"]); + } +} + +export interface ScratchFieldAngleJsonConfig { + angle?: number; +} + +/** + * Register the field and any dependencies. + */ +export function registerScratchFieldAngle() { + Blockly.fieldRegistry.register("field_angle", ScratchFieldAngle); +} diff --git a/src/fields/scratch_field_dropdown.ts b/src/fields/scratch_field_dropdown.ts new file mode 100644 index 0000000000..d0a13d2ad1 --- /dev/null +++ b/src/fields/scratch_field_dropdown.ts @@ -0,0 +1,44 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; + +class ScratchFieldDropdown extends Blockly.FieldDropdown { + private originalStyle: string; + + showEditor_(event: PointerEvent) { + super.showEditor_(event); + const sourceBlock = this.getSourceBlock() as Blockly.BlockSvg; + const style = sourceBlock.style; + if (sourceBlock.isShadow()) { + this.originalStyle = sourceBlock.getStyleName(); + sourceBlock.setStyle(`${this.originalStyle}_selected`); + } else if (this.borderRect_) { + this.borderRect_.setAttribute( + "fill", + "colourQuaternary" in style + ? `${style.colourQuaternary}` + : style.colourTertiary + ); + } + } + + dropdownDispose_() { + super.dropdownDispose_(); + const sourceBlock = this.getSourceBlock(); + if (sourceBlock.isShadow()) { + sourceBlock.setStyle(this.originalStyle); + } + } +} + +/** + * Register the field and any dependencies. + */ +export function registerScratchFieldDropdown() { + Blockly.fieldRegistry.unregister("field_dropdown"); + Blockly.fieldRegistry.register("field_dropdown", ScratchFieldDropdown); +} diff --git a/src/fields/scratch_field_number.ts b/src/fields/scratch_field_number.ts new file mode 100644 index 0000000000..79e1c155a5 --- /dev/null +++ b/src/fields/scratch_field_number.ts @@ -0,0 +1,385 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2016 Massachusetts Institute of Technology + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Field for numbers. Includes validator and numpad on touch. + * @author tmickel@mit.edu (Tim Mickel) + */ +import * as Blockly from "blockly/core"; +import { Colours } from "../colours"; + +/** + * Class for an editable number field. + * In scratch-blocks, the min/max/precision properties are only used + * to construct a restrictor on typable characters, and to inform the pop-up + * numpad on touch devices. + * These properties are included here (i.e. instead of just accepting a + * decimalAllowed, negativeAllowed) to maintain API compatibility with Blockly + * and Blockly for Android. + * @param {(string|number)=} opt_value The initial content of the field. The value + * should cast to a number, and if it does not, '0' will be used. + * @param {(string|number)=} opt_min Minimum value. + * @param {(string|number)=} opt_max Maximum value. + * @param {(string|number)=} opt_precision Precision for value. + * @param {Function=} opt_validator An optional function that is called + * to validate any constraints on what the user entered. Takes the new + * text as an argument and returns the accepted text or null to abort + * the change. + * @extends {Blockly.FieldTextInput} + * @constructor + */ +class ScratchFieldNumber extends Blockly.FieldTextInput { + private negativeAllowed_ = true; + private decimalAllowed_ = true; + private exponentialAllowed_ = true; + /** + * Fixed width of the num-pad drop-down, in px. + * @type {number} + * @const + */ + static DROPDOWN_WIDTH = 168; + + /** + * Buttons for the num-pad, in order from the top left. + * Values are strings of the number or symbol will be added to the field text + * when the button is pressed. + * @type {Array.} + * @const + */ + // Calculator order + static NUMPAD_BUTTONS = [ + "7", + "8", + "9", + "4", + "5", + "6", + "1", + "2", + "3", + ".", + "0", + "-", + " ", + ]; + + /** + * Src for the delete icon to be shown on the num-pad. + * @type {string} + * @const + */ + static NUMPAD_DELETE_ICON = + "data:image/svg+xml;utf8," + + "' + + ''; + + configure_(config: Blockly.FieldNumberFromJsonConfig) { + super.configure_(config); + this.decimalAllowed_ = + typeof config.precision == "undefined" || + isNaN(config.precision) || + config.precision == 0 || + Math.floor(config.precision) != config.precision; + this.negativeAllowed_ = + typeof config.min == "undefined" || isNaN(config.min) || config.min < 0; + this.exponentialAllowed_ = this.decimalAllowed_; + } + + /** + * Return an appropriate restrictor, depending on whether this FieldNumber + * allows decimal or negative numbers. + * @return {!RegExp} Regular expression for this FieldNumber's restrictor. + */ + getNumRestrictor() { + let pattern = "[\\d]"; // Always allow digits. + if (this.decimalAllowed_) { + pattern += "|[\\.]"; + } + if (this.negativeAllowed_) { + pattern += "|[-]"; + } + if (this.exponentialAllowed_) { + pattern += "|[eE]"; + } + return new RegExp(pattern); + } + + /** + * Show the inline free-text editor on top of the text and the num-pad if + * appropriate. + */ + showEditor_(e: PointerEvent) { + // Do not focus on mobile devices so we can show the num-pad + const showNumPad = e && e.pointerType === "touch"; + super.showEditor_(e, showNumPad); + + // Show a numeric keypad in the drop-down on touch + if (showNumPad) { + this.htmlInput_.select(); + this.showNumPad_(); + } + } + + onHtmlInputKeyDown_(e: KeyboardEvent) { + super.onHtmlInputKeyDown_(e); + // key can be things like "Backspace", so only validate when it represents a single + // character so as to allow non-textual input to work as normal. + if (e.key.length === 1) { + const validator = this.getNumRestrictor(); + if (!e.key.match(validator)) { + e.preventDefault(); + } + } + } + + /** + * Show the number pad. + */ + private showNumPad_() { + const contentDiv = Blockly.DropDownDiv.getContentDiv(); + + // Accessibility properties + contentDiv.setAttribute("role", "menu"); + contentDiv.setAttribute("aria-haspopup", "true"); + + this.addButtons_(contentDiv); + + // Set colour and size of drop-down + const sourceBlock = this.getSourceBlock() as Blockly.BlockSvg; + Blockly.DropDownDiv.setColour( + sourceBlock.getParent().getColour(), + sourceBlock.getColourTertiary() + ); + contentDiv.style.width = ScratchFieldNumber.DROPDOWN_WIDTH + "px"; + + this.position_(); + } + + /** + * Figure out where to place the drop-down, and move it there. + */ + private position_() { + // Calculate positioning for the drop-down + // sourceBlock_ is the rendered shadow field input box + const sourceBlock = this.getSourceBlock() as Blockly.BlockSvg; + const scale = sourceBlock.workspace.scale; + let bBox = sourceBlock.getHeightWidth(); + bBox.width *= scale; + bBox.height *= scale; + const position = this.getAbsoluteXY_(); + // If we can fit it, render below the shadow block + const primaryX = position.x + bBox.width / 2; + const primaryY = position.y + bBox.height; + // If we can't fit it, render above the entire parent block + const secondaryX = primaryX; + const secondaryY = position.y; + + Blockly.DropDownDiv.setBoundsElement( + sourceBlock.workspace.getParentSvg().parentElement + ); + Blockly.DropDownDiv.show( + this, + this.getSourceBlock().RTL, + primaryX, + primaryY, + secondaryX, + secondaryY, + this.onHide_.bind(this) + ); + } + + /** + * Add number, punctuation, and erase buttons to the numeric keypad's content + * div. + * + * @param contentDiv The div for the numeric keypad. + */ + private addButtons_(contentDiv: Element) { + const sourceBlock = this.getSourceBlock() as Blockly.BlockSvg; + const buttonColour = sourceBlock.getParent().getColour(); + const buttonBorderColour = sourceBlock.getParent().getColourTertiary(); + + // Add numeric keypad buttons + const buttons = ScratchFieldNumber.NUMPAD_BUTTONS; + for (let i = 0, buttonText; (buttonText = buttons[i]); i++) { + const button = document.createElement("button"); + button.setAttribute("role", "menuitem"); + button.setAttribute("class", "blocklyNumPadButton"); + button.setAttribute( + "style", + "background:" + + buttonColour + + ";" + + "border: 1px solid " + + buttonBorderColour + + ";" + ); + button.title = buttonText; + button.innerHTML = buttonText; + Blockly.browserEvents.bind( + button, + "mousedown", + button, + this.numPadButtonTouch.bind(this) + ); + if (buttonText == "." && !this.decimalAllowed_) { + // Don't show the decimal point for inputs that must be round numbers + button.setAttribute("style", "visibility: hidden"); + } else if (buttonText == "-" && !this.negativeAllowed_) { + continue; + } else if (buttonText == " " && !this.negativeAllowed_) { + continue; + } else if (buttonText == " " && this.negativeAllowed_) { + button.setAttribute("style", "visibility: hidden"); + } + contentDiv.appendChild(button); + } + // Add erase button to the end + const eraseButton = document.createElement("button"); + eraseButton.setAttribute("role", "menuitem"); + eraseButton.setAttribute("class", "blocklyNumPadButton"); + eraseButton.setAttribute( + "style", + "background:" + + buttonColour + + ";" + + "border: 1px solid " + + buttonBorderColour + + ";" + ); + eraseButton.title = "Delete"; + + const eraseImage = document.createElement("img"); + eraseImage.src = ScratchFieldNumber.NUMPAD_DELETE_ICON; + eraseButton.appendChild(eraseImage); + + Blockly.browserEvents.bind( + eraseButton, + "mousedown", + null, + this.numPadEraseButtonTouch.bind(this) + ); + contentDiv.appendChild(eraseButton); + } + + /** + * Call for when a num-pad number or punctuation button is touched. + * Determine what the user is inputting and update the text field appropriately. + * + * @param e DOM event triggering the touch. + */ + numPadButtonTouch(e: PointerEvent) { + // String of the button (e.g., '7') + const spliceValue = (e.target as HTMLElement).innerText; + // Old value of the text field + const oldValue = this.htmlInput_.value; + // Determine the selected portion of the text field + const selectionStart = this.htmlInput_.selectionStart; + const selectionEnd = this.htmlInput_.selectionEnd; + + // Splice in the new value + const newValue = + oldValue.slice(0, selectionStart) + + spliceValue + + oldValue.slice(selectionEnd); + + // Set new value and advance the cursor + this.updateDisplay_(newValue, selectionStart + spliceValue.length); + + // This is just a click. + Blockly.Touch.clearTouchIdentifier(); + + // Prevent default to not lose input focus + e.preventDefault(); + } + + /** + * Call for when the num-pad erase button is touched. + * Determine what the user is asking to erase, and erase it. + * + * @param e DOM event triggering the touch. + */ + numPadEraseButtonTouch(e: PointerEvent) { + // Old value of the text field + const oldValue = this.htmlInput_.value; + // Determine what is selected to erase (if anything) + let selectionStart = this.htmlInput_.selectionStart; + const selectionEnd = this.htmlInput_.selectionEnd; + + // If selection is zero-length, shift start to the left 1 character + if (selectionStart == selectionEnd) { + selectionStart = Math.max(0, selectionStart - 1); + } + + // Cut out selected range + const newValue = + oldValue.slice(0, selectionStart) + oldValue.slice(selectionEnd); + + this.updateDisplay_(newValue, selectionStart); + + // This is just a click. + Blockly.Touch.clearTouchIdentifier(); + + // Prevent default to not lose input focus which resets cursors in Chrome + e.preventDefault(); + } + + /** + * Update the displayed value and resize/scroll the text field as needed. + * + * @param newValue The new text to display. + * @param newSelection The new index to put the cursor + */ + private updateDisplay_(newValue: string, newSelection: number) { + this.setEditorValue_(newValue); + // Resize and scroll the text field appropriately + const htmlInput = this.htmlInput_; + htmlInput.setSelectionRange(newSelection, newSelection); + htmlInput.scrollLeft = htmlInput.scrollWidth; + } + + /** + * Callback for when the drop-down is hidden. + */ + onHide_() { + // Clear accessibility properties + Blockly.DropDownDiv.getContentDiv().removeAttribute("role"); + Blockly.DropDownDiv.getContentDiv().removeAttribute("aria-haspopup"); + } +} + +ScratchFieldNumber.prototype.DEFAULT_VALUE = ""; + +/** + * Register the field and any dependencies. + */ +export function registerScratchFieldNumber() { + Blockly.fieldRegistry.unregister("field_number"); + Blockly.fieldRegistry.register("field_number", ScratchFieldNumber); +} diff --git a/src/fields/scratch_field_variable.ts b/src/fields/scratch_field_variable.ts new file mode 100644 index 0000000000..b0fe2cf59e --- /dev/null +++ b/src/fields/scratch_field_variable.ts @@ -0,0 +1,191 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2012 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Variable input field. + * @author fraser@google.com (Neil Fraser) + */ +import * as Blockly from "blockly/core"; +import * as Constants from "../constants"; +import { ScratchMsgs } from "../../msg/scratch_msgs.js"; +import { createVariable, renameVariable } from "../variables"; +import type { ScratchVariableModel } from "../scratch_variable_model"; + +export class ScratchFieldVariable extends Blockly.FieldVariable { + private originalStyle: string; + + constructor( + varName: string | null | typeof Blockly.Field.SKIP_SETUP, + validator?: Blockly.FieldVariableValidator, + variableTypes?: string[], + defaultType?: string, + config?: Blockly.FieldVariableConfig + ) { + super(varName, validator, variableTypes, defaultType, config); + this.menuGenerator_ = ScratchFieldVariable.dropdownCreate; + } + + initModel() { + if (!this.getVariable()) { + const sourceBlock = this.getSourceBlock(); + if (sourceBlock) { + const broadcastVariable = this.initFlyoutBroadcast( + sourceBlock.workspace as Blockly.WorkspaceSvg + ); + if (broadcastVariable) { + this.doValueUpdate_(broadcastVariable.getId()); + return; + } + } + } + + super.initModel(); + } + + /** + * Initialize broadcast blocks in the flyout. + * Implicit deletion of broadcast messages from the scratch vm may cause + * broadcast blocks in the flyout to change which variable they display as the + * selected option when the workspace is refreshed. + * Re-sort the broadcast messages by name, and set the field value to the id + * of the variable that comes first in sorted order. + * + * @param workspace The flyout workspace containing the broadcast block. + * @returns The variable of type 'broadcast_msg' that comes first in sorted + * order. + */ + initFlyoutBroadcast( + workspace: Blockly.WorkspaceSvg + ): Blockly.IVariableModel { + const broadcastVars = workspace.getVariablesOfType( + Constants.BROADCAST_MESSAGE_VARIABLE_TYPE + ); + if ( + workspace.isFlyout && + this.getDefaultType() == Constants.BROADCAST_MESSAGE_VARIABLE_TYPE && + broadcastVars.length != 0 + ) { + broadcastVars.sort(Blockly.Variables.compareByName); + return broadcastVars[0]; + } + } + + /** + * Return a sorted list of variable names for variable dropdown menus. + * Include a special option at the end for creating a new variable name. + * + * @returns Array of variable names. + */ + static dropdownCreate(this: ScratchFieldVariable): Blockly.MenuOption[] { + let options = super.dropdownCreate(); + const type = this.getDefaultType(); + if (type === Constants.BROADCAST_MESSAGE_VARIABLE_TYPE) { + options.splice(-2, 2, [ + ScratchMsgs.translate("NEW_BROADCAST_MESSAGE"), + Constants.NEW_BROADCAST_MESSAGE_ID, + ]); + } else if (type === Constants.LIST_VARIABLE_TYPE) { + options = options.map((option) => { + if (option[1] === Blockly.RENAME_VARIABLE_ID) { + return [ScratchMsgs.translate("RENAME_LIST"), option[1]]; + } else if (option[1] === Blockly.DELETE_VARIABLE_ID) { + return [ + ScratchMsgs.translate("DELETE_LIST").replace("%1", this.getText()), + option[1], + ]; + } + return option; + }); + } + + return options; + } + + /** Handle the selection of an item in the variable dropdown menu. + * Special case the 'Rename variable...', 'Delete variable...', + * and 'New message...' options. + * In the rename case, prompt the user for a new name. + * + * @param menu The Menu component clicked. + * @param menuItem The MenuItem selected within menu. + */ + onItemSelected_(menu: Blockly.Menu, menuItem: Blockly.MenuItem) { + const sourceBlock = this.getSourceBlock(); + if (sourceBlock && !sourceBlock.isDeadOrDying()) { + const selectedItem = menuItem.getValue(); + if (selectedItem === Constants.NEW_BROADCAST_MESSAGE_ID) { + createVariable( + sourceBlock.workspace as Blockly.WorkspaceSvg, + (varId) => { + if (varId) { + this.setValue(varId); + } + }, + Constants.BROADCAST_MESSAGE_VARIABLE_TYPE + ); + return; + } else if (selectedItem === Blockly.RENAME_VARIABLE_ID) { + renameVariable( + sourceBlock.workspace as Blockly.WorkspaceSvg, + this.getVariable() as ScratchVariableModel + ); + return; + } + } + super.onItemSelected_(menu, menuItem); + } + + showEditor_(event: PointerEvent) { + super.showEditor_(event); + const sourceBlock = this.getSourceBlock(); + const styleName = sourceBlock.getStyleName(); + const style = (sourceBlock.workspace as Blockly.WorkspaceSvg) + .getRenderer() + .getConstants() + .getBlockStyle(styleName); + if (sourceBlock.isShadow()) { + this.originalStyle = styleName; + sourceBlock.setStyle(`${this.originalStyle}_selected`); + } else if (this.borderRect_) { + this.borderRect_.setAttribute( + "fill", + "colourQuaternary" in style + ? `${style.colourQuaternary}` + : style.colourTertiary + ); + } + } + + dropdownDispose_() { + super.dropdownDispose_(); + const sourceBlock = this.getSourceBlock(); + if (sourceBlock.isShadow()) { + sourceBlock.setStyle(this.originalStyle); + } + } +} + +/** + * Register the field and any dependencies. + */ +export function registerScratchFieldVariable() { + Blockly.fieldRegistry.unregister("field_variable"); + Blockly.fieldRegistry.register("field_variable", ScratchFieldVariable); +} diff --git a/src/flyout_checkbox_icon.ts b/src/flyout_checkbox_icon.ts new file mode 100644 index 0000000000..62c54f1da6 --- /dev/null +++ b/src/flyout_checkbox_icon.ts @@ -0,0 +1,78 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import { CheckboxBubble } from "./checkbox_bubble"; + +/** + * Invisible icon that exists solely to host the corresponding checkbox bubble. + */ +export class FlyoutCheckboxIcon + extends Blockly.icons.Icon + implements Blockly.IHasBubble +{ + private checkboxBubble: CheckboxBubble; + private type = new Blockly.icons.IconType("checkbox"); + + constructor(protected override sourceBlock: Blockly.BlockSvg) { + super(sourceBlock); + if (this.sourceBlock.workspace.isFlyout) { + this.checkboxBubble = new CheckboxBubble(this.sourceBlock); + } + } + + getType(): Blockly.icons.IconType { + return this.type; + } + + getSize(): Blockly.utils.Size { + // Awful hack to cancel out the default padding added to icons. + return new Blockly.utils.Size(-8, 0); + } + + isClickableInFlyout(): boolean { + return false; + } + + bubbleIsVisible(): boolean { + return this.sourceBlock.workspace.isFlyout; + } + + onLocationChange(blockOrigin: Blockly.utils.Coordinate) { + this.checkboxBubble?.updateLocation(); + } + + setChecked(checked: boolean) { + this.checkboxBubble?.setChecked(checked); + } + + dispose() { + this.checkboxBubble?.dispose(); + super.dispose(); + } + + // These methods are required by the interfaces, but intentionally have no + // implementation, largely because this icon has no visual representation. + + async setBubbleVisible(visible: boolean) {} + + initView(pointerDownListener: (e: PointerEvent) => void) {} + + canBeFocused() { + return false; + } + + getBubble() { + return this.checkboxBubble; + } +} + +Blockly.registry.register( + Blockly.registry.Type.ICON, + "checkbox", + FlyoutCheckboxIcon, + true +); diff --git a/src/glows.ts b/src/glows.ts new file mode 100644 index 0000000000..cb5ca49dfa --- /dev/null +++ b/src/glows.ts @@ -0,0 +1,109 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import { Colours } from "./colours"; + +/** + * Glow/unglow a stack in the workspace. + * + * @param id ID of block which starts the stack. + * @param isGlowingStack Whether to glow the stack. + */ +export function glowStack(id: string, isGlowingStack: boolean) { + const block = (Blockly.getMainWorkspace().getBlockById(id) || + (Blockly.getMainWorkspace() as Blockly.WorkspaceSvg) + .getFlyout() + .getWorkspace() + .getBlockById(id)) as Blockly.BlockSvg; + if (!block) { + throw "Tried to glow block that does not exist."; + } + + const svg = block.getSvgRoot(); + if (isGlowingStack && !svg.hasAttribute("filter")) { + svg.setAttribute("filter", "url(#blocklyStackGlowFilter)"); + } else if (!isGlowingStack && svg.hasAttribute("filter")) { + svg.removeAttribute("filter"); + } +} + +/** + * Creates an SVG filter to render block glows and adds it to the DOM. + * @param workspace The workspace whose DOM the filter will be inserted in. + */ +export function buildGlowFilter(workspace: Blockly.WorkspaceSvg) { + const svg = workspace.getParentSvg(); + const defs = Blockly.utils.dom.createSvgElement( + Blockly.utils.Svg.DEFS, + {}, + svg + ); + // Using a dilate distorts the block shape. + // Instead use a gaussian blur, and then set all alpha to 1 with a transfer. + const stackGlowFilter = Blockly.utils.dom.createSvgElement( + "filter", + { + id: "blocklyStackGlowFilter", + height: "160%", + width: "180%", + y: "-30%", + x: "-40%", + }, + defs + ); + Blockly.utils.dom.createSvgElement( + "feGaussianBlur", + { + in: "SourceGraphic", + stdDeviation: Colours.stackGlowSize, + }, + stackGlowFilter + ); + // Set all gaussian blur pixels to 1 opacity before applying flood + const componentTransfer = Blockly.utils.dom.createSvgElement( + "feComponentTransfer", + { result: "outBlur" }, + stackGlowFilter + ); + Blockly.utils.dom.createSvgElement( + "feFuncA", + { + type: "table", + tableValues: "0" + " 1".repeat(16), + }, + componentTransfer + ); + // Color the highlight + Blockly.utils.dom.createSvgElement( + "feFlood", + { + "flood-color": Colours.stackGlow, + "flood-opacity": Colours.stackGlowOpacity, + result: "outColor", + }, + stackGlowFilter + ); + Blockly.utils.dom.createSvgElement( + "feComposite", + { + in: "outColor", + in2: "outBlur", + operator: "in", + result: "outGlow", + }, + stackGlowFilter + ); + Blockly.utils.dom.createSvgElement( + "feComposite", + { + in: "SourceGraphic", + in2: "outGlow", + operator: "over", + }, + stackGlowFilter + ); +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000000..0772aee395 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,139 @@ +/** + * @license + * Copyright 2023 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import "./blocks/colour"; +import "./blocks/math"; +import "./blocks/matrix"; +import "./blocks/note"; +import "./blocks/text"; +import "./blocks/vertical_extensions"; +import "./blocks/control"; +import "./blocks/data"; +import "./blocks/event"; +import "./blocks/looks"; +import "./blocks/motion"; +import "./blocks/operators"; +import "./blocks/procedures"; +import "./blocks/sensing"; +import "./blocks/sound"; +import * as scratchBlocksUtils from "./scratch_blocks_utils"; +import * as ScratchVariables from "./variables"; +import "./css"; +import "./renderer/renderer"; +import * as contextMenuItems from "./context_menu_items"; +import { + registerContinuousToolbox, + ContinuousMetrics, +} from "@blockly/continuous-toolbox"; +import { CheckableContinuousFlyout } from "./checkable_continuous_flyout"; +import { buildGlowFilter, glowStack } from "./glows"; +import { ScratchContinuousToolbox } from "./scratch_continuous_toolbox"; +import "./scratch_comment_icon"; +import "./scratch_dragger"; +import "./scratch_variable_map"; +import "./scratch_variable_model"; +import "./scratch_connection_checker"; +import "./scratch_insertion_marker_previewer"; +import "./flyout_checkbox_icon"; +import "./events/events_block_comment_change"; +import "./events/events_block_comment_collapse"; +import "./events/events_block_comment_create"; +import "./events/events_block_comment_delete"; +import "./events/events_block_comment_move"; +import "./events/events_block_comment_resize"; +import "./events/events_scratch_variable_create"; +import { buildShadowFilter } from "./shadows"; +import { registerScratchFieldAngle } from "./fields/scratch_field_angle"; +import { + registerFieldColourSlider, + FieldColourSlider, +} from "./fields/field_colour_slider"; +import { registerScratchFieldDropdown } from "./fields/scratch_field_dropdown"; +import { registerFieldMatrix } from "./fields/field_matrix"; +import { registerFieldNote, FieldNote } from "./fields/field_note"; +import { registerScratchFieldNumber } from "./fields/scratch_field_number"; +import { registerFieldTextInputRemovable } from "./fields/field_textinput_removable"; +import { registerFieldVariableGetter } from "./fields/field_variable_getter"; +import { registerScratchFieldVariable } from "./fields/scratch_field_variable"; +import { registerFieldVerticalSeparator } from "./fields/field_vertical_separator"; +import { registerRecyclableBlockFlyoutInflater } from "./recyclable_block_flyout_inflater"; +import { registerScratchBlockPaster } from "./scratch_block_paster"; +import { registerStatusIndicatorLabelFlyoutInflater } from "./status_indicator_label_flyout_inflater"; +import { registerScratchContinuousCategory } from "./scratch_continuous_category"; + +export * from "blockly/core"; +export * from "./block_reporting"; +export * from "./procedures"; +export * from "../msg/scratch_msgs.js"; +export * from "./constants"; +export { glowStack }; +export { scratchBlocksUtils }; +export { CheckableContinuousFlyout }; +export { ScratchVariables }; +export { contextMenuItems }; +export { FieldColourSlider, FieldNote }; +export { CheckboxBubble } from "./checkbox_bubble"; +export { + StatusIndicatorLabel, + StatusButtonState, +} from "./status_indicator_label"; +export * from "./xml"; + +export function inject(container: Element, options: Blockly.BlocklyOptions) { + registerScratchFieldAngle(); + registerFieldColourSlider(); + registerScratchFieldDropdown(); + registerFieldMatrix(); + registerFieldNote(); + registerScratchFieldNumber(); + registerFieldTextInputRemovable(); + registerFieldVariableGetter(); + registerScratchFieldVariable(); + registerFieldVerticalSeparator(); + registerRecyclableBlockFlyoutInflater(); + registerScratchBlockPaster(); + registerStatusIndicatorLabelFlyoutInflater(); + registerScratchContinuousCategory(); + + Object.assign(options, { + renderer: "scratch", + plugins: { + toolbox: ScratchContinuousToolbox, + flyoutsVerticalToolbox: CheckableContinuousFlyout, + metricsManager: ContinuousMetrics, + }, + }); + const workspace = Blockly.inject(container, options); + + buildGlowFilter(workspace); + buildShadowFilter(workspace); + + Blockly.config.dragRadius = 3; + Blockly.config.snapRadius = 48; + Blockly.config.connectingSnapRadius = 68; + Blockly.config.currentConnectionPreference = 20; + Blockly.config.bumpDelay = 0; + + return workspace; +} + +registerContinuousToolbox(); +Blockly.Scrollbar.scrollbarThickness = Blockly.Touch.TOUCH_ENABLED ? 14 : 11; +Blockly.FlyoutButton.TEXT_MARGIN_X = 40; +Blockly.FlyoutButton.TEXT_MARGIN_Y = 10; +Blockly.ContextMenuRegistry.registry.unregister("blockDisable"); +Blockly.ContextMenuRegistry.registry.unregister("blockInline"); +Blockly.ContextMenuItems.registerCommentOptions(); +Blockly.ContextMenuRegistry.registry.unregister("blockDelete"); +contextMenuItems.registerDeleteBlock(); +contextMenuItems.registerDuplicateBlock(); +Blockly.ContextMenuRegistry.registry.unregister("workspaceDelete"); +contextMenuItems.registerDeleteAll(); +Blockly.comments.CommentView.defaultCommentSize = new Blockly.utils.Size( + 200, + 200 +); diff --git a/src/procedures.ts b/src/procedures.ts new file mode 100644 index 0000000000..3098fa3a45 --- /dev/null +++ b/src/procedures.ts @@ -0,0 +1,462 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2012 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Utility functions for handling procedures. + * @author fraser@google.com (Neil Fraser) + */ + +import * as Blockly from "blockly/core"; +import * as Constants from "./constants"; +import * as scratchBlocksUtils from "../src/scratch_blocks_utils"; + +/** + * Find all user-created procedure definition mutations in a workspace. + * @param root Root workspace. + * @return Array of mutation xml elements. + */ +function allProcedureMutations(root: Blockly.WorkspaceSvg): Element[] { + const blocks = root.getAllBlocks(); + return blocks + .filter((b) => b.type === Constants.PROCEDURES_PROTOTYPE_BLOCK_TYPE) + .map((b) => b.mutationToDom(/* opt_generateShadows */ true)); +} + +/** + * Sorts an array of procedure definition mutations alphabetically. + * (Does not mutate the given array.) + * @param mutations Array of mutation xml elements. + * @return Sorted array of mutation xml elements. + */ +function sortProcedureMutations(mutations: Element[]): Element[] { + return mutations.slice().sort(function (a, b) { + const procCodeA = a.getAttribute("proccode"); + const procCodeB = b.getAttribute("proccode"); + + return scratchBlocksUtils.compareStrings(procCodeA, procCodeB); + }); +} + +/** + * Construct the blocks required by the flyout for the procedure category. + * @param workspace The workspace containing procedures. + * @return Array of XML block elements. + */ +function getProceduresCategory(workspace: Blockly.WorkspaceSvg): Element[] { + var xmlList: Element[] = []; + + addCreateButton(workspace, xmlList); + + // Create call blocks for each procedure defined in the workspace + const mutations = sortProcedureMutations(allProcedureMutations(workspace)); + for (const mutation of mutations) { + // + // + // + const block = document.createElement("block"); + block.setAttribute("type", "procedures_call"); + block.setAttribute("gap", "16"); + block.appendChild(mutation); + xmlList.push(block); + } + return xmlList; +} + +/** + * Create the "Make a Block..." button. + * @param workspace The workspace containing procedures. + * @param xmlList Array of XML block elements to add to. + */ +function addCreateButton(workspace: Blockly.WorkspaceSvg, xmlList: Element[]) { + const button = document.createElement("button"); + const msg = Blockly.Msg.NEW_PROCEDURE; + const callbackKey = "CREATE_PROCEDURE"; + const callback = function () { + // Run the callback after a delay to avoid it getting captured by the React + // modal in scratch-gui and being registered as a click on the scrim that + // dismisses the dialog. + requestAnimationFrame(() => { + setTimeout(() => { + createProcedureDefCallback(workspace); + }); + }); + }; + button.setAttribute("text", msg); + button.setAttribute("callbackKey", callbackKey); + workspace.registerButtonCallback(callbackKey, callback); + xmlList.push(button); +} + +/** + * Find all callers of a named procedure. + * @param name Name of procedure (procCode in scratch-blocks). + * @param workspace The workspace to find callers in. + * @param definitionRoot The root of the stack where the + * procedure is defined. + * @param allowRecursive True if the search should include recursive + * procedure calls. False if the search should ignore the stack starting + * with definitionRoot. + * @return Array of caller blocks. + */ +export function getCallers( + name: string, + workspace: Blockly.WorkspaceSvg, + definitionRoot: Blockly.BlockSvg, + allowRecursive: boolean +): Blockly.BlockSvg[] { + return workspace.getTopBlocks().flatMap((block) => { + if (block.id === definitionRoot.id && !allowRecursive) { + return []; + } + + return block.getDescendants(false).filter((descendant) => { + return ( + isProcedureBlock(descendant) && + descendant.type === Constants.PROCEDURES_CALL_BLOCK_TYPE && + descendant.getProcCode() === name + ); + }); + }); +} + +/** + * Find and edit all callers with a procCode using a new mutation. + * @param name Name of procedure (procCode in scratch-blocks). + * @param workspace The workspace to find callers in. + * @param mutation New mutation for the callers. + */ +function mutateCallersAndPrototype( + name: string, + workspace: Blockly.WorkspaceSvg, + mutation: Element +) { + const defineBlock = getDefineBlock(name, workspace); + const prototypeBlock = getPrototypeBlock(name, workspace); + if (!(defineBlock && prototypeBlock)) { + alert("No define block on workspace"); // TODO decide what to do about this. + return; + } + + const callers = getCallers( + name, + defineBlock.workspace, + defineBlock, + true /* allowRecursive */ + ); + callers.push(prototypeBlock); + Blockly.Events.setGroup(true); + callers.forEach((caller) => { + const oldMutationDom = caller.mutationToDom(); + const oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom); + caller.domToMutation(mutation); + const newMutationDom = caller.mutationToDom(); + const newMutation = newMutationDom && Blockly.Xml.domToText(newMutationDom); + if (oldMutation !== newMutation) { + Blockly.Events.fire( + new (Blockly.Events.get(Blockly.Events.BLOCK_CHANGE))( + caller, + "mutation", + null, + oldMutation, + newMutation + ) + ); + } + }); + Blockly.Events.setGroup(false); +} + +/** + * Find the definition block for the named procedure. + * @param procCode The identifier of the procedure. + * @param workspace The workspace to search. + * @return The procedure definition block, or undefined if not found. + */ +function getDefineBlock( + procCode: string, + workspace: Blockly.WorkspaceSvg +): Blockly.BlockSvg | undefined { + // Assume that a procedure definition is a top block. + return workspace.getTopBlocks(false).find((block) => { + if (block.type === Constants.PROCEDURES_DEFINITION_BLOCK_TYPE) { + const prototypeBlock = block + .getInput("custom_block") + .connection.targetBlock() as Blockly.BlockSvg; + return ( + isProcedureBlock(prototypeBlock) && + prototypeBlock.getProcCode() === procCode + ); + } + + return false; + }); +} + +/** + * Find the prototype block for the named procedure. + * @param procCode The identifier of the procedure. + * @param workspace The workspace to search. + * @return The procedure prototype block, or undefined if not found. + */ +function getPrototypeBlock( + procCode: string, + workspace: Blockly.WorkspaceSvg +): Blockly.BlockSvg | undefined { + const defineBlock = getDefineBlock(procCode, workspace); + if (defineBlock) { + return defineBlock + .getInput("custom_block") + .connection.targetBlock() as Blockly.BlockSvg; + } + return undefined; +} + +/** + * Create a mutation for a brand new custom procedure. + * @return The mutation for a new custom procedure + */ +function newProcedureMutation(): Element { + const mutationText = ` + + + + `; + return Blockly.utils.xml.textToDom(mutationText).firstElementChild; +} + +/** + * Callback to create a new procedure custom command block. + * @param workspace The workspace to create the new procedure on. + */ +function createProcedureDefCallback(workspace: Blockly.WorkspaceSvg) { + ScratchProcedures.externalProcedureDefCallback( + newProcedureMutation(), + createProcedureCallbackFactory(workspace) + ); +} + +/** + * Callback factory for adding a new custom procedure from a mutation. + * @param workspace The workspace to create the new procedure on. + * @return callback for creating the new custom procedure. + */ +function createProcedureCallbackFactory( + workspace: Blockly.WorkspaceSvg +): (mutation?: Element) => void { + return (mutation?: Element) => { + if (!mutation) return; + + const blockText = ` + + + + + ${Blockly.Xml.domToText(mutation)} + + + + `; + const blockDom = Blockly.utils.xml.textToDom(blockText).firstElementChild; + Blockly.Events.setGroup(true); + const block = Blockly.Xml.domToBlock( + blockDom, + workspace + ) as Blockly.BlockSvg; + Blockly.renderManagement.finishQueuedRenders().then(() => { + // To convert from pixel units to workspace units + const scale = workspace.scale; + // Position the block so that it is at the top left of the visible + // workspace, padded from the edge by 30 units. Position in the top right + // if RTL. + let posX = -workspace.scrollX; + if (workspace.RTL) { + posX += workspace.getMetrics().contentWidth - 30; + } else { + posX += 30; + } + block.moveBy(posX / scale, (-workspace.scrollY + 30) / scale); + block.scheduleSnapAndBump(); + Blockly.Events.setGroup(false); + }); + }; +} + +/** + * Callback to open the modal for editing custom procedures. + * @param block The block that was right-clicked. + */ +function editProcedureCallback(block: Blockly.BlockSvg) { + // Edit can come from one of three block types (call, define, prototype) + // Normalize by setting the block to the prototype block for the procedure. + let prototypeBlock: Blockly.BlockSvg; + if (block.type === Constants.PROCEDURES_DEFINITION_BLOCK_TYPE) { + const input = block.getInput("custom_block"); + if (!input) { + alert("Bad input"); // TODO: Decide what to do about this. + return; + } + const conn = input.connection; + if (!conn) { + alert("Bad connection"); // TODO: Decide what to do about this. + return; + } + const innerBlock = conn.targetBlock(); + if ( + !innerBlock || + innerBlock.type !== Constants.PROCEDURES_PROTOTYPE_BLOCK_TYPE + ) { + alert("Bad inner block"); // TODO: Decide what to do about this. + return; + } + prototypeBlock = innerBlock as Blockly.BlockSvg; + } else if ( + block.type === Constants.PROCEDURES_CALL_BLOCK_TYPE && + isProcedureBlock(block) + ) { + // This is a call block, find the prototype corresponding to the procCode. + // Make sure to search the correct workspace, call block can be in flyout. + const workspaceToSearch = block.workspace.isFlyout + ? block.workspace.targetWorkspace + : block.workspace; + prototypeBlock = getPrototypeBlock(block.getProcCode(), workspaceToSearch); + } else { + prototypeBlock = block; + } + // Block now refers to the procedure prototype block, it is safe to proceed. + ScratchProcedures.externalProcedureDefCallback( + prototypeBlock.mutationToDom(), + editProcedureCallbackFactory(prototypeBlock) + ); +} + +/** + * Callback factory for editing an existing custom procedure. + * @param block The procedure prototype block being edited. + * @return Callback for editing the custom procedure. + */ +function editProcedureCallbackFactory( + block: Blockly.BlockSvg +): (mutation?: Element) => void { + return (mutation?: Element) => { + if (mutation && isProcedureBlock(block)) { + mutateCallersAndPrototype(block.getProcCode(), block.workspace, mutation); + } + }; +} + +/** + * Make a context menu option for editing a custom procedure. + * This appears in the context menu for procedure definitions and procedure + * calls. + * @param block The block where the right-click originated. + * @return A menu option, containing text, enabled, and a callback. + */ +function makeEditOption( + block: Blockly.BlockSvg +): Blockly.ContextMenuRegistry.ContextMenuOption { + return { + enabled: true, + text: Blockly.Msg.EDIT_PROCEDURE, + callback: () => { + editProcedureCallback(block); + }, + scope: block, + weight: 7, + }; +} + +/** + * Callback to try to delete a custom block definitions. + * @param procCode The identifier of the procedure to delete. + * @param definitionRoot The root block of the stack that defines the custom + * procedure. + * @return True if the custom procedure was deleted, false otherwise. + */ +function deleteProcedureDefCallback( + procCode: string, + definitionRoot: Blockly.BlockSvg +): boolean { + const callers = getCallers( + procCode, + definitionRoot.workspace, + definitionRoot, + false /* allowRecursive */ + ); + if (callers.length > 0) { + return false; + } + + const workspace = definitionRoot.workspace; + // Bypass the checkAndDelete provided by the procedure block mixin + Blockly.BlockSvg.prototype.checkAndDelete.call(definitionRoot); + return true; +} + +/** + * Returns whether the given block is a procedure block and narrows its type. + * + * @param block The block to check. + * @returns True if the block is a procedure block, otherwise false. + */ +export function isProcedureBlock( + block: Blockly.BlockSvg +): block is ProcedureBlock { + return ( + block.type === Constants.PROCEDURES_CALL_BLOCK_TYPE || + block.type === Constants.PROCEDURES_DECLARATION_BLOCK_TYPE || + block.type === Constants.PROCEDURES_PROTOTYPE_BLOCK_TYPE + ); +} + +/** + * Interface for procedure blocks, which have the getProcCode method added + * through an extension. + */ +interface ProcedureBlock extends Blockly.BlockSvg { + getProcCode(): string; +} + +/** + * Type for a callback function invoked after a procedure is modified. + */ +type ProcedureDefCallback = ( + mutation: Element, + postEditCallback: (mutation?: Element) => void +) => void; + +const ScratchProcedures: { + externalProcedureDefCallback: ProcedureDefCallback | undefined; + createProcedureDefCallback: typeof createProcedureDefCallback; + deleteProcedureDefCallback: typeof deleteProcedureDefCallback; + getProceduresCategory: typeof getProceduresCategory; + makeEditOption: typeof makeEditOption; +} = { + externalProcedureDefCallback: undefined, + createProcedureDefCallback, + deleteProcedureDefCallback, + getProceduresCategory, + makeEditOption, +}; +export { ScratchProcedures }; diff --git a/src/recyclable_block_flyout_inflater.ts b/src/recyclable_block_flyout_inflater.ts new file mode 100644 index 0000000000..96c22a46fe --- /dev/null +++ b/src/recyclable_block_flyout_inflater.ts @@ -0,0 +1,51 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import { CheckboxBubble } from "./checkbox_bubble"; +import { RecyclableBlockFlyoutInflater as BlocklyRecyclableBlockFlyoutInflater } from "@blockly/continuous-toolbox"; + +/** + * A block inflater that caches and reuses blocks to improve performance. + */ +export class RecyclableBlockFlyoutInflater extends BlocklyRecyclableBlockFlyoutInflater { + /** + * Creates a block on the flyout workspace from the given block definition. + * + * @param state A JSON representation of a block to load. + * @param flyoutWorkspace The flyout's workspace. + * @returns The newly created block. + */ + load( + state: Blockly.utils.toolbox.BlockInfo, + flyoutWorkspace: Blockly.WorkspaceSvg + ): Blockly.FlyoutItem { + const flyoutItem = super.load(state, flyoutWorkspace); + const block = flyoutItem.getElement(); + if ("checkboxInFlyout" in block && block.checkboxInFlyout) { + block.moveBy( + (flyoutWorkspace.RTL ? -1 : 1) * + (CheckboxBubble.CHECKBOX_SIZE + CheckboxBubble.CHECKBOX_MARGIN), + 0 + ); + } + + return flyoutItem; + } +} + +/** + * Registers the recyclable block flyout inflater, replacing the standard + * block flyout inflater. + */ +export function registerRecyclableBlockFlyoutInflater() { + Blockly.registry.unregister(Blockly.registry.Type.FLYOUT_INFLATER, "block"); + Blockly.registry.register( + Blockly.registry.Type.FLYOUT_INFLATER, + "block", + RecyclableBlockFlyoutInflater + ); +} diff --git a/src/renderer/bowler_hat.ts b/src/renderer/bowler_hat.ts new file mode 100644 index 0000000000..adf8187fd3 --- /dev/null +++ b/src/renderer/bowler_hat.ts @@ -0,0 +1,17 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; + +export class BowlerHat extends Blockly.blockRendering.Hat { + constructor(constants: Blockly.blockRendering.ConstantProvider) { + super(constants); + // Calculated dynamically by computeBounds_(). + this.width = 0; + this.height = 20; + this.ascenderHeight = this.height; + } +} diff --git a/src/renderer/constants.ts b/src/renderer/constants.ts new file mode 100644 index 0000000000..a8fe060d0d --- /dev/null +++ b/src/renderer/constants.ts @@ -0,0 +1,59 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; + +export class ConstantProvider extends Blockly.zelos.ConstantProvider { + REPLACEMENT_GLOW_COLOUR = "#ffffff"; + SELECTED_GLOW_COLOUR = "#ffffff"; + + /** + * Sets the visual theme used to render the workspace. + * This method also synthesizes a "selected" theme, used to color blocks with + * dropdown menus when the menu is active. Additionally, if the theme's block + * styles contain any raw color values, corresponding CSS variables will be + * created/overridden so that those colors can be dynamically referenced in + * stylesheets. + * + * @param theme The new theme to apply. + */ + setTheme(theme: Blockly.Theme) { + const root = document.querySelector(":root") as HTMLElement; + for (const [key, colour] of Object.entries(theme.blockStyles)) { + if (typeof colour !== "object") { + const varKey = `--colour-${key}`; + root.style.setProperty(varKey, colour); + } else { + const style = { + colourPrimary: + "colourQuaternary" in colour + ? `${colour.colourQuaternary}` + : colour.colourTertiary, + colourSecondary: + "colourQuaternary" in colour + ? `${colour.colourQuaternary}` + : colour.colourTertiary, + colourTertiary: + "colourQuaternary" in colour + ? `${colour.colourQuaternary}` + : colour.colourTertiary, + colourQuaternary: + "colourQuaternary" in colour + ? `${colour.colourQuaternary}` + : colour.colourTertiary, + hat: "", + }; + theme.setBlockStyle(`${key}_selected`, style); + } + } + super.setTheme(theme); + } + + createDom(svg: SVGElement, tagName: string, selector: string) { + super.createDom(svg, tagName, selector); + this.selectedGlowFilterId = ""; + } +} diff --git a/src/renderer/drawer.ts b/src/renderer/drawer.ts new file mode 100644 index 0000000000..0585a22fa3 --- /dev/null +++ b/src/renderer/drawer.ts @@ -0,0 +1,56 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import type { RenderInfo } from "./render_info"; + +export class Drawer extends Blockly.zelos.Drawer { + info_: RenderInfo; + drawStatementInput_(row: Blockly.blockRendering.Row) { + if (this.info_.isBowlerHatBlock()) { + // Bowler hat blocks have straight sides with no C-shape/indentation for + // statement blocks. + this.drawRightSideRow_(row); + this.positionStatementInputConnection_(row); + } else { + super.drawStatementInput_(row); + } + } + + drawRightSideRow_(row: Blockly.blockRendering.Row) { + if ( + this.info_.isBowlerHatBlock() && + Blockly.blockRendering.Types.isSpacer(row) + ) { + // Multi-row bowler hat blocks are not supported, this may need + // adjustment to do so. + this.outlinePath_ += Blockly.utils.svgPaths.lineOnAxis( + "V", + row.yPos + row.height + ); + } else { + super.drawRightSideRow_(row); + } + } + + drawTop_() { + super.drawTop_(); + // This is a horrible hack, but the superclass' implementation of drawTop_() + // provides no way to cleanly override a hat's path without copying and + // pasting the entire implementation here. We know that there will only be + // one hat on a block, and its path is a known constant, so we just find and + // replace it with the desired bowler hat path here. + // If https://github.com/google/blockly/issues/7292 is resolved, this should + // be revisited. + if (this.info_.isBowlerHatBlock()) { + const capHatPath = this.constants_.START_HAT.path; + const bowlerHatPath = `a20,20 0 0,1 20,-20 l ${ + this.info_.width - 40 + } 0 a20,20 0 0,1 20,20`; + this.outlinePath_ = this.outlinePath_.replace(capHatPath, bowlerHatPath); + } + } +} diff --git a/src/renderer/path_object.ts b/src/renderer/path_object.ts new file mode 100644 index 0000000000..eb765242f6 --- /dev/null +++ b/src/renderer/path_object.ts @@ -0,0 +1,35 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; + +/** + * An object that handles creating and setting each of the SVG elements + * used by the renderer. + */ +export class PathObject extends Blockly.zelos.PathObject { + /** + * Apply the stored colours to the block's path, taking into account whether + * the paths belong to a shadow block. + * + * @param block The source block. + */ + applyColour(block: Blockly.BlockSvg) { + super.applyColour(block); + + // These blocks are special in that, while they are technically shadow + // blocks when contained in a procedure definition/prototype, their parent + // (the sample procedure caller block embedded in the definition block) is + // also a shadow, so they need to use normal block colors in order to + // provide contrast with it. + if ( + block.type === "argument_reporter_string_number" || + block.type === "argument_reporter_boolean" + ) { + this.svgPath.setAttribute("fill", this.style.colourPrimary); + } + } +} diff --git a/src/renderer/render_info.ts b/src/renderer/render_info.ts new file mode 100644 index 0000000000..81f1bcf033 --- /dev/null +++ b/src/renderer/render_info.ts @@ -0,0 +1,110 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import { BowlerHat } from "./bowler_hat"; + +export class RenderInfo extends Blockly.zelos.RenderInfo { + populateTopRow_() { + if (this.isBowlerHatBlock()) { + const bowlerHat = new BowlerHat(this.constants_); + this.topRow.elements.push( + new Blockly.blockRendering.SquareCorner(this.constants_) + ); + this.topRow.elements.push(bowlerHat); + this.topRow.elements.push( + new Blockly.blockRendering.SquareCorner(this.constants_) + ); + this.topRow.minHeight = 0; + this.topRow.capline = bowlerHat.ascenderHeight; + } else { + super.populateTopRow_(); + } + } + + populateBottomRow_() { + super.populateBottomRow_(); + if (this.isBowlerHatBlock()) { + this.bottomRow.minHeight = this.constants_.MEDIUM_PADDING; + } + } + + computeBounds_() { + super.computeBounds_(); + if (this.isBowlerHatBlock()) { + // Resize the render info to the same width as the widest part of a + // bowler hat block. + const statementRow = this.rows.find((r) => r.hasStatement); + this.width = + statementRow.widthWithConnectedBlocks - + statementRow.elements.find((e) => + Blockly.blockRendering.Types.isInput(e) + ).width + + this.constants_.MEDIUM_PADDING; + + // The bowler hat's width is the same as the block's width, so it can't + // be derived from the constants like a normal hat and has to be set here. + const hat = this.topRow.elements.find((e) => + Blockly.blockRendering.Types.isHat(e) + ); + hat.width = this.width; + this.topRow.measure(); + } + } + + getInRowSpacing_( + prev: Blockly.blockRendering.Measurable, + next: Blockly.blockRendering.Measurable + ): number { + if ( + this.isBowlerHatBlock() && + ((prev && Blockly.blockRendering.Types.isHat(prev)) || + (next && Blockly.blockRendering.Types.isHat(next))) + ) { + // Bowler hat rows have no spacing/gaps, just the hat. + return 0; + } + + return super.getInRowSpacing_(prev, next); + } + + getSpacerRowHeight_( + prev: Blockly.blockRendering.Row, + next: Blockly.blockRendering.Row + ): number { + if (this.isBowlerHatBlock() && prev === this.topRow) { + return 0; + } + + return super.getSpacerRowHeight_(prev, next); + } + + getElemCenterline_( + row: Blockly.blockRendering.Row, + elem: Blockly.blockRendering.Measurable + ): number { + if (this.isBowlerHatBlock() && Blockly.blockRendering.Types.isField(elem)) { + return row.yPos + row.height / 2; + } else if ( + "isScratchExtension" in this.block_ && + this.block_.isScratchExtension && + Blockly.blockRendering.Types.isField(elem) && + (elem as Blockly.blockRendering.Field).field instanceof + Blockly.FieldImage && + (elem as Blockly.blockRendering.Field).field === + this.block_.inputList[0].fieldRow[0] && + this.block_.previousConnection + ) { + // Vertically center the icon on extension blocks. + return super.getElemCenterline_(row, elem) + this.constants_.GRID_UNIT; + } + return super.getElemCenterline_(row, elem); + } + + isBowlerHatBlock() { + return this.block_.hat === "bowler"; + } +} diff --git a/src/renderer/renderer.ts b/src/renderer/renderer.ts new file mode 100644 index 0000000000..2c14613889 --- /dev/null +++ b/src/renderer/renderer.ts @@ -0,0 +1,76 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import { Drawer } from "./drawer"; +import { RenderInfo } from "./render_info"; +import { ConstantProvider } from "./constants"; +import { PathObject } from "./path_object"; + +/** + * Custom renderer for Scratch-style blocks. + */ +export class ScratchRenderer extends Blockly.zelos.Renderer { + /** + * Create a new instance of the renderer's drawer. + * + * @param block The block to render. + * @param infoAn object containing all the information needed to render this + * block. + * @returns The drawer. + */ + makeDrawer_(block: Blockly.BlockSvg, info: RenderInfo): Drawer { + return new Drawer(block, info); + } + + /** + * Create a new instance of the renderer's render info object. + * + * @param block The block to measure. + * @returns The render info object. + */ + makeRenderInfo_(block: Blockly.BlockSvg): RenderInfo { + return new RenderInfo(this, block); + } + + /** + * Create a new instance of the renderer's constant provider. + * + * @returns The constant provider. + */ + makeConstants_(): ConstantProvider { + return new ConstantProvider(); + } + + /** + * Create a new instance of a renderer path object. + * + * @param root The root SVG element. + * @param style The style object to use for colouring. + * @returns The renderer path object. + */ + makePathObject( + root: SVGElement, + style: Blockly.Theme.BlockStyle + ): PathObject { + return new PathObject(root, style, this.getConstants()); + } + + /** + * Determine whether or not to highlight a connection. + * + * @param connection The connection to determine whether or not to highlight. + * @returns True if we should highlight the connection. + */ + shouldHighlightConnection(connection: Blockly.RenderedConnection): boolean { + return ( + connection.type === Blockly.ConnectionType.INPUT_VALUE && + connection.getCheck()?.includes("Boolean") + ); + } +} + +Blockly.blockRendering.register("scratch", ScratchRenderer); diff --git a/src/scratch_block_paster.ts b/src/scratch_block_paster.ts new file mode 100644 index 0000000000..2b07d64e26 --- /dev/null +++ b/src/scratch_block_paster.ts @@ -0,0 +1,58 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import type { ScratchCommentIcon } from "./scratch_comment_icon"; + +/** + * Class responsible for handling the pasting of copied blocks. + */ +class ScratchBlockPaster extends Blockly.clipboard.BlockPaster { + /** + * Deserializes the given block data onto the workspace. + * + * @param copyData The serialized block state to create a copy of on the + * workspace. + * @param workspace The workspace to paste the block onto. + * @param coordinate The location to paste the block. + */ + paste( + copyData: Blockly.clipboard.BlockCopyData, + workspace: Blockly.WorkspaceSvg, + coordinate: Blockly.utils.Coordinate + ) { + const block = super.paste(copyData, workspace, coordinate); + if ( + block?.type === "argument_reporter_boolean" || + block?.type === "argument_reporter_string_number" + ) { + block.setDragStrategy(new Blockly.dragging.BlockDragStrategy(block)); + } + + // Deserialization of blocks suppresses events, so even though this gets + // fired for blocks with comments, the VM will never receive it, causing its + // state to get out of sync. Manually fire it here (after suppression has + // been turned off) if needed. + const commentIcon = block.getIcon(Blockly.icons.IconType.COMMENT); + if (commentIcon) { + (commentIcon as ScratchCommentIcon).fireCreateEvent(); + } + + return block; + } +} + +/** + * Unregisters the default block paster and registers ScratchBlockPaster in its + * place. + */ +export function registerScratchBlockPaster() { + Blockly.clipboard.registry.unregister(ScratchBlockPaster.TYPE); + Blockly.clipboard.registry.register( + ScratchBlockPaster.TYPE, + new ScratchBlockPaster() + ); +} diff --git a/core/blocks.js b/src/scratch_blocks_utils.ts similarity index 55% rename from core/blocks.js rename to src/scratch_blocks_utils.ts index c27be1d40d..12528fc963 100644 --- a/core/blocks.js +++ b/src/scratch_blocks_utils.ts @@ -2,7 +2,7 @@ * @license * Visual Blocks Editor * - * Copyright 2013 Google Inc. + * Copyright 2018 Google Inc. * https://developers.google.com/blockly/ * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,19 +19,21 @@ */ /** - * @fileoverview A mapping of block type names to block prototype objects. - * @author spertus@google.com (Ellen Spertus) + * @fileoverview Utility methods for Scratch Blocks but not Blockly. + * @author fenichel@google.com (Rachel Fenichel) */ -'use strict'; +import * as Blockly from "blockly/core"; /** - * A mapping of block type names to block prototype objects. - * @name Blockly.Blocks - */ -goog.provide('Blockly.Blocks'); - -/* - * A mapping of block type names to block prototype objects. - * @type {!Object.} + * Compare strings with natural number sorting. + * + * @param str1 First input. + * @param str2 Second input. + * @returns -1, 0, or 1 to signify greater than, equality, or less than. */ -Blockly.Blocks = new Object(null); +export function compareStrings(str1: string, str2: string): number { + return str1.localeCompare(str2, [], { + sensitivity: "base", + numeric: true, + }); +} diff --git a/src/scratch_comment_bubble.ts b/src/scratch_comment_bubble.ts new file mode 100644 index 0000000000..d89cfb3fee --- /dev/null +++ b/src/scratch_comment_bubble.ts @@ -0,0 +1,202 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; + +/** + * A Scratch-style comment bubble for block comments. + * @implements {IBubble} + * @implements {ISelectable} + */ +export class ScratchCommentBubble + extends Blockly.comments.CommentView + implements Blockly.IBubble, Blockly.ISelectable +{ + id: string; + private sourceBlock: Blockly.BlockSvg; + private anchor?: Blockly.utils.Coordinate; + private anchorChain?: SVGLineElement; + private dragStartLocation?: Blockly.utils.Coordinate; + + constructor(sourceBlock: Blockly.BlockSvg) { + const commentId = `${sourceBlock.id}_comment`; + super(sourceBlock.workspace, commentId); + this.sourceBlock = sourceBlock; + this.disposing = false; + this.id = commentId; + this.setPlaceholderText(Blockly.Msg.WORKSPACE_COMMENT_DEFAULT_TEXT); + this.getSvgRoot().setAttribute( + "style", + `--colour-commentBorder: ${sourceBlock.getColourTertiary()};` + ); + this.getSvgRoot().setAttribute("id", this.id); + + Blockly.browserEvents.conditionalBind( + this.getSvgRoot(), + "pointerdown", + this, + this.startGesture + ); + // Don't zoom with mousewheel; let it scroll instead. + Blockly.browserEvents.conditionalBind( + this.getSvgRoot(), + "wheel", + this, + (e: WheelEvent) => { + e.stopPropagation(); + } + ); + } + + setDeleteStyle(enable: boolean) {} + showContextMenu() {} + setDragging(start: boolean) {} + select() {} + unselect() {} + + isMovable() { + return true; + } + + moveDuringDrag(newLocation: Blockly.utils.Coordinate) { + this.moveTo(newLocation); + } + + moveTo(xOrCoordinate: number, y: number): void; + moveTo(xOrCoordinate: Blockly.utils.Coordinate): void; + moveTo(xOrCoordinate: Blockly.utils.Coordinate | number, y?: number) { + const destination = + xOrCoordinate instanceof Blockly.utils.Coordinate + ? xOrCoordinate + : new Blockly.utils.Coordinate(xOrCoordinate, y); + super.moveTo(destination); + this.redrawAnchorChain(); + } + + startGesture(e: PointerEvent) { + const gesture = this.workspace.getGesture(e); + if (gesture) { + gesture.handleBubbleStart(e, this); + Blockly.common.setSelected(this); + } + } + + startDrag(event: PointerEvent) { + this.dragStartLocation = this.getRelativeToSurfaceXY(); + this.workspace.setResizesEnabled(false); + this.workspace.getLayerManager()?.moveToDragLayer(this); + Blockly.utils.dom.addClass(this.getSvgRoot(), "blocklyDragging"); + } + + drag(newLocation: Blockly.utils.Coordinate, event: Event) { + this.moveTo(newLocation); + } + + endDrag() { + this.workspace + .getLayerManager() + ?.moveOffDragLayer(this, Blockly.layers.BUBBLE); + this.workspace.setResizesEnabled(false); + Blockly.utils.dom.removeClass(this.getSvgRoot(), "blocklyDragging"); + Blockly.Events.fire( + new (Blockly.Events.get("block_comment_move"))( + this, + this.dragStartLocation, + this.getRelativeToSurfaceXY() + ) + ); + } + + revertDrag() { + this.moveTo(this.dragStartLocation); + } + + setAnchorLocation(newAnchor: Blockly.utils.Coordinate) { + const oldAnchor = this.anchor; + const alreadyAnchored = !!this.anchor; + this.anchor = newAnchor; + if (!alreadyAnchored) { + this.dropAnchor(); + } else { + const oldLocation = this.getRelativeToSurfaceXY(); + const delta = Blockly.utils.Coordinate.difference(this.anchor, oldAnchor); + const newLocation = Blockly.utils.Coordinate.sum(oldLocation, delta); + this.moveTo(newLocation); + } + } + + dropAnchor() { + const verticalOffset = 16; + this.moveTo( + this.anchor.x + 40 * (this.workspace.RTL ? -1 : 1), + this.anchor.y - verticalOffset + ); + const location = this.getRelativeToSurfaceXY(); + this.anchorChain = Blockly.utils.dom.createSvgElement( + Blockly.utils.Svg.LINE, + { + x1: this.anchor.x - location.x, + y1: this.anchor.y - location.y, + x2: (this.getSize().width / 2) * (this.workspace.RTL ? -1 : 1), + y2: verticalOffset, + style: `stroke: ${this.sourceBlock.getColourTertiary()}; stroke-width: 1`, + }, + this.getSvgRoot() + ); + this.getSvgRoot().insertBefore( + this.anchorChain, + this.getSvgRoot().firstChild + ); + } + + redrawAnchorChain() { + if (!this.anchorChain) return; + + const location = this.getRelativeToSurfaceXY(); + this.anchorChain.setAttribute("x1", `${this.anchor.x - location.x}`); + this.anchorChain.setAttribute("y1", `${this.anchor.y - location.y}`); + } + + getId() { + return this.id; + } + + getSourceBlock() { + return this.sourceBlock; + } + + dispose() { + this.disposing = true; + Blockly.utils.dom.removeNode(this.anchorChain); + if (this.sourceBlock) { + Blockly.Events.fire( + new (Blockly.Events.get("block_comment_delete"))(this, this.sourceBlock) + ); + const block = this.sourceBlock; + this.sourceBlock = null; + if (!block.isDeadOrDying()) { + block.setCommentText(null); + } + } + super.dispose(); + } + + getFocusableElement() { + return this.getSvgRoot(); + } + + getFocusableTree() { + return this.workspace; + } + + onNodeFocus() {} + + onNodeBlur() {} + + canBeFocused() { + return true; + } +} diff --git a/src/scratch_comment_icon.ts b/src/scratch_comment_icon.ts new file mode 100644 index 0000000000..ac6b9c8e0b --- /dev/null +++ b/src/scratch_comment_icon.ts @@ -0,0 +1,227 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import { ScratchCommentBubble } from "./scratch_comment_bubble"; + +interface CommentState { + text: string; + height: number; + width: number; + x: number; + y: number; + collapsed: boolean; +} + +/** + * Custom comment icon that draws no icon indicator, used for block comments. + */ +export class ScratchCommentIcon + extends Blockly.icons.Icon + implements Blockly.ISerializable, Blockly.IHasBubble +{ + private commentBubble: ScratchCommentBubble; + private onTextChangedListener: (oldText: string, newText: string) => void; + private onSizeChangedListener: ( + oldSize: Blockly.utils.Size, + newSize: Blockly.utils.Size + ) => void; + private onCollapseListener: (collapsed: boolean) => void; + + constructor(protected sourceBlock: Blockly.BlockSvg) { + super(sourceBlock); + this.commentBubble = new ScratchCommentBubble(this.sourceBlock); + this.fireCreateEvent(); + this.onTextChangedListener = this.onTextChanged.bind(this); + this.onSizeChangedListener = this.onSizeChanged.bind(this); + this.onCollapseListener = this.onCollapsed.bind(this); + this.commentBubble.addTextChangeListener(this.onTextChangedListener); + this.commentBubble.addSizeChangeListener(this.onSizeChangedListener); + this.commentBubble.addOnCollapseListener(this.onCollapseListener); + } + + getType(): Blockly.icons.IconType { + return Blockly.icons.IconType.COMMENT; + } + + initView(pointerDownListener: (e: PointerEvent) => void) { + // Scratch comments have no indicator icon on the block. + return; + } + + getSize(): Blockly.utils.Size { + // Awful hack to cancel out the default padding added to icons. + return new Blockly.utils.Size(-8, 0); + } + + getAnchorPoint(): Blockly.utils.Coordinate { + const blockRect = this.sourceBlock.getBoundingRectangleWithoutChildren(); + const y = blockRect.top + this.offsetInBlock.y; + const x = this.sourceBlock.workspace.RTL ? blockRect.left : blockRect.right; + return new Blockly.utils.Coordinate(x, y); + } + + onLocationChange(blockOrigin: Blockly.utils.Coordinate) { + if (!this.sourceBlock || !this.commentBubble) return; + + if (this.sourceBlock.isInsertionMarker()) { + this.commentBubble.dispose(); + return; + } + + super.onLocationChange(blockOrigin); + const oldBubbleLocation = this.commentBubble.getRelativeToSurfaceXY(); + this.commentBubble.setAnchorLocation(this.getAnchorPoint()); + const newBubbleLocation = this.commentBubble.getRelativeToSurfaceXY(); + Blockly.Events.fire( + new (Blockly.Events.get("block_comment_move"))( + this.commentBubble, + oldBubbleLocation, + newBubbleLocation + ) + ); + } + + setText(text: string) { + this.commentBubble?.setText(text); + } + + getText(): string { + return this.commentBubble?.getText() ?? ""; + } + + onTextChanged(oldText: string, newText: string) { + Blockly.Events.fire( + new (Blockly.Events.get(Blockly.Events.BLOCK_CHANGE))( + this.sourceBlock, + "comment", + null, + oldText, + newText + ) + ); + Blockly.Events.fire( + new (Blockly.Events.get("block_comment_change"))( + this.commentBubble, + oldText, + newText + ) + ); + } + + onCollapsed(collapsed: boolean) { + Blockly.Events.fire( + new (Blockly.Events.get("block_comment_collapse"))( + this.commentBubble, + collapsed + ) + ); + } + + onSizeChanged(oldSize: Blockly.utils.Size, newSize: Blockly.utils.Size) { + Blockly.Events.fire( + new (Blockly.Events.get("block_comment_resize"))( + this.commentBubble, + oldSize, + newSize + ) + ); + } + + setBubbleSize(size: Blockly.utils.Size) { + this.commentBubble?.setSize(size); + } + + getBubbleSize(): Blockly.utils.Size { + return this.commentBubble?.getSize() ?? new Blockly.utils.Size(0, 0); + } + + setBubbleLocation(newLocation: Blockly.utils.Coordinate) { + const oldLocation = this.getBubbleLocation(); + this.commentBubble?.moveTo(newLocation); + Blockly.Events.fire( + new (Blockly.Events.get("block_comment_move"))( + this.commentBubble, + oldLocation, + newLocation + ) + ); + } + + getBubbleLocation(): Blockly.utils.Coordinate { + return this.commentBubble?.getRelativeToSurfaceXY(); + } + + saveState(): CommentState | null { + if (!this.commentBubble) return null; + + const size = this.getBubbleSize(); + const bubbleLocation = this.commentBubble.getRelativeToSurfaceXY(); + const delta = Blockly.utils.Coordinate.difference( + bubbleLocation, + this.workspaceLocation + ); + return { + text: this.getText(), + height: size.height, + width: size.width, + x: delta.x, + y: delta.y, + collapsed: this.commentBubble.isCollapsed(), + }; + } + + loadState(state: CommentState) { + Blockly.Events.setGroup(true); + this.setText(state["text"]); + this.setBubbleSize(new Blockly.utils.Size(state["width"], state["height"])); + const delta = new Blockly.utils.Coordinate(state["x"], state["y"]); + const newBubbleLocation = Blockly.utils.Coordinate.sum( + this.workspaceLocation, + delta + ); + this.commentBubble.moveTo(newBubbleLocation); + this.commentBubble.setCollapsed(state["collapsed"]); + Blockly.Events.setGroup(false); + } + + bubbleIsVisible(): boolean { + return true; + } + + async setBubbleVisible(visible: boolean) { + this.commentBubble.setCollapsed(!visible); + } + + getBubble() { + return this.commentBubble; + } + + dispose() { + this.commentBubble.dispose(); + super.dispose(); + } + + canBeFocused() { + return false; + } + + /** + * Fires a block comment create event corresponding to this icon's comment. + */ + fireCreateEvent() { + Blockly.Events.fire( + new (Blockly.Events.get("block_comment_create"))(this.commentBubble) + ); + } +} + +Blockly.registry.register( + Blockly.registry.Type.ICON, + Blockly.icons.IconType.COMMENT.toString(), + ScratchCommentIcon, + true +); diff --git a/src/scratch_connection_checker.ts b/src/scratch_connection_checker.ts new file mode 100644 index 0000000000..e36e0adbd8 --- /dev/null +++ b/src/scratch_connection_checker.ts @@ -0,0 +1,44 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; + +/** + * Custom connection checker to restrict which blocks can be connected. + */ +class ScratchConnectionChecker extends Blockly.ConnectionChecker { + /** + * Returns whether or not the two connections should be allowed to connect. + * + * @param a One of the connections to check. + * @param b The other connection to check. + * @param distance The maximum allowable distance between connections. + * @returns True if the connections should be allowed to connect. + */ + doDragChecks( + a: Blockly.RenderedConnection, + b: Blockly.RenderedConnection, + distance: number + ): boolean { + // This check prevents dragging a block into the slot occupied by the + // procedure caller example block in a procedure definition block. + if ( + b.getSourceBlock().type === "procedures_definition" && + b.getParentInput()?.name === "custom_block" + ) { + return false; + } + + return super.doDragChecks(a, b, distance); + } +} + +Blockly.registry.register( + Blockly.registry.Type.CONNECTION_CHECKER, + Blockly.registry.DEFAULT, + ScratchConnectionChecker, + true +); diff --git a/src/scratch_continuous_category.ts b/src/scratch_continuous_category.ts new file mode 100644 index 0000000000..38dfd72e56 --- /dev/null +++ b/src/scratch_continuous_category.ts @@ -0,0 +1,87 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import { ContinuousCategory } from "@blockly/continuous-toolbox"; + +type StatusIndicatorCategoryInfo = Blockly.utils.toolbox.CategoryInfo & { + showStatusButton?: string; +}; + +/** + * Selectable category shown in the Scratch toolbox. + */ +export class ScratchContinuousCategory extends ContinuousCategory { + /** + * Whether this toolbox category has a status indicator button on its label + * in the flyout, typically for extensions that interface with hardware + * devices. + */ + private showStatusButton = false; + + /** Creates a new ScratchContinuousCategory. + * + * @param toolboxItemDef A toolbox item definition. + * @param parentToolbox The toolbox this category is being added to. + * @param opt_parent The parent toolbox category, if any. + */ + constructor( + toolboxItemDef: StatusIndicatorCategoryInfo, + parentToolbox: Blockly.Toolbox, + opt_parent?: Blockly.ICollapsibleToolboxItem + ) { + super(toolboxItemDef, parentToolbox, opt_parent); + this.showStatusButton = toolboxItemDef["showStatusButton"] === "true"; + } + + /** + * Creates a DOM element for this category's icon. + * @returns A DOM element for this category's icon. + */ + createIconDom_(): HTMLElement { + if (this.toolboxItemDef_.iconURI) { + const icon = document.createElement("img"); + icon.src = this.toolboxItemDef_.iconURI; + icon.className = "categoryIconBubble"; + return icon; + } else { + const icon = super.createIconDom_(); + icon.style.border = `1px solid ${this.toolboxItemDef_["secondaryColour"]}`; + return icon; + } + } + + /** + * Sets whether or not this category is selected. + * @param isSelected True if this category is selected. + */ + setSelected(isSelected: boolean) { + super.setSelected(isSelected); + // Prevent hardcoding the background color to grey. + this.rowDiv_.style.backgroundColor = ""; + } + + /** + * Returns whether or not this category's label in the flyout should display + * status indicators. + */ + shouldShowStatusButton() { + return this.showStatusButton; + } +} + +/** Registers this toolbox category and unregisters the default one. */ +export function registerScratchContinuousCategory() { + Blockly.registry.unregister( + Blockly.registry.Type.TOOLBOX_ITEM, + ScratchContinuousCategory.registrationName + ); + Blockly.registry.register( + Blockly.registry.Type.TOOLBOX_ITEM, + ScratchContinuousCategory.registrationName, + ScratchContinuousCategory + ); +} diff --git a/src/scratch_continuous_toolbox.ts b/src/scratch_continuous_toolbox.ts new file mode 100644 index 0000000000..1ddf80386e --- /dev/null +++ b/src/scratch_continuous_toolbox.ts @@ -0,0 +1,86 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import { ContinuousToolbox } from "@blockly/continuous-toolbox"; +import { ScratchContinuousCategory } from "./scratch_continuous_category"; +import { STATUS_INDICATOR_LABEL_TYPE } from "./status_indicator_label_flyout_inflater"; + +/** + * A toolbox that displays items from all categories in one scrolling list. + */ +export class ScratchContinuousToolbox extends ContinuousToolbox { + /** + * List of functions to run after the next time the toolbox renders. + */ + private postRenderCallbacks: (() => void)[] = []; + + refreshSelection() { + // Intentionally a no-op, Scratch manually manages refreshing the toolbox + // via forceRerender(). + } + + /** + * Converts the given toolbox item to a corresponding array of items that + * should appear in the flyout. + * + * @param toolboxItem The toolbox item to convert. + * @returns An array of flyout item definitions. + */ + protected convertToolboxItemToFlyoutItems( + toolboxItem: Blockly.IToolboxItem + ): Blockly.utils.toolbox.FlyoutItemInfoArray { + const contents = super.convertToolboxItemToFlyoutItems(toolboxItem); + if ( + toolboxItem instanceof ScratchContinuousCategory && + toolboxItem.shouldShowStatusButton() + ) { + contents.splice(0, 1, { + kind: STATUS_INDICATOR_LABEL_TYPE, + id: toolboxItem.getId(), + text: toolboxItem.getName(), + }); + } + return contents; + } + + /** + * Forcibly rerenders the toolbox, preserving selection when possible. + */ + forceRerender() { + const selectedCategoryName = this.selectedItem_?.getName(); + this.getFlyout().show(this.getInitialFlyoutContents()); + this.selectCategoryByName(selectedCategoryName); + let callback; + while ((callback = this.postRenderCallbacks.shift())) { + callback(); + } + } + + /** + * Runs the specified callback after the next rerender. + * @param callback A callback to run whenever the toolbox next rerenders. + */ + runAfterRerender(callback: () => void) { + this.postRenderCallbacks.push(callback); + } + + /** + * Returns whether or not the given item should be deselected. + * Prevents items from being deselected without a replacement. + * + * @param oldItem The item that was previously selected. + * @param newItem The item that is proposed to be selected instead. + * @returns True if the old item should be allowed to be deselected. + */ + shouldDeselectItem_( + oldItem: Blockly.ISelectableToolboxItem | null, + newItem: Blockly.ISelectableToolboxItem | null + ) { + if (!newItem) return false; + return super.shouldDeselectItem_(oldItem, newItem); + } +} diff --git a/src/scratch_dragger.ts b/src/scratch_dragger.ts new file mode 100644 index 0000000000..bfedba5e70 --- /dev/null +++ b/src/scratch_dragger.ts @@ -0,0 +1,211 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import { BlockDragOutside } from "./events/events_block_drag_outside"; +import { BlockDragEnd } from "./events/events_block_drag_end"; +import { isProcedureBlock, getCallers } from "./procedures"; + +/** + * CSS class that allows the workspace to overflow its bounds when set. + */ +const BOUNDLESS_CLASS = "boundless"; + +/** + * Class responsible for managing dragging items on the workspace. + */ +export class ScratchDragger extends Blockly.dragging.Dragger { + /** + * Whether or not the current drag location is outside of the main workspace. + */ + draggedOutOfBounds = false; + + /** + * Whether or not the current drag started from the flyout. + */ + originatedFromFlyout = false; + + /** + * Sets the current item being dragged. + * + * @param draggable The item being dragged. + */ + setDraggable(draggable: Blockly.IDraggable) { + this.draggable = draggable; + } + + /** + * Handles the start of a drag operation. + * + * @param event The event that triggered the drag. + */ + onDragStart(event: PointerEvent) { + super.onDragStart(event); + if (this.draggable instanceof Blockly.BlockSvg) { + this.workspace.addClass(BOUNDLESS_CLASS); + const absoluteMetrics = this.workspace + .getMetricsManager() + .getAbsoluteMetrics(); + const viewMetrics = this.workspace.getMetricsManager().getViewMetrics(); + if ( + this.workspace.RTL + ? event.clientX > + this.workspace.getParentSvg().getBoundingClientRect().left + + viewMetrics.width + : event.clientX < absoluteMetrics.left + ) { + this.originatedFromFlyout = true; + } + } + } + + /** + * Handles motion during an ongoing drag operation. + * + * @param event The event that triggered this call. + * @param totalDelta The change in pointer position since the last invocation. + */ + onDrag(event: PointerEvent, totalDelta: Blockly.utils.Coordinate) { + super.onDrag(event, totalDelta); + this.updateOutOfBoundsState(event); + } + + /** + * Records whether or not the current drag is out of the workspace's bounds. + * + * @param event The event that triggered this call. + */ + updateOutOfBoundsState(event: PointerEvent) { + if (this.draggable instanceof Blockly.BlockSvg) { + const outOfBounds = !this.isInsideWorkspace(event); + if (outOfBounds !== this.draggedOutOfBounds) { + const event = new BlockDragOutside( + this.getDragRoot(this.draggable) as Blockly.BlockSvg, + outOfBounds + ); + Blockly.Events.fire(event); + this.draggedOutOfBounds = outOfBounds; + } + } + } + + /** + * Handles the end of a drag. + * + * @param event The event that ended the drag. + */ + onDragEnd(event: PointerEvent) { + if ( + this.draggable instanceof Blockly.BlockSvg && + this.draggable.type === "procedures_definition" && + this.wouldDeleteDraggable(event, this.draggable.getRootBlock()) + ) { + const prototype = this.draggable + .getInput("custom_block") + .connection.targetBlock(); + const hasCaller = + prototype instanceof Blockly.BlockSvg && + isProcedureBlock(prototype) && + getCallers( + prototype.getProcCode(), + this.draggable.workspace, + this.draggable.getRootBlock(), + false + ).length > 0; + + if (hasCaller) { + Blockly.dialog.alert(Blockly.Msg.PROCEDURE_USED); + this.draggable.revertDrag(); + this.draggable.endDrag(); + return; + } + } + + super.onDragEnd(event); + + this.updateOutOfBoundsState(event); + if (this.draggable instanceof Blockly.BlockSvg) { + const event = new BlockDragEnd( + this.getDragRoot(this.draggable) as Blockly.BlockSvg, + this.draggedOutOfBounds + ); + Blockly.Events.fire(event); + // If this block was dragged out of the flyout and dropped outside of + // the workspace (e.g. on a different sprite), the block that was created + // on the workspace in order to depict the block mid-drag needs to be + // deleted. + if (this.originatedFromFlyout && this.draggedOutOfBounds) { + Blockly.renderManagement.finishQueuedRenders().then(() => { + const rootBlock = this.getDragRoot(this.draggable); + if (rootBlock instanceof Blockly.BlockSvg) { + rootBlock.dispose(); + } + }); + } + } + this.workspace.removeClass(BOUNDLESS_CLASS); + } + + /** + * Returns whether or not the dragged item should return to its starting + * position. + * + * @param event The drag event that triggered this check. + * @param rootDraggable The topmost item being dragged. + * @returns True if the draggable should return to its starting position. + */ + shouldReturnToStart(event: PointerEvent, rootDraggable: Blockly.IDraggable) { + // If a block is dragged out of the workspace to be e.g. dropped on another + // sprite, it should remain in the same place on the workspace where it was, + // rather than being moved to an invisible part of the workspace. + return ( + this.draggedOutOfBounds || super.shouldReturnToStart(event, rootDraggable) + ); + } + + /** + * Returns the root element being dragged. For shadow blocks, this is the + * parent block. + * + * @param draggable The element being dragged directly. + * @returns The element being dragged, or its parent. + */ + getDragRoot(draggable: Blockly.IDraggable) { + // We can't just use getRootBlock() here because, when blocks are detached + // from a stack via dragging, getRootBlock() still returns the root of that + // stack. + if (draggable instanceof Blockly.BlockSvg && draggable.isShadow()) { + return draggable.getParent(); + } + + return draggable; + } + + /** + * Returns whether or not the given event occurred within the bounds of the + * workspace. + * + * @param event The event to check. + * @returns True if the event occurred inside the workspace. + */ + isInsideWorkspace(event: PointerEvent) { + const bounds = this.workspace.getParentSvg().getBoundingClientRect(); + const workspaceRect = new Blockly.utils.Rect( + bounds.top, + bounds.bottom, + bounds.left, + bounds.right + ); + return workspaceRect.contains(event.clientX, event.clientY); + } +} + +Blockly.registry.register( + Blockly.registry.Type.BLOCK_DRAGGER, + Blockly.registry.DEFAULT, + ScratchDragger, + true +); diff --git a/src/scratch_insertion_marker_previewer.ts b/src/scratch_insertion_marker_previewer.ts new file mode 100644 index 0000000000..81e4397f80 --- /dev/null +++ b/src/scratch_insertion_marker_previewer.ts @@ -0,0 +1,45 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; + +/** + * Displays an indicator of where a block being dragged will be connected. + */ +class ScratchInsertionMarkerPreviewer extends Blockly.InsertionMarkerPreviewer { + /** + * Transforms the given block into a JSON representation used to construct an + * insertion marker. + * + * @param block The block to serialize and use as an insertion marker. + * @returns A JSON-formatted string corresponding to a serialized + * representation of the given block suitable for use as an insertion + * marker. + */ + protected override serializeBlockToInsertionMarker(block: Blockly.BlockSvg) { + const blockJson = Blockly.serialization.blocks.save(block, { + addCoordinates: false, + addInputBlocks: true, + addNextBlocks: false, + doFullSerialization: false, + }); + + if (!blockJson) { + throw new Error( + `Failed to serialize source block. ${block.toDevString()}` + ); + } + + return blockJson; + } +} + +Blockly.registry.register( + Blockly.registry.Type.CONNECTION_PREVIEWER, + Blockly.registry.DEFAULT, + ScratchInsertionMarkerPreviewer, + true +); diff --git a/src/scratch_variable_map.ts b/src/scratch_variable_map.ts new file mode 100644 index 0000000000..41865639e5 --- /dev/null +++ b/src/scratch_variable_map.ts @@ -0,0 +1,28 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; + +/** + * Class that provides storage for variables. + */ +class ScratchVariableMap extends Blockly.VariableMap { + getVariable(name: string, type: string) { + // Variable names in Blockly are case-insensitive, but case sensitive in + // Scratch. Override the implementation to only return a variable whose name + // is identical to the one requested. + const variables = this.getVariablesOfType(type ?? ""); + if (!variables.length) return null; + return variables.find((v) => v.getName() === name) ?? null; + } +} + +Blockly.registry.register( + Blockly.registry.Type.VARIABLE_MAP, + Blockly.registry.DEFAULT, + ScratchVariableMap, + true +); diff --git a/src/scratch_variable_model.ts b/src/scratch_variable_model.ts new file mode 100644 index 0000000000..47acfe3157 --- /dev/null +++ b/src/scratch_variable_model.ts @@ -0,0 +1,30 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; + +/** + * Class that represents a variable with extra fields for Scratch. + */ +export class ScratchVariableModel extends Blockly.VariableModel { + constructor( + workspace: Blockly.Workspace, + name: string, + type: string, + id: string, + public isLocal = false, + public isCloud = false + ) { + super(workspace, name, type, id); + } +} + +Blockly.registry.register( + Blockly.registry.Type.VARIABLE_MODEL, + Blockly.registry.DEFAULT, + ScratchVariableModel, + true +); diff --git a/src/shadows.ts b/src/shadows.ts new file mode 100644 index 0000000000..e154a4d921 --- /dev/null +++ b/src/shadows.ts @@ -0,0 +1,65 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import { Colours } from "./colours"; + +/** + * Creates an SVG filter to apply drop shadows to blocks being dragged and + * inserts it into the DOM. + */ +export function buildShadowFilter(workspace: Blockly.WorkspaceSvg) { + const svg = workspace.getParentSvg(); + const defs = Blockly.utils.dom.createSvgElement( + Blockly.utils.Svg.DEFS, + {}, + svg + ); + // Adjust these width/height, x/y properties to stop the shadow from clipping + const dragShadowFilter = Blockly.utils.dom.createSvgElement( + "filter", + { + id: "blocklyDragShadowFilter", + height: "140%", + width: "140%", + y: "-20%", + x: "-20%", + }, + defs + ); + Blockly.utils.dom.createSvgElement( + "feGaussianBlur", + { + in: "SourceAlpha", + stdDeviation: "6", + }, + dragShadowFilter + ); + const componentTransfer = Blockly.utils.dom.createSvgElement( + "feComponentTransfer", + { result: "offsetBlur" }, + dragShadowFilter + ); + // Shadow opacity is specified in the adjustable colour library, + // since the darkness of the shadow largely depends on the workspace colour. + Blockly.utils.dom.createSvgElement( + "feFuncA", + { + type: "linear", + slope: Colours.dragShadowOpacity, + }, + componentTransfer + ); + Blockly.utils.dom.createSvgElement( + "feComposite", + { + in: "SourceGraphic", + in2: "offsetBlur", + operator: "over", + }, + dragShadowFilter + ); +} diff --git a/src/status_indicator_label.ts b/src/status_indicator_label.ts new file mode 100644 index 0000000000..62dd9ece7c --- /dev/null +++ b/src/status_indicator_label.ts @@ -0,0 +1,174 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2018 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Class for a category header in the flyout for Scratch + * extensions which can display a textual label and a status button. + * @author ericr@media.mit.edu (Eric Rosenbaum) + */ + +import * as Blockly from "blockly/core"; + +/** + * Class for a category header in the flyout for Scratch extensions which can + * display a textual label and a status button. + */ +export class StatusIndicatorLabel extends Blockly.FlyoutButton { + /** + * The ID of the Scratch extension whose status is indicated by this label. + */ + extensionId: string; + + /** + * DOM element that displays the status indicator dot. + */ + imageElement: SVGImageElement; + + /** + * Opaque data for mouse up listener used to unbind it in dispose(). + */ + mouseUpWrapper: Blockly.browserEvents.Data; + + /** + * Function to be invoked when the status indicator is clicked. + */ + static statusButtonCallback: (extensionId: string) => void; + + /** + * Creates a new StatusIndicatorLabel. + * + * @param workspace The workspace in which to place this header. + * @param targetWorkspace The flyout's target workspace. + * @param json The JSON specifying the header. + */ + constructor( + workspace: Blockly.WorkspaceSvg, + targetWorkspace: Blockly.WorkspaceSvg, + json: Blockly.utils.toolbox.LabelInfo + ) { + super(workspace, targetWorkspace, json, true); + this.extensionId = json["id"]; + + const heightDelta = 40 - this.height; + this.height = 40; + const text = this.getSvgRoot().querySelector("text"); + const previousY = Number(text.getAttribute("y")); + + text.setAttribute("y", `${previousY + heightDelta / 2}`); + + const statusButtonWidth = 30; + const marginX = 20; + const marginY = 5; + const touchPadding = 16; + const flyoutWidth = targetWorkspace.getFlyout().getWidth(); + + const statusButtonX = workspace.RTL + ? marginX - flyoutWidth + statusButtonWidth + : (flyoutWidth - statusButtonWidth - marginX) / workspace.scale; + + this.imageElement = Blockly.utils.dom.createSvgElement( + "image", + { + class: "blocklyFlyoutButton", + height: statusButtonWidth + "px", + width: statusButtonWidth + "px", + x: statusButtonX + "px", + y: marginY + "px", + }, + this.getSvgRoot() + ); + const imageElementBackground = Blockly.utils.dom.createSvgElement( + "rect", + { + class: "blocklyTouchTargetBackground", + height: statusButtonWidth + 2 * touchPadding + "px", + width: statusButtonWidth + 2 * touchPadding + "px", + x: statusButtonX - touchPadding + "px", + y: marginY - touchPadding + "px", + }, + this.getSvgRoot() + ); + + this.refreshStatus(); + + this.mouseUpWrapper = Blockly.browserEvents.bind( + imageElementBackground, + "mouseup", + null, + () => { + StatusIndicatorLabel.statusButtonCallback?.call(this, this.extensionId); + } + ); + } + + /** + * Set the image on the status button using a status string. + */ + refreshStatus() { + var status = this.getExtensionState(this.extensionId); + var basePath = Blockly.getMainWorkspace().options.pathToMedia; + if (status == StatusButtonState.READY) { + this.setImageSrc(basePath + "status-ready.svg"); + } + if (status == StatusButtonState.NOT_READY) { + this.setImageSrc(basePath + "status-not-ready.svg"); + } + } + + /** + * Set the source URL of the image for the button. + * @param src New source. + * @package + */ + setImageSrc(src: string) { + if (this.imageElement) { + this.imageElement.setAttributeNS( + "http://www.w3.org/1999/xlink", + "xlink:href", + src + ); + } + } + + /** + * Gets the extension state. Overridden externally. + * @param extensionId The ID of the extension in question. + * @return The state of the extension. + */ + getExtensionState(extensionId: string): StatusButtonState { + return StatusButtonState.NOT_READY; + } + + /** + * Disposes of this status indicator label. + */ + dispose() { + Blockly.browserEvents.unbind(this.mouseUpWrapper); + super.dispose(); + } +} + +/** + * Set of available states for a status indicator. + */ +export enum StatusButtonState { + READY = "ready", + NOT_READY = "not ready", +} diff --git a/src/status_indicator_label_flyout_inflater.ts b/src/status_indicator_label_flyout_inflater.ts new file mode 100644 index 0000000000..6358cdbca1 --- /dev/null +++ b/src/status_indicator_label_flyout_inflater.ts @@ -0,0 +1,46 @@ +/** + * @license + * Copyright 2024 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import { StatusIndicatorLabel } from "./status_indicator_label"; + +export const STATUS_INDICATOR_LABEL_TYPE = "status_indicator_label"; + +/** + * Flyout inflater responsible for creating status indicator labels. + */ +class StatusIndicatorLabelFlyoutInflater extends Blockly.LabelFlyoutInflater { + /** + * Creates a status indicator label on the flyout from the given state. + * @param state JSON representation of a status indicator label. + * @param flyoutWorkspace The workspace to create the + * label on. + * @returns The newly created status indicator label. + */ + load( + state: Blockly.utils.toolbox.LabelInfo, + flyout: Blockly.IFlyout + ): Blockly.FlyoutItem { + const label = new StatusIndicatorLabel( + flyout.getWorkspace(), + flyout.targetWorkspace, + state + ); + label.show(); + return new Blockly.FlyoutItem(label, STATUS_INDICATOR_LABEL_TYPE); + } +} + +/** + * Register the status indicator label flyout inflater. + */ +export function registerStatusIndicatorLabelFlyoutInflater() { + Blockly.registry.register( + Blockly.registry.Type.FLYOUT_INFLATER, + STATUS_INDICATOR_LABEL_TYPE, + StatusIndicatorLabelFlyoutInflater + ); +} diff --git a/src/variables.ts b/src/variables.ts new file mode 100644 index 0000000000..68e0b2ac3f --- /dev/null +++ b/src/variables.ts @@ -0,0 +1,385 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2012 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Utility functions for handling variables. + * @author fraser@google.com (Neil Fraser) + */ +import * as Blockly from "blockly/core"; +import { + LIST_VARIABLE_TYPE, + BROADCAST_MESSAGE_VARIABLE_TYPE, +} from "./constants"; +import { ScratchVariableModel } from "./scratch_variable_model"; +import { ScratchContinuousToolbox } from "./scratch_continuous_toolbox"; +import { CheckableContinuousFlyout } from "./checkable_continuous_flyout"; + +/** + * Constant prefix to differentiate cloud variable names from other types of + * variables. + * This is the \u2601 cloud unicode character followed by a space. + */ +const CLOUD_PREFIX = "☁ "; + +type PromptType = ( + message: string, + defaultValue: string, + callback: ( + variableName: string, + additionalVars: string[], + variableOptions?: { scope?: string; isCloud?: boolean } + ) => void, + title?: string, + varType?: string +) => void; + +let prompt: PromptType | undefined = undefined; + +/** + * Sets the handler for calls to prompt(). + * + * @param handler The new prompt function. + */ +export function setPromptHandler(handler: PromptType) { + prompt = handler; +} + +/** + * Create a new variable on the given workspace. + * + * @param workspace The workspace on which to create the variable. + * @param opt_callback An optional callback function to act on the id of the + * variable that is created from the user's input, or null if the change is + * to be aborted (cancel button or an invalid name was provided). + * @param opt_type Optional type of the variable to be created, like 'string' or + * 'list'. + */ +export function createVariable( + workspace: Blockly.WorkspaceSvg, + opt_callback?: (id?: string) => void, + opt_type?: string +) { + // Decide on a modal message based on the opt_type. If opt_type was not + // provided, default to the original message for scalar variables. + let newMsg, modalTitle; + if (opt_type === BROADCAST_MESSAGE_VARIABLE_TYPE) { + newMsg = Blockly.Msg.NEW_BROADCAST_MESSAGE_TITLE; + modalTitle = Blockly.Msg.BROADCAST_MODAL_TITLE; + } else if (opt_type === LIST_VARIABLE_TYPE) { + newMsg = Blockly.Msg.NEW_LIST_TITLE; + modalTitle = Blockly.Msg.LIST_MODAL_TITLE; + } else { + // Note: this case covers 1) scalar variables, 2) any new type of + // variable not explicitly checked for above, and 3) a null or undefined + // opt_type -- turns a falsey opt_type into '' + // TODO (#1251) Warn developers that they didn't provide an opt_type/ + // provided a falsey opt_type + opt_type = opt_type ? opt_type : ""; + newMsg = Blockly.Msg.NEW_VARIABLE_TITLE; + modalTitle = Blockly.Msg.VARIABLE_MODAL_TITLE; + } + const validate = nameValidator.bind(null, opt_type); + + // Prompt the user to enter a name for the variable + prompt( + newMsg, + "", + function ( + text: string, + additionalVars: string[], + variableOptions?: { scope?: string; isCloud?: boolean } + ) { + variableOptions = variableOptions || {}; + const scope = variableOptions.scope; + const isLocal = scope === "local" || false; + const isCloud = variableOptions.isCloud || false; + // Default to [] if additionalVars is not provided + additionalVars = additionalVars || []; + // Only use additionalVars for global variable creation. + const additionalVarNames = isLocal ? [] : additionalVars; + + const validatedText = validate( + text, + workspace, + additionalVarNames, + isCloud, + opt_callback + ); + if (validatedText) { + const variable = new ScratchVariableModel( + workspace, + validatedText, + opt_type, + null, + isLocal, + isCloud + ); + workspace.getVariableMap().addVariable(variable); + Blockly.Events.fire( + new (Blockly.Events.get(Blockly.Events.VAR_CREATE))(variable) + ); + + const toolbox = workspace.getToolbox(); + const flyout = toolbox.getFlyout(); + const variableBlockId = variable.getId(); + if ( + toolbox instanceof ScratchContinuousToolbox && + flyout instanceof CheckableContinuousFlyout + ) { + toolbox.runAfterRerender(() => { + flyout.setCheckboxState(variableBlockId, true); + }); + } + + if (opt_callback) { + opt_callback(variableBlockId); + } + } else { + // User canceled prompt without a value. + if (opt_callback) { + opt_callback(null); + } + } + }, + modalTitle, + opt_type + ); +} + +/** + * This function provides a common interface for variable name validation + * agnostic of type. This is so that functions like createVariable and + * renameVariable can call a single function (with a single type signature) to + * validate the user-provided name for a variable. + * + * @param type The type of the variable for which the provided name should be + * validated. + * @param text The user-provided text that should be validated as a variable + * name. + * @param workspace The workspace on which to validate the variable name. This + * is the workspace used to check whether the variable already exists. + * @param additionalVars A list of additional var names to check for conflicts + * against. + * @param isCloud Whether the variable is a cloud variable. + * @param opt_callback An optional function to be called on a pre-existing + * variable of the user-provided name. This function is currently only used + * for broadcast messages. + * @returns The validated name according to the parameters given, if the name is + * determined to be valid, or null if the name is determined to be invalid/ + * in-use, and the calling function should not proceed with creating or + * renaming the variable. + */ +function nameValidator( + type: string, + text: string, + workspace: Blockly.WorkspaceSvg, + additionalVars: string[], + isCloud: boolean, + opt_callback?: (id?: string) => void +): string { + // The validators for the different variable types require slightly different + // arguments. For broadcast messages, if a broadcast message of the provided + // name already exists, the validator needs to call a function that updates + // the selected field option of the dropdown menu of the block that was used + // to create the new message. For scalar variables and lists, the validator + // has the same validation behavior, but needs to know which type of variable + // to check for and needs a type-specific error message that is displayed when + // a variable of the given name and type already exists. + + if (type === BROADCAST_MESSAGE_VARIABLE_TYPE) { + return validateBroadcastMessageName(text, workspace, opt_callback); + } else if (type === LIST_VARIABLE_TYPE) { + return validateScalarVarOrListName( + text, + workspace, + additionalVars, + false, + type, + Blockly.Msg.LIST_ALREADY_EXISTS + ); + } else { + return validateScalarVarOrListName( + text, + workspace, + additionalVars, + isCloud, + type, + Blockly.Msg.VARIABLE_ALREADY_EXISTS + ); + } +} + +/** + * Validate the given name as a broadcast message type. + * + * @param name The name to validate + * @param workspace The workspace the name should be validated against. + * @param opt_callback An optional function to call if a broadcast message + * already exists with the given name. This function will be called on the + * id of the existing variable. + * @returns The validated name, or null if invalid. + */ +function validateBroadcastMessageName( + name: string, + workspace: Blockly.WorkspaceSvg, + opt_callback?: (id?: string) => void +): string | null { + if (!name) { + // no name was provided or the user cancelled the prompt + return null; + } + const variable = workspace.getVariable(name, BROADCAST_MESSAGE_VARIABLE_TYPE); + if (variable) { + // If the user provided a name for a broadcast message that already exists, + // use the provided callback function to update the selected option in + // the field of the block that was used to create + // this message. + if (opt_callback) { + opt_callback(variable.getId()); + } + // Return null to signal to the calling function that we do not want to create + // a new variable since one already exists. + return null; + } else { + // The name provided is actually a new name, so the calling + // function should go ahead and create it as a new variable. + return name; + } +} + +/** + * Validate the given name as a scalar variable or list type. + * This function is also responsible for any user facing error-handling. + * + * @param name The name to validate + * @param workspace The workspace the name should be validated against. + * @param additionalVars A list of additional variable names to check for + * conflicts against. + * @param isCloud Whether the variable is a cloud variable. + * @param type The type to validate the variable as. This should be one of + * SCALAR_VARIABLE_TYPE or LIST_VARIABLE_TYPE. + * @param errorMsg The type-specific error message the user should see if a + * variable of the validated, given name and type already exists. + * @returns The validated name, or null if invalid. + */ +function validateScalarVarOrListName( + name: string, + workspace: Blockly.WorkspaceSvg, + additionalVars: string[], + isCloud: boolean, + type: string, + errorMsg: string +): string | null { + // For scalar variables, we don't want leading or trailing white space + name = name.trim(); + if (!name) { + return null; + } + if (isCloud) { + name = CLOUD_PREFIX + name; + } + if (workspace.getVariable(name, type) || additionalVars.indexOf(name) >= 0) { + // error + Blockly.dialog.alert(errorMsg.replace("%1", name)); + return null; + } else { + // trimmed name is valid + return name; + } +} + +/** + * Rename a variable with the given workspace, variableType, and oldName. + * + * @param workspace The workspace on which to rename the variable. + * @param variable Variable to rename. + * @param 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. + */ +export function renameVariable( + workspace: Blockly.WorkspaceSvg, + variable: ScratchVariableModel, + opt_callback?: (id?: string) => void +) { + // Validation and modal message/title depends on the variable type + let promptMsg, modalTitle; + const varType = variable.getType(); + if (varType === BROADCAST_MESSAGE_VARIABLE_TYPE) { + console.warn( + `Unexpected attempt to rename a broadcast message with + id: "${variable.getId()} and name: ${variable.getName()}` + ); + return; + } + if (varType === LIST_VARIABLE_TYPE) { + promptMsg = Blockly.Msg.RENAME_LIST_TITLE; + modalTitle = Blockly.Msg.RENAME_LIST_MODAL_TITLE; + } else { + // Default for all other types of variables + promptMsg = Blockly.Msg.RENAME_VARIABLE_TITLE; + modalTitle = Blockly.Msg.RENAME_VARIABLE_MODAL_TITLE; + } + const validate = nameValidator.bind(null, varType); + + const promptText = promptMsg.replace("%1", variable.getName()); + let promptDefaultText = variable.getName(); + if (variable.isCloud && variable.getName().indexOf(CLOUD_PREFIX) == 0) { + promptDefaultText = promptDefaultText.substring(CLOUD_PREFIX.length); + } + + prompt( + promptText, + promptDefaultText, + (newName: string, additionalVars: string[]) => { + if ( + variable.isCloud && + newName.length > 0 && + newName.indexOf(CLOUD_PREFIX) == 0 + ) { + newName = newName.substring(CLOUD_PREFIX.length); + // The name validator will add the prefix back + } + additionalVars = additionalVars || []; + const additionalVarNames = variable.isLocal ? [] : additionalVars; + const validatedText = validate( + newName, + workspace, + additionalVarNames, + variable.isCloud + ); + if (validatedText) { + workspace.renameVariableById(variable.getId(), validatedText); + if (opt_callback) { + opt_callback(newName); + } + } else { + // User canceled prompt without a value. + if (opt_callback) { + opt_callback(null); + } + } + }, + modalTitle, + varType + ); +} + +export { getVariablesCategory } from "./data_category"; diff --git a/src/xml.ts b/src/xml.ts new file mode 100644 index 0000000000..6ab4fb00ba --- /dev/null +++ b/src/xml.ts @@ -0,0 +1,57 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as Blockly from "blockly/core"; +import { ScratchVariableModel } from "./scratch_variable_model"; + +/** + * Clears the workspace and loads the given serialized state. + * + * @param xml XML representation of a Blockly workspace. + * @param workspace The workspace to load the serialized data onto. + */ +export function clearWorkspaceAndLoadFromXml( + xml: Element, + workspace: Blockly.WorkspaceSvg +): string[] { + workspace.setResizesEnabled(false); + Blockly.Events.setGroup(true); + workspace.clear(); + + // Manually load variables to include the cloud and local properties that core + // Blockly is unaware of. + for (const variable of xml.querySelectorAll("variables variable")) { + const id = variable.getAttribute("id"); + if (!id) continue; + const type = variable.getAttribute("type"); + const name = variable.textContent; + const isLocal = variable.getAttribute("islocal") === "true"; + const isCloud = variable.getAttribute("iscloud") === "true"; + + const variableModel = new ScratchVariableModel( + workspace, + name, + type, + id, + isLocal, + isCloud + ); + Blockly.Events.fire( + new (Blockly.Events.get(Blockly.Events.VAR_CREATE))(variableModel) + ); + workspace.getVariableMap().addVariable(variableModel); + } + + // Remove the `variables` element from the XML to prevent Blockly from + // throwing or stomping on the variables we created. + xml.querySelector("variables").remove(); + + // Defer to core for the rest of the deserialization. + const blockIds = Blockly.Xml.domToWorkspace(xml, workspace); + + workspace.setResizesEnabled(true); + return blockIds; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000..7c4e19b64d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "include": [ + "./src/", + "./tests/", + "./types/" + ], + "compilerOptions": { + "outDir": "./dist/", + "noImplicitAny": true, + "module": "es6", + "target": "es6", + "allowJs": true, + "moduleResolution": "node" + } +} diff --git a/types/continuous-toolbox.d.ts b/types/continuous-toolbox.d.ts new file mode 100644 index 0000000000..9d8c17aa10 --- /dev/null +++ b/types/continuous-toolbox.d.ts @@ -0,0 +1 @@ +declare module "@blockly/continuous-toolbox"; diff --git a/webpack.config.js b/webpack.config.js index 90ab127931..380acfaf2e 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,97 +1,53 @@ -// patch 'fs' to fix EMFILE errors, for example on WSL -var realFs = require('fs'); -var gracefulFs = require('graceful-fs'); -gracefulFs.gracefulify(realFs); +const path = require("path"); -var CopyWebpackPlugin = require('copy-webpack-plugin'); -var path = require('path'); -var UglifyJsPlugin = require('uglifyjs-webpack-plugin'); +// Base config that applies to either development or production mode. +const config = { + entry: "./src/index.ts", + output: { + library: "ScratchBlocks", + libraryTarget: "commonjs2", + path: path.resolve(__dirname, "dist"), + filename: "[name].js", + }, + resolve: { + extensions: [".ts", ".js"], + }, + module: { + rules: [ + { + test: /\.ts$/, + use: "ts-loader", + exclude: /node_modules/, + }, + ], + }, + // Enable webpack-dev-server to get hot refresh of the app. + devServer: { + static: "./build", + }, +}; +module.exports = (env, argv) => { + if (argv.mode === "development") { + // Set the output path to the `build` directory + // so we don't clobber production builds. + config.output.path = path.resolve(__dirname, "build"); + // Generate source maps for our code for easier debugging. + // Not suitable for production builds. If you want source maps in + // production, choose a different one from https://webpack.js.org/configuration/devtool + config.devtool = "eval-cheap-module-source-map"; -module.exports = [{ - mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', - entry: { - horizontal: './shim/horizontal.js', - vertical: './shim/vertical.js' - }, - output: { - library: 'ScratchBlocks', - libraryTarget: 'commonjs2', - path: path.resolve(__dirname, 'dist'), - filename: '[name].js' - }, - optimization: { - minimize: false - }, - performance: { - hints: false + // Include the source maps for Blockly for easier debugging Blockly code. + config.module.rules.push({ + test: /(blockly\/.*\.js)$/, + use: [require.resolve("source-map-loader")], + enforce: "pre", + }); + + // Ignore spurious warnings from source-map-loader + // It can't find source maps for some Closure modules and that is expected + config.ignoreWarnings = [/Failed to parse source map/]; } -}, { - mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', - entry: { - horizontal: './shim/horizontal.js', - vertical: './shim/vertical.js' - }, - output: { - library: 'Blockly', - libraryTarget: 'umd', - path: path.resolve(__dirname, 'dist', 'web'), - filename: '[name].js' - }, - optimization: { - minimizer: [ - new UglifyJsPlugin({ - uglifyOptions: { - mangle: false - } - }) - ] - }, - plugins: [] -}, -{ - mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', - entry: './shim/gh-pages.js', - output: { - filename: '[name].js', - path: path.resolve(__dirname, 'gh-pages') - }, - optimization: { - minimize: false - }, - performance: { - hints: false - }, - plugins: [ - new CopyWebpackPlugin([{ - from: 'node_modules/google-closure-library', - to: 'closure-library' - }, { - from: 'blocks_common', - to: 'playgrounds/blocks_common', - }, { - from: 'blocks_horizontal', - to: 'playgrounds/blocks_horizontal', - }, { - from: 'blocks_vertical', - to: 'playgrounds/blocks_vertical', - }, { - from: 'core', - to: 'playgrounds/core' - }, { - from: 'media', - to: 'playgrounds/media' - }, { - from: 'msg', - to: 'playgrounds/msg' - }, { - from: 'tests', - to: 'playgrounds/tests' - }, { - from: '*.js', - ignore: 'webpack.config.js', - to: 'playgrounds' - }]) - ] -}]; + return config; +};